How to make a simple Python REST server and client? - python

I'm attempting to make the simplest possible REST API server and client, with both the server and client being written in Python and running on the same computer.
From this tutorial:
https://blog.miguelgrinberg.com/post/designing-a-restful-api-with-python-and-flask
I'm using this for the server:
# server.py
from flask import Flask, jsonify
app = Flask(__name__)
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.route('/todo/api/v1.0/tasks', methods=['GET'])
def get_tasks():
return jsonify({'tasks': tasks})
if __name__ == '__main__':
app.run(debug=True)
If I run this from the command line:
curl -i http://localhost:5000/todo/api/v1.0/tasks
I get this:
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 317
Server: Werkzeug/0.16.0 Python/3.6.9
Date: Thu, 05 Mar 2020 02:45:59 GMT
{
"tasks": [
{
"description": "Milk, Cheese, Pizza, Fruit, Tylenol",
"done": false,
"id": 1,
"title": "Buy groceries"
},
{
"description": "Need to find a good Python tutorial on the web",
"done": false,
"id": 2,
"title": "Learn Python"
}
]
}
Great, now my question is, how can I write a Python script using requests to obtain the same information?
I suspect this is the proper idea:
# client.py
import requests
url = 'http://todo/api/v1.0/tasks'
response = requests.get(url,
# what goes here ??
)
print('response = ' + str(response))
However as you can see from my comment, I'm not sure how to set up the parameters for requests.get.
I attempted to use this SO post:
Making a request to a RESTful API using python
as a guideline however it's not clear how to adjust the formatting per the message change.
Can provide a brief description of how to set up params to pass into requests.get and suggest the necessary changes to get the client example above working? Thanks!
--- Edit ---
Something else I can mention is that I got the client to work using Postman pretty easily, I'm just not sure how to set up the syntax in Python:
--- Edit ---
Based on icedwater's response below, this is complete, working code for the client:
# client.py
import requests
import json
url = 'http://localhost:5000/todo/api/v1.0/tasks'
response = requests.get(url)
print(str(response))
print('')
print(json.dumps(response.json(), indent=4))
result:
<Response [200]>
{
"tasks": [
{
"description": "Milk, Cheese, Pizza, Fruit, Tylenol",
"done": false,
"id": 1,
"title": "Buy groceries"
},
{
"description": "Need to find a good Python tutorial on the web",
"done": false,
"id": 2,
"title": "Learn Python"
}
]
}

From help(requests.get):
Help on function get in module requests.api:
get(url, params=None, **kwargs)
Sends a GET request.
:param url: URL for the new :class:`Request` object.
:param params: (optional) Dictionary or bytes to be sent in the query string for the :class:`Request`.
:param \*\*kwargs: Optional arguments that ``request`` takes.
:return: :class:`Response <Response>` object
:rtype: requests.Response
so I would say requests.get(url) would be enough to get a response. Then look at either the json() or data() functions in response depending on what the API is expected to return.
So for the case of a JSON response, the following code should be enough:
import requests
import json
url = "https://postman-echo.com/get?testprop=testval"
response = requests.get(url)
print(json.dumps(response.json(), indent=4))
Try the above code with an actual test API.

Related

Tiktok api /user/info endpoint

Edit: Since this question is getting a good amount of views, I'd like to let you all know before you waste hours of your life on the upload endpoint that it currently requires you to manually click confirm in the app. So it doesn't allow you to fully automate uploads. To anyone which this saves time, you're welcome :)
Currently trying to implement the TikTok api into one of my projects. However having a few difficulties with a specific endpoint. Not sure if its an error on my part or on Tiktoks.
Upon making the below request I am recieving an invalid request body error message. I have followed their documentation to the T so unsure why this is happening?
https://developers.tiktok.com/doc/login-kit-user-info-basic
data = {
"access_token": access_token,
"open_id": open_id,
"fields": [
"open_id",
"union_id",
"avatar_url",
"avatar_url_100",
"avatar_url_200",
"avatar_large_url",
"display_name"
]
}
user_info = requests.post("https://open-api.tiktok.com/user/info/", data=data)
print(user_info.json())
{'data': {}, 'error': {'code': 6007055, 'log_id': '', 'message': 'invalid request body'}}
Use json parameter instead of data
import requests
data = {
"access_token": access_token,
"open_id": open_id,
"fields": [
"open_id",
"union_id",
"avatar_url",
"avatar_url_100",
"avatar_url_200",
"avatar_large_url",
"display_name"
]
}
user_info = requests.post("https://open-api.tiktok.com/user/info/", json=data)
print(user_info.json())

