json posts in micropython - json

I use the code below in python on a rpi to do json posts
import requests
payload = '{"msg_type":"observation_payload","msg":
{"serial":"FZtank","observations":{"8":{"d":0,"m":"JSON upload
test","t":"1543540780"}}}}'
r = requests.post('https://my.website/api/observations', data =
{'post_wrapper':payload})
print r.text
it produces
{"msg": {"remote_pi": "FZtank", "stored_ids": ["8"]}, "msg_type": "receipt"}
I figured it wouldn't be too hard to do the same thing in micropython on a pycom device
import urequests as requests
payload = '{"msg_type":"observation_payload","msg":
{"serial":"FZtank","observations":{"8":{"d":0,"m":"JSON upload
test","t":"1543540780"}}}}'
r = requests.post('https://my.website/api/observations', data =
{"post_wrapper":payload})
print(r.text)
r.close()
but it gives
Traceback (most recent call last):
File "<stdin>", line 12, in <module>
File "/flash/lib/urequests.py", line 115, in post
File "/flash/lib/urequests.py", line 100, in request
File "/flash/lib/urequests.py", line 79, in request
TypeError: object with buffer protocol required
The TypeError msg means it wants a string instead of a dictionary for the data
so I tried
import urequests as requests
payload = '{"post_wrapper":{"msg_type":"observation_payload","msg":
{"serial":"FZtank","observations":{"8":{"d":0,"m":"JSON upload
test","t":"1543540780"}}}}}'
r = requests.post('https://my.website/api/observations', data =payload)
print(r.text)
r.close()
but that gives
{"msg": {"errorText": "Malformed POST - unable to decode JSON"}, "msg_type":
"error"}
So now I'm confused. Is the problem that the website expects a dictionary & micropython can only send a string?
.
Some further detail:
I'm using https://github.com/micropython/micropython-lib/blob/master/urequests/urequests.py but with line 52 changed from ai = usocket.getaddrinfo(host, port, 0, usocket.SOCK_STREAM) to ai = usocket.getaddrinfo(host, port) because it used to throw an error msg about 4 arguments Vs 2. If I run
import urequests as requests
payload = '{"post_wrapper":{"msg_type":"observation_payload","msg":
{"serial":"FZtank","observations":{"8":{"d":0,"m":"JSON upload
test","t":"1543540780"}}}}}'
r = requests.post('https://my.website/api/observations', data=payload)
or
import urequests as requests
payload = {"post_wrapper":{"msg_type":"observation_payload","msg":
{"serial":"FZtank","observations":{"8":{"d":0,"m":"JSON upload
test","t":"1543540780"}}}}}
r = requests.post('https://my.website/api/observations', json=payload)
or
import urequests as requests
payload = {"msg_type":"observation_payload","msg":
{"serial":"FZtank","observations":{"8":{"d":0,"m":"JSON upload
test","t":"1543540780"}}}}
r = requests.post('https://my.website/api/observations', json=
{"post_wrapper":payload})
I get the same result
{"msg": {"errorText": "Malformed POST - unable to decode JSON"}, "msg_type":
"error"}

I was stuck with the same issue here while trying to send push notifications through pushed.co.
I was using data=payload on urequests and getting this TypeError: object with buffer protocol required error. All worked well when I changed to json=payload following #stijn suggestion on his answer.
Steps I did:
on micropython REPL install urequests
import upip
upip.install('micropython-urequests')
for my example the code is
import urequests as requests
payload = {
"app_key": "your_app_key",
"app_secret": "your_app_secret",
"target_type": "your_target_type",
"content": "Hello from micropython",
}
r = requests.post("https://api.pushed.co/1/push", json=payload)
Probably any other url will do fine too.

I was able to make posts request on micropython dumping data into a json:
import json
import urequests
data = {"mykey": 'myvalue'}
urequests.post(url, data=json.dumps(data))

Related

Unable to parse JSON from Read The Docs API response

I'm sending following REST request to the Read The Docs API:
GET /api/v3/projects/<my_project>/redirects/
I receive JSON in the response body, but I'm unable to parse the JSON into something useful.
I'm using a Python3 program:
import requests
import json
URL = 'https://readthedocs.com/api/v3/projects/<my_project>/redirects/'
TOKEN = "<my_access_token>"
HEADERS = {'Authorization': f'token {TOKEN}'}
response = requests.get(URL, headers=HEADERS, stream=True)
print(f'Response is {response.json()}')
This program returns something that looks like JSON.
If I run it using the following command line:
python3 rtd_get_redirects.py | python3 -m json.tool
I receive the following error:
Expecting value: line 1 column 1 (char 0)
The entire response is large, so I'll just paste this snippet from the beginning:
{'count': 521, 'next': 'https://readthedocs.com/api/v3/projects/signalfx-product-docs/redirects/?limit=10&offset=10', 'previous': None, 'results': [{'_links': {'_self': 'https://readthedocs.com/api/v3/projects/signalfx-product-docs/redirects/6612/', 'project': 'https://readthedocs.com/api/v3/projects/signalfx-product-docs/'}, 'created': '2022-07-20T18:08:52.133088Z',

'NoneType' object has no attribute 'read' when reading from JSON file

I am making a script for a school project that requires that I receive a JSON file that tells me if a license plate is visible in a picture. Right now the code sends a POST with an image to an API that then gives me a JSON in return, that JSON data is sent to the file "lastResponse.json."
The code that is giving out the error
with open('lastResponse.json', 'r+') as fp:
f = json.dump(r.json(), fp, sort_keys=True, indent=4) # Where the response data is sent to the JSON
data = json.load(f) # Line that triggers the error
print(data["results"]) # Debug code
print("------------------") # Debug code
print(data) # Debug code
# This statement just checks if a license plate is visible
if data["results"]["plate"] is None:
print("No car detected!")
else:
print("Car with plate number '" + data["results"]["plate"] + "' has been detected")
The Error
Traceback (most recent call last):
File "DetectionFinished.py", line 19, in <module>
data = json.load(f)
File "/usr/lib/python3.7/json/__init__.py", line 293, in load
return loads(fp.read(),
AttributeError: 'NoneType' object has no attribute 'read'
I am not very experienced in Python so I would appreciate explanations!
It turns out, after rereading the API's documentation and using their examples I was able to fix my issues
import requests
from pprint import pprint
regions = ['gb', 'it']
with open('/path/to/car.jpg', 'rb') as fp:
response = requests.post(
'https://api.platerecognizer.com/v1/plate-reader/',
data=dict(regions=regions), # Optional
files=dict(upload=fp),
headers={'Authorization': 'Token API_TOKEN'})
pprint(response.json())

Using the reults of multiple for loops to post a single json response

Okay, so this is a loaded question but and I'm sure theres an easy method to use here, but I'm stuck.
Long story short, I am tasked with creating a function in python (to be run an AWS lambda) which can perform acceptance tests on a series of URL's using python-requests. These requests will be used to assert the HTTP response codes and a custom HTTP header identifying if an haproxy backend is correct.
The URL's themselves will be maintained in a yaml document which will be converted to a dict in python and passed to a for loop which will use python requests to HTTP GET the response code and header of the URL.
The issue I am having is getting a single body object to return the results of multiple for loops.
I have tried to find similar use cases but cannot
import requests
import json
import yaml
def acc_tests():
with open("test.yaml", 'r') as stream:
testurls = yaml.safe_load(stream)
results = {}
# endpoint/path 1
for url in testurls["health endpoints"]:
r = requests.get(url, params="none")
stat = r.status_code
result = json.dumps(print(url, stat))
results = json.dumps(result)
# endpoint path with headers
for url in testurls["xtvapi"]:
headers = {'H': 'xtvapi.cloudtv.comcast.net'}
r = requests.get(url, headers=headers, params="none")
stat = r.status_code
head = r.headers["X-FINITY-TANGO-BACKEND"]
result = json.dumps((url, stat, head))
results = json.dumps(result)
return {
'statusCode': 200,
'body': json.dumps(results)
}
acc_tests()
YAML file:
health endpoints:
- https://xfinityapi-tango-production-aws-us-east-1-active.r53.aae.comcast.net/tango-health/
- https://xfinityapi-tango-production-aws-us-east-1-active.r53.aae.comcast.net/
- https://xfinityapi-tango-production-aws-us-east-2-active.r53.aae.comcast.net/tango-health/
- https://xfinityapi-tango-production-aws-us-east-2-active.r53.aae.comcast.net/
- https://xfinityapi-tango-production-aws-us-west-2-active.r53.aae.comcast.net/tango-health/
- https://xfinityapi-tango-production-aws-us-west-2-active.r53.aae.comcast.net/
xtvapi:
- https://xfinityapi-tango-production-aws-us-east-1-active.r53.aae.comcast.net/
- https://xfinityapi-tango-production-aws-us-east-2-active.r53.aae.comcast.net/
- https://xfinityapi-tango-production-aws-us-west-2-active.r53.aae.comcast.net/
What I think is happening is that both for loops are running one after another, but the value of results is empty, but I'm not sure what to do in order to update/append the results dict with the results of each loop.
Thanks folks. I ended up solving this by creating a dict with immutable keys for each test type and then using append to add the results to a nested list within the dict.
Here is the "working" code as it is in the AWS Lambda function:
from botocore.vendored import requests
import json
import yaml
def acc_tests(event, context):
with open("test.yaml", 'r') as stream:
testurls = yaml.safe_load(stream)
results = {'tango-health': [], 'xtvapi': []}
# Tango Health
for url in testurls["health endpoints"]:
r = requests.get(url, params="none")
result = url, r.status_code
assert r.status_code == 200
results["tango-health"].append(result)
# xtvapi default/cloudtv
for url in testurls["xtvapi"]:
headers = {'H': 'xtvapi.cloudtv.comcast.net'}
r = requests.get(url, headers=headers, params="none")
result = url, r.status_code, r.headers["X-FINITY-TANGO-BACKEND"]
assert r.status_code == 200
assert r.headers["X-FINITY-TANGO-BACKEND"] == "tango-big"
results["xtvapi"].append(result)
resbody = json.dumps(results)
return {
'statusCode': 200,
'body': resbody
}

Different ways of reading data from s3 bucket using boto3

I'm trying to read some json file from s3 bucket and then trying to post the data into rds and redshift(using a post api developed/intended for that).
I approached to do this in 2 ways using boto3. Below are those:
1st way:
import boto3
import json
import requests
url = 'xxxx.us-east-1.elb.amazonaws.com/v1'
headers = {'content-type': 'application/json', 'Host': 'development.my.dns.com'}
endpoint = url+'/my/post/endpoint'
s3_client = boto3.client("s3")
fileObj = s3_client.get_object(Bucket='my-bucket-name', Key='my-key-name'])
data = fileObj['Body'].read().decode('utf-8')
with requests.request('POST', endpoint, data=data, headers=headers, auth=(username, pwd), verify=False, stream=True) as r:
print("Status Code:",r.status_code)
2nd way:
import boto3
import json
import requests
url = 'xxxx.us-east-1.elb.amazonaws.com/v1'
headers = {'content-type': 'application/json', 'Host': 'development.my.dns.com'}
endpoint = url+'/my/post/endpoint'
s3_res = boto3.resource('s3')
contentObj = s3_res.Object('my-bucket-name', 'my-key-name')
fileContent = contentObj.get()['Body'].read().decode('utf-8')
data = json.dumps(json.loads(fileContent))
with requests.request('POST', endpoint, data=data, headers=headers, auth=(username, pwd), verify=False, stream=True) as r:
print("Status Code:",r.status_code)
Basically everything is same(url, endpoint, headers, requests.request) and type(data) is <class 'str'> in both the approaches. But the status codes are always different.
The 2nd way gives Status Code: 200. But the 1st way gives Status Code: 413 or Status Code: 502 randomly
Can any one please explain why is it this way? what's different in the above two approaches? I'm trying to understand what's going on when boto3 read() the data differently.

How to pass urls from CSV list into a python GET request

I have a CSV file, which contains a list of Google extension IDs.
I'm writing a code that will read the extension IDs, add the webstore url, then perform a basic get request:
import csv
import requests
with open('small.csv', 'rb') as f:
reader = csv.reader(f)
for row in reader:
urls = "https://chrome.google.com/webstore/detail/" + row[0]
print urls
r = requests.get([urls])
Running this code results in the following Traceback:
Traceback (most recent call last):
File "C:\Users\tom\Dropbox\Python\panya\test.py", line 9, in <module>
r = requests.get([urls])
File "C:\Python27\lib\site-packages\requests\api.py", line 69, in get
return request('get', url, params=params, **kwargs)
File "C:\Python27\lib\site-packages\requests\api.py", line 50, in request
response = session.request(method=method, url=url, **kwargs)
File "C:\Python27\lib\site-packages\requests\sessions.py", line 465, in request
resp = self.send(prep, **send_kwargs)
File "C:\Python27\lib\site-packages\requests\sessions.py", line 567, in send
adapter = self.get_adapter(url=request.url)
File "C:\Python27\lib\site-packages\requests\sessions.py", line 641, in get_adapter
raise InvalidSchema("No connection adapters were found for '%s'" % url)
InvalidSchema: No connection adapters were found for '['https://chrome.google.com/webstore/detail/blpcfgokakmgnkcojhhkbfbldkacnbeo']'
How can revise the code, so that it would accept the urls in the list, and make the GET request?
requests.get expects a string, but you're creating and passing a list [urls]
r = requests.get([urls])
Change it to just
r = requests.get(urls)
and it should work.