Unable to make dns-over-https with cloudflare and python requests - json

I'm trying to write a quick script that could do dns lookups using the new 1.1.1.1 DNS over HTTPS public DNS server from CloudFlare.
Looking at their docs here https://developers.cloudflare.com/1.1.1.1/dns-over-https/json-format/ I'm not sure what I'm doing wrong and why I'm getting a 415 status code (415 Unsupported content type).
Here is my script:
#!/usr/bin/env python
import requests
import json
from pprint import pprint
url = 'https://cloudflare-dns.com/dns-query'
client = requests.session()
json1 = {'name': 'example.com','type': 'A'}
ae = client.get(url, headers = {'Content-Type':'application/dns-json'}, json = json1)
print ae.raise_for_status()
print ae.status_code
print ae.json()
client.close()
Here is the output:
raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 415 Client Error: Unsupported Media Type for url: https://cloudflare-dns.com/dns-query
and for the json response (expected I believe):
raise ValueError("No JSON object could be decoded")
ValueError: No JSON object could be decoded
Using curl this works perfectly fine.
Many thanks

You should not set a JSON request at all. The response uses JSON.
Put the application/dns-json value in a ct parameter:
JSON formatted queries are sent using a GET request. When making requests using GET, the DNS query is encoded into the URL. An additional URL parameter of ‘ct’ should indicate the MIME type (application/dns-json).
A GET request never has a body, so don't try to send JSON:
params = {
'name': 'example.com',
'type': 'A',
'ct': 'application/dns-json',
}
ae = client.get(url, params=params)
Demo:
>>> import requests
>>> url = 'https://cloudflare-dns.com/dns-query'
>>> client = requests.session()
>>> params = {
... 'name': 'example.com',
... 'type': 'A',
... 'ct': 'application/dns-json',
... }
>>> ae = client.get(url, params=params)
>>> ae.status_code
200
>>> from pprint import pprint
>>> pprint(ae.json())
{'AD': True,
'Answer': [{'TTL': 2560,
'data': '93.184.216.34',
'name': 'example.com.',
'type': 1}],
'CD': False,
'Question': [{'name': 'example.com.', 'type': 1}],
'RA': True,
'RD': True,
'Status': 0,
'TC': False}

Related

voice call ends right away, nexmo hangs up within a second

