From 472e9f7605d9aef9275998a049bf8e70bdc9bb4a Mon Sep 17 00:00:00 2001 From: Eliot Berriot Date: Thu, 26 Apr 2018 14:26:01 +0200 Subject: [PATCH 1/5] Added q filter on artists --- api/funkwhale_api/music/filters.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/api/funkwhale_api/music/filters.py b/api/funkwhale_api/music/filters.py index 752422e75..6da9cca63 100644 --- a/api/funkwhale_api/music/filters.py +++ b/api/funkwhale_api/music/filters.py @@ -20,6 +20,9 @@ class ListenableMixin(filters.FilterSet): class ArtistFilter(ListenableMixin): + q = fields.SearchFilter(search_fields=[ + 'name', + ]) class Meta: model = models.Artist From 2477aa31f9a7a89313a5bf8b649d40b67240e21e Mon Sep 17 00:00:00 2001 From: Eliot Berriot Date: Thu, 26 Apr 2018 14:26:11 +0200 Subject: [PATCH 2/5] Initial swagger setup --- api/docs/swagger.yml | 825 +++++++++++++++++++++++++++++++++++++++++++ dev.yml | 9 + 2 files changed, 834 insertions(+) create mode 100644 api/docs/swagger.yml diff --git a/api/docs/swagger.yml b/api/docs/swagger.yml new file mode 100644 index 000000000..160ef56b8 --- /dev/null +++ b/api/docs/swagger.yml @@ -0,0 +1,825 @@ +swagger: "2.0" +info: + description: "Documentation for [Funkwhale](https://funkwhale.audio) API. The API is **not** stable yet." + version: "1.0.0" + title: "Funkwhale API" +host: "demo.funkwhale.audio" +basePath: "/api/v1" +tags: +- name: "artists" + description: "Artists data" +- name: "store" + description: "Access to Petstore orders" +- name: "user" + description: "Operations about user" + +schemes: +- "http" +paths: + /artists/: + get: + tags: + - "artists" + parameters: + - name: "q" + in: "query" + description: "Search query used to filter artists" + required: false + type: "string" + example: "carpenter" + - name: "listenable" + in: "query" + description: "Filter/exclude artists with listenable tracks" + required: false + type: "boolean" + responses: + 200: + schema: + type: "object" + properties: + count: + $ref: "#/properties/resultsCount" + results: + type: "array" + items: + $ref: "#/definitions/ArtistNested" + /pet: + post: + tags: + - "pet" + summary: "Add a new pet to the store" + description: "" + operationId: "addPet" + consumes: + - "application/json" + - "application/xml" + produces: + - "application/xml" + - "application/json" + parameters: + - in: "body" + name: "body" + description: "Pet object that needs to be added to the store" + required: true + schema: + $ref: "#/definitions/Pet" + responses: + 405: + description: "Invalid input" + security: + - petstore_auth: + - "write:pets" + - "read:pets" + put: + tags: + - "pet" + summary: "Update an existing pet" + description: "" + operationId: "updatePet" + consumes: + - "application/json" + - "application/xml" + produces: + - "application/xml" + - "application/json" + parameters: + - in: "body" + name: "body" + description: "Pet object that needs to be added to the store" + required: true + schema: + $ref: "#/definitions/Pet" + responses: + 400: + description: "Invalid ID supplied" + 404: + description: "Pet not found" + 405: + description: "Validation exception" + security: + - petstore_auth: + - "write:pets" + - "read:pets" + /pet/findByStatus: + get: + tags: + - "pet" + summary: "Finds Pets by status" + description: "Multiple status values can be provided with comma separated strings" + operationId: "findPetsByStatus" + produces: + - "application/xml" + - "application/json" + parameters: + - name: "status" + in: "query" + description: "Status values that need to be considered for filter" + required: true + type: "array" + items: + type: "string" + enum: + - "available" + - "pending" + - "sold" + default: "available" + collectionFormat: "multi" + responses: + 200: + description: "successful operation" + schema: + type: "array" + items: + $ref: "#/definitions/Pet" + 400: + description: "Invalid status value" + security: + - petstore_auth: + - "write:pets" + - "read:pets" + /pet/findByTags: + get: + tags: + - "pet" + summary: "Finds Pets by tags" + description: "Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing." + operationId: "findPetsByTags" + produces: + - "application/xml" + - "application/json" + parameters: + - name: "tags" + in: "query" + description: "Tags to filter by" + required: true + type: "array" + items: + type: "string" + collectionFormat: "multi" + responses: + 200: + description: "successful operation" + schema: + type: "array" + items: + $ref: "#/definitions/Pet" + 400: + description: "Invalid tag value" + security: + - petstore_auth: + - "write:pets" + - "read:pets" + deprecated: true + /pet/{petId}: + get: + tags: + - "pet" + summary: "Find pet by ID" + description: "Returns a single pet" + operationId: "getPetById" + produces: + - "application/xml" + - "application/json" + parameters: + - name: "petId" + in: "path" + description: "ID of pet to return" + required: true + type: "integer" + format: "int64" + responses: + 200: + description: "successful operation" + schema: + $ref: "#/definitions/Pet" + 400: + description: "Invalid ID supplied" + 404: + description: "Pet not found" + security: + - api_key: [] + post: + tags: + - "pet" + summary: "Updates a pet in the store with form data" + description: "" + operationId: "updatePetWithForm" + consumes: + - "application/x-www-form-urlencoded" + produces: + - "application/xml" + - "application/json" + parameters: + - name: "petId" + in: "path" + description: "ID of pet that needs to be updated" + required: true + type: "integer" + format: "int64" + - name: "name" + in: "formData" + description: "Updated name of the pet" + required: false + type: "string" + - name: "status" + in: "formData" + description: "Updated status of the pet" + required: false + type: "string" + responses: + 405: + description: "Invalid input" + security: + - petstore_auth: + - "write:pets" + - "read:pets" + delete: + tags: + - "pet" + summary: "Deletes a pet" + description: "" + operationId: "deletePet" + produces: + - "application/xml" + - "application/json" + parameters: + - name: "api_key" + in: "header" + required: false + type: "string" + - name: "petId" + in: "path" + description: "Pet id to delete" + required: true + type: "integer" + format: "int64" + responses: + 400: + description: "Invalid ID supplied" + 404: + description: "Pet not found" + security: + - petstore_auth: + - "write:pets" + - "read:pets" + /pet/{petId}/uploadImage: + post: + tags: + - "pet" + summary: "uploads an image" + description: "" + operationId: "uploadFile" + consumes: + - "multipart/form-data" + produces: + - "application/json" + parameters: + - name: "petId" + in: "path" + description: "ID of pet to update" + required: true + type: "integer" + format: "int64" + - name: "additionalMetadata" + in: "formData" + description: "Additional data to pass to server" + required: false + type: "string" + - name: "file" + in: "formData" + description: "file to upload" + required: false + type: "file" + responses: + 200: + description: "successful operation" + schema: + $ref: "#/definitions/ApiResponse" + security: + - petstore_auth: + - "write:pets" + - "read:pets" + /store/inventory: + get: + tags: + - "store" + summary: "Returns pet inventories by status" + description: "Returns a map of status codes to quantities" + operationId: "getInventory" + produces: + - "application/json" + parameters: [] + responses: + 200: + description: "successful operation" + schema: + type: "object" + additionalProperties: + type: "integer" + format: "int32" + security: + - api_key: [] + /store/order: + post: + tags: + - "store" + summary: "Place an order for a pet" + description: "" + operationId: "placeOrder" + produces: + - "application/xml" + - "application/json" + parameters: + - in: "body" + name: "body" + description: "order placed for purchasing the pet" + required: true + schema: + $ref: "#/definitions/Order" + responses: + 200: + description: "successful operation" + schema: + $ref: "#/definitions/Order" + 400: + description: "Invalid Order" + /store/order/{orderId}: + get: + tags: + - "store" + summary: "Find purchase order by ID" + description: "For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions" + operationId: "getOrderById" + produces: + - "application/xml" + - "application/json" + parameters: + - name: "orderId" + in: "path" + description: "ID of pet that needs to be fetched" + required: true + type: "integer" + maximum: 10.0 + minimum: 1.0 + format: "int64" + responses: + 200: + description: "successful operation" + schema: + $ref: "#/definitions/Order" + 400: + description: "Invalid ID supplied" + 404: + description: "Order not found" + delete: + tags: + - "store" + summary: "Delete purchase order by ID" + description: "For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors" + operationId: "deleteOrder" + produces: + - "application/xml" + - "application/json" + parameters: + - name: "orderId" + in: "path" + description: "ID of the order that needs to be deleted" + required: true + type: "integer" + minimum: 1.0 + format: "int64" + responses: + 400: + description: "Invalid ID supplied" + 404: + description: "Order not found" + /user: + post: + tags: + - "user" + summary: "Create user" + description: "This can only be done by the logged in user." + operationId: "createUser" + produces: + - "application/xml" + - "application/json" + parameters: + - in: "body" + name: "body" + description: "Created user object" + required: true + schema: + $ref: "#/definitions/User" + responses: + default: + description: "successful operation" + /user/createWithArray: + post: + tags: + - "user" + summary: "Creates list of users with given input array" + description: "" + operationId: "createUsersWithArrayInput" + produces: + - "application/xml" + - "application/json" + parameters: + - in: "body" + name: "body" + description: "List of user object" + required: true + schema: + type: "array" + items: + $ref: "#/definitions/User" + responses: + default: + description: "successful operation" + /user/createWithList: + post: + tags: + - "user" + summary: "Creates list of users with given input array" + description: "" + operationId: "createUsersWithListInput" + produces: + - "application/xml" + - "application/json" + parameters: + - in: "body" + name: "body" + description: "List of user object" + required: true + schema: + type: "array" + items: + $ref: "#/definitions/User" + responses: + default: + description: "successful operation" + /user/login: + get: + tags: + - "user" + summary: "Logs user into the system" + description: "" + operationId: "loginUser" + produces: + - "application/xml" + - "application/json" + parameters: + - name: "username" + in: "query" + description: "The user name for login" + required: true + type: "string" + - name: "password" + in: "query" + description: "The password for login in clear text" + required: true + type: "string" + responses: + 200: + description: "successful operation" + schema: + type: "string" + headers: + X-Rate-Limit: + type: "integer" + format: "int32" + description: "calls per hour allowed by the user" + X-Expires-After: + type: "string" + format: "date-time" + description: "date in UTC when token expires" + 400: + description: "Invalid username/password supplied" + /user/logout: + get: + tags: + - "user" + summary: "Logs out current logged in user session" + description: "" + operationId: "logoutUser" + produces: + - "application/xml" + - "application/json" + parameters: [] + responses: + default: + description: "successful operation" + /user/{username}: + get: + tags: + - "user" + summary: "Get user by user name" + description: "" + operationId: "getUserByName" + produces: + - "application/xml" + - "application/json" + parameters: + - name: "username" + in: "path" + description: "The name that needs to be fetched. Use user1 for testing. " + required: true + type: "string" + responses: + 200: + description: "successful operation" + schema: + $ref: "#/definitions/User" + 400: + description: "Invalid username supplied" + 404: + description: "User not found" + put: + tags: + - "user" + summary: "Updated user" + description: "This can only be done by the logged in user." + operationId: "updateUser" + produces: + - "application/xml" + - "application/json" + parameters: + - name: "username" + in: "path" + description: "name that need to be updated" + required: true + type: "string" + - in: "body" + name: "body" + description: "Updated user object" + required: true + schema: + $ref: "#/definitions/User" + responses: + 400: + description: "Invalid user supplied" + 404: + description: "User not found" + delete: + tags: + - "user" + summary: "Delete user" + description: "This can only be done by the logged in user." + operationId: "deleteUser" + produces: + - "application/xml" + - "application/json" + parameters: + - name: "username" + in: "path" + description: "The name that needs to be deleted" + required: true + type: "string" + responses: + 400: + description: "Invalid username supplied" + 404: + description: "User not found" +securityDefinitions: + components: + securitySchemes: + bearerAuth: # arbitrary name for the security scheme + type: http + scheme: bearer + bearerFormat: JWT # optional, arbitrary value for documentation purposes + petstore_auth: + type: "oauth2" + authorizationUrl: "http://petstore.swagger.io/oauth/dialog" + flow: "implicit" + scopes: + write:pets: "modify pets in your account" + read:pets: "read your pets" + api_key: + type: "apiKey" + name: "api_key" + in: "header" +properties: + resultsCount: + type: "integer" + format: "int64" + description: "The total number of resources matching the request" + mbid: + type: "string" + formats: "uuid" + description: "A musicbrainz ID" +definitions: + Artist: + type: "object" + properties: + mbid: + required: false + $ref: "#/properties/mbid" + id: + type: "integer" + format: "int64" + example: 42 + name: + type: "string" + example: "System of a Down" + creation_date: + type: "string" + format: "date-time" + ArtistNested: + type: "object" + allOf: + - $ref: "#/definitions/Artist" + - type: "object" + properties: + albums: + type: "array" + items: + $ref: "#/definitions/AlbumNested" + + Album: + type: "object" + properties: + mbid: + required: false + $ref: "#/properties/mbid" + id: + type: "integer" + format: "int64" + example: 16 + artist: + type: "integer" + format: "int64" + example: 42 + title: + type: "string" + example: "Toxicity" + creation_date: + type: "string" + format: "date-time" + release_date: + type: "string" + required: false + format: "date" + example: "2001-01-01" + + AlbumNested: + type: "object" + allOf: + - $ref: "#/definitions/Album" + - type: "object" + properties: + tracks: + type: "array" + items: + $ref: "#/definitions/Track" + + Track: + type: "object" + properties: + mbid: + required: false + $ref: "#/properties/mbid" + id: + type: "integer" + format: "int64" + example: 66 + artist: + type: "integer" + format: "int64" + example: 42 + album: + type: "integer" + format: "int64" + example: 16 + title: + type: "string" + example: "Chop Suey!" + position: + required: false + description: "Position of the track in the album" + type: "number" + minimum: 1 + example: 1 + creation_date: + type: "string" + format: "date-time" + + Order: + type: "object" + properties: + id: + type: "integer" + format: "int64" + petId: + type: "integer" + format: "int64" + quantity: + type: "integer" + format: "int32" + shipDate: + type: "string" + format: "date-time" + status: + type: "string" + description: "Order Status" + enum: + - "placed" + - "approved" + - "delivered" + complete: + type: "boolean" + default: false + xml: + name: "Order" + Category: + type: "object" + properties: + id: + type: "integer" + format: "int64" + name: + type: "string" + xml: + name: "Category" + User: + type: "object" + properties: + id: + type: "integer" + format: "int64" + username: + type: "string" + firstName: + type: "string" + lastName: + type: "string" + email: + type: "string" + password: + type: "string" + phone: + type: "string" + userStatus: + type: "integer" + format: "int32" + description: "User Status" + xml: + name: "User" + Tag: + type: "object" + properties: + id: + type: "integer" + format: "int64" + name: + type: "string" + xml: + name: "Tag" + Pet: + type: "object" + required: + - "name" + - "photoUrls" + properties: + id: + type: "integer" + format: "int64" + category: + $ref: "#/definitions/Category" + name: + type: "string" + example: "doggie" + photoUrls: + type: "array" + xml: + name: "photoUrl" + wrapped: true + items: + type: "string" + tags: + type: "array" + xml: + name: "tag" + wrapped: true + items: + $ref: "#/definitions/Tag" + status: + type: "string" + description: "pet status in the store" + enum: + - "available" + - "pending" + - "sold" + xml: + name: "Pet" + ApiResponse: + type: "object" + properties: + code: + type: "integer" + format: "int32" + type: + type: "string" + message: + type: "string" +externalDocs: + description: "Find out more about Funkwhale" + url: "https://docs.funkwhale.audio" diff --git a/dev.yml b/dev.yml index 264fc9534..534d8f5b5 100644 --- a/dev.yml +++ b/dev.yml @@ -123,6 +123,15 @@ services: - '35730:35730' - '8001:8001' + api-docs: + image: swaggerapi/swagger-ui + environment: + - "API_URL=/swagger.yml" + ports: + - '8002:8080' + volumes: + - "./api/docs/swagger.yml:/usr/share/nginx/html/swagger.yml" + networks: internal: federation: From d2c2fb837e9d269305b7a8f064c7180c9ded3ff3 Mon Sep 17 00:00:00 2001 From: Eliot Berriot Date: Thu, 26 Apr 2018 15:17:51 +0200 Subject: [PATCH 3/5] Now support Bearer auth in complement of JWT --- api/config/settings/common.py | 1 + api/funkwhale_api/common/auth.py | 3 -- api/funkwhale_api/common/authentication.py | 37 ++++++++++++++++++++++ 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/api/config/settings/common.py b/api/config/settings/common.py index de1d653cb..f1a383c58 100644 --- a/api/config/settings/common.py +++ b/api/config/settings/common.py @@ -377,6 +377,7 @@ REST_FRAMEWORK = { ), 'DEFAULT_AUTHENTICATION_CLASSES': ( 'funkwhale_api.common.authentication.JSONWebTokenAuthenticationQS', + 'funkwhale_api.common.authentication.BearerTokenHeaderAuth', 'rest_framework_jwt.authentication.JSONWebTokenAuthentication', 'rest_framework.authentication.SessionAuthentication', 'rest_framework.authentication.BasicAuthentication', diff --git a/api/funkwhale_api/common/auth.py b/api/funkwhale_api/common/auth.py index 75839b936..faf13571d 100644 --- a/api/funkwhale_api/common/auth.py +++ b/api/funkwhale_api/common/auth.py @@ -29,9 +29,6 @@ class TokenHeaderAuth(BaseJSONWebTokenAuthentication): class TokenAuthMiddleware: - """ - Custom middleware (insecure) that takes user IDs from the query string. - """ def __init__(self, inner): # Store the ASGI application we were passed diff --git a/api/funkwhale_api/common/authentication.py b/api/funkwhale_api/common/authentication.py index b75f3b516..c7566eac8 100644 --- a/api/funkwhale_api/common/authentication.py +++ b/api/funkwhale_api/common/authentication.py @@ -1,3 +1,6 @@ +from django.utils.encoding import smart_text +from django.utils.translation import ugettext as _ + from rest_framework import exceptions from rest_framework_jwt import authentication from rest_framework_jwt.settings import api_settings @@ -18,3 +21,37 @@ class JSONWebTokenAuthenticationQS( def authenticate_header(self, request): return '{0} realm="{1}"'.format( api_settings.JWT_AUTH_HEADER_PREFIX, self.www_authenticate_realm) + + +class BearerTokenHeaderAuth( + authentication.BaseJSONWebTokenAuthentication): + """ + For backward compatibility purpose, we used Authorization: JWT + but Authorization: Bearer is probably better. + """ + www_authenticate_realm = 'api' + + def get_jwt_value(self, request): + auth = authentication.get_authorization_header(request).split() + auth_header_prefix = 'bearer' + + if not auth: + if api_settings.JWT_AUTH_COOKIE: + return request.COOKIES.get(api_settings.JWT_AUTH_COOKIE) + return None + + if smart_text(auth[0].lower()) != auth_header_prefix: + return None + + if len(auth) == 1: + msg = _('Invalid Authorization header. No credentials provided.') + raise exceptions.AuthenticationFailed(msg) + elif len(auth) > 2: + msg = _('Invalid Authorization header. Credentials string ' + 'should not contain spaces.') + raise exceptions.AuthenticationFailed(msg) + + return auth[1] + + def authenticate_header(self, request): + return '{0} realm="{1}"'.format('Bearer', self.www_authenticate_realm) From c4777532ebe88bf393578f2d5aeb246afced6ef9 Mon Sep 17 00:00:00 2001 From: Eliot Berriot Date: Thu, 26 Apr 2018 18:12:08 +0200 Subject: [PATCH 4/5] Bundle swagger docs with sphinx docs --- .gitignore | 1 + .gitlab-ci.yml | 6 +- api/docs/swagger.yml | 825 ------------------------------------------ docs/build_docs.sh | 5 + docs/build_swagger.sh | 9 + docs/swagger.yml | 186 ++++++++++ 6 files changed, 205 insertions(+), 827 deletions(-) delete mode 100644 api/docs/swagger.yml create mode 100755 docs/build_docs.sh create mode 100755 docs/build_swagger.sh create mode 100644 docs/swagger.yml diff --git a/.gitignore b/.gitignore index 548cfd7b3..25b088739 100644 --- a/.gitignore +++ b/.gitignore @@ -89,3 +89,4 @@ data/ .env po/*.po +docs/swagger diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 6a0f4b9d8..5f65e60da 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -92,12 +92,14 @@ build_front: pages: stage: test - image: python:3.6-alpine + image: python:3.6 + variables: + BUILD_PATH: "../public" before_script: - cd docs script: - pip install sphinx - - python -m sphinx . ../public + - ./build_docs.sh artifacts: paths: - public diff --git a/api/docs/swagger.yml b/api/docs/swagger.yml deleted file mode 100644 index 160ef56b8..000000000 --- a/api/docs/swagger.yml +++ /dev/null @@ -1,825 +0,0 @@ -swagger: "2.0" -info: - description: "Documentation for [Funkwhale](https://funkwhale.audio) API. The API is **not** stable yet." - version: "1.0.0" - title: "Funkwhale API" -host: "demo.funkwhale.audio" -basePath: "/api/v1" -tags: -- name: "artists" - description: "Artists data" -- name: "store" - description: "Access to Petstore orders" -- name: "user" - description: "Operations about user" - -schemes: -- "http" -paths: - /artists/: - get: - tags: - - "artists" - parameters: - - name: "q" - in: "query" - description: "Search query used to filter artists" - required: false - type: "string" - example: "carpenter" - - name: "listenable" - in: "query" - description: "Filter/exclude artists with listenable tracks" - required: false - type: "boolean" - responses: - 200: - schema: - type: "object" - properties: - count: - $ref: "#/properties/resultsCount" - results: - type: "array" - items: - $ref: "#/definitions/ArtistNested" - /pet: - post: - tags: - - "pet" - summary: "Add a new pet to the store" - description: "" - operationId: "addPet" - consumes: - - "application/json" - - "application/xml" - produces: - - "application/xml" - - "application/json" - parameters: - - in: "body" - name: "body" - description: "Pet object that needs to be added to the store" - required: true - schema: - $ref: "#/definitions/Pet" - responses: - 405: - description: "Invalid input" - security: - - petstore_auth: - - "write:pets" - - "read:pets" - put: - tags: - - "pet" - summary: "Update an existing pet" - description: "" - operationId: "updatePet" - consumes: - - "application/json" - - "application/xml" - produces: - - "application/xml" - - "application/json" - parameters: - - in: "body" - name: "body" - description: "Pet object that needs to be added to the store" - required: true - schema: - $ref: "#/definitions/Pet" - responses: - 400: - description: "Invalid ID supplied" - 404: - description: "Pet not found" - 405: - description: "Validation exception" - security: - - petstore_auth: - - "write:pets" - - "read:pets" - /pet/findByStatus: - get: - tags: - - "pet" - summary: "Finds Pets by status" - description: "Multiple status values can be provided with comma separated strings" - operationId: "findPetsByStatus" - produces: - - "application/xml" - - "application/json" - parameters: - - name: "status" - in: "query" - description: "Status values that need to be considered for filter" - required: true - type: "array" - items: - type: "string" - enum: - - "available" - - "pending" - - "sold" - default: "available" - collectionFormat: "multi" - responses: - 200: - description: "successful operation" - schema: - type: "array" - items: - $ref: "#/definitions/Pet" - 400: - description: "Invalid status value" - security: - - petstore_auth: - - "write:pets" - - "read:pets" - /pet/findByTags: - get: - tags: - - "pet" - summary: "Finds Pets by tags" - description: "Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing." - operationId: "findPetsByTags" - produces: - - "application/xml" - - "application/json" - parameters: - - name: "tags" - in: "query" - description: "Tags to filter by" - required: true - type: "array" - items: - type: "string" - collectionFormat: "multi" - responses: - 200: - description: "successful operation" - schema: - type: "array" - items: - $ref: "#/definitions/Pet" - 400: - description: "Invalid tag value" - security: - - petstore_auth: - - "write:pets" - - "read:pets" - deprecated: true - /pet/{petId}: - get: - tags: - - "pet" - summary: "Find pet by ID" - description: "Returns a single pet" - operationId: "getPetById" - produces: - - "application/xml" - - "application/json" - parameters: - - name: "petId" - in: "path" - description: "ID of pet to return" - required: true - type: "integer" - format: "int64" - responses: - 200: - description: "successful operation" - schema: - $ref: "#/definitions/Pet" - 400: - description: "Invalid ID supplied" - 404: - description: "Pet not found" - security: - - api_key: [] - post: - tags: - - "pet" - summary: "Updates a pet in the store with form data" - description: "" - operationId: "updatePetWithForm" - consumes: - - "application/x-www-form-urlencoded" - produces: - - "application/xml" - - "application/json" - parameters: - - name: "petId" - in: "path" - description: "ID of pet that needs to be updated" - required: true - type: "integer" - format: "int64" - - name: "name" - in: "formData" - description: "Updated name of the pet" - required: false - type: "string" - - name: "status" - in: "formData" - description: "Updated status of the pet" - required: false - type: "string" - responses: - 405: - description: "Invalid input" - security: - - petstore_auth: - - "write:pets" - - "read:pets" - delete: - tags: - - "pet" - summary: "Deletes a pet" - description: "" - operationId: "deletePet" - produces: - - "application/xml" - - "application/json" - parameters: - - name: "api_key" - in: "header" - required: false - type: "string" - - name: "petId" - in: "path" - description: "Pet id to delete" - required: true - type: "integer" - format: "int64" - responses: - 400: - description: "Invalid ID supplied" - 404: - description: "Pet not found" - security: - - petstore_auth: - - "write:pets" - - "read:pets" - /pet/{petId}/uploadImage: - post: - tags: - - "pet" - summary: "uploads an image" - description: "" - operationId: "uploadFile" - consumes: - - "multipart/form-data" - produces: - - "application/json" - parameters: - - name: "petId" - in: "path" - description: "ID of pet to update" - required: true - type: "integer" - format: "int64" - - name: "additionalMetadata" - in: "formData" - description: "Additional data to pass to server" - required: false - type: "string" - - name: "file" - in: "formData" - description: "file to upload" - required: false - type: "file" - responses: - 200: - description: "successful operation" - schema: - $ref: "#/definitions/ApiResponse" - security: - - petstore_auth: - - "write:pets" - - "read:pets" - /store/inventory: - get: - tags: - - "store" - summary: "Returns pet inventories by status" - description: "Returns a map of status codes to quantities" - operationId: "getInventory" - produces: - - "application/json" - parameters: [] - responses: - 200: - description: "successful operation" - schema: - type: "object" - additionalProperties: - type: "integer" - format: "int32" - security: - - api_key: [] - /store/order: - post: - tags: - - "store" - summary: "Place an order for a pet" - description: "" - operationId: "placeOrder" - produces: - - "application/xml" - - "application/json" - parameters: - - in: "body" - name: "body" - description: "order placed for purchasing the pet" - required: true - schema: - $ref: "#/definitions/Order" - responses: - 200: - description: "successful operation" - schema: - $ref: "#/definitions/Order" - 400: - description: "Invalid Order" - /store/order/{orderId}: - get: - tags: - - "store" - summary: "Find purchase order by ID" - description: "For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions" - operationId: "getOrderById" - produces: - - "application/xml" - - "application/json" - parameters: - - name: "orderId" - in: "path" - description: "ID of pet that needs to be fetched" - required: true - type: "integer" - maximum: 10.0 - minimum: 1.0 - format: "int64" - responses: - 200: - description: "successful operation" - schema: - $ref: "#/definitions/Order" - 400: - description: "Invalid ID supplied" - 404: - description: "Order not found" - delete: - tags: - - "store" - summary: "Delete purchase order by ID" - description: "For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors" - operationId: "deleteOrder" - produces: - - "application/xml" - - "application/json" - parameters: - - name: "orderId" - in: "path" - description: "ID of the order that needs to be deleted" - required: true - type: "integer" - minimum: 1.0 - format: "int64" - responses: - 400: - description: "Invalid ID supplied" - 404: - description: "Order not found" - /user: - post: - tags: - - "user" - summary: "Create user" - description: "This can only be done by the logged in user." - operationId: "createUser" - produces: - - "application/xml" - - "application/json" - parameters: - - in: "body" - name: "body" - description: "Created user object" - required: true - schema: - $ref: "#/definitions/User" - responses: - default: - description: "successful operation" - /user/createWithArray: - post: - tags: - - "user" - summary: "Creates list of users with given input array" - description: "" - operationId: "createUsersWithArrayInput" - produces: - - "application/xml" - - "application/json" - parameters: - - in: "body" - name: "body" - description: "List of user object" - required: true - schema: - type: "array" - items: - $ref: "#/definitions/User" - responses: - default: - description: "successful operation" - /user/createWithList: - post: - tags: - - "user" - summary: "Creates list of users with given input array" - description: "" - operationId: "createUsersWithListInput" - produces: - - "application/xml" - - "application/json" - parameters: - - in: "body" - name: "body" - description: "List of user object" - required: true - schema: - type: "array" - items: - $ref: "#/definitions/User" - responses: - default: - description: "successful operation" - /user/login: - get: - tags: - - "user" - summary: "Logs user into the system" - description: "" - operationId: "loginUser" - produces: - - "application/xml" - - "application/json" - parameters: - - name: "username" - in: "query" - description: "The user name for login" - required: true - type: "string" - - name: "password" - in: "query" - description: "The password for login in clear text" - required: true - type: "string" - responses: - 200: - description: "successful operation" - schema: - type: "string" - headers: - X-Rate-Limit: - type: "integer" - format: "int32" - description: "calls per hour allowed by the user" - X-Expires-After: - type: "string" - format: "date-time" - description: "date in UTC when token expires" - 400: - description: "Invalid username/password supplied" - /user/logout: - get: - tags: - - "user" - summary: "Logs out current logged in user session" - description: "" - operationId: "logoutUser" - produces: - - "application/xml" - - "application/json" - parameters: [] - responses: - default: - description: "successful operation" - /user/{username}: - get: - tags: - - "user" - summary: "Get user by user name" - description: "" - operationId: "getUserByName" - produces: - - "application/xml" - - "application/json" - parameters: - - name: "username" - in: "path" - description: "The name that needs to be fetched. Use user1 for testing. " - required: true - type: "string" - responses: - 200: - description: "successful operation" - schema: - $ref: "#/definitions/User" - 400: - description: "Invalid username supplied" - 404: - description: "User not found" - put: - tags: - - "user" - summary: "Updated user" - description: "This can only be done by the logged in user." - operationId: "updateUser" - produces: - - "application/xml" - - "application/json" - parameters: - - name: "username" - in: "path" - description: "name that need to be updated" - required: true - type: "string" - - in: "body" - name: "body" - description: "Updated user object" - required: true - schema: - $ref: "#/definitions/User" - responses: - 400: - description: "Invalid user supplied" - 404: - description: "User not found" - delete: - tags: - - "user" - summary: "Delete user" - description: "This can only be done by the logged in user." - operationId: "deleteUser" - produces: - - "application/xml" - - "application/json" - parameters: - - name: "username" - in: "path" - description: "The name that needs to be deleted" - required: true - type: "string" - responses: - 400: - description: "Invalid username supplied" - 404: - description: "User not found" -securityDefinitions: - components: - securitySchemes: - bearerAuth: # arbitrary name for the security scheme - type: http - scheme: bearer - bearerFormat: JWT # optional, arbitrary value for documentation purposes - petstore_auth: - type: "oauth2" - authorizationUrl: "http://petstore.swagger.io/oauth/dialog" - flow: "implicit" - scopes: - write:pets: "modify pets in your account" - read:pets: "read your pets" - api_key: - type: "apiKey" - name: "api_key" - in: "header" -properties: - resultsCount: - type: "integer" - format: "int64" - description: "The total number of resources matching the request" - mbid: - type: "string" - formats: "uuid" - description: "A musicbrainz ID" -definitions: - Artist: - type: "object" - properties: - mbid: - required: false - $ref: "#/properties/mbid" - id: - type: "integer" - format: "int64" - example: 42 - name: - type: "string" - example: "System of a Down" - creation_date: - type: "string" - format: "date-time" - ArtistNested: - type: "object" - allOf: - - $ref: "#/definitions/Artist" - - type: "object" - properties: - albums: - type: "array" - items: - $ref: "#/definitions/AlbumNested" - - Album: - type: "object" - properties: - mbid: - required: false - $ref: "#/properties/mbid" - id: - type: "integer" - format: "int64" - example: 16 - artist: - type: "integer" - format: "int64" - example: 42 - title: - type: "string" - example: "Toxicity" - creation_date: - type: "string" - format: "date-time" - release_date: - type: "string" - required: false - format: "date" - example: "2001-01-01" - - AlbumNested: - type: "object" - allOf: - - $ref: "#/definitions/Album" - - type: "object" - properties: - tracks: - type: "array" - items: - $ref: "#/definitions/Track" - - Track: - type: "object" - properties: - mbid: - required: false - $ref: "#/properties/mbid" - id: - type: "integer" - format: "int64" - example: 66 - artist: - type: "integer" - format: "int64" - example: 42 - album: - type: "integer" - format: "int64" - example: 16 - title: - type: "string" - example: "Chop Suey!" - position: - required: false - description: "Position of the track in the album" - type: "number" - minimum: 1 - example: 1 - creation_date: - type: "string" - format: "date-time" - - Order: - type: "object" - properties: - id: - type: "integer" - format: "int64" - petId: - type: "integer" - format: "int64" - quantity: - type: "integer" - format: "int32" - shipDate: - type: "string" - format: "date-time" - status: - type: "string" - description: "Order Status" - enum: - - "placed" - - "approved" - - "delivered" - complete: - type: "boolean" - default: false - xml: - name: "Order" - Category: - type: "object" - properties: - id: - type: "integer" - format: "int64" - name: - type: "string" - xml: - name: "Category" - User: - type: "object" - properties: - id: - type: "integer" - format: "int64" - username: - type: "string" - firstName: - type: "string" - lastName: - type: "string" - email: - type: "string" - password: - type: "string" - phone: - type: "string" - userStatus: - type: "integer" - format: "int32" - description: "User Status" - xml: - name: "User" - Tag: - type: "object" - properties: - id: - type: "integer" - format: "int64" - name: - type: "string" - xml: - name: "Tag" - Pet: - type: "object" - required: - - "name" - - "photoUrls" - properties: - id: - type: "integer" - format: "int64" - category: - $ref: "#/definitions/Category" - name: - type: "string" - example: "doggie" - photoUrls: - type: "array" - xml: - name: "photoUrl" - wrapped: true - items: - type: "string" - tags: - type: "array" - xml: - name: "tag" - wrapped: true - items: - $ref: "#/definitions/Tag" - status: - type: "string" - description: "pet status in the store" - enum: - - "available" - - "pending" - - "sold" - xml: - name: "Pet" - ApiResponse: - type: "object" - properties: - code: - type: "integer" - format: "int32" - type: - type: "string" - message: - type: "string" -externalDocs: - description: "Find out more about Funkwhale" - url: "https://docs.funkwhale.audio" diff --git a/docs/build_docs.sh b/docs/build_docs.sh new file mode 100755 index 000000000..fbf2036af --- /dev/null +++ b/docs/build_docs.sh @@ -0,0 +1,5 @@ +#!/bin/bash -eux +# Building sphinx and swagger docs + +python -m sphinx . $BUILD_PATH +TARGET_PATH="$BUILD_PATH/swagger" ./build_swagger.sh diff --git a/docs/build_swagger.sh b/docs/build_swagger.sh new file mode 100755 index 000000000..13ae21b06 --- /dev/null +++ b/docs/build_swagger.sh @@ -0,0 +1,9 @@ +#!/bin/bash -eux + +SWAGGER_VERSION="3.13.6" +TARGET_PATH=${TARGET_PATH-"swagger"} +rm -rf $TARGET_PATH /tmp/swagger-ui +git clone --branch="v$SWAGGER_VERSION" --depth=1 "https://github.com/swagger-api/swagger-ui.git" /tmp/swagger-ui +mv /tmp/swagger-ui/dist $TARGET_PATH +cp swagger.yml $TARGET_PATH +sed -i "s,http://petstore.swagger.io/v2/swagger.json,swagger.yml,g" $TARGET_PATH/index.html diff --git a/docs/swagger.yml b/docs/swagger.yml new file mode 100644 index 000000000..7735a8f20 --- /dev/null +++ b/docs/swagger.yml @@ -0,0 +1,186 @@ +openapi: "3.0" +info: + description: "Documentation for [Funkwhale](https://funkwhale.audio) API. The API is **not** stable yet." + version: "1.0.0" + title: "Funkwhale API" + +servers: + - url: https://demo.funkwhale.audio/api/v1 + description: Demo server + - url: https://node1.funkwhale.test/api/v1 + description: Node 1 (local) + +components: + securitySchemes: + jwt: + type: http + scheme: bearer + bearerFormat: JWT + description: "You can get a token by using the /token endpoint" + +security: + - jwt: [] + +paths: + /token/: + post: + tags: + - "auth" + description: + Obtain a JWT token you can use for authenticating your next requests. + security: [] + responses: + '200': + description: Successfull auth + '400': + description: Invalid credentials + requestBody: + required: true + content: + application/json: + schema: + type: "object" + properties: + username: + type: "string" + example: "demo" + password: + type: "string" + example: "demo" + + /artists/: + get: + tags: + - "artists" + parameters: + - name: "q" + in: "query" + description: "Search query used to filter artists" + schema: + required: false + type: "string" + example: "carpenter" + - name: "listenable" + in: "query" + description: "Filter/exclude artists with listenable tracks" + schema: + required: false + type: "boolean" + responses: + 200: + content: + application/json: + schema: + type: "object" + properties: + count: + $ref: "#/properties/resultsCount" + results: + type: "array" + items: + $ref: "#/definitions/ArtistNested" + +properties: + resultsCount: + type: "integer" + format: "int64" + description: "The total number of resources matching the request" + mbid: + type: "string" + formats: "uuid" + description: "A musicbrainz ID" +definitions: + Artist: + type: "object" + properties: + mbid: + required: false + $ref: "#/properties/mbid" + id: + type: "integer" + format: "int64" + example: 42 + name: + type: "string" + example: "System of a Down" + creation_date: + type: "string" + format: "date-time" + ArtistNested: + type: "object" + allOf: + - $ref: "#/definitions/Artist" + - type: "object" + properties: + albums: + type: "array" + items: + $ref: "#/definitions/AlbumNested" + + Album: + type: "object" + properties: + mbid: + required: false + $ref: "#/properties/mbid" + id: + type: "integer" + format: "int64" + example: 16 + artist: + type: "integer" + format: "int64" + example: 42 + title: + type: "string" + example: "Toxicity" + creation_date: + type: "string" + format: "date-time" + release_date: + type: "string" + required: false + format: "date" + example: "2001-01-01" + + AlbumNested: + type: "object" + allOf: + - $ref: "#/definitions/Album" + - type: "object" + properties: + tracks: + type: "array" + items: + $ref: "#/definitions/Track" + + Track: + type: "object" + properties: + mbid: + required: false + $ref: "#/properties/mbid" + id: + type: "integer" + format: "int64" + example: 66 + artist: + type: "integer" + format: "int64" + example: 42 + album: + type: "integer" + format: "int64" + example: 16 + title: + type: "string" + example: "Chop Suey!" + position: + required: false + description: "Position of the track in the album" + type: "number" + minimum: 1 + example: 1 + creation_date: + type: "string" + format: "date-time" From 56d9c5873c04b29b612a482e273fab637b2c42b0 Mon Sep 17 00:00:00 2001 From: Eliot Berriot Date: Thu, 26 Apr 2018 18:23:50 +0200 Subject: [PATCH 5/5] Fix #178: Foundations for API documentation with Swagger --- changes/changelog.d/178.doc | 1 + docs/api.rst | 6 ++++++ docs/index.rst | 1 + 3 files changed, 8 insertions(+) create mode 100644 changes/changelog.d/178.doc create mode 100644 docs/api.rst diff --git a/changes/changelog.d/178.doc b/changes/changelog.d/178.doc new file mode 100644 index 000000000..419e6984b --- /dev/null +++ b/changes/changelog.d/178.doc @@ -0,0 +1 @@ +Foundations for API documentation with Swagger (#178) diff --git a/docs/api.rst b/docs/api.rst new file mode 100644 index 000000000..650b3885e --- /dev/null +++ b/docs/api.rst @@ -0,0 +1,6 @@ +Funkwhale API +============= + +Funkwhale API is still a work in progress and should not be considered as +stable. We offer an `interactive documentation using swagger `_ +were you can browse available endpoints and try the API. diff --git a/docs/index.rst b/docs/index.rst index 82dcf8c88..cea50ea22 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -16,6 +16,7 @@ Funkwhale is a self-hosted, modern free and open-source music server, heavily in configuration importing-music federation + api upgrading changelog