I'm building an app and I want to make some tests. I need to convert the response data from the test client to JSON.
The app:
tasks = [
{
'id': 1,
'title': u'Buy groceries',
'description': u'Milk, Cheese, Pizza, Fruit, Tylenol',
'done': False
},
{
'id': 2,
'title': u'Learn Python',
'description': u'Need to find a good Python tutorial on the web',
'done': False
}
]
app = Flask(__name__, static_url_path="")
#app.route('/myapp/api/v1.0/tasks', methods=['GET'])
def get_tasks():
return jsonify({'tasks': [task for task in tasks]})
if __name__ == '__main__':
app.run(debug=True)
The tests:
class MyTestCase(unittest.TestCase):
def setUp(self):
myapp.app.config['TESTING'] = True
self.app = myapp.app.test_client()
def test_empty_url(self):
response = self.app.get('/myapp/api/v1.0/tasks')
resp = json.loads(response.data)
print(resp)
if __name__ == '__main__':
unittest.main()
When I try to convert response.data to JSON, I get the following error:
TypeError: the JSON object must be str, not 'bytes'
How can I fix this error and get the JSON data?
Flask 1.0 adds the get_json method to the response object, similar to the request object. It handles parsing the response data as JSON, or raises an error if it can't.
data = response.get_json()
Prior to that, and prior to Python 3.6, json.loads expects text, but data is bytes. The response object provides the method get_data, with the as_text parameter to control this.
data = json.loads(response.get_data(as_text=True))
Related
I am Getting this error. I am executing code of aws lambda function using python 3.7 to know quicksight dashboard version. Thanks in advance!
errorMessage: "Unable to marshal response: Object of type datetime is not JSON serializable",
errorType : "Runtime.MarshalError"
Code-
import boto3
import time
import sys
client = boto3.client('quicksight')
def lambda_handler(event, context):
response = client.list_dashboard_versions(AwsAccountId='11111', DashboardId='2222',MaxResults=10)
return response
I quick fix could be:
import boto3
import time
import sys
import json
client = boto3.client('quicksight')
def lambda_handler(event, context):
response = client.list_dashboard_versions(AwsAccountId='11111', DashboardId='2222',MaxResults=10)
return json.dumps(response, default=str)
Looking at https://boto3.amazonaws.com/v1/documentation/api/1.14.8/reference/services/quicksight.html#QuickSight.Client.list_dashboard_versions the return looks like this -
{
'DashboardVersionSummaryList': [
{
'Arn': 'string',
'CreatedTime': datetime(2015, 1, 1),
'VersionNumber': 123,
'Status': 'CREATION_IN_PROGRESS'|'CREATION_SUCCESSFUL'|'CREATION_FAILED'|'UPDATE_IN_PROGRESS'|'UPDATE_SUCCESSFUL'|'UPDATE_FAILED',
'SourceEntityArn': 'string',
'Description': 'string'
},
],
'NextToken': 'string',
'Status': 123,
'RequestId': 'string'
}
As you can see, CreatedTime is returned as datetime. If you want to return this as a JSON, you should transform this value.
I was struggling with this today with a method that also returns a datetime.
In my example 'JoinedTimestamp': datetime(2015, 1, 1) resulting in the same Unable to marshal response.
If you don't need the CreatedTime value you might as well remove it from the response as:
for account in list_accounts_response["Accounts"]:
if "JoinedTimestamp" in account:
del account["JoinedTimestamp"]
To follow up on Joseph Lane's answer, transforming this value could be something along the lines of:
for account in list_accounts_response["Accounts"]:
if "JoinedTimestamp" in account:
account["JoinedTimestamp"] = str(account["JoinedTimestamp"])
I want to use this route here (below) and render my react file. Using a python route.... Could someone give me some direction on how I can accomplish this?
#Auth.route('/login', methods=['GET'])
def login():
#data = {'username':'bob', 'password':'peepee123'}
#session['token'] = 'jsdkfkj934ujeklfjdlndsflds'
auth = request.authorization
if auth and auth.password == 'password':
token = jwt.encode({'user': auth.username}, app.config['SECRET_KEY'])
return jsonfiy({'token': token.decode('UTF-8')})
return make_response('Could Not verify!', 401, {'WWW-Authenticate': 'Basic realm = "Login Required"'})
If you want to render react in server site in python you can use python-react-v8, but you need to have the same react tree in server and client or it wont work, checkout hydrate in docs. For that you need to have working react app.
Example of usage:
import react
# setup react
react.set_up() # Initialize V8 machinery
react.utils.load_libs(['./bundle.js'])
#Auth.route('/login', methods=['GET'])
def login():
#data = {'username':'bob', 'password':'peepee123'}
#session['token'] = 'jsdkfkj934ujeklfjdlndsflds'
auth = request.authorization
if auth and auth.password == 'password':
token = jwt.encode({'user': auth.username}, app.config['SECRET_KEY'])
data = {'token': token.decode('UTF-8')};
react_ = react.React({
'url': request.get_full_url(),
'data': data
})
context = {
'content': react_.render(),
'data': react_.to_json(data)}
return render('index.html', context);
data = {'token': null, 'reason': "Login Required"}
react_ = react.React({
'url': request.get_full_url(),
'data': data
})
context = {
'content': react_.render(),
'data': react_.to_json(data)
}
return render('index.html', context);
I'm calling a SOAP WebService using Zeep, and it returns a JSON-like response with a datetime object. I want to write a micro-service using Flask and return proper JSON response. However, Flask complains that:
TypeError: Object of type datetime is not JSON serializable
from flask import Flask
from flask_restful import Resource, Api
import datetime
app = Flask(__name__)
api = Api(app)
class foo(Resource):
def get(self, x):
zeepResponse = {
'Response': {
'Number': x,
'DateTime': datetime.datetime(2020, 1, 1, 0, 0, 0),
'Other': None
}
}
return zeepResponse
api.add_resource(foo, '/post/<int:x>')
if __name__ == '__main__':
app.run(debug=True)
To test from the command line, simply run:
% curl http://localhost:5000/post/911
Would you guide me how to convert zeepResponse (and the datetime specifically) to a proper JSON serializable structure?
Calling json.dumps(zeepResponse, default=str) seems to fix my problem. From Stack Overflow 11875770
from flask import Flask
from flask_restful import Resource, Api
import datetime
import json
app = Flask(__name__)
api = Api(app)
class foo(Resource):
def get(self, x):
zeepResponse = {
'Response': {
'Number': x,
'DateTime': datetime.datetime(2020, 1, 1, 0, 0, 0),
'Other': None
}
}
return json.loads(json.dumps(zeepResponse, default=str))
api.add_resource(foo, '/post/<int:x>')
if __name__ == '__main__':
app.run(debug=True)
What is the proper way to handle response classes in Flask-RESTplus?
I am experimenting with a simple GET request seen below:
i_throughput = api.model('Throughput', {
'date': fields.String,
'value': fields.String
})
i_server = api.model('Server', {
'sessionId': fields.String,
'throughput': fields.Nested(i_throughput)
})
#api.route('/servers')
class Server(Resource):
#api.marshal_with(i_server)
def get(self):
servers = mongo.db.servers.find()
data = []
for x in servers:
data.append(x)
return data
I want to return my data in as part of a response object that looks like this:
{
status: // some boolean value
message: // some custom response message
error: // if there is an error store it here
trace: // if there is some stack trace dump throw it in here
data: // what was retrieved from DB
}
I am new to Python in general and new to Flask/Flask-RESTplus. There is a lot of tutorials out there and information. One of my biggest problems is that I'm not sure what to exactly search for to get the information I need. Also how does this work with marshalling? If anyone can post good documentation or examples of excellent API's, it would be greatly appreciated.
https://blog.miguelgrinberg.com/post/customizing-the-flask-response-class
from flask import Flask, Response, jsonify
app = Flask(__name__)
class CustomResponse(Response):
#classmethod
def force_type(cls, rv, environ=None):
if isinstance(rv, dict):
rv = jsonify(rv)
return super(MyResponse, cls).force_type(rv, environ)
app.response_class = CustomResponse
#app.route('/hello', methods=['GET', 'POST'])
def hello():
return {'status': 200, 'message': 'custom_message',
'error': 'error_message', 'trace': 'trace_message',
'data': 'input_data'}
result
import requests
response = requests.get('http://localhost:5000/hello')
print(response.text)
{
"data": "input_data",
"error": "error_message",
"message": "custom_message",
"status": 200,
"trace": "trace_message"
}
I am having a problem understanding how mock works and how to write unittests with mock objects. I wanted to mock an external api call every time when my model calls save() method.
My code:
models.py
from . import utils
class Book(Titleable, Isactiveable, Timestampable, IsVoidable, models.Model):
title
orig_author
orig_title
isbn
def save(self, *args, **kwargs):
if self.isbn:
google_data = utils.get_original_title_and_name(self.isbn)
if google_data:
self.original_author = google_data['author']
self.original_title = google_data['title']
super().save(*args, **kwargs)
utils.py
def get_original_title_and_name(isbn, **kawargs):
isbn_search_string = 'isbn:{}'.format(isbn)
payload = {
'key': GOOGLE_API_KEY,
'q': isbn_search_string,
'printType': 'books',
}
r = requests.get(GOOGLE_API_URL, params=payload)
response = r.json()
if 'items' in response.keys():
title = response['items'][THE_FIRST_INDEX]['volumeInfo']['title']
author = response['items'][THE_FIRST_INDEX]['volumeInfo']['authors'][THE_FIRST_INDEX]
return {
'title': title,
'author': author
}
else:
return None
I began read docs and write test:
test.py:
from unittest import mock
from django.test import TestCase
from rest_framework import status
from .constants import THE_FIRST_INDEX, GOOGLE_API_URL, GOOGLE_API_KEY
class BookModelTestCase(TestCase):
#mock.patch('requests.get')
def test_get_original_title_and_name_from_google_api(self, mock_get):
# Define new Mock object
mock_response = mock.Mock()
# Define response data from Google API
expected_dict = {
'kind': 'books#volumes',
'totalItems': 1,
'items': [
{
'kind': 'books#volume',
'id': 'IHxXBAAAQBAJ',
'etag': 'B3N9X8vAMWg',
'selfLink': 'https://www.googleapis.com/books/v1/volumes/IHxXBAAAQBAJ',
'volumeInfo': {
'title': "Alice's Adventures in Wonderland",
'authors': [
'Lewis Carroll'
]
}
}
]
}
# Define response data for my Mock object
mock_response.json.return_value = expected_dict
mock_response.status_code = 200
# Define response for the fake API
mock_get.return_value = mock_response
The first of all, I can't write target for the #mock.patch correct. If a define target as utuls.get_original_title_and_name.requests.get, I get ModuleNotFoundError. Also I can't understand how to make fake-call to external API and verify recieved data (whether necessarly its, if I've already define mock_response.json.return_value = expected_dict?) and verify that my save() method work well?
How do I write test for this cases? Could anyone explain me this case?
You should mock the direct collaborators of the code under test. For Book that would be utils. For utils that would be requests.
So for the BookModelTestCase:
class BookModelTestCase(TestCase):
#mock.patch('app.models.utils')
def test_save_book_calls_google_api(self, mock_utils):
mock_utils.get_original_title_and_name.return_value = {
'title': 'Google title',
'author': 'Google author'
}
book = Book(
title='Some title',
isbn='12345'
)
book.save()
self.assertEqual(book.title, 'Google title')
self.assertEqual(book.author, 'Google author')
mock_utils.get_original_title_and_name.assert_called_once_with('12345')
And then you can create a separate test case to test get_original_title_and_name:
class GetOriginalTitleAndNameTestCase(TestCase):
#mock.patch('app.utils.requests.get')
def test_get_original_title_and_name_from_google_api(self, mock_get):
mock_response = mock.Mock()
# Define response data from Google API
expected_dict = {
'kind': 'books#volumes',
'totalItems': 1,
'items': [
{
'kind': 'books#volume',
'id': 'IHxXBAAAQBAJ',
'etag': 'B3N9X8vAMWg',
'selfLink': 'https://www.googleapis.com/books/v1/volumes/IHxXBAAAQBAJ',
'volumeInfo': {
'title': "Alice's Adventures in Wonderland",
'authors': [
'Lewis Carroll'
]
}
}
]
}
# Define response data for my Mock object
mock_response.json.return_value = expected_dict
mock_response.status_code = 200
# Define response for the fake API
mock_get.return_value = mock_response
# Call the function
result = get_original_title_and_name(12345)
self.assertEqual(result, {
'title': "Alice's Adventures in Wonderland",
'author': 'Lewis Carroll'
})
mock_get.assert_called_once_with(GOOGLE_API_URL, params={
'key': GOOGLE_API_KEY,
'q': 'isbn:12345',
'printType': 'books',
})