Flask Restful PUT update list with object - python

I want to use Flask Restful to update a list with an object using a PUT request.
The resulting JSON should look like:
{"EmployeeID":"12345", "firstname":"Joe","lastname":"Bloggs","SupervisorName":"Name","SupervisorID":"2468","role":"Role","active":"True","hours":["{'date':'01/01/2017','qty':'3','project':'Project 1'}"]"}
The Hours field in the JSON is a list. The aim is to append an object to the list on each PUT request.
The Parser for hours is:
parser.add_argument('hours', action='append')
The Put method code is:
def put(self, EmployeeID=None):
data = parser.parse_args()
if not data:
data = {'ERROR': 'No Data'}
return jsonify(data)
else:
if EmployeeID:
if mongo.db.employee.find_one({'EmployeeID': EmployeeID}):
mongo.db.employee.update_one({'EmployeeID': EmployeeID}, {set: {"hours": data.get('hours')}})
return {'response': 'Employee:'+str(EmployeeID)+' updated'}
else:
return {'Error': 'employee ' + str(EmployeeID) + ' not found'}
else:
return {'response': 'Employee ID missing'}
Is the update_one method the right one to use here?
using curl PUT request :
curl -H "Content-type: application/json" -X PUT -d '{"EmployeeID":"1234",...,"hours":{'time':'','qty':'3','project':'Project 1'}}' http://127.0.0.1:5000/api/people/1234
Gave the error:
{
"message": "Failed to decode JSON object: Expecting property name enclosed in double quotes: line 1 column 168 (char 167)"
}
But When I add the quotations into the request it return an error:
{
"message": "Failed to decode JSON object: Unterminated string starting at: line 1 column 167 (char 166)"
}
I can't figure out whether there is an issue with the requests or with the actual Flask-Restful code.
What is the correct way to go about updating a Mongo document list with an object?

I don't have much experience with Flask, but from the error messages you've posted, it looks like the JSON in the request cannot be correctly decoded by Flask. I believe this is because you are using single quotes in your JSON string and are wrapping the entire JSON with yet another set of single quotes.
Try to just use double quotes in your JSON string and wrap the entire JSON string in single quotes like so:
curl -H "Content-type: application/json" -X PUT -d '{"EmployeeID":"1234","hours":{"time":"","qty":"3","project":"Project 1"}}' http://127.0.0.1:5000/api/people/1234
I think that should solve the issue of not being able to decode JSON form the request.

If anyone runs into this issue I finally got there.
I had to change the RequestParser:
parser.add_argument('hours')
Removing the action='append'
Then using the mongo update:
mongo.db.employee.update_one({'EmployeeID': EmployeeID}, {'$addToSet': {"hours": data.get('hours')}})

Related

How to handle API responses(JSON) containing \x00 (or \u0000) in its data, and store the data in Postgres using django models?

I'm making an api call, and trying to store its response in Postgres using Django models.
Here's what I have been doing:
response = requests.post(url='some.url.com', data=json.dumps(data), headers={'some': 'header'})
response_data = json.loads(response.content.decode('utf-8'))
#handler is a object of a model
handler.api_response = response_data
handler.save()
But this used to fail, when my json had fields like 'field_name': '\x00\x00\x00\x00\x00'. It used to give following error :
DataError at /api/booking/reprice/
unsupported Unicode escape sequence
LINE 1: ... NULL, "api_status" = 0, "api_response" = '{"errorRe...
^
DETAIL: \u0000 cannot be converted to text.
CONTEXT: JSON data, line 1: ..."hoursConfirmed": 0, "field_name":...
How i tried to resolve this is by using the following:
response = requests.post(url='some.url.com', data=json.dumps(data), headers={'some': 'header'})
response_data = json.loads(response.content.decode('utf-8'))
#handler is a object of a model
handler.api_response = json.loads(json.dumps(response_data).encode("unicode-escape").decode())
handler.save()
The initial issue was solved then. But recently, when i got a field with value 'field2_name': 'Hey "Whats up"'. This thing failed by giving error:
json.decoder.JSONDecodeError: Expecting ',' delimiter: line 1 column 143 (char 142)
Probably because json.loads() got confused with " inside the value as an enclosing " and not an escaped ".
Now, If i print the initial response just after json.loads(response.content.decode('utf-8')) statement, it shows the field as \x00\x00\x00\x00\x00.
But output of following code:
response = requests.post(url='some.url.com', data=json.dumps(data), headers={'some': 'header'})
response_data = json.loads(response.content.decode('utf-8'))
print(json.dumps(response_data))
This shows the field as \\u0000\\u0000\\u0000\\u0000\\u0000.
How does \x00 change to \\u0000
And how do i save this field into postgres tables ?
This is what i could think of.
json.loads(json.dumps(response_data).replace('\\u0000',''))
To add this statement before saving to postgres.
Is there a better way ?
Is the code response_data = json.loads(response.content.decode('utf-8')) wrong ? Or causing not to escape that particular character ?
FWIW, I recently ran into this and your proposed solution also worked for me. I think it's probably the simplest way to deal with it. Apparently this is the only character that can't go into a Postgres JSON column.
json.loads(json.dumps(response_data).replace('\\u0000',''))

JSON response from swagger-server received in single quotes, robot-framework validation fails as json.loads fails

I am trying to validate my swagger-server code via robot-framework.The server handler is returning a dictionary (or json object?). The robot-framework tries to validate the response, but gives this error.
failed: JSONDecodeError: Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
The problem is response has single-quotes instead of double-quotes (or the response is not identified as a json) so json.loads fails.
I tried adding content-type and mimetype headers as 'application/json'. Tried flask's jsonify.
Here is the part of the code that returns the response.
def grants_post(project, GrantRequest, Accept=None, Content_Type=None, Authorization=None):
# code that creates a large dictionary called rsp.
grant_uri_id = str(uuid.uuid4())
AsyncGrantResponses[grant_uri_id] = rsp
print(rsp['output'])
return rsp['output'], 201, {'location': "sol003/grant/" + project + "/grant/v1/grants/" + grant_uri_id , 'mimetype' : "application/json" }
Here is robot-framework results. After schema validation it does json.loads. Used '.......' in b/w since the data is large.
Create a new Grant - Synchronous mode
| FAIL | Evaluating expression json.loads('''{'extVirtualLinks':
[{'vimConnectionId': 'vimAccount',
'extCps':........................... 'id':
'718bb9a2-37dd-4238-a393-7936a8b53086'}''') failed: JSONDecodeError:
Expecting property name enclosed in double quotes: line 1 column 2
(char 1)
I think the expected output should have double quotes instead of single quotes in the data.
The issue was with the robot test suite code, I changed this line,
${json} = evaluate json.loads('''${result}''') json
to
${json} = evaluate json.loads(json.dumps(${result})) json
Now it validates fine.

Python Flask - Passing hash input in API call

I am a beginner in making API. I have followed the blog https://blog.miguelgrinberg.com/post/designing-a-restful-api-with-python-and-flask and able to create the Get - Post API method.
I am using Flask to making Rest API. Please see the code below in which I want to take question as input in API and return an answer in JSON format by making my code as an API.
app = Flask(__name__)
#app.route('/match/api/v1', methods = ['POST'])
def my_form_post():
if not request.json or not 'question' in request.json:
abort(400)
input_text_p = request.json['question'] # access input from curl request
reference_data = request.json['data'] # to access data field from the API request
path = 'airtel_faq.xlsx'
question_list, answer_list = read_excel_file(path) # reading some reference data from an excel file
input_text = input_text_p.translate(None, string.punctuation) # remove punctuation
final_answer = find_similarity(input_text, answer_list, question_list)
print "Final Answer is : ", final_answer
values = [
{'id' : 1,
'answer' : final_answer # answer I want in JSON
'done' : False
}
]
return jsonify({'values': values}), 201
if __name__ == '__main__':
app.run(debug = True)
I am trying to pass hash input for data tag in the request to API. I am not aware how to do this. The curl request I am making is giving error:
Failed to decode JSON object: Expecting ',' delimiter or '}': line 1 column 80 (char 79)
The curl request I am making :
curl -i -H "Content-Type: application/json" -X POST -d '{"question":"Hi","cutoff":"0.3", "data":"{"q":"hello whats going on","a":"I am fine","type":"1"}"}' http://localhost:5000/match/api
Please let me know how to do this. Do I have to include anything in my script. Or is there a way to pass the JSON file in API call by giving path to the file.
In order to parse the JSON request, it needs to be valid JSON. Try this request:
curl -i -H "Content-Type: application/json" -X POST -d '{"question": "Hi", "cutoff": "0.3", "data": {"q": "hello whats going on", "a": "I am fine", "type": "1"}}' http://localhost:5000/match/api

Creating ticket with attachment in Freshdesk

I want to create a ticket with attachment in freshdesk api. I am able to create a ticket without attachment. This is my sample code:
post_dict = {
'helpdesk_ticket': {
'description': "Testing Code sample 3",
'subject': "example7",
'email': "example7#example.com",
'priority': 2,
'status': 2,
},
}
headers = {'Content-Type': 'application/json'}
r = requests.post(FRESHDESK_URL + '/helpdesk/tickets.json',
auth=(FRESHDESK_API_KEY, "X"),
headers=headers,
data=json.dumps(post_dict),
)
raw_input(r.status_code)
raw_input(r.content)
This is for just creating ticket in Freshdesk. Now using the same post_dict i would like to create tickets with attachments. Any suggestions about how I can achieve this using this json request method or any other methods are welcome.
Creating a ticket with an attachment requires a multi-part form submission. Unfortunately that means that the request is very different to a simple request without an attachment.
Each field needs to be written to the request as a separate form with a 'boundary' before it and a character return after it.
Then, each attachment should be written to the request, again with a boundary before the attachment is written and a character return added after.
At the end of the response, a final boundary must be written. This is the same as the string used for the boundary but also includes 2 dashes ( -- ) before and after the boundary to signify it as final. Without the final boundary, FreshDesk gives a 500 internal server error since something changed in their api a few weeks back (it used to accept a non-final boundary at the end).
For Send attachment, the Content-Type must be in multipart/form-data.The sample cURL is may helps you.
curl -v -u yourmail#gmail.com:yourpassword -F
"attachments[]=#/path/to/attachment1.ext" -F
"attachments[]=#/path/to/attachment2.ext" -F "email=example#example.com" -F
"subject=Ticket Title" -F "description=this is a sample ticket" -X POST
'https://yoururl.freshdesk.com/api/v2/tickets'

Python cherrypy, get the query_string parsed

I'm trying to get the query and the data (GET params and the POST params) from a request
curl --data "foo=bar&hello=world" "http://localhost:8080/mypath?orange=5&apple=8"
.
query_string = cherrypy.request.query_string # 'orange=5&apple=8'
post_data = cherrypy.request.body.params # {'foo': 'bar', 'hello': 'world'}
The post_data is correctly dict formed.
how can i parse the query_string like the post_data?
I was reading at cherrypy doc, and I was seeing this:
process_query_string()
Parse the query string into Python structures. (Core)
But this is not working, cherrypy.request.process_query_string() ais returning None
Any ideas?
CherryPy uses cherrypy.lib.httputil.parse_query_string for populating request.params with GET parameters, you can use it like this:
from cherrypy.lib.httputil import parse_query_string
parse_query_string(cherrypy.request.query_string)
Which returns the dict with parsed query string parameters.
query = urllib.parse.parse_qs(cherrypy.request.query_string, True)

Categories

Resources