Generating invoice in django with Shiprocket API - django

I'm trying to generate invoice of my order in django with shiprocket API. So far I'm getting success Response but the generated URL of PDF is somewhat not understandable.
#Response Im getting
"{\"is_invoice_created\":true,
\"invoice_url\":\"https:\/\/s3-ap-south-1.amazonaws.com\/kr-shipmultichannel-mum\/3116291\/invoices\/Retail000058ba9b309-c4bd-4c06-ba15-2cc76b7c5d1f.pdf\",
\"not_created\":[],\"irn_no\":\"\"}"
when I try to access above URL get - Access-Denied
This XML file does not appear to have any style information associated with it. The document tree is shown below.
<Error>
<Code>AccessDenied</Code>
<Message>Access Denied</Message>
<RequestId>94ARWRYHGSBEZEH1</RequestId>
<HostId>j32LvksmWatylm3TdFGYuJ1qvBwJ39Lj+qIbbQ3CC7AEcrgHnOYGd9fDocMeEwDD4GpzjXQLQhg=</HostId>
</Error>
This is the way I want the url to be
https://s3-ap-south-1.amazonaws.com/kr-shipmultichannel-mum/3116291/invoices/Retail000058ba9b309-c4bd-4c06-ba15-2cc76b7c5d1f.pdf
When I remove the slashed I get in Response I'm able to download the PDF and that's done manually. So how do I get working URL.
#Views.py
class GenerateInvoice(APIView):
def post(self, request):
order_id = request.query_params.get("ids", None)
print(order_id)
data = json.dumps({"ids": [order_id]})
headers = {
"Content-Type": "application/json",
"Authorization": settings.SHIPROCKET_TOKEN,
}
url = "https://apiv2.shiprocket.in/v1/external/orders/print/invoice"
response = requests.post(url=url, data=data, headers=headers)
return Response(response)

I think the result is response object. Try adding these:
response = json.loads(response.decode("utf-8"))

Related

DRF: making my gateway forward incoming request with InMemoryUploadedFile to next service

What is the correct way to just route a front-end request (that used to work) through a gateway to a service (that used to work). (Even now, if I send the front-end request strait to the service everything still works, so it it just my gateway view that is not handling and forwarding the request properly)
My front-end used to post images with user posts to my back-end just fine. But I have implemented a gateway and now the only thing I am still struggling with is just forwarding a file that the user posts to the required service:
All my gateways just json.dumps() the incoming request data and moves on, but with the image I get an error along the lines of: TypeError: Object of type InMemoryUploadedFile is not JSON serializable - which I perfectly understand, but what is the correct way to send the image along to the service?
My gateway view:
class DailyReportImages(APIView):
def post(self, request, *args, **kwargs):
url = INSERT_DAILY_REPORT_IMAGE
res = requests.post(url, headers=request.headers, data=json.dumps(request.data), files=request.files)
return Response(res.json(), res.status_code)
This is what I have for now after days of searching, and at least it reaches the backend now, but it still gives almost every error in the book depending on how I tweak it...
res = requests.post(url, headers=request.headers, data={"name": "test", "report": "myreport"}, files={'Screenshot from 2020-01-06 19-04-33.png': request.FILES["image"]})
For instance service complains that the name/report values are empty...
TL;DR
a minimal verifiable example is here,
import requests
from rest_framework.response import Response
from rest_framework.views import APIView
class DailyReportImages(APIView):
def post(self, request, *args, **kwargs):
url = 'http://httpbin.org/post'
cleaned_headers = {
header_name: header_value for header_name, header_value in request.META.items() if
type(header_value) == str
}
if cleaned_headers.get('CONTENT_TYPE') == 'application/json':
res = requests.post(url, json=request.data, headers=cleaned_headers)
else:
# we must send as `multipart/form-data`
data_if_any = request.POST.dict()
files_data_if_any = request.FILES.dict()
composed_data = {**{key: (None, value) for key, value in data_if_any.items()}, **files_data_if_any}
res = requests.post(url, files=composed_data, headers=cleaned_headers)
return Response(res.json(), res.status_code)
Why the current code does not work?
The request.data -- (DRF doc) may contains the uploaded files which is a InMemoryUploadedFile type and is not JSON Serializable.
Note: The OP's code snippet will not raise a "TypeError: Object of type InMemoryUploadedFile is not JSON serializable" error if there is no file object in the payload/request-data
References
You can use the json parameter of requests library to send dict like object
How to send multipart/form-data using requests module?
How to convert QueryDict (or MultiValueDict) to a dict object?
How can I get all the request headers in Django?

Django: call REST API through a view instead of Postman

I have a working python code on my desktop that prints and makes PDFs perfectly. All I want to do is use that code and use Django to allow users to enter a value.
My code uses docusign API to call data. I use postman which needs a key and other parameters to use the API. The value entered by my user will determine what data they get.
What I think I have to do is rewrite my code, put it somewhere, then turn it into a view. The view will be sent to template.
Edit -
My code:
# Get Envelope Data- use account ID from above
# Get Todays Date, Daily Setting
day = datetime.datetime.today().strftime('%Y-%m-%d')
url = "https://demo.docusign.net/restapi/v2/accounts/" + accountId + "/envelopes"
# if Envelope is completed
querystring = {"from_date": Date, "status": "completed"}
headers = {
'X-DocuSign-Authentication': "{\"Username\":\""+ email +"\",\"Password\":\""+Password+"\",\"IntegratorKey\": \""+IntegratorKey+"\"}",
'Content-Type': "application/json",
'Cache-Control': "no-cache",
'Postman-Token': "e53ceaba-512d-467b-9f95-1b89f6f65211"
}
response = requests.request("GET", url, headers=headers, params=querystring)
envelopes = response.text
Sorry, let me try again. I currently have a python3 program on my desktop. I run it with idle and everything is how I want it.
What I want to do with Django is use this code to print its outputs on a webpage and have the user download it’s additional csv file output. I have managed to make a Django localhost and I am stuck at that point. I do not know how to use my python3 code to run to webpage.
The code is made up of API calls, I use postman to help me with sending the right parameters. I will add a picture of code. All I want is for user to enter value such as accountID so that the API can complete the request and give them data for their own request.
I'll try to give you a overview of how this could work with Django.
You could have a form to obtain the users account_id.
class AccountForm(forms.Form):
account_id = forms.IntegerField()
You could display this form through a generic FormView (see also this):
class AccountView(views.FormView):
form_class = AccountForm
template_name = 'account.html'
def form_valid(self, form):
# here you make your request to the external API
account_id = form.cleaned_data['account_id']
url = "https://demo.docusign.net/restapi/v2/accounts/" + account_id + "/envelopes"
headers = ...
querystring = ...
resp = requests.request("GET", url, headers=headers, params=querystring)
ctx = {
'result': resp.text,
}
return render(self.request, 'result.html', ctx)
I don't show the template account.html here. You will have to figure that one out yourself; the links I provided should point you in the right direction.
Now, what remains to be determined is what exactly the method form_valid should return. The code I showed renders a template with the API call response in the context, so in your template result.html you could display the result data any way you like.
You mentioned downloading a CSV file as well. That could be a different view, probably triggered by a link or button in result.html.

Django comments CSRF error

Getting a csrf error I cant figure out how to fix, i have rest auth working, user is able to update their details like so:
but with Django Comments i get this csrf error using the same csrf token Error:
I would like to get rid of this error on the /comments/post/ endpoint, such that this endpoint behaves similar to /rest-auth/user/ view which accepts an "Authorization: Token 792b5fb27b4fe805e895c91274f26b6ab13cb654" header field to relevant provide data to the authenticated user.
The following is an exert of the csrf related decotaros on the respective views shown in the screen shots:
From the /comments/post/ endpoint
#csrf_protect
#require_POST
def post_comment(request, next=None, using=None):
# Fill out some initial data fields from an authenticated user, if present
data = request.POST.copy()
if request.user.is_authenticated():
if not data.get('name', ''):
data["name"] = request.user.get_full_name() or request.user.get_username()
if not data.get('email', ''):
data["email"] = request.user.email
From the /rest-auth/user/ endpoint
#api_view(['GET'])
#permission_classes((IsAuthenticated, ))
def get_user(request, **kwargs):
pk = request.data['pk']
user = MyUser.objects.get(pk=pk)
serializers = UsersSerializer(user)
return Response(serializers.data)
You're using the wrong content type. Please change it into application/json and try again.
The decorators for your endpoints are different, thus you need to adjust the headers accordingly.
For your /rest-auth/ view the WWW-Authenticate header is required as mentioned here.
The comments view /comments/ endpoint has the csrf_protect decorators which means that the header must match the csrf-token returned in the cookie,as Fede mentions in your header you only require 'X-CSRFToken' with the matching value from the cookie.
I think you are using django-rest-framework which comes with the csfr token exempt by default, but postman is sending a csfr token that is why you are getting that error.
cleaning the cookies might solve the problem.

how to specify tastypie always return json data

my resource code is
class MessageResource(ModelResource):
class Meta:
queryset = Message.objects.all()
resource_name = "message"
always_return_data = True
authentication = ApiKeyAuthentication()
authorization = Authorization()
def determine_format(self, request):
return "application/json"
If I get the message resource without any authentication info, the response is
401 UNAUTHORIZED
"Content-Type" = "text/html; charset=utf-8";
Date = "Mon, 19 Nov 2012 15:36:28 GMT";
Server = "WSGIServer/0.1 Python/2.7.2";
instead of html, I want to get the JSON response. Actually, I want to know how to specify tastypie only send back JSON response in any cases?
I want to get the JSON response like
{ "code": "401", "message": "Unauthorized" }
Is there any way to implement it?
This is valid HTTP response. There is no JSON 401 response specified nowhere and there is no need for tastypie to come up with any default one.
If you have such need, simply wrap your API with proper middleware.
Update: How to setup middleware
Fisrt, read through middleware documentation.
Then, I think you will need to implement process_response method on your new middleware class, and do whatever you need to do with response depending on its status code.
Don't forget to add your middleware class to settings.MIDDLEWARE_CLASSES.
Or you could simply in your settings file do TASTYPIE_DEFAULT_FORMATS = ['json'].

How to receive POST data in django

I've been trying to integrate a payment gateway into my site in django.
I'm having trouble getting the response data from the payment gateway.
The payment gateway has sample docs for php which looks like this :
$ErrorTx = isset($_POST['Error']) ? $_POST['Error'] : ''; //Error Number
$ErrorResult = isset($_POST['ErrorText']) ? $_POST['ErrorText'] : ''; //Error message
$payID = isset($_POST['paymentid']) ? $_POST['paymentid'] : ''; //Payment Id
In the view for the url that the payment gateway is redirecting to after entering card details etc, I'm checking if it's a GET if request.method == "GET" and then passing the request to a function. When I debug the request, I can see an empty query dict. and if I try something like res = request.GET['paymentid'] I get an error that says there's no key called paymentid.
Am I missing something obvious? I'm still pretty new to django, so I'm sure I'm doing something wrong.
res = request.GET['paymentid'] will raise a KeyError if paymentid is not in the GET data.
Your sample php code checks to see if paymentid is in the POST data, and sets $payID to '' otherwise:
$payID = isset($_POST['paymentid']) ? $_POST['paymentid'] : ''
The equivalent in python is to use the get() method with a default argument:
payment_id = request.POST.get('payment_id', '')
while debugging, this is what I see in the response.GET: <QueryDict: {}>, request.POST: <QueryDict: {}>
It looks as if the problem is not accessing the POST data, but that there is no POST data. How are you are debugging? Are you using your browser, or is it the payment gateway accessing your page? It would be helpful if you shared your view.
Once you are managing to submit some post data to your page, it shouldn't be too tricky to convert the sample php to python.
You should have access to the POST dictionary on the request object.
for class based views, try this:
class YourApiView(generics.ListAPIView):
"""
API endpoint
"""
def post(self, request, *args, **kwargs):
print("request data")
print(request.data)