I've been working with the spotipy python API for a few days, trying to get it to work. Each time I try for a login request, it tracebacks with bad oauth request.
I've used this code:
id='my_client_id'
secret='my_client_secret'
url='https://mywebsite.mydomain/callback'
username='myusername'
scope='a list of scopes'
token=util.prompt(username, scope, client_id=id, client_secret=secret, url)
I then paste in a url that looks like:
https://mywebsite.mydomain/callback?code=a_long_code
But each time it gives me a bad request from oauth. Am I missing something? It seems to go through the login process fine, it's just it tracebacks at the end.
Just in case people have this issue in the future, here is what I did:
In oauth2.py, find where it raises the error, and before that put something like: self.problem=response.
Run the steps that util.prompt does by hand, I.E, do the oauth_url requests yourself, rather than through util.prompt.
See what sp_oauth.problem.text says.
In my case, it was an incorrect app secret.
MORTIFIED!
Related
Here is how I want my program to work. Step 2 is what I am unsure of how to implement.
Client makes API call to /email endpoint
/email endpoint has a script run that gather emails from GMAIL API
Put contents into response object
Returns response object back to client
I understand how to make a static api response. But I can't get a python script to run when the api endpoint is hit.
I saw the flask tag in your post.
I only played around with flask for certain interviews, but know enough to say calling a python script outside your running server is somewhat of an antipattern.
I assume your backend is a flask app, so ideally, you'd want to wrap whatever script you have in your python script file in a function and simply call it from your flask method when the endpoint is hit.
Something like:
from flask import Flask
from custom_emails_module import gather_email
#api.route('/email', methods=["GET"])
def method_associated_with_your_endpoint():
# additional
gather_email()
where custom_emails_module should be the module you create for your gather_emails script.
Now, at the end of your gather_emails function, simple remember to return the correct type, usually done with:
return json.dumps("success": True, "data": python_object_with_several_emails)
Use something like PostMan for local debugging and remember to use application/json in header for Content-Type.
Good luck!
I would like to test a site with APIs that at this stage of development is classified as "not secure".
In the Locust documentation is said that:
Safe mode:
The HTTP client is configured to run in safe_mode. What this does is that any request that fails due to a connection error, timeout, or similar will not raise an exception, but rather return an empty dummy Response object. The request will be reported as a failure in User’s statistics. The returned dummy Response’s content attribute will be set to None, and its status_code will be 0.
I would like to know if there is a configuration that let you disable this option.
Thank you for your time
safe_mode has nothing to do with SSL/TLS, it refers to requests not throwing an exception when the request fails (for whatever reason).
To make HttpUser/requests ignore SSL issues, add verify=False to your call.
——
You're looking at a very old Locust documentation, right? I dont think the text you mention has been there for quite a while.
safe_mode has been removed from requests (it used to look like this https://github.com/psf/requests/pull/604) but the same behaviour is implemented by Locust here https://github.com/locustio/locust/blob/51b1d5038a2be6e2823b2576c4436f2ff9f7c7c2/locust/clients.py#L195
I am just learning FastAPI (and loving it), so it is quite likely I am doing something wrong. But here is my problem:
In the code snippet below, I am creating a new user, if there is no user already.
The code works fine, but it is the error handling that I am having trouble with. The errors are properly being pushed forward to FastAPI's internal docs or to an API client like Postman, but not back to the actual client that I am using or the command line.
#app.post("/users/", response_model=schemas.User)
def create_user(user: schemas.UserCreate, db: Session = Depends(get_db)):
db_user = crud.get_user_by_username(db, username=user.username)
if db_user:
raise HTTPException(
status_code=400, detail=f"Username '{user.username}' already registered"
)
return crud.create_user(db=db, user=user)
If I use the auto-generated FastAPI docs (or Postman) and monitor the response in that way, I get the error I am expecting:
But when I look at what I am receiving at the client end (Vue) or what the uvicorn server is logging, it does not contain that information:
As you can see, it just says Bad Request instead of responding with the JSON dict of {"detail": "Username 'miketest' already registered"}
What am I doing wrong? What can I do to make sure that the full HTTPException information is being returned? I am pretty sure the problem is on the FastAPI end, because the client is receiving exactly what the server is outputting as well.
I figured out the problem, and it was not a FastAPI issue, per se, but it was on how it passes information back to the front end.
I thought I should keep this question in case someone has the same issue.
Solution:
try {
await api().post('register',JSON.stringify(data);
} catch (err) {
error = err.response.data.detail;
}
That is to say, the error sent from FastAPI is an object that has a response, and in it a data, and in that a detail.
The response from Postman or anything similar just gives an object with detail. I did not see that there was a middle data layer, and I was having trouble seeing the entire object from within Vue.
This screenshot belongs to console log and it will not contain the API response, the JSON response.
You can see the actual response if you send the request the API using some client, like POSTMAN.
I'm using soundcloud-python https://github.com/soundcloud/soundcloud-python for Soundcloud API on Ubuntu Server 16.04.1 (installed with pip install soundcloud).
Soundcloud API Rate Limits official page https://developers.soundcloud.com/docs/api/rate-limits#play-requests says that, in case an app exceeds the API rate limits, the body of the 429 Client Error response would be a JSON object, containing some additional info.
I'm interested in getting reset_time field, to inform the user when the block will be over.
The problem is that when, for example, like rate limits is exceeded, doing response = client.put('/me/favorites/%d' % song_id) the app crashes and response is null.
How can I get the JSON response body?
Why don't you read the package's source code and find out by yourself ?
Let's see... You don't explain you got that client object but browsing the source code we can see there's a "client.py" module that defines a Client class. This class does'nt not define a put method explicitely but it defines the __getattr__ hook :
def __getattr__(self, name, **kwargs):
"""Translate an HTTP verb into a request method."""
if name not in ('get', 'post', 'put', 'head', 'delete'):
raise AttributeError
return partial(self._request, name, **kwargs)
Ok, so Client.put(...) returns a partial object wrapping Client._request, which is a quite uselessly convoluted way to define Client.put(**kwargs) as return self._request("put", **kwargs).
Now let's look at Client._request: it basically make a couple sanity checks, updates **kwargs and returns wrapped_resource(make_request(method, url, kwargs)).
Looking up the imports at the beginning of the module, we can see that make_request comes from "request.py" and wrapped_resource from "resources.py".
You mention that doing an api call while over the rate limit "crashes the application" - I assume you mean "raises an exception" (BTW please post exceptions and tracebacks when saking about such problems) - so assuming this is handled at the lower level, let's start with request.make_request. A lot of data formatting / massaging obviously and finally the interesting part: a call to response.raise_for_status(). This is a hint that we are actually delegating to the famous python-requests package, which is actually confirmed a few lines above and in the requirements file
If we read python-requests fine manual, we find out what raise_for_status does - it raises a requests.exceptions.HTTPError for client (4XX) and server (5XX) status codes.
Ok now we know what exception we have. Note that you had all those informations already in your exception and traceback, which would have saved us a lot of pain here had you posted it.
But anyway... It looks like we won't get the response content, does it ? Well, wait, we're not done yet - python-requests is a fairly well designed package, so chances are we can still rescue our response. And indeed, if we look at requests.exceptions source code, we find out that HttpError is a subclass of RequestException, and that RequestException is "Initialize(d)" with "request and response objects."
Hurray, we do have our response - in the exception. So all we have to do is catch the exception and check it's response attribute - which should contains the "additional informations".
Now please understand that this took me more than half an hour to write, but about 7 minutes to sort out without the traceback - with the traceback it would have boiled down to a mere 2 minutes, the time to go to the requests.exceptions source code and make sure it keeped the request and response. Ok I'm cheating, I'm used to read source code and I use python-requests a lot, but still: you could have solved this by yourself in less than an hour, specially with python's interactive shell which let's you explore and test live objects in real time.
The default message for Flask 400 exception (abort()) is:
{
"message": "The browser (or proxy) sent a request that this server could not understand."
}
For 404:
{
"message": "The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again. You have requested this URI [/obj/] but did you mean /obj/ or /obj/<int:id>/ or /obj/<int:id>/kill/ ?"
}
I have trouble comprehending these messages when I'm getting them as replies in my API (especially the first one, I thought there's something wrong with encryption or headers) and I thing it's kinda tiresome to try to override text manually for every abort() exception. So I change the mapping:
from flask import abort
from werkzeug.exceptions import HTTPException
class BadRequest(HTTPException):
code = 400
description = 'Bad request.'
class NotFound(HTTPException):
code = 404
description = 'Resource not found.'
abort.mapping.update({
400: BadRequest,
404: NotFound
})
For the case of 400 it works beautifully. But when it comes to 404 it is still the same message. I tested it in the same place in my code - it works for abort(400), abort(403) and some of the others, but it gets mysteriously overridden by default message on abort(404). Debugging didn't help much. What may be the culprit here?
Update. Yes, I'm using abort imported from flask not flask_restful as the latter doesn't have the mapping and it's a function not an Aborter object. Besides, it does work for most exceptions, so it's probably not the real issue here.
Update 2. The abort.mapping seems to be perfectly fine on execution. The exceptions in question are overridden, including 404.
Update 3: I've put together a little sandbox, that I use for debugging. (removed the repo since the mystery is long solved).
It took me some time, but now I actually found the place, where it all derails on 404 error. It's actually an undocumented feature in flask-restful. Look at the code here. Whatever message you chose persists until that very place and then it becomes the default. What we need now is just put ERROR_404_HELP = False to our config and everything works as intended.
Why is this code even there in the first place? OK, perhaps, I can live with that, but it should be all over the documentation. Even when I googled the name of the constant, I only got a couple of GitHub issues (1, 2).
Anyways, the mystery is officially solved.
By the way... I can't point to documentation for how I discovered this, I just tried it (that's how I learn most development!) but, you can simply abort with the desired response code but instead return a custom string with it. I think this makes sense because you're using the framework the way it's intended, you're not writing tons of code, you're returning the correct response code and in the fashion the framework expects, and you're informing any human who reads it as to the application's context for the error.
from flask import abort
abort(404, "And here's why.")