vws-python-mock 2026.2.18.2__tar.gz → 2026.2.21__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.
Files changed (140) hide show
  1. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/CHANGELOG.rst +8 -0
  2. {vws_python_mock-2026.2.18.2/src/vws_python_mock.egg-info → vws_python_mock-2026.2.21}/PKG-INFO +5 -5
  3. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/README.rst +3 -3
  4. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/docs/source/basic-example.rst +3 -3
  5. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/docs/source/docker.rst +3 -3
  6. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/docs/source/mock-api-reference.rst +14 -3
  7. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/pyproject.toml +3 -2
  8. vws_python_mock-2026.2.21/secrets.tar.gpg +0 -0
  9. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/spelling_private_dict.txt +1 -0
  10. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/mock_vws/_constants.py +1 -0
  11. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/mock_vws/_database_matchers.py +8 -6
  12. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/mock_vws/_flask_server/target_manager.py +166 -54
  13. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/mock_vws/_flask_server/vwq.py +5 -5
  14. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/mock_vws/_flask_server/vws.py +79 -27
  15. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/mock_vws/_query_tools.py +2 -2
  16. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/mock_vws/_query_validators/__init__.py +2 -2
  17. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/mock_vws/_query_validators/auth_validators.py +3 -3
  18. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/mock_vws/_query_validators/project_state_validators.py +2 -2
  19. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/mock_vws/_requests_mock_server/decorators.py +22 -6
  20. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/mock_vws/_requests_mock_server/mock_web_query_api.py +2 -2
  21. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/mock_vws/_requests_mock_server/mock_web_services_api.py +68 -41
  22. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/mock_vws/_services_validators/__init__.py +2 -2
  23. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/mock_vws/_services_validators/auth_validators.py +6 -4
  24. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/mock_vws/_services_validators/exceptions.py +40 -0
  25. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/mock_vws/_services_validators/name_validators.py +6 -4
  26. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/mock_vws/_services_validators/project_state_validators.py +9 -3
  27. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/mock_vws/_services_validators/target_validators.py +5 -3
  28. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/mock_vws/database.py +102 -19
  29. vws_python_mock-2026.2.21/src/mock_vws/database_type.py +13 -0
  30. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/mock_vws/target.py +45 -7
  31. vws_python_mock-2026.2.21/src/mock_vws/target_manager.py +158 -0
  32. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21/src/vws_python_mock.egg-info}/PKG-INFO +5 -5
  33. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/vws_python_mock.egg-info/SOURCES.txt +1 -0
  34. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/vws_python_mock.egg-info/requires.txt +1 -1
  35. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/tests/conftest.py +5 -5
  36. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/tests/mock_vws/fixtures/credentials.py +14 -14
  37. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/tests/mock_vws/fixtures/prepared_requests.py +12 -12
  38. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/tests/mock_vws/fixtures/vuforia_backends.py +54 -53
  39. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/tests/mock_vws/test_authorization_header.py +5 -5
  40. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/tests/mock_vws/test_database_summary.py +4 -4
  41. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/tests/mock_vws/test_docker.py +3 -3
  42. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/tests/mock_vws/test_flask_app_usage.py +81 -35
  43. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/tests/mock_vws/test_query.py +22 -22
  44. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/tests/mock_vws/test_requests_mock_usage.py +82 -38
  45. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/tests/mock_vws/test_target_summary.py +2 -2
  46. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/tests/mock_vws/test_target_validators.py +8 -8
  47. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/tests/mock_vws/test_vumark_generation_api.py +60 -11
  48. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/tests/mock_vws/utils/usage_test_helpers.py +2 -2
  49. vws_python_mock-2026.2.18.2/secrets.tar.gpg +0 -0
  50. vws_python_mock-2026.2.18.2/src/mock_vws/target_manager.py +0 -87
  51. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/.git_archival.txt +0 -0
  52. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/.gitattributes +0 -0
  53. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/.github/dependabot.yml +0 -0
  54. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/.github/workflows/dependabot-merge.yml +0 -0
  55. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/.github/workflows/docker-build.yml +0 -0
  56. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/.github/workflows/lint.yml +0 -0
  57. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/.github/workflows/publish-site.yml +0 -0
  58. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/.github/workflows/release.yml +0 -0
  59. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/.github/workflows/test.yml +0 -0
  60. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/.gitignore +0 -0
  61. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/.pre-commit-config.yaml +0 -0
  62. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/.prettierrc +0 -0
  63. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/.vscode/extensions.json +0 -0
  64. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/.vscode/settings.json +0 -0
  65. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/CODE_OF_CONDUCT.rst +0 -0
  66. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/LICENSE +0 -0
  67. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/MANIFEST.in +0 -0
  68. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/admin/__init__.py +0 -0
  69. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/admin/create_secrets_files.py +0 -0
  70. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/admin/vumark_template.svg +0 -0
  71. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/ci/__init__.py +0 -0
  72. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/ci/decrypt_secret.sh +0 -0
  73. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/ci/test_custom_linters.py +0 -0
  74. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/conftest.py +0 -0
  75. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/docker-bake.hcl +0 -0
  76. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/docs/source/__init__.py +0 -0
  77. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/docs/source/changelog.rst +0 -0
  78. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/docs/source/ci-setup.rst +0 -0
  79. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/docs/source/conf.py +0 -0
  80. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/docs/source/contributing.rst +0 -0
  81. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/docs/source/differences-to-vws.rst +0 -0
  82. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/docs/source/getting-started.rst +0 -0
  83. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/docs/source/index.rst +0 -0
  84. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/docs/source/installation.rst +0 -0
  85. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/docs/source/release-process.rst +0 -0
  86. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/docs/source/versioning-and-api-stability.rst +0 -0
  87. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/setup.cfg +0 -0
  88. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/mock_vws/__init__.py +0 -0
  89. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/mock_vws/_base64_decoding.py +0 -0
  90. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/mock_vws/_flask_server/Dockerfile +0 -0
  91. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/mock_vws/_flask_server/__init__.py +0 -0
  92. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/mock_vws/_flask_server/healthcheck.py +0 -0
  93. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/mock_vws/_mock_common.py +0 -0
  94. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/mock_vws/_query_validators/accept_header_validators.py +0 -0
  95. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/mock_vws/_query_validators/content_length_validators.py +0 -0
  96. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/mock_vws/_query_validators/content_type_validators.py +0 -0
  97. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/mock_vws/_query_validators/date_validators.py +0 -0
  98. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/mock_vws/_query_validators/exceptions.py +0 -0
  99. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/mock_vws/_query_validators/fields_validators.py +0 -0
  100. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/mock_vws/_query_validators/image_validators.py +0 -0
  101. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/mock_vws/_query_validators/include_target_data_validators.py +0 -0
  102. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/mock_vws/_query_validators/num_results_validators.py +0 -0
  103. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/mock_vws/_requests_mock_server/__init__.py +0 -0
  104. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/mock_vws/_services_validators/active_flag_validators.py +0 -0
  105. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/mock_vws/_services_validators/content_length_validators.py +0 -0
  106. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/mock_vws/_services_validators/content_type_validators.py +0 -0
  107. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/mock_vws/_services_validators/date_validators.py +0 -0
  108. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/mock_vws/_services_validators/image_validators.py +0 -0
  109. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/mock_vws/_services_validators/json_validators.py +0 -0
  110. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/mock_vws/_services_validators/key_validators.py +0 -0
  111. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/mock_vws/_services_validators/metadata_validators.py +0 -0
  112. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/mock_vws/_services_validators/width_validators.py +0 -0
  113. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/mock_vws/image_matchers.py +0 -0
  114. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/mock_vws/py.typed +0 -0
  115. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/mock_vws/states.py +0 -0
  116. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/mock_vws/target_raters.py +0 -0
  117. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/vws_python_mock.egg-info/dependency_links.txt +0 -0
  118. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/vws_python_mock.egg-info/not-zip-safe +0 -0
  119. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/src/vws_python_mock.egg-info/top_level.txt +0 -0
  120. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/tests/__init__.py +0 -0
  121. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/tests/mock_vws/__init__.py +0 -0
  122. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/tests/mock_vws/fixtures/__init__.py +0 -0
  123. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/tests/mock_vws/test_add_target.py +0 -0
  124. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/tests/mock_vws/test_content_length.py +0 -0
  125. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/tests/mock_vws/test_date_header.py +0 -0
  126. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/tests/mock_vws/test_delete_target.py +0 -0
  127. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/tests/mock_vws/test_get_duplicates.py +0 -0
  128. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/tests/mock_vws/test_get_target.py +0 -0
  129. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/tests/mock_vws/test_invalid_given_id.py +0 -0
  130. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/tests/mock_vws/test_invalid_json.py +0 -0
  131. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/tests/mock_vws/test_target_list.py +0 -0
  132. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/tests/mock_vws/test_target_raters.py +0 -0
  133. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/tests/mock_vws/test_unexpected_json.py +0 -0
  134. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/tests/mock_vws/test_update_target.py +0 -0
  135. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/tests/mock_vws/utils/__init__.py +0 -0
  136. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/tests/mock_vws/utils/assertions.py +0 -0
  137. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/tests/mock_vws/utils/retries.py +0 -0
  138. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/tests/mock_vws/utils/too_many_requests.py +0 -0
  139. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/vuforia_secrets.env.example +0 -0
  140. {vws_python_mock-2026.2.18.2 → vws_python_mock-2026.2.21}/zizmor.yml +0 -0
