Why is django test client throwing away my extra headers - python-3.x

I'm trying to test a view that makes use of some headers. In my test code I have something like this:
headers = {'X-Github-Event': 'pull_request'}
body = {useful stuff}
url = reverse(my_view)
I've tried making requests to my view using all possible combinations of the following clients and post calls:
client = Client(extra=headers)
client = APIClient(headers=headers)
client = APIClient(extra=headers)
response = client.post(url, data=body, format="json", headers=headers)
response = client.post(url, data=body, format="json", extra=headers)
My view effectively looks like this:
#api_view(["POST", "GET"])
def github_webhook(request):
print(request.headers)
My X-Github-Event header is never printed out by my view when it is called from my test code.
If I run runserver and send a request to that endpoint then the headers work perfectlty fine. It's just the test code that is broken.
What am I missing here? How can I set the headers for my tests?

I think that the following snippet will help you:
import json
from django.test import TestCase
from rest_framework.test import APIClient
class FooTestCase(TestCase):
def setUpTestData(cls):
cls.client = APIClient(ACCEPT='application/json')
def test_foo(self):
headers = {"ACCEPT": "application/json", 'HTTP_X_GITHUB_EVENT': 'pull_request'}
url = reverse(my_view)
payload = json.dumps(body)
response = self.client.post(url, data=payload, content_type='application/json', **headers)

Related

Urllib3 POST request with attachment in python

