Skip to content

Commit

Permalink
#123 - complete OpenAPI spec documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
louise-davies committed Apr 20, 2020
1 parent dfebf55 commit 03d768f
Show file tree
Hide file tree
Showing 10 changed files with 11,663 additions and 14,116 deletions.
3 changes: 1 addition & 2 deletions dev-requirements.in
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
Faker == 2.0.2
pyyaml == 5.1.2
Faker == 2.0.2
3 changes: 1 addition & 2 deletions dev-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@
# This file is autogenerated by pip-compile
# To update, run:
#
# pip-compile '.\dev-requirements.in'
# pip-compile dev-requirements.in
#
faker==2.0.2
python-dateutil==2.8.0 # via faker
pyyaml==5.1.2
six==1.12.0 # via faker, python-dateutil
text-unidecode==1.3 # via faker
3 changes: 2 additions & 1 deletion requirements.in
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ sqlalchemy == 1.3.8
pymysql == 0.9.3
flask-cors == 3.0.8
apispec == 3.3.0
flask-swagger-ui == 3.25.0
flask-swagger-ui == 3.25.0
pyyaml == 5.1.2
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
# pip-compile requirements.in
#
aniso8601==8.0.0 # via flask-restful
apispec-flask-restful==0.1
apispec==3.3.0
click==7.0 # via flask
flask-cors==3.0.8
Expand All @@ -17,6 +16,7 @@ jinja2==2.10.1 # via flask
markupsafe==1.1.1 # via jinja2
pymysql==0.9.3
pytz==2019.2 # via flask-restful
pyyaml==5.1.2
six==1.12.0 # via flask-cors, flask-restful
sqlalchemy==1.3.8
werkzeug==0.16.0 # via flask
32 changes: 18 additions & 14 deletions src/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,32 +70,36 @@
api.add_resource(Sessions, "/sessions")
spec.path(resource=Sessions, api=api)

# TODO: move this to a script that we run separately?
# with app.test_request_context():
openapi_spec_path = Path(__file__).parent / "swagger/openapi-new.yaml"
with open(openapi_spec_path, "w") as f:
f.write(spec.to_yaml())


@app.route("/openapi.json")
def specs():
resp = app.make_response(json.dumps(spec.to_dict(), indent=2))
resp.mimetype = "application/json"
return resp


# Table specific endpoints
api.add_resource(UsersInvestigations, "/users/<int:id>/investigations")
spec.path(resource=UsersInvestigations, api=api)
api.add_resource(UsersInvestigationsCount,
"/users/<int:id>/investigations/count")
spec.path(resource=UsersInvestigationsCount, api=api)
api.add_resource(InstrumentsFacilityCycles,
"/instruments/<int:id>/facilitycycles")
spec.path(resource=InstrumentsFacilityCycles, api=api)
api.add_resource(InstrumentsFacilityCyclesCount,
"/instruments/<int:id>/facilitycycles/count")
spec.path(resource=InstrumentsFacilityCyclesCount, api=api)
api.add_resource(InstrumentsFacilityCyclesInvestigations,
"/instruments/<int:instrument_id>/facilitycycles/<int:cycle_id>/investigations")
spec.path(resource=InstrumentsFacilityCyclesInvestigations, api=api)
api.add_resource(InstrumentsFacilityCyclesInvestigationsCount,
"/instruments/<int:instrument_id>/facilitycycles/<int:cycle_id>/investigations/count")
spec.path(resource=InstrumentsFacilityCyclesInvestigationsCount, api=api)

openapi_spec_path = Path(__file__).parent / "swagger/openapi.yaml"
with open(openapi_spec_path, "w") as f:
f.write(spec.to_yaml())


@app.route("/openapi.json")
def specs():
resp = app.make_response(json.dumps(spec.to_dict(), indent=2))
resp.mimetype = "application/json"
return resp