@@ -4,6 +4,14 @@ Changelog
4
4
  Next
5
5
  ----
6
6
 
7
+ 2026.02.21
8
+ ----------
9
+
10
+
11
+ - Add ``VuMarkTarget`` class for VuMark template targets, alongside the renamed ``ImageTarget`` class (previously ``Target``).
12
+ ``ImageTarget`` is for image-based targets and ``VuMarkTarget`` is for VuMark template targets.
13
+ Both can be stored in a ``VuforiaDatabase``.
14
+
7
15
  2026.02.18.2
8
16
  ------------
9
17
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vws-python-mock
3
- Version: 2026.2.18.2
3
+ Version: 2026.2.21
4
4
  Summary: A mock for the Vuforia Web Services (VWS) API.
5
5
  Author-email: Adam Dangoor <adamdangoor@gmail.com>
6
6
  License-Expression: MIT
@@ -84,7 +84,7 @@ Requires-Dist: urllib3==2.6.3; extra == "dev"
84
84
  Requires-Dist: vulture==2.14; extra == "dev"
85
85
  Requires-Dist: vws-python==2026.2.15; extra == "dev"
86
86
  Requires-Dist: vws-test-fixtures==2023.3.5; extra == "dev"
87
- Requires-Dist: vws-web-tools==2026.2.17.1; extra == "dev"
87
+ Requires-Dist: vws-web-tools==2026.2.20; extra == "dev"
88
88
  Requires-Dist: yamlfix==1.19.1; extra == "dev"
