Django API Client Get Request with Payload - django

I am looking to see if this is possible. If I send a get request with body in postman I get back results matching what I need.
Can we send a get request with a body using the APIClient?
Here is my code;
def setUp(self):
self.client = APIClient()
HistoryLog.objects.create(username='User1', log_time='2020-05-05', logon_time='08:00', logout_time='09:00')
HistoryLog.objects.create(username='User2', log_time='2020-05-05', logon_time='08:30', logout_time='10:00')
HistoryLog.objects.create(username='User3', log_time='2020-05-08', logon_time='08:40', logout_time='11:00')
HistoryLog.objects.create(username='User4', log_time='2020-05-10', logon_time='08:50', logout_time='12:00')
def test_get_data(self):
payload = {'date': '2020-05-05', 'start_time': '06:00', 'end_time': '12:00'}
res = self.client.get(HISTORY_URL, payload)
self.assertEqual(res.status_code, status.HTTP_200_OK) -- this passes.
self.assertEqual(len(res.data), 2) -- this always come back that res.data is zero

A bit hacky, but seems to work:
def test_get_data(self):
payload = {'date': '2020-05-05', 'start_time': '06:00', 'end_time': '12:00'}
data, content_type = self.client._encode_data(payload, 'json')
res = self.client.generic('get', HISTORY_URL, data, content_type)
...

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,
)

PyTest: How to add X-BULK-OPERATION with HTTP_AUTHORIZATION for session credentials (headers)

I'm writing test for Django Rest Framework with PyTest. There was a problem with headers X-BULK-OPERATION because I can't add X-BULK-OPERATION with HTTP_AUTHORIZATION on header.
my code
#pytest.fixture(scope="module")
def session(django_db_setup, django_db_blocker):
with django_db_blocker.unblock():
user = User.objects.get(email=TEST_EMAIL)
user.set_password(TEST_EMAIL)
user.save(update_fields=["password"])
client = APIClient()
url = reverse("login_sys:login")
response = client.post(url, {
"email": TEST_EMAIL,
"password": TEST_EMAIL
})
client.credentials(**{"X-CSRFToken": response.cookies['csrftoken'].value})
yield client
client.logout()
#pytest.fixture(scope='module')
def choose_crm(session):
crm = Crm.objects.get()
url = reverse("crm:set-jwt", kwargs={"pk": crm.id})
response = session.get(url)
session.credentials(HTTP_AUTHORIZATION=response.data.get("token", None),
**{"X-BULK-OPERATION": True})
yield session
session.logout()
error
{'detail': "Header 'X-BULK-OPERATION' should be provided for bulk operation."}
Debug:print request, as you can see I have Bulk-Operation in header but still gives the error no BULK-OPERATION in header
'HTTP_AUTHORIZATION': 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJjcm1fdXNlciI6MSwiY3JtX2lkIjoxLCJncm91cF9pZCI6MSwiZGVhbF9wZXJtIjp7ImNyZWF0ZSI6MSwiZGlzcGxheSI6MSwiZWRpdCI6MSwiZGVsZXRlIjoxLCJleHBvcnQiOjF9LCJjb250YWN0X3Blcm0iOnsiY3JlYXRlIjoxLCJkaXNwbGF5IjoxLCJlZGl0IjoxLCJkZWxldGUiOjEsImV4cG9ydCI6MX0sImxlYWRfcGVybSI6eyJjcmVhdGUiOjEsImRpc3BsYXkiOjEsImVkaXQiOjEsImRlbGV0ZSI6MSwiZXhwb3J0IjoxfSwidGFza19wZXJtIjp7ImNyZWF0ZSI6MSwiZGlzcGxheSI6MSwiZWRpdCI6MSwiZGVsZXRlIjoxLCJleHBvcnQiOjF9fQ.Wrd89a8jpRnNu1XhNsOKFAkNQR6v_Y_X0nKySocMtLM', 'HTTP_X-BULK-OPERATION': True}

getting code 400 message Bad request syntax , after post from flutter

