From 7389bda4f005070506121a0640f1d49c8f701216 Mon Sep 17 00:00:00 2001 From: Erwan Rouchet <rouchet@teklia.com> Date: Fri, 5 Apr 2019 08:55:28 +0000 Subject: [PATCH] OpenAPI client --- .gitignore | 2 + .gitlab-ci.yml | 18 + Makefile | 8 +- arkindex/dataimport/api.py | 10 +- arkindex/dataimport/serializers.py | 14 + arkindex/dataimport/tests/test_repos.py | 51 ++- arkindex/documents/serializers/ml.py | 9 +- arkindex/users/api.py | 3 +- arkindex/users/serializers.py | 9 + openapi/Dockerfile | 9 + openapi/patch.py | 132 +++++++ openapi/patch.yml | 439 ++++++++++++++++++++++++ openapi/requirements.txt | 3 + openapi/run.sh | 3 + requirements.txt | 2 +- 15 files changed, 694 insertions(+), 18 deletions(-) create mode 100644 openapi/Dockerfile create mode 100755 openapi/patch.py create mode 100644 openapi/patch.yml create mode 100644 openapi/requirements.txt create mode 100755 openapi/run.sh diff --git a/.gitignore b/.gitignore index 4caa39dc02..3338e08b48 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,5 @@ arkindex/iiif-users/ .coverage htmlcov ponos +openapi/*.yml +!openapi/patch.yml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e4efdda93a..c0b978413b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,6 +1,7 @@ image: registry.gitlab.com/arkindex/backend:base-0.9.3 stages: - test + - build cache: paths: @@ -41,3 +42,20 @@ backend-lint: script: - flake8 + +backend-openapi: + stage: build + image: registry.gitlab.com/arkindex/backend/openapi:latest + + before_script: [] + script: + - mkdir -p output + - arkindex/manage.py generateschema > output/original.yml + - openapi/patch.py openapi/patch.yml output/original.yml > output/schema.yml + + variables: + PONOS_DATA_DIR: /tmp + + artifacts: + paths: + - output/ diff --git a/Makefile b/Makefile index 9b14aff8b6..96d9bec742 100644 --- a/Makefile +++ b/Makefile @@ -8,6 +8,7 @@ VERSION=$(shell git rev-parse --short HEAD) TAG_APP=arkindex-app TAG_BASE=arkindex-base TAG_SHELL=arkindex-shell +TAG_OPENAPI=arkindex-openapi .PHONY: build base all: clean build @@ -29,11 +30,15 @@ build: build-shell: docker build -t $(TAG_SHELL):$(VERSION) -t $(TAG_SHELL):latest $(ROOT_DIR)/shell +build-openapi: + docker build -t $(TAG_OPENAPI):$(VERSION) -t $(TAG_OPENAPI):latest $(ROOT_DIR)/openapi + publish-version: require-docker-auth [ -f $(ROOT_DIR)/arkindex/project/local_settings.py ] && mv $(ROOT_DIR)/arkindex/project/local_settings.py $(ROOT_DIR)/arkindex/project/local_settings.py.bak || true $(MAKE) build TAG_APP=registry.gitlab.com/arkindex/backend $(MAKE) build-shell TAG_SHELL=registry.gitlab.com/arkindex/backend/shell - docker push registry.gitlab.com/arkindex/backend:$(VERSION) + $(MAKE) build-openapi TAG_OPENAPI=registry.gitlab.com/arkindex/backend/openapi + docker push registry.gitlab.com/arkindex/backend:$(VERSION) registry.gitlab.com/arkindex/backend/shell:$(VERSION) registry.gitlab.com/arkindex/backend/openapi:$(VERSION) [ -f $(ROOT_DIR)/arkindex/project/local_settings.py.bak ] && mv $(ROOT_DIR)/arkindex/project/local_settings.py.bak $(ROOT_DIR)/arkindex/project/local_settings.py || true latest: @@ -44,6 +49,7 @@ release: $(MAKE) publish-version VERSION=$(version) docker push registry.gitlab.com/arkindex/backend:latest docker push registry.gitlab.com/arkindex/backend/shell:latest + docker push registry.gitlab.com/arkindex/backend/openapi:latest git tag $(version) tunnel: diff --git a/arkindex/dataimport/api.py b/arkindex/dataimport/api.py index 5b339b4876..1bb1c1893b 100644 --- a/arkindex/dataimport/api.py +++ b/arkindex/dataimport/api.py @@ -20,7 +20,8 @@ from arkindex.dataimport.models import \ from arkindex.dataimport.serializers import ( DataImportLightSerializer, DataImportSerializer, DataImportFromFilesSerializer, DataImportFailureSerializer, DataFileSerializer, - RepositorySerializer, ExternalRepositorySerializer, EventSerializer, MLToolSerializer, + RepositorySerializer, RepositoryStartImportSerializer, + ExternalRepositorySerializer, EventSerializer, MLToolSerializer, ) from arkindex.users.models import OAuthCredentials from arkindex_common.ml_tool import MLTool @@ -364,13 +365,14 @@ class RepositoryRetrieve(CorpusACLMixin, RetrieveDestroyAPIView): class RepositoryStartImport(RetrieveAPIView): permission_classes = (IsAdminUser, ) + serializer_class = RepositoryStartImportSerializer def get_queryset(self): return Repository.objects.filter( corpus__in=Corpus.objects.writable(self.request.user), ) - def get(self, request, *args, **kwargs): + def retrieve(self, request, *args, **kwargs): repo = self.get_object() if not repo.enabled: @@ -380,7 +382,9 @@ class RepositoryStartImport(RetrieveAPIView): credentials=repo.credentials, ).get_or_create_latest_revision(repo) - return Response(data={'import_id': str(rev.start_import().id)}) + di = rev.start_import() + + return Response(self.get_serializer(di).data) class ElementHistory(ListAPIView): diff --git a/arkindex/dataimport/serializers.py b/arkindex/dataimport/serializers.py index 072883902c..0d4d3cc6ff 100644 --- a/arkindex/dataimport/serializers.py +++ b/arkindex/dataimport/serializers.py @@ -222,6 +222,20 @@ class RepositorySerializer(serializers.ModelSerializer): } +class RepositoryStartImportSerializer(serializers.ModelSerializer): + """ + A serializer used by the RepositoryStartImport endpoint to return a DataImport ID. + This serializer is required to get the OpenAPI schema generation to work. + """ + + import_id = serializers.UUIDField(source='id') + + class Meta: + model = DataImport + fields = ('import_id',) + read_only_fields = ('import_id',) + + class ExternalRepositorySerializer(serializers.Serializer): """ Serialize a Git repository from an external API diff --git a/arkindex/dataimport/tests/test_repos.py b/arkindex/dataimport/tests/test_repos.py index 0d2b6d5384..9aa44f4f22 100644 --- a/arkindex/dataimport/tests/test_repos.py +++ b/arkindex/dataimport/tests/test_repos.py @@ -1,3 +1,6 @@ +from django.urls import reverse +from rest_framework import status +from unittest.mock import patch from arkindex.project.tests import FixtureTestCase from arkindex.dataimport.models import Repository, DataImport, DataImportMode from ponos.models import Workflow @@ -6,12 +9,11 @@ from rest_framework.exceptions import ValidationError class TestRepositories(FixtureTestCase): - @classmethod - def setUpTestData(cls): - super().setUpTestData() - cls.creds = cls.user.credentials.get() - cls.repo = cls.creds.repos.get() - cls.rev = cls.repo.revisions.get() + def setUp(self): + super().setUp() + self.creds = self.user.credentials.get() + self.repo = self.creds.repos.get() + self.rev = self.repo.revisions.get() def test_delete_credentials_null(self): """ @@ -21,13 +23,13 @@ class TestRepositories(FixtureTestCase): self.assertTrue(Repository.objects.filter(url='http://gitlab/repo').exists()) self.repo.refresh_from_db() self.assertTrue(self.repo.revisions.exists()) - self.creds.save() # Put them back def test_no_credentials_no_import(self): """ Check Repository imports do not start without credentials """ - self.creds.delete() + self.repo.credentials = None + self.repo.save() self.assertEqual(Workflow.objects.count(), 0) di = DataImport.objects.create( @@ -44,4 +46,35 @@ class TestRepositories(FixtureTestCase): di.retry() self.assertEqual(Workflow.objects.count(), 0) - self.creds.save() + + @patch('arkindex.dataimport.providers.GitLabProvider.get_or_create_latest_revision') + def test_start(self, gitlab_rev_mock): + gitlab_rev_mock.return_value = self.rev, False + self.client.force_login(self.superuser) + self.assertEqual(Workflow.objects.count(), 0) + + resp = self.client.get(reverse('api:repository-import', kwargs={'pk': str(self.repo.id)})) + self.assertEqual(resp.status_code, status.HTTP_200_OK) + data = resp.json() + + di = DataImport.objects.get(id=data['import_id']) + self.assertEqual(di.corpus, self.corpus) + self.assertEqual(di.mode, DataImportMode.Repository) + self.assertEqual(di.creator, self.user) + self.assertEqual(di.revision, self.rev) + + self.assertEqual(Workflow.objects.count(), 1) + + def test_start_no_credentials(self): + """ + Test the repository start endpoint fails without credentials + """ + self.client.force_login(self.superuser) + self.repo.credentials = None + self.repo.save() + self.assertFalse(self.repo.enabled) + self.assertEqual(Workflow.objects.count(), 0) + + resp = self.client.get(reverse('api:repository-import', kwargs={'pk': str(self.repo.id)})) + self.assertEqual(resp.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(Workflow.objects.count(), 0) diff --git a/arkindex/documents/serializers/ml.py b/arkindex/documents/serializers/ml.py index f8d1b0a568..56f0b18ccb 100644 --- a/arkindex/documents/serializers/ml.py +++ b/arkindex/documents/serializers/ml.py @@ -82,7 +82,8 @@ class TranscriptionCreateSerializer(serializers.Serializer): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - assert 'request' in self.context, 'An API request is required to initialize this serializer' + if not self.context.get('request'): # May be None when generating an OpenAPI schema or using from a REPL + return self.fields['element'].queryset = Element.objects.filter( corpus__in=Corpus.objects.writable(self.context['request'].user), ) @@ -118,7 +119,8 @@ class TranscriptionsSerializer(serializers.Serializer): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - assert 'request' in self.context, 'An API request is required to initialize this serializer' + if not self.context.get('request'): # May be None when generating an OpenAPI schema or using from a REPL + return self.fields['parent'].queryset = Element.objects.filter( corpus__in=Corpus.objects.writable(self.context['request'].user), ) @@ -143,7 +145,8 @@ class ClassificationsSerializer(serializers.Serializer): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - assert 'request' in self.context, 'An API request is required to initialize this serializer' + if not self.context.get('request'): # May be None when generating an OpenAPI schema or using from a REPL + return self.fields['parent'].queryset = Page.objects.filter( corpus__in=Corpus.objects.writable(self.context['request'].user), ) diff --git a/arkindex/users/api.py b/arkindex/users/api.py index 0a55e9c24c..c0742ac74c 100644 --- a/arkindex/users/api.py +++ b/arkindex/users/api.py @@ -18,7 +18,7 @@ from arkindex.documents.models import Corpus, Element, ElementType from arkindex.users.providers import oauth_providers, get_provider from arkindex.users.models import User, OAuthStatus from arkindex.users.serializers import ( - OAuthCredentialsSerializer, OAuthProviderClassSerializer, + OAuthCredentialsSerializer, OAuthProviderClassSerializer, OAuthRetrySerializer, UserSerializer, NewDemoUserSerializer, EmailLoginSerializer, PasswordResetSerializer, PasswordResetConfirmSerializer, ) @@ -242,6 +242,7 @@ class OAuthRetry(RetrieveAPIView): Restart an OAuth authentication workflow for existing credentials """ permission_classes = (IsVerified, ) + serializer_class = OAuthRetrySerializer def check_permissions(self, request): # Will raise REST framework exceptions for denied requests diff --git a/arkindex/users/serializers.py b/arkindex/users/serializers.py index 27b8b94b83..553c13ec9d 100644 --- a/arkindex/users/serializers.py +++ b/arkindex/users/serializers.py @@ -29,6 +29,15 @@ class OAuthProviderClassSerializer(serializers.Serializer): default_url = serializers.URLField(source='url') +class OAuthRetrySerializer(serializers.Serializer): + """ + A serializer used by the OAuthRetry endpoint to return an authorization URL. + Required to get the OpenAPI schema generation to work. + """ + + url = serializers.URLField() + + class UserSerializer(serializers.ModelSerializer): class Meta: diff --git a/openapi/Dockerfile b/openapi/Dockerfile new file mode 100644 index 0000000000..1290a6b66f --- /dev/null +++ b/openapi/Dockerfile @@ -0,0 +1,9 @@ +# FROM registry.gitlab.com/arkindex/backend:latest +FROM arkindex-app + +RUN pip uninstall -y djangorestframework +COPY ["patch.py", "run.sh", "requirements.txt", "patch.yml", "/"] +RUN pip install -r /requirements.txt && rm /requirements.txt + +ENTRYPOINT ["/bin/sh", "-c"] +CMD ["/run.sh"] diff --git a/openapi/patch.py b/openapi/patch.py new file mode 100755 index 0000000000..bf855c6362 --- /dev/null +++ b/openapi/patch.py @@ -0,0 +1,132 @@ +#!/usr/bin/env python3 +import argparse +import apistar +import os.path +import pkg_resources +import sys +import yaml + +BASE_DIR = os.path.dirname(os.path.abspath(__file__)) + + +def get_args(): + parser = argparse.ArgumentParser() + parser.add_argument( + 'patch', + help='File describing patches to make on the schema', + type=argparse.FileType('r'), + ) + parser.add_argument( + 'original', + help='Generated OpenAPI schema', + type=argparse.FileType('r'), + nargs='?', + default=sys.stdin, + ) + parser.add_argument( + 'output', + help='Destination file', + type=argparse.FileType('w'), + nargs='?', + default=sys.stdout, + ) + args = vars(parser.parse_args()) + + # Load YAML files + args['original'] = yaml.load(args['original']) + args['patch'] = yaml.load(args['patch']) + return args + + +def update_schema(schema, patches): + """ + Perform updates on an OpenAPI schema using another YAML file describing patches. + The update is not recursive; any root key will overwrite the schema's original data. + For paths, each operation has its keys updated; the whole operation is not overwritten. + """ + # Update each method separately + paths = patches.pop('paths', {}) + for path, methods in paths.items(): + if path not in schema['paths']: + print('Creating path {}'.format(path), file=sys.stderr) + schema['paths'][path] = {} + + for method, operation in methods.items(): + print( + '{} method {} on path {}'.format( + 'Updating' if method in schema['paths'][path] else 'Creating', + method, + path, + ), + file=sys.stderr, + ) + schema['paths'][path].setdefault(method, {}) + schema['paths'][path][method].update(operation) + + # Update other root keys + for k, v in patches.items(): + print('Patching {}'.format(k), file=sys.stderr) + schema[k] = v + + # Set the API version to the Arkindex package version + try: + # Try with the VERSION file + with open(os.path.join(BASE_DIR, '..', 'VERSION')) as f: + ark_version = f.read().strip() + except FileNotFoundError: + # Fall back to the pip package version + ark_version = pkg_resources.get_distribution('arkindex').version + + schema.setdefault('info', {}) + schema['info']['version'] = ark_version + + # Add x-name property on request body objects to let apistar register the parameters + for path, methods in schema['paths'].items(): + for method, operation in methods.items(): + if 'requestBody' not in operation: + continue + if not operation['requestBody']['content']['application/json']['schema']: + # Ignore empty schemas + continue + + # Make sure we do not duplicate an existing parameter name + assert not any(param['name'] == 'body' for param in operation['parameters']), \ + 'Operation already has a body parameter' + + print('Adding x-name to {} on {}'.format(method, path), file=sys.stderr) + operation['requestBody']['x-name'] = 'body' + + return schema + + +def print_endpoints(schema): + """ + Output a list of all endpoints with their operation IDs and security settings to stderr + """ + for path, methods in schema['paths'].items(): + print(path, file=sys.stderr) + for method, operation in methods.items(): + security = operation.get('security') + auth = 'Custom' + if security is None: + auth = 'Default' + elif security == []: + auth = 'No' + + print(' {}: {} - {} authentication'.format( + method, + operation.get('operationId', 'No operation ID!'), + auth, + ), file=sys.stderr) + + +def main(): + args = get_args() + schema = update_schema(args['original'], args['patch']) + print_endpoints(schema) + apistar.validate(schema) + yaml.dump(schema, args['output'], default_flow_style=False) + + +if __name__ == '__main__': + main() diff --git a/openapi/patch.yml b/openapi/patch.yml new file mode 100644 index 0000000000..f97d5db790 --- /dev/null +++ b/openapi/patch.yml @@ -0,0 +1,439 @@ +info: + title: Arkindex API + contact: + name: Teklia + url: https://www.teklia.com/ + email: paris@teklia.com +components: + securitySchemes: + sessionAuth: + in: cookie + name: arkindex.auth + type: apiKey + tokenAuth: + scheme: Token + type: http +security: +- tokenAuth: [] +- sessionAuth: [] +servers: + - description: Arkindex + url: https://arkindex.teklia.com + - description: Arkindex preproduction + url: https://arkindex.dev.teklia.com +tags: + - name: corpora + - name: elements + - name: search + - name: oauth + - name: imports + - name: files + - name: ponos + - name: iiif + description: IIIF manifests, annotation lists and services + - name: ml + description: Machine Learning tools and results +paths: + /api/v1/act/{id}/: + get: + description: Retrieve an act + security: [] + tags: + - elements + /api/v1/acts/: + get: + operationId: SearchActs + description: >- + Get a list of acts with their parent registers or volumes, the total + number of transcriptions found in the act, and a few (not all) of the + transcriptions found inside of each act, with their source, type, + zone and image, for a given search query. + security: [] + tags: + - search + /api/v1/classification/bulk/: + post: + description: >- + Create multiple classifications at once on the same element with + the same classifier. + tags: + - ml + /api/v1/corpus/: + get: + description: List corpora with their access rights + security: [] + tags: + - corpora + post: + description: Create a new corpus + tags: + - corpora + /api/v1/corpus/{id}/: + get: + description: Retrieve a single corpus + security: [] + tags: + - corpora + put: + description: Update a corpus + tags: + - corpora + patch: + description: Partially update a corpus + tags: + - corpora + delete: + description: >- + Delete a corpus. Requires the "admin" right on the corpus. + + Warning: This operation might not work on corpora with a large amount + of elements, as the deletion process will take more than 30 seconds. + tags: + - corpora + /api/v1/corpus/{id}/pages/: + get: + operationId: ListCorpusPages + description: List all pages in all volumes of a corpus + security: [] + tags: + - elements + /api/v1/element/{id}/: + get: + description: Retrieve detailed information on a single element + security: [] + tags: + - elements + patch: + description: Rename an element + tags: + - elements + put: + description: Rename an element + tags: + - elements + /api/v1/element/{id}/history/: + get: + description: List an element's update history + security: [] + tags: + - elements + /api/v1/elements/: + get: + operationId: ListElements + description: List all elements, filtered by type + security: [] + tags: + - elements + /api/v1/elements/{id}/: + get: + operationId: ListRelatedElements + description: List all parents and children of a single element + security: [] + tags: + - elements + /api/v1/elements/{id}/pages/: + get: + operationId: ListElementPages + description: Detailed list of all children pages of an element + security: [] + tags: + - elements + /api/v1/elements/{id}/surfaces/: + get: + operationId: ListElementSurfaces + description: Detailed list of all children surfaces of an element + security: [] + tags: + - elements + /api/v1/imports/: + get: + operationId: ListDataImports + description: List all data imports + tags: + - imports + /api/v1/imports/demo/{id}/: + post: + description: Run a data import with reduced access for demo users + security: [] + tags: + - imports + /api/v1/imports/file/{id}/: + get: + description: Get an uploaded file's metadata + tags: + - files + patch: + description: Rename an uploaded file + tags: + - files + put: + description: Rename an uploaded file + tags: + - files + delete: + description: Delete an uploaded file + tags: + - files + /api/v1/imports/files/{id}/: + get: + operationId: ListDataFiles + description: List uploaded files in a corpus + tags: + - files + /api/v1/imports/fromfiles/: + post: + description: Start a data import from one or more uploaded files + tags: + - files + /api/v1/imports/hook/{id}/: + post: + operationId: GitPushHook + description: >- + This endpoint is intended as a webhook for Git repository hosting applications like GitLab. + security: [] + tags: + - repos + /api/v1/imports/mltools/: + get: + description: List available machine learning tools + security: [] + tags: + - ml + /api/v1/imports/repos/: + get: + operationId: ListRepositories + description: List connected repositories + tags: + - repos + /api/v1/imports/repos/search/{id}/: + get: + description: >- + Search for a repository to connect to. + + Using the given OAuth credentials ID, this uses the Git hosting + application API's search feature to look for a repository matching + the given query. Without a query, returns a full list. + tags: + - repos + post: + description: >- + Using the given OAuth credentials, this links an external Git repository + to Arkindex, connects a push hook and starts an initial import. + tags: + - repos + /api/v1/imports/repos/{id}/: + get: + description: Get a repository + tags: + - repos + delete: + description: Delete a repository + tags: + - repos + /api/v1/imports/repos/{id}/start/: + get: + operationId: StartRepositoryImport + description: Start a data import on the latest revision on a repository + tags: + - repos + /api/v1/imports/upload/{id}/: + post: + operationId: UploadDataFile + description: Upload a file to a corpus + tags: + - files + /api/v1/imports/{id}/: + get: + description: Retrieve a data import + tags: + - imports + delete: + description: Delete a data import. Cannot be used on currently running data imports. + tags: + - imports + /api/v1/imports/{id}/failures/: + get: + description: List a data import's XML errors + tags: + - imports + /api/v1/imports/{id}/retry/: + post: + operationId: RetryDataImport + description: Retry a data import. Can only be used on imports with Error or Failed states. + tags: + - imports + /api/v1/manifest/{id}/act/: + get: + operationId: RetrieveActManifest + description: Retrieve a IIIF manifest for an act + security: [] + tags: + - iiif + /api/v1/manifest/{id}/acts/: + get: + operationId: RetrieveActAnnotationList + description: Retrieve an IIIF annotation list for a volume's surfaces and acts + security: [] + tags: + - iiif + /api/v1/manifest/{id}/pages/: + get: + description: Retrieve an IIIF manifest for a volume. + security: [] + tags: + - iiif + /api/v1/manifest/{id}/search/: + get: + operationId: SearchTranscriptionsAnnotationList + description: >- + Search for transcriptions on a volume manifest. + This endpoint is intended as a IIIF Search API 2.0 service. + security: [] + tags: + - iiif + /api/v1/manifest/{id}/surface/: + get: + description: Retrieve an IIIF annotation list for a single surface + security: [] + tags: + - iiif + /api/v1/manifest/{id}/transcriptions/: + get: + operationId: RetrieveTranscriptionAnnotationList + description: Retrieve an IIIF annotation list for a volume's transcriptions + security: [] + tags: + - iiif + /api/v1/oauth/credentials/: + get: + description: List all OAuth credentials for the authenticated user + tags: + - oauth + /api/v1/oauth/credentials/{id}/: + get: + description: Retrieve OAuth credentials + tags: + - oauth + delete: + description: Delete OAuth credentials. This may disable access to some Git repositories. + tags: + - oauth + /api/v1/oauth/credentials/{id}/retry/: + get: + operationId: RetryOAuthCredentials + description: Retry the OAuth authentication code flow for pending credentials + tags: + - oauth + /api/v1/oauth/providers/: + get: + operationId: ListOAuthProviders + description: List supported OAuth providers + tags: + - oauth + /api/v1/oauth/providers/{provider}/signin/: + get: + operationId: StartOAuthSignIn + description: Start the OAuth authentication code flow for a given provider + tags: + - oauth + /api/v1/page/{id}/: + get: + description: Retrieve detailed information about a page + security: [] + tags: + - elements + /api/v1/pages/: + get: + operationId: SearchPages + description: >- + Get a list of pages with their parent registers or volumes, the total + number of transcriptions found in the page, and a few (not all) of the + transcriptions found inside of each page, with their source, type, + zone and image, for a given search query. + security: [] + tags: + - search + /api/v1/surface/{id}/: + get: + description: Retrieve detailed information about a surface + security: [] + tags: + - elements + /api/v1/transcription/: + post: + operationId: CreateTranscription + description: Create a single transcription on a page + tags: + - ml + /api/v1/transcription/bulk/: + post: + description: >- + Create multiple transcriptions at once, all linked to the same page + and to the same classifier. + tags: + - ml + /api/v1/user/: + get: + description: Retrieve information about the authenticated user + tags: + - users + patch: + description: Update a user's password. This action is not allowed to users without confirmed e-mails. + tags: + - users + put: + description: Update a user's password. This action is not allowed to users without confirmed e-mails. + tags: + - users + delete: + operationId: Logout + description: Log out from the API + tags: + - users + /api/v1/user/login/: + post: + operationId: Login + description: Login using a username and a password + security: [] + tags: + - users + /api/v1/user/new/: + post: + operationId: Register + description: Register as a demo user + security: [] + tags: + - users + /api/v1/user/password-reset/: + post: + operationId: ResetPassword + description: Start a password reset flow + security: [] + tags: + - users + /api/v1/user/password-reset/confirm/: + post: + operationId: PasswordResetConfirm + description: Confirm a password reset using data from the confirmation e-mail + security: [] + tags: + - users + /ponos/v1/task/{id}/: + get: + description: Retrieve a Ponos task status + security: [] + tags: + - ponos + /ponos/v1/task/{id}/log/: + get: + operationId: RetrieveTaskLog + description: Retrieve the full task log as plain text + security: [] + tags: + - ponos + /ponos/v1/workflow/{id}/: + get: + description: Retrieve a Ponos workflow status + security: [] + tags: + - ponos diff --git a/openapi/requirements.txt b/openapi/requirements.txt new file mode 100644 index 0000000000..e9cc0800b3 --- /dev/null +++ b/openapi/requirements.txt @@ -0,0 +1,3 @@ +git+https://github.com/encode/django-rest-framework.git@ac64c0a536b0ae21b81d86c3c2a37bc0c70f932e#egg=djangorestframework +coreapi==2.3.3 +apistar>=0.7.2 diff --git a/openapi/run.sh b/openapi/run.sh new file mode 100755 index 0000000000..47c4f7391c --- /dev/null +++ b/openapi/run.sh @@ -0,0 +1,3 @@ +#!/bin/sh +PONOS_DATA_DIR=/tmp manage.py generateschema > original.yml +./patch.py patch.yml original.yml diff --git a/requirements.txt b/requirements.txt index 155f512b7a..10dd5e991a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,7 +6,7 @@ certifi==2017.7.27.1 chardet==3.0.4 django-cors-headers==2.4.0 django-enumfields==1.0.0 -djangorestframework==3.7.1 +djangorestframework==3.9.2 et-xmlfile==1.0.1 gitpython==2.1.11 idna==2.6 -- GitLab