89
89
  Requires-Dist: zizmor==1.22.0; extra == "dev"
90
90
  Provides-Extra: release
@@ -119,11 +119,11 @@ This requires Python |minimum-python-version|\+.
119
119
  import requests
120
120
 
121
121
  from mock_vws import MockVWS
122
- from mock_vws.database import VuforiaDatabase
122
+ from mock_vws.database import CloudDatabase
123
123
 
124
124
  with MockVWS() as mock:
125
- database = VuforiaDatabase()
126
- mock.add_database(database=database)
125
+ database = CloudDatabase()
126
+ mock.add_cloud_database(cloud_database=database)
127
127
  # This will use the Vuforia mock.
128
128
  requests.get(url="https://vws.vuforia.com/summary", timeout=30)
129
129
 
@@ -26,11 +26,11 @@ This requires Python |minimum-python-version|\+.
26
26
  import requests
27
27
 
28
28
  from mock_vws import MockVWS
29
- from mock_vws.database import VuforiaDatabase
29
+ from mock_vws.database import CloudDatabase
30
30
 
31
31
  with MockVWS() as mock:
32
- database = VuforiaDatabase()
33
- mock.add_database(database=database)
32
+ database = CloudDatabase()
33
+ mock.add_cloud_database(cloud_database=database)
34
34
  # This will use the Vuforia mock.
35
35
  requests.get(url="https://vws.vuforia.com/summary", timeout=30)
36
36
 
@@ -7,11 +7,11 @@ Using the mock redirects requests to Vuforia made with `requests`_ to an in-memo
7
7
  import requests
8
8
 
9
9
  from mock_vws import MockVWS
10
- from mock_vws.database import VuforiaDatabase
10
+ from mock_vws.database import CloudDatabase
11
11
 
12
12
  with MockVWS() as mock:
13
- database = VuforiaDatabase()
14
- mock.add_database(database=database)
13
+ database = CloudDatabase()
14
+ mock.add_cloud_database(cloud_database=database)
15
15
  # This will use the Vuforia mock.
16
16
  requests.get(url="https://vws.vuforia.com/summary", timeout=30)
17
17
 
@@ -54,7 +54,7 @@ To mimic this functionality, this mock provides a target manager container which
54
54
  To add a database, make a request to the following endpoint against the target manager container:
55
55
 
56
56
  .. autoflask:: mock_vws._flask_server.target_manager:TARGET_MANAGER_FLASK_APP
57
- :endpoints: create_database
57
+ :endpoints: create_cloud_database
58
58
 
