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/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) diff --git a/requirements.txt b/requirements.txt index 1d0d658..f51ce4f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,3 +7,4 @@ gunicorn requests rq sparse_list +flasgger diff --git a/web.py b/web.py index 31fd9fa..7464780 100644 --- a/web.py +++ b/web.py @@ -14,6 +14,8 @@ from flask_cors import CORS from rq import Queue +from flasgger import Swagger + import qmk_redis import qmk_storage from kle2xy import KLE2xy @@ -51,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(): @@ -146,16 +149,28 @@ def kle_to_qmk(kle): ## Views + +### swagger specific ### + + @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'], @@ -168,11 +183,12 @@ def GET_v1(): @app.route('/v1/healthcheck', methods=['GET']) def GET_v1_healthcheck(): - """Checks over the health of the API. - - 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. + """ + 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 """ rq.enqueue(ping, at_front=True) @@ -187,7 +203,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, @@ -206,7 +226,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']}) @@ -214,7 +238,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: @@ -256,7 +284,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) @@ -264,7 +296,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: + - Dangerous """ allkb = qmk_redis.get('qmk_api_kb_all') if allkb: @@ -274,7 +310,14 @@ 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 + parameters: + - in: path + name: keyboard """ keyboards = qmk_redis.get('qmk_api_last_updated') keyboards['keyboards'] = {} @@ -292,7 +335,14 @@ 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 + parameters: + - in: path + name: keyboard """ readme = qmk_redis.get('qmk_api_kb_%s_readme' % (keyboard)) @@ -322,8 +372,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) @@ -331,18 +384,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 + * `works`: Boolean indicating whether the compile was successful + * `message` The compile output for failed builds + --- + tags: + - Keyboards + """ json_data = qmk_redis.get('qmk_api_configurator_status') return jsonify(json_data) @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') @@ -351,7 +411,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') @@ -360,7 +424,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: @@ -375,7 +443,14 @@ 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 + parameters: + - in: path + name: job_id """ # Check redis first. job = rq.fetch_job(job_id) @@ -413,10 +488,17 @@ 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 + parameters: + - in: path + name: job_id + """ + job = get_job_metadata(job_id) if not job: return error("Compile job not found", 404) @@ -426,7 +508,14 @@ 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 + parameters: + - in: path + name: job_id """ job = get_job_metadata(job_id) if not job: @@ -437,7 +526,14 @@ 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 + parameters: + - in: path + name: job_id """ job = get_job_metadata(job_id) if not job: