Django test - How to send a HTTP Post Multipart with JSON - python

in my django tests, I want to send an HTTP Post Multipart which contains 2 parameters:
a JSON string
a file
def test_upload_request(self):
temp_file = tempfile.NamedTemporaryFile(delete=False).name
with open(temp_file) as f:
file_form = {
"file": f
}
my_json = json.dumps({
"list": {
"name": "test name",
"description": "test description"
}
})
response = self.client.post(reverse('api:upload'),
my_json,
content=file_form,
content_type="application/json")
os.remove(temp_file)
def upload(request):
print request.FILES['file']
print json.loads(request.body)
My code doesn't work. Any help ?
If necessary, I can use an external python lib (I'm trying with requests)
Thank you

With application/json content-type, you cannot upload file.
Try following:
view:
def upload(request):
print request.FILES['file']
print json.loads(request.POST['json'])
return HttpResponse('OK')
test:
def test_upload_request(self):
with tempfile.NamedTemporaryFile() as f:
my_json = json.dumps({
"list": {
"name": "test name",
"description": "test description"
}
})
form = {
"file": f,
"json": my_json,
}
response = self.client.post(reverse('api:upload'),
data=form)
self.assertEqual(response.status_code, 200)

Related

Django: byte indices must be integers or slices, not str error

I am trying to get some data from a payload using a webhook, in the response that I sent along side I added the meta data that should return with the payload and this is how I did it (code below), if you take a close look at the keys, there I one called meta, now I am trying to get some of the data that is in the meta from my webhook view like this:
views.py webhook
#csrf_exempt
#require_http_methods(['POST', 'GET'])
def webhook(request):
payload = request.body
order_id = payload['meta']['order_id']
print(order_id)
return HttpResponse("testing...")
Sending the reponse to the payment gateway
#csrf_exempt
def process_payment(request, name, email, amount, order_id):
auth_token = "FLWSECK_TEST-9efb9ee17d8afe40c6c890294a1163de-X"
hed = {'Authorization': 'Bearer ' + auth_token}
data = {
"tx_ref":''+str(math.floor(1000000 + random.random()*9000000)),
"amount":amount,
"currency":"USD",
"order_id":order_id,
"redirect_url":f"http://localhost:3000/service-detail/booking/confirmation/{order_id}",
"payment_options":"card",
"meta":{
"order_id":order_id,
"consumer_id":23,
"consumer_mac":"92a3-912ba-1192a"
},
"customer":{
"email":email,
"name":name,
"order_id":order_id
},
"customizations":{
"title":"ForecastFaceoff",
"description":"Leading Political Betting Platform",
"logo":"https://i.im.ge/2022/08/03m/FELzix.stridearn-high-quality-logo-circle.jpg"
}
}
url = ' https://api.flutterwave.com/v3/payments'
response = requests.post(url, json=data, headers=hed)
response=response.json()
link=response['data']['link']
return redirect(link)
payload
{
"event": "charge.completed",
"data": {
"id": 4136234873,
"tx_ref": "6473247093",
"flw_ref": "FLW-MOCK-b33e86ab2342316fec664110e8eb842a3c2f956",
"device_fingerprint": "df38c8854324598c54e16feacc65348a5e446",
"amount": 152,
"currency": "USD",
"charged_amount": 152,
"app_fee": 5.78,
"merchant_fee": 0,
"processor_response": "Approved. Successful",
"auth_model": "VBVSECUREertrCODE",
"ip": "52.209.154.143",
"narration": "CARD Transaction ",
"status": "successful",
"payment_type": "card",
"created_at": "2023-02-06T11:19:45.000Z",
"account_id": 685622,
"customer": {
"id": 1970338,
"name": "destiny ",
"phone_number": null,
"email": "******#gmail.com",
"created_at": "2023-02-06T11:19:45.000Z"
},
"card": {
"first_6digits": "653188",
"last_4digits": "2340",
"issuer": "MASTERCARD CREDIT",
"country": "NG",
"type": "MASTERCARD",
"expiry": "49/32"
}
},
"event.type": "CARD_TRANSACTION"
}
The metadata is not even showing in the payload, must it show up there in the payload before I can grab the data that is sent with it?
request.body is byte not a dict, so you need to load the string as JSON.
payload = json.loads(request.body)
The meta data is not in the payload dictionary. At first try to extract the data key from the payload like this:
#csrf_exempt
#require_http_methods(['POST', 'GET'])
def webhook(request):
payload = json.loads(request.body.decode("utf-8"))
data = payload.get("data")
if data:
order_id = data.get("meta", {}).get("order_id")
print(order_id)
return HttpResponse("just testing")
Edit:
The error message
as you described above in comment JSONDecodeError: Expecting value: line 1 column 1 (char 0) indicates that json.loads() is expecting a JSON string as input.
It may be possible that request body is empty. You can check that by printing the request.body before calling json.loads().
Try this:
#csrf_exempt
#require_http_methods(['POST', 'GET'])
def webhook(request):
payload = json.loads(request.body.decode("utf-8"))
data = payload.get("data")
if data and "meta" in data:
order_id = data.get("meta").get("order_id")
print(order_id)
else:
print("Meta key not found in data or data not found in payload")
print(payload)
return HttpResponse("just testing")
It would tell you whether the "meta" is present in data dictionary or not and also prints payload dict at the end.

Microsoft Graph API how to send an attachment in a chat

I want to use the Microsoft Graph API to send messages with attachments to chats or channels.
https://learn.microsoft.com/en-us/graph/api/chatmessage-post?view=graph-rest-1.0&tabs=http#example-4-send-a-message-with-file-attachment-in-it
I can send normal messages already just fine like this:
def post_message(chat_id: str, subject: str = "", content_type: str = "text", content: str = "") -> None:
url = f"https://graph.microsoft.com/v1.0/chats/{chat_id}/messages"
json = {
"subject": subject,
"body": {
"contentType": content_type,
"content": content
}
}
res = requests.post(url, headers=header, json=json)
I try to copy the body from the example in the link above, substitute for my values and swap json variable for this one:
attachment_id = '7QW90B10D7-B5AK-420A-AC78-1156324A54F2' # not real, only to show how it looks like
json = {
"body": {
"contentType": 'html',
"content": f'i dunno what i\'m doing. <attachment id="{attachment_id}"></attachment>'
},
'attachments': [
{
'id': attachment_id,
'contentType': 'reference',
'contentUrl': 'https://foo.sharepoint.com/sites/bar/User%20documentation/Databricks/Databricks%20guide.pptx',
'name': 'Databricks guide.pptx'
}
]
}
I get requests.exceptions.HTTPError: 400 Client Error: Bad Request for url
What's wrong with the code? How to get attachment id from a file correctly because I am not sure I got the right value?
I was able to get it working with your code, (using both formats <attachment id=\"\"> and <attachment id="">), so it seems the error is probably with your attachment_id.
I retrieved the driveItem ID by following this answer, where the driveItem ID is the GUID value in the eTag property in the response.
You could get the file by the path:
https://graph.microsoft.com/v1.0/sites/{site-id}/drive/root:/{item-path}
For example:
https://graph.microsoft.com/v1.0/sites/{site-id}/drive/root:/test.docx
If the file is in a folder, it would be like this:
https://graph.microsoft.com/v1.0/sites/{site-id}/drive/root:/folder1/test.docx
Sample request after obtaining the ID
access_token = ""
attachment_name = "file.txt"
attachment_path = "https://domain.sharepoint.com/Shared%20Documents"
attachment_id = "12345678-1234-1234-1234-123456789123"
attachment_url = f"{attachment_path}/{attachment_name}"
chat_id = ""
req_url = f"https://graph.microsoft.com/v1.0/chats/{chat_id}/messages"
req_headers = {
"Authorization": "Bearer " + access_token,
"Content-Type": "application/json"
}
json = {
"body": {
"contentType": "html",
"content": f"Test message <attachment id=\"{attachment_id}\"></attachment>"
},
"attachments": [
{
"id": attachment_id,
"contentType": "reference",
"contentUrl": attachment_url,
"name": attachment_name
}
]
}
result = requests.post(url = req_url, headers = req_headers, json = json)

AssertionError: 302 != 200 : Couldn't retrieve redirection page '/api/v2/app/nextdialog': response code was 302 (expected 200)

AssertionError: 302 != 200 : Couldn't retrieve redirection page '/api/v2/app/nextdialog': response code was 302 (expected 200)
In Django is it possible to have three views where each one redirects to the next. View 1 redirects to view 2 and view 2 redirects to view 3?
Views.py:
class ConversationLayerView(generics.GenericAPIView):
permission_classes = (permissions.IsAuthenticated,)
def post(self, request, *args, **kwargs):
body = request.data
cardType = body["cardType"]
if cardType == "CHOICE":
serializer = appChoiceTypeSerializer(body)
elif cardType == "TEXT":
serializer = appTextTypeSerializer(body)
elif cardType == "INPUT":
serializer = appInputTypeSerializer(body)
else:
serializer = StartAssessmentSerializer(body)
validated_body = serializer.validate_value(body)
url = get_endpoint(validated_body)
reverse_url = encodeurl(body, url)
return HttpResponseRedirect(reverse_url)
class NextDialog(generics.GenericAPIView):
permission_classes = (permissions.AllowAny,)
def get(self, request, *args, **kwargs):
result = request.GET
data = result.dict()
contact_uuid = data["contact_uuid"]
step = data["step"]
value = data["value"]
description = data["description"]
title = data["title"]
if data["cardType"] == "CHOICE":
optionId = int(value) - 1
else:
optionId = data["optionId"]
path = get_path(data)
assessment_id = path.split("/")[-3]
request = build_rp_request(data)
app_response = post_to_app(request, path)
if data["cardType"] != "TEXT":
if data["cardType"] == "INPUT":
optionId = None
store_url_entry = appAssessments(
contact_id=contact_uuid,
assessment_id=assessment_id,
title=title,
description=description,
step=step,
user_input=value,
optionId=optionId,
)
store_url_entry.save()
pdf = pdf_ready(app_response)
if pdf:
response = pdf_endpoint(app_response)
return HttpResponseRedirect(response)
else:
message = format_message(app_response)
return Response(message, status=status.HTTP_200_OK)
class Reports(generics.GenericAPIView):
permission_classes = (permissions.AllowAny,)
def get(self, request, *args, **kwargs):
report_id = request.GET.get("report_id")
response = get_report(report_id)
return Response(response, status=status.HTTP_200_OK)
Utils.py
def get_endpoint(payload):
value = payload["value"]
if value != "":
if value == "back":
url = reverse("app-previous-dialog")
elif value == "abort":
url = reverse("app-abort")
else:
url = reverse("app-next-dialog")
elif value == "":
url = reverse("app-start-assessment")
return url
def encodeurl(payload, url):
qs = "?" + urlencode(payload, safe="")
reverse_url = url + qs
return reverse_url
Code explanation:
The app receives a json payload in the ConversationLayerView. It calls the endpoint method to know which endpoint to redirect to based on the 'value' key in the payload is set to.
The request looks something like this:
{
"contact_uuid": "67460e74-02e3-11e8-b443-00163e990bdb",
"choices": null,
"value": "Charles",
"cardType": "INPUT",
"step": null,
"optionId": null,
"path": "",
"title": "",
"description": "What's your name",
"message": ""
}
Since "value" is "Charles", the view redirects to "app-next-dialog" in the NextDialog view.
In this view a POST request is done to an external application and a json response is received. If the response has a PDF key this view redirects to the third view.
THis happens here:
if pdf:
response = pdf_endpoint(app_response)
return HttpResponseRedirect(response)
else:
message = format_message(app_response)
return Response(message, status=status.HTTP_200_OK)
If the key is present then redirect to "response" whose output is "/api/v2/app/reports?report_id=/reports/17340f51604cb35bd2c6b7b9b16f3aec" otherwise do not redirect but return a 200.
Error:
AssertionError: 302 != 200 : Couldn't retrieve redirection page '/api/v2/app/nextdialog': response code was 302 (expected 200)
Url.py
path(
"api/v2/app/assessments",
views.PresentationLayerView.as_view(),
name="app-assessments",
),
path("api/v2/app/nextdialog", views.NextDialog.as_view(), name="app-next-dialog"),
path("api/v2/app/reports", views.Reports.as_view(), name="app-reports"),
test_views.py:
class appAssessmentReport(APITestCase):
data = {
"contact_uuid": "67460e74-02e3-11e8-b443-00163e990bdd",
"choices": None,
"message": (
"How old are you?\n\nReply *back* to go to "
"the previous question or *abort* to "
"end the assessment"
),
"explanations": "",
"step": 39,
"value": "27",
"optionId": None,
"path": "/assessments/f9d4be32-78fa-48e0-b9a3-e12e305e73ce/dialog/next",
"cardType": "INPUT",
"title": "Patient Information",
"description": "How old are you?",
}
start_url = reverse("app-assessments")
next_dialog_url = reverse("app-next-dialog")
pdf_url = reverse("app-reports")
destination_url = (
"/api/v2/app/nextdialog?contact_uuid="
"67460e74-02e3-11e8-b443-00163e990bdd&"
"choices=None&message=How+old+are+you%3F"
"%0A%0AReply+%2Aback%2A+to+go+to+the+"
"previous+question+or+%2Aabort%2A+to+end"
"+the+assessment&explanations=&step="
"39&value=27&optionId=None&path=%2F"
"assessments%2Ff9d4be32-78fa-48e0-b9a3"
"-e12e305e73ce%2Fdialog%2Fnext&cardType"
"=INPUT&title=Patient+Information&"
"description=How+old+are+you%3F"
)
#patch("app.views.pdf_ready")
#patch("app.views.post_to_app")
def test_assessment_report(self, mock_post_to_app, mock_pdf_ready):
mock_post_to_app.return_value = {
"cardType": "CHOICE",
"step": 40,
"title": {"en-US": "YOUR REPORT"},
"description": {"en-US": ""},
"options": [
{"optionId": 0, "text": {"en-US": "Option 1"}},
{"optionId": 1, "text": {"en-US": "Option 2"}},
{"optionId": 2, "text": {"en-US": "Option 3"}},
],
"_links": {
"self": {
"method": "GET",
"href": "/assessments/898d915e-229f-48f2-9b98-cfd760ba8965",
},
"report": {
"method": "GET",
"href": "/reports/17340f51604cb35bd2c6b7b9b16f3aec",
},
},
}
mock_pdf_ready.return_value = utils.pdf_ready(mock_post_to_app.return_value)
user = get_user_model().objects.create_user("test")
self.client.force_authenticate(user)
response = self.client.post(self.start_url, self.data, format="json")
print(response)
self.assertRedirects(response, self.destination_url)
So far this isn't working. In summary I'm just trying to start in view 1, redirect to view 2 and redirect to view 3 from view 2.
Is this possible in Django?
Thanks.
The problem is with your tests, rather than with the view logic - it's perfectly possible to have a chain of redirects.
assertRedirects checks the status of the response, and by default requires it to be a HTTP 200 response. Because you have a chain of redirects, the response is 302 (another redirect) instead of 200, which is why the test fails with the error "response code was 302 (expected 200)".
You need to modify the target_status_code argument to tell assertRedirects that you are expecting the response to have a 302 status code:
self.assertRedirects(response, self.destination_url, target_status_code=302)

openstack cannot retrieve authentication token from API

I am trying to retrieve the authentication token from the API using requests library from python. Here is the attempt I have made so far:
def get_token():
data = {
"auth" : {
"identity" : {
"methods" : [ "password" ],
"password": {
"user" : {
"name" : OS_USERNAME,
"domain": { "name": "Default" },
"password": OS_PASSWORD
}
}
}
}
}
r = requests.post(
OS_AUTH_URL+'/auth/tokens',
headers = HEADERS,
json = data, # https://stackoverflow.com/questions/9733638
verify = False
)
print(r.content)
j = json.loads(r.content)
return j['token']['user']['id']
I get token in the response :
{
"token": {
"issued_at": "2018-07-03T11:03:59.000000Z",
"audit_ids": [
"Fg1ywtZBQ1CkigCw70If9g"
],
"methods": [
"password"
],
"expires_at": "2018-07-03T12:03:59.000000Z",
"user": {
"password_expires_at": null,
"domain": {
"id": "default",
"name": "Default"
},
"id": "e0dc5beb383a46b98dad824c5d76e719",
"name": "admin"
}
}
}
However, when I am reusing this token to get, for instance, the list of projects :
def get_tenantID():
r = requests.get(
OS_AUTH_URL+'/auth/projects',
headers = HEADERS,
verify = False
)
return r
r = get_token()
HEADERS['X-Auth-Project-Id'] = 'admin'
HEADERS['X-Auth-Token'] = r
r = get_tenantID()
I get this error as if I would not be authenticated:
<Response [401]>
{"error": {"message": "The request you have made requires authentication.", "code": 401, "title": "Unauthorized"}}
Googling around and using openstack token issue command showed me that usually, token are more like:
gAAAAABaCo1F1CIMVlsTBeuqYH8tm2qR29tbkmUL4vZuhCNPXJI39TQ2YzL6Twoj8fNcAyLe3WhCYW2O1YpbBF0G8mo4bt7Kf0IRsoDOoJ6uWa3RYyJ5SQNoB_5n8EnVXbKPxFYOZ_iFBnaVtL1_XDrGbwsrlDeyy8lZTDdLsqY52pUhFR-7Uow
which is not what I get with get_token.
What am I doing wrong?
Many thanks!
When you use the auth API directly, the token issued comes in the X-Subject-Token header.
Thus, to retrieve in your python example, you could access the response.headers dict like this:
token = r.headers['X-Subject-Token']
More info about authentication in the Keystone v3 docs
1. fetch authentication token as mentioned below:
r = requests.post(
OS_AUTH_URL+'/auth/tokens',
headers = HEADERS,
json = data,
verify = False
)
token = r.headers[X-Subject-Token]
2. Pass this token in header for further request:
{
'X-Auth-Token': token
}

Jasper server rest_v2 api using requests in python

I m using jasperserver rest_v2 web service. I am using requests module in python to hit the api. The report which is on jasper server is executing and
report url is following:
http://localhost:8080/jasperserver/rest_v2/reportExecutions
and I m gettng a json response like that:
{
"status": "ready",
"totalPages": 0,
"requestId": "d0ae905c-1538-4e40-b5ad-c145f521707c",
"reportURI": "/reports/Invoices/COD",
"exports": [
{
"id": "49f47112-4698-4398-9ed8-a62d552a7aa5",
"status": "ready",
"outputResource": {
"contentType": "application/pdf",
"fileName": "COD.pdf",
"outputFinal": true
}
}
]
}
It means my report was executed.
Then I hit the following url to download the report output.
http://localhost:8080/jasperserver/rest_v2/reportExecutions/d0ae90c-1538-4e40-b5ad-c145f521707c/exports/49f47112-4698-4398-9ed8-a62d552a7aa5/outputResource
It gives me the following response with 404 status code:
{
"message": "Resource d0ae95c-1538-4e40-b5ad-c145f521707c not found.",
"errorCode": "resource.not.found",
"parameters": [
"d0ae95c-1538-4e40-b5ad-c145f521707c"
]
}
then I checked my report status using the following url:
http://localhost:8080/jasperserver/rest_v2/reportExecutions/d0ae905c-1538-4e40-b5ad-c145f521707c
I m getting a response 404 and following json:
{
"message": "Resource d0ae905c-1538-4e40-b5ad-c145f521707c not found.",
"errorCode": "resource.not.found",
"parameters": [
"d0ae905c-1538-4e40-b5ad-c145f521707c"
]
}
And the exact same thing is working fine using Postman.
NOTE: I m passing the valid credentials and appropriate headers.
Below is my django view for this:
class JasperView(View):
template = "report.html"
def get_token(self):
username = "zeeshan"
password = "zeeshan"
token = base64.encodestring('%s:%s' % (username, password)).replace('\n', '')
return token
def get(self, request, *args, **kwargs):
return render(request, self.template)
def post(self, request, *args, **kwargs):
token = self.get_token()
data = """
<reportExecutionRequest>
<reportUnitUri>/reports/Invoices/COD</reportUnitUri>
<async>false</async>
<freshData>false</freshData>
<saveDataSnapshot>false</saveDataSnapshot>
<outputFormat>pdf</outputFormat>
<interactive>true</interactive>
<ignorePagination>false</ignorePagination>
<parameters>
<reportParameter name="StartDate">
<value>2016-06-08 00:00:00</value>
</reportParameter>
<reportParameter name="EndDate">
<value>2016-07-01 00:00:00</value>
</reportParameter>
<reportParameter name="ReportID">
<value>COD2345</value>
</reportParameter>
<reportParameter name="client">
<value>Fetchr</value>
</reportParameter>
<reportParameter name="manager">
<value>Zeeshan Asgar</value>
</reportParameter>
<reportParameter name="client_add">
<value>Near Bhagat Singh Park, Malviya Nagar, New Delhi-110017</value>
</reportParameter>
</parameters>
</reportExecutionRequest>
"""
response = requests.post(url="http://localhost:8080/jasperserver/rest_v2/reportExecutions",
headers={
"Authorization": "Basic " + token,
"Accept": "application/json",
"Content-Type": "application/xml",
},
data=data)
data = response.json()
request_id = data.get("requestId")
export_id = data.get("exports")[0].get("id")
report_url = "http://localhost:8080/jasperserver/rest_v2/reportExecutions/{request_id}/exports/{export_id}/outputResource".format(
request_id=request_id, export_id=export_id)
report_resp = requests.get(url=report_url,
headers={"Authorization": "Basic " + token, "Content-Type": "application/xml"})
response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename="COD.pdf"'
response.write(report_resp)
return response
Help.
Thanks in advance.

Categories