Middleware logging limitation Django 1.11 - python

Background:
I have integration test which is working fine, but later on failed when I
added the customized middleware
def test_mobile_update_customer(self):
user = User.objects.create(username='warhead')
self.client.force_authenticate(user=user)
mommy.make(Customer, family_name='IBM', created_user=self.soken_staff, updated_user=self.soken_staff)
data = {
"family_name": "C0D1UM"
}
customer = Customer.objects.first()
res = self.client.patch(reverse('api:customer-detail', kwargs={'pk': customer.id}), data=data)
self.assertEqual(200, res.status_code)
customer.refresh_from_db()
self.assertEqual('C0D1UM', customer.family_name)
self.assertEqual('spearhead', customer.created_user.username)
self.assertEqual('warhead', customer.updated_user.username)
Problem:
The middleware break it with Exception
File "/Users/el/Code/norak-cutter/soken/soken-web/soken_web/middleware.py", line 47, in process_request
data['PATCH'] = json.loads(request.body)
File "/Users/el/.pyenv/versions/soken/lib/python3.6/site-packages/django/http/request.py", line 264, in body
raise RawPostDataException("You cannot access body after reading from request's data stream")
django.http.request.RawPostDataException: You cannot access body after reading from request's data stream
The problem is data has been read before the RESTful api do the job.
Then the program raises an exception.
def process_request(self, request):
if request.path.startswith('/api/'):
data = collections.OrderedDict()
data["user"] = request.user.username
data["path"] = request.path
data["method"] = request.method
data["content-type"] = request.content_type
if request.method == 'GET':
data['GET'] = request.GET
elif request.method == 'POST':
data['POST'] = request.POST
# https://stackoverflow.com/questions/4994789/django-where-are-the-params-stored-on-a-put-delete-request
# elif request.method == 'PUT':
# data['PUT'] = json.loads(request.body)
# test_mobile_update_customer
# raise RawPostDataException("You cannot access body after reading from request's data stream")
# django.http.request.RawPostDataException: You cannot access body after reading from request's data stream
# elif request.method == 'PATCH':
# data['PATCH'] = json.loads(request.body)
elif request.method == 'DELETE':
pass
self.__uuid = str(uuid.uuid4())
request_logger.info(f'{self.__uuid} {json.dumps(data)}')
Update2
Attempt:
I change client constructor refer to https://github.com/encode/django-rest-framework/issues/2774
def test_mobile_update_customer(self):
user = User.objects.create(username='warhead')
# self.client.force_authenticate(user=user)
from django.test import Client
client = Client()
client.force_login(user)
mommy.make(Customer, family_name='IBM', created_user=self.soken_staff, updated_user=self.soken_staff)
data = {
"family_name": "C0D1UM"
}
customer = Customer.objects.first()
res = client.patch(reverse('api:customer-detail', kwargs={'pk': customer.id}), data=data, content_type='application/json')
self.assertEqual(200, res.status_code)
customer.refresh_from_db()
self.assertEqual('C0D1UM', customer.family_name)
self.assertEqual('spearhead', customer.created_user.username)
self.assertEqual('warhead', customer.updated_user.username)
It does not work. The Client misinterpret the payload
>>> res.content
b'{"detail":"JSON parse error - Expecting property name enclosed in double quotes: line 1 column 2 (char 1)"}'
Workaround:
I don't know this is correct for all cases or not, but I workaround by this
test.py
https://gist.github.com/elcolie/88eb7c90cfca1c369a020ac232c7fbcc
middleware.py
https://gist.github.com/elcolie/5d9b1a2f890a0efcb46fdb95c0e90908
result.py
https://gist.github.com/elcolie/298e595b404c1a5839ed8dd584d2f07f
Question:
How do I do integration test of PATCH with the same time that testcase will not break by my middleware?
Right now I have to choose either one of them.

Related

invalid(empty) response with Django

