How to parse the POST argument to a REST service? - python

It seems I have another JSON problem, this time when posting to the REST service.
I am using Flask-Restful.
api.add_resource(Records, '/rest/records/<string:email>/<string:password>/<string:last_sync_date>')
parser = reqparse.RequestParser()
parser.add_argument('record_date', type=str)
parser.add_argument('records', type=str)
parser.add_argument('rating', type=str)
parser.add_argument('notes', type=str)
class Records(Resource):
def post(self, email, password, last_sync_date):
args = parser.parse_args()
records = args['records'] # 'records' = None, but why?
return records, 201
Unit test:
resource_fields = {
'record_date': fields.String,
'rating': fields.Integer,
'notes': fields.String,
'last_updated': fields.DateTime,
}
records = {"records":[]}
records["records"].append(marshal(record1, resource_fields))
rv = self.app.post('/rest/records/{0}/{1}/{2}'.format(email, password, sync_date), data=json.dumps(records))
json.dumps(records) is:
str: {"records": [{"rating": 1, "notes": null, "last_updated": "Tue, 15 Oct 2013 15:52:44 -0000", "record_date": "2013-10-15 15:52:44.746815"}]}
Why is args['records'] None, where I am clearly sending it over the wire?
UPDATE:
Strange part is when I send a single object, its all dandy. So strange:
record = dict(record_date=record1.record_date, rating=record1.rating, notes=record1.notes, last_updated=record1.last_updated)
rv = self.app.post('/rest/records/{0}/{1}/{2}'.format(email, password, sync_date), data=record)
args:
{'records': None, 'notes': None, 'task': None, 'record_date': '2013-10-15 16:48:40.662744', 'rating': '1'}

I ended up raising this as an issue on flask-resful github and got this solution, which works for me. Credit goes to Doug Black.
reqparse doesn't really know how to handle JSON. To deal with JSON posts, you'll want to use the flask.request.json dict.
Here's an updated example for what you probably want:
from flask import Flask, request
from flask.ext import restful
class Records(restful.Resource):
def post(self, email, password, last_sync_date):
records = request.json['records']
return records, 201
app = Flask(__name__)
api = restful.Api(app)
api.add_resource(
Records,
'/rest/records/<string:email>/<string:password>/<string:last_sync_date>'
)
The docs on request.json are here.
You'll need to make sure you post with the content type header set to application/json so flask knows to populate the json dictionary.
self.app.post(
'/rest/records/{0}/{1}/{2}'.format(email, password, sync_date),
data=json.dumps(records),
headers={'Content-Type': 'application/json'
)

class Records(Resource):
parser = reqparse.RequestParser()
parser.add_argument('record_date', type=str)
parser.add_argument('records', type=str)
parser.add_argument('rating', type=str)
parser.add_argument('notes', type=str)
def post(self):
args = parser.parse_args()
records = args['records']
return records, 201
I hope it works.

Related

Integrating 2checkout with Django 2.2

I try to implement 2checkout to my django app (Python 3.7 and Django 2.2).
I'm using the Sandox and when I see API logs my orders are successfully passed. I also chose a URL for the Passback and selected Header Redirect in my account settings.
Unfortunately, I don't succeed in getting the redirection works :
I get this error after twocheckout.Charge.authorize(params) if successfully executed:
The view subscriptions.views.subscription_payment didn't return an HttpResponse object. It returned None instead.
When I manually add an HttpResponseRedirect like this:
return HttpResponseRedirect('/payment-success') the GET request is empty because it doesn't come from 2checkout.
I tried to follow this tutorial https://github.com/2Checkout/2checkout-python-tutorial and it doesn't mention any HttpResponseRedirect.
Could you please help me with this issue? It's really frustrating. I'm a beginner developper so maybe I missed something. Please feel free to ask any further information that would be helpful to understand my issue.
Many thanks in advance
I was integrating the API so no need for Passback URL. The question was a non sense :) That's why the tutorial doesn't mention HttpResponseRedirect.
Because it has been asked in the comments, here is the code used to fulfil the need I had at that moment. Hope this could help someone else.
def subscription_payment(request, pk):
property = Property.objects.get(pk=pk)
if request.method == 'POST':
pay_token = request.POST.get('token')
twocheckout.Api.auth_credentials({
'private_key': '######-####-####-####-#######',
'seller_id': '#########',
'mode': 'sandbox' # Uncomment to use Sandbox
})
data = serializers.serialize('json', Profile.objects.filter(user=request.user), fields=(
'address_2', 'city', 'zip_code', 'country', 'phone_number'))
data = json.loads(data)
params = {
'merchantOrderId': request.user.id,
'token': pay_token,
'currency': 'USD',
'billingAddr': {
'name': request.user.get_full_name(),
'addrLine1': data[0]['fields']['address_2'],
'city': data[0]['fields']['city'],
'state': ' ',
'zipCode': data[0]['fields']['zip_code'],
'country': data[0]['fields']['country'],
'email': request.user.email,
'phoneNumber': data[0]['fields']['phone_number'],
},
'lineItems': [
{
'type': 'product',
'name': 'Abonnement mensuel basic',
'recurrence': '1 Month',
'duration': '3 Month',
'tangible': 'N',
'price': '150.00',
'description': '',
'productId': property.id,
}
]
}
try:
result = twocheckout.Charge.authorize(params)
backup_response = json.dumps(result)
print(result.responseCode)
if result.responseCode == 'APPROVED':
offer = Offer.objects.get(type='MONTHLY_BASIC')
user = request.user
payment_data = dict()
payment_data['response_code'] = result.responseCode
payment_data['transaction_id'] = result.transactionId
payment_data['order_number'] = result.orderNumber
payment_data['amount'] = result['lineItems'][0]['price']
payment_data['property_title_bkp'] = property.title
payment_data['backup_response'] = backup_response
Payment.objects.create(
offer=offer,
property=property,
user=user,
initiated_on=timezone.now(),
**payment_data
)
return render(request, 'subscriptions/payment_success.html', payment_data)
except TwocheckoutError as error:
return HttpResponse(error.msg)

