|
| 1 | +# -*- coding: utf-8 -*- |
| 2 | +# |
| 3 | +# Copyright 2020 - Swiss Data Science Center (SDSC) |
| 4 | +# A partnership between École Polytechnique Fédérale de Lausanne (EPFL) and |
| 5 | +# Eidgenössische Technische Hochschule Zürich (ETHZ). |
| 6 | +# |
| 7 | +# Licensed under the Apache License, Version 2.0 (the "License"); |
| 8 | +# you may not use this file except in compliance with the License. |
| 9 | +# You may obtain a copy of the License at |
| 10 | +# |
| 11 | +# http://www.apache.org/licenses/LICENSE-2.0 |
| 12 | +# |
| 13 | +# Unless required by applicable law or agreed to in writing, software |
| 14 | +# distributed under the License is distributed on an "AS IS" BASIS, |
| 15 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 16 | +# See the License for the specific language governing permissions and |
| 17 | +# limitations under the License. |
| 18 | +"""Renku service apispec views.""" |
| 19 | +from apispec import APISpec |
| 20 | +from apispec.ext.marshmallow import MarshmallowPlugin |
| 21 | +from apispec_webframeworks.flask import FlaskPlugin |
| 22 | +from flask import Blueprint, current_app, jsonify |
| 23 | + |
| 24 | +from renku.service.config import ( |
| 25 | + API_VERSION, |
| 26 | + OIDC_URL, |
| 27 | + OPENAPI_VERSION, |
| 28 | + SERVICE_API_BASE_PATH, |
| 29 | + SERVICE_NAME, |
| 30 | + SERVICE_PREFIX, |
| 31 | +) |
| 32 | + |
| 33 | +apispec_blueprint = Blueprint("apispec", __name__, url_prefix=SERVICE_PREFIX) |
| 34 | + |
| 35 | +# security schemes |
| 36 | +oidc_scheme = {"type": "openIdConnect", "openIdConnectUrl": OIDC_URL} |
| 37 | +jwt_scheme = {"type": "apiKey", "name": "Renku-User", "in": "header"} |
| 38 | +gitlab_token_scheme = {"type": "apiKey", "name": "Authorization", "in": "header"} |
| 39 | + |
| 40 | +TOP_LEVEL_DESCRIPTION = """ |
| 41 | +This is the API specification of the renku core service. The API follows the |
| 42 | +[JSON-RPC 2.0](https://www.jsonrpc.org/specification) specifications and mirrors |
| 43 | +the functionality of the renku CLI. |
| 44 | +
|
| 45 | +The basic API is low-level and requires that the client handles project |
| 46 | +(repository) state in the service cache by invoking the `cache.project_clone` |
| 47 | +method. This returns a `project_id` that is required for many of the other API |
| 48 | +calls. Note that the `project_id` identifies a combination of `git_url` and |
| 49 | +`ref` - i.e. each combination of `git_url` and `ref` receives a different |
| 50 | +`project_id`. |
| 51 | +
|
| 52 | +## Higher-level interface |
| 53 | +
|
| 54 | +Some API methods allow the client to defer repository management to the service. |
| 55 | +In these cases, the API documentation will include `project_id` _and_ |
| 56 | +`git_url`+`ref` in the spec. Note that for such methods, _either_ `project_id` |
| 57 | +_or_ `git_url` (and optionally `ref`) should be passed in the request body. |
| 58 | +
|
| 59 | +## Responses |
| 60 | +
|
| 61 | +Following the JSON-RPC 2.0 Specification, the methods all return with HTTP code |
| 62 | +200 and include a [response |
| 63 | +object](https://www.jsonrpc.org/specification#response_object) may contain |
| 64 | +either a `result` or an `error` object. If the call succeeds, the returned |
| 65 | +`result` follows the schema documented in the individual methods. In the case of |
| 66 | +an error, the [`error` |
| 67 | +object](https://www.jsonrpc.org/specification#error_object), contains a code and |
| 68 | +a message describing the nature of the error. In addition to the [standard JSON-RPC |
| 69 | +response codes](https://www.jsonrpc.org/specification#error_object), we define application-specific |
| 70 | +codes: |
| 71 | +
|
| 72 | +``` |
| 73 | +GIT_ACCESS_DENIED_ERROR_CODE = -32000 |
| 74 | +GIT_UNKNOWN_ERROR_CODE = -32001 |
| 75 | +
|
| 76 | +RENKU_EXCEPTION_ERROR_CODE = -32100 |
| 77 | +REDIS_EXCEPTION_ERROR_CODE = -32200 |
| 78 | +
|
| 79 | +INVALID_HEADERS_ERROR_CODE = -32601 |
| 80 | +INVALID_PARAMS_ERROR_CODE = -32602 |
| 81 | +INTERNAL_FAILURE_ERROR_CODE = -32603 |
| 82 | +
|
| 83 | +HTTP_SERVER_ERROR = -32000 |
| 84 | +``` |
| 85 | +
|
| 86 | +""" |
| 87 | + |
| 88 | +spec = APISpec( |
| 89 | + title=SERVICE_NAME, |
| 90 | + openapi_version=OPENAPI_VERSION, |
| 91 | + version=API_VERSION, |
| 92 | + plugins=[FlaskPlugin(), MarshmallowPlugin()], |
| 93 | + servers=[{"url": SERVICE_API_BASE_PATH}], |
| 94 | + security=[{"oidc": []}, {"JWT": [], "gitlab-token": []}], |
| 95 | + info={"description": TOP_LEVEL_DESCRIPTION}, |
| 96 | +) |
| 97 | + |
| 98 | +spec.components.security_scheme("oidc", oidc_scheme) |
| 99 | +spec.components.security_scheme("jwt", jwt_scheme) |
| 100 | +spec.components.security_scheme("gitlab-token", gitlab_token_scheme) |
| 101 | + |
| 102 | + |
| 103 | +@apispec_blueprint.route("/spec.json") |
| 104 | +def openapi(): |
| 105 | + """Return the OpenAPI spec for this service.""" |
| 106 | + return jsonify(get_apispec(current_app).to_dict()) |
| 107 | + |
| 108 | + |
| 109 | +def get_apispec(app): |
| 110 | + """Return the apispec.""" |
| 111 | + for rule in current_app.url_map.iter_rules(): |
| 112 | + spec.path(view=app.view_functions[rule.endpoint]) |
| 113 | + return spec |
0 commit comments