AM building a USSD application, in Django with this API https://documenter.getpostman.com/view/7705958/UyrEhaLQ#intro. I get the responses from the API and initialize the data to be processed. But I don't get the menu (MSG) to display on the user phone successfully. The error I get is invalid(empty) response. This is the response to the user’s request. The content provider should provide a response to the request in the same format.
USERID = This is the ID provided by NALO to the client
MSISDN = The mobile number of the user
MSG =This is a mandatory parameter that holds the message to be displayed on the user’s phone
MSGTYPE = This indicates whether the session should continue or be terminated (True/false)
#csrf_exempt
def ussd(request):
if request.method == 'GET':
html = "<html><body>Nothing here baby!</body></html>"
return HttpResponse(html)
elif request.method == 'POST':
url = "https://99c9-102-176-94-213.ngrok.io/ussd"
response_data = json.loads(request.body)
code_id = response_data["USERID"]
serviceCode = response_data["MSISDN"]
type = response_data["MSGTYPE"]
session_id = response_data["SESSIONID"]
text = response_data["USERDATA"]
msg = ""
if text == "":
msg = "Welcome To TEST Dev"
elif text == "1":
msg = "This is Test Two"
payload ={
"USERID": code_id,
"MSISDN": serviceCode,
"MSGTYPE": type,
"USERDATA": text,
"SESSIONID": session_id,
"MSG": msg,
}
headers = {
'Content-Type': 'application/json'
}
response = requests.request("POST", url, headers=headers, data=json.dumps(payload))
return HttpResponse(response, status=200)

request.POST.get() returning none

I am running a website using Django. I am trying to Login but it is returning none and i am not getting any error. It shows 200 status code.
Here is my views.py code:
def user_login(request):
datas= {'log':False}
if request.method == "POST":
usern=request.POST.get('Username')
passw=request.POST.get('password')
response = requests.post(url='http://www.onebookingsystem.com/productionApi/API/Admin/login.php',data={"Username":usern,"password":passw})
json_data = response.json()
if json_data['status'] == 1:
user=authenticate(Username=usern,password=passw)
login(request,user)
range_yearly = 0
range_monthly = 0
respo = requests.get(url='http://www.onebookingsystem.com/productionApi/API/Admin/admin_dashboard.php')
data_dash = json.loads(respo.text)
when i am running in POST HTTP request,it shows successfully logged in,but
in GET HTTP request, it shows :Status 0 "mobile or password is missing".//required post params is missing.
This is propably because you're using requests without the session storage.
You can try it this (untested) way:
def user_login(request):
# Create client with session storage to store session cookies
client = requests.Session()
datas= {'log':False}
if request.method == "POST":
usern=request.POST.get('Username')
passw=request.POST.get('password')
# client stores session cookie at login
response = client.post(url='http://www.onebookingsystem.com/productionApi/API/Admin/login.php',data={"Username":usern,"password":passw})
# you can check that with
# print(client.cookies)
json_data = response.json()
if json_data['status'] == 1:
user=authenticate(Username=usern,password=passw)
login(request,user)
range_yearly = 0
range_monthly = 0
# client should be able to access because of its stored session cookie
respo = client.get(url='http://www.onebookingsystem.com/productionApi/API/Admin/admin_dashboard.php')
data_dash = json.loads(respo.text)
More information

Error with asynchronous request in DRF