if __name__ == "__main__":
app.run(host=config.get_host(), port=config.get_port(),
Expand Down
159 changes: 154 additions & 5 deletions src/resources/entities/entity_endpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,15 @@ def get(self):
- INCLUDE_FILTER
responses:
200:
description: Success - a user's session details
description: Success - returns {table.__name__} that satisfy the filters
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/{table.__name__.strip("_")}'
400:
description: Bad request - something was wrong with the request
description: Bad request - Something was wrong with the request
401:
description: Unauthorized - No session ID was found in the HTTP Authorization header
403:
Expand Down Expand Up @@ -87,7 +87,7 @@ def post(self):
items:
$ref: '#/components/schemas/{table.__name__.strip("_")}'
400:
description: Bad request - something was wrong with the request
description: Bad request - Something was wrong with the request
401:
description: Unauthorized - No session ID was found in the HTTP Authorization header
403:
Expand Down Expand Up @@ -120,7 +120,7 @@ def patch(self):
$ref: '#/components/schemas/{table.__name__.strip("_")}'
responses:
200:
description: Success - returns the updated objects
description: Success - returns the updated object(s)
content:
application/json:
schema:
Expand All @@ -130,7 +130,7 @@ def patch(self):
items:
$ref: '#/components/schemas/{table.__name__.strip("_")}'
400:
description: Bad request - something was wrong with the request
description: Bad request - Something was wrong with the request
401:
description: Unauthorized - No session ID was found in the HTTP Authorization header
403:
Expand Down Expand Up @@ -159,18 +159,111 @@ class EndpointWithID(Resource):
def get(self, id):
return get_row_by_id(table, id).to_dict(), 200

get.__doc__ = f"""
---
summary: Find the {table.__name__} matching the given ID
description: Retrieves a list of {table.__name__} objects
tags:
- {name}
parameters:
- in: path
required: true
name: ID
description: The id of the entity to retrieve
schema:
type: integer
responses:
200:
description: Success - the matching {table.__name__}
content:
application/json:
schema:
$ref: '#/components/schemas/{table.__name__.strip("_")}'
400:
description: Bad request - Something was wrong with the request
401:
description: Unauthorized - No session ID was found in the HTTP Authorization header
403:
description: Forbidden - The session ID provided is invalid
404:
description: No such record - Unable to find a record in the database
"""

@requires_session_id
@queries_records
def delete(self, id):
delete_row_by_id(table, id)
return "", 204

delete.__doc__ = f"""
---
summary: Delete {name} by id
description: Updates {table.__name__} with the specified ID with details provided in the request body
tags:
- {name}
parameters:
- in: path
required: true
name: ID
description: The id of the entity to delete
schema:
type: integer
responses:
204:
description: No Content - Object was successfully deleted
400:
description: Bad request - Something was wrong with the request
401:
description: Unauthorized - No session ID was found in the HTTP Authorization header
403:
description: Forbidden - The session ID provided is invalid
404:
description: No such record - Unable to find a record in the database
"""

@requires_session_id
@queries_records
def patch(self, id):
update_row_from_id(table, id, request.json)
return get_row_by_id(table, id).to_dict(), 200

patch.__doc__ = f"""
---
summary: Update {name} by id
description: Updates {table.__name__} with the specified ID with details provided in the request body
tags:
- {name}
parameters:
- in: path
required: true
name: ID
description: The id of the entity to update
schema:
type: integer
requestBody:
description: The values to use to update the object(s) with
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/{table.__name__.strip("_")}'
responses:
200:
description: Success - returns the updated object
content:
application/json:
schema:
$ref: '#/components/schemas/{table.__name__.strip("_")}'
400:
description: Bad request - Something was wrong with the request
401:
description: Unauthorized - No session ID was found in the HTTP Authorization header
403:
description: Forbidden - The session ID provided is invalid
404:
description: No such record - Unable to find a record in the database
"""

EndpointWithID.__name__ = f"{name}WithID"
return EndpointWithID

Expand All @@ -192,6 +285,32 @@ def get(self):
filters = get_filters_from_query_string()
return get_filtered_row_count(table, filters), 200

get.__doc__ = f"""
---
summary: Count {name}
description: Return the count of the {table.__name__} objects that would be retrieved given the filters provided
tags:
- {name}
parameters:
- WHERE_FILTER
- DISTINCT_FILTER
responses:
200:
description: Success - The count of the {table.__name__} objects
content:
application/json:
schema:
type: integer
400:
description: Bad request - Something was wrong with the request
401:
description: Unauthorized - No session ID was found in the HTTP Authorization header
403:
description: Forbidden - The session ID provided is invalid
404:
description: No such record - Unable to find a record in the database
"""

CountEndpoint.__name__ = f"{name}Count"
return CountEndpoint

Expand All @@ -213,5 +332,35 @@ def get(self):
filters = get_filters_from_query_string()
return get_first_filtered_row(table, filters), 200

get.__doc__ = f"""
---
summary: Get single {table.__name__}
description: Retrieves the first {table.__name__} objects that satisfies the filters.
tags:
- {name}
parameters:
- WHERE_FILTER
- ORDER_FILTER
- LIMIT_FILTER
- SKIP_FILTER
- DISTINCT_FILTER
- INCLUDE_FILTER
responses:
200:
description: Success - a {table.__name__} object that satisfies the filters
content:
application/json:
schema:
$ref: '#/components/schemas/{table.__name__.strip("_")}'
400:
description: Bad request - Something was wrong with the request
401:
description: Unauthorized - No session ID was found in the HTTP Authorization header
403:
description: Forbidden - The session ID provided is invalid
404:
description: No such record - Unable to find a record in the database
"""

FindOneEndpoint.__name__ = f"{name}FindOne"
return FindOneEndpoint
Loading

0 comments on commit 03d768f

Please sign in to comment.