Callback URL for the API Response Django - django

I am building a code editor using the Hackerearth API.I have made the code to send a asynchronous API Request as it would speed up performance and reduce waiting time.
I referred their docs about sending an async request.I need to specify a callback url.Currently my project is running locally.So I couldn't figure out how to specify the callback URL and render out the Response from that callback URL.The logic to process the response received at the callback url is also specified in their docs.
def compileCode(request):
if request.is_ajax():
source = request.POST.get('source')
lang = request.POST.get('lang')
client_secret = settings.CLIENT_SECRET
data = {
"client_secret": client_secret,
"async": 1,
'id': 123,
'callback': **what to do here**,
"source": source,
"lang": lang,
}
res = requests.post(RUN_URL, data=data)
return JsonResponse(res.json(), safe=False)
return HttpResponseBadRequest()
Code to process the Response from the callback URL
def api_response(request):
payload = request.POST.get('payload', '')
payload = json.loads(payload)
run_status = payload.get('run_status')
o = run_status['output']
return HttpResponse('API Response Recieved!')
Any help is welcome :)

A callback URL "calls back" a web address rather than a bit of code and it can be invoked by API method, you can call after it's done. That URL can be anything. It doesn't have to be a static URL. Often it is a script to perform certain functions.
As here you don't need to perform anything after receiving results.
You don't need to pass the callback url, it will work even without it.
I made it work by just passing by below code.
RUN_URL = "https://api.hackerearth.com/v3/code/run/"
CLIENT_SECRET = 'your-client-secret-from-hackerearth'
data = {
'client_secret': CLIENT_SECRET,
'async': 1,
'source': source,
'lang': lang,
}
r = requests.post(RUN_URL, data=data)

Related

Data in request.body can't be found by request.data - Django Rest Framework

I'm writing a django application. I am trying to call my django rest framework from outside, and expecting an answer.
I use requests to send some data to a function in the DRF like this:
j=[i.json() for i in AttachmentType.objects.annotate(text_len=Length('terms')).filter(text_len__gt=1)]
j = json.dumps(j)
url = settings.WEBSERVICE_URL + '/api/v1/inference'
headers = {
'Content-Disposition': f'attachment; filename={file_name}',
'callback': 'http://localhost',
'type':j,
'x-api-key': settings.WEBSERVICE_API_KEY
}
data = {
'type':j
}
files = {
'file':file
}
response = requests.post(
url,
headers=headers,
files=files,
json=data,
)
In the DRF, i use the request object to get the data.
class InferenceView(APIView):
"""
From a pdf file, extract infos and return it
"""
permission_classes = [HasAPIKey]
def post(self, request):
print("REQUEST FILE",request.FILES)
print("REQUEST DATA",request.data)
callback = request.headers.get('callback', None)
# check correctness of callback
msg, ok = check_callback(callback)
if not ok: # if not ok return bad request
return build_json_response(msg, 400)
# get zip file
zip_file = request.FILES.get('file', None)
parsed = json.loads(request.data.get('type', None).replace("'","\""))
The problem is that the data in the DRF are not received correctly. Whatever I send from the requests.post is not received.
I am sending a file and a JSON together. The file somehow is received, but other data are not.
If I try to do something like
request.data.update({"type":j})
in the DRF, the JSON is correctly added to the data, so it is not a problem with the JSON I'm trying to send itself.
Another thing, request.body shows that the JSON is somehow present in the body, but request.data can't find it.
I don't want to use request.body directly because I can't understand why it is present in the body but not visible with request.data.
In this line
response = requests.post(
url,
headers=headers,
files=files,
json=data,
)
replace json=data with data=data
like this:
response = requests.post(
url,
headers=headers,
files=files,
data=data,
)

How do i notify the user about a webhook In django