APNS Notifications: Python HTTP/2 POST request returning 404 ({"reason":"BadPath"})

I am developing a watchOS app and I want to send push notifications to my users. I have enabled "Push Notifications" in signing and capabilities and a file called "app_name WatchKit Extension" was generated containing one entitlement called APS environment whose value is set to "development".
I have also generated a .p8 file in the Apple Developer Website with the authentication key and that also gives me the key id.
I have created a Swift class called App Delegate that conforms to UNUserNotificationCenterDelegate. I have implemented the method applicationDidFinishLaunching from where I call WKExtension.shared().registerForRemoteNotifications(). Then I implemented the didRegisterForRemoteNotifications where I receive the device token and convert it into a string by executing these lines of code:
let tokenParts = deviceToken.map { data in String(format: "%02.2hhx", data) }
let token = tokenParts.joined()
Then I send the token to the server. In the server I have a Python script that makes the post request to https://api.sandbox.push.apple.com:443 with the headers and notification payload. However, I always get a 404 error ({"reason":"BadPath"}).
I don't know what I am doing wrong. Am I configuring anything wrong? This is the Python script I am using:
import time
import httpx
import asyncio
import jwt
ALGORITHM = 'ES256'
APNS_AUTH_KEY = "path to .p8 file"
f = open(APNS_AUTH_KEY)
secret = f.read()
apns_token = jwt.encode(
{
'iss': 'cert_id',
'iat': time.time()
},
secret,
algorithm=ALGORITHM,
headers={
'alg':ALGORITHM,
'kid':'key_id'
}
)
dev_server = "https://api.sandbox.push.apple.com:443"
device_token = "9fe2814b6586bbb683b1a3efabdbe1ddd7c6918f51a3b83e90fce038dc058550"
headers = {
'method': 'POST',
'path': '/3/device/{0}'.format(device_token),
'autorization': 'bearer {0}'.format(apns_token),
'apns-push-type': 'myCategory',
'apns-expiration': '0',
'apns-priority': '10',
}
payload = {
"aps" : {
"alert" : {
"title" : "Hello Push",
"message": "This is a notification!"
},
"category": "myCategory"
}
}
async def test():
async with httpx.AsyncClient(http2=True) as client:
client = httpx.AsyncClient(http2=True)
r = await client.post(dev_server, headers=headers, data=payload)
print(r.text)
print(r)
asyncio.run(test())
Is there anything wrong with the way I am setting things up or performing the post request?
Thank you for your help!

Make Slack API buttons "do something"

Edit: Since I asked this question I have created a ngrok URL to receive the Slack POST when the user his the button.
However, every time I press the button I get this: "POST / HTTP/1.1" 404 -
I am using a local Flask URL, code below:
from flask import Flask, request
app = Flask(__name__)
#app.route('/payload', methods=['POST'])
def incoming_slack_message():
req = request.get_json(Force=True)
info = request.form['channel_id']
print(req)
print(info)
print('did it work???')
return 'action successful'
#app.route('/slack/blocks', methods=['POST'])
def incoming_slack_options():
req = request.get_json(Force=True)
info = request.form['payload']
print(req)
print(info)
print('Did it work here??')
return 'ok'
if __name__ == '__main__':
app.run(port=3000, debug = True)
I have read that I need to include a callback_id in the block but whenever I do I get TypeError: 'NoneType' object is not subscriptable
This is the block I am using less the callback_id
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Once your machine is selected, click here."
},
"accessory": {
"type": "button",
"text": {
"type": "plain_text",
"text": "Change",
},
"value": "click_me_123",
"action_id": "button"
}
}
I am SURE something is wrong in my flask code as I know little to nothing about Flask. I don't think there's anything wrong with my Slack Block but I still feel there should be a callback_id. Thank you for your time.
Okay I got the button to generate a response! The Flask code is as follows
from flask import Flask, request, Response, jsonify
import requests
import json
app = Flask(__name__)
#app.route('/', methods=['POST'])
def resp():
data = request.json
button_info = request.form['payload']
webhook_url = 'webhook from slack'
slack_data = { 'text': "How can I help you?"}
response = requests.post(
webhook_url, data=json.dumps(slack_data),
headers={'Content-Type': 'application/json'}
)
return(jsonify(data), print(button_info))
if __name__ == '__main__':
app.run(port=3000, debug = True)
The button_info will print out all the data.
I'm sure I can get rid of some of these line/imports and it will still work but this will get the job done!
*** This code is in conjunction with a ngrok public URL for the local web server.
brew cask install ngrok followed by
ngrok http 3000 to match the 3000 port in my code
Thewebhook_url is from the slack API website.