I need to fulfill a request to two services.
The code looks like this:
async def post1(data):
response = await aiohttp.request('post', 'http://', json=data)
json_response = await response.json()
response.close()
return json_response
async def get2():
response = await aiohttp.request('get', 'http://')
json_response = await response.json()
response.close()
return json_response
async def asynchronous(parameters):
task1 = post1(parameters['data'])
task2 = get2()
result_list = []
for body in await asyncio.gather(task1, task2):
result_list.append(body)
return result_list
If I run the code locally, it's OK. The code looks like this:
if __name__ == "__main__":
ioloop = asyncio.get_event_loop()
parameters = {'data': data}
result = ioloop.run_until_complete(asynchronous(parameters))
ioloop.close()
print(result)
I get the right result. But if I try to execute code from the DRF method, an error occurs:
TypeError: object _SessionRequestContextManager can't be used in
'await' expression
example code that I run:
.....
class MyViewSet(GenericAPIView):
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
......
ioloop = asyncio.get_event_loop()
result = ioloop.run_until_complete(asynchronous(serializer.data)) # <<<<< error here
ioloop.close()
......
return Response(serializer.data, status=status.HTTP_201_CREATED)
Please, tell me what the problem may be?
The object returned by aiohttp.request cannot be awaited, it must be used as an async context manager. This code:
response = await aiohttp.request('post', 'http://', json=data)
json_response = await response.json()
response.close()
needs to changed to something like:
async with aiohttp.request('post', 'http://', json=data) as response:
json_response = await response.json()
See the documentation for more usage examples.
Perhaps you have a different aiohttp version on the server where you run DRF, which is why it works locally and fails under DRF.
Try it https://github.com/Skorpyon/aiorestframework
Create some middleware for authentication:
import re
from xml.etree import ElementTree as etree
from json.decoder import JSONDecodeError
from multidict import MultiDict, MultiDictProxy
from aiohttp import web
from aiohttp.hdrs import (
METH_POST, METH_PUT, METH_PATCH, METH_DELETE
)
from aiorestframework import exceptions
from aiorestframework omport serializers
from aiorestframework import Response
from aiorestframework.views import BaseViewSet
from aiorestframework.permissions import set_permissions, AllowAny
from aiorestframework.app import APIApplication
from my_project.settings import Settings # Generated by aiohttp-devtools
TOKEN_RE = re.compile(r'^\s*BEARER\s{,3}(\S{64})\s*$')
async def token_authentication(app, handler):
"""
Authorization middleware
Catching Authorization: BEARER <token> from request headers
Found user in Tarantool by token and bind User or AnonymousUser to request
"""
async def middleware_handler(request):
# Check that `Authorization` header exists
if 'authorization' in request.headers:
authorization = request.headers['authorization']
# Check matches in header value
match = TOKEN_RE.match(authorization)
if not match:
setattr(request, 'user', AnonymousUser())
return await handler(request)
else:
token = match[1]
elif 'authorization_token' in request.query:
token = request.query['authorization_token']
else:
setattr(request, 'user', AnonymousUser())
return await handler(request)
# Try select user auth record from Tarantool by token index
res = await app['tnt'].select('auth', [token, ])
cached = res.body
if not cached:
raise exceptions.AuthenticationFailed()
# Build basic user data and bind it to User instance
record = cached[0]
user = User()
user.bind_cached_tarantool(record)
# Add User to request
setattr(request, 'user', user)
return await handler(request)
return middleware_handler
And for data extraction from request:
DATA_METHODS = [METH_POST, METH_PUT, METH_PATCH, METH_DELETE]
JSON_CONTENT = ['application/json', ]
XML_CONTENT = ['application/xml', ]
FORM_CONTENT = ['application/x-www-form-urlencoded', 'multipart/form-data']
async def request_data_handler(app, handler):
"""
Request .data middleware
Try extract POST data or application/json from request body
"""
async def middleware_handler(request):
data = None
if request.method in DATA_METHODS:
if request.has_body:
if request.content_type in JSON_CONTENT:
# If request has body - try to decode it to JSON
try:
data = await request.json()
except JSONDecodeError:
raise exceptions.ParseError()
elif request.content_type in XML_CONTENT:
if request.charset is not None:
encoding = request.charset
else:
encoding = api_settings.DEFAULT_CHARSET
parser = etree.XMLParser(encoding=encoding)
try:
text = await request.text()
tree = etree.XML(text, parser=parser)
except (etree.ParseError, ValueError) as exc:
raise exceptions.ParseError(
detail='XML parse error - %s' % str(exc))
data = tree
elif request.content_type in FORM_CONTENT:
data = await request.post()
if data is None:
# If not catch any data create empty MultiDictProxy
data = MultiDictProxy(MultiDict())
# Attach extracted data to request
setattr(request, 'data', data)
return await handler(request)
return middleware_handler
Create few serializers:
class UserRegisterSerializer(s.Serializer):
"""Register new user"""
email = s.EmailField(max_length=256)
password = s.CharField(min_length=8, max_length=64)
first_name = s.CharField(min_length=2, max_length=64)
middle_name = s.CharField(default='', min_length=2, max_length=64,
required=False, allow_blank=True)
last_name = s.CharField(min_length=2, max_length=64)
phone = s.CharField(max_length=32, required=False,
allow_blank=True, default='')
async def register_user(self, app):
user = User()
data = self.validated_data
try:
await user.register_user(data, app)
except Error as e:
resolve_db_exception(e, self)
return user
And few ViewSets. It may be nested in bindings['custom']
class UserViewSet(BaseViewSet):
name = 'user'
lookup_url_kwarg = '{user_id:[0-9a-f]{32}}'
permission_classes = [AllowAny, ]
bindings = {
'list': {
'retrieve': 'get',
'update': 'put'
},
'custom': {
'list': {
'set_status': 'post',
'create_new_sip_password': 'post',
'get_registration_domain': 'get',
'report': UserReportViewSet
}
}
}
#staticmethod
async def resolve_sip_host(data, user, app):
sip_host = await resolve_registration_switch(user, app)
data.update({'sip_host': sip_host})
async def retrieve(self, request):
user = User()
await user.load_from_db(request.match_info['user_id'], request.app)
serializer = user_ser.UserProfileSerializer(instance=user)
data = serializer.data
await self.resolve_sip_host(data, user, request.app)
return Response(data=data)
#atomic
#set_permissions([AuthenticatedOnly, IsCompanyMember, CompanyIsEnabled])
async def update(self, request):
serializer = user_ser.UserProfileSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = await serializer.update_user(user_id=request.user.id,
app=request.app)
serializer = user_ser.UserProfileSerializer(instance=user)
data = serializer.data
await self.resolve_sip_host(data, user, request.app)
return Response(data=data)
Register Viewsets and run Application:
def setup_routes(app: APIApplication):
"""Add app routes here"""
# Auth API
app.router.register_viewset('/auth', auth_vws.AuthViewSet())
# User API
app.router.register_viewset('/user', user_vws.UserViewSet())
# Root redirection to Swagger
redirect = app.router.add_resource('/', name='home_redirect')
redirect.add_route('*', swagger_redirect)
def create_api_app():
sentry = get_sentry_middleware(settings.SENTRY_CONNECT_STRING, settings.SENTRY_ENVIRONMENT)
middlewares = [sentry, token_authentication, request_data_handler]
api_app = APIApplication(name='api', middlewares=middlewares,
client_max_size=10*(1024**2))
api_app.on_startup.append(startup.startup_api)
api_app.on_shutdown.append(startup.shutdown_api)
api_app.on_cleanup.append(startup.cleanup_api)
setup_routes(api_app)
if __name__ == '__main__':
app = create_api_app()
web.run_app(app)