Hello ive been having problems with web hooks in django i need a way to notify a user that he/she has made a successful payment am notified about a successful web hook from adding a URL to my processor that sends a payment successful request to my web hook how can i show this to the user to notify if its successful
my code is: this is where the user sends checkout details to my server
hopping to add a waiting spinner that resolves to a tick or an X depending if the user has paid
<script>
document.getElementById("checkout-btn").addEventListener('click', analyzeText());
function analyzeText(){
var wallet = document.getElementById('wallet').value;
var phone = document.getElementById('phone').value;
var order_id = document.getElementById('order_id').value;
$.ajax({
type: "POST",
url: "{% url 'orders:Checkout' %}", /* Call python function in this script */
data: {csrfmiddlewaretoken: '{{ csrf_token }}',
wallet: wallet,
phone:phone,
order_id:order_id,
}, /* Passing the text data */
success: callback
});
}
function callback(response){
console.log(response);
}
</script>
the view this is sent to does this and its basically just calling the checkout API which now deals with the checkout if succesful or failed a webhook should be sent to my callback url
def processOrder(request):
def spektraCheckoutLink(amount, reference, transaction_id, order_id):
url = "https://api.spektra.co/api/v1/payments/pay-in"
# request payload
# spektraAccountName is Optional, used for subaccount-specific payments
payload = {
"amount": amount,
"account": phone,
"currency": "KES",
"description": reference,
}
# pass authorization token obtained during authentication in headers
authcode = str(auth())
headers = {
'Content-Type': "application/json",
'Authorization': "Bearer " + authcode
}
response = requests.request("POST", url, data=json.dumps(payload), headers=headers)
# Print response data
print(response.json())
data = response.json()
return True
if request.method == "POST":
global order_id
try:
order_id = request.POST['order_id']
except Exception as e:
print(e)
print(traceback.format_exc())
raise HttpResponseServerError()
order = Order.objects.get(id=order_id)
try:
wallet = request.POST['wallet']
order.wallet = wallet
except Exception as e:
print(e)
print(traceback.format_exc())
raise HttpResponseServerError()
try:
phone = request.POST['phone']
order.phone = phone
except Exception as e:
print(e)
print(traceback.format_exc())
raise HttpResponseServerError()
order.save()
try:
current_user = get_user_model().objects.get(id=request.user.id)
Transaction.objects.get_or_create(user=current_user, order=order, amount=order.price, phone=order.phone)
trans = Transaction.objects.get(order=order)
link = spektraCheckoutLink(order.price, order.order_reference, trans.id, order.id)
return redirect(link)
except Exception as e:
print(e)
print(traceback.format_exc())
# redirect to 404
raise HttpResponseServerError()
what i don't know is how i'm going to change the spinner status on the users front end when the callback arrives to notify them if the transaction was successful or not
this is my webhook view which updates the order to paid my question is how can i notify the user in his browser that the payment is complete i dont know how to do it
def processOrderStatus(request):
# updates transaction number and status
data = json.loads(request.body)
order = Order.objects.get(phone=data['account'])
order.order_status = order.PAID
order.save
id = request.user.id
return redirect('orders:Orders', user_id=id)
Knowing your needs I will try to put you on a couple of possible ways to accomplish what you need, the first one is using a javascript interval with and ajax call to get the order status every 5 seconds, the second approach will use a long-polling strategy.
The following code must be adapted to your needs and it's not tested, take it as a guide.
AJAX requests using an interval of 5 seconds
Create a view in your django app which will receive an order PK and return it's status in a json like
{
'pk': XX,
'status': ['processing_payment', 'paid', 'refused', ...]
}
(Adjust the status values on your own)
Python view
class OrderStatusView(View):
""" just return a json with the current status of the order """
def get(self, request, *args, **kwargs):
order_pk = kwargs.get('pk')
order = Order.objects.get(pk=order_pk)
ret_values = {'pk': order.pk, 'status': order.status}
return HttpResponse(json.dumps(ret_values))
The javascript part
var check_order_status = function (order_pk) {
$.ajax({
url: '/ajax/order/' + order_pk + '/status/',
dataType: 'json',
success: function (data) {
switch (data['status']) {
case 'processing_payment':
// just keep the spinner on
break;
case 'paid':
// Change the spinner to a green check or whatever you prefer
// Stop sending requests
clearInterval(order_status_interval);
break;
case 'refused':
// Change the spinner to a red cross or whatever fit better to your app
// Stop sending requests
clearInterval(order_status_interval);
break;
}
},
error: function (jqXHR, textStatus, errorThrown) {}
});
};
$(function () {
var order_status_interval = setInterval(function() { check_order_status(ORDER_PK); }, 5000);
});
As you can see we will send a request to the server every 5 seconds, if it returns an 'ending' status like paid or refused you should update your layout and not do further requests, otherwise, keep looking for a change.
Long-polling
Using a long-polling strategy will be similar but instead of sending a request to your backend every 5 seconds, your view should implement a sleep time and check for a status change keeping the thread with the client open, if a timeout is raised, the front-end must re-send the request again.
Your view shold look something like this
class OrderStatusView(View):
""" Wait until order status changes to paid or refused to long poll for a change """
def get(self, request, *args, **kwargs):
order_pk = kwargs.get('pk')
order = Order.objects.get(pk=order_pk)
while order.status not in ['paid', 'refused']:
time.sleep(5) # sleep for 5 seconds
order = Order.objects.get(pk=order_pk)
# order status is a final one
ret_values = {'pk': order.pk, 'status': order.status}
return HttpResponse(json.dumps(ret_values))
Your javascript code should control if a timeout is returned, in that case, do a new request.
var check_order_status = function (order_pk) {
$.ajax({
url: '/ajax/order/' + order_pk + '/status/',
dataType: 'json',
success: function (data) {
switch (data['status']) {
case 'processing_payment':
// just keep the spinner on
break;
case 'paid':
// Change the spinner to a green check or whatever you prefer
// Stop sending requests
clearInterval(order_status_interval);
break;
case 'refused':
// Change the spinner to a red cross or whatever fit better to your app
// Stop sending requests
clearInterval(order_status_interval);
break;
}
},
error: function (jqXHR, textStatus, errorThrown) {
switch (jqXHR.status) {
// timeout http codes
// 408 Request timeout
// 504 Gateway timeout
case 408:
case 504:
check_order_status(ORDER_PK);
break;
default:
// Throw some error
}
}
});
};
$(function () {
check_order_status(ORDER_PK);
});
Keep in mind that this approach can consume lot's of threads in high traffic conditions which can, at some point, affect your server performance.
WebSockets
The most over-head option will be to implement websockets using django-channels or centrifugo but I will not give it a try just for this.
THE SIMPLEST ONE
Since you don't know when your webhook will be called, consider to just send an email to your user notifying the payment result and with a CTA to the order details, I think that this is the easiest way to keep you user informed.
Hope this puts U on the right way