59
59
  For example, with the containers set up as in :ref:`creating-containers`, use ``curl``:
60
60
 
@@ -63,7 +63,7 @@ For example, with the containers set up as in :ref:`creating-containers`, use ``
63
63
  $ curl --request POST \
64
64
  --header "Content-Type: application/json" \
65
65
  --data '{}' \
66
- '127.0.0.1:5005/databases'
66
+ '127.0.0.1:5005/cloud_databases'
67
67
  {
68
68
  "client_access_key": "2d61c1d17bb94694bee77c1f1f41e5d9",
69
69
  "client_secret_key": "b73f8170cf7d42728fa8ce66221ad147",
@@ -80,7 +80,7 @@ Deleting a database
80
80
  To delete a database use the following endpoint:
81
81
 
82
82
  .. autoflask:: mock_vws._flask_server.target_manager:TARGET_MANAGER_FLASK_APP
83
- :endpoints: delete_database
83
+ :endpoints: delete_cloud_database
84
84
 
85
85
 
86
86
  .. _Target Manager: https://developer.vuforia.com/target-manager
@@ -11,20 +11,31 @@ API Reference
11
11
  :members:
12
12
  :undoc-members:
13
13
 
14
- .. Many parts of the VuforiaDatabase API are used for the Flask target
14
+ .. Many parts of the CloudDatabase API are used for the Flask target
15
15
  .. database app, but Python users are not expected to use them.
16
16
  .. Therefore, they are not documented.
17
17
 
18
- .. autoclass:: mock_vws.database.VuforiaDatabase
18
+ .. autoclass:: mock_vws.database.CloudDatabase
19
19
  :members:
20
20
  :undoc-members:
21
21
  :exclude-members: to_dict, get_target, from_dict, not_deleted_targets, active_targets, inactive_targets, failed_targets, processing_targets
22
22
 
23
+ .. autoclass:: mock_vws.database.VuMarkDatabase
24
+ :members:
25
+ :undoc-members:
26
+ :exclude-members: to_dict, from_dict, not_deleted_targets
27
+
23
28
  .. autoenum:: mock_vws.states.States
24
29
  :members:
25
30
  :undoc-members:
26
31
 
27
- .. autoclass:: mock_vws.target.Target
32
+ .. autoenum:: mock_vws.database_type.DatabaseType
33
+ :members:
34
+ :undoc-members:
35
+
36
+ .. autoclass:: mock_vws.target.ImageTarget
37
+
38
+ .. autoclass:: mock_vws.target.VuMarkTarget
28
39
 
29
40
  Image matchers
30
41
  --------------
@@ -105,7 +105,7 @@ optional-dependencies.dev = [
105
105
  "vulture==2.14",
106
106
  "vws-python==2026.2.15",
107
107
  "vws-test-fixtures==2023.3.5",
108
- "vws-web-tools==2026.2.17.1",
108
+ "vws-web-tools==2026.2.20",
109
109
  "yamlfix==1.19.1",
110
110
  "zizmor==1.22.0",
111
111
  ]
@@ -450,8 +450,9 @@ ignore_names = [
450
450
  # pydantic-settings
451
451
  "model_config",
452
452
  # Used in TYPE_CHECKING for type hints
453
- "DatabaseDict",
453
+ "CloudDatabaseDict",
454
454
  "VuMarkDatabaseDict",
455
+ "VuMarkTargetDict",
455
456
  ]
456
457
  # Duplicate some of .gitignore
457
458
  exclude = [ ".venv" ]
@@ -3,6 +3,7 @@ MPixel
3
3
  MiB
4
4
  MissingSchema
5
5
  Ubuntu
6
+ VuMark
6
7
  admin
7
8
  another's
8
9
  api
@@ -63,6 +63,7 @@ class ResultCodes(Enum):
63
63
  INVALID_ACCEPT_HEADER = "InvalidAcceptHeader"
64
64
  INVALID_INSTANCE_ID = "InvalidInstanceId"
65
65
  BAD_REQUEST = "BadRequest"
66
+ INVALID_TARGET_TYPE = "InvalidTargetType"
66
67
 
67
68
 
68
69
  @beartype
@@ -5,7 +5,9 @@ from collections.abc import Iterable, Mapping
5
5
  from beartype import beartype
6
6
  from vws_auth_tools import authorization_header
7
7
 
8
- from mock_vws.database import VuforiaDatabase
8
+ from mock_vws.database import CloudDatabase, VuMarkDatabase
9
+
10
+ AnyDatabase = CloudDatabase | VuMarkDatabase
9
11
 
10
12
 
11
13
  @beartype
@@ -15,8 +17,8 @@ def get_database_matching_client_keys(
15
17
  request_body: bytes | None,
16
18
  request_method: str,
17
19
  request_path: str,
18
- databases: Iterable[VuforiaDatabase],
19
- ) -> VuforiaDatabase:
20
+ databases: Iterable[CloudDatabase],
21
+ ) -> CloudDatabase:
20
22
  """Return the first of the given databases which is being accessed by
21
23
  the
22
24
  given client request.
@@ -58,14 +60,14 @@ def get_database_matching_client_keys(
58
60
 
59
61
 
60
62
  @beartype
61
- def get_database_matching_server_keys(
63
+ def get_database_matching_server_keys[DatabaseT: AnyDatabase](
62
64
  *,
63
65
  request_headers: Mapping[str, str],
64
66
  request_body: bytes | None,
65
67
  request_method: str,
66
68
  request_path: str,
67
- databases: Iterable[VuforiaDatabase],
68
- ) -> VuforiaDatabase:
69
+ databases: Iterable[DatabaseT],
70
+ ) -> DatabaseT:
69
71
  """Return the first of the given databases which is being accessed by