Python places escape symbols in the Swagger web services response

I have an store procedure Oracle that returns a variable type CLOB with information in JSON format. That variable caught her in a Python
and I return it for a Web Service. The store procedure output is of this style:
{"role":"Proof_Rol","identification":"31056235002761","class":"Proof_Clase","country":"ARGENTINA","stateOrProvince":"Santa Fe","city":"Rosario","locality":"Rosario","streetName":"Brown","streetNr":"2761","x":"5438468,710153","y":"6356634,962204"}
But at the Python service exit it is shown as follows:
{"Atributos": "{\"role\":\"Proof_Rol\",\"identification\":\"31056235002761\",\"class\":\"Proof_Clase\",\"country\":\"ARGENTINA\",\"stateOrProvince\":\"Santa Fe\",\"city\":\"Rosario\",\"locality\":\"Rosario\",\"streetName\":\"Brown\",\"streetNr\":\"2761\",\"x\":\"5438468,710153\",\"y\":\"6356634,962204\"}"}
Does anyone know how to prevent the escape characters from entering that string at the exit of the service?
Part of my Python Code is:
api = Api(APP, version='1.0', title='attributes API',
description='Attibute Microservice\n'
'Conection DB:' + db_str + '\n'
'Max try:' + limite)
ns = api.namespace('attributes', description='Show descriptions of an object')
md_respuesta = api.model('attributes', {
'Attribute': fields.String(required=True, description='Attribute List')
})
class listAtriClass:
Attribute = None
#ns.route('/<string:elementId>')
#ns.response(200, 'Success')
#ns.response(404, 'Not found')
#ns.response(429, 'Too many request')
#ns.param('elementId', 'Id Element (ej:31056235002761)')
class attibuteClass(Resource):
#ns.doc('attributes')
#ns.marshal_with(md_respuesta)
def post(self, elementId):
try:
cur = database.db.cursor()
listOutput = cur.var(cx_Oracle.CLOB)
e, l = cur.callproc('attributes.get_attributes', (elementId, listOutput))
except Exception as e:
database.init()
if database.db is not None:
log.err('Reconection OK')
cur = database.db.cursor()
listOutput = cur.var(cx_Oracle.CLOB)
e, l = cur.callproc('attributes.get_attributes', (elementId, listOutput))
print(listOutput)
else:
log.err('Conection Fails')
listOutput = None
result = listAtriClass()
result.Attribute =listOutput.getvalue()
print(result.Attribute)
return result, 200
Attribute is defined to render as a fields.String but actually it should be defined to render as fields.Nested.
attribute_fields = {
"role": fields.String,
"identification": fields.String,
"class": fields.String,
# ...you get the idea.
}
md_respuesta = api.model('attributes', {
'Attribute': fields.Nested(attribute_fields)
})
Update for flask-restplus
In flask-restplus, a nested field must also register a model.
attribute_fields = api.model('fields', {
"role": fields.String,
"identification": fields.String,
"class": fields.String,
# ...you get the idea.
})
Another way is to inline attribute_fields instead of registering a separate model for it.
md_respuesta = api.model('attributes', {
'Attribute': {
'role': fields.String,
'identification': fields.String,
'class': fields.String,
# ...you get the idea.
}
})