I have created an application in the Nexmo dashboard with an event url and answer url. I run the following code I got from Nexmo´s GitHub:
client = nexmo.Client(key=api_key, secret=api_secret, application_id=application_key, private_key=private_key)
response = client.create_call({
'to': [{'type': 'phone', 'number': 'call_to_number'}],
'from': {'type': 'phone', 'number': 'call_from_number'},
'answer_url': ['http://my_event_url']
})
And the phonenumber is called, but nexmo hangs up right away (within a second without saying anything).
On my server I see Nexmo calls the answer url (with the ncco)
what I do when the answer url is called:
import nexmo
import json
from django.http import JsonResponse
#csrf_exempt
def answer(request):
ncco = [{
"action": "talk",
"voiceName": "Amy",
"text": "Thank you for calling Nexmo. Please leave your message after the tone."
}]
d = json.dumps(ncco)
j = is_json(d)
if j:
MyObject.objects.create(message="valid")
else:
MyObject.objects.create(message=user_ip + "json error")
return JsonResponse(d, status=200, safe=False)
def is_json(myjson):
try:
json_object = json.loads(myjson)
except ValueError:
return False
return True
This is what I do when my event_url is called:
#csrf_exempt
def event(request):
d = json.dumps(request.POST)
DataReceived.objects.create(answer=d)
data = {"ok": True}
return JsonResponse(data, status=200)
The event url is called 5 times by Nexmo but the dictionaries are always empty.
`I think you might be "double JSON dumping" your NCCO,
you create the ncco as a python dict, then turn that into a json string with d = json.dumps(ncco) and then you are calling JsonResponse on it, try passing the ncco object to JsonResponse
return JsonResponse(ncco, status=200, safe=False)

How to correctly produce and consume UTF-8 json in Python3? (with flask and requests)

I've made some basic code to explain my question:
produceUTF8.py (replies 'ít wórked!' with unicode characters) - you run this first
# -*- coding: utf-8 -*-
import os
from sys import argv
from flask import Flask, request, Response, jsonify
import json
app = Flask(__name__)
app.config['JSON_AS_ASCII'] = False # contribution from Erdem
#app.route('/reply', methods=['POST'])
def reply():
"""Fetch a reply
"""
print("DEBUG entered")
params = request.json
print("DEBUG entered2")
if not params:
return jsonify({
'status': 'error',
'error': 'Request must be of the application/json type!',
})
reply = "ít wórked!"
# Send the response.
return jsonify({
'status': 'ok',
'reply': reply,
})
if __name__ == '__main__':
app.run(host='0.0.0.0', debug=True)
consumeUTF8.py (posts the message 'óíá' to get answer from producer)
# -*- coding: utf-8 -*-
import requests
HEADERS = {'Content-Type': 'application/json; charset=utf-8',}
DATA = '{"message": "óíá"}'
my_request = requests.post('http://localhost:5000/reply', headers=HEADERS, data=DATA)
response = my_request.json()['reply']
In my producer I am getting "Bad Request (400)" and in the consumer "json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)."
It seems to be a problem in params = request.json as seen in the debug prints. What is the recommended approach here?
Thanks!
You can fix the way you make the request by encoding the data object :
my_request = requests.post('http://localhost:5000/reply',
headers=HEADERS,
data=DATA.encode('utf-8'))
#>>> ít wórked with óíá!
If you add a try/except statement in the app, it returns :
try:
params = request.json
except Exception as e:
params = None
print(e)
400 Bad Request: Failed to decode JSON object: 'utf-8' codec can't decode byte 0xf3 in position 14: invalid continuation byte
You could use this pattern to assign a default value for param
I have not tried the code but may be setting
app.config['JSON_AS_ASCII'] = False
may help.

Validating trello board API responses in Python unittest

I am writing a unittest that queries the trello board API and want to assert that a particular card exists.
The first attempt was using the /1/boards/[board_id]/lists rewuest which gives results like:
[{'cards': [
{'id': 'id1', 'name': 'item1'},
{'id': 'id2', 'name': 'item2'},
{'id': 'id3', 'name': 'item3'},
{'id': 'id4', 'name': 'item4'},
{'id': 'id5', 'name': 'item5'},
{'id': 'id6', 'name': 'item6'}],
'id': 'id7',
'name': 'ABC'},
{'cards': [], 'id': 'id8', 'name': 'DEF'},
{'cards': [], 'id': 'id9', 'name': 'GHI'}]
I want to assert that 'item6' is indeed in the above mentioned list. Loading the json and using assertTrue, like this:
element = [item for item in json_data if item['name'] == "item6"]
self.assertTrue(element)
but I receive an error: 'TypeError: the JSON object must be str, bytes or bytearray, not 'list'.
Then discovered using the /1/boards/[board_id]/cards request gives a plain list of cards:
[
{'id': 'id1', 'name': 'item1'},
{'id': 'id2', 'name': 'item2'},
...
]
How should I write this unittest assertion?
The neatest option is to create a class that will equal the dict for the card you want to ensure is there, then use that in an assertion. For your example, with a list of cards returned over the api:
cards = board.get_cards()
self.assertIn(Card(name="item6"), cards)
Here's a reasonable implementation for the Card() helper class, it may look a little complex but is mostly straight forward:
class Card(object):
"""Class that matches a dict with card details from json api response."""
def __init__(self, name):
self.name = name
def __eq__(self, other):
if isinstance(other, dict):
return other.get("name", None) == self.name
return NotImplemented
def __repr__(self):
return "{}({!r}, {!r})".format(
self.__class__.__name__, self.key, self.value)
You could add more fields to validate as needed.
One question worth touching on at this point is whether the unit test should be making real api queries. Generally a unit test would have test data to just focus on the function you control, but perhaps this is really an integration test for your trello deployment using the unittest module?
import unittest
from urllib.request import urlopen
import json
class Basic(unittest.TestCase):
url = 'https://api.trello.com/1/boards/[my_id]/cards?fields=id,name,idList,url&key=[my_key]&token=[my_token]'
response = urlopen(url)
resp = response.read()
json_ob = json.loads(resp)
el_list = [item for item in json_ob if item['name'] == 'card6']
def testBasic(self):
self.assertTrue(self.el_list)
if __name__ == '__main__':
unittest.main()
So what I did wrong: I focused too much on the list itself which I got after using the following code:
import requests
from pprint import pprint
import json
url = "https://api.trello.com/1/boards/[my_id]/lists"
params = {"cards":"open","card_fields":"name","fields":"name","key":"[my_key]","token":"[my_token]"}
response = requests.get(url=url, params=params)
pprint(response.json())

How to get JSON data in an Odoo controller using type='json'?

A few days ago I did a similar question here: How to get JSON data in an Odoo controller?
But now, I need to create a controller which receives only JSON data. So, I am doing the request from a Python console, this way:
import requests
import json
url = 'http://localhost:8069/odoo/test'
headers = {'Content-Type': 'application/json'}
data = {
'name': 'Jane',
'email': 'jane.doe#gmail.com',
}
data_json = json.dumps(data)
r = requests.post(url=url, data=data_json, headers=headers)
I have created a controller which listens to http://localhost:8069/odoo/test, this way:
import openerp.http as http
from openerp.http import Response
import logging
_logger = logging.getLogger(__name__)
class WebFormController(http.Controller):
#http.route('/odoo/test', type='json',
auth='public', methods=['POST'], website=True)
def index(self, **args):
_logger.info('CONNECTION SUCCESSFUL')
_logger.info(args)
name = args.get('name', False)
email = args.get('email', False)
_logger.info(name)
_logger.info(email)
if not name:
Response.status = '400 Bad Request'
return '{"response": "OK"}'
The problem is that I am receiving an empty JSON in the controller. I can read CONNECTION SUCCESFUL in the log, with no error, but when I show args, I get {}, and obviously due to that, False when writing name and email.
If I pass the data as a Python dictionary or as a string, I get the following error:
Invalid JSON data: 'name=Jane&email=jane.doe%40gmail.com' or
Invalid JSON data: "{'name': 'Jane', 'email': 'jane.doe#gmail.com'}" respectively.
If I modify type='json' and I write type='http' instead, I get the following error:
Function declared as capable of handling request of type 'http' but called with a request of type 'json'.
I have read that may be this could be solved if the request is sent using the parameter json instead of data, this way:
r = requests.post(url=url, json=data_json, headers=headers)
Unfortunately, the server which is going to make the request has an old operating system which cannot update the python-requests package, so I cannot use json parameter since it did not exist at the version installed in that server.
Please, can anyone help me? I need get JSON data in the controller, not a string neither Python dictionaries.
You have just forgotten to put your data inside the params keywords:
Use this correct syntax :
data = {"params": dict(key="value")}
data = {
"params": {
"name":"prakashsharma",
"email":"prakashsharmacs24#gmail.com",
"phone":"+917859884833"
}
}
Please don't forget to use json.dumps(data) and 'Content-Type': 'application/json' while requesting a resource in json format.
I am damn sure your issue will be solved after using this one my friend... cheers :)!!
You can use below format for a POST request
{
"params" : {
"name" : "Order/1/18",
"session_id" : 1,
"customer_count" : 2,
"partner_id" : 9,
"lines": [
{
"product_id": 37,
"qty" : 2,
"price_unit" : 2,
"discount" : 10
}
],
"pos_reference" : 2,
"note" : "This is a test note"
}
}
Content type must be application/json
How odoo route will handle request ?
Route will help creating a POS order in odoo [POST]
#http.route(['/api/v1/resources/<string:api_key>/pos_order'],
auth="public",
website=False,
type="json",
csrf=False,
methods = ['POST'])
def create_update_pos_order(self, api_key=None, **kwargs):
print(kwargs.get('name')) -> Order/1/18
God Bless Forvas::
But for more clearity:
if you want to test through cURL:
curl -i -X POST -H "Content-Type: application/json" -d '{"params": {"name":"prakashsharma","email":"prakashsharmacs24#gmail.com","phone":"+917859884833"}}' 'http://localhost:8069/web/yourlistoner/'
if you want to test through python request:
import requests
headers = {
'Content-Type': 'application/json',
}
data = '{"params": {"name":"prakashsharma","email":"prakashsharmacs24#gmail.com","phone":"+917859884833"}}'
requests.post('http://localhost:8069/web/yourlistoner/', headers=headers, data=data)
the function in odoo will be something like
from odoo import http
import json
class YourClass(http.Controller):
#http.route('/web/yourlistoner/', type='json', auth="none", methods=['POST'],cors="*", csrf=False)
def listoner(self, **kw):
print http.request.params
print "lllllllllllllllllllll"
return json.dumps({"result":"Success"})

invalid json format of facebook graph api

I am using graph api to fetch ad audiences information, when i tried https://graph.facebook.com/act_adaccountid/customaudiences?fields=
but when i tried it through a program i am getting invalid json format
from urlib2 import urlopen
from simplejson import loads
x = loads(urlopen('https://graph.facebook.com/act_adaccountid/customaudiences?fields=<comma_separate_list_of_fields?access_token='XXXXXXXXXX').read())
output:
{'paging': {'cursors': {'after': 'NjAxMDE5ODE5NjgxMw==', 'before': 'NjAxNTAzNDkwOTAxMw=='}}, 'data': [{'account_id': 1377346239145180L, 'id': '6015034909013'}, {'account_id': 1377346239145180L, 'id': '6015034901213'}, {'account_id': 1377346239145180L, 'id': '6015034901013'}, {'account_id': 1377346239145180L, 'id': '6015034900413'}
{'data': []}
expected output:
http://pastebin.com/5265tJ8w