70
72
  the
71
73
  given server request.
@@ -12,9 +12,10 @@ from beartype import beartype
12
12
  from flask import Flask, Response, request
13
13
  from pydantic_settings import BaseSettings
14
14
 
15
- from mock_vws.database import VuforiaDatabase
15
+ from mock_vws.database import CloudDatabase, VuMarkDatabase
16
+ from mock_vws.database_type import DatabaseType
16
17
  from mock_vws.states import States
17
- from mock_vws.target import Target
18
+ from mock_vws.target import ImageTarget, VuMarkTarget
18
19
  from mock_vws.target_manager import TargetManager
19
20
  from mock_vws.target_raters import (
20
21
  BrisqueTargetTrackingRater,
@@ -58,82 +59,127 @@ class TargetManagerSettings(BaseSettings):
58
59
 
59
60
 
60
61
  @TARGET_MANAGER_FLASK_APP.route(
61
- rule="/databases/<string:database_name>",
62
+ rule="/cloud_databases/<string:database_name>",
62
63
  methods=[HTTPMethod.DELETE],
63
64
  )
64
65
  @beartype
65
- def delete_database(database_name: str) -> Response:
66
- """Delete a database.
66
+ def delete_cloud_database(database_name: str) -> Response:
67
+ """Delete a cloud database.
67
68
 
68
- :status 200: The database has been deleted.
69
+ :status 200: The cloud database has been deleted.
69
70
  """
70
71
  try:
71
72
  (matching_database,) = {
72
73
  database
73
- for database in TARGET_MANAGER.databases
74
+ for database in TARGET_MANAGER.cloud_databases
74
75
  if database_name == database.database_name
75
76
  }
76
77
  except ValueError:
77
78
  return Response(response="", status=HTTPStatus.NOT_FOUND)
78
79
 
79
- TARGET_MANAGER.remove_database(database=matching_database)
80
+ TARGET_MANAGER.remove_cloud_database(cloud_database=matching_database)
80
81
  return Response(response="", status=HTTPStatus.OK)
81
82
 
82
83
 
83
- @TARGET_MANAGER_FLASK_APP.route(rule="/databases", methods=[HTTPMethod.GET])
84
+ @TARGET_MANAGER_FLASK_APP.route(
85
+ rule="/vumark_databases/<string:database_name>",
86
+ methods=[HTTPMethod.DELETE],
87
+ )
88
+ @beartype
89
+ def delete_vumark_database(database_name: str) -> Response:
90
+ """Delete a VuMark database.
91
+
92
+ :status 200: The VuMark database has been deleted.
93
+ """
94
+ (matching_database,) = {
95
+ database
96
+ for database in TARGET_MANAGER.vumark_databases
97
+ if database_name == database.database_name
98
+ }
99
+ TARGET_MANAGER.remove_vumark_database(vumark_database=matching_database)
100
+ return Response(response="", status=HTTPStatus.OK)
101
+
102
+
103
+ @TARGET_MANAGER_FLASK_APP.route(
104
+ rule="/cloud_databases", methods=[HTTPMethod.GET]
105
+ )
84
106
  @beartype
85
- def get_databases() -> Response:
86
- """Return a list of all databases."""
87
- databases = [database.to_dict() for database in TARGET_MANAGER.databases]
107
+ def get_cloud_databases() -> Response:
108
+ """Return a list of all cloud databases."""
109
+ databases = [
110
+ database.to_dict() for database in TARGET_MANAGER.cloud_databases
111
+ ]
88
112
  return Response(
89
113
  response=json.dumps(obj=databases),
90
114
  status=HTTPStatus.OK,
91
115
  )
92
116
 
93
117
 
94
- @TARGET_MANAGER_FLASK_APP.route(rule="/databases", methods=[HTTPMethod.POST])
118
+ @TARGET_MANAGER_FLASK_APP.route(
119
+ rule="/vumark_databases",
120
+ methods=[HTTPMethod.GET],
121
+ )
95
122
  @beartype
96
- def create_database() -> Response:
97
- """Create a new database.
123
+ def get_vumark_databases() -> Response:
124
+ """Return a list of all VuMark databases."""
125
+ databases = [
126
+ database.to_dict() for database in TARGET_MANAGER.vumark_databases
127
+ ]
128
+ return Response(
129
+ response=json.dumps(obj=databases),
130
+ status=HTTPStatus.OK,
131
+ )
132
+
133
+
134
+ @TARGET_MANAGER_FLASK_APP.route(
135
+ rule="/cloud_databases", methods=[HTTPMethod.POST]
136
+ )
137
+ @beartype
138
+ def create_cloud_database() -> Response:
139
+ """Create a new cloud database.
98
140
 
99
141
  :reqheader Content-Type: application/json
100
142
  :resheader Content-Type: application/json
101
143
 
102
144
  :reqjson string client_access_key: (Optional) The client access key for the
103
- database.
145
+ cloud database.
104
146
 
105
147
  :reqjson string client_secret_key: (Optional) The client secret key for the
106
- database.
148
+ cloud database.
107
149
 
108
- :reqjson string database_name: (Optional) The name of the database.
150
+ :reqjson string database_name: (Optional) The name of the cloud database.
109
151
 
110
152
  :reqjson string server_access_key: (Optional) The server access key for the
111
- database.
153
+ cloud database.
112
154
 
113
155
  :reqjson string server_secret_key: (Optional) The server secret key for the
114
- database.
156
+ cloud database.
115
157
 
116
- :reqjson string state_name: (Optional) The state of the database. This can
117
- be "WORKING" or "PROJECT_INACTIVE". This defaults to "WORKING".
158
+ :reqjson string state_name: (Optional) The state of the cloud database.
159
+ This can be "WORKING" or "PROJECT_INACTIVE". This defaults to "WORKING".
118
160
 
119
- :resjson string client_access_key: The client access key for the database.
161
+ :resjson string client_access_key: The client access key for the cloud
162
+ database.
120
163
 
121
- :resjson string client_secret_key: The client secret key for the database.
164
+ :resjson string client_secret_key: The client secret key for the cloud
165
+ database.
122
166
 
123
- :resjson string database_name: The database name.
167
+ :resjson string database_name: The cloud database name.
124
168
 
125
- :resjson string server_access_key: The server access key for the database.
169
+ :resjson string server_access_key: The server access key for the cloud
170
+ database.
126
171
 
127
- :resjson string server_secret_key: The server secret key for the database.
172
+ :resjson string server_secret_key: The server secret key for the cloud
173
+ database.
128
174
 
129
- :resjson string state_name: The database state. This will be "WORKING" or
130
- "PROJECT_INACTIVE".
175
+ :resjson string state_name: The cloud database state. This will be
176
+ "WORKING" or "PROJECT_INACTIVE".
131
177
 
132
- :reqjsonarr targets: The targets in the database.
178
+ :reqjsonarr targets: The targets in the cloud database.
133
179
 
134
- :status 201: The database has been successfully created.
180
+ :status 201: The cloud database has been successfully created.
135
181
  """
136
- random_database = VuforiaDatabase()
182
+ random_database = CloudDatabase()
137
183
  request_json = json.loads(s=request.data)
138
184
  server_access_key = request_json.get(
139
185
  "server_access_key",
@@ -159,19 +205,66 @@ def create_database() -> Response:
159
205
  "state_name",
160
206
  random_database.state.name,
161
207
  )
208
+ database_type_name = request_json.get(
209
+ "database_type_name",
210
+ random_database.database_type.name,
211
+ )
162
212
 
163
213
  state = States[state_name]
214
+ database_type = DatabaseType[database_type_name]
164
215
 
165
- database = VuforiaDatabase(
216
+ database = CloudDatabase(
166
217
  server_access_key=server_access_key,
167
218
  server_secret_key=server_secret_key,
168
219
  client_access_key=client_access_key,
169
220
  client_secret_key=client_secret_key,
170
221
  database_name=database_name,
171
222
  state=state,
223
+ database_type=database_type,
224
+ )
225
+ try:
226
+ TARGET_MANAGER.add_cloud_database(cloud_database=database)
227
+ except ValueError as exc:
228
+ return Response(
229
+ response=str(object=exc),
230
+ status=HTTPStatus.CONFLICT,
231
+ )
232
+
233
+ return Response(
234
+ response=json.dumps(obj=database.to_dict()),
235
+ status=HTTPStatus.CREATED,
236
+ )
237
+
238
+
239
+ @TARGET_MANAGER_FLASK_APP.route(
240
+ rule="/vumark_databases",
241
+ methods=[HTTPMethod.POST],
242
+ )
243
+ @beartype
244
+ def create_vumark_database() -> Response:
245
+ """Create a new VuMark database.
246
+
247
+ :status 201: The database has been successfully created.
248
+ """
249
+ request_json = json.loads(s=request.data)
250
+ random_vumark_database = VuMarkDatabase()
251
+ database = VuMarkDatabase(
252
+ server_access_key=request_json.get(
253
+ "server_access_key",
254
+ random_vumark_database.server_access_key,
255
+ ),
256
+ server_secret_key=request_json.get(
257
+ "server_secret_key",
258
+ random_vumark_database.server_secret_key,
259
+ ),
260
+ database_name=request_json.get(
261
+ "database_name",
262
+ random_vumark_database.database_name,
263
+ ),
172
264
  )
265
+
173
266
  try:
174
- TARGET_MANAGER.add_database(database=database)
267
+ TARGET_MANAGER.add_vumark_database(vumark_database=database)
175
268
  except ValueError as exc:
176
269
  return Response(
177
270
  response=str(object=exc),
@@ -185,24 +278,23 @@ def create_database() -> Response:
185
278
 
186
279
 
187
280
  @TARGET_MANAGER_FLASK_APP.route(
188
- rule="/databases/<string:database_name>/targets",
281
+ rule="/cloud_databases/<string:database_name>/targets",
189
282
  methods=[HTTPMethod.POST],
190
283
  )
191
284
  @beartype
192
285
  def create_target(database_name: str) -> Response:
193
- """Create a new target in a given database."""
286
+ """Create a new target in a given cloud database."""
194
287
  (database,) = (
195
288
  database
196
- for database in TARGET_MANAGER.databases
289
+ for database in TARGET_MANAGER.cloud_databases
197
290
  if database.database_name == database_name
198
291
  )
199
292
  request_json = json.loads(s=request.data)
200
- image_base64 = request_json["image_base64"]
201
- image_bytes = base64.b64decode(s=image_base64)
202
293
  settings = TargetManagerSettings.model_validate(obj={})
203
- target_tracking_rater = settings.target_rater.to_target_rater()
204
294
 
205
- target = Target(
295
+ image_bytes = base64.b64decode(s=request_json["image_base64"])
296
+ target_tracking_rater = settings.target_rater.to_target_rater()
297
+ target = ImageTarget(
206
298
  name=request_json["name"],
207
299
  width=request_json["width"],
208
300
  image_value=image_bytes,
@@ -221,7 +313,29 @@ def create_target(database_name: str) -> Response:
221
313
 
222
314
 
223
315
  @TARGET_MANAGER_FLASK_APP.route(
224
- rule="/databases/<string:database_name>/targets/<string:target_id>",
316
+ rule="/vumark_databases/<string:database_name>/vumark_targets",
317
+ methods=[HTTPMethod.POST],
318
+ )
319
+ @beartype
320
+ def create_vumark_target(database_name: str) -> Response:
321
+ """Create a new VuMark target in a given database."""
322
+ (database,) = (
323
+ database
324
+ for database in TARGET_MANAGER.vumark_databases
325
+ if database.database_name == database_name
326
+ )
327
+ request_json = json.loads(s=request.data)
328
+ target = VuMarkTarget.from_dict(target_dict=request_json)
329
+ database.vumark_targets.add(target)
330
+
331
+ return Response(
332
+ response=json.dumps(obj=target.to_dict()),
333
+ status=HTTPStatus.CREATED,
334
+ )
335
+
336
+
337
+ @TARGET_MANAGER_FLASK_APP.route(
338
+ rule="/cloud_databases/<string:database_name>/targets/<string:target_id>",
225
339
  methods={HTTPMethod.DELETE},
226
340
  )
227
341
  @beartype
@@ -229,13 +343,13 @@ def delete_target(database_name: str, target_id: str) -> Response:
229
343
  """Delete a target."""
230
344
  (database,) = (
231
345
  database
232
- for database in TARGET_MANAGER.databases
346
+ for database in TARGET_MANAGER.cloud_databases
233
347
  if database.database_name == database_name
234
348
  )
235
349
  target = database.get_target(target_id=target_id)
236
350
  now = datetime.datetime.now(tz=target.upload_date.tzinfo)
237
351
  # See https://github.com/facebook/pyrefly/issues/1897
238
- new_target = copy.replace(
352
+ new_target: ImageTarget = copy.replace(
239
353
  target, # pyrefly: ignore[bad-argument-type]
240
354
  delete_date=now,
241
355
  )
@@ -248,37 +362,35 @@ def delete_target(database_name: str, target_id: str) -> Response:
248
362
 
249
363
 
250
364
  @TARGET_MANAGER_FLASK_APP.route(
251
- rule="/databases/<string:database_name>/targets/<string:target_id>",
365
+ rule="/cloud_databases/<string:database_name>/targets/<string:target_id>",
252
366
  methods=[HTTPMethod.PUT],
253
367
  )
254
368
  def update_target(database_name: str, target_id: str) -> Response:
255
369
  """Update a target."""
256
370
  (database,) = (
257
371
  database
258
- for database in TARGET_MANAGER.databases
372
+ for database in TARGET_MANAGER.cloud_databases
259
373
  if database.database_name == database_name
260
374
  )
261
375
  target = database.get_target(target_id=target_id)
262
376
 
263
377
  request_json = json.loads(s=request.data)
264
- width = request_json.get("width", target.width)
265
378
  name = request_json.get("name", target.name)
266
379
  active_flag = request_json.get("active_flag", target.active_flag)
380
+
381
+ gmt = ZoneInfo(key="GMT")
382
+ last_modified_date = datetime.datetime.now(tz=gmt)
383
+
384
+ width = request_json.get("width", target.width)
267
385
  application_metadata = request_json.get(
268
386
  "application_metadata",
269
387
  target.application_metadata,
270
388
  )
271
-
272
389
  image_value = target.image_value
273
- request_json = json.loads(s=request.data)
274
390
  if "image" in request_json:
275
391
  image_value = base64.b64decode(s=request_json["image"])
276
-
277
- gmt = ZoneInfo(key="GMT")
278
- last_modified_date = datetime.datetime.now(tz=gmt)
279
-
280
392
  # See https://github.com/facebook/pyrefly/issues/1897
281
- new_target = copy.replace(
393
+ new_target: ImageTarget = copy.replace(
282
394
  target, # pyrefly: ignore[bad-argument-type]
283
395
  name=name,
284
396
  width=width,
@@ -21,7 +21,7 @@ from mock_vws._query_validators import run_query_validators
21
21
  from mock_vws._query_validators.exceptions import (
22
22
  ValidatorError,
23
23
  )
24
- from mock_vws.database import VuforiaDatabase
24
+ from mock_vws.database import CloudDatabase
25
25
  from mock_vws.image_matchers import (
26
26
  ExactMatcher,
27
27
  ImageMatcher,
@@ -63,15 +63,15 @@ class VWQSettings(BaseSettings):
63
63
 
64
64
 
65
65
  @beartype
66
- def get_all_databases() -> set[VuforiaDatabase]:
66
+ def get_all_cloud_databases() -> set[CloudDatabase]:
67
67
  """Get all database objects from the target manager back-end."""
68
68
  settings = VWQSettings.model_validate(obj={})
69
69
  response = requests.get(
70
- url=f"{settings.target_manager_base_url}/databases",
70
+ url=f"{settings.target_manager_base_url}/cloud_databases",
71
71
  timeout=30,
72
72
  )
73
73
  return {
74
- VuforiaDatabase.from_dict(database_dict=database_dict)
74
+ CloudDatabase.from_dict(database_dict=database_dict)
75
75
  for database_dict in response.json()
76
76
  }
77
77
 
@@ -132,7 +132,7 @@ def query() -> Response:
132
132
  settings = VWQSettings.model_validate(obj={})
133
133
  query_match_checker = settings.query_image_matcher.to_image_matcher()
134
134
 
135
- databases = get_all_databases()
135
+ databases = get_all_cloud_databases()
136
136
  request_body = request.stream.read()
137
137
  run_query_validators(
138
138
  request_headers=dict(request.headers),