Getting Call Status with Sinch API

After placing a call with Sinch API using a 'ttsCallout' method, I'm trying to get the status for that call. The calls goes through, I'm receiving its 'callId'. But when it comes to requesting my call's status, I receive 200 OK as a HTTP Response and empty ('null') content.
First, I though something is wrong with my code and the way I form Authorization headers (I use Python, its requests library and basic auth), but then I tried Sinch API explorer (https://www.sinch.com/dashboard/#/api) and it shows the same output to me (see the attached screenshot). Have to notice that this code worked about a month ago, and I was able to get call status.
Also, here is the code I use to communicate to Sinch API.
class CallNotification(Notification):
def __init__(self, data):
self.id = ""
self.call = self.data['call']
self.api_url = self.call['api_url']
self.auth = ("application:" + self.call['auth']['key'], self.call['auth']['secret'])
self.status = {'http': "new", 'result': "none", 'reason': "none"}
def send(self):
request_url = self.api_url + "/callouts"
request = requests.post(request_url,
auth=self.auth,
json={
'method': "ttsCallout",
'ttsCallout': {
'cli': "private",
'destination': {
'type': "number",
'endpoint': self.call['to']
},
'domain': "pstn",
'custom': "customData",
'locale': "en-US",
'text': self.call['message']
}
},
headers={'content-type': 'application/json'})
self.id = json.loads(request.content)['callId']
self.status['http'] = request.reason
return self.status['http']
def getstatus(self):
if not self.status['http'] == "new":
request_url = self.api_url + "/calls/id/" + self.id
request = requests.get(request_url,
auth=self.auth,
headers={'Content-type': 'application/json'})
try:
self.status['result'] = json.loads(request.content)['result']
except TypeError:
self.status['result'] = "Sent/Unknown"
return self.status['result']
Am I doing something wrong? What do I do to get status for a placed call?

Flask: Decorator to verify JSON and JSON Schema

I have a flask application with calls expecting JSON payload. Before each call is processed, I have a 2-step error checking process:
Assert that the payload is a valid JSON
Assert that the JSON payload complies with a specific schema
Which is implemented in the following fashion:
#app.route('/activate', methods=['POST'])
def activate():
request_id = request.__hash__()
# Assert that the payload is a valid JSON
try:
input = request.json
except BadRequest, e:
msg = "payload must be a valid json"
return jsonify({"error": msg}), 400
# JSON Schema Validation
try:
validate(request.json, app.config['activate_schema'])
except ValidationError, e:
return jsonify({"error": e.message}), 400
Since this code is duplicated over many calls, I wonder If I can elegantly move it to a decorator, something in the formof:
#validate_json
#validate_schema(schema=app.config['activate_schema'])
#app.route('/activate', methods=['POST'])
def activate():
....
The problem is that the request argument is implicit: I can refer to it within the function, but it is not a parameter to it. Therefore, I am not sure how to use it within the decorator.
How can I implement the validation checks using Python decorators?
Just use the request context global in your decorator. It is available during any request.
from functools import wraps
from flask import (
current_app,
jsonify,
request,
)
def validate_json(f):
#wraps(f)
def wrapper(*args, **kw):
try:
request.json
except BadRequest, e:
msg = "payload must be a valid json"
return jsonify({"error": msg}), 400
return f(*args, **kw)
return wrapper
def validate_schema(schema_name):
def decorator(f):
#wraps(f)
def wrapper(*args, **kw):
try:
validate(request.json, current_app.config[schema_name])
except ValidationError, e:
return jsonify({"error": e.message}), 400
return f(*args, **kw)
return wrapper
return decorator
Apply these decorators before applying the #route decorator; you want to register the wrapped function, not the original function for the route:
#app.route('/activate', methods=['POST'])
#validate_json
#validate_schema('activate_schema')
def activate():
input = request.json
now you can use #expect_json directly
For Example
from flask import Flask, jsonify, g, url_for
from flask_expects_json import expects_json
# example imports
from models import User
from orm import NotUniqueError
app = Flask(__name__)
schema = {
'type': 'object',
'properties': {
'name': {'type': 'string'},
'email': {'type': 'string'},
'password': {'type': 'string'}
},
'required': ['email', 'password']
}
#app.route('/register', methods=['POST'])
#expects_json(schema)
def register():
# if payload is invalid, request will be aborted with error code 400
# if payload is valid it is stored in g.data
# do something with your data
user = User().from_dict(g.data)
try:
user.save()
except NotUniqueError as e:
# exception path: duplicate database entry
return jsonify(dict(message=e.message)), 409
# happy path: json response
resp = jsonify(dict(auth_token=user.encode_auth_token(), user=user.to_dict()})
resp.headers['Location'] = url_for('users.get_user', user_id=user.id)
return resp, 201
or
from flask import Flask
from flask_expects_json import expects_json
app = Flask(__name__)
schema = {
'type': 'object',
'properties': {
'name': {'type': 'string', "minLength": 4, "maxLength": 15},
'mobile': {'type': 'string', "pattern": "^[1-9]{1}[0-9]{9}$"},
'email': {'type': 'string', "pattern": "[^#]+#[^#]+\.[^#]"},
'password': {'type': 'string', "pattern": "^.*(?=.{8,})(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[!##$%^&+=]).*$"}
},
'required': ['name', 'mobile', 'email', 'password']
}
#app.route('/', methods=['POST'])
#expects_json(schema)
def index():
values = request.get_json()
print(values)
return values
get more from here
A late answer, but you're probably looking for something like marshmallow (flask-marshmallow) or toastedmarshmallow.

Flask-restful: marshal complex object to json

I have a question regarding flask restful extension. I'm just started to use it and faced one problem. I have flask-sqlalchemy entities that are connected many-to-one relation and I want that restful endpoint return parent entity with all its children in json using marshaller. In my case Set contains many parameters. I looked at flask-restful docs but there wasn't any explanation how to solve this case.
Seems like I'm missing something obvious but cannot figure out any solution.
Here is my code:
# entities
class Set(db.Model):
id = db.Column("id", db.Integer, db.Sequence("set_id_seq"), primary_key=True)
title = db.Column("title", db.String(256))
parameters = db.relationship("Parameters", backref="set", cascade="all")
class Parameters(db.Model):
id = db.Column("id", db.Integer, db.Sequence("parameter_id_seq"), primary_key=True)
flag = db.Column("flag", db.String(256))
value = db.Column("value", db.String(256))
set_id = db.Column("set_id", db.Integer, db.ForeignKey("set.id"))
# marshallers
from flask.ext.restful import fields
parameter_marshaller = {
"flag": fields.String,
"value": fields.String
}
set_marshaller = {
'id': fields.String,
'title': fields.String,
'parameters': fields.List(fields.Nested(parameter_marshaller))
}
# endpoint
class SetApi(Resource):
#marshal_with(marshallers.set_marshaller)
def get(self, set_id):
entity = Set.query.get(set_id)
return entity
restful_api = Api(app)
restful_api.add_resource(SetApi, "/api/set/<int:set_id>")
Now when i call /api/set/1 I get server error:
TypeError: 'Set' object is unsubscriptable
So I need a way to correctly define set_marshaller that endpoint return this json:
{
"id": : "1",
"title": "any-title",
"parameters": [
{"flag": "any-flag", "value": "any-value" },
{"flag": "any-flag", "value": "any-value" },
.....
]
}
I appreciate any help.
I found solution to that problem myself.
After playing around with flask-restful i find out that i made few mistakes:
Firstly set_marshaller should look like this:
set_marshaller = {
'id': fields.String,
'title': fields.String,
'parameters': fields.Nested(parameter_marshaller)
}
Restless marshaller can handle case if parameter is list and marshals to json list.
Another problem was that in API Set parameters has lazy loading, so when i try to marshall Set i got KeyError: 'parameters', so I need explicitly load parameters like this:
class SetApi(Resource):
#marshal_with(marshallers.set_marshaller)
def get(self, set_id):
entity = Set.query.get(set_id)
entity.parameters # loads parameters from db
return entity
Or another option is to change model relationship:
parameters = db.relationship("Parameters", backref="set", cascade="all" lazy="joined")
This is an addition to Zygimantas's answer:
I'm using Flask-RESTful and this is a solution to the loading of the nested properties.
You can pass a callable to the marshal decorator:
class OrgsController(Resource):
#marshal_with(Organization.__json__())
def get(self):
return g.user.member.orgs
Then update the models to return the resource fields for its own entity. Nested entities will thus return the resource fields for its entity relatively.
class Organization(db.Model):
id = db.Column(db.Integer, primary_key=True)
...
#staticmethod
def __json__(group=None):
_json = {
'id': fields.String,
'login': fields.String,
'description': fields.String,
'avatar_url': fields.String,
'paid': fields.Boolean,
}
if group == 'flat':
return _json
from app.models import Repository
_json['repos'] = fields.Nested(Repository.__json__('flat'))
return _json
class Repository(db.Model):
id = db.Column(db.Integer, primary_key=True)
owner_id = db.Column(db.Integer, db.ForeignKey('organization.id'))
owner = db.relationship('Organization', lazy='select', backref=db.backref('repos', lazy='select'), foreign_keys=[owner_id])
...
#staticmethod
def __json__(group=None):
_json = {
'id': fields.String,
'name': fields.String,
'updated_at': fields.DateTime(dt_format='iso8601'),
}
if group == 'flat':
return _json
from app.models import Organization
_json['owner'] = fields.Nested(Organization.__json__('flat'))
return _json
This gives the representation I'm looking for, and honoring the lazy loading:
[
{
"avatar_url": "https://avatars.githubusercontent.com/u/18945?v=3",
"description": "lorem ipsum.",
"id": "1805",
"login": "foobar",
"paid": false,
"repos":
[
{
"id": "9813",
"name": "barbaz",
"updated_at": "2014-01-23T13:51:30"
},
{
"id": "12860",
"name": "bazbar",
"updated_at": "2015-04-17T11:06:36"
}
]
}
]
I like
1) how this approach allows me to define my resource fields per entity and it is available to all my resource routes across the app.
2) how the group argument allows me to customise the representation however I desire. I only have 'flat' here, but any logic can be written and passed down to deeper nested objects.
3) entities are only loaded as necessary.

Categories