Sending POST data between Django apps

I need to send POST request from one Django app to another under the same project. The request arrives, but without POST data.
Why is that? And how to send POST data properly?
Sender part, app 1 view:
def get_project_data(request):
if request.method == "POST":
if request.is_ajax():
response = etl_post('get_data', [request.user.project.id], request.POST)
def etl_post(path, identifiers=None, post_data=None):
def date_handler(obj):
if hasattr(obj, 'isoformat'):
return obj.isoformat()
else:
raise TypeError
json_data = json.dumps(post_data, default=date_handler) if post_data else None
return _request(path, identifiers, json_data)
def _request(path, identifiers=None, post_data=None, method=None):
data = None
try:
url = urlparse.urljoin(settings.ETL_WEB_API_URL, path)
if identifiers is not None:
for o in identifiers:
url += "/" + str(o)
if post_data:
url += "/"
request = urllib2.Request(url, data=post_data)
request.add_header("Content-Type", "application/json")
request.add_header("X-ETL-Authorization", settings.ETL_WEB_API_KEY)
if method:
request.get_method = lambda: method
result = urllib2.urlopen(request)
data_str = result.read()
if result.getcode() == 200:
data = json.loads(data_str)
else:
logger.error("Unexpected response %s" % result)
except Exception as e:
logger.exception(e.message)
return data
Also, I tried result = urllib2.urlopen(request, data=post_data), no success.
post_data example:
{"project_id": "nYhRTAmGkkHSlLr8BfPR", "project_name": "rocket-launch", "salt": "805b2892c16369275eerec4dd401f5f", ...}
(Pdb) type(post_data)
<type 'str'>
Receiver part, app 2 view:
#csrf_exempt
def get_project_data(request, trust_id):
if request.method == 'POST':
pdb.set_trace()
The arrived message:
(Pdb) request
<WSGIRequest: POST '/pipeline/get_project_data/2/'>
(Pdb) request.POST
<QueryDict: {}>
You're sending JSON, not form-encoded data. That is found in request.body, not request.POST.
I must say though, if these two apps are in the same project there are much easier ways of sending data between them than by making HTTP requests.