getting code 400 message Bad request syntax , after post from flutter,
with postman request send and no problem but with flutter after Post Map data to Django server i get this error
[19/May/2020 14:58:13] "POST /account/login/ HTTP/1.1" 406 42
[19/May/2020 14:58:13] code 400, message Bad request syntax ('32')
[19/May/2020 14:58:13] "32" 400 -
Django
#api_view(['POST'])
def login_user(request):
print(request.data)
if request.method == 'POST':
response = request.data
username = response.get('username')
password = response.get('password')
if password is not None and username is not None:
user = authenticate(username=username, password=password)
if user is not None:
create_or_update_token = Token.objects.update_or_create(user=user)
user_token = Token.objects.get(user=user)
return Response({'type': True, 'token': user_token.key, 'username': user.username},
status=status.HTTP_200_OK)
else:
return Response({'type': False, 'message': 'User Or Password Incorrect'},
status=status.HTTP_404_NOT_FOUND)
else:
return Response({'type': False, 'message': 'wrong parameter'}, status=status.HTTP_406_NOT_ACCEPTABLE)
else:
return Response({'type': False, 'message': 'method is wrong'}, status=status.HTTP_405_METHOD_NOT_ALLOWED)
flutter
Future<dynamic> postGoa(String endpoint, Map data)async{
Map map = {
"username":"user",
"password":"password"
};
var url = _getUrl("POST", endpoint);
var client = new HttpClient();
HttpClientRequest request = await client.postUrl(Uri.parse(url));
request.headers.set('content-type', 'application/json');
request.headers.set('Authorization', 'Bearer '+ athenticated
);
request.add(utf8.encode(json.encode(map)));
HttpClientResponse response = await request.close();
String mydata= await response.transform(utf8.decoder).join();
client.close();
return mydata;
}
}
after add
request.add(utf8.encode(json.encode(map)));
i get error in Django console
Try printing out your request headers from the Django application.
print(request.headers)
I bet one of the headers is Content-Type: ''. If that is the case, Django isn't reading your POST data because it thinks there is no data. I recommend calculating the length of the content you are sending in Flutter, then sending the correct Content-Length header with your request.
That might look something like this (in your Flutter app):
encodedData = jsonEncode(data); // jsonEncode is part of the dart:convert package
request.headers.add(HttpHeaders.contentLengthHeader, encodedData.length);

testing stripe on-boarding django with mock

