From 961f22f0b42dd33b973a4bbd58dd810f024aaf8f Mon Sep 17 00:00:00 2001 From: Matt Prahl Date: Fri, 12 Aug 2016 09:24:28 -0400 Subject: [PATCH] Add pagination and verbose flag to the /rida/module-builds/ route Adds details about the API changes --- README.rst | 108 +++++++++++++++++++++++++++++++++++++++++++++---- rida/models.py | 12 ++++++ rida/utils.py | 31 +++++++++++++- rida/views.py | 32 ++++++++++----- 4 files changed, 164 insertions(+), 19 deletions(-) diff --git a/README.rst b/README.rst index ad0735f4..219966b5 100644 --- a/README.rst +++ b/README.rst @@ -97,20 +97,112 @@ The list of all tracked builds and their states can be obtained by querying the :: - [ + { + "items": [ { - "id": 41", - "state": "done" + "id": 1, + "state": 3 }, { - "id": 42, - "state": "build" + "id": 2, + "state": 3 }, { - "id": 43, - "state": "init" + "id": 3, + "state": 3 + }, + { + "id": 4, + "state": 4 + }, + { + "id": 5, + "state": 4 + }, + { + "id": 6, + "state": 4 + }, + { + "id": 7, + "state": 4 + }, + { + "id": 8, + "state": 4 + }, + { + "id": 9, + "state": 4 + }, + { + "id": 10, + "state": 1 } - ] + ], + "meta": { + "first": "https://rida.fedora.local:5000/rida/module-builds/?per_page=10&page=1", + "last": "https://rida.fedora.local:5000/rida/module-builds/?per_page=10&page=3", + "next": "https://rida.fedora.local:5000/rida/module-builds/?per_page=10&page=2", + "page": 1, + "pages": 3, + "per_page": 10, + "total": 30 + } + } + + +The API is paginated, and defaults to 10 items per page. These values are configurable with the `page` and `per_page` +GET parameters respectively. Additionally, there is a `verbose` parameter that defaults to false, which allows you to +query all the builds with the same amount of detail as querying them individually. + +:: + + GET /rida/module-builds/?verbose=true&per_page=3&page=1 + +:: + + HTTP 200 OK + +:: + + { + "items": [ + { + "id": 1, + "state": 3, + "tasks": { + "rpms/bash": "90109464/1", + "rpms/module-build-macros": "90109446/1" + } + }, + { + "id": 2, + "state": 3, + "tasks": { + "rpms/bash": "90109465/1", + "rpms/module-build-macros": "90109450/1" + } + }, + { + "id": 3, + "state": 3, + "tasks": { + "rpms/bash": "90109497/1", + "rpms/module-build-macros": "90109480/1" + } + } + ], + "meta": { + "first": "https://127.0.0.1:5000/rida/module-builds/?per_page=3&page=1", + "last": "https://127.0.0.1:5000/rida/module-builds/?per_page=3&page=10", + "next": "https://127.0.0.1:5000/rida/module-builds/?per_page=3&page=2", + "page": 1, + "pages": 10, + "per_page": 3, + "total": 30 + } + } HTTP Response Codes diff --git a/rida/models.py b/rida/models.py index 6863d628..c2b6b677 100644 --- a/rida/models.py +++ b/rida/models.py @@ -192,6 +192,18 @@ class ModuleBuild(RidaBase): 'component_builds': [build.id for build in self.component_builds], } + def tasks(self): + """ + :return: dictionary containing the tasks associated with the build + """ + tasks = dict() + if self.id and self.state != 'init': + + for build in ComponentBuild.query.filter_by(module_id=self.id).all(): + tasks["%s/%s" % (build.format, build.package)] = "%s/%s" % (build.task_id, build.state) + + return tasks + def __repr__(self): return "" % ( self.name, self.version, self.release, diff --git a/rida/utils.py b/rida/utils.py index ecef85ae..3a54c216 100644 --- a/rida/utils.py +++ b/rida/utils.py @@ -19,11 +19,12 @@ # SOFTWARE. # # Written by Ralph Bean +# Matt Prahl """ Utility functions for rida. """ - +from flask import request, url_for import functools import time -from rida import log +from rida import log, models def retry(timeout=120, interval=30, wait_on=Exception): @@ -69,3 +70,29 @@ def start_next_build_batch(module, session, builder, components=None): c.task_id = builder.build(artifact_name=c.package, source=c.scmurl) session.commit() + + +def pagination_metadata(p_query): + """ + Returns a dictionary containing metadata about the paginated query. This must be run as part of a Flask request. + :param p_query: flask_sqlalchemy.Pagination object + :return: a dictionary containing metadata about the paginated query + """ + + pagination_data = { + 'page': p_query.page, + 'per_page': p_query.per_page, + 'total': p_query.total, + 'pages': p_query.pages, + 'first': url_for(request.endpoint, page=1, per_page=p_query.per_page, _external=True), + 'last': url_for(request.endpoint, page=p_query.pages, per_page=p_query.per_page, _external=True) + } + + if p_query.has_prev: + pagination_data['prev'] = url_for(request.endpoint, page=p_query.prev_num, + per_page=p_query.per_page, _external=True) + if p_query.has_next: + pagination_data['next'] = url_for(request.endpoint, page=p_query.next_num, + per_page=p_query.per_page, _external=True) + + return pagination_data diff --git a/rida/views.py b/rida/views.py index f30c9c52..4975c5a2 100644 --- a/rida/views.py +++ b/rida/views.py @@ -21,6 +21,7 @@ # SOFTWARE. # # Written by Petr Ĺ abata +# Matt Prahl """ The module build orchestrator for Modularity, API. This is the implementation of the orchestrator's public RESTful API. @@ -38,6 +39,7 @@ import shutil import tempfile from rida import app, conf, db, log from rida import models +from rida.utils import pagination_metadata @app.route("/rida/module-builds/", methods=["POST"]) @@ -143,23 +145,35 @@ def submit_build(): @app.route("/rida/module-builds/", methods=["GET"]) def query_builds(): """Lists all tracked module builds.""" - return jsonify(items=[{"id": x.id, "state": x.state} - for x in models.ModuleBuild.query.all()]), 200 + page = request.args.get('page', 1, type=int) + per_page = request.args.get('per_page', 10, type=int) + p_query = models.ModuleBuild.query.paginate(page, per_page, False) + verbose_flag = request.args.get('verbose', 'false') + + json_data = { + 'meta': pagination_metadata(p_query) + } + + if verbose_flag.lower() == 'true' or verbose_flag == '1': + json_data['items'] = [{'id': item.id, 'state': item.state, 'tasks': item.tasks()} + for item in p_query.items] + else: + json_data['items'] = [{'id': item.id, 'state': item.state} for item in p_query.items] + + return jsonify(json_data), 200 + @app.route("/rida/module-builds/", methods=["GET"]) def query_build(id): """Lists details for the specified module builds.""" module = models.ModuleBuild.query.filter_by(id=id).first() + if module: - tasks = dict() - if module.state != "init": - for build in models.ComponentBuild.query.filter_by(module_id=id).all(): - tasks[build.format + "/" + build.package] = \ - str(build.task_id) + "/" + str(build.state) + return jsonify({ "id": module.id, "state": module.state, - "tasks": tasks - }), 200 + "tasks": module.tasks() + }), 200 else: return "No such module found.", 404