Django Unit Testing testing views

I am testing my views using Django Unit testing. I am making get and post requests with params to check what status i get back.
But the problem how to check for context variables which are retuned in the response?
For example, on the View Cities page, I make a get request, the context dict in the view has the variable cities. So I want to check for context.
resp = self.client.post(
path=reverse('upload_video'),
data={"video_url": video_url, "pro": 1}
)
self.assertEqual(resp.status_code, 200)
Condition is True both ways, if the form is invalid or valid it returns 200. If I can check for context, then I can check what has been retuned from the view in response.
What I tried
=> resp.__dict__
{'templates': [], '_handler_class': None, '_headers': {'vary': ('Vary', 'Cookie'), 'content-type': ('Content-Type', 'application/json')}, '_charset': 'utf-8', '_closable_objects': [], 'cookies': <SimpleCookie: >, 'client': <django.test.client.Client object at 0x112bace10>, '_base_content_is_iter': False, 'context': None, 'request': {u'CONTENT_LENGTH': 202, u'wsgi.input': <django.test.client.FakePayload object at 0x113667990>, u'REQUEST_METHOD': 'POST', u'PATH_INFO': '/upload/video/modal/', u'CONTENT_TYPE': u'multipart/form-data; boundary=BoUnDaRyStRiNg', u'QUERY_STRING': ''}, '_container': ['{"error": {"msg": "Pro: Select a valid choice. That choice is not one of the available choices.", "head": null}}']}
Check _container has that variable. The form is invalidated, and retuned an error in the context. but when I do the following i get None
=> resp.context
None
Test
import os
from django.contrib.auth import authenticate
from django.core.urlresolvers import reverse
from django.test import TestCase
def test_video_upload(self):
""" Test that video upload is successful """
self.create_and_login(username="su", password="su", is_superuser=True)
video_urls = [
u"https://www.youtube.com/watch?v=abc",
u"https://vimeo.com/32222",
u"http://www.dailymotion.com/video/rer"
]
for video_url in video_urls:
resp = self.client.post(
path=reverse('upload_video'),
data={"video_url": video_url, "pro": 1}
)
set_trace() #Breakpoint
a = resp.context[-1] # <=== Not getting it here.
self.assertEqual(resp.status_code, 200) #passes
videos = Video.objects.all()
self.assertEqual(len(videos), 3)
View
ctx = {}
if request.method == Enums.Request.POST:
video_form = UploadVideoEasyForm(data=request.POST)
if video_form.is_valid():
video, log = video_form.save(request=request)
msg = 'Successfully Uploaded, View: here'.format(video.get_absolute_url())
ctx[Enums.ResponseAlert.Success] = {'msg': msg}
else:
ctx[Enums.ResponseAlert.Error] = make_alert(msg=form_error_to_string(video_form))
return HttpResponse(json.dumps(ctx), content_type="application/json")
elif request.method == Enums.Request.GET:
ctx['upload_video'] = UploadVideoEasyForm()
if request.user.is_authenticated() and request.user.is_superuser:
return render_to_response('new/modals/upload_video.html', context_instance=RequestContext(request, ctx))
Cheers.
The resp (An instance of django.test.Response) should have an context attribute.
You can access context value using context[..]:
self.assertEqual(resp.context['cities'], ...)

Categories