I wish to make a post request to add an attachment utilising urllib3 in python without success. I have confirmed the API itself is working in postman but cannot work out how to convert this request to python. Appreciating I'm mixing object types I just don't know how to avoid it.
Python code:
import urllib3
import json
api_key = "secret_key"
header = {"X-API-KEY": api_key, "ACCEPT": "application/json", "content-type": "multipart/form-data"}
url = "https://secret_url.com/api/"
http = urllib3.PoolManager()
with open("invoice.html", 'rb') as f:
file_data = f.read()
payload = {
"attchment": {
"file": file_data
}
}
payload = json.dumps(payload)
r = http.request('post', url, headers = header, fields = payload)
print(r.status)
print(r.data)
Postman - which works and properly sends file-name through also (I'm guessing it splits the bytes and filename up?)
Edit: I've also tried the requests library as I'm more familiar with this (but can't use it as the script will be running in AWS lambda). Removing the attachment element form the dict allows it to run but the API endpoint gives 401 presumably because it's missing the "attachement" part to the data structure as per postman below... but when I put this in I get runtime errors.
r = requests.post(url, headers = header, files={"file": open("invoice.html", 'rb')})
For anyone who stumbles upon this from Dr google a few points:
I was completely mis-interpreting the structure of the element. It's actually a string "attachment[file]" not a dict like object.
Postman has the ability to output python code in urllib/request syntax albeit not 100% what I was after. Note: the chrome version (depreciated) outputs gibberish code that only half works so the client version should be used. A short bit of work below shows it working as expected:
http = urllib3.PoolManager()
with open("invoice.html", "rb") as f:
file = f.read()
payload={
'attachment[file]':('invoice.html',file,'text/html')
}
r = http.request('post', url, headers = header, fields = payload)

Unable to Access API

I am unable to access an API.
This is what I am doing:
import os, re
import requests
import logger
from requests_oauthlib import OAuth1
oauth_consumer_key = "123abc"
oauth_secret = "aaabbbccc"
url = "https://example_testing/v1/<ID_VALUE>/reports?"
oauth = OAuth1(oauth_consumer_key, oauth_secret)
output = requests.get(url,auth=oauth)
I keep getting a 500 error.
What am I doing wrong?
I didn't know this but apparently I needed to set values for headers and the payload. This solved my problem.
headers = {"Content-Type": "application/json"}
payload = json.dumps({
"report_format":"csv",
<other stuff>
}
output = requests.get(url=url, auth=oauth, headers=headers, data=payload)
Problem Solved

Can't send proper post request with file using python3 requests

I was using Postman to send post request like on the screenshot
Now I need to implement it in python. This is what i've got for now:
import requests
data = {"sendRequest": {"apiKey": 12345, "field1": "field1value"}}
files = {"attachment": ("file.txt", open("file.txt", "rb"))}
headers = {"Content-type": "multipart/form-data"}
response = requests.post(endpoint, data=data, headers=headers, files=files)
But still it's not working - server doesn't accept it as valid request. I've tried more combinations but without any results and I really couldn't find a solution.
I need this request to be exactly like that one in postman
I finally found a solution. I used MultipartEncoder from requests_toolbelt library.
from requests_toolbelt import MultipartEncoder
import requests
import json
data = {"apiKey": 12345, "field1": "field1value"}}
mp = MultipartEncoder(
fields={
'sendRequest': json.dumps(data), # it is important to convert dict into json
'attachment': ('file.pdf', open('file.pdf', 'rb'), 'multipart/form-data'),
}
)
r = requests.post(ENDPOINT, data=mp, headers={'Content-Type': mp.content_type})

MissingSchema - Invalid URL Error with Requests module

Trying to get data from the eBay API using GetItem. But requests isn't reading or getting the URL properly, any ideas? Getting this error:
requests.exceptions.MissingSchema: Invalid URL '<urllib.request.Request object at 0x00E86210>': No schema supplied. Perhaps you meant http://<urllib.request.Request object at 0x00E86210>?
I swear I had this code working before but now it's not, so I'm not sure why.
My code:
from urllib import request as urllib
import requests
url = 'https://api.ebay.com/ws/api.dll'
data = '''<?xml version="1.0" encoding="utf-8"?>
<GetItemRequest xmlns="urn:ebay:apis:eBLBaseComponents">
<RequesterCredentials>
<eBayAuthToken>my-auth-token</eBayAuthToken>
</RequesterCredentials>
<ItemID>any-item-id</ItemID>
</GetItemRequest>'''
headers = {
'Content-Type' : 'text/xml',
'X-EBAY-API-COMPATIBILITY-LEVEL' : 719,
'X-EBAY-API-DEV-NAME' : 'dev-name',
'X-EBAY-API-APP-NAME' : 'app-name',
'X-EBAY-API-CERT-NAME' : 'cert-name',
'X-EBAY-API-SITEID' : 3,
'X-EBAY-API-CALL-NAME' : 'GetItem',
}
req = urllib.Request(url, data, headers)
resp = requests.get(req)
content = resp.read()
print(content)
Thank you in advance. Any good reading material for urllib would be great, too.
You are mixing the urllib and the requests library. They are different libraries that can both do HTTP-requests in Python. I'd suggest you use only the requests library.
Remove the line req = urllib.Request(url, data, headers) and replace the resp = ... line with:
r = requests.post(url, data=data, headers=headers)
Print the response body like this:
print(r.text)
Check out the Requests Quickstart here for more examples: https://2.python-requests.org//en/master/user/quickstart/

Flask's test_client() is not passing custom HTTP Headers to the Flask app

I have something like this in my test script:
def setUp(self):
app = create_app()
self.app = app.test_client()
def test_001(self):
with self.app as app:
headers = { 'API-KEY': 'myKey' }
app.get('/endpoint1', follow_redirects=True,headers=headers)
Reading through the print statements from my application, I can see that my application endpoint is called, and things look normal except for the header missing from the request.
In my API, I have this print statement:
log("Headers: " + str(request.headers))
This output the following messages in the console:
Headers: User-Agent: werkzeug/0.14.1
Host: localhost
Content-Length: 0
So apparently, the client does send some headers, but not the custom one I added.
Does anyone see what I'm doing wrong, that causes the headers either not to be sent in the first place, or them not being accessible to the server?
def setUp(self):
self.app = create_app()
self.app.config['TESTING'] = True
self.app_context = self.app.app_context()
self.app_context.push()
self.client = self.app.test_client()
def test_001(self):
headers = { 'API-KEY': 'myKey' }
response = self.client.get('/endpoint1', follow_redirects=True, headers=headers)
For anyone still struggling:
using follow_redirects=True somehow looses the headers on redirect.
Simple workaround is to do the redirect yourself:
headers = { 'KEY': '123' }
code = 301
url = '/v1/endpoint'
while code == 301:
response = client.get(url, headers=headers)
code = response._status_code
if code == 301: #'Location' is only in header if 301
url = response.headers['Location']

Resources