Add error handling to the API blueprint. - #40
Conversation
Codecov Report
@@ Coverage Diff @@
## develop #40 +/- ##
===========================================
+ Coverage 42.9% 51.73% +8.82%
===========================================
Files 20 21 +1
Lines 825 893 +68
Branches 75 79 +4
===========================================
+ Hits 354 462 +108
+ Misses 461 418 -43
- Partials 10 13 +3
Continue to review full report at Codecov.
|
b6528de to
a686985
Compare
cthoyt
left a comment
There was a problem hiding this comment.
Overall looks good!
Requires lots more unit testing, though. And having everything go through 400 makes it tricky to pick out that the right error comes at the right time (besides doing string checking)
| prepared_result_dicts = (_prepare_result_dictionary(result_data) | ||
| for result_data in claims[OCSP_RESULTS_JWT_CLAIM]) | ||
| try: | ||
| prepared_result_dicts = (_prepare_result_dictionary(result_data) |
There was a problem hiding this comment.
Come on, use proper spacing :)
There was a problem hiding this comment.
What did I do wrong here?
| except (KeyError, ValueError): | ||
| abort(HTTPStatus.BAD_REQUEST, 'invalid result data') | ||
|
|
||
| # TODO: can this raise an exception? I think yes if there's a constraint broken on the DB when commit is called() |
There was a problem hiding this comment.
catch an integrity error then
| n = request.args.get('n', type=int, default=10) # TODO make configurable at app level | ||
| if n > 10: | ||
| abort(400, 'n too large, max is 10') # TODO get the max config value here too | ||
| abort(HTTPStatus.BAD_REQUEST, 'n too large, max is 10') # TODO get the max config value here too |
There was a problem hiding this comment.
is there a wrapper for aborting on 400?
There was a problem hiding this comment.
No, the flask docs have things like abort(404). It is annoying to be so much wordier with HTTPStatus.BAD_REQUEST but I think that's better than a "magic" number even if most everyone knows it.
| invite_token = b64decode(claims['token']) | ||
| except KeyError: | ||
| abort(HTTPStatus.BAD_REQUEST, "'token' missing from claims") | ||
| except binascii.Error: |
There was a problem hiding this comment.
that's an awful name for an error
There was a problem hiding this comment.
I take what the stdlib provides.
There was a problem hiding this comment.
namespaces, those are a honking good thing
|
I agree so many 400's is not the best, but none of the other error codes seem to fit better. One thing I have seen is returning JSON errors like (for Pushover, for example): {
"user": "invalid",
"errors": [
"user identifier is invalid"
],
"status": 0,
"request": "5042853c-402d-4a18-abcb-168734a801de"
}Despite them having a list of |
|
You can return json and give it a non-200 error |
|
Well, gonna try this now http://flask.pocoo.org/docs/1.0/patterns/apierrors/ |
- Add client_session and client_function fixtures to create Flask test clients and rollback the db between tests - Add get_db staticmethod to OCSPSQLAlchemy class - Add the ability to pass session_options into the flask-sqlalchemy SQLAlchemy constructor from the create_application function so the test fixtures can take control of rollbacks. - Add stub test to start using the test client.
| logger.debug('rolling back transaction from function for web client') | ||
| transaction.rollback() | ||
|
|
||
|
|
There was a problem hiding this comment.
@cthoyt if you can figure out how to reduce the repetition between manager_session and client_session and between manager_function and client_function that'd be swell.
But that's not your real homework.
| transaction.rollback() | ||
|
|
||
|
|
||
| # TODO: fixture to pre-fill DB with some stuff for the client to test on |
There was a problem hiding this comment.
@cthoyt this is the real homework.
Something that takes in a client and spits out the same client with a db with a bunch of stuff in it.
It'd be cool if it didn't get rolled back, but that might be a huge pain so whatever works.
Scoped sessions act like sessions... why don't they just inherit from them?!?
The SQLAlchemy class takes a none, so you can pass it none (unless i'm an idiot and none means something special)
@scolby33 not sure where you want this, but here's the idea with making some test data
| location = self.get_location_by_selector(selector) | ||
| if location is None: | ||
| raise Exception(f'location not found for selector: {selector}') | ||
| raise ValueError(f'invalid invite token') |
There was a problem hiding this comment.
No, since it doesn't mean your code has been designed wrong, but rather you have to deal with the reality that someone else is going to stick weird shit inside as the invite token.
Now, the next exception checking for the expiration of the pubkey might be different, but raising an exception actually makes the logic for using this function much better. Overall, I like this design and I think it does what it's supposed to in an elegant way. Lots of helpful/useful errors for bad situations where the program should stop seems good to me.
| except jwt.JWTError: | ||
| abort(HTTPStatus.BAD_REQUEST, 'malformed JWT') | ||
| except jwt.JWTError as e: | ||
| raise InvalidUsage(f'failed to decode JWT: {str(e)}') |
There was a problem hiding this comment.
you could also do
raise InvalidUsage('failed to decode JWT') from eunless the point of this was to get all of that information in the string
| except ValueError as e: | ||
| raise InvalidUsage(f'failed to process invite: {str(e)}') | ||
|
|
||
| return '', HTTPStatus.NO_CONTENT |
There was a problem hiding this comment.
I still don't like returning no content... why don't you have it send back some information about the location that's been registered?
There was a problem hiding this comment.
201 Created specifies you should return the URL of the created entity but there is no URL for the location.
What would you want to return? The location name? I was originally just jsonifying the location, but there's some fields that probably shouldn't be returned and going down the road of to_json and safe_to_json screams bad to me.
- Use app configuration for default and maximum manifest size - Handle integrity error when inserting payload - Also add raising of integrity errors to documentation - make secret key more secret
|
yo whats going on with this PR |
|
BUMP |
Will close #25.