diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index b3a0fa5..60b05a7 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -1,20 +1,54 @@ -name: Deploy +name: Deploy to PyPI on: - workflow_dispatch: + release: + types: + - published jobs: - release: + publish-to-test-pypi: runs-on: ubuntu-latest + environment: + name: test + permissions: + id-token: write + + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.8' + + - name: Build the distribution + run: make build + - name: Install the package + run: make test-install-build + - name: Publish package distributions to Test PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + repository-url: https://test.pypi.org/legacy/ + + publish-to-pypi: + runs-on: ubuntu-latest + environment: + name: production + # if release to test PyPI fails, still run this job + # since we can check the reason for failure manually + # before approving the release + if: always() + needs: publish-to-test-pypi + permissions: + id-token: write + steps: - uses: actions/checkout@v4 - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 with: python-version: '3.8' - - run: make install-dev - - run: make build - - run: make test-install-build - - env: - TWINE_USERNAME: __token__ - TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} - run: make deploy + + - name: Build the distribution + run: make build + - name: Install the package + run: make test-install-build + - name: Publish package distributions to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f9f33df..30a2d35 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,9 +1,7 @@ -name: Release +name: Create Release on: - push: - branches: - - 'main' + workflow_dispatch: jobs: release: @@ -12,10 +10,13 @@ jobs: - uses: actions/checkout@v4 with: persist-credentials: false - - uses: pnpm/action-setup@v2 + - name: Set up pnpm + uses: pnpm/action-setup@v2 with: version: 8 - - run: pnpm install - - env: + - name: Install dependencies + run: pnpm install + - name: Generate a release + env: GITHUB_TOKEN: ${{ secrets.CLI_PAT }} run: pnpm run release diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4e71f76..3f1c60c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,7 +21,7 @@ repos: - id: trailing-whitespace - id: no-commit-to-branch - repo: https://github.com/alessandrojcm/commitlint-pre-commit-hook - rev: v9.5.0 + rev: v9.22.0 hooks: - id: commitlint stages: [commit-msg] diff --git a/tests/test_simple.py b/tests/test_simple.py index 174077f..2b6785a 100644 --- a/tests/test_simple.py +++ b/tests/test_simple.py @@ -1,6 +1,9 @@ +from typing import Any, Dict from fastapi.testclient import TestClient from unittest import TestCase + +import pydantic from examples.simple import app, versions @@ -134,798 +137,803 @@ def test_simple_example(self) -> None: self.assertEqual(200, test_client.get('/v1/swagger').status_code) self.assertEqual(200, test_client.get('/v2/swagger').status_code) self.assertEqual(200, test_client.get('/latest/swagger').status_code) - - # openapi - self.assertDictEqual( - { - 'openapi': '3.1.0', - 'info': { - 'title': 'test', - 'description': 'Simple example of FastAPI Versionizer.', - 'termsOfService': 'https://github.com/alexschimpf/fastapi-versionizer', - 'version': '0.1.0' - }, - 'paths': { - '/v1/items': { - 'get': { - 'tags': [ - 'Items' - ], - 'summary': 'Get Items', - 'operationId': 'get_items_v1_items_get', - 'responses': { - '200': { - 'description': 'Successful Response', - 'content': { - 'application/json': { - 'schema': { - 'items': { - '$ref': '#/components/schemas/Item' - }, - 'type': 'array', - 'title': 'Response Get Items V1 Items Get' - } + expected_response: Dict[str, Any] = { + 'openapi': '3.1.0', + 'info': { + 'title': 'test', + 'description': 'Simple example of FastAPI Versionizer.', + 'termsOfService': 'https://github.com/alexschimpf/fastapi-versionizer', + 'version': '0.1.0' + }, + 'paths': { + '/v1/items': { + 'get': { + 'tags': [ + 'Items' + ], + 'summary': 'Get Items', + 'operationId': 'get_items_v1_items_get', + 'responses': { + '200': { + 'description': 'Successful Response', + 'content': { + 'application/json': { + 'schema': { + 'items': { + '$ref': '#/components/schemas/Item' + }, + 'type': 'array', + 'title': 'Response Get Items V1 Items Get' } } } + } + }, + 'deprecated': True + }, + 'post': { + 'tags': [ + 'Items' + ], + 'summary': 'Create Item', + 'operationId': 'create_item_v1_items_post', + 'requestBody': { + 'content': { + 'application/json': { + 'schema': { + '$ref': '#/components/schemas/Item' + } + } }, - 'deprecated': True + 'required': True }, - 'post': { - 'tags': [ - 'Items' - ], - 'summary': 'Create Item', - 'operationId': 'create_item_v1_items_post', - 'requestBody': { + 'responses': { + '200': { + 'description': 'Successful Response', 'content': { 'application/json': { 'schema': { '$ref': '#/components/schemas/Item' } } - }, - 'required': True + } }, - 'responses': { - '200': { - 'description': 'Successful Response', - 'content': { - 'application/json': { - 'schema': { - '$ref': '#/components/schemas/Item' - } + '422': { + 'description': 'Validation Error', + 'content': { + 'application/json': { + 'schema': { + '$ref': '#/components/schemas/HTTPValidationError' } } - }, - '422': { - 'description': 'Validation Error', - 'content': { - 'application/json': { - 'schema': { - '$ref': '#/components/schemas/HTTPValidationError' - } + } + } + }, + 'deprecated': True + } + }, + '/v1/items/{item_id}': { + 'get': { + 'tags': [ + 'Items' + ], + 'summary': 'Get Item', + 'operationId': 'get_item_v1_items__item_id__get', + 'deprecated': True, + 'parameters': [ + { + 'name': 'item_id', + 'in': 'path', + 'required': True, + 'schema': { + 'type': 'integer', + 'title': 'Item Id' + } + } + ], + 'responses': { + '200': { + 'description': 'Successful Response', + 'content': { + 'application/json': { + 'schema': { + '$ref': '#/components/schemas/Item' } } } }, - 'deprecated': True - } - }, - '/v1/items/{item_id}': { - 'get': { - 'tags': [ - 'Items' - ], - 'summary': 'Get Item', - 'operationId': 'get_item_v1_items__item_id__get', - 'deprecated': True, - 'parameters': [ - { - 'name': 'item_id', - 'in': 'path', - 'required': True, - 'schema': { - 'type': 'integer', - 'title': 'Item Id' - } - } - ], - 'responses': { - '200': { - 'description': 'Successful Response', - 'content': { - 'application/json': { - 'schema': { - '$ref': '#/components/schemas/Item' - } + '422': { + 'description': 'Validation Error', + 'content': { + 'application/json': { + 'schema': { + '$ref': '#/components/schemas/HTTPValidationError' } } - }, - '422': { - 'description': 'Validation Error', - 'content': { - 'application/json': { - 'schema': { - '$ref': '#/components/schemas/HTTPValidationError' - } + } + } + } + } + }, + '/v1/status': { + 'get': { + 'tags': [ + 'Status' + ], + 'summary': 'Get Status', + 'operationId': 'get_status_v1_status_get', + 'responses': { + '200': { + 'description': 'Successful Response', + 'content': { + 'application/json': { + 'schema': { + 'type': 'string', + 'title': 'Response Get Status V1 Status Get' } } } } } - }, - '/v1/status': { - 'get': { - 'tags': [ - 'Status' - ], - 'summary': 'Get Status', - 'operationId': 'get_status_v1_status_get', - 'responses': { - '200': { - 'description': 'Successful Response', - 'content': { - 'application/json': { - 'schema': { - 'type': 'string', - 'title': 'Response Get Status V1 Status Get' - } + } + }, + '/v1/users': { + 'get': { + 'tags': [ + 'Users' + ], + 'summary': 'Get Users', + 'operationId': 'get_users_v1_users_get', + 'responses': { + '200': { + 'description': 'Successful Response', + 'content': { + 'application/json': { + 'schema': { + 'items': { + '$ref': '#/components/schemas/User' + }, + 'type': 'array', + 'title': 'Response Get Users V1 Users Get' } } } } - } + }, + 'deprecated': True }, - '/v1/users': { - 'get': { - 'tags': [ - 'Users' - ], - 'summary': 'Get Users', - 'operationId': 'get_users_v1_users_get', - 'responses': { - '200': { - 'description': 'Successful Response', - 'content': { - 'application/json': { - 'schema': { - 'items': { - '$ref': '#/components/schemas/User' - }, - 'type': 'array', - 'title': 'Response Get Users V1 Users Get' - } - } + 'post': { + 'tags': [ + 'Users' + ], + 'summary': 'Create User', + 'operationId': 'create_user_v1_users_post', + 'requestBody': { + 'content': { + 'application/json': { + 'schema': { + '$ref': '#/components/schemas/User' } } }, - 'deprecated': True + 'required': True }, - 'post': { - 'tags': [ - 'Users' - ], - 'summary': 'Create User', - 'operationId': 'create_user_v1_users_post', - 'requestBody': { + 'responses': { + '200': { + 'description': 'Successful Response', 'content': { 'application/json': { 'schema': { '$ref': '#/components/schemas/User' } } - }, - 'required': True + } }, - 'responses': { - '200': { - 'description': 'Successful Response', - 'content': { - 'application/json': { - 'schema': { - '$ref': '#/components/schemas/User' - } + '422': { + 'description': 'Validation Error', + 'content': { + 'application/json': { + 'schema': { + '$ref': '#/components/schemas/HTTPValidationError' } } - }, - '422': { - 'description': 'Validation Error', - 'content': { - 'application/json': { - 'schema': { - '$ref': '#/components/schemas/HTTPValidationError' - } + } + } + }, + 'deprecated': True + } + }, + '/v1/users/{user_id}': { + 'get': { + 'tags': [ + 'Users' + ], + 'summary': 'Get User', + 'operationId': 'get_user_v1_users__user_id__get', + 'deprecated': True, + 'parameters': [ + { + 'name': 'user_id', + 'in': 'path', + 'required': True, + 'schema': { + 'type': 'integer', + 'title': 'User Id' + } + } + ], + 'responses': { + '200': { + 'description': 'Successful Response', + 'content': { + 'application/json': { + 'schema': { + '$ref': '#/components/schemas/User' } } } }, - 'deprecated': True + '422': { + 'description': 'Validation Error', + 'content': { + 'application/json': { + 'schema': { + '$ref': '#/components/schemas/HTTPValidationError' + } + } + } + } } - }, - '/v1/users/{user_id}': { - 'get': { - 'tags': [ - 'Users' - ], - 'summary': 'Get User', - 'operationId': 'get_user_v1_users__user_id__get', - 'deprecated': True, - 'parameters': [ - { - 'name': 'user_id', - 'in': 'path', - 'required': True, - 'schema': { - 'type': 'integer', - 'title': 'User Id' - } - } - ], - 'responses': { - '200': { - 'description': 'Successful Response', - 'content': { - 'application/json': { - 'schema': { - '$ref': '#/components/schemas/User' - } - } - } - }, - '422': { - 'description': 'Validation Error', - 'content': { - 'application/json': { - 'schema': { - '$ref': '#/components/schemas/HTTPValidationError' - } - } + } + }, + '/v2/items': { + 'get': { + 'tags': [ + 'Items' + ], + 'summary': 'Get Items V2', + 'operationId': 'get_items_v2_v2_items_get', + 'responses': { + '200': { + 'description': 'Successful Response', + 'content': { + 'application/json': { + 'schema': { + 'items': { + '$ref': '#/components/schemas/ItemV2' + }, + 'type': 'array', + 'title': 'Response Get Items V2 V2 Items Get' + } } } } } }, - '/v2/items': { - 'get': { - 'tags': [ - 'Items' - ], - 'summary': 'Get Items V2', - 'operationId': 'get_items_v2_v2_items_get', - 'responses': { - '200': { - 'description': 'Successful Response', - 'content': { - 'application/json': { - 'schema': { - 'items': { - '$ref': '#/components/schemas/ItemV2' - }, - 'type': 'array', - 'title': 'Response Get Items V2 V2 Items Get' - } - } + 'post': { + 'tags': [ + 'Items' + ], + 'summary': 'Create Item V2', + 'operationId': 'create_item_v2_v2_items_post', + 'requestBody': { + 'content': { + 'application/json': { + 'schema': { + '$ref': '#/components/schemas/ItemV2' } } - } + }, + 'required': True }, - 'post': { - 'tags': [ - 'Items' - ], - 'summary': 'Create Item V2', - 'operationId': 'create_item_v2_v2_items_post', - 'requestBody': { + 'responses': { + '200': { + 'description': 'Successful Response', 'content': { 'application/json': { 'schema': { '$ref': '#/components/schemas/ItemV2' } } - }, - 'required': True + } }, - 'responses': { - '200': { - 'description': 'Successful Response', - 'content': { - 'application/json': { - 'schema': { - '$ref': '#/components/schemas/ItemV2' - } + '422': { + 'description': 'Validation Error', + 'content': { + 'application/json': { + 'schema': { + '$ref': '#/components/schemas/HTTPValidationError' } } - }, - '422': { - 'description': 'Validation Error', - 'content': { - 'application/json': { - 'schema': { - '$ref': '#/components/schemas/HTTPValidationError' - } + } + } + } + } + }, + '/v2/status': { + 'get': { + 'tags': [ + 'Status' + ], + 'summary': 'Get Status', + 'operationId': 'get_status_v2_status_get', + 'responses': { + '200': { + 'description': 'Successful Response', + 'content': { + 'application/json': { + 'schema': { + 'type': 'string', + 'title': 'Response Get Status V2 Status Get' } } } } } - }, - '/v2/status': { - 'get': { - 'tags': [ - 'Status' - ], - 'summary': 'Get Status', - 'operationId': 'get_status_v2_status_get', - 'responses': { - '200': { - 'description': 'Successful Response', - 'content': { - 'application/json': { - 'schema': { - 'type': 'string', - 'title': 'Response Get Status V2 Status Get' - } + } + }, + '/v2/users': { + 'get': { + 'tags': [ + 'Users' + ], + 'summary': 'Get Users V2', + 'operationId': 'get_users_v2_v2_users_get', + 'responses': { + '200': { + 'description': 'Successful Response', + 'content': { + 'application/json': { + 'schema': { + 'items': { + '$ref': '#/components/schemas/UserV2' + }, + 'type': 'array', + 'title': 'Response Get Users V2 V2 Users Get' } } } } } }, - '/v2/users': { - 'get': { - 'tags': [ - 'Users' - ], - 'summary': 'Get Users V2', - 'operationId': 'get_users_v2_v2_users_get', - 'responses': { - '200': { - 'description': 'Successful Response', - 'content': { - 'application/json': { - 'schema': { - 'items': { - '$ref': '#/components/schemas/UserV2' - }, - 'type': 'array', - 'title': 'Response Get Users V2 V2 Users Get' - } - } + 'post': { + 'tags': [ + 'Users' + ], + 'summary': 'Create User V2', + 'operationId': 'create_user_v2_v2_users_post', + 'requestBody': { + 'content': { + 'application/json': { + 'schema': { + '$ref': '#/components/schemas/UserV2' } } - } + }, + 'required': True }, - 'post': { - 'tags': [ - 'Users' - ], - 'summary': 'Create User V2', - 'operationId': 'create_user_v2_v2_users_post', - 'requestBody': { + 'responses': { + '200': { + 'description': 'Successful Response', 'content': { 'application/json': { 'schema': { '$ref': '#/components/schemas/UserV2' } } - }, - 'required': True + } }, - 'responses': { - '200': { - 'description': 'Successful Response', - 'content': { - 'application/json': { - 'schema': { - '$ref': '#/components/schemas/UserV2' - } - } - } - }, - '422': { - 'description': 'Validation Error', - 'content': { - 'application/json': { - 'schema': { - '$ref': '#/components/schemas/HTTPValidationError' - } + '422': { + 'description': 'Validation Error', + 'content': { + 'application/json': { + 'schema': { + '$ref': '#/components/schemas/HTTPValidationError' } } } } } - }, - '/v2/users/{user_id}': { - 'get': { - 'tags': [ - 'Users' - ], - 'summary': 'Get User V2', - 'operationId': 'get_user_v2_v2_users__user_id__get', - 'parameters': [ - { - 'name': 'user_id', - 'in': 'path', - 'required': True, - 'schema': { - 'type': 'integer', - 'title': 'User Id' - } + } + }, + '/v2/users/{user_id}': { + 'get': { + 'tags': [ + 'Users' + ], + 'summary': 'Get User V2', + 'operationId': 'get_user_v2_v2_users__user_id__get', + 'parameters': [ + { + 'name': 'user_id', + 'in': 'path', + 'required': True, + 'schema': { + 'type': 'integer', + 'title': 'User Id' } - ], - 'responses': { - '200': { - 'description': 'Successful Response', - 'content': { - 'application/json': { - 'schema': { - '$ref': '#/components/schemas/UserV2' - } + } + ], + 'responses': { + '200': { + 'description': 'Successful Response', + 'content': { + 'application/json': { + 'schema': { + '$ref': '#/components/schemas/UserV2' } } - }, - '422': { - 'description': 'Validation Error', - 'content': { - 'application/json': { - 'schema': { - '$ref': '#/components/schemas/HTTPValidationError' - } + } + }, + '422': { + 'description': 'Validation Error', + 'content': { + 'application/json': { + 'schema': { + '$ref': '#/components/schemas/HTTPValidationError' } } } } } - }, - '/latest/items': { - 'get': { - 'tags': [ - 'Items' - ], - 'summary': 'Get Items V2', - 'operationId': 'get_items_v2_latest_items_get', - 'responses': { - '200': { - 'description': 'Successful Response', - 'content': { - 'application/json': { - 'schema': { - 'items': { - '$ref': '#/components/schemas/ItemV2' - }, - 'type': 'array', - 'title': 'Response Get Items V2 Latest Items Get' - } + } + }, + '/latest/items': { + 'get': { + 'tags': [ + 'Items' + ], + 'summary': 'Get Items V2', + 'operationId': 'get_items_v2_latest_items_get', + 'responses': { + '200': { + 'description': 'Successful Response', + 'content': { + 'application/json': { + 'schema': { + 'items': { + '$ref': '#/components/schemas/ItemV2' + }, + 'type': 'array', + 'title': 'Response Get Items V2 Latest Items Get' } } } } + } + }, + 'post': { + 'tags': [ + 'Items' + ], + 'summary': 'Create Item V2', + 'operationId': 'create_item_v2_latest_items_post', + 'requestBody': { + 'content': { + 'application/json': { + 'schema': { + '$ref': '#/components/schemas/ItemV2' + } + } + }, + 'required': True }, - 'post': { - 'tags': [ - 'Items' - ], - 'summary': 'Create Item V2', - 'operationId': 'create_item_v2_latest_items_post', - 'requestBody': { + 'responses': { + '200': { + 'description': 'Successful Response', 'content': { 'application/json': { 'schema': { '$ref': '#/components/schemas/ItemV2' } } - }, - 'required': True + } }, - 'responses': { - '200': { - 'description': 'Successful Response', - 'content': { - 'application/json': { - 'schema': { - '$ref': '#/components/schemas/ItemV2' - } + '422': { + 'description': 'Validation Error', + 'content': { + 'application/json': { + 'schema': { + '$ref': '#/components/schemas/HTTPValidationError' } } - }, - '422': { - 'description': 'Validation Error', - 'content': { - 'application/json': { - 'schema': { - '$ref': '#/components/schemas/HTTPValidationError' - } + } + } + } + } + }, + '/latest/status': { + 'get': { + 'tags': [ + 'Status' + ], + 'summary': 'Get Status', + 'operationId': 'get_status_latest_status_get', + 'responses': { + '200': { + 'description': 'Successful Response', + 'content': { + 'application/json': { + 'schema': { + 'type': 'string', + 'title': 'Response Get Status Latest Status Get' } } } } } - }, - '/latest/status': { - 'get': { - 'tags': [ - 'Status' - ], - 'summary': 'Get Status', - 'operationId': 'get_status_latest_status_get', - 'responses': { - '200': { - 'description': 'Successful Response', - 'content': { - 'application/json': { - 'schema': { - 'type': 'string', - 'title': 'Response Get Status Latest Status Get' - } + } + }, + '/latest/users': { + 'get': { + 'tags': [ + 'Users' + ], + 'summary': 'Get Users V2', + 'operationId': 'get_users_v2_latest_users_get', + 'responses': { + '200': { + 'description': 'Successful Response', + 'content': { + 'application/json': { + 'schema': { + 'items': { + '$ref': '#/components/schemas/UserV2' + }, + 'type': 'array', + 'title': 'Response Get Users V2 Latest Users Get' } } } } } }, - '/latest/users': { - 'get': { - 'tags': [ - 'Users' - ], - 'summary': 'Get Users V2', - 'operationId': 'get_users_v2_latest_users_get', - 'responses': { - '200': { - 'description': 'Successful Response', - 'content': { - 'application/json': { - 'schema': { - 'items': { - '$ref': '#/components/schemas/UserV2' - }, - 'type': 'array', - 'title': 'Response Get Users V2 Latest Users Get' - } - } + 'post': { + 'tags': [ + 'Users' + ], + 'summary': 'Create User V2', + 'operationId': 'create_user_v2_latest_users_post', + 'requestBody': { + 'content': { + 'application/json': { + 'schema': { + '$ref': '#/components/schemas/UserV2' } } - } + }, + 'required': True }, - 'post': { - 'tags': [ - 'Users' - ], - 'summary': 'Create User V2', - 'operationId': 'create_user_v2_latest_users_post', - 'requestBody': { + 'responses': { + '200': { + 'description': 'Successful Response', 'content': { 'application/json': { 'schema': { '$ref': '#/components/schemas/UserV2' } } - }, - 'required': True + } }, - 'responses': { - '200': { - 'description': 'Successful Response', - 'content': { - 'application/json': { - 'schema': { - '$ref': '#/components/schemas/UserV2' - } - } - } - }, - '422': { - 'description': 'Validation Error', - 'content': { - 'application/json': { - 'schema': { - '$ref': '#/components/schemas/HTTPValidationError' - } + '422': { + 'description': 'Validation Error', + 'content': { + 'application/json': { + 'schema': { + '$ref': '#/components/schemas/HTTPValidationError' } } } } } - }, - '/latest/users/{user_id}': { - 'get': { - 'tags': [ - 'Users' - ], - 'summary': 'Get User V2', - 'operationId': 'get_user_v2_latest_users__user_id__get', - 'parameters': [ - { - 'name': 'user_id', - 'in': 'path', - 'required': True, - 'schema': { - 'type': 'integer', - 'title': 'User Id' - } + } + }, + '/latest/users/{user_id}': { + 'get': { + 'tags': [ + 'Users' + ], + 'summary': 'Get User V2', + 'operationId': 'get_user_v2_latest_users__user_id__get', + 'parameters': [ + { + 'name': 'user_id', + 'in': 'path', + 'required': True, + 'schema': { + 'type': 'integer', + 'title': 'User Id' } - ], - 'responses': { - '200': { - 'description': 'Successful Response', - 'content': { - 'application/json': { - 'schema': { - '$ref': '#/components/schemas/UserV2' - } + } + ], + 'responses': { + '200': { + 'description': 'Successful Response', + 'content': { + 'application/json': { + 'schema': { + '$ref': '#/components/schemas/UserV2' } } - }, - '422': { - 'description': 'Validation Error', - 'content': { - 'application/json': { - 'schema': { - '$ref': '#/components/schemas/HTTPValidationError' - } + } + }, + '422': { + 'description': 'Validation Error', + 'content': { + 'application/json': { + 'schema': { + '$ref': '#/components/schemas/HTTPValidationError' } } } } } - }, - '/versions': { - 'get': { - 'tags': [ - 'Versions' - ], - 'summary': 'Get Versions', - 'operationId': 'get_versions_versions_get', - 'responses': { - '200': { - 'description': 'Successful Response', - 'content': { - 'application/json': { - 'schema': { - 'type': 'object', - 'title': 'Response Get Versions Versions Get' - } + } + }, + '/versions': { + 'get': { + 'tags': [ + 'Versions' + ], + 'summary': 'Get Versions', + 'operationId': 'get_versions_versions_get', + 'responses': { + '200': { + 'description': 'Successful Response', + 'content': { + 'application/json': { + 'schema': { + 'type': 'object', + 'title': 'Response Get Versions Versions Get' } } } } } } - }, - 'components': { - 'schemas': { - 'HTTPValidationError': { - 'properties': { - 'detail': { - 'items': { - '$ref': '#/components/schemas/ValidationError' - }, - 'type': 'array', - 'title': 'Detail' - } - }, - 'type': 'object', - 'title': 'HTTPValidationError' - }, - 'Item': { - 'properties': { - 'id': { - 'type': 'integer', - 'title': 'Id' + } + }, + 'components': { + 'schemas': { + 'HTTPValidationError': { + 'properties': { + 'detail': { + 'items': { + '$ref': '#/components/schemas/ValidationError' }, - 'name': { - 'type': 'string', - 'title': 'Name' - } + 'type': 'array', + 'title': 'Detail' + } + }, + 'type': 'object', + 'title': 'HTTPValidationError' + }, + 'Item': { + 'properties': { + 'id': { + 'type': 'integer', + 'title': 'Id' }, - 'type': 'object', - 'required': [ - 'id', - 'name' - ], - 'title': 'Item' + 'name': { + 'type': 'string', + 'title': 'Name' + } }, - 'ItemV2': { - 'properties': { - 'id': { - 'type': 'integer', - 'title': 'Id' - }, - 'name': { - 'type': 'string', - 'title': 'Name' - }, - 'cost': { - 'type': 'integer', - 'title': 'Cost' - } + 'type': 'object', + 'required': [ + 'id', + 'name' + ], + 'title': 'Item' + }, + 'ItemV2': { + 'properties': { + 'id': { + 'type': 'integer', + 'title': 'Id' }, - 'type': 'object', - 'required': [ - 'id', - 'name', - 'cost' - ], - 'title': 'ItemV2' + 'name': { + 'type': 'string', + 'title': 'Name' + }, + 'cost': { + 'type': 'integer', + 'title': 'Cost' + } }, - 'User': { - 'properties': { - 'id': { - 'type': 'integer', - 'title': 'Id' - }, - 'name': { - 'type': 'string', - 'title': 'Name' - } + 'type': 'object', + 'required': [ + 'id', + 'name', + 'cost' + ], + 'title': 'ItemV2' + }, + 'User': { + 'properties': { + 'id': { + 'type': 'integer', + 'title': 'Id' }, - 'type': 'object', - 'required': [ - 'id', - 'name' - ], - 'title': 'User' + 'name': { + 'type': 'string', + 'title': 'Name' + } }, - 'UserV2': { - 'properties': { - 'id': { - 'type': 'integer', - 'title': 'Id' - }, - 'name': { - 'type': 'string', - 'title': 'Name' - }, - 'age': { - 'type': 'integer', - 'title': 'Age' - } + 'type': 'object', + 'required': [ + 'id', + 'name' + ], + 'title': 'User' + }, + 'UserV2': { + 'properties': { + 'id': { + 'type': 'integer', + 'title': 'Id' }, - 'type': 'object', - 'required': [ - 'id', - 'name', - 'age' - ], - 'title': 'UserV2' + 'name': { + 'type': 'string', + 'title': 'Name' + }, + 'age': { + 'type': 'integer', + 'title': 'Age' + } }, - 'ValidationError': { - 'properties': { - 'loc': { - 'items': { - 'anyOf': [ - { - 'type': 'string' - }, - { - 'type': 'integer' - } - ] - }, - 'type': 'array', - 'title': 'Location' - }, - 'msg': { - 'type': 'string', - 'title': 'Message' + 'type': 'object', + 'required': [ + 'id', + 'name', + 'age' + ], + 'title': 'UserV2' + }, + 'ValidationError': { + 'properties': { + 'loc': { + 'items': { + 'anyOf': [ + { + 'type': 'string' + }, + { + 'type': 'integer' + } + ] }, - 'type': { - 'type': 'string', - 'title': 'Error Type' - } + 'type': 'array', + 'title': 'Location' }, - 'type': 'object', - 'required': [ - 'loc', - 'msg', - 'type' - ], - 'title': 'ValidationError' - } + 'msg': { + 'type': 'string', + 'title': 'Message' + }, + 'type': { + 'type': 'string', + 'title': 'Error Type' + } + }, + 'type': 'object', + 'required': [ + 'loc', + 'msg', + 'type' + ], + 'title': 'ValidationError' } } - }, + } + } + if pydantic.__version__ >= '2.11.0': + # added 'additionalProperties': True in the /versions API + expected_response['paths']['/versions']['get']['responses']['200'][ + 'content' + ]['application/json']['schema']['additionalProperties'] = True + # openapi + self.assertDictEqual( + expected_response, test_client.get('/api_schema.json').json() ) self.assertDictEqual( diff --git a/tests/test_websocket.py b/tests/test_websocket.py index 0213375..43094e7 100644 --- a/tests/test_websocket.py +++ b/tests/test_websocket.py @@ -1,3 +1,5 @@ +from typing import Any, Dict +import pydantic from fastapi import WebSocketDisconnect from fastapi.testclient import TestClient @@ -85,99 +87,97 @@ def test_simple_example(self) -> None: self.assertEqual(200, test_client.get('/latest/swagger').status_code) # openapi - self.assertDictEqual( - { - 'openapi': '3.1.0', - 'info': { - 'title': 'test', - 'description': 'Websocket example of FastAPI Versionizer.', - 'termsOfService': 'https://github.com/alexschimpf/fastapi-versionizer', - 'version': '0.1.0' - }, - 'paths': { - '/v1/chatterbox': { - 'get': { - 'tags': [ - 'Chatting' - ], - 'summary': 'Get Explaination', - 'operationId': 'get_explaination_v1_chatterbox_get', - 'responses': { - '200': { - 'description': 'Successful Response', - 'content': { - 'application/json': { - 'schema': { - 'type': 'string', - 'title': 'Response Get Explaination V1 Chatterbox Get' - } + expected_response: Dict[str, Any] = { + 'openapi': '3.1.0', + 'info': { + 'title': 'test', + 'description': 'Websocket example of FastAPI Versionizer.', + 'termsOfService': 'https://github.com/alexschimpf/fastapi-versionizer', + 'version': '0.1.0' + }, + 'paths': { + '/v1/chatterbox': { + 'get': { + 'tags': [ + 'Chatting' + ], + 'summary': 'Get Explaination', + 'operationId': 'get_explaination_v1_chatterbox_get', + 'responses': { + '200': { + 'description': 'Successful Response', + 'content': { + 'application/json': { + 'schema': { + 'type': 'string', + 'title': 'Response Get Explaination V1 Chatterbox Get' } } } - }, - 'deprecated': True - } - }, - '/v2/chatterbox': { - 'get': { - 'tags': [ - 'Chatting' - ], - 'summary': 'Get Explaination V2', - 'operationId': 'get_explaination_v2_v2_chatterbox_get', - 'responses': { - '200': { - 'description': 'Successful Response', - 'content': { - 'application/json': { - 'schema': { - 'type': 'string', - 'title': 'Response Get Explaination V2 V2 Chatterbox Get' - } + } + }, + 'deprecated': True + } + }, + '/v2/chatterbox': { + 'get': { + 'tags': [ + 'Chatting' + ], + 'summary': 'Get Explaination V2', + 'operationId': 'get_explaination_v2_v2_chatterbox_get', + 'responses': { + '200': { + 'description': 'Successful Response', + 'content': { + 'application/json': { + 'schema': { + 'type': 'string', + 'title': 'Response Get Explaination V2 V2 Chatterbox Get' } } } } } - }, - '/latest/chatterbox': { - 'get': { - 'tags': [ - 'Chatting' - ], - 'summary': 'Get Explaination V2', - 'operationId': 'get_explaination_v2_latest_chatterbox_get', - 'responses': { - '200': { - 'description': 'Successful Response', - 'content': { - 'application/json': { - 'schema': { - 'type': 'string', - 'title': 'Response Get Explaination V2 Latest Chatterbox Get' - } + } + }, + '/latest/chatterbox': { + 'get': { + 'tags': [ + 'Chatting' + ], + 'summary': 'Get Explaination V2', + 'operationId': 'get_explaination_v2_latest_chatterbox_get', + 'responses': { + '200': { + 'description': 'Successful Response', + 'content': { + 'application/json': { + 'schema': { + 'type': 'string', + 'title': 'Response Get Explaination V2 Latest Chatterbox Get' } } } } } - }, - '/versions': { - 'get': { - 'tags': [ - 'Versions' - ], - 'summary': 'Get Versions', - 'operationId': 'get_versions_versions_get', - 'responses': { - '200': { - 'description': 'Successful Response', - 'content': { - 'application/json': { - 'schema': { - 'type': 'object', - 'title': 'Response Get Versions Versions Get' - } + } + }, + '/versions': { + 'get': { + 'tags': [ + 'Versions' + ], + 'summary': 'Get Versions', + 'operationId': 'get_versions_versions_get', + 'responses': { + '200': { + 'description': 'Successful Response', + 'content': { + 'application/json': { + 'schema': { + 'type': 'object', + 'title': 'Response Get Versions Versions Get' } } } @@ -185,7 +185,15 @@ def test_simple_example(self) -> None: } } } - }, + } + } + if pydantic.__version__ >= '2.11.0': + # added 'additionalProperties': True in the /versions API + expected_response['paths']['/versions']['get']['responses']['200'][ + 'content' + ]['application/json']['schema']['additionalProperties'] = True + self.assertDictEqual( + expected_response, test_client.get('/api_schema.json').json() ) self.assertDictEqual( diff --git a/tests/test_with_root_path.py b/tests/test_with_root_path.py index 7613d50..3a440c3 100644 --- a/tests/test_with_root_path.py +++ b/tests/test_with_root_path.py @@ -1,3 +1,5 @@ +from typing import Any, Dict +import pydantic from fastapi.testclient import TestClient from unittest import TestCase @@ -44,101 +46,99 @@ def test_with_root_path_example(self) -> None: self.assertEqual(200, test_client.get('/latest/swagger').status_code) # openapi - self.assertDictEqual( - { - 'openapi': '3.1.0', - 'info': { - 'title': 'test', - 'version': '0.1.0' - }, - 'servers': [ - { - 'url': '/api' - } - ], - 'paths': { - '/v1/status': { - 'get': { - 'tags': [ - 'Status' - ], - 'summary': 'Get Status V1', - 'operationId': 'get_status_v1_v1_status_get', - 'responses': { - '200': { - 'description': 'Successful Response', - 'content': { - 'application/json': { - 'schema': { - 'type': 'string', - 'title': 'Response Get Status V1 V1 Status Get' - } + expected_response: Dict[str, Any] = { + 'openapi': '3.1.0', + 'info': { + 'title': 'test', + 'version': '0.1.0' + }, + 'servers': [ + { + 'url': '/api' + } + ], + 'paths': { + '/v1/status': { + 'get': { + 'tags': [ + 'Status' + ], + 'summary': 'Get Status V1', + 'operationId': 'get_status_v1_v1_status_get', + 'responses': { + '200': { + 'description': 'Successful Response', + 'content': { + 'application/json': { + 'schema': { + 'type': 'string', + 'title': 'Response Get Status V1 V1 Status Get' } } } } } - }, - '/v2/status': { - 'get': { - 'tags': [ - 'Status' - ], - 'summary': 'Get Status V2', - 'operationId': 'get_status_v2_v2_status_get', - 'responses': { - '200': { - 'description': 'Successful Response', - 'content': { - 'application/json': { - 'schema': { - 'type': 'string', - 'title': 'Response Get Status V2 V2 Status Get' - } + } + }, + '/v2/status': { + 'get': { + 'tags': [ + 'Status' + ], + 'summary': 'Get Status V2', + 'operationId': 'get_status_v2_v2_status_get', + 'responses': { + '200': { + 'description': 'Successful Response', + 'content': { + 'application/json': { + 'schema': { + 'type': 'string', + 'title': 'Response Get Status V2 V2 Status Get' } } } } } - }, - '/latest/status': { - 'get': { - 'tags': [ - 'Status' - ], - 'summary': 'Get Status V2', - 'operationId': 'get_status_v2_latest_status_get', - 'responses': { - '200': { - 'description': 'Successful Response', - 'content': { - 'application/json': { - 'schema': { - 'type': 'string', - 'title': 'Response Get Status V2 Latest Status Get' - } + } + }, + '/latest/status': { + 'get': { + 'tags': [ + 'Status' + ], + 'summary': 'Get Status V2', + 'operationId': 'get_status_v2_latest_status_get', + 'responses': { + '200': { + 'description': 'Successful Response', + 'content': { + 'application/json': { + 'schema': { + 'type': 'string', + 'title': 'Response Get Status V2 Latest Status Get' } } } } } - }, - '/versions': { - 'get': { - 'tags': [ - 'Versions' - ], - 'summary': 'Get Versions', - 'operationId': 'get_versions_versions_get', - 'responses': { - '200': { - 'description': 'Successful Response', - 'content': { - 'application/json': { - 'schema': { - 'type': 'object', - 'title': 'Response Get Versions Versions Get' - } + } + }, + '/versions': { + 'get': { + 'tags': [ + 'Versions' + ], + 'summary': 'Get Versions', + 'operationId': 'get_versions_versions_get', + 'responses': { + '200': { + 'description': 'Successful Response', + 'content': { + 'application/json': { + 'schema': { + 'type': 'object', + 'title': 'Response Get Versions Versions Get' } } } @@ -146,7 +146,15 @@ def test_with_root_path_example(self) -> None: } } } - }, + } + } + if pydantic.__version__ >= '2.11.0': + # added 'additionalProperties': True in the /versions API + expected_response['paths']['/versions']['get']['responses']['200'][ + 'content' + ]['application/json']['schema']['additionalProperties'] = True + self.assertDictEqual( + expected_response, test_client.get('/api/api_schema.json').json() ) self.assertDictEqual( diff --git a/tests/test_with_static_file_mount.py b/tests/test_with_static_file_mount.py index 859f0fa..d807cfb 100644 --- a/tests/test_with_static_file_mount.py +++ b/tests/test_with_static_file_mount.py @@ -17,5 +17,5 @@ def test_with_static_file_mount_example(self) -> None: self.assertEqual(expected, test_client.get('/examples/with_static_file_mount.py').text) # Check that a static mount before instantiating Versionizer will not work - self.assertEquals('{"detail":"Not Found"}', - test_client.get('/examples-not-working/with_static_file_mount.py').text) + self.assertEqual('{"detail":"Not Found"}', + test_client.get('/examples-not-working/with_static_file_mount.py').text)