diff --git a/.circleci/config.yml b/.circleci/config.yml index c8161e3eb..c7633b59d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -141,14 +141,6 @@ jobs: - run: name: Seed DB command: poetry run ./manage.py seeddb --n-users 3 --n-workspaces 1 --n-projects 1 --n-labels 1 --n-tasks 1 - backend_schema: - executor: python_and_postgres - working_directory: ~/projectify/backend - steps: - - prepare_poetry - - run: - name: Test if schema.yml up to date - command: poetry run bin/test-schema tools_reuse_lint: executor: python working_directory: ~/projectify/tools @@ -170,5 +162,4 @@ workflows: - backend_tests - backend_seeddb - backend_collectstatic - - backend_schema - tools_reuse_lint diff --git a/backend/bin/build-schema b/backend/bin/build-schema deleted file mode 100755 index 12f76281f..000000000 --- a/backend/bin/build-schema +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env sh -# SPDX-License-Identifier: AGPL-3.0-or-later -# SPDX-FileCopyrightText: 2024 JWP Consulting GK -# -# Build an up-to-date OpenAPI schema for the Projectify backend using -# drf-spectacular -set -e -./manage.py spectacular --file schema/schema.yml --fail-on-warn --validate diff --git a/backend/bin/test-schema b/backend/bin/test-schema deleted file mode 100755 index 7703e7a5c..000000000 --- a/backend/bin/test-schema +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env sh -# SPDX-License-Identifier: AGPL-3.0-or-later -# SPDX-FileCopyrightText: 2024 JWP Consulting GK -# -# Tests if schema/schema.yml is up to date -set -e - -if ! ./manage.py spectacular --file schema/schema.yml -then - echo "Could not output schema" - exit 1 -fi - -if git diff --exit-code schema/schema.yml -then - echo "Schema has not changed" -else - echo "Schema changed, check diff" - exit 1 -fi diff --git a/backend/poetry.lock b/backend/poetry.lock index d0c06231d..9f72fe89e 100644 --- a/backend/poetry.lock +++ b/backend/poetry.lock @@ -840,7 +840,7 @@ version = "3.15.2" description = "Web APIs for Django, made easy." optional = false python-versions = ">=3.8" -groups = ["main", "dev"] +groups = ["main"] files = [ {file = "djangorestframework-3.15.2-py3-none-any.whl", hash = "sha256:2b8871b062ba1aefc2de01f773875441a961fefbf79f5eed1e32b2f096944b20"}, {file = "djangorestframework-3.15.2.tar.gz", hash = "sha256:36fe88cd2d6c6bec23dca9804bab2ba5517a8bb9d8f47ebc68981b56840107ad"}, @@ -892,46 +892,6 @@ pyyaml = ">=6" regex = ">=2023" tqdm = ">=4.62.2" -[[package]] -name = "drf-spectacular" -version = "0.27.2" -description = "Sane and flexible OpenAPI 3 schema generation for Django REST framework" -optional = false -python-versions = ">=3.7" -groups = ["dev"] -files = [ - {file = "drf-spectacular-0.27.2.tar.gz", hash = "sha256:a199492f2163c4101055075ebdbb037d59c6e0030692fc83a1a8c0fc65929981"}, - {file = "drf_spectacular-0.27.2-py3-none-any.whl", hash = "sha256:b1c04bf8b2fbbeaf6f59414b4ea448c8787aba4d32f76055c3b13335cf7ec37b"}, -] - -[package.dependencies] -Django = ">=2.2" -djangorestframework = ">=3.10.3" -drf-spectacular-sidecar = {version = "*", optional = true, markers = "extra == \"sidecar\""} -inflection = ">=0.3.1" -jsonschema = ">=2.6.0" -PyYAML = ">=5.1" -uritemplate = ">=2.0.0" - -[package.extras] -offline = ["drf-spectacular-sidecar"] -sidecar = ["drf-spectacular-sidecar"] - -[[package]] -name = "drf-spectacular-sidecar" -version = "2024.4.1" -description = "Serve self-contained distribution builds of Swagger UI and Redoc with Django" -optional = false -python-versions = ">=3.6" -groups = ["dev"] -files = [ - {file = "drf-spectacular-sidecar-2024.4.1.tar.gz", hash = "sha256:68532dd094714f79c1775c00848f22c10f004826abc856442ff30c3bc9c40bb4"}, - {file = "drf_spectacular_sidecar-2024.4.1-py3-none-any.whl", hash = "sha256:8359befe69a8953fea86be01c1ff37038854a62546225551de16c47c07dccd4e"}, -] - -[package.dependencies] -Django = ">=2.2" - [[package]] name = "editorconfig" version = "0.12.4" @@ -1058,18 +1018,6 @@ perf = ["ipython"] test = ["flufl.flake8", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] type = ["mypy (<1.19) ; platform_python_implementation == \"PyPy\"", "pytest-mypy (>=1.0.1)"] -[[package]] -name = "inflection" -version = "0.5.1" -description = "A port of Ruby on Rails inflector to Python" -optional = false -python-versions = ">=3.5" -groups = ["dev"] -files = [ - {file = "inflection-0.5.1-py2.py3-none-any.whl", hash = "sha256:f38b2b640938a4f35ade69ac3d053042959b62a0f1076a5bbaa1b9526605a8a2"}, - {file = "inflection-0.5.1.tar.gz", hash = "sha256:1a29730d366e996aaacffb2f1f1cb9593dc38e2ddd30c91250c6dde09ea9b417"}, -] - [[package]] name = "iniconfig" version = "2.0.0" @@ -1168,43 +1116,6 @@ files = [ [package.extras] dev = ["build (==1.2.2.post1)", "coverage (==7.5.3)", "mypy (==1.13.0)", "pip (==24.3.1)", "pylint (==3.2.3)", "ruff (==0.7.3)", "twine (==5.1.1)", "uv (==0.5.1)"] -[[package]] -name = "jsonschema" -version = "4.21.1" -description = "An implementation of JSON Schema validation for Python" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "jsonschema-4.21.1-py3-none-any.whl", hash = "sha256:7996507afae316306f9e2290407761157c6f78002dcf7419acb99822143d1c6f"}, - {file = "jsonschema-4.21.1.tar.gz", hash = "sha256:85727c00279f5fa6bedbe6238d2aa6403bedd8b4864ab11207d07df3cc1b2ee5"}, -] - -[package.dependencies] -attrs = ">=22.2.0" -jsonschema-specifications = ">=2023.03.6" -referencing = ">=0.28.4" -rpds-py = ">=0.7.1" - -[package.extras] -format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] -format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] - -[[package]] -name = "jsonschema-specifications" -version = "2023.12.1" -description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "jsonschema_specifications-2023.12.1-py3-none-any.whl", hash = "sha256:87e4fdf3a94858b8a2ba2778d9ba57d8a9cafca7c7489c46ba0d30a8bc6a9c3c"}, - {file = "jsonschema_specifications-2023.12.1.tar.gz", hash = "sha256:48a76787b3e70f5ed53f1160d2b81f586e4ca6d1548c5de7085d1682674764cc"}, -] - -[package.dependencies] -referencing = ">=0.31.0" - [[package]] name = "kombu" version = "5.4.2" @@ -1981,7 +1892,7 @@ version = "6.0.2" description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.8" -groups = ["dev", "test"] +groups = ["test"] files = [ {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, @@ -2054,22 +1965,6 @@ files = [ hiredis = ["hiredis (>=1.0.0)"] ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)"] -[[package]] -name = "referencing" -version = "0.34.0" -description = "JSON Referencing + Python" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "referencing-0.34.0-py3-none-any.whl", hash = "sha256:d53ae300ceddd3169f1ffa9caf2cb7b769e92657e4fafb23d34b93679116dfd4"}, - {file = "referencing-0.34.0.tar.gz", hash = "sha256:5773bd84ef41799a5a8ca72dc34590c041eb01bf9aa02632b4a973fb0181a844"}, -] - -[package.dependencies] -attrs = ">=22.2.0" -rpds-py = ">=0.7.0" - [[package]] name = "regex" version = "2024.11.6" @@ -2196,115 +2091,6 @@ urllib3 = ">=1.21.1,<3" socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] -[[package]] -name = "rpds-py" -version = "0.18.0" -description = "Python bindings to Rust's persistent data structures (rpds)" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "rpds_py-0.18.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:5b4e7d8d6c9b2e8ee2d55c90b59c707ca59bc30058269b3db7b1f8df5763557e"}, - {file = "rpds_py-0.18.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c463ed05f9dfb9baebef68048aed8dcdc94411e4bf3d33a39ba97e271624f8f7"}, - {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01e36a39af54a30f28b73096dd39b6802eddd04c90dbe161c1b8dbe22353189f"}, - {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d62dec4976954a23d7f91f2f4530852b0c7608116c257833922a896101336c51"}, - {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dd18772815d5f008fa03d2b9a681ae38d5ae9f0e599f7dda233c439fcaa00d40"}, - {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:923d39efa3cfb7279a0327e337a7958bff00cc447fd07a25cddb0a1cc9a6d2da"}, - {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39514da80f971362f9267c600b6d459bfbbc549cffc2cef8e47474fddc9b45b1"}, - {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a34d557a42aa28bd5c48a023c570219ba2593bcbbb8dc1b98d8cf5d529ab1434"}, - {file = "rpds_py-0.18.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:93df1de2f7f7239dc9cc5a4a12408ee1598725036bd2dedadc14d94525192fc3"}, - {file = "rpds_py-0.18.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:34b18ba135c687f4dac449aa5157d36e2cbb7c03cbea4ddbd88604e076aa836e"}, - {file = "rpds_py-0.18.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c0b5dcf9193625afd8ecc92312d6ed78781c46ecbf39af9ad4681fc9f464af88"}, - {file = "rpds_py-0.18.0-cp310-none-win32.whl", hash = "sha256:c4325ff0442a12113a6379af66978c3fe562f846763287ef66bdc1d57925d337"}, - {file = "rpds_py-0.18.0-cp310-none-win_amd64.whl", hash = "sha256:7223a2a5fe0d217e60a60cdae28d6949140dde9c3bcc714063c5b463065e3d66"}, - {file = "rpds_py-0.18.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:3a96e0c6a41dcdba3a0a581bbf6c44bb863f27c541547fb4b9711fd8cf0ffad4"}, - {file = "rpds_py-0.18.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30f43887bbae0d49113cbaab729a112251a940e9b274536613097ab8b4899cf6"}, - {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fcb25daa9219b4cf3a0ab24b0eb9a5cc8949ed4dc72acb8fa16b7e1681aa3c58"}, - {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d68c93e381010662ab873fea609bf6c0f428b6d0bb00f2c6939782e0818d37bf"}, - {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b34b7aa8b261c1dbf7720b5d6f01f38243e9b9daf7e6b8bc1fd4657000062f2c"}, - {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2e6d75ab12b0bbab7215e5d40f1e5b738aa539598db27ef83b2ec46747df90e1"}, - {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b8612cd233543a3781bc659c731b9d607de65890085098986dfd573fc2befe5"}, - {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:aec493917dd45e3c69d00a8874e7cbed844efd935595ef78a0f25f14312e33c6"}, - {file = "rpds_py-0.18.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:661d25cbffaf8cc42e971dd570d87cb29a665f49f4abe1f9e76be9a5182c4688"}, - {file = "rpds_py-0.18.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1df3659d26f539ac74fb3b0c481cdf9d725386e3552c6fa2974f4d33d78e544b"}, - {file = "rpds_py-0.18.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a1ce3ba137ed54f83e56fb983a5859a27d43a40188ba798993812fed73c70836"}, - {file = "rpds_py-0.18.0-cp311-none-win32.whl", hash = "sha256:69e64831e22a6b377772e7fb337533c365085b31619005802a79242fee620bc1"}, - {file = "rpds_py-0.18.0-cp311-none-win_amd64.whl", hash = "sha256:998e33ad22dc7ec7e030b3df701c43630b5bc0d8fbc2267653577e3fec279afa"}, - {file = "rpds_py-0.18.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:7f2facbd386dd60cbbf1a794181e6aa0bd429bd78bfdf775436020172e2a23f0"}, - {file = "rpds_py-0.18.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1d9a5be316c15ffb2b3c405c4ff14448c36b4435be062a7f578ccd8b01f0c4d8"}, - {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd5bf1af8efe569654bbef5a3e0a56eca45f87cfcffab31dd8dde70da5982475"}, - {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5417558f6887e9b6b65b4527232553c139b57ec42c64570569b155262ac0754f"}, - {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:56a737287efecafc16f6d067c2ea0117abadcd078d58721f967952db329a3e5c"}, - {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8f03bccbd8586e9dd37219bce4d4e0d3ab492e6b3b533e973fa08a112cb2ffc9"}, - {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4457a94da0d5c53dc4b3e4de1158bdab077db23c53232f37a3cb7afdb053a4e3"}, - {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0ab39c1ba9023914297dd88ec3b3b3c3f33671baeb6acf82ad7ce883f6e8e157"}, - {file = "rpds_py-0.18.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9d54553c1136b50fd12cc17e5b11ad07374c316df307e4cfd6441bea5fb68496"}, - {file = "rpds_py-0.18.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0af039631b6de0397ab2ba16eaf2872e9f8fca391b44d3d8cac317860a700a3f"}, - {file = "rpds_py-0.18.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:84ffab12db93b5f6bad84c712c92060a2d321b35c3c9960b43d08d0f639d60d7"}, - {file = "rpds_py-0.18.0-cp312-none-win32.whl", hash = "sha256:685537e07897f173abcf67258bee3c05c374fa6fff89d4c7e42fb391b0605e98"}, - {file = "rpds_py-0.18.0-cp312-none-win_amd64.whl", hash = "sha256:e003b002ec72c8d5a3e3da2989c7d6065b47d9eaa70cd8808b5384fbb970f4ec"}, - {file = "rpds_py-0.18.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:08f9ad53c3f31dfb4baa00da22f1e862900f45908383c062c27628754af2e88e"}, - {file = "rpds_py-0.18.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c0013fe6b46aa496a6749c77e00a3eb07952832ad6166bd481c74bda0dcb6d58"}, - {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e32a92116d4f2a80b629778280103d2a510a5b3f6314ceccd6e38006b5e92dcb"}, - {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e541ec6f2ec456934fd279a3120f856cd0aedd209fc3852eca563f81738f6861"}, - {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bed88b9a458e354014d662d47e7a5baafd7ff81c780fd91584a10d6ec842cb73"}, - {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2644e47de560eb7bd55c20fc59f6daa04682655c58d08185a9b95c1970fa1e07"}, - {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e8916ae4c720529e18afa0b879473049e95949bf97042e938530e072fde061d"}, - {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:465a3eb5659338cf2a9243e50ad9b2296fa15061736d6e26240e713522b6235c"}, - {file = "rpds_py-0.18.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:ea7d4a99f3b38c37eac212dbd6ec42b7a5ec51e2c74b5d3223e43c811609e65f"}, - {file = "rpds_py-0.18.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:67071a6171e92b6da534b8ae326505f7c18022c6f19072a81dcf40db2638767c"}, - {file = "rpds_py-0.18.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:41ef53e7c58aa4ef281da975f62c258950f54b76ec8e45941e93a3d1d8580594"}, - {file = "rpds_py-0.18.0-cp38-none-win32.whl", hash = "sha256:fdea4952db2793c4ad0bdccd27c1d8fdd1423a92f04598bc39425bcc2b8ee46e"}, - {file = "rpds_py-0.18.0-cp38-none-win_amd64.whl", hash = "sha256:7cd863afe7336c62ec78d7d1349a2f34c007a3cc6c2369d667c65aeec412a5b1"}, - {file = "rpds_py-0.18.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:5307def11a35f5ae4581a0b658b0af8178c65c530e94893345bebf41cc139d33"}, - {file = "rpds_py-0.18.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:77f195baa60a54ef9d2de16fbbfd3ff8b04edc0c0140a761b56c267ac11aa467"}, - {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39f5441553f1c2aed4de4377178ad8ff8f9d733723d6c66d983d75341de265ab"}, - {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9a00312dea9310d4cb7dbd7787e722d2e86a95c2db92fbd7d0155f97127bcb40"}, - {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f2fc11e8fe034ee3c34d316d0ad8808f45bc3b9ce5857ff29d513f3ff2923a1"}, - {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:586f8204935b9ec884500498ccc91aa869fc652c40c093bd9e1471fbcc25c022"}, - {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ddc2f4dfd396c7bfa18e6ce371cba60e4cf9d2e5cdb71376aa2da264605b60b9"}, - {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5ddcba87675b6d509139d1b521e0c8250e967e63b5909a7e8f8944d0f90ff36f"}, - {file = "rpds_py-0.18.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7bd339195d84439cbe5771546fe8a4e8a7a045417d8f9de9a368c434e42a721e"}, - {file = "rpds_py-0.18.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:d7c36232a90d4755b720fbd76739d8891732b18cf240a9c645d75f00639a9024"}, - {file = "rpds_py-0.18.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6b0817e34942b2ca527b0e9298373e7cc75f429e8da2055607f4931fded23e20"}, - {file = "rpds_py-0.18.0-cp39-none-win32.whl", hash = "sha256:99f70b740dc04d09e6b2699b675874367885217a2e9f782bdf5395632ac663b7"}, - {file = "rpds_py-0.18.0-cp39-none-win_amd64.whl", hash = "sha256:6ef687afab047554a2d366e112dd187b62d261d49eb79b77e386f94644363294"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ad36cfb355e24f1bd37cac88c112cd7730873f20fb0bdaf8ba59eedf8216079f"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:36b3ee798c58ace201289024b52788161e1ea133e4ac93fba7d49da5fec0ef9e"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8a2f084546cc59ea99fda8e070be2fd140c3092dc11524a71aa8f0f3d5a55ca"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e4461d0f003a0aa9be2bdd1b798a041f177189c1a0f7619fe8c95ad08d9a45d7"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8db715ebe3bb7d86d77ac1826f7d67ec11a70dbd2376b7cc214199360517b641"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:793968759cd0d96cac1e367afd70c235867831983f876a53389ad869b043c948"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66e6a3af5a75363d2c9a48b07cb27c4ea542938b1a2e93b15a503cdfa8490795"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6ef0befbb5d79cf32d0266f5cff01545602344eda89480e1dd88aca964260b18"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:1d4acf42190d449d5e89654d5c1ed3a4f17925eec71f05e2a41414689cda02d1"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:a5f446dd5055667aabaee78487f2b5ab72e244f9bc0b2ffebfeec79051679984"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:9dbbeb27f4e70bfd9eec1be5477517365afe05a9b2c441a0b21929ee61048124"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:22806714311a69fd0af9b35b7be97c18a0fc2826e6827dbb3a8c94eac6cf7eeb"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:b34ae4636dfc4e76a438ab826a0d1eed2589ca7d9a1b2d5bb546978ac6485461"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c8370641f1a7f0e0669ddccca22f1da893cef7628396431eb445d46d893e5cd"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c8362467a0fdeccd47935f22c256bec5e6abe543bf0d66e3d3d57a8fb5731863"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:11a8c85ef4a07a7638180bf04fe189d12757c696eb41f310d2426895356dcf05"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b316144e85316da2723f9d8dc75bada12fa58489a527091fa1d5a612643d1a0e"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf1ea2e34868f6fbf070e1af291c8180480310173de0b0c43fc38a02929fc0e3"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e546e768d08ad55b20b11dbb78a745151acbd938f8f00d0cfbabe8b0199b9880"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:4901165d170a5fde6f589acb90a6b33629ad1ec976d4529e769c6f3d885e3e80"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-musllinux_1_2_i686.whl", hash = "sha256:618a3d6cae6ef8ec88bb76dd80b83cfe415ad4f1d942ca2a903bf6b6ff97a2da"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:ed4eb745efbff0a8e9587d22a84be94a5eb7d2d99c02dacf7bd0911713ed14dd"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:6c81e5f372cd0dc5dc4809553d34f832f60a46034a5f187756d9b90586c2c307"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:43fbac5f22e25bee1d482c97474f930a353542855f05c1161fd804c9dc74a09d"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d7faa6f14017c0b1e69f5e2c357b998731ea75a442ab3841c0dbbbfe902d2c4"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:08231ac30a842bd04daabc4d71fddd7e6d26189406d5a69535638e4dcb88fe76"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:044a3e61a7c2dafacae99d1e722cc2d4c05280790ec5a05031b3876809d89a5c"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3f26b5bd1079acdb0c7a5645e350fe54d16b17bfc5e71f371c449383d3342e17"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:482103aed1dfe2f3b71a58eff35ba105289b8d862551ea576bd15479aba01f66"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1374f4129f9bcca53a1bba0bb86bf78325a0374577cf7e9e4cd046b1e6f20e24"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:635dc434ff724b178cb192c70016cc0ad25a275228f749ee0daf0eddbc8183b1"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:bc362ee4e314870a70f4ae88772d72d877246537d9f8cb8f7eacf10884862432"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:4832d7d380477521a8c1644bbab6588dfedea5e30a7d967b5fb75977c45fd77f"}, - {file = "rpds_py-0.18.0.tar.gz", hash = "sha256:42821446ee7a76f5d9f71f9e33a4fb2ffd724bb3e7f93386150b61a43115788d"}, -] - [[package]] name = "ruff" version = "0.6.1" @@ -2725,18 +2511,6 @@ files = [ ] markers = {dev = "sys_platform == \"win32\""} -[[package]] -name = "uritemplate" -version = "4.1.1" -description = "Implementation of RFC 6570 URI Templates" -optional = false -python-versions = ">=3.6" -groups = ["dev"] -files = [ - {file = "uritemplate-4.1.1-py2.py3-none-any.whl", hash = "sha256:830c08b8d99bdd312ea4ead05994a38e8936266f84b9a7878232db50b044e02e"}, - {file = "uritemplate-4.1.1.tar.gz", hash = "sha256:4346edfc5c3b79f694bccd6d6099a322bbeb628dbf2cd86eea55a456ce5124f0"}, -] - [[package]] name = "urllib3" version = "2.6.3" @@ -2899,4 +2673,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.1" python-versions = "~3.12.7" -content-hash = "fa8c2452391b84212b3886ed714c6dd98d012c14af83f7ede3b01eea2525b872" +content-hash = "5e7370525f41a950e7ee1d3624ca7f515ec88762417d64c1494bda492665809c" diff --git a/backend/projectify/corporate/views/coupon.py b/backend/projectify/corporate/views/coupon.py index 5669d1a35..b57c40f00 100644 --- a/backend/projectify/corporate/views/coupon.py +++ b/backend/projectify/corporate/views/coupon.py @@ -13,8 +13,6 @@ from rest_framework.views import APIView from projectify.corporate.services.coupon import coupon_redeem -from projectify.lib.error_schema import DeriveSchema -from projectify.lib.schema import extend_schema from projectify.workspace.selectors.workspace import ( workspace_find_by_workspace_uuid, ) @@ -28,13 +26,6 @@ class CouponRedeemSerializer(serializers.Serializer): code = serializers.CharField() - @extend_schema( - request=CouponRedeemSerializer, - responses={ - 204: None, - 400: DeriveSchema, - }, - ) def post(self, request: Request, workspace_uuid: UUID) -> Response: """Handle POST.""" workspace = workspace_find_by_workspace_uuid( diff --git a/backend/projectify/corporate/views/customer.py b/backend/projectify/corporate/views/customer.py index d1767dca0..f97d17e8e 100644 --- a/backend/projectify/corporate/views/customer.py +++ b/backend/projectify/corporate/views/customer.py @@ -14,9 +14,6 @@ from rest_framework.status import HTTP_200_OK from rest_framework.views import APIView -from projectify.lib.error_schema import DeriveSchema -from projectify.lib.schema import extend_schema - from ..selectors.customer import customer_find_by_workspace_uuid from ..serializers import CustomerSerializer from ..services.customer import ( @@ -30,9 +27,6 @@ class WorkspaceCustomerRetrieve(APIView): """Retrieve customer for a workspace.""" - @extend_schema( - responses={200: CustomerSerializer}, - ) def get(self, request: Request, workspace_uuid: UUID) -> Response: """Handle GET.""" user = request.user @@ -68,13 +62,6 @@ class WorkspaceCheckoutSessionCreateOutputSerializer( # was only needed if a stripe url is constructed client side url = serializers.URLField() - @extend_schema( - request=WorkspaceCheckoutSessionCreateInputSerializer, - responses={ - 200: WorkspaceCheckoutSessionCreateOutputSerializer, - 400: DeriveSchema, - }, - ) def post(self, request: Request, workspace_uuid: UUID) -> Response: """Handle POST.""" customer = customer_find_by_workspace_uuid( @@ -112,10 +99,6 @@ class WorkspaceBillingPortalSessionCreateOutputSerializer( url = serializers.URLField() - @extend_schema( - request=None, - responses={200: WorkspaceBillingPortalSessionCreateOutputSerializer}, - ) def post(self, request: Request, workspace_uuid: UUID) -> Response: """Handle POST.""" customer = customer_find_by_workspace_uuid( diff --git a/backend/projectify/corporate/views/stripe.py b/backend/projectify/corporate/views/stripe.py index 1b7118fbd..1dd34ec85 100644 --- a/backend/projectify/corporate/views/stripe.py +++ b/backend/projectify/corporate/views/stripe.py @@ -23,7 +23,6 @@ HTTP_500_INTERNAL_SERVER_ERROR, ) -from projectify.lib.schema import extend_schema from projectify.lib.settings import get_settings from ..lib.stripe import stripe_client @@ -317,11 +316,6 @@ def _handle_event( return "ok" -@extend_schema( - # TODO request schema - request=None, - responses={200: None, 400: None}, -) @csrf_exempt @api_view(["POST"]) @permission_classes([AllowAny]) diff --git a/backend/projectify/lib/error_schema.py b/backend/projectify/lib/error_schema.py deleted file mode 100644 index 7be70cb4c..000000000 --- a/backend/projectify/lib/error_schema.py +++ /dev/null @@ -1,341 +0,0 @@ -# SPDX-License-Identifier: AGPL-3.0-or-later -# -# SPDX-FileCopyrightText: 2024 JWP Consulting GK -"""Functionality for building error schemas.""" - -import inspect -import logging -from collections.abc import Callable, Sequence -from typing import Any, Literal, Optional, Union - -from rest_framework import fields, permissions, serializers, status -from rest_framework.views import APIView - -from projectify.lib.exception_handler import ( - ForbiddenSerializer, - InternalServerErrorSerializer, - NotFoundSerializer, - TooManyRequestsSerializer, -) -from projectify.lib.schema import OpenApiResponse, _SchemaType - -logger = logging.getLogger(__name__) - -SerializerField = Union[ - serializers.ListSerializer[Any], - serializers.ListField, - serializers.Field, - serializers.Serializer, -] - - -# The following two functions are vendored in from drf_spectacular/plumbing.py -# build_array_type -# build_object_type -# Copyright © 2011-present, Encode OSS Ltd. -# Copyright © 2019-2021, T. Franzel , Cashlink Technologies GmbH. -# Copyright © 2021-present, T. Franzel . -# -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, this -# list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# * Neither the name of the copyright holder nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -def build_array_type(schema: _SchemaType) -> _SchemaType: - """Return OpenAPI array type.""" - return {"type": "array", "items": schema} - - -def build_object_type( - properties: Optional[_SchemaType] = None, - required: Optional[list[str]] = None, - description: Optional[str] = None, - **kwargs: Any, -) -> _SchemaType: - """Return OpenAPI object type.""" - schema: _SchemaType = {"type": "object"} - if description: - schema["description"] = description.strip() - if properties: - schema["properties"] = properties - if "additionalProperties" in kwargs: - schema["additionalProperties"] = kwargs.pop("additionalProperties") - if required: - schema["required"] = sorted(required) - schema.update(kwargs) - return schema - - -def field_to_type(field: SerializerField) -> _SchemaType: - """Map a serializer field to an OpenApi schema type.""" - match field: - case serializers.ListSerializer() as instance: - child_serializer = instance.child - if child_serializer is None: - raise ValueError( - f".child for ListSerializer {instance} was None" - ) - - schema = make_schema_nested(child_serializer) - return build_array_type(schema) - case serializers.Serializer() as instance: - many = getattr(instance, "many", False) - schema = make_schema_nested(instance) - if many: - return build_array_type(schema) - else: - return schema - case serializers.ListField() as instance: - child_field = instance.child - if child_field is None: - raise ValueError(f".child for ListField {instance} was None") - schema = field_to_type(child_field) - return build_array_type(schema) - case serializers.Field() as instance: - return {"type": "string"} - - -def make_schema_nested(instance: serializers.Serializer) -> _SchemaType: - """Derive a schema for a serializer instance.""" - fields: dict[str, _SchemaType] = { - name: field_to_type(field) - for name, field in instance.get_fields().items() - } - instance_doc = instance.__class__.__name__ - description = f"Errors for {instance_doc}" - result = build_object_type(properties=fields, description=description) - return result - - -def make_schema(instance: serializers.Serializer) -> _SchemaType: - """Derive a schema for a serializer instance.""" - schema = build_object_type( - description="Error schema", - properties={ - "code": {"type": "integer", "enum": [400]}, - "details": make_schema_nested(instance), - "general": {"type": "string"}, - "status": {"type": "string", "enum": ["invalid"]}, - }, - required=[ - "code", - "details", - "status", - ], - ) - return schema - - -def derive_bad_request_serializer( - serializer: Union[type[serializers.Serializer], serializers.Serializer], -) -> OpenApiResponse: - """Derive an error serializer.""" - if isinstance(serializer, serializers.Serializer): - instance = serializer - else: - instance = serializer() - schema = make_schema(instance) - response = OpenApiResponse(response=schema) - return response - - -# More methods than you ever asked for -# https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods -Method = Literal[ - "GET", - "HEAD", - "POST", - "PUT", - "DELETE", - "CONNECT", - "OPTIONS", - "TRACE", - "PATCH", -] -# Has to extend something like this: Callable[..., HttpResponse] -# and add "cls" attribute -SchemaAnnotatedCallable = Any -# path, path_regex, method, callback -Endpoints = Sequence[tuple[str, str, Method, SchemaAnnotatedCallable]] -RequestAnnotation = Union[ - None, - serializers.Serializer, - type[serializers.Serializer], - fields.empty, -] -ResponseAnnotation = Union[ - None, - OpenApiResponse, - serializers.Serializer, - type[serializers.Serializer], - fields.empty, - dict[int, "ResponseAnnotation"], -] -ResponsesDict = dict[int, "ResponseAnnotation"] - -SerializerClassOrInstance = Union[ - type[serializers.Serializer], serializers.Serializer -] - - -DeriveSchema = object() - - -def get_request_serializer( - annotation: RequestAnnotation, -) -> Optional[SerializerClassOrInstance]: - """Extract a Serializer instance or constructor from request annot.""" - match annotation: - case None | fields.empty: - return None - case type() | serializers.Serializer() as ser_inst: - return ser_inst - case _: - raise ValueError(f"Don't know what to do with {annotation}") - return None - - -def maybe_annotate_400( - path: str, d: ResponsesDict, s: Optional[SerializerClassOrInstance] -) -> None: - """Annotate 404.""" - if d.get(status.HTTP_400_BAD_REQUEST) is not DeriveSchema: - return - if s is None: - raise ValueError( - f"Trying to create 400 response schema, but no " - f"request serializer was provided in {path}" - ) - d[status.HTTP_400_BAD_REQUEST] = derive_bad_request_serializer(s) - - -def maybe_annotate_403(v: type[APIView], d: ResponsesDict) -> None: - """Annotate 403 for methods without AllowAny.""" - match v.permission_classes: - case (permissions.AllowAny,): - return - case (permissions.IsAuthenticated,): - pass - case _: - raise ValueError( - f"Permission classes in {v} {v.permission_classes} not supported" - ) - if status.HTTP_403_FORBIDDEN in d: - raise ValueError(f"403 has already been annotated in {v}") - d[status.HTTP_403_FORBIDDEN] = ForbiddenSerializer - - -ViewMethod = Callable[..., Any] - - -def maybe_annotate_404(method: ViewMethod, d: ResponsesDict) -> None: - """Annotate 404.""" - if not has_view_args(method): - return - if status.HTTP_404_NOT_FOUND in d: - raise ValueError(f"404 serializer was already specified in {method}") - d[status.HTTP_404_NOT_FOUND] = NotFoundSerializer - - -def maybe_annotate_429(d: ResponsesDict) -> None: - """Annotate 429.""" - if d.get(status.HTTP_429_TOO_MANY_REQUESTS) is not DeriveSchema: - return - d[status.HTTP_429_TOO_MANY_REQUESTS] = TooManyRequestsSerializer - - -def annotate_500(d: ResponsesDict) -> None: - """Annotate 500.""" - if status.HTTP_500_INTERNAL_SERVER_ERROR in d: - raise ValueError("Must not specify 500 serializer") - d[status.HTTP_500_INTERNAL_SERVER_ERROR] = InternalServerErrorSerializer - - -def has_view_args(c: ViewMethod) -> bool: - """Return True if a view function takes UUID or other args.""" - sig = inspect.signature(c) - params = list(sig.parameters.values()) - match params: - # APIView with at least one extra arg - case [ - inspect.Parameter(name="self"), - inspect.Parameter(name="request"), - _, - *_, - ]: - return True - # @api_view is not supported well, since rest_framework decorators - # don't anntoate __wrapped__ - case _: - return False - - -def preprocess_derive_error_schemas(endpoints: Endpoints) -> Endpoints: - """Process drf-spectactular schema and add missing HTTP error schemas.""" - method_names = ["GET", "POST", "PUT", "DELETE"] - # These are the functions we want to annotate - # https://github.com/tfranzel/drf-spectacular/blob/b1a34b05230316ca6c6d6724f2b9bb970a8dbe79/projectify.lib.schema.py#L549 - endpoints_edit = ( - (path, path_regex, method_name, callback) - for path, path_regex, method_name, callback in endpoints - if method_name in method_names - and callable(callback) - and hasattr(callback, "cls") - ) - for _path, _, method_name, callback in endpoints_edit: - view_class = getattr(callback, "cls", None) - if view_class is None: - continue - if not issubclass(view_class, APIView): - continue - method = getattr(view_class, method_name.lower()) - schema = method.kwargs["schema"] - # XXX HAXXX - # Unfortunately we can't just call get_response_serializers. It expects - # a complicated dance with a view and request instance, and will - # blow up. Instead, we just peek inside and find what extend_schema - # has been called with - request_nonlocals, _, _, _ = inspect.getclosurevars( - schema.get_request_serializer - ) - responses_nonlocals, _, _, _ = inspect.getclosurevars( - schema.get_response_serializers - ) - request: RequestAnnotation = request_nonlocals.get("request", None) - responses: ResponseAnnotation = responses_nonlocals.get( - "responses", None - ) - match responses: - case dict() as responses_dict: - pass - case _: - continue - request_serializer = get_request_serializer(request) - maybe_annotate_400(_path, responses_dict, request_serializer) - maybe_annotate_403(view_class, responses_dict) - maybe_annotate_404(method, responses_dict) - maybe_annotate_429(responses_dict) - annotate_500(responses_dict) - - return endpoints diff --git a/backend/projectify/lib/schema.py b/backend/projectify/lib/schema.py deleted file mode 100644 index e6813a46c..000000000 --- a/backend/projectify/lib/schema.py +++ /dev/null @@ -1,61 +0,0 @@ -# SPDX-License-Identifier: AGPL-3.0-or-later -# -# SPDX-FileCopyrightText: 2024 JWP Consulting GK -""" -Override spectacular's schema utils. - -Allows us to run production code without needing for spectacular to be -present. -""" - -from typing import Any, Callable, Dict, TypeVar - -from rest_framework.fields import empty - -F = TypeVar("F", bound=Callable[..., Any]) - - -def extend_schema( - request: Any = empty, responses: Any = empty -) -> Callable[[F], F]: - """Lazily load extend_schema.""" - try: - from drf_spectacular.utils import extend_schema as _extend_schema - except ImportError: - return lambda x: x - return _extend_schema(request=request, responses=responses) - - -_SchemaType = Dict[str, Any] - - -try: - from drf_spectacular.utils import ( - OpenApiResponse, - PolymorphicProxySerializer, - ) -except ModuleNotFoundError: - - class OpenApiResponse: # type: ignore[no-redef] - """Dummy OpenApiResponse.""" - - def __init__(self, *args: Any, **kwargs: Any) -> None: - """Do nothing useful.""" - del args - del kwargs - - class PolymorphicProxySerializer: # type: ignore[no-redef] - """Dummy PolymorphicProxySerializer.""" - - def __init__(self, *args: Any, **kwargs: Any) -> None: - """Do nothing useful.""" - del args - del kwargs - - -__all__ = ( - "extend_schema", - "OpenApiResponse", - "PolymorphicProxySerializer", - "_SchemaType", -) diff --git a/backend/projectify/lib/tests/test_error_schema.py b/backend/projectify/lib/tests/test_error_schema.py deleted file mode 100644 index 049e4b1fe..000000000 --- a/backend/projectify/lib/tests/test_error_schema.py +++ /dev/null @@ -1,113 +0,0 @@ -# SPDX-License-Identifier: AGPL-3.0-or-later -# -# SPDX-FileCopyrightText: 2024 JWP Consulting GK -"""Test error schema generation.""" - -import pytest -from rest_framework import serializers -from rest_framework.serializers import Serializer - -from projectify.lib.error_schema import make_schema - - -@pytest.fixture -def simple_serializer() -> Serializer: - """Generate a simple serializer.""" - - class SimpleSerializer(Serializer): - """A simple serializer.""" - - text = serializers.CharField() - number = serializers.IntegerField() - - return SimpleSerializer() - - -@pytest.fixture -def complex_serializer() -> Serializer: - """Generate a complex serializer.""" - - class NestedSerializer(Serializer): - """Serializer for a nested field.""" - - text = serializers.CharField() - - class ComplexSerializer(Serializer): - """A complex serializer with many kinds of fields.""" - - nested_plural = NestedSerializer(many=True) - nested_singular = NestedSerializer() - text_list = serializers.ListField(child=serializers.CharField()) - dict_field = serializers.DictField() - text = serializers.CharField() - - return ComplexSerializer() - - -def test_make_schema_simple(simple_serializer: Serializer) -> None: - """Test make_schema with simple serializer.""" - assert make_schema(simple_serializer) == { - "type": "object", - "description": "Error schema", - "properties": { - "details": { - "type": "object", - "description": "Errors for SimpleSerializer", - "properties": { - "text": {"type": "string"}, - "number": {"type": "string"}, - }, - }, - "status": {"type": "string", "enum": ["invalid"]}, - "code": {"type": "integer", "enum": [400]}, - "general": {"type": "string"}, - }, - "required": [ - "code", - "details", - "status", - ], - } - - -def test_make_schema_complex(complex_serializer: Serializer) -> None: - """Test make_schema with complex serializer.""" - assert make_schema(complex_serializer) == { - "type": "object", - "description": "Error schema", - "properties": { - "details": { - "type": "object", - "description": "Errors for ComplexSerializer", - "properties": { - "nested_plural": { - "type": "array", - "items": { - "type": "object", - "description": "Errors for NestedSerializer", - "properties": {"text": {"type": "string"}}, - }, - }, - "nested_singular": { - "type": "object", - "description": "Errors for NestedSerializer", - "properties": {"text": {"type": "string"}}, - }, - "text_list": { - "type": "array", - "items": {"type": "string"}, - }, - "dict_field": {"type": "string"}, - "text": {"type": "string"}, - }, - }, - "status": {"type": "string", "enum": ["invalid"]}, - "code": {"type": "integer", "enum": [400]}, - "general": {"type": "string"}, - }, - "required": [ - "code", - "details", - "status", - ], - } diff --git a/backend/projectify/settings/development.py b/backend/projectify/settings/development.py index 1d73345ed..0e3328052 100644 --- a/backend/projectify/settings/development.py +++ b/backend/projectify/settings/development.py @@ -16,7 +16,6 @@ ) from e from .base import Base -from .spectacular import SpectacularSettings def add_dev_middleware( @@ -45,7 +44,7 @@ def environ_get_or_warn(key: str) -> Optional[str]: return None -class Development(SpectacularSettings, Base): +class Development(Base): """Development configuration.""" SITE_TITLE = "Local Development" @@ -57,8 +56,6 @@ class Development(SpectacularSettings, Base): # Needs to be there before django.contrib.staticfiles "daphne", *Base.INSTALLED_APPS, - "drf_spectacular", - "drf_spectacular_sidecar", "django_browser_reload", "django_extensions", ) @@ -138,12 +135,6 @@ class Development(SpectacularSettings, Base): # TODO remove after Svelte frontend is gone SESSION_COOKIE_SAMESITE = "Lax" - # Rest Framework settings for drf-spectacular - REST_FRAMEWORK = { - **Base.REST_FRAMEWORK, - "DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema", - } - # Admins for local logging ADMINS = [["Local user", "user@localhost"]] diff --git a/backend/projectify/settings/spectacular.py b/backend/projectify/settings/spectacular.py deleted file mode 100644 index 57ed96c1a..000000000 --- a/backend/projectify/settings/spectacular.py +++ /dev/null @@ -1,26 +0,0 @@ -# SPDX-License-Identifier: AGPL-3.0-or-later -# -# SPDX-FileCopyrightText: 2024 JWP Consulting GK -"""DRF-Spectacular settings.""" - - -class SpectacularSettings: - """Settings mixin used for drf-spectacular.""" - - # drf-spectacular - # from - # https://drf-spectacular.readthedocs.io/en/latest/readme.html#installation - SPECTACULAR_SETTINGS = { - "TITLE": "Projectify backend API", - "DESCRIPTION": "API for the Projectify project management software", - "VERSION": "1.0.0", - "SERVE_INCLUDE_SCHEMA": False, - "SWAGGER_UI_DIST": "SIDECAR", # shorthand to use the sidecar instead - "SWAGGER_UI_FAVICON_HREF": "SIDECAR", - "REDOC_DIST": "SIDECAR", - "PREPROCESSING_HOOKS": ( - "projectify.lib.error_schema.preprocess_derive_error_schemas", - ), - } - - SERVE_SPECTACULAR = True diff --git a/backend/projectify/settings/test.py b/backend/projectify/settings/test.py index 5e5180430..17d54ccd4 100644 --- a/backend/projectify/settings/test.py +++ b/backend/projectify/settings/test.py @@ -11,17 +11,11 @@ ) from e from .base import Base -from .spectacular import SpectacularSettings -class Test(SpectacularSettings, Base): +class Test(Base): """Test configuration.""" - INSTALLED_APPS = ( - *Base.INSTALLED_APPS, - "drf_spectacular", - ) - SITE_TITLE = "Projectify Pytest" # TODO populate me @@ -39,12 +33,6 @@ class Test(SpectacularSettings, Base): # django-ratelimit RATELIMIT_ENABLE = False - # Rest Framework settings for drf-spectacular - REST_FRAMEWORK = { - **Base.REST_FRAMEWORK, - "DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema", - } - # Enable premail preview for testing PREMAIL_PREVIEW = True diff --git a/backend/projectify/urls.py b/backend/projectify/urls.py index 875c33b3d..54c7c79a6 100644 --- a/backend/projectify/urls.py +++ b/backend/projectify/urls.py @@ -112,33 +112,5 @@ path("test/", include("projectify.user.testing_urls")), ] -if settings.SERVE_SPECTACULAR: - try: - from drf_spectacular.views import ( - SpectacularAPIView, - SpectacularRedocView, - SpectacularSwaggerView, - ) - except ImportError as e: - raise RuntimeError( - "drf_spectacular was not found. Did you enable SERVE_SPECTACULAR " - "while running in production?" - ) from e - - urlpatterns = ( - *urlpatterns, - path("api/schema/", SpectacularAPIView.as_view(), name="schema"), - path( - "api/schema/swagger-ui/", - SpectacularSwaggerView.as_view(url_name="schema"), - name="swagger-ui", - ), - path( - "api/schema/redoc/", - SpectacularRedocView.as_view(url_name="schema"), - name="redoc", - ), - ) - __all__ = ("handler404", "handler500", "handler403") diff --git a/backend/projectify/user/views/auth.py b/backend/projectify/user/views/auth.py index 1298e8b15..5743168ca 100644 --- a/backend/projectify/user/views/auth.py +++ b/backend/projectify/user/views/auth.py @@ -25,9 +25,7 @@ from rest_framework.response import Response from rest_framework.status import HTTP_200_OK, HTTP_204_NO_CONTENT -from projectify.lib.error_schema import DeriveSchema from projectify.lib.forms import populate_form_with_drf_errors -from projectify.lib.schema import extend_schema from projectify.user.serializers import ( AnonymousUserSerializer, LoggedInUserSerializer, @@ -364,7 +362,6 @@ def password_reset(request: HttpRequest) -> HttpResponse: class LogOut(views.APIView): """Log a user out.""" - @extend_schema(request=None, responses={200: AnonymousUserSerializer}) def post(self, request: Request) -> Response: """Handle POST.""" user_log_out(request=request) @@ -388,10 +385,6 @@ class SignUpSerializer(serializers.Serializer): tos_agreed = serializers.BooleanField() privacy_policy_agreed = serializers.BooleanField() - @extend_schema( - request=SignUpSerializer, - responses={204: None, 400: DeriveSchema, 429: DeriveSchema}, - ) @method_decorator(ratelimit(key="ip", rate="60/h")) def post(self, request: Request) -> Response: """Handle POST.""" @@ -440,10 +433,6 @@ class ConfirmEmailSerializer(serializers.Serializer): email = serializers.EmailField() token = serializers.CharField() - @extend_schema( - request=ConfirmEmailSerializer, - responses={204: None, 400: DeriveSchema}, - ) def post(self, request: Request) -> Response: """Handle POST.""" serializer = self.ConfirmEmailSerializer(data=request.data) @@ -467,14 +456,6 @@ class LogInSerializer(serializers.Serializer): email = serializers.EmailField() password = serializers.CharField() - @extend_schema( - request=LogInSerializer, - responses={ - 200: LoggedInUserSerializer, - 400: DeriveSchema, - 429: DeriveSchema, - }, - ) @method_decorator( ratelimit( group="projectify.user.views.auth.login.post", @@ -529,10 +510,6 @@ class PasswordResetRequestSerializer(serializers.Serializer): email = serializers.EmailField() - @extend_schema( - request=PasswordResetRequestSerializer, - responses={204: None, 400: DeriveSchema, 429: DeriveSchema}, - ) @method_decorator(ratelimit(key="post:email", rate="5/h")) @method_decorator(ratelimit(key="ip", rate="5/h")) @method_decorator(ratelimit(key="ip", rate="1/m")) @@ -557,10 +534,6 @@ class PasswordResetConfirmSerializer(serializers.Serializer): token = serializers.CharField() new_password = serializers.CharField() - @extend_schema( - request=PasswordResetConfirmSerializer, - responses={204: None, 400: DeriveSchema}, - ) def post(self, request: Request) -> Response: """Handle POST.""" serializer = self.PasswordResetConfirmSerializer(data=request.data) @@ -584,11 +557,6 @@ class PasswordPoliciesSerializer(serializers.Serializer): policies = serializers.ListField(child=serializers.CharField()) - @extend_schema( - responses={ - 200: PasswordPoliciesSerializer, - }, - ) def get(self, request: Request) -> Response: """Return all information about current password policy.""" del request diff --git a/backend/projectify/user/views/user.py b/backend/projectify/user/views/user.py index 46847b0aa..8cb2664da 100644 --- a/backend/projectify/user/views/user.py +++ b/backend/projectify/user/views/user.py @@ -27,9 +27,7 @@ from rest_framework.response import Response from rest_framework.status import HTTP_200_OK, HTTP_204_NO_CONTENT -from projectify.lib.error_schema import DeriveSchema from projectify.lib.forms import populate_form_with_drf_errors -from projectify.lib.schema import PolymorphicProxySerializer, extend_schema from projectify.lib.types import AuthenticatedHttpRequest from projectify.lib.views import platform_view from projectify.user.models import User @@ -262,18 +260,6 @@ class UserRead(views.APIView): permission_classes = (AllowAny,) - @extend_schema( - responses={ - 200: PolymorphicProxySerializer( - "auth_info", - [ - LoggedInUserSerializer, - AnonymousUserSerializer, - ], - None, - ), - } - ) def get(self, request: Request) -> Response: """Handle GET.""" user = cast(Union[User, AnonymousUser], request.user) @@ -299,10 +285,6 @@ class Meta: fields = ("preferred_name",) model = User - @extend_schema( - request=UserUpdateSerializer, - responses={200: LoggedInUserSerializer, 400: DeriveSchema}, - ) def put(self, request: Request) -> Response: """Update a user.""" user = cast(Union[User, AnonymousUser], request.user) @@ -338,10 +320,6 @@ class ProfilePictureUploadSerializer(serializers.Serializer): file = serializers.ImageField(required=False) - @extend_schema( - request=ProfilePictureUploadSerializer, - responses={204: None, 400: DeriveSchema}, - ) def post(self, request: Request) -> Response: """Handle POST.""" serializer = self.ProfilePictureUploadSerializer(data=request.data) @@ -365,10 +343,6 @@ class ChangePasswordSerializer(serializers.Serializer): current_password = serializers.CharField() new_password = serializers.CharField() - @extend_schema( - request=ChangePasswordSerializer, - responses={204: None, 400: DeriveSchema, 429: DeriveSchema}, - ) @method_decorator(ratelimit(key="user", rate="5/h")) def post(self, request: Request) -> Response: """Handle POST.""" @@ -394,10 +368,6 @@ class RequestEmailAddressUpdateSerializer(serializers.Serializer): password = serializers.CharField() new_email = serializers.EmailField() - @extend_schema( - request=RequestEmailAddressUpdateSerializer, - responses={204: None, 400: DeriveSchema, 429: DeriveSchema}, - ) @method_decorator(ratelimit(key="user", rate="5/h")) def post(self, request: Request) -> Response: """Handle POST.""" @@ -423,13 +393,6 @@ class ConfirmEmailAddressUpdateSerializer(serializers.Serializer): confirmation_token = serializers.CharField() - @extend_schema( - request=ConfirmEmailAddressUpdateSerializer, - responses={ - 204: None, - 400: DeriveSchema, - }, - ) def post(self, request: Request) -> Response: """Handle POST.""" user = request.user diff --git a/backend/projectify/workspace/views/label.py b/backend/projectify/workspace/views/label.py index e326ac574..5b75f2a62 100644 --- a/backend/projectify/workspace/views/label.py +++ b/backend/projectify/workspace/views/label.py @@ -18,8 +18,6 @@ ) from rest_framework.views import APIView -from projectify.lib.error_schema import DeriveSchema -from projectify.lib.schema import extend_schema from projectify.workspace.models.label import Label from projectify.workspace.selectors.workspace import ( workspace_find_by_workspace_uuid, @@ -47,10 +45,6 @@ class Meta: fields = "name", "color", "workspace_uuid" model = Label - @extend_schema( - request=LabelCreateSerializer, - responses={201: LabelBaseSerializer, 400: DeriveSchema}, - ) def post(self, request: Request) -> Response: """Create the label.""" user = request.user @@ -100,10 +94,6 @@ class Meta: fields = "name", "color" model = Label - @extend_schema( - request=LabelUpdateSerializer, - responses={201: LabelBaseSerializer, 400: DeriveSchema}, - ) def put(self, request: Request, label_uuid: UUID) -> Response: """Handle PUT.""" label = self.get_label(request, label_uuid) @@ -119,9 +109,6 @@ def put(self, request: Request, label_uuid: UUID) -> Response: output_serializer = LabelBaseSerializer(instance=label) return Response(data=output_serializer.data, status=HTTP_200_OK) - @extend_schema( - responses={204: None}, - ) def delete(self, request: Request, label_uuid: UUID) -> Response: """Handle DELETE.""" label = self.get_label(request, label_uuid) diff --git a/backend/projectify/workspace/views/project.py b/backend/projectify/workspace/views/project.py index 7e37e8aa9..bd1ac1108 100644 --- a/backend/projectify/workspace/views/project.py +++ b/backend/projectify/workspace/views/project.py @@ -21,10 +21,8 @@ from rest_framework.response import Response from rest_framework.views import APIView -from projectify.lib.error_schema import DeriveSchema from projectify.lib.forms import populate_form_with_drf_errors from projectify.lib.htmx import HttpResponseClientRefresh -from projectify.lib.schema import extend_schema from projectify.lib.types import AuthenticatedHttpRequest from projectify.lib.views import platform_view @@ -610,10 +608,6 @@ class Meta: model = Project fields = "title", "description", "workspace_uuid", "due_date" - @extend_schema( - request=ProjectCreateSerializer, - responses={201: ProjectDetailSerializer, 400: DeriveSchema}, - ) def post(self, request: Request) -> Response: """Create a project.""" user = request.user @@ -644,9 +638,6 @@ def post(self, request: Request) -> Response: class ProjectReadUpdateDelete(APIView): """Project retrieve view.""" - @extend_schema( - responses={200: ProjectDetailSerializer}, - ) def get(self, request: Request, project_uuid: UUID) -> Response: """Handle GET.""" project = project_find_by_project_uuid( @@ -673,10 +664,6 @@ class Meta: "due_date", ) - @extend_schema( - request=ProjectUpdateSerializer, - responses={200: ProjectUpdateSerializer, 400: DeriveSchema}, - ) def put(self, request: Request, project_uuid: UUID) -> Response: """Handle PUT.""" project = project_find_by_project_uuid( @@ -700,9 +687,6 @@ def put(self, request: Request, project_uuid: UUID) -> Response: ) return Response(data, status.HTTP_200_OK) - @extend_schema( - responses={204: None}, - ) def delete(self, request: Request, project_uuid: UUID) -> Response: """Handle DELETE.""" project = project_find_by_project_uuid( @@ -736,10 +720,6 @@ class Meta: "archived", ) - @extend_schema( - request=None, - responses={200: ArchivedProjectSerializer(many=True)}, - ) def get(self, request: Request, workspace_uuid: UUID) -> Response: """Get queryset.""" workspace = workspace_find_by_workspace_uuid( @@ -769,10 +749,6 @@ class ProjectArchiveSerializer(serializers.Serializer): archived = serializers.BooleanField() - @extend_schema( - request=ProjectArchiveSerializer, - responses={204: None, 400: DeriveSchema}, - ) def post(self, request: Request, project_uuid: UUID) -> Response: """Process request.""" serializer = self.ProjectArchiveSerializer(data=request.data) diff --git a/backend/projectify/workspace/views/section.py b/backend/projectify/workspace/views/section.py index dec41b2da..fae45bbd0 100644 --- a/backend/projectify/workspace/views/section.py +++ b/backend/projectify/workspace/views/section.py @@ -19,9 +19,7 @@ from rest_framework.response import Response from rest_framework.views import APIView -from projectify.lib.error_schema import DeriveSchema from projectify.lib.htmx import HttpResponseClientRedirect -from projectify.lib.schema import extend_schema from projectify.lib.types import AuthenticatedHttpRequest from projectify.lib.views import platform_view from projectify.workspace.models import Section @@ -214,10 +212,6 @@ class Meta: model = Section fields = "title", "description", "project_uuid" - @extend_schema( - request=SectionCreateSerializer, - responses={201: SectionDetailSerializer, 400: DeriveSchema}, - ) def post(self, request: Request) -> Response: """Create a section.""" user = request.user @@ -250,9 +244,6 @@ def post(self, request: Request) -> Response: class SectionReadUpdateDelete(APIView): """Project retrieve view.""" - @extend_schema( - responses={200: SectionDetailSerializer}, - ) def get(self, request: Request, section_uuid: UUID) -> Response: """Handle GET.""" section = section_find_for_user_and_uuid( @@ -274,10 +265,6 @@ class Meta: fields = "title", "description" model = Section - @extend_schema( - request=SectionUpdateSerializer, - responses={200: SectionUpdateSerializer, 400: DeriveSchema}, - ) def put(self, request: Request, section_uuid: UUID) -> Response: """Update section.""" section = section_find_for_user_and_uuid( @@ -299,9 +286,6 @@ def put(self, request: Request, section_uuid: UUID) -> Response: ) return Response(data=serializer.validated_data) - @extend_schema( - responses={204: None}, - ) def delete(self, request: Request, section_uuid: UUID) -> Response: """Handle DELETE.""" section = section_find_for_user_and_uuid( @@ -327,10 +311,6 @@ class SectionMoveSerializer(serializers.Serializer): order = serializers.IntegerField() - @extend_schema( - request=SectionMoveSerializer, - responses={200: None, 400: DeriveSchema}, - ) def post(self, request: Request, section_uuid: UUID) -> Response: """Process request.""" serializer = self.SectionMoveSerializer(data=request.data) diff --git a/backend/projectify/workspace/views/task.py b/backend/projectify/workspace/views/task.py index 0555c9163..c6a9f09b8 100644 --- a/backend/projectify/workspace/views/task.py +++ b/backend/projectify/workspace/views/task.py @@ -23,9 +23,7 @@ from rest_framework.response import Response from rest_framework.views import APIView -from projectify.lib.error_schema import DeriveSchema from projectify.lib.htmx import HttpResponseClientRedirect -from projectify.lib.schema import extend_schema from projectify.lib.types import AuthenticatedHttpRequest from projectify.lib.views import platform_view from projectify.workspace.selectors.team_member import ( @@ -673,10 +671,6 @@ def task_delete_view( class TaskCreate(APIView): """Create a task.""" - @extend_schema( - request=TaskCreateSerializer, - responses={201: TaskDetailSerializer, 400: DeriveSchema}, - ) def post(self, request: Request) -> Response: """Handle POST.""" serializer = TaskCreateSerializer( @@ -714,19 +708,12 @@ def post(self, request: Request) -> Response: class TaskRetrieveUpdateDelete(APIView): """Retrieve a task.""" - @extend_schema( - responses={200: TaskDetailSerializer}, - ) def get(self, request: Request, task_uuid: UUID) -> Response: """Handle GET.""" instance = get_object(request, task_uuid) serializer = TaskDetailSerializer(instance=instance) return Response(data=serializer.data) - @extend_schema( - request=TaskUpdateSerializer, - responses={200: TaskDetailSerializer, 400: DeriveSchema}, - ) def put(self, request: Request, task_uuid: UUID) -> Response: """ Override update behavior. Return using different serializer. @@ -773,9 +760,6 @@ def put(self, request: Request, task_uuid: UUID) -> Response: status=status.HTTP_200_OK, data=response_serializer.data ) - @extend_schema( - responses={204: None}, - ) def delete(self, request: Request, task_uuid: UUID) -> Response: """Delete task.""" instance = get_object(request, task_uuid) @@ -792,10 +776,6 @@ class TaskMoveToSectionSerializer(serializers.Serializer): section_uuid = serializers.UUIDField() - @extend_schema( - request=TaskMoveToSectionSerializer, - responses={200: TaskDetailSerializer, 400: DeriveSchema}, - ) def post(self, request: Request, task_uuid: UUID) -> Response: """Process the request.""" user = request.user @@ -831,10 +811,6 @@ class TaskMoveAfterTaskSerializer(serializers.Serializer): task_uuid = serializers.UUIDField() - @extend_schema( - request=TaskMoveAfterTaskSerializer, - responses={200: TaskDetailSerializer, 400: DeriveSchema}, - ) def post(self, request: Request, task_uuid: UUID) -> Response: """Process the request.""" user = request.user diff --git a/backend/projectify/workspace/views/team_member.py b/backend/projectify/workspace/views/team_member.py index 3103998ef..07c3b790d 100644 --- a/backend/projectify/workspace/views/team_member.py +++ b/backend/projectify/workspace/views/team_member.py @@ -13,8 +13,6 @@ from rest_framework.response import Response from rest_framework.status import HTTP_200_OK, HTTP_204_NO_CONTENT -from projectify.lib.error_schema import DeriveSchema -from projectify.lib.schema import extend_schema from projectify.user.serializers import UserSerializer from projectify.workspace.selectors.team_member import ( team_member_find_by_team_member_uuid, @@ -53,9 +51,6 @@ class Meta: "job_title": {"required": True}, } - @extend_schema( - responses={200: TeamMemberDetailSerializer}, - ) def get(self, request: Request, team_member_uuid: UUID) -> Response: """Handle GET.""" team_member = team_member_find_by_team_member_uuid( @@ -77,10 +72,6 @@ class Meta: fields = "job_title", "role" model = TeamMember - @extend_schema( - request=TeamMemberUpdateSerializer, - responses={200: TeamMemberDetailSerializer, 400: DeriveSchema}, - ) def put(self, request: Request, team_member_uuid: UUID) -> Response: """Handle PUT.""" team_member = team_member_find_by_team_member_uuid( @@ -108,9 +99,6 @@ def put(self, request: Request, team_member_uuid: UUID) -> Response: ) return Response(status=HTTP_200_OK, data=output_serializer.data) - @extend_schema( - responses={204: None}, - ) def delete(self, request: Request, team_member_uuid: UUID) -> Response: """Handle DELETE.""" team_member = team_member_find_by_team_member_uuid( diff --git a/backend/projectify/workspace/views/workspace.py b/backend/projectify/workspace/views/workspace.py index a8a6f3d54..33d9c08c6 100644 --- a/backend/projectify/workspace/views/workspace.py +++ b/backend/projectify/workspace/views/workspace.py @@ -36,10 +36,8 @@ create_billing_portal_session_for_customer, customer_create_stripe_checkout_session, ) -from projectify.lib.error_schema import DeriveSchema from projectify.lib.forms import populate_form_with_drf_errors from projectify.lib.htmx import HttpResponseClientRefresh -from projectify.lib.schema import extend_schema from projectify.lib.types import AuthenticatedHttpRequest from projectify.lib.views import platform_view @@ -971,10 +969,6 @@ class Meta: fields = "title", "description" model = Workspace - @extend_schema( - request=WorkspaceCreateSerializer, - responses={201: WorkspaceBaseSerializer, 400: DeriveSchema}, - ) def post(self, request: Request) -> Response: """Create the workspace and add this user.""" serializer = self.WorkspaceCreateSerializer(data=request.data) @@ -1002,7 +996,6 @@ class Meta: fields = ("title", "uuid") model = Workspace - @extend_schema(responses={200: UserWorkspaceSerializer(many=True)}) def get(self, request: Request) -> Response: """Handle GET.""" workspaces = workspace_find_for_user(who=request.user) @@ -1016,9 +1009,6 @@ def get(self, request: Request) -> Response: class WorkspaceReadUpdate(views.APIView): """Workspace read and update view.""" - @extend_schema( - responses={200: WorkspaceDetailSerializer}, - ) def get(self, request: Request, workspace_uuid: UUID) -> Response: """Handle GET.""" workspace = workspace_find_by_workspace_uuid( @@ -1041,10 +1031,6 @@ class Meta: fields = "title", "description" model = Workspace - @extend_schema( - request=WorkspaceUpdateSerializer, - responses={200: WorkspaceUpdateSerializer, 400: DeriveSchema}, - ) def put(self, request: Request, workspace_uuid: UUID) -> Response: """Handle PUT.""" workspace = workspace_find_by_workspace_uuid( @@ -1081,10 +1067,6 @@ class WorkspacePictureUploadSerializer(serializers.Serializer): file = serializers.ImageField(required=False) - @extend_schema( - request=WorkspacePictureUploadSerializer, - responses={204: None, 400: DeriveSchema}, - ) def post(self, request: Request, workspace_uuid: UUID) -> Response: """Handle POST.""" workspace = workspace_find_by_workspace_uuid( @@ -1114,10 +1096,6 @@ class InviteUserToWorkspaceSerializer(serializers.Serializer): email = serializers.EmailField() - @extend_schema( - request=InviteUserToWorkspaceSerializer, - responses={201: InviteUserToWorkspaceSerializer, 400: DeriveSchema}, - ) @method_decorator(ratelimit(key="user", rate="5/h")) def post(self, request: Request, workspace_uuid: UUID) -> Response: """Handle POST.""" @@ -1163,10 +1141,6 @@ class UninviteUserFromWorkspaceSerializer(serializers.Serializer): email = serializers.EmailField() - @extend_schema( - request=UninviteUserFromWorkspaceSerializer, - responses={204: None, 400: DeriveSchema}, - ) def post(self, request: Request, workspace_uuid: UUID) -> Response: """Handle POST.""" workspace = workspace_find_by_workspace_uuid( diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 7f83ba352..62adab59c 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -51,7 +51,6 @@ django-debug-toolbar = "^4.3.0" ipython = "~8.22" ruff = "0.6.1" -drf-spectacular = {extras = ["sidecar"], version = "^0.27.2"} selenium = "^4.40.0" [tool.poetry.group.test] @@ -203,15 +202,10 @@ exclude = [ "projectify/premail/views.py", "projectify/premail/test/test_views.py", "projectify/premail/test/test_emails.py", - # TODO typing for spectacular is underspecified - "projectify/lib/error_schema.py", # TODO false positive in base_url override "projectify/storage.py", # Nix build "result", - # spectacular classes are being overriden here, we avoid having - # to conditonally ignore no-redef (mypy) or reportAssignmentType (pyright) - "projectify/lib/schema.py", # Pyright reports a few unknown types here that mypy doesn't catch "projectify/workspace/views/task.py", # Pyright thinks that Deferrable.DEFERRED, a string, is not assignable diff --git a/backend/schema/schema.yml b/backend/schema/schema.yml deleted file mode 100644 index 515ddbecd..000000000 --- a/backend/schema/schema.yml +++ /dev/null @@ -1,4335 +0,0 @@ -openapi: 3.0.3 -info: - title: Projectify backend API - version: 1.0.0 - description: API for the Projectify project management software -paths: - /corporate/stripe-webhook/: - post: - operationId: corporate_stripe_webhook_create - description: Construct event type using data coming from stripe. - tags: - - corporate - security: - - cookieAuth: [] - - {} - responses: - '200': - description: No response body - '400': - description: No response body - '500': - content: - application/json: - schema: - $ref: '#/components/schemas/InternalServerError' - description: '' - /corporate/workspace/{workspace_uuid}/create-billing-portal-session: - post: - operationId: corporate_workspace_create_billing_portal_session_create - description: Handle POST. - parameters: - - in: path - name: workspace_uuid - schema: - type: string - format: uuid - required: true - tags: - - corporate - security: - - cookieAuth: [] - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/WorkspaceBillingPortalSessionCreateOutput' - description: '' - '403': - content: - application/json: - schema: - $ref: '#/components/schemas/Forbidden' - description: '' - '404': - content: - application/json: - schema: - $ref: '#/components/schemas/NotFound' - description: '' - '500': - content: - application/json: - schema: - $ref: '#/components/schemas/InternalServerError' - description: '' - /corporate/workspace/{workspace_uuid}/create-checkout-session: - post: - operationId: corporate_workspace_create_checkout_session_create - description: Handle POST. - parameters: - - in: path - name: workspace_uuid - schema: - type: string - format: uuid - required: true - tags: - - corporate - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/WorkspaceCheckoutSessionCreateInput' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/WorkspaceCheckoutSessionCreateInput' - multipart/form-data: - schema: - $ref: '#/components/schemas/WorkspaceCheckoutSessionCreateInput' - required: true - security: - - cookieAuth: [] - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/WorkspaceCheckoutSessionCreateOutput' - description: '' - '400': - content: - application/json: - schema: - type: object - description: Error schema - properties: - code: - type: integer - enum: - - 400 - details: - type: object - description: Errors for WorkspaceCheckoutSessionCreateInputSerializer - properties: - seats: - type: string - general: - type: string - status: - type: string - enum: - - invalid - required: - - code - - details - - status - description: '' - '403': - content: - application/json: - schema: - $ref: '#/components/schemas/Forbidden' - description: '' - '404': - content: - application/json: - schema: - $ref: '#/components/schemas/NotFound' - description: '' - '500': - content: - application/json: - schema: - $ref: '#/components/schemas/InternalServerError' - description: '' - /corporate/workspace/{workspace_uuid}/customer: - get: - operationId: corporate_workspace_customer_retrieve - description: Handle GET. - parameters: - - in: path - name: workspace_uuid - schema: - type: string - format: uuid - required: true - tags: - - corporate - security: - - cookieAuth: [] - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/Customer' - description: '' - '403': - content: - application/json: - schema: - $ref: '#/components/schemas/Forbidden' - description: '' - '404': - content: - application/json: - schema: - $ref: '#/components/schemas/NotFound' - description: '' - '500': - content: - application/json: - schema: - $ref: '#/components/schemas/InternalServerError' - description: '' - /corporate/workspace/{workspace_uuid}/redeem-coupon: - post: - operationId: corporate_workspace_redeem_coupon_create - description: Handle POST. - parameters: - - in: path - name: workspace_uuid - schema: - type: string - format: uuid - required: true - tags: - - corporate - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/CouponRedeem' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/CouponRedeem' - multipart/form-data: - schema: - $ref: '#/components/schemas/CouponRedeem' - required: true - security: - - cookieAuth: [] - responses: - '204': - description: No response body - '400': - content: - application/json: - schema: - type: object - description: Error schema - properties: - code: - type: integer - enum: - - 400 - details: - type: object - description: Errors for CouponRedeemSerializer - properties: - code: - type: string - general: - type: string - status: - type: string - enum: - - invalid - required: - - code - - details - - status - description: '' - '403': - content: - application/json: - schema: - $ref: '#/components/schemas/Forbidden' - description: '' - '404': - content: - application/json: - schema: - $ref: '#/components/schemas/NotFound' - description: '' - '500': - content: - application/json: - schema: - $ref: '#/components/schemas/InternalServerError' - description: '' - /user/user/change-password: - post: - operationId: user_user_change_password_create - description: Handle POST. - tags: - - user - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/ChangePassword' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/ChangePassword' - multipart/form-data: - schema: - $ref: '#/components/schemas/ChangePassword' - required: true - security: - - cookieAuth: [] - responses: - '204': - description: No response body - '400': - content: - application/json: - schema: - type: object - description: Error schema - properties: - code: - type: integer - enum: - - 400 - details: - type: object - description: Errors for ChangePasswordSerializer - properties: - current_password: - type: string - new_password: - type: string - general: - type: string - status: - type: string - enum: - - invalid - required: - - code - - details - - status - description: '' - '429': - content: - application/json: - schema: - $ref: '#/components/schemas/TooManyRequests' - description: '' - '403': - content: - application/json: - schema: - $ref: '#/components/schemas/Forbidden' - description: '' - '500': - content: - application/json: - schema: - $ref: '#/components/schemas/InternalServerError' - description: '' - /user/user/confirm-email: - post: - operationId: user_user_confirm_email_create - description: Handle POST. - tags: - - user - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/ConfirmEmail' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/ConfirmEmail' - multipart/form-data: - schema: - $ref: '#/components/schemas/ConfirmEmail' - required: true - security: - - cookieAuth: [] - - {} - responses: - '204': - description: No response body - '400': - content: - application/json: - schema: - type: object - description: Error schema - properties: - code: - type: integer - enum: - - 400 - details: - type: object - description: Errors for ConfirmEmailSerializer - properties: - email: - type: string - token: - type: string - general: - type: string - status: - type: string - enum: - - invalid - required: - - code - - details - - status - description: '' - '500': - content: - application/json: - schema: - $ref: '#/components/schemas/InternalServerError' - description: '' - /user/user/confirm-password-reset: - post: - operationId: user_user_confirm_password_reset_create - description: Handle POST. - tags: - - user - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/PasswordResetConfirm' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/PasswordResetConfirm' - multipart/form-data: - schema: - $ref: '#/components/schemas/PasswordResetConfirm' - required: true - security: - - cookieAuth: [] - - {} - responses: - '204': - description: No response body - '400': - content: - application/json: - schema: - type: object - description: Error schema - properties: - code: - type: integer - enum: - - 400 - details: - type: object - description: Errors for PasswordResetConfirmSerializer - properties: - email: - type: string - token: - type: string - new_password: - type: string - general: - type: string - status: - type: string - enum: - - invalid - required: - - code - - details - - status - description: '' - '500': - content: - application/json: - schema: - $ref: '#/components/schemas/InternalServerError' - description: '' - /user/user/current-user: - get: - operationId: user_user_current_user_retrieve - description: Handle GET. - tags: - - user - security: - - cookieAuth: [] - - {} - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/auth_info' - description: '' - '500': - content: - application/json: - schema: - $ref: '#/components/schemas/InternalServerError' - description: '' - /user/user/current-user/update: - put: - operationId: user_user_current_user_update_update - description: Update a user. - tags: - - user - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/UserUpdate' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/UserUpdate' - multipart/form-data: - schema: - $ref: '#/components/schemas/UserUpdate' - security: - - cookieAuth: [] - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/LoggedInUser' - description: '' - '400': - content: - application/json: - schema: - type: object - description: Error schema - properties: - code: - type: integer - enum: - - 400 - details: - type: object - description: Errors for UserUpdateSerializer - properties: - preferred_name: - type: string - general: - type: string - status: - type: string - enum: - - invalid - required: - - code - - details - - status - description: '' - '403': - content: - application/json: - schema: - $ref: '#/components/schemas/Forbidden' - description: '' - '500': - content: - application/json: - schema: - $ref: '#/components/schemas/InternalServerError' - description: '' - /user/user/email-address-update/confirm: - post: - operationId: user_user_email_address_update_confirm_create - description: Handle POST. - tags: - - user - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/ConfirmEmailAddressUpdate' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/ConfirmEmailAddressUpdate' - multipart/form-data: - schema: - $ref: '#/components/schemas/ConfirmEmailAddressUpdate' - required: true - security: - - cookieAuth: [] - responses: - '204': - description: No response body - '400': - content: - application/json: - schema: - type: object - description: Error schema - properties: - code: - type: integer - enum: - - 400 - details: - type: object - description: Errors for ConfirmEmailAddressUpdateSerializer - properties: - confirmation_token: - type: string - general: - type: string - status: - type: string - enum: - - invalid - required: - - code - - details - - status - description: '' - '403': - content: - application/json: - schema: - $ref: '#/components/schemas/Forbidden' - description: '' - '500': - content: - application/json: - schema: - $ref: '#/components/schemas/InternalServerError' - description: '' - /user/user/email-address-update/request: - post: - operationId: user_user_email_address_update_request_create - description: Handle POST. - tags: - - user - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/RequestEmailAddressUpdate' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/RequestEmailAddressUpdate' - multipart/form-data: - schema: - $ref: '#/components/schemas/RequestEmailAddressUpdate' - required: true - security: - - cookieAuth: [] - responses: - '204': - description: No response body - '400': - content: - application/json: - schema: - type: object - description: Error schema - properties: - code: - type: integer - enum: - - 400 - details: - type: object - description: Errors for RequestEmailAddressUpdateSerializer - properties: - password: - type: string - new_email: - type: string - general: - type: string - status: - type: string - enum: - - invalid - required: - - code - - details - - status - description: '' - '429': - content: - application/json: - schema: - $ref: '#/components/schemas/TooManyRequests' - description: '' - '403': - content: - application/json: - schema: - $ref: '#/components/schemas/Forbidden' - description: '' - '500': - content: - application/json: - schema: - $ref: '#/components/schemas/InternalServerError' - description: '' - /user/user/log-in: - post: - operationId: user_user_log_in_create - description: Handle POST. - tags: - - user - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/LogIn' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/LogIn' - multipart/form-data: - schema: - $ref: '#/components/schemas/LogIn' - required: true - security: - - cookieAuth: [] - - {} - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/LoggedInUser' - description: '' - '400': - content: - application/json: - schema: - type: object - description: Error schema - properties: - code: - type: integer - enum: - - 400 - details: - type: object - description: Errors for LogInSerializer - properties: - email: - type: string - password: - type: string - general: - type: string - status: - type: string - enum: - - invalid - required: - - code - - details - - status - description: '' - '429': - content: - application/json: - schema: - $ref: '#/components/schemas/TooManyRequests' - description: '' - '500': - content: - application/json: - schema: - $ref: '#/components/schemas/InternalServerError' - description: '' - /user/user/log-out: - post: - operationId: user_user_log_out_create - description: Handle POST. - tags: - - user - security: - - cookieAuth: [] - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/AnonymousUser' - description: '' - '403': - content: - application/json: - schema: - $ref: '#/components/schemas/Forbidden' - description: '' - '500': - content: - application/json: - schema: - $ref: '#/components/schemas/InternalServerError' - description: '' - /user/user/password-policy: - get: - operationId: user_user_password_policy_retrieve - description: Return all information about current password policy. - tags: - - user - security: - - cookieAuth: [] - - {} - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/PasswordPolicies' - description: '' - '500': - content: - application/json: - schema: - $ref: '#/components/schemas/InternalServerError' - description: '' - /user/user/profile-picture/upload: - post: - operationId: user_user_profile_picture_upload_create - description: Handle POST. - tags: - - user - requestBody: - content: - multipart/form-data: - schema: - $ref: '#/components/schemas/ProfilePictureUpload' - security: - - cookieAuth: [] - responses: - '204': - description: No response body - '400': - content: - application/json: - schema: - type: object - description: Error schema - properties: - code: - type: integer - enum: - - 400 - details: - type: object - description: Errors for ProfilePictureUploadSerializer - properties: - file: - type: string - general: - type: string - status: - type: string - enum: - - invalid - required: - - code - - details - - status - description: '' - '403': - content: - application/json: - schema: - $ref: '#/components/schemas/Forbidden' - description: '' - '500': - content: - application/json: - schema: - $ref: '#/components/schemas/InternalServerError' - description: '' - /user/user/request-password-reset: - post: - operationId: user_user_request_password_reset_create - description: Handle POST. - tags: - - user - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/PasswordResetRequest' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/PasswordResetRequest' - multipart/form-data: - schema: - $ref: '#/components/schemas/PasswordResetRequest' - required: true - security: - - cookieAuth: [] - - {} - responses: - '204': - description: No response body - '400': - content: - application/json: - schema: - type: object - description: Error schema - properties: - code: - type: integer - enum: - - 400 - details: - type: object - description: Errors for PasswordResetRequestSerializer - properties: - email: - type: string - general: - type: string - status: - type: string - enum: - - invalid - required: - - code - - details - - status - description: '' - '429': - content: - application/json: - schema: - $ref: '#/components/schemas/TooManyRequests' - description: '' - '500': - content: - application/json: - schema: - $ref: '#/components/schemas/InternalServerError' - description: '' - /user/user/sign-up: - post: - operationId: user_user_sign_up_create - description: Handle POST. - tags: - - user - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/SignUp' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/SignUp' - multipart/form-data: - schema: - $ref: '#/components/schemas/SignUp' - required: true - security: - - cookieAuth: [] - - {} - responses: - '204': - description: No response body - '400': - content: - application/json: - schema: - type: object - description: Error schema - properties: - code: - type: integer - enum: - - 400 - details: - type: object - description: Errors for SignUpSerializer - properties: - email: - type: string - password: - type: string - tos_agreed: - type: string - privacy_policy_agreed: - type: string - general: - type: string - status: - type: string - enum: - - invalid - required: - - code - - details - - status - description: '' - '429': - content: - application/json: - schema: - $ref: '#/components/schemas/TooManyRequests' - description: '' - '500': - content: - application/json: - schema: - $ref: '#/components/schemas/InternalServerError' - description: '' - /workspace/label/: - post: - operationId: workspace_label_create - description: Create the label. - tags: - - workspace - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/LabelCreate' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/LabelCreate' - multipart/form-data: - schema: - $ref: '#/components/schemas/LabelCreate' - required: true - security: - - cookieAuth: [] - responses: - '201': - content: - application/json: - schema: - $ref: '#/components/schemas/LabelBase' - description: '' - '400': - content: - application/json: - schema: - type: object - description: Error schema - properties: - code: - type: integer - enum: - - 400 - details: - type: object - description: Errors for LabelCreateSerializer - properties: - name: - type: string - color: - type: string - workspace_uuid: - type: string - general: - type: string - status: - type: string - enum: - - invalid - required: - - code - - details - - status - description: '' - '403': - content: - application/json: - schema: - $ref: '#/components/schemas/Forbidden' - description: '' - '500': - content: - application/json: - schema: - $ref: '#/components/schemas/InternalServerError' - description: '' - /workspace/label/{label_uuid}: - put: - operationId: workspace_label_update - description: Handle PUT. - parameters: - - in: path - name: label_uuid - schema: - type: string - format: uuid - required: true - tags: - - workspace - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/LabelUpdate' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/LabelUpdate' - multipart/form-data: - schema: - $ref: '#/components/schemas/LabelUpdate' - required: true - security: - - cookieAuth: [] - responses: - '201': - content: - application/json: - schema: - $ref: '#/components/schemas/LabelBase' - description: '' - '400': - content: - application/json: - schema: - type: object - description: Error schema - properties: - code: - type: integer - enum: - - 400 - details: - type: object - description: Errors for LabelUpdateSerializer - properties: - name: - type: string - color: - type: string - general: - type: string - status: - type: string - enum: - - invalid - required: - - code - - details - - status - description: '' - '403': - content: - application/json: - schema: - $ref: '#/components/schemas/Forbidden' - description: '' - '404': - content: - application/json: - schema: - $ref: '#/components/schemas/NotFound' - description: '' - '500': - content: - application/json: - schema: - $ref: '#/components/schemas/InternalServerError' - description: '' - delete: - operationId: workspace_label_destroy - description: Handle DELETE. - parameters: - - in: path - name: label_uuid - schema: - type: string - format: uuid - required: true - tags: - - workspace - security: - - cookieAuth: [] - responses: - '204': - description: No response body - '403': - content: - application/json: - schema: - $ref: '#/components/schemas/Forbidden' - description: '' - '404': - content: - application/json: - schema: - $ref: '#/components/schemas/NotFound' - description: '' - '500': - content: - application/json: - schema: - $ref: '#/components/schemas/InternalServerError' - description: '' - /workspace/project/: - post: - operationId: workspace_project_create - description: Create a project. - tags: - - workspace - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/ProjectCreate' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/ProjectCreate' - multipart/form-data: - schema: - $ref: '#/components/schemas/ProjectCreate' - required: true - security: - - cookieAuth: [] - responses: - '201': - content: - application/json: - schema: - $ref: '#/components/schemas/ProjectDetail' - description: '' - '400': - content: - application/json: - schema: - type: object - description: Error schema - properties: - code: - type: integer - enum: - - 400 - details: - type: object - description: Errors for ProjectCreateSerializer - properties: - title: - type: string - description: - type: string - workspace_uuid: - type: string - due_date: - type: string - general: - type: string - status: - type: string - enum: - - invalid - required: - - code - - details - - status - description: '' - '403': - content: - application/json: - schema: - $ref: '#/components/schemas/Forbidden' - description: '' - '500': - content: - application/json: - schema: - $ref: '#/components/schemas/InternalServerError' - description: '' - /workspace/project/{project_uuid}: - get: - operationId: workspace_project_retrieve - description: Handle GET. - parameters: - - in: path - name: project_uuid - schema: - type: string - format: uuid - required: true - tags: - - workspace - security: - - cookieAuth: [] - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/ProjectDetail' - description: '' - '403': - content: - application/json: - schema: - $ref: '#/components/schemas/Forbidden' - description: '' - '404': - content: - application/json: - schema: - $ref: '#/components/schemas/NotFound' - description: '' - '500': - content: - application/json: - schema: - $ref: '#/components/schemas/InternalServerError' - description: '' - put: - operationId: workspace_project_update - description: Handle PUT. - parameters: - - in: path - name: project_uuid - schema: - type: string - format: uuid - required: true - tags: - - workspace - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/ProjectUpdate' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/ProjectUpdate' - multipart/form-data: - schema: - $ref: '#/components/schemas/ProjectUpdate' - required: true - security: - - cookieAuth: [] - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/ProjectUpdate' - description: '' - '400': - content: - application/json: - schema: - type: object - description: Error schema - properties: - code: - type: integer - enum: - - 400 - details: - type: object - description: Errors for ProjectUpdateSerializer - properties: - title: - type: string - description: - type: string - due_date: - type: string - general: - type: string - status: - type: string - enum: - - invalid - required: - - code - - details - - status - description: '' - '403': - content: - application/json: - schema: - $ref: '#/components/schemas/Forbidden' - description: '' - '404': - content: - application/json: - schema: - $ref: '#/components/schemas/NotFound' - description: '' - '500': - content: - application/json: - schema: - $ref: '#/components/schemas/InternalServerError' - description: '' - delete: - operationId: workspace_project_destroy - description: Handle DELETE. - parameters: - - in: path - name: project_uuid - schema: - type: string - format: uuid - required: true - tags: - - workspace - security: - - cookieAuth: [] - responses: - '204': - description: No response body - '403': - content: - application/json: - schema: - $ref: '#/components/schemas/Forbidden' - description: '' - '404': - content: - application/json: - schema: - $ref: '#/components/schemas/NotFound' - description: '' - '500': - content: - application/json: - schema: - $ref: '#/components/schemas/InternalServerError' - description: '' - /workspace/project/{project_uuid}/archive: - post: - operationId: workspace_project_archive_create - description: Process request. - parameters: - - in: path - name: project_uuid - schema: - type: string - format: uuid - required: true - tags: - - workspace - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/ProjectArchive' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/ProjectArchive' - multipart/form-data: - schema: - $ref: '#/components/schemas/ProjectArchive' - required: true - security: - - cookieAuth: [] - responses: - '204': - description: No response body - '400': - content: - application/json: - schema: - type: object - description: Error schema - properties: - code: - type: integer - enum: - - 400 - details: - type: object - description: Errors for ProjectArchiveSerializer - properties: - archived: - type: string - general: - type: string - status: - type: string - enum: - - invalid - required: - - code - - details - - status - description: '' - '403': - content: - application/json: - schema: - $ref: '#/components/schemas/Forbidden' - description: '' - '404': - content: - application/json: - schema: - $ref: '#/components/schemas/NotFound' - description: '' - '500': - content: - application/json: - schema: - $ref: '#/components/schemas/InternalServerError' - description: '' - /workspace/section/: - post: - operationId: workspace_section_create - description: Create a section. - tags: - - workspace - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/SectionCreate' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/SectionCreate' - multipart/form-data: - schema: - $ref: '#/components/schemas/SectionCreate' - required: true - security: - - cookieAuth: [] - responses: - '201': - content: - application/json: - schema: - $ref: '#/components/schemas/SectionDetail' - description: '' - '400': - content: - application/json: - schema: - type: object - description: Error schema - properties: - code: - type: integer - enum: - - 400 - details: - type: object - description: Errors for SectionCreateSerializer - properties: - title: - type: string - description: - type: string - project_uuid: - type: string - general: - type: string - status: - type: string - enum: - - invalid - required: - - code - - details - - status - description: '' - '403': - content: - application/json: - schema: - $ref: '#/components/schemas/Forbidden' - description: '' - '500': - content: - application/json: - schema: - $ref: '#/components/schemas/InternalServerError' - description: '' - /workspace/section/{section_uuid}: - get: - operationId: workspace_section_retrieve - description: Handle GET. - parameters: - - in: path - name: section_uuid - schema: - type: string - format: uuid - required: true - tags: - - workspace - security: - - cookieAuth: [] - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/SectionDetail' - description: '' - '403': - content: - application/json: - schema: - $ref: '#/components/schemas/Forbidden' - description: '' - '404': - content: - application/json: - schema: - $ref: '#/components/schemas/NotFound' - description: '' - '500': - content: - application/json: - schema: - $ref: '#/components/schemas/InternalServerError' - description: '' - put: - operationId: workspace_section_update - description: Update section. - parameters: - - in: path - name: section_uuid - schema: - type: string - format: uuid - required: true - tags: - - workspace - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/SectionUpdate' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/SectionUpdate' - multipart/form-data: - schema: - $ref: '#/components/schemas/SectionUpdate' - required: true - security: - - cookieAuth: [] - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/SectionUpdate' - description: '' - '400': - content: - application/json: - schema: - type: object - description: Error schema - properties: - code: - type: integer - enum: - - 400 - details: - type: object - description: Errors for SectionUpdateSerializer - properties: - title: - type: string - description: - type: string - general: - type: string - status: - type: string - enum: - - invalid - required: - - code - - details - - status - description: '' - '403': - content: - application/json: - schema: - $ref: '#/components/schemas/Forbidden' - description: '' - '404': - content: - application/json: - schema: - $ref: '#/components/schemas/NotFound' - description: '' - '500': - content: - application/json: - schema: - $ref: '#/components/schemas/InternalServerError' - description: '' - delete: - operationId: workspace_section_destroy - description: Handle DELETE. - parameters: - - in: path - name: section_uuid - schema: - type: string - format: uuid - required: true - tags: - - workspace - security: - - cookieAuth: [] - responses: - '204': - description: No response body - '403': - content: - application/json: - schema: - $ref: '#/components/schemas/Forbidden' - description: '' - '404': - content: - application/json: - schema: - $ref: '#/components/schemas/NotFound' - description: '' - '500': - content: - application/json: - schema: - $ref: '#/components/schemas/InternalServerError' - description: '' - /workspace/section/{section_uuid}/move: - post: - operationId: workspace_section_move_create - description: Process request. - parameters: - - in: path - name: section_uuid - schema: - type: string - format: uuid - required: true - tags: - - workspace - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/SectionMove' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/SectionMove' - multipart/form-data: - schema: - $ref: '#/components/schemas/SectionMove' - required: true - security: - - cookieAuth: [] - responses: - '200': - description: No response body - '400': - content: - application/json: - schema: - type: object - description: Error schema - properties: - code: - type: integer - enum: - - 400 - details: - type: object - description: Errors for SectionMoveSerializer - properties: - order: - type: string - general: - type: string - status: - type: string - enum: - - invalid - required: - - code - - details - - status - description: '' - '403': - content: - application/json: - schema: - $ref: '#/components/schemas/Forbidden' - description: '' - '404': - content: - application/json: - schema: - $ref: '#/components/schemas/NotFound' - description: '' - '500': - content: - application/json: - schema: - $ref: '#/components/schemas/InternalServerError' - description: '' - /workspace/task/: - post: - operationId: workspace_task_create - description: Handle POST. - tags: - - workspace - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/TaskCreate' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/TaskCreate' - multipart/form-data: - schema: - $ref: '#/components/schemas/TaskCreate' - required: true - security: - - cookieAuth: [] - responses: - '201': - content: - application/json: - schema: - $ref: '#/components/schemas/TaskDetail' - description: '' - '400': - content: - application/json: - schema: - type: object - description: Error schema - properties: - code: - type: integer - enum: - - 400 - details: - type: object - description: Errors for TaskCreateSerializer - properties: - title: - type: string - description: - type: string - assignee: - type: object - description: Errors for UuidObjectSerializer - properties: - uuid: - type: string - labels: - type: array - items: - type: object - description: Errors for UuidObjectSerializer - properties: - uuid: - type: string - due_date: - type: string - sub_tasks: - type: array - items: - type: object - description: Errors for SubTaskCreateUpdateSerializer - properties: - uuid: - type: string - title: - type: string - description: - type: string - done: - type: string - section: - type: object - description: Errors for UuidObjectSerializer - properties: - uuid: - type: string - general: - type: string - status: - type: string - enum: - - invalid - required: - - code - - details - - status - description: '' - '403': - content: - application/json: - schema: - $ref: '#/components/schemas/Forbidden' - description: '' - '500': - content: - application/json: - schema: - $ref: '#/components/schemas/InternalServerError' - description: '' - /workspace/task/{task_uuid}: - get: - operationId: workspace_task_retrieve - description: Handle GET. - parameters: - - in: path - name: task_uuid - schema: - type: string - format: uuid - required: true - tags: - - workspace - security: - - cookieAuth: [] - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/TaskDetail' - description: '' - '403': - content: - application/json: - schema: - $ref: '#/components/schemas/Forbidden' - description: '' - '404': - content: - application/json: - schema: - $ref: '#/components/schemas/NotFound' - description: '' - '500': - content: - application/json: - schema: - $ref: '#/components/schemas/InternalServerError' - description: '' - put: - operationId: workspace_task_update - description: |- - Override update behavior. Return using different serializer. - - The idea is that we accept abbreviated nested fields, but return - the data whole. (ws board section, sub tasks, labels, etc.) - parameters: - - in: path - name: task_uuid - schema: - type: string - format: uuid - required: true - tags: - - workspace - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/TaskUpdate' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/TaskUpdate' - multipart/form-data: - schema: - $ref: '#/components/schemas/TaskUpdate' - required: true - security: - - cookieAuth: [] - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/TaskDetail' - description: '' - '400': - content: - application/json: - schema: - type: object - description: Error schema - properties: - code: - type: integer - enum: - - 400 - details: - type: object - description: Errors for TaskUpdateSerializer - properties: - title: - type: string - description: - type: string - assignee: - type: object - description: Errors for UuidObjectSerializer - properties: - uuid: - type: string - labels: - type: array - items: - type: object - description: Errors for UuidObjectSerializer - properties: - uuid: - type: string - due_date: - type: string - sub_tasks: - type: array - items: - type: object - description: Errors for SubTaskCreateUpdateSerializer - properties: - uuid: - type: string - title: - type: string - description: - type: string - done: - type: string - general: - type: string - status: - type: string - enum: - - invalid - required: - - code - - details - - status - description: '' - '403': - content: - application/json: - schema: - $ref: '#/components/schemas/Forbidden' - description: '' - '404': - content: - application/json: - schema: - $ref: '#/components/schemas/NotFound' - description: '' - '500': - content: - application/json: - schema: - $ref: '#/components/schemas/InternalServerError' - description: '' - delete: - operationId: workspace_task_destroy - description: Delete task. - parameters: - - in: path - name: task_uuid - schema: - type: string - format: uuid - required: true - tags: - - workspace - security: - - cookieAuth: [] - responses: - '204': - description: No response body - '403': - content: - application/json: - schema: - $ref: '#/components/schemas/Forbidden' - description: '' - '404': - content: - application/json: - schema: - $ref: '#/components/schemas/NotFound' - description: '' - '500': - content: - application/json: - schema: - $ref: '#/components/schemas/InternalServerError' - description: '' - /workspace/task/{task_uuid}/move-after-task: - post: - operationId: workspace_task_move_after_task_create - description: Process the request. - parameters: - - in: path - name: task_uuid - schema: - type: string - format: uuid - required: true - tags: - - workspace - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/TaskMoveAfterTask' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/TaskMoveAfterTask' - multipart/form-data: - schema: - $ref: '#/components/schemas/TaskMoveAfterTask' - required: true - security: - - cookieAuth: [] - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/TaskDetail' - description: '' - '400': - content: - application/json: - schema: - type: object - description: Error schema - properties: - code: - type: integer - enum: - - 400 - details: - type: object - description: Errors for TaskMoveAfterTaskSerializer - properties: - task_uuid: - type: string - general: - type: string - status: - type: string - enum: - - invalid - required: - - code - - details - - status - description: '' - '403': - content: - application/json: - schema: - $ref: '#/components/schemas/Forbidden' - description: '' - '404': - content: - application/json: - schema: - $ref: '#/components/schemas/NotFound' - description: '' - '500': - content: - application/json: - schema: - $ref: '#/components/schemas/InternalServerError' - description: '' - /workspace/task/{task_uuid}/move-to-section: - post: - operationId: workspace_task_move_to_section_create - description: Process the request. - parameters: - - in: path - name: task_uuid - schema: - type: string - format: uuid - required: true - tags: - - workspace - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/TaskMoveToSection' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/TaskMoveToSection' - multipart/form-data: - schema: - $ref: '#/components/schemas/TaskMoveToSection' - required: true - security: - - cookieAuth: [] - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/TaskDetail' - description: '' - '400': - content: - application/json: - schema: - type: object - description: Error schema - properties: - code: - type: integer - enum: - - 400 - details: - type: object - description: Errors for TaskMoveToSectionSerializer - properties: - section_uuid: - type: string - general: - type: string - status: - type: string - enum: - - invalid - required: - - code - - details - - status - description: '' - '403': - content: - application/json: - schema: - $ref: '#/components/schemas/Forbidden' - description: '' - '404': - content: - application/json: - schema: - $ref: '#/components/schemas/NotFound' - description: '' - '500': - content: - application/json: - schema: - $ref: '#/components/schemas/InternalServerError' - description: '' - /workspace/team-member/{team_member_uuid}: - get: - operationId: workspace_team_member_retrieve - description: Handle GET. - parameters: - - in: path - name: team_member_uuid - schema: - type: string - format: uuid - required: true - tags: - - workspace - security: - - cookieAuth: [] - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/TeamMemberDetail' - description: '' - '403': - content: - application/json: - schema: - $ref: '#/components/schemas/Forbidden' - description: '' - '404': - content: - application/json: - schema: - $ref: '#/components/schemas/NotFound' - description: '' - '500': - content: - application/json: - schema: - $ref: '#/components/schemas/InternalServerError' - description: '' - put: - operationId: workspace_team_member_update - description: Handle PUT. - parameters: - - in: path - name: team_member_uuid - schema: - type: string - format: uuid - required: true - tags: - - workspace - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/TeamMemberUpdate' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/TeamMemberUpdate' - multipart/form-data: - schema: - $ref: '#/components/schemas/TeamMemberUpdate' - security: - - cookieAuth: [] - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/TeamMemberDetail' - description: '' - '400': - content: - application/json: - schema: - type: object - description: Error schema - properties: - code: - type: integer - enum: - - 400 - details: - type: object - description: Errors for TeamMemberUpdateSerializer - properties: - job_title: - type: string - role: - type: string - general: - type: string - status: - type: string - enum: - - invalid - required: - - code - - details - - status - description: '' - '403': - content: - application/json: - schema: - $ref: '#/components/schemas/Forbidden' - description: '' - '404': - content: - application/json: - schema: - $ref: '#/components/schemas/NotFound' - description: '' - '500': - content: - application/json: - schema: - $ref: '#/components/schemas/InternalServerError' - description: '' - delete: - operationId: workspace_team_member_destroy - description: Handle DELETE. - parameters: - - in: path - name: team_member_uuid - schema: - type: string - format: uuid - required: true - tags: - - workspace - security: - - cookieAuth: [] - responses: - '204': - description: No response body - '403': - content: - application/json: - schema: - $ref: '#/components/schemas/Forbidden' - description: '' - '404': - content: - application/json: - schema: - $ref: '#/components/schemas/NotFound' - description: '' - '500': - content: - application/json: - schema: - $ref: '#/components/schemas/InternalServerError' - description: '' - /workspace/workspace/: - post: - operationId: workspace_workspace_create - description: Create the workspace and add this user. - tags: - - workspace - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/WorkspaceCreate' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/WorkspaceCreate' - multipart/form-data: - schema: - $ref: '#/components/schemas/WorkspaceCreate' - required: true - security: - - cookieAuth: [] - responses: - '201': - content: - application/json: - schema: - $ref: '#/components/schemas/WorkspaceBase' - description: '' - '400': - content: - application/json: - schema: - type: object - description: Error schema - properties: - code: - type: integer - enum: - - 400 - details: - type: object - description: Errors for WorkspaceCreateSerializer - properties: - title: - type: string - description: - type: string - general: - type: string - status: - type: string - enum: - - invalid - required: - - code - - details - - status - description: '' - '403': - content: - application/json: - schema: - $ref: '#/components/schemas/Forbidden' - description: '' - '500': - content: - application/json: - schema: - $ref: '#/components/schemas/InternalServerError' - description: '' - /workspace/workspace/{workspace_uuid}: - get: - operationId: workspace_workspace_retrieve - description: Handle GET. - parameters: - - in: path - name: workspace_uuid - schema: - type: string - format: uuid - required: true - tags: - - workspace - security: - - cookieAuth: [] - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/WorkspaceDetail' - description: '' - '403': - content: - application/json: - schema: - $ref: '#/components/schemas/Forbidden' - description: '' - '404': - content: - application/json: - schema: - $ref: '#/components/schemas/NotFound' - description: '' - '500': - content: - application/json: - schema: - $ref: '#/components/schemas/InternalServerError' - description: '' - put: - operationId: workspace_workspace_update - description: Handle PUT. - parameters: - - in: path - name: workspace_uuid - schema: - type: string - format: uuid - required: true - tags: - - workspace - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/WorkspaceUpdate' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/WorkspaceUpdate' - multipart/form-data: - schema: - $ref: '#/components/schemas/WorkspaceUpdate' - required: true - security: - - cookieAuth: [] - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/WorkspaceUpdate' - description: '' - '400': - content: - application/json: - schema: - type: object - description: Error schema - properties: - code: - type: integer - enum: - - 400 - details: - type: object - description: Errors for WorkspaceUpdateSerializer - properties: - title: - type: string - description: - type: string - general: - type: string - status: - type: string - enum: - - invalid - required: - - code - - details - - status - description: '' - '403': - content: - application/json: - schema: - $ref: '#/components/schemas/Forbidden' - description: '' - '404': - content: - application/json: - schema: - $ref: '#/components/schemas/NotFound' - description: '' - '500': - content: - application/json: - schema: - $ref: '#/components/schemas/InternalServerError' - description: '' - /workspace/workspace/{workspace_uuid}/archived-projects/: - get: - operationId: workspace_workspace_archived_projects_list - description: Get queryset. - parameters: - - in: path - name: workspace_uuid - schema: - type: string - format: uuid - required: true - tags: - - workspace - security: - - cookieAuth: [] - responses: - '200': - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/ArchivedProject' - description: '' - '403': - content: - application/json: - schema: - $ref: '#/components/schemas/Forbidden' - description: '' - '404': - content: - application/json: - schema: - $ref: '#/components/schemas/NotFound' - description: '' - '500': - content: - application/json: - schema: - $ref: '#/components/schemas/InternalServerError' - description: '' - /workspace/workspace/{workspace_uuid}/invite-team-member: - post: - operationId: workspace_workspace_invite_team_member_create - description: Handle POST. - parameters: - - in: path - name: workspace_uuid - schema: - type: string - format: uuid - required: true - tags: - - workspace - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/InviteUserToWorkspace' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/InviteUserToWorkspace' - multipart/form-data: - schema: - $ref: '#/components/schemas/InviteUserToWorkspace' - required: true - security: - - cookieAuth: [] - responses: - '201': - content: - application/json: - schema: - $ref: '#/components/schemas/InviteUserToWorkspace' - description: '' - '400': - content: - application/json: - schema: - type: object - description: Error schema - properties: - code: - type: integer - enum: - - 400 - details: - type: object - description: Errors for InviteUserToWorkspaceSerializer - properties: - email: - type: string - general: - type: string - status: - type: string - enum: - - invalid - required: - - code - - details - - status - description: '' - '403': - content: - application/json: - schema: - $ref: '#/components/schemas/Forbidden' - description: '' - '404': - content: - application/json: - schema: - $ref: '#/components/schemas/NotFound' - description: '' - '500': - content: - application/json: - schema: - $ref: '#/components/schemas/InternalServerError' - description: '' - /workspace/workspace/{workspace_uuid}/picture-upload: - post: - operationId: workspace_workspace_picture_upload_create - description: Handle POST. - parameters: - - in: path - name: workspace_uuid - schema: - type: string - format: uuid - required: true - tags: - - workspace - requestBody: - content: - multipart/form-data: - schema: - $ref: '#/components/schemas/WorkspacePictureUpload' - security: - - cookieAuth: [] - responses: - '204': - description: No response body - '400': - content: - application/json: - schema: - type: object - description: Error schema - properties: - code: - type: integer - enum: - - 400 - details: - type: object - description: Errors for WorkspacePictureUploadSerializer - properties: - file: - type: string - general: - type: string - status: - type: string - enum: - - invalid - required: - - code - - details - - status - description: '' - '403': - content: - application/json: - schema: - $ref: '#/components/schemas/Forbidden' - description: '' - '404': - content: - application/json: - schema: - $ref: '#/components/schemas/NotFound' - description: '' - '500': - content: - application/json: - schema: - $ref: '#/components/schemas/InternalServerError' - description: '' - /workspace/workspace/{workspace_uuid}/uninvite-team-member: - post: - operationId: workspace_workspace_uninvite_team_member_create - description: Handle POST. - parameters: - - in: path - name: workspace_uuid - schema: - type: string - format: uuid - required: true - tags: - - workspace - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/UninviteUserFromWorkspace' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/UninviteUserFromWorkspace' - multipart/form-data: - schema: - $ref: '#/components/schemas/UninviteUserFromWorkspace' - required: true - security: - - cookieAuth: [] - responses: - '204': - description: No response body - '400': - content: - application/json: - schema: - type: object - description: Error schema - properties: - code: - type: integer - enum: - - 400 - details: - type: object - description: Errors for UninviteUserFromWorkspaceSerializer - properties: - email: - type: string - general: - type: string - status: - type: string - enum: - - invalid - required: - - code - - details - - status - description: '' - '403': - content: - application/json: - schema: - $ref: '#/components/schemas/Forbidden' - description: '' - '404': - content: - application/json: - schema: - $ref: '#/components/schemas/NotFound' - description: '' - '500': - content: - application/json: - schema: - $ref: '#/components/schemas/InternalServerError' - description: '' - /workspace/workspace/user-workspaces/: - get: - operationId: workspace_workspace_user_workspaces_list - description: Handle GET. - tags: - - workspace - security: - - cookieAuth: [] - responses: - '200': - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/UserWorkspace' - description: '' - '403': - content: - application/json: - schema: - $ref: '#/components/schemas/Forbidden' - description: '' - '500': - content: - application/json: - schema: - $ref: '#/components/schemas/InternalServerError' - description: '' -components: - schemas: - AnonymousUser: - type: object - description: Serialize anonymous user. - properties: - kind: - $ref: '#/components/schemas/AnonymousUserKindEnum' - required: - - kind - AnonymousUserKindEnum: - enum: - - unauthenticated - type: string - description: '* `unauthenticated` - unauthenticated' - ArchivedProject: - type: object - description: Serialize an archived project. - properties: - title: - type: string - maxLength: 255 - uuid: - type: string - format: uuid - readOnly: true - archived: - type: string - format: date-time - nullable: true - description: Archival timestamp of this workspace board. - required: - - title - - uuid - Assignee: - type: object - description: Serialize a task assignee. - properties: - user: - allOf: - - $ref: '#/components/schemas/User' - readOnly: true - uuid: - type: string - format: uuid - readOnly: true - role: - $ref: '#/components/schemas/RoleEnum' - required: - - role - - user - - uuid - ChangePassword: - type: object - description: Accept old and new password. - properties: - current_password: - type: string - new_password: - type: string - required: - - current_password - - new_password - ChatMessageBase: - type: object - description: ChatMessage model serializer. - properties: - created: - type: string - format: date-time - readOnly: true - modified: - type: string - format: date-time - readOnly: true - uuid: - type: string - format: uuid - readOnly: true - text: - type: string - author: - allOf: - - $ref: '#/components/schemas/TeamMemberBase' - readOnly: true - required: - - author - - created - - modified - - text - - uuid - ConfirmEmail: - type: object - description: Take email and password. - properties: - email: - type: string - format: email - token: - type: string - required: - - email - - token - ConfirmEmailAddressUpdate: - type: object - description: Accept new email. - properties: - confirmation_token: - type: string - required: - - confirmation_token - CouponRedeem: - type: object - description: Serializer that takes in a Coupon's code. - properties: - code: - type: string - required: - - code - Customer: - type: object - description: Serializer for customer. - properties: - seats: - type: integer - maximum: 2147483647 - minimum: 0 - uuid: - type: string - format: uuid - readOnly: true - subscription_status: - $ref: '#/components/schemas/SubscriptionStatusEnum' - required: - - seats - - subscription_status - - uuid - Forbidden: - type: object - description: Serialize 403 forbidden error. - properties: - status: - $ref: '#/components/schemas/ForbiddenStatusEnum' - code: - $ref: '#/components/schemas/ForbiddenCodeEnum' - required: - - code - - status - ForbiddenCodeEnum: - enum: - - 403 - type: integer - description: '* `403` - 403' - ForbiddenStatusEnum: - enum: - - permission_denied - type: string - description: '* `permission_denied` - permission_denied' - InternalServerError: - type: object - description: Serialize 429 too many requests error. - properties: - status: - $ref: '#/components/schemas/InternalServerErrorStatusEnum' - code: - $ref: '#/components/schemas/InternalServerErrorCodeEnum' - required: - - code - - status - InternalServerErrorCodeEnum: - enum: - - 500 - type: integer - description: '* `500` - 500' - InternalServerErrorStatusEnum: - enum: - - error - type: string - description: '* `error` - error' - InviteUserToWorkspace: - type: object - description: Accept email. - properties: - email: - type: string - format: email - required: - - email - LabelBase: - type: object - description: Label model serializer. - properties: - name: - type: string - maxLength: 255 - color: - type: integer - maximum: 9223372036854775807 - minimum: 0 - format: int64 - description: Color index - uuid: - type: string - format: uuid - readOnly: true - required: - - color - - name - - uuid - LabelCreate: - type: object - description: Serializer for label creation. - properties: - name: - type: string - maxLength: 255 - color: - type: integer - maximum: 9223372036854775807 - minimum: 0 - format: int64 - description: Color index - workspace_uuid: - type: string - format: uuid - required: - - color - - name - - workspace_uuid - LabelUpdate: - type: object - description: Serializer for Label update. - properties: - name: - type: string - maxLength: 255 - color: - type: integer - maximum: 9223372036854775807 - minimum: 0 - format: int64 - description: Color index - required: - - color - - name - LogIn: - type: object - description: Take email and password. - properties: - email: - type: string - format: email - password: - type: string - required: - - email - - password - LoggedInUser: - type: object - description: Serialize logged in user. - properties: - email: - type: string - format: email - maxLength: 254 - kind: - allOf: - - $ref: '#/components/schemas/LoggedInUserKindEnum' - readOnly: true - default: authenticated - preferred_name: - type: string - nullable: true - maxLength: 255 - profile_picture: - type: string - nullable: true - description: Return profile picture. - readOnly: true - required: - - email - - kind - - preferred_name - - profile_picture - LoggedInUserKindEnum: - enum: - - authenticated - type: string - description: '* `authenticated` - authenticated' - NotFound: - type: object - description: Serialize 404 not found error. - properties: - status: - $ref: '#/components/schemas/NotFoundStatusEnum' - code: - $ref: '#/components/schemas/NotFoundCodeEnum' - required: - - code - - status - NotFoundCodeEnum: - enum: - - 404 - type: integer - description: '* `404` - 404' - NotFoundStatusEnum: - enum: - - not_found - type: string - description: '* `not_found` - not_found' - PasswordPolicies: - type: object - description: Serialize password policies. - properties: - policies: - type: array - items: - type: string - required: - - policies - PasswordResetConfirm: - type: object - description: Take email, token and a new password. - properties: - email: - type: string - format: email - token: - type: string - new_password: - type: string - required: - - email - - new_password - - token - PasswordResetRequest: - type: object - description: Take an email address. - properties: - email: - type: string - format: email - required: - - email - ProfilePictureUpload: - type: object - description: Deserialize picture upload. - properties: - file: - type: string - format: uri - ProjectArchive: - type: object - description: Accept the desired archival status. - properties: - archived: - type: boolean - required: - - archived - ProjectCreate: - type: object - description: Parse project creation input. - properties: - title: - type: string - maxLength: 255 - description: - type: string - nullable: true - workspace_uuid: - type: string - format: uuid - due_date: - type: string - format: date-time - nullable: true - description: Due date for this workspace board - required: - - title - - workspace_uuid - ProjectDetail: - type: object - description: |- - Project serializer. - - Serializes in both directions, workspace and sections, including their - tasks. - properties: - title: - type: string - maxLength: 255 - description: - type: string - nullable: true - uuid: - type: string - format: uuid - readOnly: true - archived: - type: string - format: date-time - nullable: true - description: Archival timestamp of this workspace board. - sections: - type: array - items: - $ref: '#/components/schemas/ProjectDetailSection' - readOnly: true - workspace: - allOf: - - $ref: '#/components/schemas/WorkspaceDetail' - readOnly: true - required: - - archived - - description - - sections - - title - - uuid - - workspace - ProjectDetailSection: - type: object - description: Reduced section serializer. - properties: - uuid: - type: string - format: uuid - readOnly: true - _order: - type: integer - readOnly: true - title: ' order' - title: - type: string - maxLength: 255 - description: - type: string - nullable: true - tasks: - type: array - items: - $ref: '#/components/schemas/ProjectDetailTask' - readOnly: true - required: - - _order - - tasks - - title - - uuid - ProjectDetailTask: - type: object - description: Serialize all task details. - properties: - title: - type: string - maxLength: 255 - uuid: - type: string - format: uuid - readOnly: true - due_date: - type: string - format: date-time - nullable: true - description: Due date for this task - number: - type: integer - maximum: 2147483647 - minimum: 0 - labels: - type: array - items: - $ref: '#/components/schemas/LabelBase' - readOnly: true - assignee: - allOf: - - $ref: '#/components/schemas/ProjectTaskAssignee' - readOnly: true - nullable: true - sub_task_progress: - type: number - format: double - nullable: true - description: - type: string - nullable: true - required: - - assignee - - description - - due_date - - labels - - number - - sub_task_progress - - title - - uuid - ProjectTaskAssignee: - type: object - description: Serialize a task assignee. - properties: - user: - allOf: - - $ref: '#/components/schemas/User' - readOnly: true - uuid: - type: string - format: uuid - readOnly: true - role: - $ref: '#/components/schemas/RoleEnum' - required: - - role - - user - - uuid - ProjectUpdate: - type: object - description: Serializer for PUT. - properties: - title: - type: string - maxLength: 255 - description: - type: string - nullable: true - due_date: - type: string - format: date-time - nullable: true - description: Due date for this workspace board - required: - - title - RequestEmailAddressUpdate: - type: object - description: Accept new email. - properties: - password: - type: string - new_email: - type: string - format: email - required: - - new_email - - password - RoleEnum: - enum: - - OBSERVER - - CONTRIBUTOR - - MAINTAINER - - OWNER - type: string - description: |- - * `OBSERVER` - Observer - * `CONTRIBUTOR` - Contributor - * `MAINTAINER` - Maintainer - * `OWNER` - Owner - SectionCreate: - type: object - description: Parse section creation input. - properties: - title: - type: string - maxLength: 255 - description: - type: string - nullable: true - project_uuid: - type: string - format: uuid - required: - - project_uuid - - title - SectionDetail: - type: object - description: |- - Section detail serializer. - - Goes both up (to workspace) and down (all tasks). - properties: - created: - type: string - format: date-time - readOnly: true - modified: - type: string - format: date-time - readOnly: true - title: - type: string - maxLength: 255 - description: - type: string - nullable: true - _order: - type: integer - readOnly: true - title: ' order' - uuid: - type: string - format: uuid - readOnly: true - project: - allOf: - - $ref: '#/components/schemas/SectionDetailProject' - readOnly: true - tasks: - type: array - items: - $ref: '#/components/schemas/TaskWithSubTask' - readOnly: true - required: - - _order - - created - - description - - modified - - project - - tasks - - title - - uuid - SectionDetailProject: - type: object - description: Serialize a project's section. - properties: - title: - type: string - maxLength: 255 - uuid: - type: string - format: uuid - readOnly: true - workspace: - allOf: - - $ref: '#/components/schemas/SectionDetailWorkspace' - readOnly: true - required: - - title - - uuid - - workspace - SectionDetailWorkspace: - type: object - description: Serialize a section's workspace. - properties: - title: - type: string - maxLength: 255 - uuid: - type: string - format: uuid - readOnly: true - required: - - title - - uuid - SectionMove: - type: object - description: Accept the desired position within project. - properties: - order: - type: integer - required: - - order - SectionUpdate: - type: object - description: Input serializer for PUT. - properties: - title: - type: string - maxLength: 255 - description: - type: string - nullable: true - required: - - title - SignUp: - type: object - description: Take in email and password. - properties: - email: - type: string - format: email - password: - type: string - tos_agreed: - type: boolean - privacy_policy_agreed: - type: boolean - required: - - email - - password - - privacy_policy_agreed - - tos_agreed - SingleQuota: - type: object - description: Serializer a single Quota dataclass. - properties: - current: - type: integer - nullable: true - limit: - type: integer - nullable: true - can_create_more: - type: boolean - required: - - can_create_more - - current - - limit - SubTaskBase: - type: object - description: SubTask model serializer. - properties: - created: - type: string - format: date-time - readOnly: true - modified: - type: string - format: date-time - readOnly: true - title: - type: string - maxLength: 255 - description: - type: string - nullable: true - uuid: - type: string - format: uuid - readOnly: true - done: - type: boolean - description: Designate whether this sub task is done - _order: - type: integer - readOnly: true - title: ' order' - required: - - _order - - created - - description - - done - - modified - - title - - uuid - SubTaskCreateUpdate: - type: object - description: A sub task serializer that accepts a missing UUID. - properties: - uuid: - type: string - format: uuid - title: - type: string - maxLength: 255 - description: - type: string - nullable: true - done: - type: boolean - description: Designate whether this sub task is done - required: - - title - SubscriptionStatusEnum: - enum: - - ACTIVE - - UNPAID - - CANCELLED - - CUSTOM - type: string - description: |- - * `ACTIVE` - Active - * `UNPAID` - Unpaid - * `CANCELLED` - Cancelled - * `CUSTOM` - Custom subscription - TaskCreate: - type: object - description: Serializer for creating tasks. - properties: - title: - type: string - maxLength: 255 - description: - type: string - nullable: true - assignee: - allOf: - - $ref: '#/components/schemas/UuidObject' - nullable: true - labels: - type: array - items: - $ref: '#/components/schemas/UuidObject' - writeOnly: true - due_date: - type: string - format: date-time - nullable: true - description: Due date for this task - sub_tasks: - type: array - items: - $ref: '#/components/schemas/SubTaskCreateUpdate' - section: - $ref: '#/components/schemas/UuidObject' - required: - - assignee - - description - - due_date - - labels - - section - - title - TaskDetail: - type: object - description: |- - Serialize all task details. - - Serializes up to the workspace in one direction, and all chat messages, - labels and sub task in the other direction. - properties: - created: - type: string - format: date-time - readOnly: true - modified: - type: string - format: date-time - readOnly: true - title: - type: string - maxLength: 255 - description: - type: string - nullable: true - _order: - type: integer - readOnly: true - title: ' order' - uuid: - type: string - format: uuid - readOnly: true - due_date: - type: string - format: date-time - nullable: true - description: Due date for this task - number: - type: integer - readOnly: true - sub_tasks: - type: array - items: - $ref: '#/components/schemas/SubTaskBase' - readOnly: true - labels: - type: array - items: - $ref: '#/components/schemas/LabelBase' - readOnly: true - assignee: - allOf: - - $ref: '#/components/schemas/Assignee' - readOnly: true - nullable: true - chat_messages: - type: array - items: - $ref: '#/components/schemas/ChatMessageBase' - readOnly: true - section: - allOf: - - $ref: '#/components/schemas/TaskDetailSection' - readOnly: true - required: - - _order - - assignee - - chat_messages - - created - - description - - due_date - - labels - - modified - - number - - section - - sub_tasks - - title - - uuid - TaskDetailProject: - type: object - description: Serialize a task's project. - properties: - title: - type: string - maxLength: 255 - uuid: - type: string - format: uuid - readOnly: true - workspace: - allOf: - - $ref: '#/components/schemas/TaskDetailWorkspace' - readOnly: true - description: - type: string - nullable: true - due_date: - type: string - format: date-time - nullable: true - description: Due date for this workspace board - required: - - description - - due_date - - title - - uuid - - workspace - TaskDetailSection: - type: object - description: Serialize a task's section. - properties: - title: - type: string - maxLength: 255 - uuid: - type: string - format: uuid - readOnly: true - project: - allOf: - - $ref: '#/components/schemas/TaskDetailProject' - readOnly: true - description: - type: string - nullable: true - _order: - type: integer - readOnly: true - title: ' order' - required: - - _order - - description - - project - - title - - uuid - TaskDetailWorkspace: - type: object - description: Serializes a task's workspace. - properties: - title: - type: string - maxLength: 255 - uuid: - type: string - format: uuid - readOnly: true - required: - - title - - uuid - TaskMoveAfterTask: - type: object - description: Accept a task uuid after which this task should be moved. - properties: - task_uuid: - type: string - format: uuid - required: - - task_uuid - TaskMoveToSection: - type: object - description: Accept the target section uuid. - properties: - section_uuid: - type: string - format: uuid - required: - - section_uuid - TaskUpdate: - type: object - description: Serializer for updating tasks. - properties: - title: - type: string - maxLength: 255 - description: - type: string - nullable: true - assignee: - allOf: - - $ref: '#/components/schemas/UuidObject' - nullable: true - labels: - type: array - items: - $ref: '#/components/schemas/UuidObject' - writeOnly: true - due_date: - type: string - format: date-time - nullable: true - description: Due date for this task - sub_tasks: - type: array - items: - $ref: '#/components/schemas/SubTaskCreateUpdate' - required: - - assignee - - description - - due_date - - labels - - title - TaskWithSubTask: - type: object - description: Serialize all task details. - properties: - created: - type: string - format: date-time - readOnly: true - modified: - type: string - format: date-time - readOnly: true - title: - type: string - maxLength: 255 - description: - type: string - nullable: true - _order: - type: integer - readOnly: true - title: ' order' - uuid: - type: string - format: uuid - readOnly: true - due_date: - type: string - format: date-time - nullable: true - description: Due date for this task - number: - type: integer - readOnly: true - sub_tasks: - type: array - items: - $ref: '#/components/schemas/SubTaskBase' - readOnly: true - labels: - type: array - items: - $ref: '#/components/schemas/LabelBase' - readOnly: true - assignee: - allOf: - - $ref: '#/components/schemas/Assignee' - readOnly: true - nullable: true - required: - - _order - - assignee - - created - - description - - due_date - - labels - - modified - - number - - sub_tasks - - title - - uuid - TeamMemberBase: - type: object - description: Team member serializer. - properties: - user: - allOf: - - $ref: '#/components/schemas/User' - readOnly: true - uuid: - type: string - format: uuid - readOnly: true - role: - $ref: '#/components/schemas/RoleEnum' - required: - - role - - user - - uuid - TeamMemberDetail: - type: object - description: Serialize details about team member. - properties: - user: - allOf: - - $ref: '#/components/schemas/User' - readOnly: true - uuid: - type: string - format: uuid - readOnly: true - role: - $ref: '#/components/schemas/RoleEnum' - job_title: - type: string - nullable: true - maxLength: 255 - required: - - job_title - - role - - user - - uuid - TeamMemberInvite: - type: object - description: Serializer team member invites. - properties: - email: - type: string - format: email - created: - type: string - format: date-time - required: - - created - - email - TeamMemberUpdate: - type: object - description: Serializer for PUT updates. - properties: - job_title: - type: string - nullable: true - maxLength: 255 - role: - $ref: '#/components/schemas/RoleEnum' - TooManyRequests: - type: object - description: Serialize 429 too many requests error. - properties: - status: - $ref: '#/components/schemas/TooManyRequestsStatusEnum' - code: - $ref: '#/components/schemas/TooManyRequestsCodeEnum' - required: - - code - - status - TooManyRequestsCodeEnum: - enum: - - 429 - type: integer - description: '* `429` - 429' - TooManyRequestsStatusEnum: - enum: - - throttled - type: string - description: '* `throttled` - throttled' - UninviteUserFromWorkspace: - type: object - description: Accept email. - properties: - email: - type: string - format: email - required: - - email - User: - type: object - description: User serializer. - properties: - email: - type: string - format: email - maxLength: 254 - preferred_name: - type: string - nullable: true - maxLength: 255 - profile_picture: - type: string - nullable: true - description: Return profile picture. - readOnly: true - required: - - email - - preferred_name - - profile_picture - UserUpdate: - type: object - description: Take only preferred_name in. - properties: - preferred_name: - type: string - nullable: true - maxLength: 255 - UserWorkspace: - type: object - description: Serialize a workspace for overview purposes. - properties: - title: - type: string - maxLength: 255 - uuid: - type: string - format: uuid - readOnly: true - required: - - title - - uuid - UuidObject: - type: object - description: Deserialize the UUID for a any object with a UUID. - properties: - uuid: - type: string - format: uuid - required: - - uuid - WorkspaceBase: - type: object - description: Workspace base serializer. - properties: - created: - type: string - format: date-time - readOnly: true - modified: - type: string - format: date-time - readOnly: true - title: - type: string - maxLength: 255 - description: - type: string - nullable: true - uuid: - type: string - format: uuid - readOnly: true - picture: - type: string - nullable: true - description: Return profile picture. - readOnly: true - required: - - created - - description - - modified - - picture - - title - - uuid - WorkspaceBillingPortalSessionCreateOutput: - type: object - description: Return the url to a billing portal session. - properties: - url: - type: string - format: uri - required: - - url - WorkspaceCheckoutSessionCreateInput: - type: object - description: Accept a number of seats to be added into checkout. - properties: - seats: - type: integer - minimum: 1 - required: - - seats - WorkspaceCheckoutSessionCreateOutput: - type: object - description: Return the url to a checkout session. - properties: - url: - type: string - format: uri - required: - - url - WorkspaceCreate: - type: object - description: Accept title, description. - properties: - title: - type: string - maxLength: 255 - description: - type: string - nullable: true - required: - - title - WorkspaceDetail: - type: object - description: |- - Workspace detail serializer. - - Serializers ws board as well, but not the sections and so forth that they - contain. - properties: - uuid: - type: string - format: uuid - readOnly: true - title: - type: string - maxLength: 255 - description: - type: string - nullable: true - picture: - type: string - nullable: true - description: Return profile picture. - readOnly: true - team_members: - type: array - items: - $ref: '#/components/schemas/WorkspaceTeamMember' - readOnly: true - team_member_invites: - type: array - items: - $ref: '#/components/schemas/TeamMemberInvite' - readOnly: true - projects: - type: array - items: - $ref: '#/components/schemas/WorkspaceProject' - readOnly: true - labels: - type: array - items: - $ref: '#/components/schemas/LabelBase' - readOnly: true - quota: - $ref: '#/components/schemas/WorkspaceQuota' - required: - - description - - labels - - picture - - projects - - quota - - team_member_invites - - team_members - - title - - uuid - WorkspacePictureUpload: - type: object - description: Deserialize an image attachment. - properties: - file: - type: string - format: uri - WorkspaceProject: - type: object - description: Serialize a single project. - properties: - title: - type: string - maxLength: 255 - description: - type: string - nullable: true - uuid: - type: string - format: uuid - readOnly: true - archived: - type: string - format: date-time - nullable: true - description: Archival timestamp of this workspace board. - required: - - title - - uuid - WorkspaceQuota: - type: object - description: Serializer quota. - properties: - workspace_status: - $ref: '#/components/schemas/WorkspaceStatusEnum' - chat_messages: - $ref: '#/components/schemas/SingleQuota' - labels: - $ref: '#/components/schemas/SingleQuota' - sub_tasks: - $ref: '#/components/schemas/SingleQuota' - tasks: - $ref: '#/components/schemas/SingleQuota' - task_labels: - $ref: '#/components/schemas/SingleQuota' - projects: - $ref: '#/components/schemas/SingleQuota' - sections: - $ref: '#/components/schemas/SingleQuota' - team_members_and_invites: - $ref: '#/components/schemas/SingleQuota' - required: - - chat_messages - - labels - - projects - - sections - - sub_tasks - - task_labels - - tasks - - team_members_and_invites - - workspace_status - WorkspaceStatusEnum: - enum: - - full - - trial - - inactive - type: string - description: |- - * `full` - full - * `trial` - trial - * `inactive` - inactive - WorkspaceTeamMember: - type: object - description: Workspace team member serializer. - properties: - user: - allOf: - - $ref: '#/components/schemas/User' - readOnly: true - uuid: - type: string - format: uuid - readOnly: true - role: - $ref: '#/components/schemas/RoleEnum' - job_title: - type: string - nullable: true - maxLength: 255 - required: - - job_title - - role - - user - - uuid - WorkspaceUpdate: - type: object - description: Accept title, description. - properties: - title: - type: string - maxLength: 255 - description: - type: string - nullable: true - required: - - title - auth_info: - oneOf: - - $ref: '#/components/schemas/LoggedInUser' - - $ref: '#/components/schemas/AnonymousUser' - securitySchemes: - cookieAuth: - type: apiKey - in: cookie - name: sessionid diff --git a/backend/schema/schema.yml.license b/backend/schema/schema.yml.license deleted file mode 100644 index d723339a2..000000000 --- a/backend/schema/schema.yml.license +++ /dev/null @@ -1,3 +0,0 @@ -SPDX-FileCopyrightText: 2024 JWP Consulting GK - -SPDX-License-Identifier: AGPL-3.0-or-later