I'm creating a REST API with flask_restful and want to parse the arguments coming from a PUT request. Below is the relevant code:
user_put_args = reqparse.RequestParser()
user_put_args.add_argument("user_id", type="str", help="user_id is needed for this user")
user_put_args.add_argument("spotify_username", type="str", help="Spotify username for the corresponding user_id")
user_put_args.add_argument("apple_music_username", type="str", help="Apple Music username for the corresponding user_id")
PUT function declaration:
def put(self, user_id):
args = user_put_args.parse_args()
rest of code ...
and here's the put request:
req4 = requests.put(HEROKU_BASE + "/users/" + "test1", {"spotify_username": "test_1", "apple_music_username":"something"})
print(req4.json())
The response I'm getting from the put call is:
{'message': {'spotify_username': 'Spotify username for the corresponding user_id'}}
meaning it's getting hung up on the request parse.
Any ideas on how to fix this? I'm following a guide that led me to this and my code is almost exactly the same. Thanks!
When you write type="str", reqparser tries to call the argument of type on your data and it is cannot be done with string
You shouldn't use quotes there, it will just be:
user_put_args.add_argument("spotify_username", type=str, help="Spotify username for the corresponding user_id")
Related
I am trying to use the api call users.profile.get to find a users profile picture. The problem is that the request requires not JSON, but URL encoded queries (I think?). I have the user id already, but I need to know how to send it to slack correctly, preferably with the api_call method.How would I go along doing this?
Here is the documention: https://api.slack.com/methods/users.profile.get
for users in collection.find():
start_date, end_date = users['start_date'], users['end_date']
user_data = client.api_call('users.profile.get',
"""What would I do here?""")
user_images[users['user_id']] = user_data['image_72']
block.section(
text=
f'from *{date_to_words(start_date[0], start_date[1], start_date[2])}* to *{date_to_words(end_date[0], end_date[1], end_date[2])}*'
)
block.context(data=((
'img', user_images[users['user_id']],
'_error displaying image_'
), ('text',
f'<!{users["user_id"]}>. _Contact them if you have any concerns_'
)))
You can pass the parameters of the API as names arguments in your function call.
For users.get.profile you want to provide the user ID, e.g. "U1245678".
Then your call would look like this (with slackclient v1):
response = sc.api_call(
"users.profile.get",
user="U12345678"
)
assert response["ok"]
user_data = response["profile"]
Or like this with slackclient v2:
response = sc.users_profile_get(user="U12345678")
assert response["ok"]
user_data = response["profile"]
To answer your question: You do not have to worry about how the API is called, since that is handled by library. But technically most of Slack's API endpoints accepts parameters both as URL endocded form and JSON.
I use flask-assistant on python 3 with dilaogflow as a webhook. I looked at the official documentation and I don't find how to get the user message ("queryText" on dialogflow json request).
I tried this with no success:
# -*- coding: utf-8 -*-
from flask import Flask
from flask_assistant import Assistant, ask, tell, context_manager, event
project_id='myproject_id'
app = Flask(__name__)
assist = Assistant(app, route='/', project_id = project_id)
#assist.action('Default Fallback Intent')
def manage_fallback(queryText):
print('*'*40,queryText)
speech='Running'
return tell(speech)
if __name__ == '__main__':
app.run(debug=True)
The print of queryText always return None, but when I inspect on the ngrok web interface (http://127.0.0.1:4040) , I can see the request.
And I want to know how canI get the user message from flask-assistant?
I also asked about this question on github and get the answer, so I will share for the others:
You can get the query text from the flask-assistant request object.
from flask_assistant import request
...
...
#assist.action('Default Fallback Intent')
def manage_fallback():
user_query = request['queryResult']['queryText']
speech = "You said " + user_query
return tell(speech)
The reason the value of queryText expected by your manage_fallback function is None is because the parameter name must match the entity type expected by the intent.
Parameters are used to receive parsed entities for an intent, not the full user query.
I use URL parameters on my site as identifiers for user data. If a user clicks one specific link on the site, these parameters will be 'none', and I try to use the following function to then send them back to the same page with the correct parameters:
Site
#app.route('/my-site/<param1>/<param2>/<param3>/<param4>', methods=['GET', 'POST'])
def site():
if param1 == 'none':
linking_data = Posts.query.filter_by(user=current_user.email).first()
return render_template('site', param1=linking_data.param1,
param2=linking_data.param2,
param3=linking_data.param3,
param4=linking_data.param4)
If the params are hard-coded, this approach works well. If I attempt to define the params as I have above however, it results in the following error:
werkzeug.routing.BuildError: Could not build url for endpoint 'site' with values ['param3', 'param4'].
Did you forget to specify values ['param1', 'param2']?
Why does it think I haven't specified the first two parameters?
You need to add the url parameters to the handler:
#app.route('/my-site/<param1>/<param2>/<param3>/<param4>', methods=['GET', 'POST'])
def site(param1=None, param2=None, param3=None, param4=None):
....
TIP: The None value outputs as string None, not none. It would be better if you updated your code to handle a string value for "none" that is not case-sensitive.
I have a Google App Engine API using Python and NDB working except for HTTP response code/error checking. I put in some code to handle 406 (to only accept json requests) and 400 errors (to prevent a user from leaving a required field blank) to the post function for one of my entities but now it seems to have broken my code. This is the code with the error checking included:
class Task_action(webapp2.RequestHandler):
def post(self):
#Only allows a JSON, if not, then error
if 'application/json' not in self.request.accept:
self.response.status = 406
self.response.status_message = "Not Acceptable, API only supports application/json MIME type"
return
new_task = Task(parent=PARENT_KEY,
name = self.request.get("task_name"),
hours = int(self.request.get("task_hours")),
id = self.request.get("task_name"))
#has error code, since name and hours is required
if name:
new_task.name = name
else:
self.response.status = 400
self.response.status_message = "Invalid request, task name is Required."
if hours:
new_task.hours = hours
else:
self.response.status = 400
self.response.status_message = "Invalid request, task hours is Required."
key = new_task.put()
out = new_task.to_dict()
self.response.write(json.dumps(out))
I am using curl to test it:
curl --data-urlencode "name=clean" -H "Accept: application/json" http://localhost:15080/task
I know the problem is in the error checking code (all the if else statements) because when I take it out the curl test works fine and the object is added to the ndb database correctly. However, with the error checking code included my curl test does not add the object as it should. Does anyone have an idea why the error checking code is breaking my post statement? Is there a better way to return HTTP error response codes?
You had some uninitialized variables in the code (name, hours, maybe PARENT_KEY) and also you didn't return after preparing the error response, thus flowing in areas where the code wouldn't work.
I'd suggest re-organizing the error checking code for minimal impact on the functional code (checks should be done as early as possible, to simplify the remaining functional code. Also, I prefer to use the more concise webapp2.abort() function (which doesn't need a return statement).
Something along these lines:
class Task_action(webapp2.RequestHandler):
def post(self):
# Only allows a JSON, if not, then error
if 'application/json' not in self.request.accept:
webapp2.abort(406, details="Not Acceptable, API only supports application/json MIME type")
# request must contain a valid task name
name = self.request.get("task_name")
if not name:
webapp2.abort(400, details="Invalid request, task name is Required.")
# request must contain a valid task hours
try:
hours = int(self.request.get("task_hours"))
except Exception:
hours = 0
if not hours:
webapp2.abort(400, details="Invalid request, task hours is Required.")
new_task = Task(parent=PARENT_KEY, name=name, hours=hours, id=hours)
new_task.name = name # isn't this done by Task() above?
new_task.hours = hours # isn't this done by Task() above?
key = new_task.put()
out = new_task.to_dict()
self.response.write(json.dumps(out))
Another note: you're specifying the id parameter in the Task() call, which doesn't work unless you know each Task() entity has a unique hours value. You may want to let the datastore assign IDs automatically.
I have asked a few questions about this before, but still haven't solved my problem.
I am trying to allow Salesforce to remotely send commands to a Raspberry Pi via JSON (REST API). The Raspberry Pi controls the power of some RF Plugs via an RF Transmitter called a TellStick. This is all setup, and I can use Python to send these commands. All I need to do now is make the Pi accept JSON, then work out how to send the commands from Salesforce.
Someone kindly forked my repo on GitHub, and provided me with some code which should make it work. But unfortunately it still isn't working.
Here is the previous question: How to accept a JSON POST?
And here is the forked repo: https://github.com/bfagundez/RemotePiControl/blob/master/power.py
What do I need to do? I have sent test JSON messages n the Postman extension and in cURL but keep getting errors.
I just want to be able to send various variables, and let the script work the rest out.
I can currently post to a .py script I have with some URL variables, so /python.py?power=on&device=1&time=10&pass=whatever and it figures it out. Surely there's a simple way to send this in JSON?
Here is the power.py code:
# add flask here
from flask import Flask
app = Flask(__name__)
app.debug = True
# keep your code
import time
import cgi
from tellcore.telldus import TelldusCore
core = TelldusCore()
devices = core.devices()
# define a "power ON api endpoint"
#app.route("/API/v1.0/power-on/<deviceId>",methods=['POST'])
def powerOnDevice(deviceId):
payload = {}
#get the device by id somehow
device = devices[deviceId]
# get some extra parameters
# let's say how long to stay on
params = request.get_json()
try:
device.turn_on()
payload['success'] = True
return payload
except:
payload['success'] = False
# add an exception description here
return payload
# define a "power OFF api endpoint"
#app.route("/API/v1.0/power-off/<deviceId>",methods=['POST'])
def powerOffDevice(deviceId):
payload = {}
#get the device by id somehow
device = devices[deviceId]
try:
device.turn_off()
payload['success'] = True
return payload
except:
payload['success'] = False
# add an exception description here
return payload
app.run()
Your deviceID variable is a string, not an integer; it contains a '1' digit, but that's not yet an integer.
You can either convert it explicitly:
device = devices[int(deviceId)]
or tell Flask you wanted an integer parameter in the route:
#app.route("/API/v1.0/power-on/<int:deviceId>", methods=['POST'])
def powerOnDevice(deviceId):
where the int: part is a URL route converter.
Your views should return a response object, a string or a tuple instead of a dictionary (as you do now), see About Responses. If you wanted to return JSON, use the flask.json.jsonify() function:
# define a "power ON api endpoint"
#app.route("/API/v1.0/power-on/<int:deviceId>", methods=['POST'])
def powerOnDevice(deviceId):
device = devices[deviceId]
# get some extra parameters
# let's say how long to stay on
params = request.get_json()
try:
device.turn_on()
return jsonify(success=True)
except SomeSpecificException as exc:
return jsonify(success=False, exception=str(exc))
where I also altered the exception handler to handle a specific exception only; try to avoid Pokemon exception handling; do not try to catch them all!
To retrieve the Json Post values you must use request.json
if request.json and 'email' in request.json:
request.json['email']