vws-python-mock 2026.8.4__py3-none-any.whl
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.
- mock_vws/__init__.py +9 -0
- mock_vws/_base64_decoding.py +35 -0
- mock_vws/_constants.py +84 -0
- mock_vws/_database_matchers.py +107 -0
- mock_vws/_flask_server/Dockerfile +32 -0
- mock_vws/_flask_server/__init__.py +1 -0
- mock_vws/_flask_server/healthcheck.py +31 -0
- mock_vws/_flask_server/target_manager.py +447 -0
- mock_vws/_flask_server/vwq.py +173 -0
- mock_vws/_flask_server/vws.py +954 -0
- mock_vws/_mock_common.py +75 -0
- mock_vws/_model_target_web_api.py +486 -0
- mock_vws/_query_tools.py +136 -0
- mock_vws/_query_validators/__init__.py +128 -0
- mock_vws/_query_validators/accept_header_validators.py +31 -0
- mock_vws/_query_validators/auth_validators.py +143 -0
- mock_vws/_query_validators/content_length_validators.py +90 -0
- mock_vws/_query_validators/content_type_validators.py +65 -0
- mock_vws/_query_validators/date_validators.py +110 -0
- mock_vws/_query_validators/exceptions.py +769 -0
- mock_vws/_query_validators/fields_validators.py +47 -0
- mock_vws/_query_validators/image_validators.py +197 -0
- mock_vws/_query_validators/include_target_data_validators.py +51 -0
- mock_vws/_query_validators/num_results_validators.py +64 -0
- mock_vws/_query_validators/project_state_validators.py +49 -0
- mock_vws/_requests_mock_server/__init__.py +1 -0
- mock_vws/_requests_mock_server/decorators.py +275 -0
- mock_vws/_requests_mock_server/mock_web_query_api.py +139 -0
- mock_vws/_requests_mock_server/mock_web_services_api.py +954 -0
- mock_vws/_respx_mock_server/__init__.py +1 -0
- mock_vws/_respx_mock_server/decorators.py +186 -0
- mock_vws/_services_validators/__init__.py +186 -0
- mock_vws/_services_validators/active_flag_validators.py +43 -0
- mock_vws/_services_validators/auth_validators.py +124 -0
- mock_vws/_services_validators/content_length_validators.py +102 -0
- mock_vws/_services_validators/content_type_validators.py +42 -0
- mock_vws/_services_validators/date_validators.py +78 -0
- mock_vws/_services_validators/exceptions.py +858 -0
- mock_vws/_services_validators/image_validators.py +220 -0
- mock_vws/_services_validators/json_validators.py +69 -0
- mock_vws/_services_validators/key_validators.py +171 -0
- mock_vws/_services_validators/metadata_validators.py +106 -0
- mock_vws/_services_validators/name_validators.py +235 -0
- mock_vws/_services_validators/project_state_validators.py +76 -0
- mock_vws/_services_validators/request_quota_validators.py +42 -0
- mock_vws/_services_validators/target_quota_validators.py +41 -0
- mock_vws/_services_validators/target_validators.py +69 -0
- mock_vws/_services_validators/width_validators.py +38 -0
- mock_vws/database.py +257 -0
- mock_vws/database_type.py +13 -0
- mock_vws/image_matchers.py +124 -0
- mock_vws/model_target.py +79 -0
- mock_vws/py.typed +0 -0
- mock_vws/states.py +20 -0
- mock_vws/target.py +285 -0
- mock_vws/target_manager.py +178 -0
- mock_vws/target_raters.py +109 -0
- vws_python_mock-2026.8.4.dist-info/METADATA +177 -0
- vws_python_mock-2026.8.4.dist-info/RECORD +62 -0
- vws_python_mock-2026.8.4.dist-info/WHEEL +5 -0
- vws_python_mock-2026.8.4.dist-info/licenses/LICENSE +21 -0
- vws_python_mock-2026.8.4.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,954 @@
|
|
|
1
|
+
"""A fake implementation of the Vuforia Web Services API.
|
|
2
|
+
|
|
3
|
+
See
|
|
4
|
+
https://developer.vuforia.com/library/web-api/cloud-targets-web-services-api
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import base64
|
|
8
|
+
import email.utils
|
|
9
|
+
import json
|
|
10
|
+
import logging
|
|
11
|
+
import time
|
|
12
|
+
import uuid
|
|
13
|
+
from enum import StrEnum, auto
|
|
14
|
+
from http import HTTPMethod, HTTPStatus
|
|
15
|
+
from typing import assert_never
|
|
16
|
+
|
|
17
|
+
import requests
|
|
18
|
+
from beartype import beartype
|
|
19
|
+
from flask import Flask, Response, request
|
|
20
|
+
from pydantic_settings import BaseSettings
|
|
21
|
+
|
|
22
|
+
from mock_vws._constants import (
|
|
23
|
+
VUMARK_PDF,
|
|
24
|
+
VUMARK_PNG,
|
|
25
|
+
VUMARK_SVG,
|
|
26
|
+
ResultCodes,
|
|
27
|
+
TargetStatuses,
|
|
28
|
+
)
|
|
29
|
+
from mock_vws._database_matchers import get_database_matching_server_keys
|
|
30
|
+
from mock_vws._flask_server.target_manager import TARGET_MANAGER
|
|
31
|
+
from mock_vws._mock_common import RequestData, json_dump
|
|
32
|
+
from mock_vws._model_target_web_api import (
|
|
33
|
+
create_model_target_dataset,
|
|
34
|
+
delete_model_target_dataset,
|
|
35
|
+
download_model_target_dataset,
|
|
36
|
+
get_model_target_dataset_status,
|
|
37
|
+
)
|
|
38
|
+
from mock_vws._model_target_web_api import (
|
|
39
|
+
oauth2_token as model_target_oauth2_token,
|
|
40
|
+
)
|
|
41
|
+
from mock_vws._services_validators import run_services_validators
|
|
42
|
+
from mock_vws._services_validators.exceptions import (
|
|
43
|
+
FailError,
|
|
44
|
+
InvalidAcceptHeaderError,
|
|
45
|
+
InvalidInstanceIdError,
|
|
46
|
+
InvalidTargetTypeError,
|
|
47
|
+
TargetStatusNotSuccessError,
|
|
48
|
+
TargetStatusProcessingError,
|
|
49
|
+
ValidatorError,
|
|
50
|
+
)
|
|
51
|
+
from mock_vws.database import CloudDatabase, VuMarkDatabase
|
|
52
|
+
from mock_vws.image_matchers import (
|
|
53
|
+
ExactMatcher,
|
|
54
|
+
ImageMatcher,
|
|
55
|
+
StructuralSimilarityMatcher,
|
|
56
|
+
)
|
|
57
|
+
from mock_vws.model_target import ModelTargetDatasetType
|
|
58
|
+
from mock_vws.target import ImageTarget
|
|
59
|
+
from mock_vws.target_manager import TargetManager
|
|
60
|
+
from mock_vws.target_raters import (
|
|
61
|
+
HardcodedTargetTrackingRater,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
VWS_FLASK_APP = Flask(import_name=__name__, static_folder=None)
|
|
65
|
+
VWS_FLASK_APP.config["PROPAGATE_EXCEPTIONS"] = True
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
_LOGGER = logging.getLogger(name=__name__)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@beartype
|
|
72
|
+
class _ImageMatcherChoice(StrEnum):
|
|
73
|
+
"""Image matcher choices."""
|
|
74
|
+
|
|
75
|
+
EXACT = auto()
|
|
76
|
+
STRUCTURAL_SIMILARITY = auto()
|
|
77
|
+
|
|
78
|
+
def to_image_matcher(self: _ImageMatcherChoice) -> ImageMatcher:
|
|
79
|
+
"""Get the image matcher."""
|
|
80
|
+
match self:
|
|
81
|
+
case _ImageMatcherChoice.EXACT:
|
|
82
|
+
return ExactMatcher()
|
|
83
|
+
case _ImageMatcherChoice.STRUCTURAL_SIMILARITY:
|
|
84
|
+
return StructuralSimilarityMatcher()
|
|
85
|
+
case _ as unreachable:
|
|
86
|
+
assert_never(unreachable)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@beartype
|
|
90
|
+
class VWSSettings(BaseSettings):
|
|
91
|
+
"""Settings for the VWS Flask app."""
|
|
92
|
+
|
|
93
|
+
target_manager_base_url: str
|
|
94
|
+
processing_time_seconds: float = 2.0
|
|
95
|
+
vws_host: str = ""
|
|
96
|
+
duplicates_image_matcher: _ImageMatcherChoice = (
|
|
97
|
+
_ImageMatcherChoice.STRUCTURAL_SIMILARITY
|
|
98
|
+
)
|
|
99
|
+
response_delay_seconds: float = 0.0
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
@beartype
|
|
103
|
+
def get_all_cloud_databases() -> set[CloudDatabase]:
|
|
104
|
+
"""Get all database objects from the task manager back-end."""
|
|
105
|
+
settings = VWSSettings.model_validate(obj={})
|
|
106
|
+
timeout_seconds = 30
|
|
107
|
+
response = requests.get(
|
|
108
|
+
url=f"{settings.target_manager_base_url}/cloud_databases",
|
|
109
|
+
timeout=timeout_seconds,
|
|
110
|
+
)
|
|
111
|
+
return {
|
|
112
|
+
CloudDatabase.from_dict(database_dict=database_dict)
|
|
113
|
+
for database_dict in response.json()
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
@beartype
|
|
118
|
+
def get_all_vumark_databases() -> set[VuMarkDatabase]:
|
|
119
|
+
"""Get all VuMark database objects from the task manager back-end."""
|
|
120
|
+
settings = VWSSettings.model_validate(obj={})
|
|
121
|
+
timeout_seconds = 30
|
|
122
|
+
response = requests.get(
|
|
123
|
+
url=f"{settings.target_manager_base_url}/vumark_databases",
|
|
124
|
+
timeout=timeout_seconds,
|
|
125
|
+
)
|
|
126
|
+
return {
|
|
127
|
+
VuMarkDatabase.from_dict(database_dict=database_dict)
|
|
128
|
+
for database_dict in response.json()
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
@beartype
|
|
133
|
+
def _flask_request_data() -> RequestData:
|
|
134
|
+
"""Return the current Flask request as shared request data."""
|
|
135
|
+
return RequestData(
|
|
136
|
+
method=request.method,
|
|
137
|
+
path=request.path,
|
|
138
|
+
headers=dict(request.headers),
|
|
139
|
+
body=request.get_data(parse_form_data=False),
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
@beartype
|
|
144
|
+
def _model_target_manager() -> TargetManager:
|
|
145
|
+
"""Return the target manager backing the Flask app."""
|
|
146
|
+
return TARGET_MANAGER
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
@beartype
|
|
150
|
+
def _to_flask_response(
|
|
151
|
+
api_response: tuple[int, dict[str, str], str | bytes],
|
|
152
|
+
) -> Response:
|
|
153
|
+
"""Convert a shared API response to a Flask response."""
|
|
154
|
+
status_code, headers, body = api_response
|
|
155
|
+
return Response(response=body, status=status_code, headers=headers)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
@VWS_FLASK_APP.before_request
|
|
159
|
+
@beartype
|
|
160
|
+
def set_terminate_wsgi_input() -> None:
|
|
161
|
+
"""We set ``wsgi.input_terminated`` to ``True`` when going through
|
|
162
|
+
``requests`` in our tests, so that requests have the given ``Content-
|
|
163
|
+
Length`` headers and the given data in ``request.headers`` and
|
|
164
|
+
``request.data``.
|
|
165
|
+
|
|
166
|
+
We do not set this at all when running an application as standalone.
|
|
167
|
+
This is because when running the Flask application, if this is set,
|
|
168
|
+
reading ``request.data`` hangs.
|
|
169
|
+
|
|
170
|
+
Therefore, when running the real Flask application, the behavior is not the
|
|
171
|
+
same as the real Vuforia.
|
|
172
|
+
This is documented as a difference in the documentation for this package.
|
|
173
|
+
"""
|
|
174
|
+
try:
|
|
175
|
+
set_terminate_wsgi_input_true = (
|
|
176
|
+
VWS_FLASK_APP.config["VWS_MOCK_TERMINATE_WSGI_INPUT"] is True
|
|
177
|
+
)
|
|
178
|
+
except KeyError:
|
|
179
|
+
set_terminate_wsgi_input_true = False
|
|
180
|
+
|
|
181
|
+
if set_terminate_wsgi_input_true:
|
|
182
|
+
request.environ["wsgi.input_terminated"] = True
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
@VWS_FLASK_APP.before_request
|
|
186
|
+
@beartype
|
|
187
|
+
def validate_request() -> None:
|
|
188
|
+
"""Run validators on the request.
|
|
189
|
+
|
|
190
|
+
The VuMark endpoint does its own validation because it needs to
|
|
191
|
+
authenticate against both cloud and VuMark databases.
|
|
192
|
+
"""
|
|
193
|
+
if request.endpoint == "generate_vumark_instance":
|
|
194
|
+
return
|
|
195
|
+
if request.path == "/oauth2/token" or request.path.startswith(
|
|
196
|
+
"/modeltargets/",
|
|
197
|
+
):
|
|
198
|
+
return
|
|
199
|
+
run_services_validators(
|
|
200
|
+
request_headers=dict(request.headers),
|
|
201
|
+
request_body=request.data,
|
|
202
|
+
request_method=request.method,
|
|
203
|
+
request_path=request.path,
|
|
204
|
+
databases=get_all_cloud_databases(),
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
@VWS_FLASK_APP.after_request
|
|
209
|
+
@beartype
|
|
210
|
+
def add_response_delay(response: Response) -> Response:
|
|
211
|
+
"""Add a delay to each response."""
|
|
212
|
+
settings = VWSSettings.model_validate(obj={})
|
|
213
|
+
time.sleep(settings.response_delay_seconds)
|
|
214
|
+
return response
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
@VWS_FLASK_APP.errorhandler(code_or_exception=ValidatorError)
|
|
218
|
+
@beartype
|
|
219
|
+
def handle_exceptions(exc: ValidatorError) -> Response:
|
|
220
|
+
"""Return the error response associated with the given exception."""
|
|
221
|
+
response = Response(
|
|
222
|
+
status=exc.status_code.value,
|
|
223
|
+
response=exc.response_text,
|
|
224
|
+
headers=exc.headers,
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
response.headers.clear()
|
|
228
|
+
response.headers.extend(exc.headers)
|
|
229
|
+
return response
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
@VWS_FLASK_APP.route(rule="/oauth2/token", methods=[HTTPMethod.POST])
|
|
233
|
+
@beartype
|
|
234
|
+
def oauth2_token() -> Response:
|
|
235
|
+
"""Obtain an OAuth2 token for the Model Target Web API."""
|
|
236
|
+
return _to_flask_response(
|
|
237
|
+
api_response=model_target_oauth2_token(
|
|
238
|
+
request=_flask_request_data(),
|
|
239
|
+
),
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
@VWS_FLASK_APP.route(
|
|
244
|
+
rule="/modeltargets/datasets",
|
|
245
|
+
methods=[HTTPMethod.POST],
|
|
246
|
+
)
|
|
247
|
+
@beartype
|
|
248
|
+
def create_standard_model_target_dataset() -> Response:
|
|
249
|
+
"""Create a standard Model Target dataset."""
|
|
250
|
+
settings = VWSSettings.model_validate(obj={})
|
|
251
|
+
return _to_flask_response(
|
|
252
|
+
api_response=create_model_target_dataset(
|
|
253
|
+
request=_flask_request_data(),
|
|
254
|
+
target_manager=_model_target_manager(),
|
|
255
|
+
processing_time_seconds=settings.processing_time_seconds,
|
|
256
|
+
dataset_type=ModelTargetDatasetType.STANDARD,
|
|
257
|
+
),
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
@VWS_FLASK_APP.route(
|
|
262
|
+
rule="/modeltargets/advancedDatasets",
|
|
263
|
+
methods=[HTTPMethod.POST],
|
|
264
|
+
)
|
|
265
|
+
@beartype
|
|
266
|
+
def create_advanced_model_target_dataset() -> Response:
|
|
267
|
+
"""Create an advanced Model Target dataset."""
|
|
268
|
+
settings = VWSSettings.model_validate(obj={})
|
|
269
|
+
return _to_flask_response(
|
|
270
|
+
api_response=create_model_target_dataset(
|
|
271
|
+
request=_flask_request_data(),
|
|
272
|
+
target_manager=_model_target_manager(),
|
|
273
|
+
processing_time_seconds=settings.processing_time_seconds,
|
|
274
|
+
dataset_type=ModelTargetDatasetType.ADVANCED,
|
|
275
|
+
),
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
@VWS_FLASK_APP.route(
|
|
280
|
+
rule="/modeltargets/datasets/<string:dataset_uuid>/status",
|
|
281
|
+
methods=[HTTPMethod.GET],
|
|
282
|
+
)
|
|
283
|
+
@beartype
|
|
284
|
+
def get_standard_model_target_dataset_status(
|
|
285
|
+
dataset_uuid: str,
|
|
286
|
+
) -> Response:
|
|
287
|
+
"""Return a standard Model Target dataset creation status."""
|
|
288
|
+
return _to_flask_response(
|
|
289
|
+
api_response=get_model_target_dataset_status(
|
|
290
|
+
request=_flask_request_data(),
|
|
291
|
+
target_manager=_model_target_manager(),
|
|
292
|
+
dataset_uuid=dataset_uuid,
|
|
293
|
+
),
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
@VWS_FLASK_APP.route(
|
|
298
|
+
rule="/modeltargets/advancedDatasets/<string:dataset_uuid>/status",
|
|
299
|
+
methods=[HTTPMethod.GET],
|
|
300
|
+
)
|
|
301
|
+
@beartype
|
|
302
|
+
def get_advanced_model_target_dataset_status(
|
|
303
|
+
dataset_uuid: str,
|
|
304
|
+
) -> Response:
|
|
305
|
+
"""Return an advanced Model Target dataset creation status."""
|
|
306
|
+
return _to_flask_response(
|
|
307
|
+
api_response=get_model_target_dataset_status(
|
|
308
|
+
request=_flask_request_data(),
|
|
309
|
+
target_manager=_model_target_manager(),
|
|
310
|
+
dataset_uuid=dataset_uuid,
|
|
311
|
+
),
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
@VWS_FLASK_APP.route(
|
|
316
|
+
rule="/modeltargets/datasets/<string:dataset_uuid>/dataset",
|
|
317
|
+
methods=[HTTPMethod.GET],
|
|
318
|
+
)
|
|
319
|
+
@beartype
|
|
320
|
+
def download_standard_model_target_dataset(
|
|
321
|
+
dataset_uuid: str,
|
|
322
|
+
) -> Response:
|
|
323
|
+
"""Download a standard Model Target dataset."""
|
|
324
|
+
return _to_flask_response(
|
|
325
|
+
api_response=download_model_target_dataset(
|
|
326
|
+
request=_flask_request_data(),
|
|
327
|
+
target_manager=_model_target_manager(),
|
|
328
|
+
dataset_uuid=dataset_uuid,
|
|
329
|
+
),
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
@VWS_FLASK_APP.route(
|
|
334
|
+
rule="/modeltargets/advancedDatasets/<string:dataset_uuid>/dataset",
|
|
335
|
+
methods=[HTTPMethod.GET],
|
|
336
|
+
)
|
|
337
|
+
@beartype
|
|
338
|
+
def download_advanced_model_target_dataset(
|
|
339
|
+
dataset_uuid: str,
|
|
340
|
+
) -> Response:
|
|
341
|
+
"""Download an advanced Model Target dataset."""
|
|
342
|
+
return _to_flask_response(
|
|
343
|
+
api_response=download_model_target_dataset(
|
|
344
|
+
request=_flask_request_data(),
|
|
345
|
+
target_manager=_model_target_manager(),
|
|
346
|
+
dataset_uuid=dataset_uuid,
|
|
347
|
+
),
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
@VWS_FLASK_APP.route(
|
|
352
|
+
rule="/modeltargets/datasets/<string:dataset_uuid>",
|
|
353
|
+
methods=[HTTPMethod.DELETE],
|
|
354
|
+
)
|
|
355
|
+
@beartype
|
|
356
|
+
def delete_standard_model_target_dataset(dataset_uuid: str) -> Response:
|
|
357
|
+
"""Delete a standard Model Target dataset."""
|
|
358
|
+
return _to_flask_response(
|
|
359
|
+
api_response=delete_model_target_dataset(
|
|
360
|
+
request=_flask_request_data(),
|
|
361
|
+
target_manager=_model_target_manager(),
|
|
362
|
+
dataset_uuid=dataset_uuid,
|
|
363
|
+
),
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
@VWS_FLASK_APP.route(
|
|
368
|
+
rule="/modeltargets/advancedDatasets/<string:dataset_uuid>",
|
|
369
|
+
methods=[HTTPMethod.DELETE],
|
|
370
|
+
)
|
|
371
|
+
@beartype
|
|
372
|
+
def delete_advanced_model_target_dataset(dataset_uuid: str) -> Response:
|
|
373
|
+
"""Delete an advanced Model Target dataset."""
|
|
374
|
+
return _to_flask_response(
|
|
375
|
+
api_response=delete_model_target_dataset(
|
|
376
|
+
request=_flask_request_data(),
|
|
377
|
+
target_manager=_model_target_manager(),
|
|
378
|
+
dataset_uuid=dataset_uuid,
|
|
379
|
+
),
|
|
380
|
+
)
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
@VWS_FLASK_APP.route(rule="/targets", methods=[HTTPMethod.POST])
|
|
384
|
+
@beartype
|
|
385
|
+
def add_target() -> Response:
|
|
386
|
+
"""Add a target.
|
|
387
|
+
|
|
388
|
+
Fake implementation of
|
|
389
|
+
https://developer.vuforia.com/library/web-api/cloud-targets-web-services-api#add
|
|
390
|
+
"""
|
|
391
|
+
settings = VWSSettings.model_validate(obj={})
|
|
392
|
+
databases = get_all_cloud_databases()
|
|
393
|
+
database = get_database_matching_server_keys(
|
|
394
|
+
request_headers=dict(request.headers),
|
|
395
|
+
request_body=request.data,
|
|
396
|
+
request_method=request.method,
|
|
397
|
+
request_path=request.path,
|
|
398
|
+
databases=databases,
|
|
399
|
+
)
|
|
400
|
+
|
|
401
|
+
# We do not use ``request.get_json(force=True)`` because this only works
|
|
402
|
+
# when the content type is given as ``application/json``.
|
|
403
|
+
request_json = json.loads(s=request.data)
|
|
404
|
+
name = request_json["name"]
|
|
405
|
+
active_flag = request_json.get("active_flag")
|
|
406
|
+
if active_flag is None:
|
|
407
|
+
active_flag = True
|
|
408
|
+
|
|
409
|
+
# This rater is not used.
|
|
410
|
+
target_tracking_rater = HardcodedTargetTrackingRater(rating=1)
|
|
411
|
+
|
|
412
|
+
new_target = ImageTarget(
|
|
413
|
+
name=name,
|
|
414
|
+
width=request_json["width"],
|
|
415
|
+
image_value=base64.b64decode(s=request_json["image"]),
|
|
416
|
+
active_flag=active_flag,
|
|
417
|
+
processing_time_seconds=settings.processing_time_seconds,
|
|
418
|
+
application_metadata=request_json.get("application_metadata"),
|
|
419
|
+
target_tracking_rater=target_tracking_rater,
|
|
420
|
+
)
|
|
421
|
+
|
|
422
|
+
databases_url = f"{settings.target_manager_base_url}/cloud_databases"
|
|
423
|
+
timeout_seconds = 30
|
|
424
|
+
requests.post(
|
|
425
|
+
url=f"{databases_url}/{database.database_name}/targets",
|
|
426
|
+
json=new_target.to_dict(),
|
|
427
|
+
timeout=timeout_seconds,
|
|
428
|
+
)
|
|
429
|
+
|
|
430
|
+
date = email.utils.formatdate(timeval=None, localtime=False, usegmt=True)
|
|
431
|
+
headers = {
|
|
432
|
+
"Connection": "keep-alive",
|
|
433
|
+
"Content-Type": "application/json",
|
|
434
|
+
"server": "envoy",
|
|
435
|
+
"Date": date,
|
|
436
|
+
"x-envoy-upstream-service-time": "5",
|
|
437
|
+
"strict-transport-security": "max-age=31536000",
|
|
438
|
+
"x-aws-region": "us-east-2, us-west-2",
|
|
439
|
+
"x-content-type-options": "nosniff",
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
body = {
|
|
443
|
+
"transaction_id": uuid.uuid4().hex,
|
|
444
|
+
"result_code": ResultCodes.TARGET_CREATED.value,
|
|
445
|
+
"target_id": new_target.target_id,
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
return Response(
|
|
449
|
+
status=HTTPStatus.CREATED,
|
|
450
|
+
response=json_dump(body=body),
|
|
451
|
+
headers=headers,
|
|
452
|
+
)
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
@VWS_FLASK_APP.route(
|
|
456
|
+
rule="/targets/<string:target_id>", methods=[HTTPMethod.GET]
|
|
457
|
+
)
|
|
458
|
+
@beartype
|
|
459
|
+
def get_target(target_id: str) -> Response:
|
|
460
|
+
"""Get details of a target.
|
|
461
|
+
|
|
462
|
+
Fake implementation of
|
|
463
|
+
https://developer.vuforia.com/library/web-api/cloud-targets-web-services-api#target-record
|
|
464
|
+
"""
|
|
465
|
+
databases = get_all_cloud_databases()
|
|
466
|
+
database = get_database_matching_server_keys(
|
|
467
|
+
request_headers=dict(request.headers),
|
|
468
|
+
request_body=request.data,
|
|
469
|
+
request_method=request.method,
|
|
470
|
+
request_path=request.path,
|
|
471
|
+
databases=databases,
|
|
472
|
+
)
|
|
473
|
+
|
|
474
|
+
(target,) = (
|
|
475
|
+
target for target in database.targets if target.target_id == target_id
|
|
476
|
+
)
|
|
477
|
+
|
|
478
|
+
width = target.width
|
|
479
|
+
tracking_rating = target.tracking_rating
|
|
480
|
+
reco_rating = target.reco_rating
|
|
481
|
+
target_record = {
|
|
482
|
+
"target_id": target.target_id,
|
|
483
|
+
"active_flag": target.active_flag,
|
|
484
|
+
"name": target.name,
|
|
485
|
+
"width": width,
|
|
486
|
+
"tracking_rating": tracking_rating,
|
|
487
|
+
"reco_rating": reco_rating,
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
date = email.utils.formatdate(timeval=None, localtime=False, usegmt=True)
|
|
491
|
+
headers = {
|
|
492
|
+
"Connection": "keep-alive",
|
|
493
|
+
"Content-Type": "application/json",
|
|
494
|
+
"server": "envoy",
|
|
495
|
+
"Date": date,
|
|
496
|
+
"x-envoy-upstream-service-time": "5",
|
|
497
|
+
"strict-transport-security": "max-age=31536000",
|
|
498
|
+
"x-aws-region": "us-east-2, us-west-2",
|
|
499
|
+
"x-content-type-options": "nosniff",
|
|
500
|
+
}
|
|
501
|
+
body = {
|
|
502
|
+
"result_code": ResultCodes.SUCCESS.value,
|
|
503
|
+
"transaction_id": uuid.uuid4().hex,
|
|
504
|
+
"target_record": target_record,
|
|
505
|
+
"status": target.status,
|
|
506
|
+
}
|
|
507
|
+
return Response(
|
|
508
|
+
status=HTTPStatus.OK,
|
|
509
|
+
response=json_dump(body=body),
|
|
510
|
+
headers=headers,
|
|
511
|
+
)
|
|
512
|
+
|
|
513
|
+
|
|
514
|
+
@VWS_FLASK_APP.route(
|
|
515
|
+
rule="/targets/<string:target_id>",
|
|
516
|
+
methods=[HTTPMethod.DELETE],
|
|
517
|
+
)
|
|
518
|
+
@beartype
|
|
519
|
+
def delete_target(target_id: str) -> Response:
|
|
520
|
+
"""Delete a target.
|
|
521
|
+
|
|
522
|
+
Fake implementation of
|
|
523
|
+
https://developer.vuforia.com/library/web-api/cloud-targets-web-services-api#delete
|
|
524
|
+
"""
|
|
525
|
+
settings = VWSSettings.model_validate(obj={})
|
|
526
|
+
databases = get_all_cloud_databases()
|
|
527
|
+
database = get_database_matching_server_keys(
|
|
528
|
+
request_headers=dict(request.headers),
|
|
529
|
+
request_body=request.data,
|
|
530
|
+
request_method=request.method,
|
|
531
|
+
request_path=request.path,
|
|
532
|
+
databases=databases,
|
|
533
|
+
)
|
|
534
|
+
|
|
535
|
+
(target,) = (
|
|
536
|
+
target for target in database.targets if target.target_id == target_id
|
|
537
|
+
)
|
|
538
|
+
|
|
539
|
+
if target.status == TargetStatuses.PROCESSING.value:
|
|
540
|
+
raise TargetStatusProcessingError
|
|
541
|
+
|
|
542
|
+
databases_url = f"{settings.target_manager_base_url}/cloud_databases"
|
|
543
|
+
requests.delete(
|
|
544
|
+
url=f"{databases_url}/{database.database_name}/targets/{target_id}",
|
|
545
|
+
timeout=30,
|
|
546
|
+
)
|
|
547
|
+
|
|
548
|
+
body = {
|
|
549
|
+
"transaction_id": uuid.uuid4().hex,
|
|
550
|
+
"result_code": ResultCodes.SUCCESS.value,
|
|
551
|
+
}
|
|
552
|
+
date = email.utils.formatdate(timeval=None, localtime=False, usegmt=True)
|
|
553
|
+
headers = {
|
|
554
|
+
"Connection": "keep-alive",
|
|
555
|
+
"Content-Type": "application/json",
|
|
556
|
+
"server": "envoy",
|
|
557
|
+
"Date": date,
|
|
558
|
+
"x-envoy-upstream-service-time": "5",
|
|
559
|
+
"strict-transport-security": "max-age=31536000",
|
|
560
|
+
"x-aws-region": "us-east-2, us-west-2",
|
|
561
|
+
"x-content-type-options": "nosniff",
|
|
562
|
+
}
|
|
563
|
+
return Response(
|
|
564
|
+
status=HTTPStatus.OK,
|
|
565
|
+
response=json_dump(body=body),
|
|
566
|
+
headers=headers,
|
|
567
|
+
)
|
|
568
|
+
|
|
569
|
+
|
|
570
|
+
@VWS_FLASK_APP.route(
|
|
571
|
+
rule="/targets/<string:target_id>/instances",
|
|
572
|
+
methods=[HTTPMethod.POST],
|
|
573
|
+
)
|
|
574
|
+
@beartype
|
|
575
|
+
def generate_vumark_instance(target_id: str) -> Response:
|
|
576
|
+
"""Generate a VuMark instance.
|
|
577
|
+
|
|
578
|
+
Fake implementation of
|
|
579
|
+
https://developer.vuforia.com/library/web-api/cloud-targets-web-services-api#generate-instance
|
|
580
|
+
"""
|
|
581
|
+
cloud_databases = get_all_cloud_databases()
|
|
582
|
+
vumark_databases = get_all_vumark_databases()
|
|
583
|
+
all_databases: list[CloudDatabase | VuMarkDatabase] = [
|
|
584
|
+
*cloud_databases,
|
|
585
|
+
*vumark_databases,
|
|
586
|
+
]
|
|
587
|
+
run_services_validators(
|
|
588
|
+
request_headers=dict(request.headers),
|
|
589
|
+
request_body=request.data,
|
|
590
|
+
request_method=request.method,
|
|
591
|
+
request_path=request.path,
|
|
592
|
+
databases=all_databases,
|
|
593
|
+
)
|
|
594
|
+
|
|
595
|
+
database = get_database_matching_server_keys(
|
|
596
|
+
request_headers=dict(request.headers),
|
|
597
|
+
request_body=request.data,
|
|
598
|
+
request_method=request.method,
|
|
599
|
+
request_path=request.path,
|
|
600
|
+
databases=all_databases,
|
|
601
|
+
)
|
|
602
|
+
if not isinstance(database, VuMarkDatabase):
|
|
603
|
+
raise InvalidTargetTypeError
|
|
604
|
+
|
|
605
|
+
target = database.get_vumark_target(target_id=target_id)
|
|
606
|
+
if target.status != TargetStatuses.SUCCESS.value:
|
|
607
|
+
raise TargetStatusNotSuccessError
|
|
608
|
+
|
|
609
|
+
accept = request.headers.get(key="Accept", default="")
|
|
610
|
+
valid_accept_types: dict[str, bytes] = {
|
|
611
|
+
"image/png": VUMARK_PNG,
|
|
612
|
+
"image/svg+xml": VUMARK_SVG,
|
|
613
|
+
"application/pdf": VUMARK_PDF,
|
|
614
|
+
}
|
|
615
|
+
if accept not in valid_accept_types:
|
|
616
|
+
raise InvalidAcceptHeaderError
|
|
617
|
+
|
|
618
|
+
request_json = json.loads(s=request.data)
|
|
619
|
+
instance_id = request_json.get("instance_id", "")
|
|
620
|
+
if not instance_id:
|
|
621
|
+
raise InvalidInstanceIdError
|
|
622
|
+
|
|
623
|
+
response_body = valid_accept_types[accept]
|
|
624
|
+
content_type = accept
|
|
625
|
+
date = email.utils.formatdate(timeval=None, localtime=False, usegmt=True)
|
|
626
|
+
headers = {
|
|
627
|
+
"Connection": "keep-alive",
|
|
628
|
+
"Content-Type": content_type,
|
|
629
|
+
"server": "envoy",
|
|
630
|
+
"Date": date,
|
|
631
|
+
"x-envoy-upstream-service-time": "5",
|
|
632
|
+
"strict-transport-security": "max-age=31536000",
|
|
633
|
+
"x-aws-region": "us-east-2, us-west-2",
|
|
634
|
+
"x-content-type-options": "nosniff",
|
|
635
|
+
}
|
|
636
|
+
return Response(
|
|
637
|
+
status=HTTPStatus.OK,
|
|
638
|
+
response=response_body,
|
|
639
|
+
headers=headers,
|
|
640
|
+
)
|
|
641
|
+
|
|
642
|
+
|
|
643
|
+
@VWS_FLASK_APP.route(rule="/summary", methods=[HTTPMethod.GET])
|
|
644
|
+
@beartype
|
|
645
|
+
def database_summary() -> Response:
|
|
646
|
+
"""Get a database summary report.
|
|
647
|
+
|
|
648
|
+
Fake implementation of
|
|
649
|
+
https://developer.vuforia.com/library/web-api/cloud-targets-web-services-api#summary-report
|
|
650
|
+
"""
|
|
651
|
+
databases = get_all_cloud_databases()
|
|
652
|
+
database = get_database_matching_server_keys(
|
|
653
|
+
request_headers=dict(request.headers),
|
|
654
|
+
request_body=request.data,
|
|
655
|
+
request_method=request.method,
|
|
656
|
+
request_path=request.path,
|
|
657
|
+
databases=databases,
|
|
658
|
+
)
|
|
659
|
+
|
|
660
|
+
body = {
|
|
661
|
+
"result_code": ResultCodes.SUCCESS.value,
|
|
662
|
+
"transaction_id": uuid.uuid4().hex,
|
|
663
|
+
"name": database.database_name,
|
|
664
|
+
"active_images": len(database.active_targets),
|
|
665
|
+
"inactive_images": len(database.inactive_targets),
|
|
666
|
+
"failed_images": len(database.failed_targets),
|
|
667
|
+
"target_quota": database.target_quota,
|
|
668
|
+
"total_recos": database.total_recos,
|
|
669
|
+
"current_month_recos": database.current_month_recos,
|
|
670
|
+
"previous_month_recos": database.previous_month_recos,
|
|
671
|
+
"processing_images": len(database.processing_targets),
|
|
672
|
+
"reco_threshold": database.reco_threshold,
|
|
673
|
+
"request_quota": database.request_quota,
|
|
674
|
+
# We have ``self.request_count`` but Vuforia always shows 0.
|
|
675
|
+
# This was not always the case.
|
|
676
|
+
"request_usage": 0,
|
|
677
|
+
}
|
|
678
|
+
date = email.utils.formatdate(timeval=None, localtime=False, usegmt=True)
|
|
679
|
+
headers = {
|
|
680
|
+
"Connection": "keep-alive",
|
|
681
|
+
"Content-Type": "application/json",
|
|
682
|
+
"server": "envoy",
|
|
683
|
+
"Date": date,
|
|
684
|
+
"x-envoy-upstream-service-time": "5",
|
|
685
|
+
"strict-transport-security": "max-age=31536000",
|
|
686
|
+
"x-aws-region": "us-east-2, us-west-2",
|
|
687
|
+
"x-content-type-options": "nosniff",
|
|
688
|
+
}
|
|
689
|
+
return Response(
|
|
690
|
+
status=HTTPStatus.OK,
|
|
691
|
+
response=json_dump(body=body),
|
|
692
|
+
headers=headers,
|
|
693
|
+
)
|
|
694
|
+
|
|
695
|
+
|
|
696
|
+
@VWS_FLASK_APP.route(
|
|
697
|
+
rule="/summary/<string:target_id>",
|
|
698
|
+
methods=[HTTPMethod.GET],
|
|
699
|
+
)
|
|
700
|
+
@beartype
|
|
701
|
+
def target_summary(target_id: str) -> Response:
|
|
702
|
+
"""Get a summary report for a target.
|
|
703
|
+
|
|
704
|
+
Fake implementation of
|
|
705
|
+
https://developer.vuforia.com/library/web-api/cloud-targets-web-services-api#retrieve-report
|
|
706
|
+
"""
|
|
707
|
+
databases = get_all_cloud_databases()
|
|
708
|
+
database = get_database_matching_server_keys(
|
|
709
|
+
request_headers=dict(request.headers),
|
|
710
|
+
request_body=request.data,
|
|
711
|
+
request_method=request.method,
|
|
712
|
+
request_path=request.path,
|
|
713
|
+
databases=databases,
|
|
714
|
+
)
|
|
715
|
+
|
|
716
|
+
(target,) = (
|
|
717
|
+
target for target in database.targets if target.target_id == target_id
|
|
718
|
+
)
|
|
719
|
+
tracking_rating = target.tracking_rating
|
|
720
|
+
total_recos = target.total_recos
|
|
721
|
+
current_month_recos = target.current_month_recos
|
|
722
|
+
previous_month_recos = target.previous_month_recos
|
|
723
|
+
body = {
|
|
724
|
+
"status": target.status,
|
|
725
|
+
"transaction_id": uuid.uuid4().hex,
|
|
726
|
+
"result_code": ResultCodes.SUCCESS.value,
|
|
727
|
+
"database_name": database.database_name,
|
|
728
|
+
"target_name": target.name,
|
|
729
|
+
"upload_date": target.upload_date.strftime(format="%Y-%m-%d"),
|
|
730
|
+
"active_flag": target.active_flag,
|
|
731
|
+
"tracking_rating": tracking_rating,
|
|
732
|
+
"total_recos": total_recos,
|
|
733
|
+
"current_month_recos": current_month_recos,
|
|
734
|
+
"previous_month_recos": previous_month_recos,
|
|
735
|
+
}
|
|
736
|
+
date = email.utils.formatdate(timeval=None, localtime=False, usegmt=True)
|
|
737
|
+
headers = {
|
|
738
|
+
"Connection": "keep-alive",
|
|
739
|
+
"Content-Type": "application/json",
|
|
740
|
+
"server": "envoy",
|
|
741
|
+
"Date": date,
|
|
742
|
+
"x-envoy-upstream-service-time": "5",
|
|
743
|
+
"strict-transport-security": "max-age=31536000",
|
|
744
|
+
"x-aws-region": "us-east-2, us-west-2",
|
|
745
|
+
"x-content-type-options": "nosniff",
|
|
746
|
+
}
|
|
747
|
+
return Response(
|
|
748
|
+
status=HTTPStatus.OK,
|
|
749
|
+
response=json_dump(body=body),
|
|
750
|
+
headers=headers,
|
|
751
|
+
)
|
|
752
|
+
|
|
753
|
+
|
|
754
|
+
@VWS_FLASK_APP.route(
|
|
755
|
+
rule="/duplicates/<string:target_id>",
|
|
756
|
+
methods=[HTTPMethod.GET],
|
|
757
|
+
)
|
|
758
|
+
@beartype
|
|
759
|
+
def get_duplicates(target_id: str) -> Response:
|
|
760
|
+
"""Get targets which may be considered duplicates of a given target.
|
|
761
|
+
|
|
762
|
+
Fake implementation of
|
|
763
|
+
https://developer.vuforia.com/library/web-api/cloud-targets-web-services-api#check
|
|
764
|
+
"""
|
|
765
|
+
databases = get_all_cloud_databases()
|
|
766
|
+
settings = VWSSettings.model_validate(obj={})
|
|
767
|
+
database = get_database_matching_server_keys(
|
|
768
|
+
request_headers=dict(request.headers),
|
|
769
|
+
request_body=request.data,
|
|
770
|
+
request_method=request.method,
|
|
771
|
+
request_path=request.path,
|
|
772
|
+
databases=databases,
|
|
773
|
+
)
|
|
774
|
+
image_match_checker = settings.duplicates_image_matcher.to_image_matcher()
|
|
775
|
+
|
|
776
|
+
(target,) = (
|
|
777
|
+
target for target in database.targets if target.target_id == target_id
|
|
778
|
+
)
|
|
779
|
+
other_targets = database.targets - {target}
|
|
780
|
+
|
|
781
|
+
similar_targets = [
|
|
782
|
+
other.target_id
|
|
783
|
+
for other in other_targets
|
|
784
|
+
if image_match_checker(
|
|
785
|
+
first_image_content=target.image_value,
|
|
786
|
+
second_image_content=other.image_value,
|
|
787
|
+
)
|
|
788
|
+
and TargetStatuses.FAILED.value not in {target.status, other.status}
|
|
789
|
+
and TargetStatuses.PROCESSING.value != other.status
|
|
790
|
+
and other.active_flag
|
|
791
|
+
]
|
|
792
|
+
|
|
793
|
+
body = {
|
|
794
|
+
"transaction_id": uuid.uuid4().hex,
|
|
795
|
+
"result_code": ResultCodes.SUCCESS.value,
|
|
796
|
+
"similar_targets": similar_targets,
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
date = email.utils.formatdate(timeval=None, localtime=False, usegmt=True)
|
|
800
|
+
headers = {
|
|
801
|
+
"Connection": "keep-alive",
|
|
802
|
+
"Content-Type": "application/json",
|
|
803
|
+
"server": "envoy",
|
|
804
|
+
"Date": date,
|
|
805
|
+
"x-envoy-upstream-service-time": "5",
|
|
806
|
+
"strict-transport-security": "max-age=31536000",
|
|
807
|
+
"x-aws-region": "us-east-2, us-west-2",
|
|
808
|
+
"x-content-type-options": "nosniff",
|
|
809
|
+
}
|
|
810
|
+
return Response(
|
|
811
|
+
status=HTTPStatus.OK,
|
|
812
|
+
response=json_dump(body=body),
|
|
813
|
+
headers=headers,
|
|
814
|
+
)
|
|
815
|
+
|
|
816
|
+
|
|
817
|
+
@VWS_FLASK_APP.route(rule="/targets", methods=[HTTPMethod.GET])
|
|
818
|
+
@beartype
|
|
819
|
+
def target_list() -> Response:
|
|
820
|
+
"""Get a list of all targets.
|
|
821
|
+
|
|
822
|
+
Fake implementation of
|
|
823
|
+
https://developer.vuforia.com/library/web-api/cloud-targets-web-services-api#details-list
|
|
824
|
+
"""
|
|
825
|
+
databases = get_all_cloud_databases()
|
|
826
|
+
database = get_database_matching_server_keys(
|
|
827
|
+
request_headers=dict(request.headers),
|
|
828
|
+
request_body=request.data,
|
|
829
|
+
request_method=request.method,
|
|
830
|
+
request_path=request.path,
|
|
831
|
+
databases=databases,
|
|
832
|
+
)
|
|
833
|
+
results = [target.target_id for target in database.not_deleted_targets]
|
|
834
|
+
|
|
835
|
+
body = {
|
|
836
|
+
"transaction_id": uuid.uuid4().hex,
|
|
837
|
+
"result_code": ResultCodes.SUCCESS.value,
|
|
838
|
+
"results": results,
|
|
839
|
+
}
|
|
840
|
+
date = email.utils.formatdate(timeval=None, localtime=False, usegmt=True)
|
|
841
|
+
headers = {
|
|
842
|
+
"Connection": "keep-alive",
|
|
843
|
+
"Content-Type": "application/json",
|
|
844
|
+
"server": "envoy",
|
|
845
|
+
"Date": date,
|
|
846
|
+
"x-envoy-upstream-service-time": "5",
|
|
847
|
+
"strict-transport-security": "max-age=31536000",
|
|
848
|
+
"x-aws-region": "us-east-2, us-west-2",
|
|
849
|
+
"x-content-type-options": "nosniff",
|
|
850
|
+
}
|
|
851
|
+
return Response(
|
|
852
|
+
status=HTTPStatus.OK,
|
|
853
|
+
response=json_dump(body=body),
|
|
854
|
+
headers=headers,
|
|
855
|
+
)
|
|
856
|
+
|
|
857
|
+
|
|
858
|
+
@VWS_FLASK_APP.route(
|
|
859
|
+
rule="/targets/<string:target_id>", methods=[HTTPMethod.PUT]
|
|
860
|
+
)
|
|
861
|
+
@beartype
|
|
862
|
+
def update_target(target_id: str) -> Response:
|
|
863
|
+
"""Update a target.
|
|
864
|
+
|
|
865
|
+
Fake implementation of
|
|
866
|
+
https://developer.vuforia.com/library/web-api/cloud-targets-web-services-api#update
|
|
867
|
+
"""
|
|
868
|
+
settings = VWSSettings.model_validate(obj={})
|
|
869
|
+
# We do not use ``request.get_json(force=True)`` because this only works
|
|
870
|
+
# when the content type is given as ``application/json``.
|
|
871
|
+
request_json = json.loads(s=request.data)
|
|
872
|
+
databases = get_all_cloud_databases()
|
|
873
|
+
database = get_database_matching_server_keys(
|
|
874
|
+
request_headers=dict(request.headers),
|
|
875
|
+
request_body=request.data,
|
|
876
|
+
request_method=request.method,
|
|
877
|
+
request_path=request.path,
|
|
878
|
+
databases=databases,
|
|
879
|
+
)
|
|
880
|
+
|
|
881
|
+
(target,) = (
|
|
882
|
+
target for target in database.targets if target.target_id == target_id
|
|
883
|
+
)
|
|
884
|
+
|
|
885
|
+
if target.status != TargetStatuses.SUCCESS.value:
|
|
886
|
+
raise TargetStatusNotSuccessError
|
|
887
|
+
|
|
888
|
+
update_values: dict[str, str | int | float | bool | None] = {}
|
|
889
|
+
if "width" in request_json:
|
|
890
|
+
update_values["width"] = request_json["width"]
|
|
891
|
+
|
|
892
|
+
if "active_flag" in request_json:
|
|
893
|
+
active_flag = request_json["active_flag"]
|
|
894
|
+
if active_flag is None:
|
|
895
|
+
_LOGGER.warning(
|
|
896
|
+
msg=(
|
|
897
|
+
'The value of "active_flag" was None. '
|
|
898
|
+
"This is not allowed. "
|
|
899
|
+
),
|
|
900
|
+
)
|
|
901
|
+
raise FailError(status_code=HTTPStatus.BAD_REQUEST)
|
|
902
|
+
update_values["active_flag"] = active_flag
|
|
903
|
+
|
|
904
|
+
if "application_metadata" in request_json:
|
|
905
|
+
application_metadata = request_json["application_metadata"]
|
|
906
|
+
if application_metadata is None:
|
|
907
|
+
_LOGGER.warning(
|
|
908
|
+
msg=(
|
|
909
|
+
'The value of "application_metadata" was None. '
|
|
910
|
+
"This is not allowed."
|
|
911
|
+
),
|
|
912
|
+
)
|
|
913
|
+
raise FailError(status_code=HTTPStatus.BAD_REQUEST)
|
|
914
|
+
update_values["application_metadata"] = application_metadata
|
|
915
|
+
|
|
916
|
+
if "name" in request_json:
|
|
917
|
+
name = request_json["name"]
|
|
918
|
+
update_values["name"] = name
|
|
919
|
+
|
|
920
|
+
if "image" in request_json:
|
|
921
|
+
image = request_json["image"]
|
|
922
|
+
update_values["image"] = image
|
|
923
|
+
|
|
924
|
+
put_url = (
|
|
925
|
+
f"{settings.target_manager_base_url}/cloud_databases/"
|
|
926
|
+
f"{database.database_name}/targets/{target_id}"
|
|
927
|
+
)
|
|
928
|
+
requests.put(url=put_url, json=update_values, timeout=30)
|
|
929
|
+
|
|
930
|
+
date = email.utils.formatdate(timeval=None, localtime=False, usegmt=True)
|
|
931
|
+
headers = {
|
|
932
|
+
"Connection": "keep-alive",
|
|
933
|
+
"Content-Type": "application/json",
|
|
934
|
+
"server": "envoy",
|
|
935
|
+
"Date": date,
|
|
936
|
+
"x-envoy-upstream-service-time": "5",
|
|
937
|
+
"strict-transport-security": "max-age=31536000",
|
|
938
|
+
"x-aws-region": "us-east-2, us-west-2",
|
|
939
|
+
"x-content-type-options": "nosniff",
|
|
940
|
+
}
|
|
941
|
+
body = {
|
|
942
|
+
"result_code": ResultCodes.SUCCESS.value,
|
|
943
|
+
"transaction_id": uuid.uuid4().hex,
|
|
944
|
+
}
|
|
945
|
+
return Response(
|
|
946
|
+
status=HTTPStatus.OK,
|
|
947
|
+
response=json_dump(body=body),
|
|
948
|
+
headers=headers,
|
|
949
|
+
)
|
|
950
|
+
|
|
951
|
+
|
|
952
|
+
if __name__ == "__main__": # pragma: no cover
|
|
953
|
+
SETTINGS = VWSSettings.model_validate(obj={})
|
|
954
|
+
VWS_FLASK_APP.run(host=SETTINGS.vws_host)
|