vws-python-mock 2026.2.18__tar.gz → 2026.2.18.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/CHANGELOG.rst +8 -0
- {vws_python_mock-2026.2.18/src/vws_python_mock.egg-info → vws_python_mock-2026.2.18.2}/PKG-INFO +1 -1
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/docs/source/differences-to-vws.rst +7 -0
- vws_python_mock-2026.2.18.2/docs/source/installation.rst +34 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/mock_vws/_constants.py +18 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/mock_vws/_flask_server/Dockerfile +3 -7
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/mock_vws/_flask_server/vws.py +28 -3
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/mock_vws/_query_validators/image_validators.py +41 -39
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/mock_vws/_requests_mock_server/mock_web_services_api.py +37 -10
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/mock_vws/_services_validators/__init__.py +1 -1
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/mock_vws/_services_validators/exceptions.py +116 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/mock_vws/_services_validators/json_validators.py +9 -2
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2/src/vws_python_mock.egg-info}/PKG-INFO +1 -1
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/tests/conftest.py +1 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/tests/mock_vws/fixtures/credentials.py +1 -1
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/tests/mock_vws/fixtures/prepared_requests.py +48 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/tests/mock_vws/test_date_header.py +12 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/tests/mock_vws/test_invalid_json.py +6 -1
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/tests/mock_vws/test_query.py +8 -1
- vws_python_mock-2026.2.18.2/tests/mock_vws/test_vumark_generation_api.py +137 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/tests/mock_vws/utils/__init__.py +1 -1
- vws_python_mock-2026.2.18.2/vuforia_secrets.env.example +21 -0
- vws_python_mock-2026.2.18/docs/source/installation.rst +0 -8
- vws_python_mock-2026.2.18/tests/mock_vws/test_vumark_generation_api.py +0 -55
- vws_python_mock-2026.2.18/vuforia_secrets.env.example +0 -21
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/.git_archival.txt +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/.gitattributes +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/.github/dependabot.yml +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/.github/workflows/dependabot-merge.yml +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/.github/workflows/docker-build.yml +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/.github/workflows/lint.yml +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/.github/workflows/publish-site.yml +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/.github/workflows/release.yml +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/.github/workflows/test.yml +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/.gitignore +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/.pre-commit-config.yaml +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/.prettierrc +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/.vscode/extensions.json +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/.vscode/settings.json +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/CODE_OF_CONDUCT.rst +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/LICENSE +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/MANIFEST.in +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/README.rst +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/admin/__init__.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/admin/create_secrets_files.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/admin/vumark_template.svg +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/ci/__init__.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/ci/decrypt_secret.sh +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/ci/test_custom_linters.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/conftest.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/docker-bake.hcl +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/docs/source/__init__.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/docs/source/basic-example.rst +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/docs/source/changelog.rst +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/docs/source/ci-setup.rst +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/docs/source/conf.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/docs/source/contributing.rst +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/docs/source/docker.rst +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/docs/source/getting-started.rst +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/docs/source/index.rst +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/docs/source/mock-api-reference.rst +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/docs/source/release-process.rst +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/docs/source/versioning-and-api-stability.rst +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/pyproject.toml +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/secrets.tar.gpg +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/setup.cfg +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/spelling_private_dict.txt +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/mock_vws/__init__.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/mock_vws/_base64_decoding.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/mock_vws/_database_matchers.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/mock_vws/_flask_server/__init__.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/mock_vws/_flask_server/healthcheck.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/mock_vws/_flask_server/target_manager.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/mock_vws/_flask_server/vwq.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/mock_vws/_mock_common.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/mock_vws/_query_tools.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/mock_vws/_query_validators/__init__.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/mock_vws/_query_validators/accept_header_validators.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/mock_vws/_query_validators/auth_validators.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/mock_vws/_query_validators/content_length_validators.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/mock_vws/_query_validators/content_type_validators.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/mock_vws/_query_validators/date_validators.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/mock_vws/_query_validators/exceptions.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/mock_vws/_query_validators/fields_validators.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/mock_vws/_query_validators/include_target_data_validators.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/mock_vws/_query_validators/num_results_validators.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/mock_vws/_query_validators/project_state_validators.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/mock_vws/_requests_mock_server/__init__.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/mock_vws/_requests_mock_server/decorators.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/mock_vws/_requests_mock_server/mock_web_query_api.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/mock_vws/_services_validators/active_flag_validators.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/mock_vws/_services_validators/auth_validators.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/mock_vws/_services_validators/content_length_validators.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/mock_vws/_services_validators/content_type_validators.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/mock_vws/_services_validators/date_validators.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/mock_vws/_services_validators/image_validators.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/mock_vws/_services_validators/key_validators.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/mock_vws/_services_validators/metadata_validators.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/mock_vws/_services_validators/name_validators.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/mock_vws/_services_validators/project_state_validators.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/mock_vws/_services_validators/target_validators.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/mock_vws/_services_validators/width_validators.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/mock_vws/database.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/mock_vws/image_matchers.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/mock_vws/py.typed +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/mock_vws/states.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/mock_vws/target.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/mock_vws/target_manager.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/mock_vws/target_raters.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/vws_python_mock.egg-info/SOURCES.txt +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/vws_python_mock.egg-info/dependency_links.txt +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/vws_python_mock.egg-info/not-zip-safe +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/vws_python_mock.egg-info/requires.txt +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/vws_python_mock.egg-info/top_level.txt +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/tests/__init__.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/tests/mock_vws/__init__.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/tests/mock_vws/fixtures/__init__.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/tests/mock_vws/fixtures/vuforia_backends.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/tests/mock_vws/test_add_target.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/tests/mock_vws/test_authorization_header.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/tests/mock_vws/test_content_length.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/tests/mock_vws/test_database_summary.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/tests/mock_vws/test_delete_target.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/tests/mock_vws/test_docker.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/tests/mock_vws/test_flask_app_usage.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/tests/mock_vws/test_get_duplicates.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/tests/mock_vws/test_get_target.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/tests/mock_vws/test_invalid_given_id.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/tests/mock_vws/test_requests_mock_usage.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/tests/mock_vws/test_target_list.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/tests/mock_vws/test_target_raters.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/tests/mock_vws/test_target_summary.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/tests/mock_vws/test_target_validators.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/tests/mock_vws/test_unexpected_json.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/tests/mock_vws/test_update_target.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/tests/mock_vws/utils/assertions.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/tests/mock_vws/utils/retries.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/tests/mock_vws/utils/too_many_requests.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/tests/mock_vws/utils/usage_test_helpers.py +0 -0
- {vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/zizmor.yml +0 -0
{vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/docs/source/differences-to-vws.rst
RENAMED
|
@@ -103,6 +103,13 @@ These are:
|
|
|
103
103
|
|
|
104
104
|
When the given ``Content-Length`` header does not match the length of the given data, the mock server (written with Flask) will not behave as the real Vuforia Web Services behaves.
|
|
105
105
|
|
|
106
|
+
VuMark instance images
|
|
107
|
+
----------------------
|
|
108
|
+
|
|
109
|
+
The mock returns a fixed minimal image in the requested format.
|
|
110
|
+
The ``instance_id`` value is not encoded into the response image.
|
|
111
|
+
Real Vuforia encodes the instance ID into the VuMark pattern.
|
|
112
|
+
|
|
106
113
|
Header cases
|
|
107
114
|
------------
|
|
108
115
|
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
Installation
|
|
2
|
+
------------
|
|
3
|
+
|
|
4
|
+
.. code-block:: console
|
|
5
|
+
|
|
6
|
+
$ pip install vws-python-mock
|
|
7
|
+
|
|
8
|
+
This requires Python |minimum-python-version|\+.
|
|
9
|
+
|
|
10
|
+
Faster installation
|
|
11
|
+
~~~~~~~~~~~~~~~~~~~
|
|
12
|
+
|
|
13
|
+
This package depends on `PyTorch`_, which pip installs from PyPI as a large CUDA-enabled build (~873 MB) even on CPU-only machines.
|
|
14
|
+
To get a much smaller CPU-only build (~200 MB, no CUDA dependencies), install ``torch`` and ``torchvision`` from PyTorch's CPU index before installing this package:
|
|
15
|
+
|
|
16
|
+
.. code-block:: console
|
|
17
|
+
|
|
18
|
+
$ pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu
|
|
19
|
+
$ pip install vws-python-mock
|
|
20
|
+
|
|
21
|
+
If you manage dependencies with ``uv``, add the following to your ``pyproject.toml`` instead:
|
|
22
|
+
|
|
23
|
+
.. code-block:: toml
|
|
24
|
+
|
|
25
|
+
[[tool.uv.index]]
|
|
26
|
+
name = "pytorch-cpu"
|
|
27
|
+
url = "https://download.pytorch.org/whl/cpu"
|
|
28
|
+
explicit = true
|
|
29
|
+
|
|
30
|
+
[tool.uv.sources]
|
|
31
|
+
torch = { index = "pytorch-cpu" }
|
|
32
|
+
torchvision = { index = "pytorch-cpu" }
|
|
33
|
+
|
|
34
|
+
.. _PyTorch: https://pytorch.org
|
|
@@ -11,6 +11,21 @@ VUMARK_PNG = (
|
|
|
11
11
|
b"\xaeB`\x82"
|
|
12
12
|
)
|
|
13
13
|
|
|
14
|
+
VUMARK_SVG = (
|
|
15
|
+
b'<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100"></svg>'
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
VUMARK_PDF = (
|
|
19
|
+
b"%PDF-1.4\n"
|
|
20
|
+
b"1 0 obj<</Type /Catalog /Pages 2 0 R>>endobj\n"
|
|
21
|
+
b"2 0 obj<</Type /Pages /Kids [3 0 R] /Count 1>>endobj\n"
|
|
22
|
+
b"3 0 obj<</Type /Page /MediaBox [0 0 100 100]>>endobj\n"
|
|
23
|
+
b"xref\n0 4\n"
|
|
24
|
+
b"0000000000 65535 f \n"
|
|
25
|
+
b"trailer<</Size 4/Root 1 0 R>>\n"
|
|
26
|
+
b"startxref\n9\n%%EOF"
|
|
27
|
+
)
|
|
28
|
+
|
|
14
29
|
|
|
15
30
|
@beartype
|
|
16
31
|
@unique
|
|
@@ -45,6 +60,9 @@ class ResultCodes(Enum):
|
|
|
45
60
|
PROJECT_INACTIVE = "ProjectInactive"
|
|
46
61
|
INACTIVE_PROJECT = "InactiveProject"
|
|
47
62
|
TOO_MANY_REQUESTS = "TooManyRequests"
|
|
63
|
+
INVALID_ACCEPT_HEADER = "InvalidAcceptHeader"
|
|
64
|
+
INVALID_INSTANCE_ID = "InvalidInstanceId"
|
|
65
|
+
BAD_REQUEST = "BadRequest"
|
|
48
66
|
|
|
49
67
|
|
|
50
68
|
@beartype
|
{vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/src/mock_vws/_flask_server/Dockerfile
RENAMED
|
@@ -1,24 +1,20 @@
|
|
|
1
|
-
FROM
|
|
1
|
+
FROM ghcr.io/astral-sh/uv:0.10.4-python3.13-trixie-slim AS base
|
|
2
2
|
# We set this pretend version as we do not have Git in our path, and we do
|
|
3
3
|
# not care enough about having the version correct inside the Docker container
|
|
4
4
|
# to install it.
|
|
5
5
|
ENV SETUPTOOLS_SCM_PRETEND_VERSION=0.0.0
|
|
6
6
|
# Avoid using root user.
|
|
7
|
-
# This avoids having to use ``--root-user-action=ignore`` with pip.
|
|
8
7
|
RUN useradd -ms /bin/bash myuser
|
|
9
8
|
USER myuser
|
|
10
9
|
COPY --chown=myuser:myuser . /app
|
|
11
10
|
|
|
12
11
|
# See https://pythonspeed.com/articles/activate-virtualenv-dockerfile/
|
|
13
12
|
# For why we use this method of activating the virtual environment.
|
|
14
|
-
ENV VIRTUAL_ENV=/app/docker_venvs/.venv
|
|
15
13
|
ENV UV_PROJECT_ENVIRONMENT=/app/docker_venvs/.venv
|
|
16
|
-
ENV PATH="$
|
|
14
|
+
ENV PATH="$UV_PROJECT_ENVIRONMENT/bin:$PATH"
|
|
17
15
|
|
|
18
16
|
WORKDIR /app
|
|
19
|
-
RUN
|
|
20
|
-
pip install --no-cache-dir uv==0.10.4 && \
|
|
21
|
-
uv sync --no-cache
|
|
17
|
+
RUN uv sync --no-cache
|
|
22
18
|
EXPOSE 5000
|
|
23
19
|
ENTRYPOINT ["python"]
|
|
24
20
|
HEALTHCHECK --interval=1s --timeout=10s --start-period=5s --retries=3 CMD ["python", "/app/src/mock_vws/_flask_server/healthcheck.py"]
|
|
@@ -18,12 +18,20 @@ from beartype import beartype
|
|
|
18
18
|
from flask import Flask, Response, request
|
|
19
19
|
from pydantic_settings import BaseSettings
|
|
20
20
|
|
|
21
|
-
from mock_vws._constants import
|
|
21
|
+
from mock_vws._constants import (
|
|
22
|
+
VUMARK_PDF,
|
|
23
|
+
VUMARK_PNG,
|
|
24
|
+
VUMARK_SVG,
|
|
25
|
+
ResultCodes,
|
|
26
|
+
TargetStatuses,
|
|
27
|
+
)
|
|
22
28
|
from mock_vws._database_matchers import get_database_matching_server_keys
|
|
23
29
|
from mock_vws._mock_common import json_dump
|
|
24
30
|
from mock_vws._services_validators import run_services_validators
|
|
25
31
|
from mock_vws._services_validators.exceptions import (
|
|
26
32
|
FailError,
|
|
33
|
+
InvalidAcceptHeaderError,
|
|
34
|
+
InvalidInstanceIdError,
|
|
27
35
|
TargetStatusNotSuccessError,
|
|
28
36
|
TargetStatusProcessingError,
|
|
29
37
|
ValidatorError,
|
|
@@ -351,10 +359,27 @@ def generate_vumark_instance(target_id: str) -> Response:
|
|
|
351
359
|
"""
|
|
352
360
|
# ``target_id`` is validated by request validators.
|
|
353
361
|
del target_id
|
|
362
|
+
|
|
363
|
+
accept = request.headers.get(key="Accept", default="")
|
|
364
|
+
valid_accept_types: dict[str, bytes] = {
|
|
365
|
+
"image/png": VUMARK_PNG,
|
|
366
|
+
"image/svg+xml": VUMARK_SVG,
|
|
367
|
+
"application/pdf": VUMARK_PDF,
|
|
368
|
+
}
|
|
369
|
+
if accept not in valid_accept_types:
|
|
370
|
+
raise InvalidAcceptHeaderError
|
|
371
|
+
|
|
372
|
+
request_json = json.loads(s=request.data)
|
|
373
|
+
instance_id = request_json.get("instance_id", "")
|
|
374
|
+
if not instance_id:
|
|
375
|
+
raise InvalidInstanceIdError
|
|
376
|
+
|
|
377
|
+
response_body = valid_accept_types[accept]
|
|
378
|
+
content_type = accept
|
|
354
379
|
date = email.utils.formatdate(timeval=None, localtime=False, usegmt=True)
|
|
355
380
|
headers = {
|
|
356
381
|
"Connection": "keep-alive",
|
|
357
|
-
"Content-Type":
|
|
382
|
+
"Content-Type": content_type,
|
|
358
383
|
"server": "envoy",
|
|
359
384
|
"Date": date,
|
|
360
385
|
"x-envoy-upstream-service-time": "5",
|
|
@@ -364,7 +389,7 @@ def generate_vumark_instance(target_id: str) -> Response:
|
|
|
364
389
|
}
|
|
365
390
|
return Response(
|
|
366
391
|
status=HTTPStatus.OK,
|
|
367
|
-
response=
|
|
392
|
+
response=response_body,
|
|
368
393
|
headers=headers,
|
|
369
394
|
)
|
|
370
395
|
|
|
@@ -7,6 +7,7 @@ from email.message import EmailMessage
|
|
|
7
7
|
|
|
8
8
|
from beartype import beartype
|
|
9
9
|
from PIL import Image
|
|
10
|
+
from werkzeug.datastructures import FileStorage, MultiDict
|
|
10
11
|
from werkzeug.formparser import MultiPartParser
|
|
11
12
|
|
|
12
13
|
from mock_vws._query_validators.exceptions import (
|
|
@@ -19,19 +20,19 @@ _LOGGER = logging.getLogger(name=__name__)
|
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
@beartype
|
|
22
|
-
def
|
|
23
|
+
def _parse_multipart_files(
|
|
23
24
|
*,
|
|
24
25
|
request_headers: Mapping[str, str],
|
|
25
26
|
request_body: bytes,
|
|
26
|
-
) ->
|
|
27
|
-
"""
|
|
27
|
+
) -> MultiDict[str, FileStorage]:
|
|
28
|
+
"""Parse the multipart body and return the files section.
|
|
28
29
|
|
|
29
30
|
Args:
|
|
30
31
|
request_headers: The headers sent with the request.
|
|
31
32
|
request_body: The body of the request.
|
|
32
33
|
|
|
33
|
-
|
|
34
|
-
|
|
34
|
+
Returns:
|
|
35
|
+
The files parsed from the multipart body.
|
|
35
36
|
"""
|
|
36
37
|
email_message = EmailMessage()
|
|
37
38
|
email_message["Content-Type"] = request_headers["Content-Type"]
|
|
@@ -42,6 +43,28 @@ def validate_image_field_given(
|
|
|
42
43
|
boundary=boundary.encode(encoding="utf-8"),
|
|
43
44
|
content_length=len(request_body),
|
|
44
45
|
)
|
|
46
|
+
return files
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@beartype
|
|
50
|
+
def validate_image_field_given(
|
|
51
|
+
*,
|
|
52
|
+
request_headers: Mapping[str, str],
|
|
53
|
+
request_body: bytes,
|
|
54
|
+
) -> None:
|
|
55
|
+
"""Validate that the image field is given.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
request_headers: The headers sent with the request.
|
|
59
|
+
request_body: The body of the request.
|
|
60
|
+
|
|
61
|
+
Raises:
|
|
62
|
+
ImageNotGivenError: The image field is not given.
|
|
63
|
+
"""
|
|
64
|
+
files = _parse_multipart_files(
|
|
65
|
+
request_headers=request_headers,
|
|
66
|
+
request_body=request_body,
|
|
67
|
+
)
|
|
45
68
|
if files.get(key="image") is not None:
|
|
46
69
|
return
|
|
47
70
|
|
|
@@ -64,14 +87,9 @@ def validate_image_file_size(
|
|
|
64
87
|
Raises:
|
|
65
88
|
RequestEntityTooLargeError: The image file size is too large.
|
|
66
89
|
"""
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
parser = MultiPartParser()
|
|
71
|
-
_, files = parser.parse(
|
|
72
|
-
stream=io.BytesIO(initial_bytes=request_body),
|
|
73
|
-
boundary=boundary.encode(encoding="utf-8"),
|
|
74
|
-
content_length=len(request_body),
|
|
90
|
+
files = _parse_multipart_files(
|
|
91
|
+
request_headers=request_headers,
|
|
92
|
+
request_body=request_body,
|
|
75
93
|
)
|
|
76
94
|
image_part = files["image"]
|
|
77
95
|
image_value = image_part.stream.read()
|
|
@@ -105,14 +123,9 @@ def validate_image_dimensions(
|
|
|
105
123
|
BadImageError: The image is given and is not within the maximum width
|
|
106
124
|
and height limits.
|
|
107
125
|
"""
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
parser = MultiPartParser()
|
|
112
|
-
_, files = parser.parse(
|
|
113
|
-
stream=io.BytesIO(initial_bytes=request_body),
|
|
114
|
-
boundary=boundary.encode(encoding="utf-8"),
|
|
115
|
-
content_length=len(request_body),
|
|
126
|
+
files = _parse_multipart_files(
|
|
127
|
+
request_headers=request_headers,
|
|
128
|
+
request_body=request_body,
|
|
116
129
|
)
|
|
117
130
|
image_part = files["image"]
|
|
118
131
|
image_value = image_part.stream.read()
|
|
@@ -142,14 +155,9 @@ def validate_image_format(
|
|
|
142
155
|
Raises:
|
|
143
156
|
BadImageError: The image is given and is not either a PNG or a JPEG.
|
|
144
157
|
"""
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
parser = MultiPartParser()
|
|
149
|
-
_, files = parser.parse(
|
|
150
|
-
stream=io.BytesIO(initial_bytes=request_body),
|
|
151
|
-
boundary=boundary.encode(encoding="utf-8"),
|
|
152
|
-
content_length=len(request_body),
|
|
158
|
+
files = _parse_multipart_files(
|
|
159
|
+
request_headers=request_headers,
|
|
160
|
+
request_body=request_body,
|
|
153
161
|
)
|
|
154
162
|
image_part = files["image"]
|
|
155
163
|
pil_image = Image.open(fp=image_part.stream)
|
|
@@ -175,17 +183,11 @@ def validate_image_is_image(
|
|
|
175
183
|
Raises:
|
|
176
184
|
BadImageError: Image data is given and it is not an image file.
|
|
177
185
|
"""
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
parser = MultiPartParser()
|
|
182
|
-
_, files = parser.parse(
|
|
183
|
-
stream=io.BytesIO(initial_bytes=request_body),
|
|
184
|
-
boundary=boundary.encode(encoding="utf-8"),
|
|
185
|
-
content_length=len(request_body),
|
|
186
|
+
files = _parse_multipart_files(
|
|
187
|
+
request_headers=request_headers,
|
|
188
|
+
request_body=request_body,
|
|
186
189
|
)
|
|
187
|
-
|
|
188
|
-
image_file = image_part.stream
|
|
190
|
+
image_file = files["image"].stream
|
|
189
191
|
|
|
190
192
|
try:
|
|
191
193
|
Image.open(fp=image_file)
|
|
@@ -18,12 +18,20 @@ from zoneinfo import ZoneInfo
|
|
|
18
18
|
from beartype import BeartypeConf, beartype
|
|
19
19
|
from requests.models import PreparedRequest
|
|
20
20
|
|
|
21
|
-
from mock_vws._constants import
|
|
21
|
+
from mock_vws._constants import (
|
|
22
|
+
VUMARK_PDF,
|
|
23
|
+
VUMARK_PNG,
|
|
24
|
+
VUMARK_SVG,
|
|
25
|
+
ResultCodes,
|
|
26
|
+
TargetStatuses,
|
|
27
|
+
)
|
|
22
28
|
from mock_vws._database_matchers import get_database_matching_server_keys
|
|
23
29
|
from mock_vws._mock_common import Route, json_dump
|
|
24
30
|
from mock_vws._services_validators import run_services_validators
|
|
25
31
|
from mock_vws._services_validators.exceptions import (
|
|
26
32
|
FailError,
|
|
33
|
+
InvalidAcceptHeaderError,
|
|
34
|
+
InvalidInstanceIdError,
|
|
27
35
|
TargetStatusNotSuccessError,
|
|
28
36
|
TargetStatusProcessingError,
|
|
29
37
|
ValidatorError,
|
|
@@ -295,14 +303,33 @@ class MockVuforiaWebServicesAPI:
|
|
|
295
303
|
self, request: PreparedRequest
|
|
296
304
|
) -> _ResponseType:
|
|
297
305
|
"""Generate a VuMark instance."""
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
306
|
+
valid_accept_types: dict[str, bytes] = {
|
|
307
|
+
"image/png": VUMARK_PNG,
|
|
308
|
+
"image/svg+xml": VUMARK_SVG,
|
|
309
|
+
"application/pdf": VUMARK_PDF,
|
|
310
|
+
}
|
|
311
|
+
try:
|
|
312
|
+
run_services_validators(
|
|
313
|
+
request_headers=request.headers,
|
|
314
|
+
request_body=_body_bytes(request=request),
|
|
315
|
+
request_method=request.method or "",
|
|
316
|
+
request_path=request.path_url,
|
|
317
|
+
databases=self._target_manager.databases,
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
accept = dict(request.headers).get("Accept", "")
|
|
321
|
+
if accept not in valid_accept_types:
|
|
322
|
+
raise InvalidAcceptHeaderError
|
|
323
|
+
|
|
324
|
+
request_json = json.loads(s=_body_bytes(request=request))
|
|
325
|
+
instance_id = request_json.get("instance_id", "")
|
|
326
|
+
if not instance_id:
|
|
327
|
+
raise InvalidInstanceIdError
|
|
328
|
+
except ValidatorError as exc:
|
|
329
|
+
return exc.status_code, exc.headers, exc.response_text
|
|
305
330
|
|
|
331
|
+
response_body = valid_accept_types[accept]
|
|
332
|
+
content_type = accept
|
|
306
333
|
date = email.utils.formatdate(
|
|
307
334
|
timeval=None,
|
|
308
335
|
localtime=False,
|
|
@@ -310,7 +337,7 @@ class MockVuforiaWebServicesAPI:
|
|
|
310
337
|
)
|
|
311
338
|
headers = {
|
|
312
339
|
"Connection": "keep-alive",
|
|
313
|
-
"Content-Type":
|
|
340
|
+
"Content-Type": content_type,
|
|
314
341
|
"Date": date,
|
|
315
342
|
"server": "envoy",
|
|
316
343
|
"x-envoy-upstream-service-time": "5",
|
|
@@ -318,7 +345,7 @@ class MockVuforiaWebServicesAPI:
|
|
|
318
345
|
"x-aws-region": "us-east-2, us-west-2",
|
|
319
346
|
"x-content-type-options": "nosniff",
|
|
320
347
|
}
|
|
321
|
-
return HTTPStatus.OK, headers,
|
|
348
|
+
return HTTPStatus.OK, headers, response_body
|
|
322
349
|
|
|
323
350
|
@route(path_pattern="/summary", http_methods={HTTPMethod.GET})
|
|
324
351
|
def database_summary(self, request: PreparedRequest) -> _ResponseType:
|
|
@@ -103,7 +103,7 @@ def run_services_validators(
|
|
|
103
103
|
validate_date_format(request_headers=request_headers)
|
|
104
104
|
validate_date_in_range(request_headers=request_headers)
|
|
105
105
|
|
|
106
|
-
validate_json(request_body=request_body)
|
|
106
|
+
validate_json(request_body=request_body, request_path=request_path)
|
|
107
107
|
|
|
108
108
|
validate_keys(
|
|
109
109
|
request_body=request_body,
|
|
@@ -184,6 +184,46 @@ class FailError(ValidatorError):
|
|
|
184
184
|
}
|
|
185
185
|
|
|
186
186
|
|
|
187
|
+
@beartype
|
|
188
|
+
class BadRequestError(ValidatorError):
|
|
189
|
+
"""Exception raised when Vuforia returns a response with a result code
|
|
190
|
+
'BadRequest'.
|
|
191
|
+
"""
|
|
192
|
+
|
|
193
|
+
def __init__(self) -> None:
|
|
194
|
+
"""
|
|
195
|
+
Attributes:
|
|
196
|
+
status_code: The status code to use in a response if this is
|
|
197
|
+
raised.
|
|
198
|
+
response_text: The response text to use in a response if this
|
|
199
|
+
is
|
|
200
|
+
raised.
|
|
201
|
+
"""
|
|
202
|
+
super().__init__()
|
|
203
|
+
self.status_code = HTTPStatus.BAD_REQUEST
|
|
204
|
+
body = {
|
|
205
|
+
"transaction_id": uuid.uuid4().hex,
|
|
206
|
+
"result_code": ResultCodes.BAD_REQUEST.value,
|
|
207
|
+
}
|
|
208
|
+
self.response_text = json_dump(body=body)
|
|
209
|
+
date = email.utils.formatdate(
|
|
210
|
+
timeval=None,
|
|
211
|
+
localtime=False,
|
|
212
|
+
usegmt=True,
|
|
213
|
+
)
|
|
214
|
+
self.headers = {
|
|
215
|
+
"Connection": "keep-alive",
|
|
216
|
+
"Content-Type": "application/json",
|
|
217
|
+
"server": "envoy",
|
|
218
|
+
"Date": date,
|
|
219
|
+
"x-envoy-upstream-service-time": "5",
|
|
220
|
+
"Content-Length": str(object=len(self.response_text)),
|
|
221
|
+
"strict-transport-security": "max-age=31536000",
|
|
222
|
+
"x-aws-region": "us-east-2, us-west-2",
|
|
223
|
+
"x-content-type-options": "nosniff",
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
|
|
187
227
|
@beartype
|
|
188
228
|
class MetadataTooLargeError(ValidatorError):
|
|
189
229
|
"""Exception raised when Vuforia returns a response with a result code
|
|
@@ -530,6 +570,82 @@ class TargetStatusNotSuccessError(ValidatorError):
|
|
|
530
570
|
}
|
|
531
571
|
|
|
532
572
|
|
|
573
|
+
@beartype
|
|
574
|
+
class InvalidAcceptHeaderError(ValidatorError):
|
|
575
|
+
"""Exception raised when an unsupported Accept header is given."""
|
|
576
|
+
|
|
577
|
+
def __init__(self) -> None:
|
|
578
|
+
"""
|
|
579
|
+
Attributes:
|
|
580
|
+
status_code: The status code to use in a response if this is
|
|
581
|
+
raised.
|
|
582
|
+
response_text: The response text to use in a response if this
|
|
583
|
+
is
|
|
584
|
+
raised.
|
|
585
|
+
"""
|
|
586
|
+
super().__init__()
|
|
587
|
+
self.status_code = HTTPStatus.BAD_REQUEST
|
|
588
|
+
body = {
|
|
589
|
+
"transaction_id": uuid.uuid4().hex,
|
|
590
|
+
"result_code": ResultCodes.INVALID_ACCEPT_HEADER.value,
|
|
591
|
+
}
|
|
592
|
+
self.response_text = json_dump(body=body)
|
|
593
|
+
date = email.utils.formatdate(
|
|
594
|
+
timeval=None,
|
|
595
|
+
localtime=False,
|
|
596
|
+
usegmt=True,
|
|
597
|
+
)
|
|
598
|
+
self.headers = {
|
|
599
|
+
"Connection": "keep-alive",
|
|
600
|
+
"Content-Type": "application/json",
|
|
601
|
+
"server": "envoy",
|
|
602
|
+
"Date": date,
|
|
603
|
+
"x-envoy-upstream-service-time": "5",
|
|
604
|
+
"Content-Length": str(object=len(self.response_text)),
|
|
605
|
+
"strict-transport-security": "max-age=31536000",
|
|
606
|
+
"x-aws-region": "us-east-2, us-west-2",
|
|
607
|
+
"x-content-type-options": "nosniff",
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
|
|
611
|
+
@beartype
|
|
612
|
+
class InvalidInstanceIdError(ValidatorError):
|
|
613
|
+
"""Exception raised when an invalid instance_id is given."""
|
|
614
|
+
|
|
615
|
+
def __init__(self) -> None:
|
|
616
|
+
"""
|
|
617
|
+
Attributes:
|
|
618
|
+
status_code: The status code to use in a response if this is
|
|
619
|
+
raised.
|
|
620
|
+
response_text: The response text to use in a response if this
|
|
621
|
+
is
|
|
622
|
+
raised.
|
|
623
|
+
"""
|
|
624
|
+
super().__init__()
|
|
625
|
+
self.status_code = HTTPStatus.UNPROCESSABLE_ENTITY
|
|
626
|
+
body = {
|
|
627
|
+
"transaction_id": uuid.uuid4().hex,
|
|
628
|
+
"result_code": ResultCodes.INVALID_INSTANCE_ID.value,
|
|
629
|
+
}
|
|
630
|
+
self.response_text = json_dump(body=body)
|
|
631
|
+
date = email.utils.formatdate(
|
|
632
|
+
timeval=None,
|
|
633
|
+
localtime=False,
|
|
634
|
+
usegmt=True,
|
|
635
|
+
)
|
|
636
|
+
self.headers = {
|
|
637
|
+
"Connection": "keep-alive",
|
|
638
|
+
"Content-Type": "application/json",
|
|
639
|
+
"server": "envoy",
|
|
640
|
+
"Date": date,
|
|
641
|
+
"x-envoy-upstream-service-time": "5",
|
|
642
|
+
"Content-Length": str(object=len(self.response_text)),
|
|
643
|
+
"strict-transport-security": "max-age=31536000",
|
|
644
|
+
"x-aws-region": "us-east-2, us-west-2",
|
|
645
|
+
"x-content-type-options": "nosniff",
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
|
|
533
649
|
@beartype
|
|
534
650
|
class TargetStatusProcessingError(ValidatorError):
|
|
535
651
|
"""Exception raised when trying to delete a target which is processing."""
|
|
@@ -8,6 +8,7 @@ from json.decoder import JSONDecodeError
|
|
|
8
8
|
from beartype import beartype
|
|
9
9
|
|
|
10
10
|
from mock_vws._services_validators.exceptions import (
|
|
11
|
+
BadRequestError,
|
|
11
12
|
FailError,
|
|
12
13
|
UnnecessaryRequestBodyError,
|
|
13
14
|
)
|
|
@@ -43,14 +44,18 @@ def validate_body_given(*, request_body: bytes, request_method: str) -> None:
|
|
|
43
44
|
|
|
44
45
|
|
|
45
46
|
@beartype
|
|
46
|
-
def validate_json(*, request_body: bytes) -> None:
|
|
47
|
+
def validate_json(*, request_body: bytes, request_path: str) -> None:
|
|
47
48
|
"""Validate that any given body is valid JSON.
|
|
48
49
|
|
|
49
50
|
Args:
|
|
50
51
|
request_body: The body of the request.
|
|
52
|
+
request_path: The path of the request.
|
|
51
53
|
|
|
52
54
|
Raises:
|
|
53
|
-
|
|
55
|
+
BadRequestError: The request body includes invalid JSON for the
|
|
56
|
+
VuMark instance generation endpoint.
|
|
57
|
+
FailError: The request body includes invalid JSON for other
|
|
58
|
+
endpoints.
|
|
54
59
|
"""
|
|
55
60
|
if not request_body:
|
|
56
61
|
return
|
|
@@ -59,4 +64,6 @@ def validate_json(*, request_body: bytes) -> None:
|
|
|
59
64
|
json.loads(s=request_body.decode())
|
|
60
65
|
except JSONDecodeError as exc:
|
|
61
66
|
_LOGGER.warning(msg="The request body is not valid JSON.")
|
|
67
|
+
if request_path.endswith("/instances"):
|
|
68
|
+
raise BadRequestError from exc
|
|
62
69
|
raise FailError(status_code=HTTPStatus.BAD_REQUEST) from exc
|
{vws_python_mock-2026.2.18 → vws_python_mock-2026.2.18.2}/tests/mock_vws/fixtures/credentials.py
RENAMED
|
@@ -42,7 +42,7 @@ class _VuMarkVuforiaDatabaseSettings(BaseSettings):
|
|
|
42
42
|
target_manager_database_name: str
|
|
43
43
|
server_access_key: str
|
|
44
44
|
server_secret_key: str
|
|
45
|
-
target_id: str
|
|
45
|
+
target_id: str
|
|
46
46
|
|
|
47
47
|
model_config = SettingsConfigDict(
|
|
48
48
|
env_prefix="VUMARK_VUFORIA_",
|