Django backend receives one less param than sent by frontend

I have a small web app with AngularJS front-end and Django ReST in the back. There's a strange hitch going on when I make POST request to the web service: the browser console clearly shows 3 parameters being sent, but the backend logging reports only 2 params received. The result is that the server throws a code 500 error due to a bad database lookup.
Here's the code:
Client
var b = newQuesForm.username.value;
$http.post('/myapp/questions/new', {username:b,title:q.title,description:q.description}).
success(function(data, status, headers, config) {
$http.get('/myapp/questions').success(function(data){
$scope.questions = data;
q = null;
$scope.newQuesForm.$setPristine();
}).error(function(data, status, headers, config) {
console.log(headers+data);
});
}).
error(function(data, status, headers, config) {
console.log(headers+data);
});
Both my manual logging and the dev console show a string like:
{"username":"admin","description":"What's your name?","title":"question 1"}
Server
class CreateQuestionSerializer(serializers.Serializer):
author = UserSerializer(required=False)
title = serializers.CharField(max_length=150)
description = serializers.CharField(max_length=350)
def create(self, data):
q= Question()
d = data
q.title = d.get('title')
q.description = d.get("description")
q.author = User.objects.get(username=d.get('username'))
q.save()
return q
Server-side logging shows the username parameter never succeeds in making the trip, and thus I end up with code 500 and error message:
User matching query does not exist. (No user with id=none)
What's causing some of the data to get lost?
So it turns out the problem was really with the serialization of fields, as #nikhiln began to point out. I followed his lead to refactor the code, moving the create() method to api.py, rather than serializers.py, and stopped relying altogether on the client-side data for the user's identity, something that was a bit silly in the first place (passing User to a hidden input in the view, and then harvesting the username from there and passing it back to the server in the AJAX params). Here's the new code, that works perfectly:
class QuestionCreate(generics.CreateAPIView):
model = Question
serializer_class = CreateQuestionSerializer
def create(self, request,*args,**kwargs):
q= Question()
d = request.data
q.title = d.get('title')
q.description = d.get("description")
q.author = request.user
q.save()
if q.pk:
return Response({'id':q.pk,'author':q.author.username}, status=status.HTTP_201_CREATED)
return Response({'error':'record not created'}, status=status.HTTP_400_BAD_REQUEST)
So here, I do it the right way: pull the User from the request param directly in the backend.

AngularJS/Django Post Response Data

