Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pluggable views part 3 #132

Merged
merged 2 commits into from
Mar 21, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
future API naming conventions. (#123)
+ Added an internal api for datapoints to `db.py`. (#125)
+ The REST api is now viewable via a Swagger (or ReDoc!) web page! (#34)
+ Core API routes have been refactored to use Flask's Pluggable Views. (#128)


## 0.5.0 (2019-02-28)
Expand Down
291 changes: 142 additions & 149 deletions src/trendlines/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ def delete(self, datapoint_id):


@api.route("/api/v1/metric")
class Metrics(MethodView):
class Metric(MethodView):
def get(self):
"""
Return a list of all metrics in the database.
Expand Down Expand Up @@ -256,173 +256,166 @@ def post(self):
return jsonify(body), 201


@api.route("/api/v1/metric/<metric_name>", methods=["GET"])
def get_metric_as_json(metric_name):
"""
Return metric information as JSON
"""
logger.debug("API: get metric '%s'" % metric_name)
@api.route("/api/v1/metric/<metric_name>")
class MetricById(MethodView):
def get(self, metric_name):
"""
Return metric information as JSON
"""
logger.debug("API: get metric '%s'" % metric_name)

try:
raw_data = db.Metric.get(db.Metric.name == metric_name)
except DoesNotExist:
return ErrorResponse.metric_not_found(metric_name)
try:
raw_data = db.Metric.get(db.Metric.name == metric_name)
except DoesNotExist:
return ErrorResponse.metric_not_found(metric_name)

data = model_to_dict(raw_data)
data = model_to_dict(raw_data)

return jsonify(data)
return jsonify(data)

def put(self, metric_name):
"""
Replace a metric with new values.

This function cannot change the ``metric_id`` value.

Keys not given are assumed to be ``None``.

@api.route("/api/v1/metric/<metric_name>", methods=["DELETE"])
def delete_metric(metric_name):
logger.debug("'api: DELETE '%s'" % metric_name)
Accepts JSON data with the following format:

try:
found = db.Metric.get(db.Metric.name == metric_name)
found.delete_instance()
except DoesNotExist:
return ErrorResponse.metric_not_found(metric_name)
else:
return "", 204
.. code-block::json
{
"name": "your.metric_name.here",
"units": {string, optional},
"upper_limit": {float, optional},
"lower_limit": {float, optional},
}

Returns
-------
200 :
Success. Returned JSON data has two keys: ``old_value`` and
``new_value``, each containing a full :class:`orm.Metric` object.
400 :
Malformed JSON data (such as when ``name`` is missing)
404 :
The requested metric is not found.
409 :
The metric already exists.

@api.route("/api/v1/metric/<metric_name>", methods=["PUT"])
def put_metric(metric_name):
"""
Replace a metric with new values.

This function cannot change the ``metric_id`` value.

Keys not given are assumed to be ``None``.

Accepts JSON data with the following format:

.. code-block::json
{
"name": "your.metric_name.here",
"units": {string, optional},
"upper_limit": {float, optional},
"lower_limit": {float, optional},
}

Returns
-------
200 :
Success. Returned JSON data has two keys: ``old_value`` and
``new_value``, each containing a full :class:`orm.Metric` object.
400 :
Malformed JSON data (such as when ``name`` is missing)
404 :
The requested metric is not found.
409 :
The metric already exists.


See Also
--------
:func:`routes.get_metric_as_json`
:func:`routes.post_metric`
:func:`routes.delete_metric`
"""
data = request.get_json()

# First see if our item actually exists
try:
metric = db.Metric.get(db.Metric.name == metric_name)
old = model_to_dict(metric)
except DoesNotExist:
return ErrorResponse.metric_not_found(metric_name)
See Also
--------
:func:`routes.get_metric_as_json`
:func:`routes.post_metric`
:func:`routes.delete_metric`
"""
data = request.get_json()

# Parse our json.
try:
name = data['name']
except KeyError:
return ErrorResponse.missing_required_key('name')
# First see if our item actually exists
try:
metric = db.Metric.get(db.Metric.name == metric_name)
old = model_to_dict(metric)
except DoesNotExist:
return ErrorResponse.metric_not_found(metric_name)

# Parse our json.
try:
name = data['name']
except KeyError:
return ErrorResponse.missing_required_key('name')

# Update fields with new values, assuming None if missing.
for k in old.keys():
setattr(metric, k, data.get(k, None))
# Update fields with new values, assuming None if missing.
for k in old.keys():
setattr(metric, k, data.get(k, None))

# We need to manually set the primary key because our little setattr
# hack above doesn't seem to work. We need to set the PK in general so
# that peewee's `save` method performs an UPDATE instead of INSERT.
metric.metric_id = old['metric_id']
# We need to manually set the primary key because our little setattr
# hack above doesn't seem to work. We need to set the PK in general so
# that peewee's `save` method performs an UPDATE instead of INSERT.
metric.metric_id = old['metric_id']

try:
metric.save()
new = model_to_dict(metric)
except IntegrityError:
# Failed the unique constraint on Metric.name
return ErrorResponse.unique_metric_name_required(old['name'], name)
try:
metric.save()
new = model_to_dict(metric)
except IntegrityError:
# Failed the unique constraint on Metric.name
return ErrorResponse.unique_metric_name_required(old['name'], name)

rv = {"old_value": old, "new_value": new}
rv = {"old_value": old, "new_value": new}

return jsonify(rv), 200
return jsonify(rv), 200

def patch(self, metric_name):
"""
Update the values for a given metric.

@api.route("/api/v1/metric/<metric_name>", methods=["PATCH"])
def patch_metric(metric_name):
"""
Update the values for a given metric.

This cannot change the ``metric_id`` value.

Accepts JSON data with the following format:

.. code-block::json
{
"name": {string, optional},
"units": {string, optional},
"upper_limit": {float, optional},
"lower_limit": {float, optional}
}

Returns
-------
200 :
Success. Returned JSON data has two keys: ``old_value`` and
``new_value``, each containing only the values of the
:class:`orm.Metric` object *that were requested to be changed*.
404 :
The requested metric is not found.
409 :
The target metric name already exists.

See Also
--------
:func:`routes.get_metric_as_json`
:func:`routes.post_metric`
:func:`routes.delete_metric`
:func:`routes.put_metric`
"""
# XXX: This is essentially the same code as `put`... Gotta refactor ASAP
data = request.get_json()
This cannot change the ``metric_id`` value.

# First see if our item actually exists
try:
metric = db.Metric.get(db.Metric.name == metric_name)
old = model_to_dict(metric)
except DoesNotExist:
return ErrorResponse.metric_not_found(metric_name)
Accepts JSON data with the following format:

.. code-block::json
{
"name": {string, optional},
"units": {string, optional},
"upper_limit": {float, optional},
"lower_limit": {float, optional}
}

metric = update_model_from_dict(metric, data)
Returns
-------
200 :
Success. Returned JSON data has two keys: ``old_value`` and
``new_value``, each containing only the values of the
:class:`orm.Metric` object *that were requested to be changed*.
404 :
The requested metric is not found.
409 :
The target metric name already exists.

try:
metric.save()
except IntegrityError:
# Failed the unique constraint on Metric.name
return ErrorResponse.unique_metric_name_required(old['name'], metric.name)

new = model_to_dict(metric)

# Only return the values that were requested to be changed (even if they
# did not change).
# This seems like a silly way to do it. Is there a better way?
rv = {'old_value': {}, 'new_value': {}}
for item in data.keys():
rv['old_value'][item] = old[item]
rv['new_value'][item] = new[item]

return jsonify(rv), 200
See Also
--------
:func:`routes.get_metric_as_json`
:func:`routes.post_metric`
:func:`routes.delete_metric`
:func:`routes.put_metric`
"""
# XXX: This is essentially the same code as `put`... Gotta refactor ASAP
data = request.get_json()

# First see if our item actually exists
try:
metric = db.Metric.get(db.Metric.name == metric_name)
old = model_to_dict(metric)
except DoesNotExist:
return ErrorResponse.metric_not_found(metric_name)

metric = update_model_from_dict(metric, data)

try:
metric.save()
except IntegrityError:
# Failed the unique constraint on Metric.name
return ErrorResponse.unique_metric_name_required(old['name'], metric.name)

new = model_to_dict(metric)

# Only return the values that were requested to be changed (even if they
# did not change).
# This seems like a silly way to do it. Is there a better way?
rv = {'old_value': {}, 'new_value': {}}
for item in data.keys():
rv['old_value'][item] = old[item]
rv['new_value'][item] = new[item]

return jsonify(rv), 200

def delete(self, metric_name):
logger.debug("'api: DELETE '%s'" % metric_name)

try:
found = db.Metric.get(db.Metric.name == metric_name)
found.delete_instance()
except DoesNotExist:
return ErrorResponse.metric_not_found(metric_name)
else:
return "", 204