From a0793403f323fa688a1ffe12e0b6e3568b20f0c7 Mon Sep 17 00:00:00 2001 From: Cecilie Seim <68303562+tilen1976@users.noreply.github.com> Date: Fri, 6 Dec 2024 13:57:17 +0100 Subject: [PATCH 01/31] add create draft exception example --- poetry.lock | 40 ++++--- pyproject.toml | 2 +- .../resources/variable-definitions-0.1.yml | 108 +++++++++++++++++- 3 files changed, 135 insertions(+), 15 deletions(-) diff --git a/poetry.lock b/poetry.lock index 024a53e..ec6656c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1356,12 +1356,12 @@ files = [ google-auth = ">=2.14.1,<3.0.dev0" googleapis-common-protos = ">=1.56.2,<2.0.dev0" grpcio = [ - {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}, {version = ">=1.33.2,<2.0dev", optional = true, markers = "python_version < \"3.11\" and extra == \"grpc\""}, + {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}, ] grpcio-status = [ - {version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}, {version = ">=1.33.2,<2.0.dev0", optional = true, markers = "python_version < \"3.11\" and extra == \"grpc\""}, + {version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}, ] proto-plus = ">=1.22.3,<2.0.0dev" protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<6.0.0.dev0" @@ -1451,8 +1451,8 @@ grpcio-status = ">=1.33.2" opentelemetry-api = {version = ">=1.27.0", markers = "python_version >= \"3.8\""} opentelemetry-sdk = {version = ">=1.27.0", markers = "python_version >= \"3.8\""} proto-plus = [ - {version = ">=1.22.2,<2.0.0dev", markers = "python_version >= \"3.11\""}, {version = ">=1.22.0,<2.0.0dev", markers = "python_version < \"3.11\""}, + {version = ">=1.22.2,<2.0.0dev", markers = "python_version >= \"3.11\""}, ] protobuf = ">=3.20.2,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<6.0.0dev" @@ -2997,9 +2997,9 @@ files = [ [package.dependencies] lxml = {version = ">=4.9.2", optional = true, markers = "extra == \"xml\""} numpy = [ - {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, {version = ">=1.22.4", markers = "python_version < \"3.11\""}, {version = ">=1.23.2", markers = "python_version == \"3.11\""}, + {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, ] odfpy = {version = ">=1.4.1", optional = true, markers = "extra == \"excel\""} openpyxl = {version = ">=3.1.0", optional = true, markers = "extra == \"excel\""} @@ -3563,8 +3563,8 @@ files = [ annotated-types = ">=0.6.0" pydantic-core = "2.23.4" typing-extensions = [ - {version = ">=4.12.2", markers = "python_version >= \"3.13\""}, {version = ">=4.6.1", markers = "python_version < \"3.13\""}, + {version = ">=4.12.2", markers = "python_version >= \"3.13\""}, ] [package.extras] @@ -4899,16 +4899,15 @@ name = "testcontainers" version = "4.8.2" description = "Python library for throwaway instances of anything that can run in a Docker container" optional = false -python-versions = "<4.0,>=3.9" -files = [ - {file = "testcontainers-4.8.2-py3-none-any.whl", hash = "sha256:9e19af077cd96e1957c13ee466f1f32905bc6c5bc1bc98643eb18be1a989bfb0"}, - {file = "testcontainers-4.8.2.tar.gz", hash = "sha256:dd4a6a2ea09e3c3ecd39e180b6548105929d0bb78d665ce9919cb3f8c98f9853"}, -] +python-versions = ">=3.9,<4.0" +files = [] +develop = true [package.dependencies] docker = "*" -httpx = {version = "*", optional = true, markers = "extra == \"aws\" or extra == \"generic\" or extra == \"test-module-import\""} -redis = {version = "*", optional = true, markers = "extra == \"generic\" or extra == \"redis\""} +httpx = {version = "*", optional = true} +python-dotenv = "*" +redis = {version = "*", optional = true} typing-extensions = "*" urllib3 = "*" wrapt = "*" @@ -4917,26 +4916,36 @@ wrapt = "*" arangodb = ["python-arango (>=7.8,<8.0)"] aws = ["boto3", "httpx"] azurite = ["azure-storage-blob (>=12.19,<13.0)"] +cassandra = [] chroma = ["chromadb-client"] clickhouse = ["clickhouse-driver"] +cockroachdb = [] cosmosdb = ["azure-cosmos"] db2 = ["ibm_db_sa", "sqlalchemy"] +elasticsearch = [] generic = ["httpx", "redis"] google = ["google-cloud-datastore (>=2)", "google-cloud-pubsub (>=2)"] influxdb = ["influxdb", "influxdb-client"] k3s = ["kubernetes", "pyyaml"] +kafka = [] keycloak = ["python-keycloak"] localstack = ["boto3"] mailpit = ["cryptography"] +memcached = [] +milvus = [] minio = ["minio"] mongodb = ["pymongo"] +mqtt = [] mssql = ["pymssql", "sqlalchemy"] mysql = ["pymysql[rsa]", "sqlalchemy"] nats = ["nats-py"] neo4j = ["neo4j"] +nginx = [] +ollama = [] opensearch = ["opensearch-py"] oracle = ["oracledb", "sqlalchemy"] oracle-free = ["oracledb", "sqlalchemy"] +postgres = [] qdrant = ["qdrant-client"] rabbitmq = ["pika"] redis = ["redis"] @@ -4946,8 +4955,13 @@ selenium = ["selenium"] sftp = ["cryptography"] test-module-import = ["httpx"] trino = ["trino"] +vault = [] weaviate = ["weaviate-client (>=4.5.4,<5.0.0)"] +[package.source] +type = "directory" +url = "../testcontainers-python" + [[package]] name = "toml" version = "0.10.2" @@ -5717,4 +5731,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">=3.10,<4.0" -content-hash = "5763f94bb3ae2dfb5387864d98607e264e86fc403150e7d93fc4f522e8b64923" +content-hash = "41ff087038439b9d0a337e603812d942242b5d11093100228778d71c8f4a8356" diff --git a/pyproject.toml b/pyproject.toml index 7dde83a..428e74d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -63,7 +63,7 @@ types-beautifulsoup4 = ">=4.12.0.20240511" ipykernel = "^6.29.5" rich = "^13.9.4" bpython = "^0.24" -testcontainers = { version = "^4.8.2", extras = ["generic"] } +testcontainers = {path = "../testcontainers-python", extras = ["generic"], develop = true} [tool.pytest.ini_options] pythonpath = [ diff --git a/tests/variable_definitions/resources/variable-definitions-0.1.yml b/tests/variable_definitions/resources/variable-definitions-0.1.yml index 9447b25..b7945af 100644 --- a/tests/variable_definitions/resources/variable-definitions-0.1.yml +++ b/tests/variable_definitions/resources/variable-definitions-0.1.yml @@ -439,7 +439,7 @@ paths: items: $ref: "#/components/schemas/CompleteResponse" examples: - Specific date: + List of one variable definition: value: |- [ { @@ -522,6 +522,8 @@ paths: examples: create_draft: value: dapla-felles-developers + bad_request: + value: dapla-felles-developers requestBody: content: application/json: @@ -609,6 +611,24 @@ paths: "400": description: "Malformed data, missing data or attempt to specify disallowed\ \ fields." + content: + application/json: + schema: + $ref: "#/components/schemas/HttpClientException" + examples: + bad_request: + value: + _links: + self: + - href: /variable-definitions?active_group=dapla-felles-developers + templated: false + _embedded: + errors: + - _links: {} + _embedded: {} + message: "draft.unitTypes[0]: Code a is not a member of classification\ + \ with id 702" + message: Bad Request "409": description: Short name is already in use by another variable definition. security: @@ -1775,6 +1795,30 @@ components: nb: Seksjon for befolkningsstatistikk nn: Seksjon for befolkningsstatistikk email: s320@ssb.no + Exception: + $ref: "#/components/schemas/Throwable" + HttpClientException: + required: + - message + - serviceId + type: object + allOf: + - $ref: "#/components/schemas/HttpException" + - $ref: "#/components/schemas/RuntimeException" + - $ref: "#/components/schemas/Exception" + - $ref: "#/components/schemas/Throwable" + - properties: + serviceId: + type: string + message: + type: string + nullable: true + HttpException: + type: object + allOf: + - $ref: "#/components/schemas/RuntimeException" + - $ref: "#/components/schemas/Exception" + - $ref: "#/components/schemas/Throwable" KlassReference: required: - reference_uri @@ -2091,6 +2135,40 @@ components: title: Seksjon for befolkningsstatistikk email: s320@ssb.no last_updated_at: 2024-06-12T10:39:41.038Z + RuntimeException: + type: object + allOf: + - $ref: "#/components/schemas/Exception" + - $ref: "#/components/schemas/Throwable" + StackTraceElement: + required: + - class_loader_name + - class_name + - file_name + - line_number + - method_name + - module_name + - module_version + - native_method + type: object + properties: + file_name: + type: string + line_number: + type: integer + format: int32 + module_name: + type: string + module_version: + type: string + class_loader_name: + type: string + class_name: + type: string + method_name: + type: string + native_method: + type: boolean SupportedLanguages: type: string description: Languages the application supports. @@ -2098,6 +2176,34 @@ components: - nb - nn - en + x-enum-descriptions: + - Norwegian Bokmål + - Norwegian Nynorsk + - English + Throwable: + required: + - localized_message + - stack_trace + - suppressed + type: object + properties: + localized_message: + type: string + stack_trace: + type: array + items: + $ref: "#/components/schemas/StackTraceElement" + suppressed: + type: array + items: + $ref: "#/components/schemas/Throwable" + cause: + nullable: true + allOf: + - $ref: "#/components/schemas/Throwable" + message: + type: string + nullable: true UpdateDraft: type: object properties: From 7e8abba144445112c2ccdfd341e568140719aaf7 Mon Sep 17 00:00:00 2001 From: Cecilie Seim <68303562+tilen1976@users.noreply.github.com> Date: Fri, 6 Dec 2024 14:23:21 +0100 Subject: [PATCH 02/31] Add test invalid unit types - correct unittypes happy path --- tests/variable_definitions/conftest.py | 40 ++++++++++++++++++- .../vardef_client/api/test_error_path.py | 18 +++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 tests/variable_definitions/vardef_client/api/test_error_path.py diff --git a/tests/variable_definitions/conftest.py b/tests/variable_definitions/conftest.py index 65cdf42..07008f7 100644 --- a/tests/variable_definitions/conftest.py +++ b/tests/variable_definitions/conftest.py @@ -73,7 +73,45 @@ def draft(language_string_type, contact) -> Draft: short_name="test", definition=language_string_type, classification_reference="91", - unit_types=["a", "b"], + unit_types=["01"], + subject_fields=["a", "b"], + contains_sensitive_personal_information=True, + measurement_type="test", + valid_from=date(2024, 11, 1), + external_reference_uri="http://www.example.com", + comment=language_string_type, + related_variable_definition_uris=["http://www.example.com"], + contact=contact, + ) + + +@pytest.fixture +def draft_invalid_unit_types(language_string_type, contact) -> Draft: + return Draft( + name=language_string_type, + short_name="test", + definition=language_string_type, + classification_reference="91", + unit_types=["a"], + subject_fields=["a", "b"], + contains_sensitive_personal_information=True, + measurement_type="test", + valid_from=date(2024, 11, 1), + external_reference_uri="http://www.example.com", + comment=language_string_type, + related_variable_definition_uris=["http://www.example.com"], + contact=contact, + ) + + +@pytest.fixture +def draft_invalid_unit_type(language_string_type, contact) -> Draft: + return Draft( + name=language_string_type, + short_name="test", + definition=language_string_type, + classification_reference="91", + unit_types=["a"], subject_fields=["a", "b"], contains_sensitive_personal_information=True, measurement_type="test", diff --git a/tests/variable_definitions/vardef_client/api/test_error_path.py b/tests/variable_definitions/vardef_client/api/test_error_path.py new file mode 100644 index 0000000..c152788 --- /dev/null +++ b/tests/variable_definitions/vardef_client/api/test_error_path.py @@ -0,0 +1,18 @@ +"""Simple tests for exceptions.""" + +import pytest + +from dapla_metadata.variable_definitions.generated import vardef_client +from dapla_metadata.variable_definitions.generated.vardef_client.exceptions import ( + BadRequestException, +) +from tests.utils.constants import VARDEF_EXAMPLE_ACTIVE_GROUP + + +def test_create_draft(api_client, draft_invalid_unit_types): + api_instance = vardef_client.DraftVariableDefinitionsApi(api_client) + with pytest.raises(BadRequestException): + api_instance.create_variable_definition( + VARDEF_EXAMPLE_ACTIVE_GROUP, + draft_invalid_unit_types, + ) From e29b083191c441865a41674f49f41abc86b3d5bc Mon Sep 17 00:00:00 2001 From: Cecilie Seim <68303562+tilen1976@users.noreply.github.com> Date: Fri, 6 Dec 2024 14:25:54 +0100 Subject: [PATCH 03/31] change exception --- .../variable_definitions/vardef_client/api/test_error_path.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/variable_definitions/vardef_client/api/test_error_path.py b/tests/variable_definitions/vardef_client/api/test_error_path.py index c152788..1daecbb 100644 --- a/tests/variable_definitions/vardef_client/api/test_error_path.py +++ b/tests/variable_definitions/vardef_client/api/test_error_path.py @@ -4,14 +4,14 @@ from dapla_metadata.variable_definitions.generated import vardef_client from dapla_metadata.variable_definitions.generated.vardef_client.exceptions import ( - BadRequestException, + ApiException, ) from tests.utils.constants import VARDEF_EXAMPLE_ACTIVE_GROUP def test_create_draft(api_client, draft_invalid_unit_types): api_instance = vardef_client.DraftVariableDefinitionsApi(api_client) - with pytest.raises(BadRequestException): + with pytest.raises(ApiException): api_instance.create_variable_definition( VARDEF_EXAMPLE_ACTIVE_GROUP, draft_invalid_unit_types, From 0bf1efcb95c278c7790838ef676e06147bdd235c Mon Sep 17 00:00:00 2001 From: Cecilie Seim <68303562+tilen1976@users.noreply.github.com> Date: Fri, 6 Dec 2024 14:44:28 +0100 Subject: [PATCH 04/31] add test exception message --- .../vardef_client/api/test_error_path.py | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/variable_definitions/vardef_client/api/test_error_path.py b/tests/variable_definitions/vardef_client/api/test_error_path.py index 1daecbb..319b039 100644 --- a/tests/variable_definitions/vardef_client/api/test_error_path.py +++ b/tests/variable_definitions/vardef_client/api/test_error_path.py @@ -1,11 +1,16 @@ """Simple tests for exceptions.""" +import json + import pytest from dapla_metadata.variable_definitions.generated import vardef_client from dapla_metadata.variable_definitions.generated.vardef_client.exceptions import ( ApiException, ) +from dapla_metadata.variable_definitions.generated.vardef_client.exceptions import ( + BadRequestException, +) from tests.utils.constants import VARDEF_EXAMPLE_ACTIVE_GROUP @@ -16,3 +21,22 @@ def test_create_draft(api_client, draft_invalid_unit_types): VARDEF_EXAMPLE_ACTIVE_GROUP, draft_invalid_unit_types, ) + + +def test_error_message_create_draft(api_client, draft_invalid_unit_types): + api_instance = vardef_client.DraftVariableDefinitionsApi(api_client) + try: + api_instance.create_variable_definition( + VARDEF_EXAMPLE_ACTIVE_GROUP, + draft_invalid_unit_types, + ) + except BadRequestException as e: + response = e.body + data = json.loads(response) + embedded = data["_embedded"] + message = embedded["errors"]["message"] + + assert ( + message + == "draft.unitTypes[0]: Code a is not a member of classification with id 702" + ) From d7adca524ac97fd6388ae36553c697e0a1db2b73 Mon Sep 17 00:00:00 2001 From: Cecilie Seim <68303562+tilen1976@users.noreply.github.com> Date: Fri, 6 Dec 2024 18:44:01 +0100 Subject: [PATCH 05/31] assert 404 not working correctly --- tests/variable_definitions/conftest.py | 20 ---- .../resources/variable-definitions-0.1.yml | 105 ++++++++++++++++++ .../vardef_client/api/test_error_path.py | 26 ++++- 3 files changed, 129 insertions(+), 22 deletions(-) diff --git a/tests/variable_definitions/conftest.py b/tests/variable_definitions/conftest.py index 07008f7..8aed61f 100644 --- a/tests/variable_definitions/conftest.py +++ b/tests/variable_definitions/conftest.py @@ -103,26 +103,6 @@ def draft_invalid_unit_types(language_string_type, contact) -> Draft: contact=contact, ) - -@pytest.fixture -def draft_invalid_unit_type(language_string_type, contact) -> Draft: - return Draft( - name=language_string_type, - short_name="test", - definition=language_string_type, - classification_reference="91", - unit_types=["a"], - subject_fields=["a", "b"], - contains_sensitive_personal_information=True, - measurement_type="test", - valid_from=date(2024, 11, 1), - external_reference_uri="http://www.example.com", - comment=language_string_type, - related_variable_definition_uris=["http://www.example.com"], - contact=contact, - ) - - @pytest.fixture def update_draft(language_string_type, contact, owner) -> UpdateDraft: return UpdateDraft( diff --git a/tests/variable_definitions/resources/variable-definitions-0.1.yml b/tests/variable_definitions/resources/variable-definitions-0.1.yml index b7945af..a45e11e 100644 --- a/tests/variable_definitions/resources/variable-definitions-0.1.yml +++ b/tests/variable_definitions/resources/variable-definitions-0.1.yml @@ -1307,6 +1307,17 @@ paths: name: Ola Nordmann "404": description: No such variable definition found + content: + application/problem+json: + schema: + $ref: "#/components/schemas/NotFoundException" + examples: + not_found: + value: + type: about:blank + status: 404 + detail: No such variable definition found + parameters: {} security: - keycloak_token: [] /variable-definitions/{variable-definition-id}/validity-periods: @@ -1819,6 +1830,92 @@ components: - $ref: "#/components/schemas/RuntimeException" - $ref: "#/components/schemas/Exception" - $ref: "#/components/schemas/Throwable" + HttpStatus: + type: string + enum: + - CONTINUE + - SWITCHING_PROTOCOLS + - PROCESSING + - EARLY_HINTS + - OK + - CREATED + - ACCEPTED + - NON_AUTHORITATIVE_INFORMATION + - NO_CONTENT + - RESET_CONTENT + - PARTIAL_CONTENT + - MULTI_STATUS + - ALREADY_IMPORTED + - IM_USED + - MULTIPLE_CHOICES + - MOVED_PERMANENTLY + - FOUND + - SEE_OTHER + - NOT_MODIFIED + - USE_PROXY + - SWITCH_PROXY + - TEMPORARY_REDIRECT + - PERMANENT_REDIRECT + - BAD_REQUEST + - UNAUTHORIZED + - PAYMENT_REQUIRED + - FORBIDDEN + - NOT_FOUND + - METHOD_NOT_ALLOWED + - NOT_ACCEPTABLE + - PROXY_AUTHENTICATION_REQUIRED + - REQUEST_TIMEOUT + - CONFLICT + - GONE + - LENGTH_REQUIRED + - PRECONDITION_FAILED + - REQUEST_ENTITY_TOO_LARGE + - REQUEST_URI_TOO_LONG + - UNSUPPORTED_MEDIA_TYPE + - REQUESTED_RANGE_NOT_SATISFIABLE + - EXPECTATION_FAILED + - I_AM_A_TEAPOT + - ENHANCE_YOUR_CALM + - MISDIRECTED_REQUEST + - UNPROCESSABLE_ENTITY + - LOCKED + - FAILED_DEPENDENCY + - TOO_EARLY + - UPGRADE_REQUIRED + - PRECONDITION_REQUIRED + - TOO_MANY_REQUESTS + - REQUEST_HEADER_FIELDS_TOO_LARGE + - NO_RESPONSE + - BLOCKED_BY_WINDOWS_PARENTAL_CONTROLS + - UNAVAILABLE_FOR_LEGAL_REASONS + - REQUEST_HEADER_TOO_LARGE + - INTERNAL_SERVER_ERROR + - NOT_IMPLEMENTED + - BAD_GATEWAY + - SERVICE_UNAVAILABLE + - GATEWAY_TIMEOUT + - HTTP_VERSION_NOT_SUPPORTED + - VARIANT_ALSO_NEGOTIATES + - INSUFFICIENT_STORAGE + - LOOP_DETECTED + - BANDWIDTH_LIMIT_EXCEEDED + - NOT_EXTENDED + - NETWORK_AUTHENTICATION_REQUIRED + - CONNECTION_TIMED_OUT + HttpStatusException: + required: + - body + - status + type: object + allOf: + - $ref: "#/components/schemas/HttpException" + - $ref: "#/components/schemas/RuntimeException" + - $ref: "#/components/schemas/Exception" + - $ref: "#/components/schemas/Throwable" + - properties: + status: + $ref: "#/components/schemas/HttpStatus" + body: {} KlassReference: required: - reference_uri @@ -1844,6 +1941,14 @@ components: en: type: string nullable: true + NotFoundException: + type: object + allOf: + - $ref: "#/components/schemas/HttpStatusException" + - $ref: "#/components/schemas/HttpException" + - $ref: "#/components/schemas/RuntimeException" + - $ref: "#/components/schemas/Exception" + - $ref: "#/components/schemas/Throwable" Owner: required: - groups diff --git a/tests/variable_definitions/vardef_client/api/test_error_path.py b/tests/variable_definitions/vardef_client/api/test_error_path.py index 319b039..97ecc4b 100644 --- a/tests/variable_definitions/vardef_client/api/test_error_path.py +++ b/tests/variable_definitions/vardef_client/api/test_error_path.py @@ -11,7 +11,11 @@ from dapla_metadata.variable_definitions.generated.vardef_client.exceptions import ( BadRequestException, ) +from dapla_metadata.variable_definitions.generated.vardef_client.exceptions import ( + OpenApiException, +) from tests.utils.constants import VARDEF_EXAMPLE_ACTIVE_GROUP +from tests.utils.constants import VARDEF_EXAMPLE_DEFINITION_ID def test_create_draft(api_client, draft_invalid_unit_types): @@ -34,9 +38,27 @@ def test_error_message_create_draft(api_client, draft_invalid_unit_types): response = e.body data = json.loads(response) embedded = data["_embedded"] - message = embedded["errors"]["message"] + message = embedded["errors"] + last = message[0]["message"] assert ( - message + last == "draft.unitTypes[0]: Code a is not a member of classification with id 702" ) + + +def test_none_existing_patch(api_client): + api_instance = vardef_client.PatchesApi(api_client) + with pytest.raises(OpenApiException): + api_instance.get_patch(VARDEF_EXAMPLE_DEFINITION_ID, 200) + + +def test_none_existing_patch_message(api_client): + api_instance = vardef_client.PatchesApi(api_client) + try: + api_instance.get_patch(VARDEF_EXAMPLE_DEFINITION_ID, 200) + except OpenApiException as e: + assert ( + str(e.body) + == "The response /patch-id=200/variable-definition-id=wypvb3wd does not exist!" + ) From 981b682701b2037c5a27fcd221dd22ebe3073d1a Mon Sep 17 00:00:00 2001 From: Cecilie Seim <68303562+tilen1976@users.noreply.github.com> Date: Sat, 7 Dec 2024 11:08:41 +0100 Subject: [PATCH 06/31] add new open api file --- .../resources/variable-definitions-0.1.yml | 105 ------------------ 1 file changed, 105 deletions(-) diff --git a/tests/variable_definitions/resources/variable-definitions-0.1.yml b/tests/variable_definitions/resources/variable-definitions-0.1.yml index a45e11e..b7945af 100644 --- a/tests/variable_definitions/resources/variable-definitions-0.1.yml +++ b/tests/variable_definitions/resources/variable-definitions-0.1.yml @@ -1307,17 +1307,6 @@ paths: name: Ola Nordmann "404": description: No such variable definition found - content: - application/problem+json: - schema: - $ref: "#/components/schemas/NotFoundException" - examples: - not_found: - value: - type: about:blank - status: 404 - detail: No such variable definition found - parameters: {} security: - keycloak_token: [] /variable-definitions/{variable-definition-id}/validity-periods: @@ -1830,92 +1819,6 @@ components: - $ref: "#/components/schemas/RuntimeException" - $ref: "#/components/schemas/Exception" - $ref: "#/components/schemas/Throwable" - HttpStatus: - type: string - enum: - - CONTINUE - - SWITCHING_PROTOCOLS - - PROCESSING - - EARLY_HINTS - - OK - - CREATED - - ACCEPTED - - NON_AUTHORITATIVE_INFORMATION - - NO_CONTENT - - RESET_CONTENT - - PARTIAL_CONTENT - - MULTI_STATUS - - ALREADY_IMPORTED - - IM_USED - - MULTIPLE_CHOICES - - MOVED_PERMANENTLY - - FOUND - - SEE_OTHER - - NOT_MODIFIED - - USE_PROXY - - SWITCH_PROXY - - TEMPORARY_REDIRECT - - PERMANENT_REDIRECT - - BAD_REQUEST - - UNAUTHORIZED - - PAYMENT_REQUIRED - - FORBIDDEN - - NOT_FOUND - - METHOD_NOT_ALLOWED - - NOT_ACCEPTABLE - - PROXY_AUTHENTICATION_REQUIRED - - REQUEST_TIMEOUT - - CONFLICT - - GONE - - LENGTH_REQUIRED - - PRECONDITION_FAILED - - REQUEST_ENTITY_TOO_LARGE - - REQUEST_URI_TOO_LONG - - UNSUPPORTED_MEDIA_TYPE - - REQUESTED_RANGE_NOT_SATISFIABLE - - EXPECTATION_FAILED - - I_AM_A_TEAPOT - - ENHANCE_YOUR_CALM - - MISDIRECTED_REQUEST - - UNPROCESSABLE_ENTITY - - LOCKED - - FAILED_DEPENDENCY - - TOO_EARLY - - UPGRADE_REQUIRED - - PRECONDITION_REQUIRED - - TOO_MANY_REQUESTS - - REQUEST_HEADER_FIELDS_TOO_LARGE - - NO_RESPONSE - - BLOCKED_BY_WINDOWS_PARENTAL_CONTROLS - - UNAVAILABLE_FOR_LEGAL_REASONS - - REQUEST_HEADER_TOO_LARGE - - INTERNAL_SERVER_ERROR - - NOT_IMPLEMENTED - - BAD_GATEWAY - - SERVICE_UNAVAILABLE - - GATEWAY_TIMEOUT - - HTTP_VERSION_NOT_SUPPORTED - - VARIANT_ALSO_NEGOTIATES - - INSUFFICIENT_STORAGE - - LOOP_DETECTED - - BANDWIDTH_LIMIT_EXCEEDED - - NOT_EXTENDED - - NETWORK_AUTHENTICATION_REQUIRED - - CONNECTION_TIMED_OUT - HttpStatusException: - required: - - body - - status - type: object - allOf: - - $ref: "#/components/schemas/HttpException" - - $ref: "#/components/schemas/RuntimeException" - - $ref: "#/components/schemas/Exception" - - $ref: "#/components/schemas/Throwable" - - properties: - status: - $ref: "#/components/schemas/HttpStatus" - body: {} KlassReference: required: - reference_uri @@ -1941,14 +1844,6 @@ components: en: type: string nullable: true - NotFoundException: - type: object - allOf: - - $ref: "#/components/schemas/HttpStatusException" - - $ref: "#/components/schemas/HttpException" - - $ref: "#/components/schemas/RuntimeException" - - $ref: "#/components/schemas/Exception" - - $ref: "#/components/schemas/Throwable" Owner: required: - groups From 9eef1d65df43626f2c93d6b2dc2786ea15c5a3a6 Mon Sep 17 00:00:00 2001 From: Cecilie Seim <68303562+tilen1976@users.noreply.github.com> Date: Mon, 9 Dec 2024 08:02:57 +0100 Subject: [PATCH 07/31] specify exception --- tests/variable_definitions/vardef_client/api/test_error_path.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/variable_definitions/vardef_client/api/test_error_path.py b/tests/variable_definitions/vardef_client/api/test_error_path.py index 97ecc4b..24bbfeb 100644 --- a/tests/variable_definitions/vardef_client/api/test_error_path.py +++ b/tests/variable_definitions/vardef_client/api/test_error_path.py @@ -20,7 +20,7 @@ def test_create_draft(api_client, draft_invalid_unit_types): api_instance = vardef_client.DraftVariableDefinitionsApi(api_client) - with pytest.raises(ApiException): + with pytest.raises(BadRequestException): api_instance.create_variable_definition( VARDEF_EXAMPLE_ACTIVE_GROUP, draft_invalid_unit_types, From 7391582ca47e2711ae3916b3c258752f125c8806 Mon Sep 17 00:00:00 2001 From: Cecilie Seim <68303562+tilen1976@users.noreply.github.com> Date: Mon, 9 Dec 2024 08:03:39 +0100 Subject: [PATCH 08/31] linting --- .../variable_definitions/vardef_client/api/test_error_path.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/variable_definitions/vardef_client/api/test_error_path.py b/tests/variable_definitions/vardef_client/api/test_error_path.py index 24bbfeb..dcf1f34 100644 --- a/tests/variable_definitions/vardef_client/api/test_error_path.py +++ b/tests/variable_definitions/vardef_client/api/test_error_path.py @@ -5,9 +5,6 @@ import pytest from dapla_metadata.variable_definitions.generated import vardef_client -from dapla_metadata.variable_definitions.generated.vardef_client.exceptions import ( - ApiException, -) from dapla_metadata.variable_definitions.generated.vardef_client.exceptions import ( BadRequestException, ) From dff02c47d2a0a00989d8e47cf9bc3a66e73f4ce4 Mon Sep 17 00:00:00 2001 From: Cecilie Seim <68303562+tilen1976@users.noreply.github.com> Date: Mon, 9 Dec 2024 08:21:20 +0100 Subject: [PATCH 09/31] update short name --- tests/variable_definitions/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/variable_definitions/conftest.py b/tests/variable_definitions/conftest.py index 8aed61f..abdf394 100644 --- a/tests/variable_definitions/conftest.py +++ b/tests/variable_definitions/conftest.py @@ -89,7 +89,7 @@ def draft(language_string_type, contact) -> Draft: def draft_invalid_unit_types(language_string_type, contact) -> Draft: return Draft( name=language_string_type, - short_name="test", + short_name="test_unit_types", definition=language_string_type, classification_reference="91", unit_types=["a"], From ad7d303b456492dce821f39f39ecf49d99129a83 Mon Sep 17 00:00:00 2001 From: Cecilie Seim <68303562+tilen1976@users.noreply.github.com> Date: Mon, 9 Dec 2024 14:17:49 +0100 Subject: [PATCH 10/31] Remove bad request example - add not found variable definition example and test --- .../resources/variable-definitions-0.1.yml | 113 ++---------------- .../vardef_client/api/test_error_path.py | 59 ++------- 2 files changed, 21 insertions(+), 151 deletions(-) diff --git a/tests/variable_definitions/resources/variable-definitions-0.1.yml b/tests/variable_definitions/resources/variable-definitions-0.1.yml index b7945af..8d30d0f 100644 --- a/tests/variable_definitions/resources/variable-definitions-0.1.yml +++ b/tests/variable_definitions/resources/variable-definitions-0.1.yml @@ -522,8 +522,6 @@ paths: examples: create_draft: value: dapla-felles-developers - bad_request: - value: dapla-felles-developers requestBody: content: application/json: @@ -611,24 +609,6 @@ paths: "400": description: "Malformed data, missing data or attempt to specify disallowed\ \ fields." - content: - application/json: - schema: - $ref: "#/components/schemas/HttpClientException" - examples: - bad_request: - value: - _links: - self: - - href: /variable-definitions?active_group=dapla-felles-developers - templated: false - _embedded: - errors: - - _links: {} - _embedded: {} - message: "draft.unitTypes[0]: Code a is not a member of classification\ - \ with id 702" - message: Bad Request "409": description: Short name is already in use by another variable definition. security: @@ -652,6 +632,8 @@ paths: value: wypvb3wd Specific date: value: wypvb3wd + not_found: + value: invalid id - name: date_of_validity in: query description: List only variable definitions which are valid on this date. @@ -814,6 +796,15 @@ paths: name: Ola Nordmann "404": description: No such variable definition found + content: + application/problem+json: + examples: + not_found: + value: + type: about:blank + status: 404 + detail: Variable with ID invalid id not found + parameters: {} security: - keycloak_token: [] delete: @@ -1795,30 +1786,6 @@ components: nb: Seksjon for befolkningsstatistikk nn: Seksjon for befolkningsstatistikk email: s320@ssb.no - Exception: - $ref: "#/components/schemas/Throwable" - HttpClientException: - required: - - message - - serviceId - type: object - allOf: - - $ref: "#/components/schemas/HttpException" - - $ref: "#/components/schemas/RuntimeException" - - $ref: "#/components/schemas/Exception" - - $ref: "#/components/schemas/Throwable" - - properties: - serviceId: - type: string - message: - type: string - nullable: true - HttpException: - type: object - allOf: - - $ref: "#/components/schemas/RuntimeException" - - $ref: "#/components/schemas/Exception" - - $ref: "#/components/schemas/Throwable" KlassReference: required: - reference_uri @@ -2135,40 +2102,6 @@ components: title: Seksjon for befolkningsstatistikk email: s320@ssb.no last_updated_at: 2024-06-12T10:39:41.038Z - RuntimeException: - type: object - allOf: - - $ref: "#/components/schemas/Exception" - - $ref: "#/components/schemas/Throwable" - StackTraceElement: - required: - - class_loader_name - - class_name - - file_name - - line_number - - method_name - - module_name - - module_version - - native_method - type: object - properties: - file_name: - type: string - line_number: - type: integer - format: int32 - module_name: - type: string - module_version: - type: string - class_loader_name: - type: string - class_name: - type: string - method_name: - type: string - native_method: - type: boolean SupportedLanguages: type: string description: Languages the application supports. @@ -2180,30 +2113,6 @@ components: - Norwegian Bokmål - Norwegian Nynorsk - English - Throwable: - required: - - localized_message - - stack_trace - - suppressed - type: object - properties: - localized_message: - type: string - stack_trace: - type: array - items: - $ref: "#/components/schemas/StackTraceElement" - suppressed: - type: array - items: - $ref: "#/components/schemas/Throwable" - cause: - nullable: true - allOf: - - $ref: "#/components/schemas/Throwable" - message: - type: string - nullable: true UpdateDraft: type: object properties: diff --git a/tests/variable_definitions/vardef_client/api/test_error_path.py b/tests/variable_definitions/vardef_client/api/test_error_path.py index dcf1f34..5457dfc 100644 --- a/tests/variable_definitions/vardef_client/api/test_error_path.py +++ b/tests/variable_definitions/vardef_client/api/test_error_path.py @@ -5,57 +5,18 @@ import pytest from dapla_metadata.variable_definitions.generated import vardef_client -from dapla_metadata.variable_definitions.generated.vardef_client.exceptions import ( - BadRequestException, -) from dapla_metadata.variable_definitions.generated.vardef_client.exceptions import ( OpenApiException, ) -from tests.utils.constants import VARDEF_EXAMPLE_ACTIVE_GROUP -from tests.utils.constants import VARDEF_EXAMPLE_DEFINITION_ID - - -def test_create_draft(api_client, draft_invalid_unit_types): - api_instance = vardef_client.DraftVariableDefinitionsApi(api_client) - with pytest.raises(BadRequestException): - api_instance.create_variable_definition( - VARDEF_EXAMPLE_ACTIVE_GROUP, - draft_invalid_unit_types, - ) - - -def test_error_message_create_draft(api_client, draft_invalid_unit_types): - api_instance = vardef_client.DraftVariableDefinitionsApi(api_client) - try: - api_instance.create_variable_definition( - VARDEF_EXAMPLE_ACTIVE_GROUP, - draft_invalid_unit_types, - ) - except BadRequestException as e: - response = e.body - data = json.loads(response) - embedded = data["_embedded"] - message = embedded["errors"] - last = message[0]["message"] - - assert ( - last - == "draft.unitTypes[0]: Code a is not a member of classification with id 702" - ) - - -def test_none_existing_patch(api_client): - api_instance = vardef_client.PatchesApi(api_client) - with pytest.raises(OpenApiException): - api_instance.get_patch(VARDEF_EXAMPLE_DEFINITION_ID, 200) -def test_none_existing_patch_message(api_client): - api_instance = vardef_client.PatchesApi(api_client) - try: - api_instance.get_patch(VARDEF_EXAMPLE_DEFINITION_ID, 200) - except OpenApiException as e: - assert ( - str(e.body) - == "The response /patch-id=200/variable-definition-id=wypvb3wd does not exist!" - ) +def test_not_found_variable(api_client): + api_instance = vardef_client.VariableDefinitionsApi(api_client) + with pytest.raises( + OpenApiException, + match="Variable with ID invalid id not found", + ) as exc_info: + api_instance.get_variable_definition_by_id("invalid id") + response = exc_info.value.body + result = json.loads(response) + assert result["detail"] == "Variable with ID invalid id not found" From 6d2c333f29150151e7945f5a359b6c01759da3b4 Mon Sep 17 00:00:00 2001 From: Cecilie Seim <68303562+tilen1976@users.noreply.github.com> Date: Mon, 9 Dec 2024 15:30:53 +0100 Subject: [PATCH 11/31] last edition not found --- .../resources/variable-definitions-0.1.yml | 158 +++++++++++++++++- 1 file changed, 156 insertions(+), 2 deletions(-) diff --git a/tests/variable_definitions/resources/variable-definitions-0.1.yml b/tests/variable_definitions/resources/variable-definitions-0.1.yml index 8d30d0f..2a0dd15 100644 --- a/tests/variable_definitions/resources/variable-definitions-0.1.yml +++ b/tests/variable_definitions/resources/variable-definitions-0.1.yml @@ -632,7 +632,7 @@ paths: value: wypvb3wd Specific date: value: wypvb3wd - not_found: + Not found: value: invalid id - name: date_of_validity in: query @@ -798,8 +798,10 @@ paths: description: No such variable definition found content: application/problem+json: + schema: + $ref: "#/components/schemas/HttpStatusException" examples: - not_found: + Not found: value: type: about:blank status: 404 @@ -1786,6 +1788,100 @@ components: nb: Seksjon for befolkningsstatistikk nn: Seksjon for befolkningsstatistikk email: s320@ssb.no + Exception: + $ref: "#/components/schemas/Throwable" + HttpException: + type: object + allOf: + - $ref: "#/components/schemas/RuntimeException" + - $ref: "#/components/schemas/Exception" + - $ref: "#/components/schemas/Throwable" + HttpStatus: + type: string + enum: + - CONTINUE + - SWITCHING_PROTOCOLS + - PROCESSING + - EARLY_HINTS + - OK + - CREATED + - ACCEPTED + - NON_AUTHORITATIVE_INFORMATION + - NO_CONTENT + - RESET_CONTENT + - PARTIAL_CONTENT + - MULTI_STATUS + - ALREADY_IMPORTED + - IM_USED + - MULTIPLE_CHOICES + - MOVED_PERMANENTLY + - FOUND + - SEE_OTHER + - NOT_MODIFIED + - USE_PROXY + - SWITCH_PROXY + - TEMPORARY_REDIRECT + - PERMANENT_REDIRECT + - BAD_REQUEST + - UNAUTHORIZED + - PAYMENT_REQUIRED + - FORBIDDEN + - NOT_FOUND + - METHOD_NOT_ALLOWED + - NOT_ACCEPTABLE + - PROXY_AUTHENTICATION_REQUIRED + - REQUEST_TIMEOUT + - CONFLICT + - GONE + - LENGTH_REQUIRED + - PRECONDITION_FAILED + - REQUEST_ENTITY_TOO_LARGE + - REQUEST_URI_TOO_LONG + - UNSUPPORTED_MEDIA_TYPE + - REQUESTED_RANGE_NOT_SATISFIABLE + - EXPECTATION_FAILED + - I_AM_A_TEAPOT + - ENHANCE_YOUR_CALM + - MISDIRECTED_REQUEST + - UNPROCESSABLE_ENTITY + - LOCKED + - FAILED_DEPENDENCY + - TOO_EARLY + - UPGRADE_REQUIRED + - PRECONDITION_REQUIRED + - TOO_MANY_REQUESTS + - REQUEST_HEADER_FIELDS_TOO_LARGE + - NO_RESPONSE + - BLOCKED_BY_WINDOWS_PARENTAL_CONTROLS + - UNAVAILABLE_FOR_LEGAL_REASONS + - REQUEST_HEADER_TOO_LARGE + - INTERNAL_SERVER_ERROR + - NOT_IMPLEMENTED + - BAD_GATEWAY + - SERVICE_UNAVAILABLE + - GATEWAY_TIMEOUT + - HTTP_VERSION_NOT_SUPPORTED + - VARIANT_ALSO_NEGOTIATES + - INSUFFICIENT_STORAGE + - LOOP_DETECTED + - BANDWIDTH_LIMIT_EXCEEDED + - NOT_EXTENDED + - NETWORK_AUTHENTICATION_REQUIRED + - CONNECTION_TIMED_OUT + HttpStatusException: + required: + - body + - status + type: object + allOf: + - $ref: "#/components/schemas/HttpException" + - $ref: "#/components/schemas/RuntimeException" + - $ref: "#/components/schemas/Exception" + - $ref: "#/components/schemas/Throwable" + - properties: + status: + $ref: "#/components/schemas/HttpStatus" + body: {} KlassReference: required: - reference_uri @@ -2102,6 +2198,40 @@ components: title: Seksjon for befolkningsstatistikk email: s320@ssb.no last_updated_at: 2024-06-12T10:39:41.038Z + RuntimeException: + type: object + allOf: + - $ref: "#/components/schemas/Exception" + - $ref: "#/components/schemas/Throwable" + StackTraceElement: + required: + - class_loader_name + - class_name + - file_name + - line_number + - method_name + - module_name + - module_version + - native_method + type: object + properties: + file_name: + type: string + line_number: + type: integer + format: int32 + module_name: + type: string + module_version: + type: string + class_loader_name: + type: string + class_name: + type: string + method_name: + type: string + native_method: + type: boolean SupportedLanguages: type: string description: Languages the application supports. @@ -2113,6 +2243,30 @@ components: - Norwegian Bokmål - Norwegian Nynorsk - English + Throwable: + required: + - localized_message + - stack_trace + - suppressed + type: object + properties: + localized_message: + type: string + stack_trace: + type: array + items: + $ref: "#/components/schemas/StackTraceElement" + suppressed: + type: array + items: + $ref: "#/components/schemas/Throwable" + cause: + nullable: true + allOf: + - $ref: "#/components/schemas/Throwable" + message: + type: string + nullable: true UpdateDraft: type: object properties: From 07144c20a43d8b2e53f416b7ce28a98f5dc3861e Mon Sep 17 00:00:00 2001 From: Cecilie Seim <68303562+tilen1976@users.noreply.github.com> Date: Mon, 9 Dec 2024 15:39:59 +0100 Subject: [PATCH 12/31] remove fixture --- tests/variable_definitions/conftest.py | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/tests/variable_definitions/conftest.py b/tests/variable_definitions/conftest.py index abdf394..cacec1c 100644 --- a/tests/variable_definitions/conftest.py +++ b/tests/variable_definitions/conftest.py @@ -85,24 +85,6 @@ def draft(language_string_type, contact) -> Draft: ) -@pytest.fixture -def draft_invalid_unit_types(language_string_type, contact) -> Draft: - return Draft( - name=language_string_type, - short_name="test_unit_types", - definition=language_string_type, - classification_reference="91", - unit_types=["a"], - subject_fields=["a", "b"], - contains_sensitive_personal_information=True, - measurement_type="test", - valid_from=date(2024, 11, 1), - external_reference_uri="http://www.example.com", - comment=language_string_type, - related_variable_definition_uris=["http://www.example.com"], - contact=contact, - ) - @pytest.fixture def update_draft(language_string_type, contact, owner) -> UpdateDraft: return UpdateDraft( From dfcd030b1e27a7bc85a067e27b44bdaba6530968 Mon Sep 17 00:00:00 2001 From: Cecilie Seim <68303562+tilen1976@users.noreply.github.com> Date: Tue, 10 Dec 2024 10:04:43 +0100 Subject: [PATCH 13/31] updated open api spec --- .../resources/variable-definitions-0.1.yml | 154 +----------------- 1 file changed, 1 insertion(+), 153 deletions(-) diff --git a/tests/variable_definitions/resources/variable-definitions-0.1.yml b/tests/variable_definitions/resources/variable-definitions-0.1.yml index 2a0dd15..fa05dfd 100644 --- a/tests/variable_definitions/resources/variable-definitions-0.1.yml +++ b/tests/variable_definitions/resources/variable-definitions-0.1.yml @@ -799,7 +799,7 @@ paths: content: application/problem+json: schema: - $ref: "#/components/schemas/HttpStatusException" + $ref: https://opensource.zalando.com/restful-api-guidelines/models/problem-1.0.1.yaml#/Problem examples: Not found: value: @@ -1788,100 +1788,6 @@ components: nb: Seksjon for befolkningsstatistikk nn: Seksjon for befolkningsstatistikk email: s320@ssb.no - Exception: - $ref: "#/components/schemas/Throwable" - HttpException: - type: object - allOf: - - $ref: "#/components/schemas/RuntimeException" - - $ref: "#/components/schemas/Exception" - - $ref: "#/components/schemas/Throwable" - HttpStatus: - type: string - enum: - - CONTINUE - - SWITCHING_PROTOCOLS - - PROCESSING - - EARLY_HINTS - - OK - - CREATED - - ACCEPTED - - NON_AUTHORITATIVE_INFORMATION - - NO_CONTENT - - RESET_CONTENT - - PARTIAL_CONTENT - - MULTI_STATUS - - ALREADY_IMPORTED - - IM_USED - - MULTIPLE_CHOICES - - MOVED_PERMANENTLY - - FOUND - - SEE_OTHER - - NOT_MODIFIED - - USE_PROXY - - SWITCH_PROXY - - TEMPORARY_REDIRECT - - PERMANENT_REDIRECT - - BAD_REQUEST - - UNAUTHORIZED - - PAYMENT_REQUIRED - - FORBIDDEN - - NOT_FOUND - - METHOD_NOT_ALLOWED - - NOT_ACCEPTABLE - - PROXY_AUTHENTICATION_REQUIRED - - REQUEST_TIMEOUT - - CONFLICT - - GONE - - LENGTH_REQUIRED - - PRECONDITION_FAILED - - REQUEST_ENTITY_TOO_LARGE - - REQUEST_URI_TOO_LONG - - UNSUPPORTED_MEDIA_TYPE - - REQUESTED_RANGE_NOT_SATISFIABLE - - EXPECTATION_FAILED - - I_AM_A_TEAPOT - - ENHANCE_YOUR_CALM - - MISDIRECTED_REQUEST - - UNPROCESSABLE_ENTITY - - LOCKED - - FAILED_DEPENDENCY - - TOO_EARLY - - UPGRADE_REQUIRED - - PRECONDITION_REQUIRED - - TOO_MANY_REQUESTS - - REQUEST_HEADER_FIELDS_TOO_LARGE - - NO_RESPONSE - - BLOCKED_BY_WINDOWS_PARENTAL_CONTROLS - - UNAVAILABLE_FOR_LEGAL_REASONS - - REQUEST_HEADER_TOO_LARGE - - INTERNAL_SERVER_ERROR - - NOT_IMPLEMENTED - - BAD_GATEWAY - - SERVICE_UNAVAILABLE - - GATEWAY_TIMEOUT - - HTTP_VERSION_NOT_SUPPORTED - - VARIANT_ALSO_NEGOTIATES - - INSUFFICIENT_STORAGE - - LOOP_DETECTED - - BANDWIDTH_LIMIT_EXCEEDED - - NOT_EXTENDED - - NETWORK_AUTHENTICATION_REQUIRED - - CONNECTION_TIMED_OUT - HttpStatusException: - required: - - body - - status - type: object - allOf: - - $ref: "#/components/schemas/HttpException" - - $ref: "#/components/schemas/RuntimeException" - - $ref: "#/components/schemas/Exception" - - $ref: "#/components/schemas/Throwable" - - properties: - status: - $ref: "#/components/schemas/HttpStatus" - body: {} KlassReference: required: - reference_uri @@ -2198,40 +2104,6 @@ components: title: Seksjon for befolkningsstatistikk email: s320@ssb.no last_updated_at: 2024-06-12T10:39:41.038Z - RuntimeException: - type: object - allOf: - - $ref: "#/components/schemas/Exception" - - $ref: "#/components/schemas/Throwable" - StackTraceElement: - required: - - class_loader_name - - class_name - - file_name - - line_number - - method_name - - module_name - - module_version - - native_method - type: object - properties: - file_name: - type: string - line_number: - type: integer - format: int32 - module_name: - type: string - module_version: - type: string - class_loader_name: - type: string - class_name: - type: string - method_name: - type: string - native_method: - type: boolean SupportedLanguages: type: string description: Languages the application supports. @@ -2243,30 +2115,6 @@ components: - Norwegian Bokmål - Norwegian Nynorsk - English - Throwable: - required: - - localized_message - - stack_trace - - suppressed - type: object - properties: - localized_message: - type: string - stack_trace: - type: array - items: - $ref: "#/components/schemas/StackTraceElement" - suppressed: - type: array - items: - $ref: "#/components/schemas/Throwable" - cause: - nullable: true - allOf: - - $ref: "#/components/schemas/Throwable" - message: - type: string - nullable: true UpdateDraft: type: object properties: From 63a93add6a063d1c8eace0b7cbc9fd0da8632168 Mon Sep 17 00:00:00 2001 From: Cecilie Seim <68303562+tilen1976@users.noreply.github.com> Date: Tue, 10 Dec 2024 12:24:46 +0100 Subject: [PATCH 14/31] Test custom exception message --- .../variable_definitions/exceptions.py | 19 +++++++++++++++++++ .../vardef_client/api/test_error_path.py | 12 ++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 src/dapla_metadata/variable_definitions/exceptions.py diff --git a/src/dapla_metadata/variable_definitions/exceptions.py b/src/dapla_metadata/variable_definitions/exceptions.py new file mode 100644 index 0000000..b493d0b --- /dev/null +++ b/src/dapla_metadata/variable_definitions/exceptions.py @@ -0,0 +1,19 @@ +"""Vardef client exceptions.""" + +import json + +from dapla_metadata.variable_definitions.generated.vardef_client.exceptions import ( + OpenApiException, +) + + +class VardefClientException(OpenApiException): + """Shape error messages for Vardef client users.""" + + def __init__(self, response_body: str) -> None: + """Initilize response and output formatted error message.""" + data = json.loads(response_body) + self.status = data["status"] + self.detail = data["detail"] + self.response_body = response_body # Save the full response for debugging + super().__init__(f"Status {self.status}: {self.detail}") diff --git a/tests/variable_definitions/vardef_client/api/test_error_path.py b/tests/variable_definitions/vardef_client/api/test_error_path.py index 5457dfc..9755e65 100644 --- a/tests/variable_definitions/vardef_client/api/test_error_path.py +++ b/tests/variable_definitions/vardef_client/api/test_error_path.py @@ -4,6 +4,7 @@ import pytest +from dapla_metadata.variable_definitions.exceptions import VardefClientException from dapla_metadata.variable_definitions.generated import vardef_client from dapla_metadata.variable_definitions.generated.vardef_client.exceptions import ( OpenApiException, @@ -20,3 +21,14 @@ def test_not_found_variable(api_client): response = exc_info.value.body result = json.loads(response) assert result["detail"] == "Variable with ID invalid id not found" + + +def test_test(api_client): + api_instance = vardef_client.VariableDefinitionsApi(api_client) + with pytest.raises( + OpenApiException, + match="Variable with ID invalid id not found", + ) as e: + api_instance.get_variable_definition_by_id("invalid id") + exception = VardefClientException(e.value.body) + assert str(exception) == "Status 404: Variable with ID invalid id not found" From 62e52f908c00f59f08e944081209f1938ff970df Mon Sep 17 00:00:00 2001 From: Cecilie Seim <68303562+tilen1976@users.noreply.github.com> Date: Tue, 10 Dec 2024 13:57:55 +0100 Subject: [PATCH 15/31] handle constraint violations --- .../variable_definitions/exceptions.py | 63 +++++++++++++++++-- tests/utils/constants.py | 24 +++++++ .../vardef_client/api/test_error_path.py | 40 +++++++++++- 3 files changed, 120 insertions(+), 7 deletions(-) diff --git a/src/dapla_metadata/variable_definitions/exceptions.py b/src/dapla_metadata/variable_definitions/exceptions.py index b493d0b..aba4afd 100644 --- a/src/dapla_metadata/variable_definitions/exceptions.py +++ b/src/dapla_metadata/variable_definitions/exceptions.py @@ -8,12 +8,63 @@ class VardefClientException(OpenApiException): - """Shape error messages for Vardef client users.""" + """Custom exception to represent errors encountered in the Vardef client. + + This exception extracts and formats error details from a JSON response body + provided by the Vardef API, enabling more descriptive error messages. + If the response body cannot be parsed as JSON or lacks expected keys, + default values are used to provide meaningful feedback. + """ def __init__(self, response_body: str) -> None: - """Initilize response and output formatted error message.""" - data = json.loads(response_body) - self.status = data["status"] - self.detail = data["detail"] - self.response_body = response_body # Save the full response for debugging + """Initialize the exception with a JSON-formatted response body. + + Args: + response_body (str): The JSON string containing error details + from the Vardef API response. + + Attributes: + status (str): The status code from the response, or "Unknown status" + if not available or the response is invalid. + detail (str || list): A detailed error message from the response, or + "No detail provided" if not provided. If "Constraint violation" + the detail is a list with field and message. + response_body (str): The raw response body string, stored for + debugging purposes. + + Raises: + None: The constructor handles invalid JSON and missing keys + gracefully, defaulting to error messages. + """ + self.detail: str | list + try: + data = json.loads(response_body) + self.status = data.get("status", "Unknown status") + if data.get("title") == "Constraint Violation": + violations = data.get("violations", []) + self.detail = self._format_violations(violations) + else: + self.detail = data.get("detail", "No detail provided") + self.response_body = response_body # Save the full response for debugging + except (json.JSONDecodeError, TypeError): + self.status = "Unknown" + self.detail = "Invalid response body" + data = None super().__init__(f"Status {self.status}: {self.detail}") + + def _format_violations(self, violations: list) -> list: + """Format a list of violations into a readable string. + + Args: + violations (list): List of violation dictionaries with 'field' and 'message'. + + Returns: + str: Formatted string of violations. + """ + return [ + { + "Field": violation.get("field", "Unknown field"), + "Message": violation.get("message", "No message provided"), + } + for violation in violations + ] diff --git a/tests/utils/constants.py b/tests/utils/constants.py index 1323741..c485742 100644 --- a/tests/utils/constants.py +++ b/tests/utils/constants.py @@ -5,3 +5,27 @@ DAPLA_REGION = "DAPLA_REGION" DAPLA_GROUP_CONTEXT = "DAPLA_GROUP_CONTEXT" DAPLA_SERVICE = "DAPLA_SERVICE" +NOT_FOUND_STATUS = 404 +BAD_REQUEST_STATUS = 400 +CONSTRAINT_VIOLATION_BODY = """{ + "cause": null, + "suppressed": [ + ], + "detail": null, + "instance": null, + "parameters": { + }, + "type": "https://zalando.github.io/problem/constraint-violation", + "title": "Constraint Violation", + "status": 400, + "violations": [ + { + "field": "updateVariableDefinitionById.updateDraft.owner.team", + "message": "Invalid Dapla team" + }, + { + "field": "updateVariableDefinitionById.updateDraft.owner.team", + "message": "must not be empty" + } + ] +}""" diff --git a/tests/variable_definitions/vardef_client/api/test_error_path.py b/tests/variable_definitions/vardef_client/api/test_error_path.py index 9755e65..a997fee 100644 --- a/tests/variable_definitions/vardef_client/api/test_error_path.py +++ b/tests/variable_definitions/vardef_client/api/test_error_path.py @@ -9,6 +9,9 @@ from dapla_metadata.variable_definitions.generated.vardef_client.exceptions import ( OpenApiException, ) +from tests.utils.constants import BAD_REQUEST_STATUS +from tests.utils.constants import CONSTRAINT_VIOLATION_BODY +from tests.utils.constants import NOT_FOUND_STATUS def test_not_found_variable(api_client): @@ -23,7 +26,7 @@ def test_not_found_variable(api_client): assert result["detail"] == "Variable with ID invalid id not found" -def test_test(api_client): +def test_not_found_response(api_client): api_instance = vardef_client.VariableDefinitionsApi(api_client) with pytest.raises( OpenApiException, @@ -32,3 +35,38 @@ def test_test(api_client): api_instance.get_variable_definition_by_id("invalid id") exception = VardefClientException(e.value.body) assert str(exception) == "Status 404: Variable with ID invalid id not found" + + +def test_valid_response_body(): + response_body = '{"status": 400, "detail": "Bad Request"}' + exc = VardefClientException(response_body) + assert exc.status == BAD_REQUEST_STATUS + assert exc.detail == "Bad Request" + assert str(exc) == "Status 400: Bad Request" + + +def test_invalid_json(): + response_body = "Not a JSON string" + exc = VardefClientException(response_body) + assert exc.status == "Unknown" + assert exc.detail == "Invalid response body" + assert str(exc) == "Status Unknown: Invalid response body" + + +def test_missing_keys(): + response_body = '{"status": 404}' + exc = VardefClientException(response_body) + assert exc.status == NOT_FOUND_STATUS + assert exc.detail == "No detail provided" + assert str(exc) == "Status 404: No detail provided" + + +def test_constraint_violation(): + response_body = CONSTRAINT_VIOLATION_BODY + exc = VardefClientException(response_body) + assert exc.status == BAD_REQUEST_STATUS + assert exc.detail[0]["Message"] == "Invalid Dapla team" + assert ( + str(exc) + == "Status 400: [{'Field': 'updateVariableDefinitionById.updateDraft.owner.team', 'Message': 'Invalid Dapla team'}, {'Field': 'updateVariableDefinitionById.updateDraft.owner.team', 'Message': 'must not be empty'}]" + ) From f002d1344d52765b619c8064482b36ba587ec0f7 Mon Sep 17 00:00:00 2001 From: Cecilie Seim <68303562+tilen1976@users.noreply.github.com> Date: Tue, 10 Dec 2024 22:06:42 +0100 Subject: [PATCH 16/31] update open api file --- .../resources/variable-definitions-0.1.yml | 116 +++++++++++------- 1 file changed, 75 insertions(+), 41 deletions(-) diff --git a/tests/variable_definitions/resources/variable-definitions-0.1.yml b/tests/variable_definitions/resources/variable-definitions-0.1.yml index fa05dfd..13f605d 100644 --- a/tests/variable_definitions/resources/variable-definitions-0.1.yml +++ b/tests/variable_definitions/resources/variable-definitions-0.1.yml @@ -87,7 +87,7 @@ paths: format: yyyy-MM-dd nullable: true examples: - Not specified: + Date not specified: value: "" Specific date: value: 1970-01-01 @@ -101,7 +101,7 @@ paths: items: $ref: "#/components/schemas/RenderedVariableDefinition" examples: - List of one variable definition: + Specific date: value: |- [ { @@ -141,7 +141,7 @@ paths: } ] - Empty list: + Date not specified: value: "[]" /public/variable-definitions/{variable-definition-id}: get: @@ -167,7 +167,9 @@ paths: schema: $ref: "#/components/schemas/SupportedLanguages" examples: - No date specified: + Date not specified: + value: nb + Specific date: value: nb - name: date_of_validity in: query @@ -177,7 +179,7 @@ paths: format: date nullable: true examples: - No date specified: + Date not specified: value: "" Specific date: value: 1970-01-01 @@ -189,7 +191,39 @@ paths: schema: $ref: "#/components/schemas/RenderedVariableDefinition" examples: - No date specified: + Date not specified: + value: + id: wypvb3wd + name: Landbakgrunn + short_name: string + definition: "For personer født i utlandet, er dette (med noen\ + \ få unntak) eget fødeland. For personer født i Norge er det\ + \ foreldrenes fødeland. I de tilfeller der foreldrene har ulikt\ + \ fødeland, er det morens fødeland som blir valgt. Hvis ikke\ + \ personen selv eller noen av foreldrene er utenlandsfødt, hentes\ + \ landbakgrunn fra de første utenlandsfødte en treffer på i\ + \ rekkefølgen mormor, morfar, farmor eller farfar." + classification_uri: https://www.ssb.no/en/klass/klassifikasjoner/91 + unit_types: + - reference_uri: https://www.ssb.no/klass/klassifikasjoner/702 + code: "20" + title: Person + subject_fields: + - reference_uri: https://www.ssb.no/klass/klassifikasjoner/618 + code: be07 + title: Innvandrere + contains_sensitive_personal_information: true + valid_from: 2003-01-01 + external_reference_uri: https://www.ssb.no/a/metadata/conceptvariable/vardok/1919/nb + comment: Fra og med 1.1.2003 ble definisjon endret til også å + trekke inn besteforeldrenes fødeland. + related_variable_definition_uris: + - https://example.com/ + contact: + title: Seksjon for befolkningsstatistikk + email: s320@ssb.no + last_updated_at: 2024-06-12T10:39:41.038Z + Specific date: value: id: wypvb3wd name: Landbakgrunn @@ -240,7 +274,7 @@ paths: schema: type: string examples: - one_validity_period: + Validity periods: value: wypvb3wd - name: Accept-Language in: header @@ -249,7 +283,7 @@ paths: schema: $ref: "#/components/schemas/SupportedLanguages" examples: - one_validity_period: + Validity periods: value: nb responses: "200": @@ -261,7 +295,7 @@ paths: items: $ref: "#/components/schemas/RenderedVariableDefinition" examples: - one_validity_period: + Validity periods: value: |- [ { @@ -316,7 +350,7 @@ paths: schema: type: string examples: - migrate_1607: + Migrate Vardok: value: "1607" - name: active_group in: query @@ -325,7 +359,7 @@ paths: schema: type: string examples: - migrate_1607: + Migrate Vardok: value: dapla-felles-developers responses: "201": @@ -335,7 +369,7 @@ paths: schema: $ref: "#/components/schemas/CompleteResponse" examples: - migrate_1607: + Migrate Vardok: value: id: wypvb3wd patch_id: 1 @@ -422,10 +456,10 @@ paths: description: List only variable definitions which are valid on this date. schema: type: string - format: yyyy-MM-dd + format: date nullable: true examples: - Not specified: + Date not specified: value: "" Specific date: value: 1970-01-01 @@ -439,7 +473,7 @@ paths: items: $ref: "#/components/schemas/CompleteResponse" examples: - List of one variable definition: + Specific date: value: |- [ { @@ -500,7 +534,7 @@ paths: } ] - Empty list: + Date not specified: value: "[]" security: - keycloak_token: [] @@ -520,7 +554,7 @@ paths: schema: type: string examples: - create_draft: + Create draft: value: dapla-felles-developers requestBody: content: @@ -536,7 +570,7 @@ paths: schema: $ref: "#/components/schemas/CompleteResponse" examples: - create_draft: + Create draft: value: id: wypvb3wd patch_id: 1 @@ -628,7 +662,7 @@ paths: schema: type: string examples: - No date specified: + Date not specified: value: wypvb3wd Specific date: value: wypvb3wd @@ -642,7 +676,7 @@ paths: format: date nullable: true examples: - No date specified: + Date not specified: value: "" Specific date: value: 1970-01-01 @@ -654,7 +688,7 @@ paths: schema: $ref: "#/components/schemas/CompleteResponse" examples: - No date specified: + Date not specified: value: id: wypvb3wd patch_id: 1 @@ -823,7 +857,7 @@ paths: schema: type: string examples: - delete: + Delete: value: wypvb3wd - name: active_group in: query @@ -832,7 +866,7 @@ paths: schema: type: string examples: - delete: + Delete: value: dapla-felles-developers responses: "204": @@ -840,7 +874,7 @@ paths: content: application/json: examples: - delete: + Delete: value: "" "404": description: No such variable definition found @@ -865,7 +899,7 @@ paths: type: string description: Unique identifier for the variable definition. examples: - update: + Update: value: wypvb3wd - name: active_group in: query @@ -874,7 +908,7 @@ paths: schema: type: string examples: - update: + Update: value: dapla-felles-developers requestBody: content: @@ -890,7 +924,7 @@ paths: schema: $ref: "#/components/schemas/CompleteResponse" examples: - update: + Update: value: id: wypvb3wd patch_id: 1 @@ -990,7 +1024,7 @@ paths: schema: type: string examples: - one_patch: + Patches: value: wypvb3wd responses: "404": @@ -1004,7 +1038,7 @@ paths: items: $ref: "#/components/schemas/CompleteResponse" examples: - one_patch: + Patches: value: |- [ { @@ -1081,7 +1115,7 @@ paths: schema: type: string examples: - create_patch: + Create patch: value: wypvb3wd - name: valid_from in: query @@ -1091,7 +1125,7 @@ paths: format: date nullable: true examples: - create_patch: + Create patch: value: 1970-01-01 - name: active_group in: query @@ -1100,7 +1134,7 @@ paths: schema: type: string examples: - create_patch: + Create patch: value: dapla-felles-developers requestBody: content: @@ -1116,7 +1150,7 @@ paths: schema: $ref: "#/components/schemas/CompleteResponse" examples: - create_patch: + Create patch: value: id: wypvb3wd patch_id: 1 @@ -1208,7 +1242,7 @@ paths: schema: type: string examples: - patch_1: + Patch: value: wypvb3wd - name: patch-id in: path @@ -1218,7 +1252,7 @@ paths: type: integer format: int32 examples: - patch_1: + Patch: value: "1" responses: "200": @@ -1228,7 +1262,7 @@ paths: schema: $ref: "#/components/schemas/CompleteResponse" examples: - patch_1: + Patch: value: id: wypvb3wd patch_id: 1 @@ -1317,7 +1351,7 @@ paths: schema: type: string examples: - one_validity_period: + Validity periods: value: wypvb3wd responses: "200": @@ -1329,7 +1363,7 @@ paths: items: $ref: "#/components/schemas/CompleteResponse" examples: - one_validity_period: + Validity periods: value: |- [ { @@ -1406,7 +1440,7 @@ paths: schema: type: string examples: - create_validity_period: + Create validity period: value: wypvb3wd - name: active_group in: query @@ -1415,7 +1449,7 @@ paths: schema: type: string examples: - create_validity_period: + Create validity period: value: dapla-felles-developers requestBody: content: @@ -1431,7 +1465,7 @@ paths: schema: $ref: "#/components/schemas/CompleteResponse" examples: - create_validity_period: + Create validity period: value: id: wypvb3wd patch_id: 1 From f0a26a38657b1067f925b426cf82fd0e985172a7 Mon Sep 17 00:00:00 2001 From: Cecilie Seim <68303562+tilen1976@users.noreply.github.com> Date: Wed, 11 Dec 2024 09:07:45 +0100 Subject: [PATCH 17/31] update open api file --- .../variable_definitions/resources/variable-definitions-0.1.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/variable_definitions/resources/variable-definitions-0.1.yml b/tests/variable_definitions/resources/variable-definitions-0.1.yml index 13f605d..c55704f 100644 --- a/tests/variable_definitions/resources/variable-definitions-0.1.yml +++ b/tests/variable_definitions/resources/variable-definitions-0.1.yml @@ -459,8 +459,6 @@ paths: format: date nullable: true examples: - Date not specified: - value: "" Specific date: value: 1970-01-01 responses: From 01462a4e259965fe586cdbadd9124f6985098f0d Mon Sep 17 00:00:00 2001 From: Cecilie Seim <68303562+tilen1976@users.noreply.github.com> Date: Wed, 11 Dec 2024 09:43:36 +0100 Subject: [PATCH 18/31] add tests for constraint violations --- tests/utils/constants.py | 54 ++++++++++++++ .../vardef_client/api/test_error_path.py | 38 ---------- .../vardef_client/test_exceptions.py | 70 +++++++++++++++++++ 3 files changed, 124 insertions(+), 38 deletions(-) create mode 100644 tests/variable_definitions/vardef_client/test_exceptions.py diff --git a/tests/utils/constants.py b/tests/utils/constants.py index c485742..71f3b3b 100644 --- a/tests/utils/constants.py +++ b/tests/utils/constants.py @@ -29,3 +29,57 @@ } ] }""" +CONSTRAINT_VIOLATION_BODY_MISSING_MESSAGES = """{ + "cause": null, + "suppressed": [ + ], + "detail": null, + "instance": null, + "parameters": { + }, + "type": "https://zalando.github.io/problem/constraint-violation", + "title": "Constraint Violation", + "status": 400, + "violations": [ + { + "field": "updateVariableDefinitionById.updateDraft.owner.team" + }, + { + "field": "updateVariableDefinitionById.updateDraft.owner.team" + } + ] +}""" + +CONSTRAINT_VIOLATION_BODY_MISSING_VIOLATIONS = """{ + "cause": null, + "suppressed": [ + ], + "detail": null, + "instance": null, + "parameters": { + }, + "type": "https://zalando.github.io/problem/constraint-violation", + "title": "Constraint Violation", + "status": 400, + "violations": [] +}""" +CONSTRAINT_VIOLATION_BODY_MISSING_FIELD = """{ + "cause": null, + "suppressed": [ + ], + "detail": null, + "instance": null, + "parameters": { + }, + "type": "https://zalando.github.io/problem/constraint-violation", + "title": "Constraint Violation", + "status": 400, + "violations": [ + { + "message": "Invalid Dapla team" + }, + { + "message": "must not be empty" + } + ] +}""" diff --git a/tests/variable_definitions/vardef_client/api/test_error_path.py b/tests/variable_definitions/vardef_client/api/test_error_path.py index a997fee..ab372d1 100644 --- a/tests/variable_definitions/vardef_client/api/test_error_path.py +++ b/tests/variable_definitions/vardef_client/api/test_error_path.py @@ -9,9 +9,6 @@ from dapla_metadata.variable_definitions.generated.vardef_client.exceptions import ( OpenApiException, ) -from tests.utils.constants import BAD_REQUEST_STATUS -from tests.utils.constants import CONSTRAINT_VIOLATION_BODY -from tests.utils.constants import NOT_FOUND_STATUS def test_not_found_variable(api_client): @@ -35,38 +32,3 @@ def test_not_found_response(api_client): api_instance.get_variable_definition_by_id("invalid id") exception = VardefClientException(e.value.body) assert str(exception) == "Status 404: Variable with ID invalid id not found" - - -def test_valid_response_body(): - response_body = '{"status": 400, "detail": "Bad Request"}' - exc = VardefClientException(response_body) - assert exc.status == BAD_REQUEST_STATUS - assert exc.detail == "Bad Request" - assert str(exc) == "Status 400: Bad Request" - - -def test_invalid_json(): - response_body = "Not a JSON string" - exc = VardefClientException(response_body) - assert exc.status == "Unknown" - assert exc.detail == "Invalid response body" - assert str(exc) == "Status Unknown: Invalid response body" - - -def test_missing_keys(): - response_body = '{"status": 404}' - exc = VardefClientException(response_body) - assert exc.status == NOT_FOUND_STATUS - assert exc.detail == "No detail provided" - assert str(exc) == "Status 404: No detail provided" - - -def test_constraint_violation(): - response_body = CONSTRAINT_VIOLATION_BODY - exc = VardefClientException(response_body) - assert exc.status == BAD_REQUEST_STATUS - assert exc.detail[0]["Message"] == "Invalid Dapla team" - assert ( - str(exc) - == "Status 400: [{'Field': 'updateVariableDefinitionById.updateDraft.owner.team', 'Message': 'Invalid Dapla team'}, {'Field': 'updateVariableDefinitionById.updateDraft.owner.team', 'Message': 'must not be empty'}]" - ) diff --git a/tests/variable_definitions/vardef_client/test_exceptions.py b/tests/variable_definitions/vardef_client/test_exceptions.py new file mode 100644 index 0000000..602459b --- /dev/null +++ b/tests/variable_definitions/vardef_client/test_exceptions.py @@ -0,0 +1,70 @@ +"""Tests for Vardef client exception handling.""" + +from dapla_metadata.variable_definitions.exceptions import VardefClientException +from tests.utils.constants import BAD_REQUEST_STATUS +from tests.utils.constants import CONSTRAINT_VIOLATION_BODY +from tests.utils.constants import CONSTRAINT_VIOLATION_BODY_MISSING_FIELD +from tests.utils.constants import CONSTRAINT_VIOLATION_BODY_MISSING_MESSAGES +from tests.utils.constants import CONSTRAINT_VIOLATION_BODY_MISSING_VIOLATIONS +from tests.utils.constants import NOT_FOUND_STATUS + + +def test_valid_response_body(): + response_body = '{"status": 400, "detail": "Bad Request"}' + exc = VardefClientException(response_body) + assert exc.status == BAD_REQUEST_STATUS + assert exc.detail == "Bad Request" + assert str(exc) == "Status 400: Bad Request" + + +def test_invalid_json(): + response_body = "Not a JSON string" + exc = VardefClientException(response_body) + assert exc.status == "Unknown" + assert exc.detail == "Invalid response body" + assert str(exc) == "Status Unknown: Invalid response body" + + +def test_missing_keys(): + response_body = '{"status": 404}' + exc = VardefClientException(response_body) + assert exc.status == NOT_FOUND_STATUS + assert exc.detail == "No detail provided" + assert str(exc) == "Status 404: No detail provided" + + +def test_constraint_violation(): + response_body = CONSTRAINT_VIOLATION_BODY + exc = VardefClientException(response_body) + assert exc.status == BAD_REQUEST_STATUS + assert exc.detail[0]["Message"] == "Invalid Dapla team" + assert ( + str(exc) == "Status 400: [" + "{'Field': 'updateVariableDefinitionById.updateDraft.owner.team', 'Message': 'Invalid Dapla team'}, " + "{'Field': 'updateVariableDefinitionById.updateDraft.owner.team', 'Message': 'must not be empty'}]" + ) + + +def test_constraint_violation_missing_messages(): + response_body = CONSTRAINT_VIOLATION_BODY_MISSING_MESSAGES + exc = VardefClientException(response_body) + assert exc.status == BAD_REQUEST_STATUS + assert exc.detail[0]["Message"] == "No message provided" + + +def test_constraint_violation_empty_violations(): + response_body = CONSTRAINT_VIOLATION_BODY_MISSING_VIOLATIONS + exc = VardefClientException(response_body) + assert exc.status == BAD_REQUEST_STATUS + assert str(exc) == "Status 400: []" + + +def test_constraint_violation_empty_field(): + response_body = CONSTRAINT_VIOLATION_BODY_MISSING_FIELD + exc = VardefClientException(response_body) + assert exc.status == BAD_REQUEST_STATUS + assert str(exc) == ( + "Status 400: [" + "{'Field': 'Unknown field', 'Message': 'Invalid Dapla team'}, " + "{'Field': 'Unknown field', 'Message': 'must not be empty'}]" + ) From 1c32418d8df6ee8f9abb5c61283c7d8819e85384 Mon Sep 17 00:00:00 2001 From: Cecilie Seim <68303562+tilen1976@users.noreply.github.com> Date: Wed, 11 Dec 2024 09:53:22 +0100 Subject: [PATCH 19/31] add tests for missing elements --- .../variable_definitions/exceptions.py | 2 +- .../vardef_client/test_exceptions.py | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/dapla_metadata/variable_definitions/exceptions.py b/src/dapla_metadata/variable_definitions/exceptions.py index aba4afd..95eea81 100644 --- a/src/dapla_metadata/variable_definitions/exceptions.py +++ b/src/dapla_metadata/variable_definitions/exceptions.py @@ -45,7 +45,7 @@ def __init__(self, response_body: str) -> None: self.detail = self._format_violations(violations) else: self.detail = data.get("detail", "No detail provided") - self.response_body = response_body # Save the full response for debugging + self.response_body = response_body except (json.JSONDecodeError, TypeError): self.status = "Unknown" self.detail = "Invalid response body" diff --git a/tests/variable_definitions/vardef_client/test_exceptions.py b/tests/variable_definitions/vardef_client/test_exceptions.py index 602459b..bcb6ddf 100644 --- a/tests/variable_definitions/vardef_client/test_exceptions.py +++ b/tests/variable_definitions/vardef_client/test_exceptions.py @@ -17,6 +17,19 @@ def test_valid_response_body(): assert str(exc) == "Status 400: Bad Request" +def test_respons_empty_status(): + response_body = '{"status": , "detail": "Bad Request"}' + exc = VardefClientException(response_body) + assert exc.status == "Unknown" + + +def tests_no_status(): + response_body = '{"detail": "Bad Request"}' + exc = VardefClientException(response_body) + assert exc.status == "Unknown status" + assert exc.detail == "Bad Request" + + def test_invalid_json(): response_body = "Not a JSON string" exc = VardefClientException(response_body) From 65424a54002fbd4e0413b9d5bc83bd3894931467 Mon Sep 17 00:00:00 2001 From: Cecilie Seim <68303562+tilen1976@users.noreply.github.com> Date: Wed, 11 Dec 2024 13:58:39 +0100 Subject: [PATCH 20/31] update open api file with new excptions --- .../resources/variable-definitions-0.1.yml | 270 ++++++++++++++++-- 1 file changed, 247 insertions(+), 23 deletions(-) diff --git a/tests/variable_definitions/resources/variable-definitions-0.1.yml b/tests/variable_definitions/resources/variable-definitions-0.1.yml index c55704f..3690418 100644 --- a/tests/variable_definitions/resources/variable-definitions-0.1.yml +++ b/tests/variable_definitions/resources/variable-definitions-0.1.yml @@ -84,7 +84,7 @@ paths: description: List only variable definitions which are valid on this date. schema: type: string - format: yyyy-MM-dd + format: date nullable: true examples: Date not specified: @@ -256,7 +256,18 @@ paths: email: s320@ssb.no last_updated_at: 2024-06-12T10:39:41.038Z "404": - description: No such variable definition found + description: Not found + content: + application/problem+json: + schema: + $ref: https://opensource.zalando.com/restful-api-guidelines/models/problem-1.0.1.yaml#/Problem + examples: + Not found: + value: + type: about:blank + status: 404 + detail: Not found + parameters: {} /public/variable-definitions/{variable-definition-id}/validity-periods: get: tags: @@ -440,7 +451,30 @@ paths: code: ano@ssb.no name: Ola Nordmann "400": - description: The definition in Vardok has missing or malformed metadata. + description: Bad request. + content: + application/problem+json: + schema: + $ref: https://opensource.zalando.com/restful-api-guidelines/models/problem-1.0.1.yaml#/Problem + examples: + Bad request: + value: + type: about:blank + status: 400 + detail: "Failed to convert argument [example] for value [null]\ + \ due to: Error deserializing type: example" + Constraint violation: + value: + suppressed: [] + parameters: {} + type: https://zalando.github.io/problem/constraint-violation + title: Constraint Violation + status: 400 + violations: + - field: myField + message: Invalid format + - field: otherField + message: must not be empty security: - keycloak_token: [] /variable-definitions: @@ -639,10 +673,42 @@ paths: code: ano@ssb.no name: Ola Nordmann "400": - description: "Malformed data, missing data or attempt to specify disallowed\ - \ fields." + description: Bad request. + content: + application/problem+json: + schema: + $ref: https://opensource.zalando.com/restful-api-guidelines/models/problem-1.0.1.yaml#/Problem + examples: + Bad request: + value: + type: about:blank + status: 400 + detail: "Failed to convert argument [example] for value [null]\ + \ due to: Error deserializing type: example" + Constraint violation: + value: + suppressed: [] + parameters: {} + type: https://zalando.github.io/problem/constraint-violation + title: Constraint Violation + status: 400 + violations: + - field: myField + message: Invalid format + - field: otherField + message: must not be empty "409": description: Short name is already in use by another variable definition. + content: + application/problem+json: + schema: + $ref: https://opensource.zalando.com/restful-api-guidelines/models/problem-1.0.1.yaml#/Problem + examples: + Conflict: + value: + type: about:blank + status: 409 + detail: Short name is already in use by another variable definition. security: - keycloak_token: [] /variable-definitions/{variable-definition-id}: @@ -827,7 +893,7 @@ paths: code: ano@ssb.no name: Ola Nordmann "404": - description: No such variable definition found + description: Not found content: application/problem+json: schema: @@ -837,7 +903,7 @@ paths: value: type: about:blank status: 404 - detail: Variable with ID invalid id not found + detail: Not found parameters: {} security: - keycloak_token: [] @@ -875,10 +941,30 @@ paths: Delete: value: "" "404": - description: No such variable definition found + description: Not found + content: + application/problem+json: + schema: + $ref: https://opensource.zalando.com/restful-api-guidelines/models/problem-1.0.1.yaml#/Problem + examples: + Not found: + value: + type: about:blank + status: 404 + detail: Not found + parameters: {} "405": - description: Attempt to delete a variable definition with status unlike - DRAFT. + description: Not allowed for variable definitions with this status. + content: + application/problem+json: + schema: + $ref: https://opensource.zalando.com/restful-api-guidelines/models/problem-1.0.1.yaml#/Problem + examples: + Method not allowed: + value: + type: about:blank + status: 405 + detail: Not allowed for variable definitions with this status. security: - keycloak_token: [] patch: @@ -993,17 +1079,67 @@ paths: code: ano@ssb.no name: Ola Nordmann "400": - description: |- - Bad request. Examples of these are: - - Reference to a Klass classification which doesn't exist. - - Owner information missing. - - Malformed email addresses. + description: Bad request. + content: + application/problem+json: + schema: + $ref: https://opensource.zalando.com/restful-api-guidelines/models/problem-1.0.1.yaml#/Problem + examples: + Bad request: + value: + type: about:blank + status: 400 + detail: "Failed to convert argument [example] for value [null]\ + \ due to: Error deserializing type: example" + Constraint violation: + value: + suppressed: [] + parameters: {} + type: https://zalando.github.io/problem/constraint-violation + title: Constraint Violation + status: 400 + violations: + - field: myField + message: Invalid format + - field: otherField + message: must not be empty "404": - description: No such variable definition found + description: Not found + content: + application/problem+json: + schema: + $ref: https://opensource.zalando.com/restful-api-guidelines/models/problem-1.0.1.yaml#/Problem + examples: + Not found: + value: + type: about:blank + status: 404 + detail: Not found + parameters: {} "405": - description: Attempt to patch a variable definition with status unlike DRAFT. + description: Not allowed for variable definitions with this status. + content: + application/problem+json: + schema: + $ref: https://opensource.zalando.com/restful-api-guidelines/models/problem-1.0.1.yaml#/Problem + examples: + Method not allowed: + value: + type: about:blank + status: 405 + detail: Not allowed for variable definitions with this status. "409": description: Short name is already in use by another variable definition. + content: + application/problem+json: + schema: + $ref: https://opensource.zalando.com/restful-api-guidelines/models/problem-1.0.1.yaml#/Problem + examples: + Conflict: + value: + type: about:blank + status: 409 + detail: Short name is already in use by another variable definition. security: - keycloak_token: [] /variable-definitions/{variable-definition-id}/patches: @@ -1025,8 +1161,6 @@ paths: Patches: value: wypvb3wd responses: - "404": - description: No such variable definition found "200": description: Ok content: @@ -1097,6 +1231,19 @@ paths: } ] + "404": + description: Not found + content: + application/problem+json: + schema: + $ref: https://opensource.zalando.com/restful-api-guidelines/models/problem-1.0.1.yaml#/Problem + examples: + Not found: + value: + type: about:blank + status: 404 + detail: Not found + parameters: {} security: - keycloak_token: [] post: @@ -1220,8 +1367,41 @@ paths: name: Ola Nordmann "400": description: Bad request. + content: + application/problem+json: + schema: + $ref: https://opensource.zalando.com/restful-api-guidelines/models/problem-1.0.1.yaml#/Problem + examples: + Bad request: + value: + type: about:blank + status: 400 + detail: "Failed to convert argument [example] for value [null]\ + \ due to: Error deserializing type: example" + Constraint violation: + value: + suppressed: [] + parameters: {} + type: https://zalando.github.io/problem/constraint-violation + title: Constraint Violation + status: 400 + violations: + - field: myField + message: Invalid format + - field: otherField + message: must not be empty "405": - description: Method only allowed for published variables. + description: Not allowed for variable definitions with this status. + content: + application/problem+json: + schema: + $ref: https://opensource.zalando.com/restful-api-guidelines/models/problem-1.0.1.yaml#/Problem + examples: + Method not allowed: + value: + type: about:blank + status: 405 + detail: Not allowed for variable definitions with this status. security: - keycloak_token: [] /variable-definitions/{variable-definition-id}/patches/{patch-id}: @@ -1331,7 +1511,18 @@ paths: code: ano@ssb.no name: Ola Nordmann "404": - description: No such variable definition found + description: Not found + content: + application/problem+json: + schema: + $ref: https://opensource.zalando.com/restful-api-guidelines/models/problem-1.0.1.yaml#/Problem + examples: + Not found: + value: + type: about:blank + status: 404 + detail: Not found + parameters: {} security: - keycloak_token: [] /variable-definitions/{variable-definition-id}/validity-periods: @@ -1534,9 +1725,42 @@ paths: code: ano@ssb.no name: Ola Nordmann "400": - description: The request is missing or has errors in required fields. + description: Bad request. + content: + application/problem+json: + schema: + $ref: https://opensource.zalando.com/restful-api-guidelines/models/problem-1.0.1.yaml#/Problem + examples: + Bad request: + value: + type: about:blank + status: 400 + detail: "Failed to convert argument [example] for value [null]\ + \ due to: Error deserializing type: example" + Constraint violation: + value: + suppressed: [] + parameters: {} + type: https://zalando.github.io/problem/constraint-violation + title: Constraint Violation + status: 400 + violations: + - field: myField + message: Invalid format + - field: otherField + message: must not be empty "405": - description: Method only allowed for published variables. + description: Not allowed for variable definitions with this status. + content: + application/problem+json: + schema: + $ref: https://opensource.zalando.com/restful-api-guidelines/models/problem-1.0.1.yaml#/Problem + examples: + Method not allowed: + value: + type: about:blank + status: 405 + detail: Not allowed for variable definitions with this status. security: - keycloak_token: [] components: From ba6e735ef0a19c78f11396e8114c80c9504c89b2 Mon Sep 17 00:00:00 2001 From: Cecilie Seim <68303562+tilen1976@users.noreply.github.com> Date: Wed, 11 Dec 2024 13:59:04 +0100 Subject: [PATCH 21/31] update tests with new exception structure --- .../vardef_client/api/test_error_path.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/variable_definitions/vardef_client/api/test_error_path.py b/tests/variable_definitions/vardef_client/api/test_error_path.py index ab372d1..67bd261 100644 --- a/tests/variable_definitions/vardef_client/api/test_error_path.py +++ b/tests/variable_definitions/vardef_client/api/test_error_path.py @@ -15,20 +15,20 @@ def test_not_found_variable(api_client): api_instance = vardef_client.VariableDefinitionsApi(api_client) with pytest.raises( OpenApiException, - match="Variable with ID invalid id not found", + match="Not found", ) as exc_info: api_instance.get_variable_definition_by_id("invalid id") response = exc_info.value.body result = json.loads(response) - assert result["detail"] == "Variable with ID invalid id not found" + assert result["detail"] == "Not found" def test_not_found_response(api_client): api_instance = vardef_client.VariableDefinitionsApi(api_client) with pytest.raises( OpenApiException, - match="Variable with ID invalid id not found", + match="Not found", ) as e: api_instance.get_variable_definition_by_id("invalid id") exception = VardefClientException(e.value.body) - assert str(exception) == "Status 404: Variable with ID invalid id not found" + assert str(exception) == "Status 404: Not found" From 3908cc3042a862b33a964040c6d26f6143328db0 Mon Sep 17 00:00:00 2001 From: Cecilie Seim <68303562+tilen1976@users.noreply.github.com> Date: Thu, 12 Dec 2024 12:01:40 +0100 Subject: [PATCH 22/31] start of decorator for vardef client exception handling --- .../variable_definitions/exceptions.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/dapla_metadata/variable_definitions/exceptions.py b/src/dapla_metadata/variable_definitions/exceptions.py index 95eea81..adb2443 100644 --- a/src/dapla_metadata/variable_definitions/exceptions.py +++ b/src/dapla_metadata/variable_definitions/exceptions.py @@ -68,3 +68,15 @@ def _format_violations(self, violations: list) -> list: } for violation in violations ] + + +def vardef_exception_handler(func): # noqa: ANN001, ANN201 + """Decorator function for handling api exceptions.""" + + def inner_function(*args, **kwargs): # noqa: ANN002, ANN003 + try: + return func(*args, **kwargs) + except OpenApiException as e: + raise VardefClientException(e.body) from e + + return inner_function From 6045bb82d076dad6263887fdaa4f255e8109815b Mon Sep 17 00:00:00 2001 From: Cecilie Seim <68303562+tilen1976@users.noreply.github.com> Date: Thu, 12 Dec 2024 13:03:13 +0100 Subject: [PATCH 23/31] decorator ok --- .../variable_definitions/exceptions.py | 12 ++++++------ src/dapla_metadata/variable_definitions/vardef.py | 3 +++ tests/variable_definitions/test_vardef.py | 15 +++++++++++++++ 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/dapla_metadata/variable_definitions/exceptions.py b/src/dapla_metadata/variable_definitions/exceptions.py index adb2443..8d4f71c 100644 --- a/src/dapla_metadata/variable_definitions/exceptions.py +++ b/src/dapla_metadata/variable_definitions/exceptions.py @@ -1,6 +1,7 @@ """Vardef client exceptions.""" import json +from functools import wraps from dapla_metadata.variable_definitions.generated.vardef_client.exceptions import ( OpenApiException, @@ -70,13 +71,12 @@ def _format_violations(self, violations: list) -> list: ] -def vardef_exception_handler(func): # noqa: ANN001, ANN201 - """Decorator function for handling api exceptions.""" - - def inner_function(*args, **kwargs): # noqa: ANN002, ANN003 +def vardef_exception_handler(method): + @wraps(method) + def _impl(self, *method_args, **method_kwargs): try: - return func(*args, **kwargs) + return method(self, *method_args, **method_kwargs) except OpenApiException as e: raise VardefClientException(e.body) from e - return inner_function + return _impl diff --git a/src/dapla_metadata/variable_definitions/vardef.py b/src/dapla_metadata/variable_definitions/vardef.py index 5d34b80..7271a06 100644 --- a/src/dapla_metadata/variable_definitions/vardef.py +++ b/src/dapla_metadata/variable_definitions/vardef.py @@ -1,6 +1,7 @@ from datetime import date from dapla_metadata.variable_definitions._client import VardefClient +from dapla_metadata.variable_definitions.exceptions import vardef_exception_handler from dapla_metadata.variable_definitions.generated.vardef_client.api.variable_definitions_api import ( VariableDefinitionsApi, ) @@ -68,6 +69,7 @@ class Vardef: """ @classmethod + @vardef_exception_handler def list_variable_definitions( cls, date_of_validity: date | None = None, @@ -96,6 +98,7 @@ def list_variable_definitions( ] @classmethod + @vardef_exception_handler def get_variable_definition( cls, variable_definition_id: str, diff --git a/tests/variable_definitions/test_vardef.py b/tests/variable_definitions/test_vardef.py index a8f66b3..395b5b9 100644 --- a/tests/variable_definitions/test_vardef.py +++ b/tests/variable_definitions/test_vardef.py @@ -1,4 +1,7 @@ +import pytest + from dapla_metadata.variable_definitions._client import VardefClient +from dapla_metadata.variable_definitions.exceptions import VardefClientException from dapla_metadata.variable_definitions.generated.vardef_client.configuration import ( Configuration, ) @@ -7,6 +10,7 @@ ) from dapla_metadata.variable_definitions.vardef import Vardef from dapla_metadata.variable_definitions.variable_definition import VariableDefinition +from tests.utils.constants import NOT_FOUND_STATUS from tests.utils.constants import VARDEF_EXAMPLE_DATE from tests.utils.constants import VARDEF_EXAMPLE_DEFINITION_ID @@ -35,6 +39,17 @@ def test_get_variable_definition(client_configuration: Configuration): assert landbak.classification_reference == "91" +def test_get_variable_definition_exception(client_configuration: Configuration): + VardefClient.set_config(client_configuration) + with pytest.raises(VardefClientException) as e: + Vardef.get_variable_definition( + variable_definition_id="invalid id", + ) + assert e.value.status == NOT_FOUND_STATUS + assert e.value.detail == "Not found" + assert str(e.value) == "Status 404: Not found" + + def test_list_patches(client_configuration: Configuration): VardefClient.set_config(client_configuration) landbak = Vardef.get_variable_definition( From 1504eee0f43a6ac4ec0c27e4ad5d448ec4a42192 Mon Sep 17 00:00:00 2001 From: Cecilie Seim <68303562+tilen1976@users.noreply.github.com> Date: Thu, 12 Dec 2024 13:05:49 +0100 Subject: [PATCH 24/31] set temp type ignore --- src/dapla_metadata/variable_definitions/exceptions.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/dapla_metadata/variable_definitions/exceptions.py b/src/dapla_metadata/variable_definitions/exceptions.py index 8d4f71c..826fed5 100644 --- a/src/dapla_metadata/variable_definitions/exceptions.py +++ b/src/dapla_metadata/variable_definitions/exceptions.py @@ -71,9 +71,11 @@ def _format_violations(self, violations: list) -> list: ] -def vardef_exception_handler(method): +def vardef_exception_handler(method): # noqa: ANN201, ANN001 + """Decorator for handling exceptions in Vardef.""" + @wraps(method) - def _impl(self, *method_args, **method_kwargs): + def _impl(self, *method_args, **method_kwargs): # noqa: ANN001, ANN002, ANN003 try: return method(self, *method_args, **method_kwargs) except OpenApiException as e: From 180984ad13d4de8b0d6f0015d74da94d7e463703 Mon Sep 17 00:00:00 2001 From: Cecilie Seim <68303562+tilen1976@users.noreply.github.com> Date: Thu, 12 Dec 2024 14:05:51 +0100 Subject: [PATCH 25/31] rename test --- tests/variable_definitions/test_vardef.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/variable_definitions/test_vardef.py b/tests/variable_definitions/test_vardef.py index 395b5b9..21a9e1f 100644 --- a/tests/variable_definitions/test_vardef.py +++ b/tests/variable_definitions/test_vardef.py @@ -39,7 +39,7 @@ def test_get_variable_definition(client_configuration: Configuration): assert landbak.classification_reference == "91" -def test_get_variable_definition_exception(client_configuration: Configuration): +def test_get_variable_definition_invalid_id(client_configuration: Configuration): VardefClient.set_config(client_configuration) with pytest.raises(VardefClientException) as e: Vardef.get_variable_definition( From dc0c42dea989afd25c84d05f9704db6358165d04 Mon Sep 17 00:00:00 2001 From: Cecilie Seim <68303562+tilen1976@users.noreply.github.com> Date: Thu, 12 Dec 2024 15:09:46 +0100 Subject: [PATCH 26/31] Remove local testcontainer path --- poetry.lock | 93 ++++---------------------------------------------- pyproject.toml | 2 +- 2 files changed, 8 insertions(+), 87 deletions(-) diff --git a/poetry.lock b/poetry.lock index ec6656c..1c172f8 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1753,52 +1753,6 @@ files = [ {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, ] -[[package]] -name = "httpcore" -version = "1.0.7" -description = "A minimal low-level HTTP client." -optional = false -python-versions = ">=3.8" -files = [ - {file = "httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd"}, - {file = "httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c"}, -] - -[package.dependencies] -certifi = "*" -h11 = ">=0.13,<0.15" - -[package.extras] -asyncio = ["anyio (>=4.0,<5.0)"] -http2 = ["h2 (>=3,<5)"] -socks = ["socksio (==1.*)"] -trio = ["trio (>=0.22.0,<1.0)"] - -[[package]] -name = "httpx" -version = "0.27.2" -description = "The next generation HTTP client." -optional = false -python-versions = ">=3.8" -files = [ - {file = "httpx-0.27.2-py3-none-any.whl", hash = "sha256:7bb2708e112d8fdd7829cd4243970f0c223274051cb35ee80c03301ee29a3df0"}, - {file = "httpx-0.27.2.tar.gz", hash = "sha256:f7c2be1d2f3c3c3160d441802406b206c2b76f5947b11115e6df10c6c65e66c2"}, -] - -[package.dependencies] -anyio = "*" -certifi = "*" -httpcore = "==1.*" -idna = "*" -sniffio = "*" - -[package.extras] -brotli = ["brotli", "brotlicffi"] -cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] -http2 = ["h2 (>=3,<5)"] -socks = ["socksio (==1.*)"] -zstd = ["zstandard (>=0.18.0)"] - [[package]] name = "identify" version = "2.6.1" @@ -4165,24 +4119,6 @@ files = [ [package.dependencies] cffi = {version = "*", markers = "implementation_name == \"pypy\""} -[[package]] -name = "redis" -version = "5.2.0" -description = "Python client for Redis database and key-value store" -optional = false -python-versions = ">=3.8" -files = [ - {file = "redis-5.2.0-py3-none-any.whl", hash = "sha256:ae174f2bb3b1bf2b09d54bf3e51fbc1469cf6c10aa03e21141f51969801a7897"}, - {file = "redis-5.2.0.tar.gz", hash = "sha256:0b1087665a771b1ff2e003aa5bdd354f15a70c9e25d5a7dbf9c722c16528a7b0"}, -] - -[package.dependencies] -async-timeout = {version = ">=4.0.3", markers = "python_full_version < \"3.11.3\""} - -[package.extras] -hiredis = ["hiredis (>=3.0.0)"] -ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==23.2.1)", "requests (>=2.31.0)"] - [[package]] name = "referencing" version = "0.35.1" @@ -4896,18 +4832,18 @@ full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7 [[package]] name = "testcontainers" -version = "4.8.2" +version = "4.9.0" description = "Python library for throwaway instances of anything that can run in a Docker container" optional = false -python-versions = ">=3.9,<4.0" -files = [] -develop = true +python-versions = "<4.0,>=3.9" +files = [ + {file = "testcontainers-4.9.0-py3-none-any.whl", hash = "sha256:c6fee929990972c40bf6b91b7072c94064ff3649b405a14fde0274c8b2479d32"}, + {file = "testcontainers-4.9.0.tar.gz", hash = "sha256:2cd6af070109ff68c1ab5389dc89c86c2dc3ab30a21ca734b2cb8f0f80ad479e"}, +] [package.dependencies] docker = "*" -httpx = {version = "*", optional = true} python-dotenv = "*" -redis = {version = "*", optional = true} typing-extensions = "*" urllib3 = "*" wrapt = "*" @@ -4916,36 +4852,26 @@ wrapt = "*" arangodb = ["python-arango (>=7.8,<8.0)"] aws = ["boto3", "httpx"] azurite = ["azure-storage-blob (>=12.19,<13.0)"] -cassandra = [] chroma = ["chromadb-client"] clickhouse = ["clickhouse-driver"] -cockroachdb = [] cosmosdb = ["azure-cosmos"] db2 = ["ibm_db_sa", "sqlalchemy"] -elasticsearch = [] generic = ["httpx", "redis"] google = ["google-cloud-datastore (>=2)", "google-cloud-pubsub (>=2)"] influxdb = ["influxdb", "influxdb-client"] k3s = ["kubernetes", "pyyaml"] -kafka = [] keycloak = ["python-keycloak"] localstack = ["boto3"] mailpit = ["cryptography"] -memcached = [] -milvus = [] minio = ["minio"] mongodb = ["pymongo"] -mqtt = [] mssql = ["pymssql", "sqlalchemy"] mysql = ["pymysql[rsa]", "sqlalchemy"] nats = ["nats-py"] neo4j = ["neo4j"] -nginx = [] -ollama = [] opensearch = ["opensearch-py"] oracle = ["oracledb", "sqlalchemy"] oracle-free = ["oracledb", "sqlalchemy"] -postgres = [] qdrant = ["qdrant-client"] rabbitmq = ["pika"] redis = ["redis"] @@ -4955,13 +4881,8 @@ selenium = ["selenium"] sftp = ["cryptography"] test-module-import = ["httpx"] trino = ["trino"] -vault = [] weaviate = ["weaviate-client (>=4.5.4,<5.0.0)"] -[package.source] -type = "directory" -url = "../testcontainers-python" - [[package]] name = "toml" version = "0.10.2" @@ -5731,4 +5652,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">=3.10,<4.0" -content-hash = "41ff087038439b9d0a337e603812d942242b5d11093100228778d71c8f4a8356" +content-hash = "f4011623ffbb1013dc30e2c44af0cc60e1b2557ac0f0aa68b0ca80e5cc008904" diff --git a/pyproject.toml b/pyproject.toml index 428e74d..6788c1e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,6 +30,7 @@ pyjwt = ">=2.8.0" ssb-klass-python = ">=0.0.9" ssb-datadoc-model = "^6.0.0" typing-extensions = ">=4.12.2" +testcontainers = "^4.9.0" [tool.poetry.group.dev.dependencies] black = ">=24.8.0" @@ -63,7 +64,6 @@ types-beautifulsoup4 = ">=4.12.0.20240511" ipykernel = "^6.29.5" rich = "^13.9.4" bpython = "^0.24" -testcontainers = {path = "../testcontainers-python", extras = ["generic"], develop = true} [tool.pytest.ini_options] pythonpath = [ From 4e5cf3fe6eee1c79e69215c2583f5e08a096b9bf Mon Sep 17 00:00:00 2001 From: Cecilie Seim <68303562+tilen1976@users.noreply.github.com> Date: Thu, 12 Dec 2024 15:13:33 +0100 Subject: [PATCH 27/31] Update src/dapla_metadata/variable_definitions/exceptions.py Co-authored-by: Miles Mason Winther <42948872+mmwinther@users.noreply.github.com> --- src/dapla_metadata/variable_definitions/exceptions.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/dapla_metadata/variable_definitions/exceptions.py b/src/dapla_metadata/variable_definitions/exceptions.py index 826fed5..ef3eb83 100644 --- a/src/dapla_metadata/variable_definitions/exceptions.py +++ b/src/dapla_metadata/variable_definitions/exceptions.py @@ -33,9 +33,6 @@ def __init__(self, response_body: str) -> None: response_body (str): The raw response body string, stored for debugging purposes. - Raises: - None: The constructor handles invalid JSON and missing keys - gracefully, defaulting to error messages. """ self.detail: str | list try: From 56d527cc00c76c129a7cf33b14b5a867c0fe8b4f Mon Sep 17 00:00:00 2001 From: Cecilie Seim <68303562+tilen1976@users.noreply.github.com> Date: Thu, 12 Dec 2024 15:13:53 +0100 Subject: [PATCH 28/31] Update src/dapla_metadata/variable_definitions/exceptions.py Co-authored-by: Miles Mason Winther <42948872+mmwinther@users.noreply.github.com> --- src/dapla_metadata/variable_definitions/exceptions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dapla_metadata/variable_definitions/exceptions.py b/src/dapla_metadata/variable_definitions/exceptions.py index ef3eb83..7407d1e 100644 --- a/src/dapla_metadata/variable_definitions/exceptions.py +++ b/src/dapla_metadata/variable_definitions/exceptions.py @@ -46,7 +46,7 @@ def __init__(self, response_body: str) -> None: self.response_body = response_body except (json.JSONDecodeError, TypeError): self.status = "Unknown" - self.detail = "Invalid response body" + self.detail = "Could not decode error response from API" data = None super().__init__(f"Status {self.status}: {self.detail}") From dc2133716c198dc5ce027beb69e1e61835ac60d5 Mon Sep 17 00:00:00 2001 From: Cecilie Seim <68303562+tilen1976@users.noreply.github.com> Date: Thu, 12 Dec 2024 15:16:20 +0100 Subject: [PATCH 29/31] Move testcontainer dependency to dev --- poetry.lock | 67 +++++++++++++++++++++++++++++++++++++++++++++++++- pyproject.toml | 2 +- 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/poetry.lock b/poetry.lock index 1c172f8..649bd0b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1753,6 +1753,51 @@ files = [ {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, ] +[[package]] +name = "httpcore" +version = "1.0.7" +description = "A minimal low-level HTTP client." +optional = false +python-versions = ">=3.8" +files = [ + {file = "httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd"}, + {file = "httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c"}, +] + +[package.dependencies] +certifi = "*" +h11 = ">=0.13,<0.15" + +[package.extras] +asyncio = ["anyio (>=4.0,<5.0)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] +trio = ["trio (>=0.22.0,<1.0)"] + +[[package]] +name = "httpx" +version = "0.28.1" +description = "The next generation HTTP client." +optional = false +python-versions = ">=3.8" +files = [ + {file = "httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad"}, + {file = "httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc"}, +] + +[package.dependencies] +anyio = "*" +certifi = "*" +httpcore = "==1.*" +idna = "*" + +[package.extras] +brotli = ["brotli", "brotlicffi"] +cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] +zstd = ["zstandard (>=0.18.0)"] + [[package]] name = "identify" version = "2.6.1" @@ -4119,6 +4164,24 @@ files = [ [package.dependencies] cffi = {version = "*", markers = "implementation_name == \"pypy\""} +[[package]] +name = "redis" +version = "5.2.1" +description = "Python client for Redis database and key-value store" +optional = false +python-versions = ">=3.8" +files = [ + {file = "redis-5.2.1-py3-none-any.whl", hash = "sha256:ee7e1056b9aea0f04c6c2ed59452947f34c4940ee025f5dd83e6a6418b6989e4"}, + {file = "redis-5.2.1.tar.gz", hash = "sha256:16f2e22dff21d5125e8481515e386711a34cbec50f0e44413dd7d9c060a54e0f"}, +] + +[package.dependencies] +async-timeout = {version = ">=4.0.3", markers = "python_full_version < \"3.11.3\""} + +[package.extras] +hiredis = ["hiredis (>=3.0.0)"] +ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==23.2.1)", "requests (>=2.31.0)"] + [[package]] name = "referencing" version = "0.35.1" @@ -4843,7 +4906,9 @@ files = [ [package.dependencies] docker = "*" +httpx = {version = "*", optional = true, markers = "extra == \"aws\" or extra == \"generic\" or extra == \"test-module-import\""} python-dotenv = "*" +redis = {version = "*", optional = true, markers = "extra == \"generic\" or extra == \"redis\""} typing-extensions = "*" urllib3 = "*" wrapt = "*" @@ -5652,4 +5717,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">=3.10,<4.0" -content-hash = "f4011623ffbb1013dc30e2c44af0cc60e1b2557ac0f0aa68b0ca80e5cc008904" +content-hash = "5763f94bb3ae2dfb5387864d98607e264e86fc403150e7d93fc4f522e8b64923" diff --git a/pyproject.toml b/pyproject.toml index 6788c1e..7dde83a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,7 +30,6 @@ pyjwt = ">=2.8.0" ssb-klass-python = ">=0.0.9" ssb-datadoc-model = "^6.0.0" typing-extensions = ">=4.12.2" -testcontainers = "^4.9.0" [tool.poetry.group.dev.dependencies] black = ">=24.8.0" @@ -64,6 +63,7 @@ types-beautifulsoup4 = ">=4.12.0.20240511" ipykernel = "^6.29.5" rich = "^13.9.4" bpython = "^0.24" +testcontainers = { version = "^4.8.2", extras = ["generic"] } [tool.pytest.ini_options] pythonpath = [ From 2bd4547b41af5d919d8abbb1697d26878d3fc1cf Mon Sep 17 00:00:00 2001 From: Cecilie Seim <68303562+tilen1976@users.noreply.github.com> Date: Thu, 12 Dec 2024 15:38:08 +0100 Subject: [PATCH 30/31] format violations inline to add default messages - remove formatting method --- .../variable_definitions/exceptions.py | 26 +++++-------------- .../vardef_client/test_exceptions.py | 16 ++++++------ 2 files changed, 15 insertions(+), 27 deletions(-) diff --git a/src/dapla_metadata/variable_definitions/exceptions.py b/src/dapla_metadata/variable_definitions/exceptions.py index 7407d1e..1f9039f 100644 --- a/src/dapla_metadata/variable_definitions/exceptions.py +++ b/src/dapla_metadata/variable_definitions/exceptions.py @@ -32,7 +32,6 @@ def __init__(self, response_body: str) -> None: the detail is a list with field and message. response_body (str): The raw response body string, stored for debugging purposes. - """ self.detail: str | list try: @@ -40,7 +39,13 @@ def __init__(self, response_body: str) -> None: self.status = data.get("status", "Unknown status") if data.get("title") == "Constraint Violation": violations = data.get("violations", []) - self.detail = self._format_violations(violations) + self.detail = [ + { + "field": violation.get("field", "Unknown field"), + "message": violation.get("message", "No message provided"), + } + for violation in violations + ] else: self.detail = data.get("detail", "No detail provided") self.response_body = response_body @@ -50,23 +55,6 @@ def __init__(self, response_body: str) -> None: data = None super().__init__(f"Status {self.status}: {self.detail}") - def _format_violations(self, violations: list) -> list: - """Format a list of violations into a readable string. - - Args: - violations (list): List of violation dictionaries with 'field' and 'message'. - - Returns: - str: Formatted string of violations. - """ - return [ - { - "Field": violation.get("field", "Unknown field"), - "Message": violation.get("message", "No message provided"), - } - for violation in violations - ] - def vardef_exception_handler(method): # noqa: ANN201, ANN001 """Decorator for handling exceptions in Vardef.""" diff --git a/tests/variable_definitions/vardef_client/test_exceptions.py b/tests/variable_definitions/vardef_client/test_exceptions.py index bcb6ddf..1f81928 100644 --- a/tests/variable_definitions/vardef_client/test_exceptions.py +++ b/tests/variable_definitions/vardef_client/test_exceptions.py @@ -34,8 +34,8 @@ def test_invalid_json(): response_body = "Not a JSON string" exc = VardefClientException(response_body) assert exc.status == "Unknown" - assert exc.detail == "Invalid response body" - assert str(exc) == "Status Unknown: Invalid response body" + assert exc.detail == "Could not decode error response from API" + assert str(exc) == "Status Unknown: Could not decode error response from API" def test_missing_keys(): @@ -50,11 +50,11 @@ def test_constraint_violation(): response_body = CONSTRAINT_VIOLATION_BODY exc = VardefClientException(response_body) assert exc.status == BAD_REQUEST_STATUS - assert exc.detail[0]["Message"] == "Invalid Dapla team" + assert exc.detail[0]["message"] == "Invalid Dapla team" assert ( str(exc) == "Status 400: [" - "{'Field': 'updateVariableDefinitionById.updateDraft.owner.team', 'Message': 'Invalid Dapla team'}, " - "{'Field': 'updateVariableDefinitionById.updateDraft.owner.team', 'Message': 'must not be empty'}]" + "{'field': 'updateVariableDefinitionById.updateDraft.owner.team', 'message': 'Invalid Dapla team'}, " + "{'field': 'updateVariableDefinitionById.updateDraft.owner.team', 'message': 'must not be empty'}]" ) @@ -62,7 +62,7 @@ def test_constraint_violation_missing_messages(): response_body = CONSTRAINT_VIOLATION_BODY_MISSING_MESSAGES exc = VardefClientException(response_body) assert exc.status == BAD_REQUEST_STATUS - assert exc.detail[0]["Message"] == "No message provided" + assert exc.detail[0]["message"] == "No message provided" def test_constraint_violation_empty_violations(): @@ -78,6 +78,6 @@ def test_constraint_violation_empty_field(): assert exc.status == BAD_REQUEST_STATUS assert str(exc) == ( "Status 400: [" - "{'Field': 'Unknown field', 'Message': 'Invalid Dapla team'}, " - "{'Field': 'Unknown field', 'Message': 'must not be empty'}]" + "{'field': 'Unknown field', 'message': 'Invalid Dapla team'}, " + "{'field': 'Unknown field', 'message': 'must not be empty'}]" ) From 790b24154e5a8ce0787ac03424cdcce5cebdd7f8 Mon Sep 17 00:00:00 2001 From: Cecilie Seim <68303562+tilen1976@users.noreply.github.com> Date: Fri, 13 Dec 2024 09:32:28 +0100 Subject: [PATCH 31/31] delete unnecessary tests --- .../vardef_client/api/test_error_path.py | 34 ------------------- 1 file changed, 34 deletions(-) delete mode 100644 tests/variable_definitions/vardef_client/api/test_error_path.py diff --git a/tests/variable_definitions/vardef_client/api/test_error_path.py b/tests/variable_definitions/vardef_client/api/test_error_path.py deleted file mode 100644 index 67bd261..0000000 --- a/tests/variable_definitions/vardef_client/api/test_error_path.py +++ /dev/null @@ -1,34 +0,0 @@ -"""Simple tests for exceptions.""" - -import json - -import pytest - -from dapla_metadata.variable_definitions.exceptions import VardefClientException -from dapla_metadata.variable_definitions.generated import vardef_client -from dapla_metadata.variable_definitions.generated.vardef_client.exceptions import ( - OpenApiException, -) - - -def test_not_found_variable(api_client): - api_instance = vardef_client.VariableDefinitionsApi(api_client) - with pytest.raises( - OpenApiException, - match="Not found", - ) as exc_info: - api_instance.get_variable_definition_by_id("invalid id") - response = exc_info.value.body - result = json.loads(response) - assert result["detail"] == "Not found" - - -def test_not_found_response(api_client): - api_instance = vardef_client.VariableDefinitionsApi(api_client) - with pytest.raises( - OpenApiException, - match="Not found", - ) as e: - api_instance.get_variable_definition_by_id("invalid id") - exception = VardefClientException(e.value.body) - assert str(exception) == "Status 404: Not found"