I'm using AngularJS for the front-end and Django for the backend of a web app I'm working on. Right now I'm working on logging in users and I'm having a strange problem. Heres the relevant Angular code:
app.factory('AuthService', ["$http", "$q", "Session", "URL", function($http, $q, Session, URL) {
return {
login: function(credentials) {
var deferred = $q.defer();
$http.post(URL.login, credentials)
.then(function(data, status, headers, config) {
data=data.data; //WHY DOES THIS WORK?
if (data.success == true) {
alert("logged in");
Session.create(credentials.username, data.api_key);
deferred.resolve();
}
else {
deferred.reject("Login failed!");
}
}, function(data, status, headers, config) {
deferred.reject("Login failed!");
});
return deferred.promise
},
And here is the corresponding Django view:
def login_user(request):
'''
Given a username and password, returns the users API key.
'''
if request.method == 'POST':
username = request.POST.get('username',None)
password = request.POST.get('password',None)
user = authenticate(username=username,password=password)
if user is not None:
api_key = ApiKey.objects.get(user=user)
response_data = {}
response_data["api_key"] = str(api_key).split(" ")[0]
response_data["success"] = True
return HttpResponse(json.dumps(response_data), content_type="application/json")
else:
return HttpResponse(json.dumps({"username":username,"success":False}),content_type="application/json")
return HttpResponseBadRequest()
When the user logs in a POST request is sent and handled by the above Django code. The response is then picked up by the AngularJS code above. As you can see the then() method in the Angular code takes the usual four parameters: data, status, config and headers. I expect to see data contain the dictionary output from the Django code, appropriately serialized into a JSON object.
However what happens is that the only parameter of the then() method which is not undefined is data, and this contains EVERYTHING; headers, data, status code,etc.
The line commented 'WHY DOES THIS WORK' fixes the problem, by accessing the data inside. However, I want to know why this is happening and if there is any way to avoid this. My best guess is that it has something to do with the way Django serializes a response but I'm not sure.
I'm using Django 1.6.5.
That is actually how Angular promises work according to the docs. Here is the relevant quote.
Since the returned value of calling the $http function is a promise,
you can also use the then method to register callbacks, and these
callbacks will receive a single argument – an object representing the
response. See the API signature and type info below for more details.
The emphasis was mine.

How to output JSON from within Django and call it with jQuery from a cross domain?

For a bookmarklet project I'm trying to get JSON data using jQuery from my server (which is naturally on a different domain) running a Django powered system.
According to jQuery docs: "As of jQuery 1.2, you can load JSON data located on another domain if you specify a JSONP callback, which can be done like so: "myurl?callback=?". jQuery automatically replaces the ? with the correct method name to call, calling your specified callback." And for example I can test it successfully in my Firebug console using the following snippet:
$.getJSON("http://api.flickr.com/services/feeds/photos_public.gne?tags=cat&tagmode=any& format=json&jsoncallback=?",
function(data){
alert(data.title);
});
It prints the returned data in an alert window, e.g. 'Recent uploads tagged cat'. However when I try the similar code with my server I don't get anything at all:
$.getJSON("http://mydjango.yafz.org/randomTest?jsoncallback=?",
function(data){
alert(data.title);
});
There are no alert windows and the Firebug status bar says "Transferring data from mydjango.yafz.org..." and keeps on waiting. On the server side I have this:
def randomTest(request):
somelist = ['title', 'This is a constant result']
encoded = json.dumps(somelist)
response = HttpResponse(encoded, mimetype = "application/json")
return response
I also tried this without any success:
def randomTest(request):
if request.is_ajax() == True:
req = {}
req ['title'] = 'This is a constant result.'
response = json.dumps(req)
return HttpResponse(response, mimetype = "application/json")
So to cut a long story short: what is the suggested method of returning a piece of data from within a Django view and retrieve it using jQuery in a cross domain fashion? What are my mistakes above?
This seems to work (I forgot to process the callback parameter!):
Server-side Python / Django code:
def randomTest(request):
callback = request.GET.get('callback', '')
req = {}
req ['title'] = 'This is a constant result.'
response = json.dumps(req)
response = callback + '(' + response + ');'
return HttpResponse(response, mimetype="application/json")
Client-side jQuery code to retrieve this data:
$.getJSON("http://mydjango.yafz.org/polls/randomTest?callback=?",
function(data){
alert(data.title);
});
Is there a better way to achieve the same effect (more established way in terms of Python and Django coding)?
From Django 1.7 onwards, you can simply use JsonResponse.
>>> from django.http import JsonResponse
>>> response = JsonResponse({'foo': 'bar'})
>>> response.content
b'{"foo": "bar"}'