How to make a post request with the Python requests library?

I am using the following filters in Postman to make a POST request in a Web API but I am unable to make a simple POST request in Python with the requests library.
First, I am sending a POST request to this URL (http://10.61.202.98:8081/T/a/api/rows/cat/ect/tickets) with the following filters in Postman applied to the Body, with the raw and JSON(application/json) options selected.
Filters in Postman
{
"filter": {
"filters": [
{
"field": "RCA_Assigned_Date",
"operator": "gte",
"value": "2017-05-31 00:00:00"
},
{
"field": "RCA_Assigned_Date",
"operator": "lte",
"value": "2017-06-04 00:00:00"
},
{
"field": "T_Subcategory",
"operator": "neq",
"value": "Temporary Degradation"
},
{
"field": "Issue_Status",
"operator": "neq",
"value": "Queued"
}],
"logic": "and"
}
}
The database where the data is stored is Cassandra and according to the following links Cassandra not equal operator, Cassandra OR operator,
Cassandra Between order by operators, Cassandra does not support the NOT EQUAL TO, OR, BETWEEN operators, so there is no way I can filter the URL with these operators except with AND.
Second, I am using the following code to apply a simple filter with the requests library.
import requests
payload = {'field':'T_Subcategory','operator':'neq','value':'Temporary Degradation'}
url = requests.post("http://10.61.202.98:8081/T/a/api/rows/cat/ect/tickets",data=payload)
But what I've got is the complete data of tickets instead of only those that are not temporary degradation.
Third, the system is actually working but we are experiencing a delay of 2-3 mins to see the data. The logic goes as follows: We have 8 users and we want to see all the tickets per user that are not temporary degradation, then we do:
def get_json():
if user_name == "user 001":
with urllib.request.urlopen(
"http://10.61.202.98:8081/T/a/api/rows/cat/ect/tickets?user_name=user&001",timeout=15) as url:
complete_data = json.loads(url.read().decode())
elif user_name == "user 002":
with urllib.request.urlopen(
"http://10.61.202.98:8081/T/a/api/rows/cat/ect/tickets?user_name=user&002",timeout=15) as url:
complete_data = json.loads(url.read().decode())
return complete_data
def get_tickets_not_temp_degradation(start_date,end_date,complete_):
return Counter([k['user_name'] for k in complete_data if start_date < dateutil.parser.parse(k.get('DateTime')) < end_date and k['T_subcategory'] != 'Temporary Degradation'])
Basically, we get the whole set of tickets from the current and last year, then we let Python to filter the complete set by user and so far there are only 10 users which means that this process is repeated 10 times and makes me no surprise to discover why we get the delay...
My questions is how can I fix this problem of the requests library? I am using the following link Requests library documentation as a tutorial to make it working but it just seems that my payload is not being read.
Your Postman request is a JSON body. Just reproduce that same body in Python. Your Python code is not sending JSON, nor is it sending the same data as your Postman sample.
For starters, sending a dictionary via the data arguments encodes that dictionary to application/x-www-form-urlencoded form, not JSON. Secondly, you appear to be sending a single filter.
The following code replicates your Postman post exactly:
import requests
filters = {"filter": {
"filters": [{
"field": "RCA_Assigned_Date",
"operator": "gte",
"value": "2017-05-31 00:00:00"
}, {
"field": "RCA_Assigned_Date",
"operator": "lte",
"value": "2017-06-04 00:00:00"
}, {
"field": "T_Subcategory",
"operator": "neq",
"value": "Temporary Degradation"
}, {
"field": "Issue_Status",
"operator": "neq",
"value": "Queued"
}],
"logic": "and"
}}
url = "http://10.61.202.98:8081/T/a/api/rows/cat/ect/tickets"
response = requests.post(url, json=filters)
Note that filters is a Python data structure here, and that it is passed to the json keyword argument. Using the latter does two things:
Encode the Python data structure to JSON (producing the exact same JSON value as your raw Postman body value).
Set the Content-Type header to application/json (as you did in your Postman configuration by picking the JSON option in the dropdown menu after picking raw for the body).
requests is otherwise just an HTTP API, it can't make Cassandra do any more than any other HTTP library. The urllib.request.urlopen code sends GET requests, and are trivially translated to requests with:
def get_json():
url = "http://10.61.202.98:8081/T/a/api/rows/cat/ect/tickets"
response = requests.get(url, params={'user_name': user}, timeout=15)
return response.json()
I removed the if branching and replaced that with using the params argument, which translates a dictionary of key-value pairs to a correctly encoded URL query (passing in the user name as the user_name key).
Note the json() call on the response; this takes care of decoding JSON data coming back from the server. This still takes long, you are not filtering the Cassandra data much here.
I would recommend using the json attribute instead of data. It handles the dumping for you.
import requests
data = {'user_name':'user&001'}
headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
url = "http://10.61.202.98:8081/T/a/api/rows/cat/ect/tickets/"
r = requests.post(url, headers=headers, json=data)
Update, answer for question 3. Is there a reason you are using urllib? I’d use python requests as well for this request.
import requests
def get_json():
r = requests.get("http://10.61.202.98:8081/T/a/api/rows/cat/ect/tickets”, params={"user_name": user_name.replace(" ", "&")})
return r.json
# not sure what you’re doing here, more context/code example would help
def get_tickets_not_temp_degradation(start_date, end_date, complete_):
return Counter([k['user_name'] for k in complete_data if start_date < dateutil.parser.parse(k.get('DateTime')) < end_date and k['T_subcategory'] != 'Temporary Degradation'])
Also, is the username really supposed to be user+001 and not user&001 or user 001?
I think, you can use requests library as follows:
import requests
import json
payload = {'field':'T_Subcategory','operator':'neq','value':'Temporary Degradation'}
url = requests.post("http://10.61.202.98:8081/T/a/api/rows/cat/ect/tickets",data=json.dumps(payload))
You are sending user in url, use it through post, but its depend upon how end points are implemented. You can try the below code :
import requests
from json import dumps
data = {'user_name':'user&001'}
headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
url = "http://10.61.202.98:8081/T/a/api/rows/cat/ect/tickets/"
r = requests.post(url, headers=headers, data=dumps(data))

Python program reading URL

I am looking for a python program which will run the URL. Simple, it just needs to run the URL produced from the application with provided credentials to access the application. I will schedule to run the python script every night.
I have an application that produces the URL. If I run that URL, it will produce the JSON file.
I am trying to pre-cache the output of the application so I want to run the URL every morning.
Below are the information:
USERNAME = "test"
PASSWORD = "test5"
HOST = 'appl.xyz.net'
PORT = 8080
Sample URL is:
http://appl.xyz.net:8080/app/content/pq/doQuery?solution=nd&path=&file=Test.nd&dataAccessId=1&paramid=4221
JSON:
{
"queryInfo":{
"totalRows":"3"
},
"resultset":[
[
4215,
null,
"AAA"
],
[
4215,
null,
"BBB"
]
],
"metadata":[
{
"colIndex":0,
"colType":"Numeric",
"colName":"id"
},
{
"colIndex":1,
"colType":"String",
"colName":"Name"
},
{
"colIndex":2,
"colType":"String",
"colName":"City"
}
]
}
Thanks
Use the python-requests library.
Then all you need to do is:
import requests
url = 'http://appl.xyz.net:8080/app/content/pq/doQuery?solution=nd&path=&file=Test.nd&dataAccessId=1&paramid=4221'
user = 'test'
password = 'test5'
# Takes care of the HTTP authentication
data = requests.get(url, auth=(user, password))
json_data = data.json()
StvnW has provided a link in the comments to CURL alternative in Python if you do not wish to install any other libraries.

Categories

Resources