i am having trouble trying to mock test the on-boarding process of stripe connect. I am just learning how to use mock and i am struggling with the StripeAuthorizeCallbackView. the process is as follows: A user reaches the StripeAuthorizeView which sends them to the stripe api to sign up for an account. Once they successfully sign up for an account their redirected back to my platform and stripe sends a temporary code which i then send back to stripe with my api keys. Once i have sent the information back to stripe they then return me credentials for the user being the stripe_user_id.
Here is the two views in question:
import urllib
import requests
class StripeAuthorizeView(LoginRequiredMixin, View):
def get(self, request):
url = 'https://connect.stripe.com/express/oauth/authorize?'
user = self.request.user
if user.account_type == 'Business':
business_type = 'company'
else:
business_type = 'individual'
params = {
'response_type': 'code',
'scope': 'read_write',
'client_id': settings.STRIPE_CONNECT_CLIENT_ID,
'redirect_uri': f'http://127.0.0.1:8000/accounts/stripe/oauth/callback',
'stripe_user[email]' : user.email,
'stripe_user[business_type]' : business_type,
'stripe_user[url]' : 'http://127.0.0.1:8000/accounts/user/%s/' %user.pk,
}
url = f'{url}?{urllib.parse.urlencode(params)}'
return redirect(url)
lass StripeAuthorizeCallbackView(LoginRequiredMixin, View):
def get(self, request):
code = request.GET.get('code')
if code:
data = {
'client_secret': settings.STRIPE_SECRET_KEY,
'grant_type': 'authorization_code',
'client_id': settings.STRIPE_CONNECT_CLIENT_ID,
'code': code
}
url = 'https://connect.stripe.com/oauth/token'
resp = requests.post(url, params=data)
stripe_user_id = resp.json()['stripe_user_id']
stripe_access_token = resp.json()['access_token']
stripe_refresh_token = resp.json()['refresh_token']
user = self.request.user
user.stripe_access_token = stripe_access_token
user.stripe_user_id = stripe_user_id
user.stripe_refresh_token = stripe_refresh_token
user.save()
notify.send(sender=user, recipient=user,
verb='You have succesfully linked a stripe account. You can now take payments for sales.',
level='info')
redirect_url = reverse('account', kwargs={'pk': user.pk})
response = redirect(redirect_url)
return response
else:
user = self.request.user
notify.send(sender=user, recipient=user,
verb='Your attempt to link a stripe account failed. Please contact customer support.',
level='warning')
url = reverse('account', kwargs={'pk': user.pk})
response = redirect(url)
return response
I am not very worried about testing the StripeAuthorizeView a lot. I am more trying to figure out how to test the StripeAuthorizeCallbackView. All i can figure out is that i will need to mock both the code returned and then mock the following requests.post. This test is important to confirm my platform is linking the users credentials after the on-boarding process. Any help on this will be greatly appricated.
edit:
So far i have the following :
#classmethod
def setUpTestData(cls):
cls.test_user = User.objects.create_user(
password='test',
full_name='test name',
email='test#test.com',
address='1 test st',
suburb='test',
state='NSW',
post_code='2000',
contact_number='0433335333' )
#patch('requests.get')
def test_authorizecallback_creates_stripe_details(self, get_mock):
code = requests.get('code')
user = self.test_user
self.client.login(email='test#test.com', password='test')
mocked = ({'stripe_user_id' : '4444','stripe_access_token' : '2222',
'stripe_refresh_token' : '1111' })
with mock.patch('requests.post', mock.Mock(return_value=mocked)):
response = self.client.get('/accounts/stripe/oauth/callback/',
{'code' : '1234'})
self.assertEqual(user.stripe_access_token, '222')
message = list(response.context.get('messages'))[0]
however i keep getting:
File "C:\Users\typef\Desktop\Projects\python_env\fox-listed\Fox-Listed\fox-listed\user\views.py", line 142, in get
stripe_user_id = resp.json()['stripe_user_id']
AttributeError: 'dict' object has no attribute 'json'
the actual response that the StripeAuthorizeCallBackView gives is:
{'access_token': 'sk_test_1KyTG74Ouw65KYTR1O03WjNA00viNjcIfO', 'livemode': False, 'refresh_token': 'rt_H3Vrhd0XbSH7zbmqfDyMNwolgt1Gd7r4ESBDBr5a4VkCzTRT', 'token_type': 'bearer', 'stripe_publishable_key': 'pk_test_**********', 'stripe_user_id': 'acct_1GVOpAF7ag87i2I6', 'scope': 'express'}
Looks like i got it, if there is a flaw here let me know but here is what i have:
class TestStripeAuthorizeCallbackView:
#patch('user.views.requests')
def test_authorizecallback_creates_stripe_details(self, requests_mock):
json = { 'stripe_user_id' : '4444', 'access_token' : '2222', 'refresh_token' : '1111'}
requests_mock.post.return_value.json.return_value = json
user = mixer.blend('user.CustomUser', stripe_user_id=None, access_token=None, refresh_token=None)
req = RequestFactory().get('/', data={'code' : '1234'})
middleware = SessionMiddleware()
middleware.process_request(req)
req.session.save()
messages = FallbackStorage(req)
setattr(req, '_messages', messages)
req.user = user
resp = StripeAuthorizeCallbackView.as_view()(req)
assert resp.status_code == 302 ,'should redirect to success url'
assert user.stripe_user_id == '4444', 'should assign stripe_user_id to user'
assert user.stripe_access_token == '2222', 'should assign an access_token'
assert user.stripe_refresh_token == '1111', 'should assign a refresh_token'
What you're describing isn't mocking so much as it is end-to-end testing, connecting actual test accounts, which you can do.
As long as you're using a test client_id then when you are redirected to Stripe to create the account you can skip the form via a link and get directed back to your site with a real (test mode) oauth code.
Essentially you can set this up and actually go through the flow to create & connect new disposable test Stripe accounts.

Django check for error messages in context

I have a simple test:
def test_project_info_form_post_submission(self):
"""
Test if project info form can be submitted via post.
"""
# set up our POST data
post_data = {
'zipcode': '90210',
'module': self.module1.pk,
'model': self.model1.pk,
'orientation': 1,
'tilt': 1,
'rails_direction': 1,
}
...
response = self.client.post(reverse(url), post_data)
self.assertEqual(response.status_code, 302)
# test empty form
response = self.client.post(reverse(url))
self.assertEqual(response.status_code, 200)
#! test for general form error message
# now test invalid responses
post_data['zipcode'] = 'abcdefg'
response = self.client.post(reverse(url), post_data)
self.assertEqual(response.status_code, 200)
#! test for specific error message associated with zipcode
So the lines I am having trouble with are marked with shebangs. I know I should have messages in the context variable but can't seem to figure out the right ones to use.
You can test if the template contains your message in your TestCase with assertContains.