From 166bafece2c4509cb5a4a76b2e6a0229bc419ecf Mon Sep 17 00:00:00 2001 From: Buckwich Date: Fri, 14 Feb 2020 11:33:16 +0100 Subject: [PATCH 1/6] add swagger ui & tag current doc --- requirements.txt | 2 + web.py | 162 +++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 137 insertions(+), 27 deletions(-) diff --git a/requirements.txt b/requirements.txt index 80d6bce..fd9330a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,3 +7,5 @@ gunicorn requests==2.20.1 rq sparse_list +flask_swagger +flask_swagger_ui diff --git a/web.py b/web.py index 345765a..fc72208 100644 --- a/web.py +++ b/web.py @@ -14,6 +14,9 @@ from flask_cors import CORS from rq import Queue +from flask_swagger import swagger +from flask_swagger_ui import get_swaggerui_blueprint + import qmk_redis import qmk_storage from kle2xy import KLE2xy @@ -140,16 +143,49 @@ def kle_to_qmk(kle): ## Views + +### swagger specific ### +SWAGGER_URL = '/swagger' +API_URL = '/spec' +SWAGGERUI_BLUEPRINT = get_swaggerui_blueprint( + SWAGGER_URL, + API_URL, + config={ + 'app_name': "QMK API" + } +) +app.register_blueprint(SWAGGERUI_BLUEPRINT, url_prefix=SWAGGER_URL) +@app.route("/spec") +def spec(): + """ + Swagger JSON + --- + tags: + - General + """ + swag = swagger(app) + swag['info']['version'] = "1.0" + swag['info']['title'] = "QMK API" + return jsonify(swag) + @app.route('/', methods=['GET']) def root(): - """Serve up the documentation for this API. + """ + Redirect to official docs + --- + tags: + - General """ return redirect('https://docs.api.qmk.fm/') @app.route('/v1', methods=['GET']) def GET_v1(): - """Return the API's status. + """ + Return the API's status. + --- + tags: + - General """ return jsonify({ 'children': ['compile', 'converters', 'keyboards'], @@ -162,12 +198,18 @@ def GET_v1(): @app.route('/v1/healthcheck', methods=['GET']) def GET_v1_healthcheck(): - """Checks over the health of the API. - + """ + Checks over the health of the API. + --- + tags: + - Dangerous + """ + ¨¨""" Note: This is used for operational purposes. Please don't hit it on the live api.qmk.fm site without talking to us first. Most of this information is available at the /v1 endpoint as well. """ + rq.enqueue(ping, at_front=True) return jsonify({ @@ -200,7 +242,11 @@ def GET_v1_update(): @app.route('/v1/converters', methods=['GET']) def GET_v1_converters(): - """Return the list of converters we support. + """ + Return the list of converters we support. + --- + tags: + - Converter """ return jsonify({'children': ['kle']}) @@ -208,7 +254,11 @@ def GET_v1_converters(): @app.route('/v1/converters/kle2qmk', methods=['POST']) @app.route('/v1/converters/kle', methods=['POST']) def POST_v1_converters_kle(): - """Convert a KLE layout to QMK's layout format. + """ + Convert a KLE layout to QMK's layout format. + --- + tags: + - Converter """ data = request.get_json(force=True) if not data: @@ -250,7 +300,11 @@ def POST_v1_converters_kle(): @app.route('/v1/keyboards', methods=['GET']) def GET_v1_keyboards(): - """Return a list of keyboards + """ + Return a list of keyboards + --- + tags: + - Keyboards """ json_blob = qmk_redis.get('qmk_api_keyboards') return jsonify(json_blob) @@ -258,7 +312,11 @@ def GET_v1_keyboards(): @app.route('/v1/keyboards/all', methods=['GET']) def GET_v1_keyboards_all(): - """Return JSON showing all available keyboards and their layouts. + """ + Return JSON showing all available keyboards and their layouts. + --- + tags: + - Keyboards """ allkb = qmk_redis.get('qmk_api_kb_all') if allkb: @@ -268,7 +326,11 @@ def GET_v1_keyboards_all(): @app.route('/v1/keyboards/', methods=['GET']) def GET_v1_keyboards_keyboard(keyboard): - """Return JSON showing data about a keyboard + """ + Return JSON showing data about a keyboard + --- + tags: + - Keyboards """ keyboards = qmk_redis.get('qmk_api_last_updated') keyboards['keyboards'] = {} @@ -286,7 +348,11 @@ def GET_v1_keyboards_keyboard(keyboard): @app.route('/v1/keyboards//readme', methods=['GET']) def GET_v1_keyboards_keyboard_readme(keyboard): - """Returns the readme for a keyboard. + """ + Returns the readme for a keyboard. + --- + tags: + - Keyboards """ readme = qmk_redis.get('qmk_api_kb_%s_readme' % (keyboard)) @@ -298,7 +364,11 @@ def GET_v1_keyboards_keyboard_readme(keyboard): @app.route('/v1/keyboards//keymaps/', methods=['GET']) def GET_v1_keyboards_keyboard_keymaps_keymap(keyboard, keymap): - """Return JSON showing data about a keyboard's keymap + """ + Return JSON showing data about a keyboard's keymap + --- + tags: + - Keyboards """ keyboards = qmk_redis.get('qmk_api_last_updated') keyboards['keyboards'] = {} @@ -326,7 +396,11 @@ def GET_v1_keyboards_keyboard_keymaps_keymap(keyboard, keymap): @app.route('/v1/keyboards//keymaps//readme', methods=['GET']) def GET_v1_keyboards_keyboard_keymaps_keymap_readme(keyboard, keymap): - """Returns the readme for a keymap. + """ + Returns the readme for a keymap. + --- + tags: + - Keyboards """ readme = qmk_redis.get('qmk_api_kb_%s_keymap_%s_readme' % (keyboard, keymap)) @@ -338,8 +412,11 @@ def GET_v1_keyboards_keyboard_keymaps_keymap_readme(keyboard, keymap): @app.route('/v1/keyboards/build_status', methods=['GET']) def GET_v1_keyboards_build_status(): - """Returns a dictionary of keyboard/layout pairs. Each entry is True if the keyboard works in configurator and - false if it doesn't. + """ + Returns a dictionary of keyboard/layout pairs. Each entry is True if the keyboard works in configurator and false if it doesn't. + --- + tags: + - Keyboards """ json_blob = qmk_redis.get('qmk_api_keyboards_tested') return jsonify(json_blob) @@ -347,18 +424,25 @@ def GET_v1_keyboards_build_status(): @app.route('/v1/keyboards/build_log', methods=['GET']) def GET_v1_keyboards_build_log(): - """Returns a dictionary of keyboard/layout pairs. Each entry is a dictionary with the following keys: - - * `works`: Boolean indicating whether the compile was successful - * `message`: The compile output for failed builds """ + Returns a dictionary of keyboard/layout pairs. Each entry is a dictionary with the following keys + --- + tags: + - Keyboards + """ json_data = qmk_redis.get('qmk_api_configurator_status') return jsonify(json_data) + # * `works` Boolean indicating whether the compile was successful + # * `message` The compile output for failed builds @app.route('/v1/keyboards/error_log', methods=['GET']) def GET_v1_keyboards_error_log(): - """Return the error log from the last run. + """ + Return the error log from the last run. + --- + tags: + - Keyboards """ json_blob = qmk_redis.get('qmk_api_update_error_log') @@ -367,7 +451,11 @@ def GET_v1_keyboards_error_log(): @app.route('/v1/usb', methods=['GET']) def GET_v1_usb(): - """Returns the list of USB device identifiers used in QMK. + """ + Returns the list of USB device identifiers used in QMK. + --- + tags: + - USB """ json_blob = qmk_redis.get('qmk_api_usb_list') @@ -376,7 +464,11 @@ def GET_v1_usb(): @app.route('/v1/compile', methods=['POST']) def POST_v1_compile(): - """Enqueue a compile job. + """ + Enqueue a compile job. + --- + tags: + - Compile """ data = request.get_json(force=True) if not data: @@ -391,7 +483,11 @@ def POST_v1_compile(): @app.route('/v1/compile/', methods=['GET']) def GET_v1_compile_job_id(job_id): - """Fetch the status of a compile job. + """ + Fetch the status of a compile job. + --- + tags: + - Compile """ # Check redis first. job = rq.fetch_job(job_id) @@ -429,10 +525,14 @@ def GET_v1_compile_job_id(job_id): @app.route('/v1/compile//download', methods=['GET']) @app.route('/v1/compile//hex', methods=['GET']) def GET_v1_compile_job_id_bin(job_id): - """Download a compiled firmware. - - New clients should prefer the `/download` URL. `/hex` is deprecated and will be removed in a future version. """ + Download a compiled firmware. + New clients should prefer the `/download` URL. `/hex` is deprecated and will be removed in a future version. + --- + tags: + - Compile + """ + job = get_job_metadata(job_id) if not job: return error("Compile job not found", 404) @@ -442,7 +542,11 @@ def GET_v1_compile_job_id_bin(job_id): @app.route('/v1/compile//keymap', methods=['GET']) def GET_v1_compile_job_id_keymap(job_id): - """Download the keymap for a completed compile job. + """ + Download the keymap for a completed compile job. + --- + tags: + - Compile """ job = get_job_metadata(job_id) if not job: @@ -453,7 +557,11 @@ def GET_v1_compile_job_id_keymap(job_id): @app.route('/v1/compile//source', methods=['GET']) def GET_v1_compile_job_id_src(job_id): - """Download the full source for a completed compile job. + """ + Download the full source for a completed compile job. + --- + tags: + - Compile """ job = get_job_metadata(job_id) if not job: From d32b095d1aa0bf7ef4ceb390cbf87c9ef96103ee Mon Sep 17 00:00:00 2001 From: Buckwich Date: Fri, 14 Feb 2020 11:38:07 +0100 Subject: [PATCH 2/6] add notes --- web.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/web.py b/web.py index fc72208..a2c6601 100644 --- a/web.py +++ b/web.py @@ -200,16 +200,11 @@ def GET_v1(): def GET_v1_healthcheck(): """ Checks over the health of the API. + This is used for operational purposes. Please don't hit it on the live api.qmk.fm site without talking to us first. Most of this information is available at the /v1 endpoint as well. --- tags: - Dangerous """ - ¨¨""" - Note: This is used for operational purposes. Please don't hit it on the - live api.qmk.fm site without talking to us first. Most of this - information is available at the /v1 endpoint as well. - """ - rq.enqueue(ping, at_front=True) return jsonify({ @@ -426,6 +421,8 @@ def GET_v1_keyboards_build_status(): def GET_v1_keyboards_build_log(): """ Returns a dictionary of keyboard/layout pairs. Each entry is a dictionary with the following keys + * `works`: Boolean indicating whether the compile was successful + * `message` The compile output for failed builds --- tags: - Keyboards From c2d955485808fd4192baf479dfa6d9338d289dcc Mon Sep 17 00:00:00 2001 From: Buckwich Date: Fri, 14 Feb 2020 11:40:11 +0100 Subject: [PATCH 3/6] add swagger link to readme --- readme.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/readme.md b/readme.md index 9617c83..d8ddf4c 100644 --- a/readme.md +++ b/readme.md @@ -18,3 +18,5 @@ We follow the style guidelines for QMK and have provided a yapf config in [setup Our documentation lives on GitBook and can be found here: > https://docs.api.qmk.fm/using-the-api + +The swagger documentation can be found at `API_URL/spec` (JSON) or `API_URL/swagger` (UI) From 73fe9b88714ae159177ff9463da29426cdb50e19 Mon Sep 17 00:00:00 2001 From: Buckwich Date: Fri, 14 Feb 2020 13:28:27 +0100 Subject: [PATCH 4/6] add path paramters --- .gitignore | 2 ++ Dockerfile | 2 +- bin/setup_virtualenv | 6 +++--- bin/start_minio | 2 +- docker-compose.yml | 18 +++++++++--------- web.py | 38 +++++++++++++++++++++++++++++++++++--- 6 files changed, 51 insertions(+), 17 deletions(-) diff --git a/.gitignore b/.gitignore index 1a5830a..cb7f5e3 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,5 @@ firmwares __pycache__ *.egg-info *.pyc +.qmk_compiler_api-* +activate* \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 5097eb2..057cd6d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,7 +4,7 @@ EXPOSE 5001 WORKDIR /qmk_api COPY . /qmk_api -RUN pip3 install -r requirements.txt git+git://github.com/qmk/qmk_compiler.git@master git+git://github.com/skullydazed/kle2xy.git@master +RUN pip3 install -r requirements.txt git+https://github.com/qmk/qmk_compiler.git@master git+https://github.com/skullydazed/kle2xy.git@master ENV LC_ALL=C.UTF-8 ENV LANG=C.UTF-8 CMD ./run diff --git a/bin/setup_virtualenv b/bin/setup_virtualenv index b542684..cc0356f 100755 --- a/bin/setup_virtualenv +++ b/bin/setup_virtualenv @@ -71,10 +71,10 @@ ln -s .qmk_compiler_api-$PYTHON_VERSION/bin/activate activate-$PYTHON_VERSION || # Setup the environment source activate-$PYTHON_VERSION || exit pip install -r requirements.txt || exit -if [ -d ../qmk_compiler_worker ]; then - pip install --editable ../qmk_compiler_worker +if [ -d ../qmk_compiler ]; then + pip install --editable ../qmk_compiler else - pip install git+git://github.com/qmk/qmk_compiler_worker.git@master + pip install git+git://github.com/qmk/qmk_compiler.git@master fi cat << EOF diff --git a/bin/start_minio b/bin/start_minio index c104cfa..b9a5d06 100755 --- a/bin/start_minio +++ b/bin/start_minio @@ -1,4 +1,4 @@ -#!/bin/sh +1#!/bin/sh S3_ACCESS_KEY='minio_dev' S3_SECRET_KEY='minio_dev_secret' diff --git a/docker-compose.yml b/docker-compose.yml index 42738c0..1437f90 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,4 @@ -version: '3' +version: "3" # This file should live in a directory about `qmk_api` and `qmk_compiler` # Eventually we would have some sort of git subdir or something to contain @@ -6,18 +6,18 @@ version: '3' services: qmk_api: - build: ./qmk_api + build: . ports: - - "5000:5000" + - "5000:5000" environment: REDIS_HOST: redis S3_HOST: http://minio:9000 S3_ACCESS_KEY: minio_dev S3_SECRET_KEY: minio_dev_secret - UPDATE_API: 'true' + UPDATE_API: "true" qmk_compiler: - build: ./qmk_compiler + build: ../qmk_compiler environment: MINIO_ACCESS_KEY: minio_dev MINIO_SECRET_KEY: minio_dev_secret @@ -27,9 +27,9 @@ services: minio: image: minio/minio:RELEASE.2018-09-25T21-34-43Z volumes: - - data:/data + - data:/data ports: - - "9000:9000" + - "9000:9000" environment: MINIO_ACCESS_KEY: minio_dev MINIO_SECRET_KEY: minio_dev_secret @@ -47,8 +47,8 @@ services: # is supposedly where redis looks for its conf # - ./redis/:/usr/local/etc/redis/ ports: - - "6379:6379" + - "6379:6379" restart: always volumes: - data: + data: diff --git a/web.py b/web.py index a2c6601..a42d31d 100644 --- a/web.py +++ b/web.py @@ -218,7 +218,11 @@ def GET_v1_healthcheck(): @app.route('/v1/update', methods=['GET']) def GET_v1_update(): - """Triggers an update of the API. + """ + Triggers an update of the API. + --- + tags: + - Dangerous """ result = { 'result': UPDATE_API, @@ -311,7 +315,7 @@ def GET_v1_keyboards_all(): Return JSON showing all available keyboards and their layouts. --- tags: - - Keyboards + - Dangerous """ allkb = qmk_redis.get('qmk_api_kb_all') if allkb: @@ -326,6 +330,9 @@ def GET_v1_keyboards_keyboard(keyboard): --- tags: - Keyboards + parameters: + - in: path + name: keyboard """ keyboards = qmk_redis.get('qmk_api_last_updated') keyboards['keyboards'] = {} @@ -348,6 +355,9 @@ def GET_v1_keyboards_keyboard_readme(keyboard): --- tags: - Keyboards + parameters: + - in: path + name: keyboard """ readme = qmk_redis.get('qmk_api_kb_%s_readme' % (keyboard)) @@ -364,6 +374,11 @@ def GET_v1_keyboards_keyboard_keymaps_keymap(keyboard, keymap): --- tags: - Keyboards + parameters: + - in: path + name: keyboard + - in: path + name: keymap """ keyboards = qmk_redis.get('qmk_api_last_updated') keyboards['keyboards'] = {} @@ -396,6 +411,11 @@ def GET_v1_keyboards_keyboard_keymaps_keymap_readme(keyboard, keymap): --- tags: - Keyboards + parameters: + - in: path + name: keyboard + - in: path + name: keymap """ readme = qmk_redis.get('qmk_api_kb_%s_keymap_%s_readme' % (keyboard, keymap)) @@ -485,6 +505,9 @@ def GET_v1_compile_job_id(job_id): --- tags: - Compile + parameters: + - in: path + name: job_id """ # Check redis first. job = rq.fetch_job(job_id) @@ -528,6 +551,9 @@ def GET_v1_compile_job_id_bin(job_id): --- tags: - Compile + parameters: + - in: path + name: job_id """ job = get_job_metadata(job_id) @@ -541,9 +567,12 @@ def GET_v1_compile_job_id_bin(job_id): def GET_v1_compile_job_id_keymap(job_id): """ Download the keymap for a completed compile job. - --- + --- tags: - Compile + parameters: + - in: path + name: job_id """ job = get_job_metadata(job_id) if not job: @@ -559,6 +588,9 @@ def GET_v1_compile_job_id_src(job_id): --- tags: - Compile + parameters: + - in: path + name: job_id """ job = get_job_metadata(job_id) if not job: From 5187e835b42949d61f24cd283618c170befdfcd1 Mon Sep 17 00:00:00 2001 From: Buckwich Date: Sun, 19 Apr 2020 13:29:25 +0200 Subject: [PATCH 5/6] modify swagger path --- web.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/web.py b/web.py index 50a1b27..6abfedc 100644 --- a/web.py +++ b/web.py @@ -151,16 +151,15 @@ def kle_to_qmk(kle): ## Views ### swagger specific ### -SWAGGER_URL = '/swagger' -API_URL = '/spec' SWAGGERUI_BLUEPRINT = get_swaggerui_blueprint( - SWAGGER_URL, - API_URL, + "", + "/spec", config={ 'app_name': "QMK API" } ) -app.register_blueprint(SWAGGERUI_BLUEPRINT, url_prefix=SWAGGER_URL) +app.register_blueprint(SWAGGERUI_BLUEPRINT, url_prefix="/swagger") + @app.route("/spec") def spec(): """ From 41898d096497472d8620b0602cfd61bc2d3e0853 Mon Sep 17 00:00:00 2001 From: Buckwich Date: Sun, 19 Apr 2020 16:49:22 +0200 Subject: [PATCH 6/6] change swagger framework --- requirements.txt | 3 +-- web.py | 24 ++---------------------- 2 files changed, 3 insertions(+), 24 deletions(-) diff --git a/requirements.txt b/requirements.txt index 67c1393..f51ce4f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,5 +7,4 @@ gunicorn requests rq sparse_list -flask_swagger -flask_swagger_ui +flasgger diff --git a/web.py b/web.py index 6abfedc..7464780 100644 --- a/web.py +++ b/web.py @@ -14,8 +14,7 @@ from flask_cors import CORS from rq import Queue -from flask_swagger import swagger -from flask_swagger_ui import get_swaggerui_blueprint +from flasgger import Swagger import qmk_redis import qmk_storage @@ -54,6 +53,7 @@ def default(self, obj): cors = CORS(app, resources={'/v*/*': {'origins': '*'}}) rq = Queue(connection=redis) +swagger = Swagger(app) ## Helper functions def client_ip(): @@ -151,27 +151,7 @@ def kle_to_qmk(kle): ## Views ### swagger specific ### -SWAGGERUI_BLUEPRINT = get_swaggerui_blueprint( - "", - "/spec", - config={ - 'app_name': "QMK API" - } -) -app.register_blueprint(SWAGGERUI_BLUEPRINT, url_prefix="/swagger") -@app.route("/spec") -def spec(): - """ - Swagger JSON - --- - tags: - - General - """ - swag = swagger(app) - swag['info']['version'] = "1.0" - swag['info']['title'] = "QMK API" - return jsonify(swag) @app.route('/', methods=['GET']) def root():