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 copy
|
|
9
|
+
import datetime
|
|
10
|
+
import email.utils
|
|
11
|
+
import json
|
|
12
|
+
import uuid
|
|
13
|
+
from collections.abc import Callable, Iterable, Mapping
|
|
14
|
+
from http import HTTPMethod, HTTPStatus
|
|
15
|
+
from typing import TYPE_CHECKING, Any, ParamSpec, Protocol, runtime_checkable
|
|
16
|
+
from zoneinfo import ZoneInfo
|
|
17
|
+
|
|
18
|
+
from beartype import BeartypeConf, beartype
|
|
19
|
+
|
|
20
|
+
from mock_vws._constants import (
|
|
21
|
+
VUMARK_PDF,
|
|
22
|
+
VUMARK_PNG,
|
|
23
|
+
VUMARK_SVG,
|
|
24
|
+
ResultCodes,
|
|
25
|
+
TargetStatuses,
|
|
26
|
+
)
|
|
27
|
+
from mock_vws._database_matchers import get_database_matching_server_keys
|
|
28
|
+
from mock_vws._mock_common import RequestData, Route, json_dump
|
|
29
|
+
from mock_vws._model_target_web_api import (
|
|
30
|
+
create_model_target_dataset,
|
|
31
|
+
delete_model_target_dataset,
|
|
32
|
+
download_model_target_dataset,
|
|
33
|
+
get_model_target_dataset_status,
|
|
34
|
+
oauth2_token,
|
|
35
|
+
)
|
|
36
|
+
from mock_vws._services_validators import run_services_validators
|
|
37
|
+
from mock_vws._services_validators.exceptions import (
|
|
38
|
+
FailError,
|
|
39
|
+
InvalidAcceptHeaderError,
|
|
40
|
+
InvalidInstanceIdError,
|
|
41
|
+
InvalidTargetTypeError,
|
|
42
|
+
TargetStatusNotSuccessError,
|
|
43
|
+
TargetStatusProcessingError,
|
|
44
|
+
ValidatorError,
|
|
45
|
+
)
|
|
46
|
+
from mock_vws.database import VuMarkDatabase
|
|
47
|
+
from mock_vws.image_matchers import ImageMatcher
|
|
48
|
+
from mock_vws.model_target import ModelTargetDatasetType
|
|
49
|
+
from mock_vws.target import ImageTarget
|
|
50
|
+
from mock_vws.target_manager import TargetManager
|
|
51
|
+
from mock_vws.target_raters import TargetTrackingRater
|
|
52
|
+
|
|
53
|
+
if TYPE_CHECKING:
|
|
54
|
+
from mock_vws.database import CloudDatabase
|
|
55
|
+
|
|
56
|
+
_TARGET_ID_PATTERN = "[A-Za-z0-9]+"
|
|
57
|
+
_MODEL_TARGET_DATASET_UUID_PATTERN = "[A-Za-z0-9-]+"
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
_ROUTES: set[Route] = set()
|
|
61
|
+
|
|
62
|
+
_ResponseType = tuple[int, Mapping[str, str], str | bytes]
|
|
63
|
+
_P = ParamSpec("_P")
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@runtime_checkable
|
|
67
|
+
class _RouteMethod(Protocol[_P]):
|
|
68
|
+
"""Callable used for routing which also exposes ``__name__``."""
|
|
69
|
+
|
|
70
|
+
__name__: str
|
|
71
|
+
|
|
72
|
+
def __call__(self, *args: _P.args, **kwargs: _P.kwargs) -> _ResponseType:
|
|
73
|
+
"""Return a mock response."""
|
|
74
|
+
... # pylint: disable=unnecessary-ellipsis
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@beartype
|
|
78
|
+
def route(
|
|
79
|
+
*,
|
|
80
|
+
path_pattern: str,
|
|
81
|
+
http_methods: Iterable[HTTPMethod],
|
|
82
|
+
) -> Callable[[_RouteMethod[_P]], _RouteMethod[_P]]:
|
|
83
|
+
"""Register a decorated method so that it can be recognized as a route.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
path_pattern: The end part of a URL pattern. E.g. `/targets` or
|
|
87
|
+
`/targets/.+`.
|
|
88
|
+
http_methods: HTTP methods that map to the route function.
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
A decorator which takes methods and makes them recognizable as routes.
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
@beartype
|
|
95
|
+
def decorator(
|
|
96
|
+
method: _RouteMethod[_P],
|
|
97
|
+
) -> _RouteMethod[_P]:
|
|
98
|
+
"""Register a decorated method so that it can be recognized as a
|
|
99
|
+
route.
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
The given `method` with multiple changes, including added
|
|
103
|
+
validators.
|
|
104
|
+
"""
|
|
105
|
+
new_route = Route(
|
|
106
|
+
route_name=method.__name__,
|
|
107
|
+
path_pattern=path_pattern,
|
|
108
|
+
http_methods=frozenset(http_methods),
|
|
109
|
+
)
|
|
110
|
+
_ROUTES.add(new_route)
|
|
111
|
+
|
|
112
|
+
return method
|
|
113
|
+
|
|
114
|
+
return decorator
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
@beartype(conf=BeartypeConf(is_pep484_tower=True))
|
|
118
|
+
class MockVuforiaWebServicesAPI:
|
|
119
|
+
"""A fake implementation of the Vuforia Web Services API."""
|
|
120
|
+
|
|
121
|
+
def __init__(
|
|
122
|
+
self,
|
|
123
|
+
*,
|
|
124
|
+
target_manager: TargetManager,
|
|
125
|
+
processing_time_seconds: float,
|
|
126
|
+
duplicate_match_checker: ImageMatcher,
|
|
127
|
+
target_tracking_rater: TargetTrackingRater,
|
|
128
|
+
) -> None:
|
|
129
|
+
"""
|
|
130
|
+
Args:
|
|
131
|
+
target_manager: Target Manager which stores databases.
|
|
132
|
+
processing_time_seconds: The number of seconds to process each
|
|
133
|
+
image for. In the real Vuforia Web Services, this is not
|
|
134
|
+
deterministic.
|
|
135
|
+
duplicate_match_checker: A callable which takes two image
|
|
136
|
+
values
|
|
137
|
+
and returns whether they are duplicates.
|
|
138
|
+
target_tracking_rater: A callable for rating targets for
|
|
139
|
+
tracking.
|
|
140
|
+
|
|
141
|
+
Attributes:
|
|
142
|
+
routes: The `Route`s to be used in the mock.
|
|
143
|
+
"""
|
|
144
|
+
self._target_manager = target_manager
|
|
145
|
+
self.routes = _ROUTES
|
|
146
|
+
self._processing_time_seconds = processing_time_seconds
|
|
147
|
+
self._duplicate_match_checker = duplicate_match_checker
|
|
148
|
+
self._target_tracking_rater = target_tracking_rater
|
|
149
|
+
|
|
150
|
+
@route(path_pattern="/oauth2/token", http_methods={HTTPMethod.POST})
|
|
151
|
+
def oauth2_token( # pylint: disable=no-self-use
|
|
152
|
+
self,
|
|
153
|
+
request: RequestData,
|
|
154
|
+
) -> _ResponseType:
|
|
155
|
+
"""Obtain an OAuth2 token for the Model Target Web API."""
|
|
156
|
+
return oauth2_token(request=request)
|
|
157
|
+
|
|
158
|
+
@route(
|
|
159
|
+
path_pattern="/modeltargets/datasets",
|
|
160
|
+
http_methods={HTTPMethod.POST},
|
|
161
|
+
)
|
|
162
|
+
def create_standard_model_target_dataset(
|
|
163
|
+
self,
|
|
164
|
+
request: RequestData,
|
|
165
|
+
) -> _ResponseType:
|
|
166
|
+
"""Create a standard Model Target dataset."""
|
|
167
|
+
return create_model_target_dataset(
|
|
168
|
+
request=request,
|
|
169
|
+
target_manager=self._target_manager,
|
|
170
|
+
processing_time_seconds=self._processing_time_seconds,
|
|
171
|
+
dataset_type=ModelTargetDatasetType.STANDARD,
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
@route(
|
|
175
|
+
path_pattern="/modeltargets/advancedDatasets",
|
|
176
|
+
http_methods={HTTPMethod.POST},
|
|
177
|
+
)
|
|
178
|
+
def create_advanced_model_target_dataset(
|
|
179
|
+
self,
|
|
180
|
+
request: RequestData,
|
|
181
|
+
) -> _ResponseType:
|
|
182
|
+
"""Create an advanced Model Target dataset."""
|
|
183
|
+
return create_model_target_dataset(
|
|
184
|
+
request=request,
|
|
185
|
+
target_manager=self._target_manager,
|
|
186
|
+
processing_time_seconds=self._processing_time_seconds,
|
|
187
|
+
dataset_type=ModelTargetDatasetType.ADVANCED,
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
@route(
|
|
191
|
+
path_pattern=(
|
|
192
|
+
"/modeltargets/datasets/"
|
|
193
|
+
f"{_MODEL_TARGET_DATASET_UUID_PATTERN}/status"
|
|
194
|
+
),
|
|
195
|
+
http_methods={HTTPMethod.GET},
|
|
196
|
+
)
|
|
197
|
+
def get_standard_model_target_dataset_status(
|
|
198
|
+
self,
|
|
199
|
+
request: RequestData,
|
|
200
|
+
) -> _ResponseType:
|
|
201
|
+
"""Return a standard Model Target dataset creation status."""
|
|
202
|
+
dataset_uuid = request.path.split(sep="/")[-2]
|
|
203
|
+
return get_model_target_dataset_status(
|
|
204
|
+
request=request,
|
|
205
|
+
target_manager=self._target_manager,
|
|
206
|
+
dataset_uuid=dataset_uuid,
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
@route(
|
|
210
|
+
path_pattern=(
|
|
211
|
+
"/modeltargets/advancedDatasets/"
|
|
212
|
+
f"{_MODEL_TARGET_DATASET_UUID_PATTERN}/status"
|
|
213
|
+
),
|
|
214
|
+
http_methods={HTTPMethod.GET},
|
|
215
|
+
)
|
|
216
|
+
def get_advanced_model_target_dataset_status(
|
|
217
|
+
self,
|
|
218
|
+
request: RequestData,
|
|
219
|
+
) -> _ResponseType:
|
|
220
|
+
"""Return an advanced Model Target dataset creation status."""
|
|
221
|
+
dataset_uuid = request.path.split(sep="/")[-2]
|
|
222
|
+
return get_model_target_dataset_status(
|
|
223
|
+
request=request,
|
|
224
|
+
target_manager=self._target_manager,
|
|
225
|
+
dataset_uuid=dataset_uuid,
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
@route(
|
|
229
|
+
path_pattern=(
|
|
230
|
+
"/modeltargets/datasets/"
|
|
231
|
+
f"{_MODEL_TARGET_DATASET_UUID_PATTERN}/dataset"
|
|
232
|
+
),
|
|
233
|
+
http_methods={HTTPMethod.GET},
|
|
234
|
+
)
|
|
235
|
+
def download_standard_model_target_dataset(
|
|
236
|
+
self,
|
|
237
|
+
request: RequestData,
|
|
238
|
+
) -> _ResponseType:
|
|
239
|
+
"""Download a standard Model Target dataset."""
|
|
240
|
+
dataset_uuid = request.path.split(sep="/")[-2]
|
|
241
|
+
return download_model_target_dataset(
|
|
242
|
+
request=request,
|
|
243
|
+
target_manager=self._target_manager,
|
|
244
|
+
dataset_uuid=dataset_uuid,
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
@route(
|
|
248
|
+
path_pattern=(
|
|
249
|
+
"/modeltargets/advancedDatasets/"
|
|
250
|
+
f"{_MODEL_TARGET_DATASET_UUID_PATTERN}/dataset"
|
|
251
|
+
),
|
|
252
|
+
http_methods={HTTPMethod.GET},
|
|
253
|
+
)
|
|
254
|
+
def download_advanced_model_target_dataset(
|
|
255
|
+
self,
|
|
256
|
+
request: RequestData,
|
|
257
|
+
) -> _ResponseType:
|
|
258
|
+
"""Download an advanced Model Target dataset."""
|
|
259
|
+
dataset_uuid = request.path.split(sep="/")[-2]
|
|
260
|
+
return download_model_target_dataset(
|
|
261
|
+
request=request,
|
|
262
|
+
target_manager=self._target_manager,
|
|
263
|
+
dataset_uuid=dataset_uuid,
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
@route(
|
|
267
|
+
path_pattern=(
|
|
268
|
+
f"/modeltargets/datasets/{_MODEL_TARGET_DATASET_UUID_PATTERN}"
|
|
269
|
+
),
|
|
270
|
+
http_methods={HTTPMethod.DELETE},
|
|
271
|
+
)
|
|
272
|
+
def delete_standard_model_target_dataset(
|
|
273
|
+
self,
|
|
274
|
+
request: RequestData,
|
|
275
|
+
) -> _ResponseType:
|
|
276
|
+
"""Delete a standard Model Target dataset."""
|
|
277
|
+
dataset_uuid = request.path.split(sep="/")[-1]
|
|
278
|
+
return delete_model_target_dataset(
|
|
279
|
+
request=request,
|
|
280
|
+
target_manager=self._target_manager,
|
|
281
|
+
dataset_uuid=dataset_uuid,
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
@route(
|
|
285
|
+
path_pattern=(
|
|
286
|
+
"/modeltargets/advancedDatasets/"
|
|
287
|
+
f"{_MODEL_TARGET_DATASET_UUID_PATTERN}"
|
|
288
|
+
),
|
|
289
|
+
http_methods={HTTPMethod.DELETE},
|
|
290
|
+
)
|
|
291
|
+
def delete_advanced_model_target_dataset(
|
|
292
|
+
self,
|
|
293
|
+
request: RequestData,
|
|
294
|
+
) -> _ResponseType:
|
|
295
|
+
"""Delete an advanced Model Target dataset."""
|
|
296
|
+
dataset_uuid = request.path.split(sep="/")[-1]
|
|
297
|
+
return delete_model_target_dataset(
|
|
298
|
+
request=request,
|
|
299
|
+
target_manager=self._target_manager,
|
|
300
|
+
dataset_uuid=dataset_uuid,
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
@route(
|
|
304
|
+
path_pattern="/targets",
|
|
305
|
+
http_methods={HTTPMethod.POST},
|
|
306
|
+
)
|
|
307
|
+
def add_target(self, request: RequestData) -> _ResponseType:
|
|
308
|
+
"""Add a target.
|
|
309
|
+
|
|
310
|
+
Fake implementation of
|
|
311
|
+
https://developer.vuforia.com/library/web-api/cloud-targets-web-services-api#add
|
|
312
|
+
"""
|
|
313
|
+
try:
|
|
314
|
+
run_services_validators(
|
|
315
|
+
request_headers=request.headers,
|
|
316
|
+
request_body=request.body,
|
|
317
|
+
request_method=request.method,
|
|
318
|
+
request_path=request.path,
|
|
319
|
+
databases=self._target_manager.cloud_databases,
|
|
320
|
+
)
|
|
321
|
+
except ValidatorError as exc:
|
|
322
|
+
return exc.status_code, exc.headers, exc.response_text
|
|
323
|
+
|
|
324
|
+
database = get_database_matching_server_keys(
|
|
325
|
+
request_headers=request.headers,
|
|
326
|
+
request_body=request.body,
|
|
327
|
+
request_method=request.method,
|
|
328
|
+
request_path=request.path,
|
|
329
|
+
databases=self._target_manager.cloud_databases,
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
request_json: dict[str, Any] = json.loads(s=request.body)
|
|
333
|
+
given_active_flag = request_json.get("active_flag")
|
|
334
|
+
active_flag = {
|
|
335
|
+
None: True,
|
|
336
|
+
True: True,
|
|
337
|
+
False: False,
|
|
338
|
+
}[given_active_flag]
|
|
339
|
+
|
|
340
|
+
application_metadata = request_json.get("application_metadata")
|
|
341
|
+
|
|
342
|
+
new_target = ImageTarget(
|
|
343
|
+
name=request_json["name"],
|
|
344
|
+
width=request_json["width"],
|
|
345
|
+
image_value=base64.b64decode(s=request_json["image"]),
|
|
346
|
+
active_flag=active_flag,
|
|
347
|
+
processing_time_seconds=self._processing_time_seconds,
|
|
348
|
+
application_metadata=application_metadata,
|
|
349
|
+
target_tracking_rater=self._target_tracking_rater,
|
|
350
|
+
)
|
|
351
|
+
database.targets.add(new_target)
|
|
352
|
+
|
|
353
|
+
date = email.utils.formatdate(
|
|
354
|
+
timeval=None,
|
|
355
|
+
localtime=False,
|
|
356
|
+
usegmt=True,
|
|
357
|
+
)
|
|
358
|
+
status_code = HTTPStatus.CREATED
|
|
359
|
+
body = {
|
|
360
|
+
"transaction_id": uuid.uuid4().hex,
|
|
361
|
+
"result_code": ResultCodes.TARGET_CREATED.value,
|
|
362
|
+
"target_id": new_target.target_id,
|
|
363
|
+
}
|
|
364
|
+
body_json = json_dump(body=body)
|
|
365
|
+
headers = {
|
|
366
|
+
"Connection": "keep-alive",
|
|
367
|
+
"Content-Type": "application/json",
|
|
368
|
+
"server": "envoy",
|
|
369
|
+
"Date": date,
|
|
370
|
+
"Content-Length": str(object=len(body_json)),
|
|
371
|
+
"x-envoy-upstream-service-time": "5",
|
|
372
|
+
"strict-transport-security": "max-age=31536000",
|
|
373
|
+
"x-aws-region": "us-east-2, us-west-2",
|
|
374
|
+
"x-content-type-options": "nosniff",
|
|
375
|
+
}
|
|
376
|
+
return status_code, headers, body_json
|
|
377
|
+
|
|
378
|
+
@route(
|
|
379
|
+
path_pattern=f"/targets/{_TARGET_ID_PATTERN}",
|
|
380
|
+
http_methods={HTTPMethod.DELETE},
|
|
381
|
+
)
|
|
382
|
+
def delete_target(self, request: RequestData) -> _ResponseType:
|
|
383
|
+
"""Delete a target.
|
|
384
|
+
|
|
385
|
+
Fake implementation of
|
|
386
|
+
https://developer.vuforia.com/library/web-api/cloud-targets-web-services-api#delete
|
|
387
|
+
"""
|
|
388
|
+
try:
|
|
389
|
+
run_services_validators(
|
|
390
|
+
request_headers=request.headers,
|
|
391
|
+
request_body=request.body,
|
|
392
|
+
request_method=request.method,
|
|
393
|
+
request_path=request.path,
|
|
394
|
+
databases=self._target_manager.cloud_databases,
|
|
395
|
+
)
|
|
396
|
+
except ValidatorError as exc:
|
|
397
|
+
return exc.status_code, exc.headers, exc.response_text
|
|
398
|
+
|
|
399
|
+
database = get_database_matching_server_keys(
|
|
400
|
+
request_headers=request.headers,
|
|
401
|
+
request_body=request.body,
|
|
402
|
+
request_method=request.method,
|
|
403
|
+
request_path=request.path,
|
|
404
|
+
databases=self._target_manager.cloud_databases,
|
|
405
|
+
)
|
|
406
|
+
|
|
407
|
+
target_id = request.path.split(sep="/")[-1]
|
|
408
|
+
target = database.get_target(target_id=target_id)
|
|
409
|
+
|
|
410
|
+
if target.status == TargetStatuses.PROCESSING.value:
|
|
411
|
+
target_processing_exception = TargetStatusProcessingError()
|
|
412
|
+
return (
|
|
413
|
+
target_processing_exception.status_code,
|
|
414
|
+
target_processing_exception.headers,
|
|
415
|
+
target_processing_exception.response_text,
|
|
416
|
+
)
|
|
417
|
+
|
|
418
|
+
now = datetime.datetime.now(tz=target.upload_date.tzinfo)
|
|
419
|
+
# See https://github.com/facebook/pyrefly/issues/1897
|
|
420
|
+
new_target: ImageTarget = copy.replace(
|
|
421
|
+
target, # pyrefly: ignore[bad-argument-type]
|
|
422
|
+
delete_date=now,
|
|
423
|
+
)
|
|
424
|
+
database.targets.remove(target)
|
|
425
|
+
database.targets.add(new_target)
|
|
426
|
+
date = email.utils.formatdate(
|
|
427
|
+
timeval=None,
|
|
428
|
+
localtime=False,
|
|
429
|
+
usegmt=True,
|
|
430
|
+
)
|
|
431
|
+
|
|
432
|
+
body = {
|
|
433
|
+
"transaction_id": uuid.uuid4().hex,
|
|
434
|
+
"result_code": ResultCodes.SUCCESS.value,
|
|
435
|
+
}
|
|
436
|
+
body_json = json_dump(body=body)
|
|
437
|
+
headers = {
|
|
438
|
+
"Connection": "keep-alive",
|
|
439
|
+
"Content-Length": str(object=len(body_json)),
|
|
440
|
+
"Content-Type": "application/json",
|
|
441
|
+
"Date": date,
|
|
442
|
+
"server": "envoy",
|
|
443
|
+
"x-envoy-upstream-service-time": "5",
|
|
444
|
+
"strict-transport-security": "max-age=31536000",
|
|
445
|
+
"x-aws-region": "us-east-2, us-west-2",
|
|
446
|
+
"x-content-type-options": "nosniff",
|
|
447
|
+
}
|
|
448
|
+
return HTTPStatus.OK, headers, body_json
|
|
449
|
+
|
|
450
|
+
@route(
|
|
451
|
+
path_pattern=f"/targets/{_TARGET_ID_PATTERN}/instances",
|
|
452
|
+
http_methods={HTTPMethod.POST},
|
|
453
|
+
)
|
|
454
|
+
def generate_vumark_instance(self, request: RequestData) -> _ResponseType:
|
|
455
|
+
"""Generate a VuMark instance."""
|
|
456
|
+
valid_accept_types: dict[str, bytes] = {
|
|
457
|
+
"image/png": VUMARK_PNG,
|
|
458
|
+
"image/svg+xml": VUMARK_SVG,
|
|
459
|
+
"application/pdf": VUMARK_PDF,
|
|
460
|
+
}
|
|
461
|
+
try:
|
|
462
|
+
all_databases: list[CloudDatabase | VuMarkDatabase] = [
|
|
463
|
+
*self._target_manager.cloud_databases,
|
|
464
|
+
*self._target_manager.vumark_databases,
|
|
465
|
+
]
|
|
466
|
+
run_services_validators(
|
|
467
|
+
request_headers=request.headers,
|
|
468
|
+
request_body=request.body,
|
|
469
|
+
request_method=request.method,
|
|
470
|
+
request_path=request.path,
|
|
471
|
+
databases=all_databases,
|
|
472
|
+
)
|
|
473
|
+
|
|
474
|
+
database = get_database_matching_server_keys(
|
|
475
|
+
request_headers=request.headers,
|
|
476
|
+
request_body=request.body,
|
|
477
|
+
request_method=request.method,
|
|
478
|
+
request_path=request.path,
|
|
479
|
+
databases=all_databases,
|
|
480
|
+
)
|
|
481
|
+
if not isinstance(database, VuMarkDatabase):
|
|
482
|
+
raise InvalidTargetTypeError
|
|
483
|
+
|
|
484
|
+
target_id = request.path.split(sep="/")[-2]
|
|
485
|
+
target = database.get_vumark_target(target_id=target_id)
|
|
486
|
+
if target.status != TargetStatuses.SUCCESS.value:
|
|
487
|
+
raise TargetStatusNotSuccessError
|
|
488
|
+
|
|
489
|
+
accept = dict(request.headers).get("Accept", "")
|
|
490
|
+
if accept not in valid_accept_types:
|
|
491
|
+
raise InvalidAcceptHeaderError
|
|
492
|
+
|
|
493
|
+
request_json = json.loads(s=request.body)
|
|
494
|
+
instance_id = request_json.get("instance_id", "")
|
|
495
|
+
if not instance_id:
|
|
496
|
+
raise InvalidInstanceIdError
|
|
497
|
+
except ValidatorError as exc:
|
|
498
|
+
return exc.status_code, exc.headers, exc.response_text
|
|
499
|
+
|
|
500
|
+
response_body = valid_accept_types[accept]
|
|
501
|
+
content_type = accept
|
|
502
|
+
date = email.utils.formatdate(
|
|
503
|
+
timeval=None,
|
|
504
|
+
localtime=False,
|
|
505
|
+
usegmt=True,
|
|
506
|
+
)
|
|
507
|
+
headers = {
|
|
508
|
+
"Connection": "keep-alive",
|
|
509
|
+
"Content-Type": content_type,
|
|
510
|
+
"Date": date,
|
|
511
|
+
"server": "envoy",
|
|
512
|
+
"x-envoy-upstream-service-time": "5",
|
|
513
|
+
"strict-transport-security": "max-age=31536000",
|
|
514
|
+
"x-aws-region": "us-east-2, us-west-2",
|
|
515
|
+
"x-content-type-options": "nosniff",
|
|
516
|
+
}
|
|
517
|
+
return HTTPStatus.OK, headers, response_body
|
|
518
|
+
|
|
519
|
+
@route(path_pattern="/summary", http_methods={HTTPMethod.GET})
|
|
520
|
+
def database_summary(self, request: RequestData) -> _ResponseType:
|
|
521
|
+
"""Get a database summary report.
|
|
522
|
+
|
|
523
|
+
Fake implementation of
|
|
524
|
+
https://developer.vuforia.com/library/web-api/cloud-targets-web-services-api#summary-report
|
|
525
|
+
"""
|
|
526
|
+
try:
|
|
527
|
+
run_services_validators(
|
|
528
|
+
request_headers=request.headers,
|
|
529
|
+
request_body=request.body,
|
|
530
|
+
request_method=request.method,
|
|
531
|
+
request_path=request.path,
|
|
532
|
+
databases=self._target_manager.cloud_databases,
|
|
533
|
+
)
|
|
534
|
+
except ValidatorError as exc:
|
|
535
|
+
return exc.status_code, exc.headers, exc.response_text
|
|
536
|
+
|
|
537
|
+
database = get_database_matching_server_keys(
|
|
538
|
+
request_headers=request.headers,
|
|
539
|
+
request_body=request.body,
|
|
540
|
+
request_method=request.method,
|
|
541
|
+
request_path=request.path,
|
|
542
|
+
databases=self._target_manager.cloud_databases,
|
|
543
|
+
)
|
|
544
|
+
|
|
545
|
+
date = email.utils.formatdate(
|
|
546
|
+
timeval=None,
|
|
547
|
+
localtime=False,
|
|
548
|
+
usegmt=True,
|
|
549
|
+
)
|
|
550
|
+
body = {
|
|
551
|
+
"result_code": ResultCodes.SUCCESS.value,
|
|
552
|
+
"transaction_id": uuid.uuid4().hex,
|
|
553
|
+
"name": database.database_name,
|
|
554
|
+
"active_images": len(database.active_targets),
|
|
555
|
+
"inactive_images": len(database.inactive_targets),
|
|
556
|
+
"failed_images": len(database.failed_targets),
|
|
557
|
+
"target_quota": database.target_quota,
|
|
558
|
+
"total_recos": database.total_recos,
|
|
559
|
+
"current_month_recos": database.current_month_recos,
|
|
560
|
+
"previous_month_recos": database.previous_month_recos,
|
|
561
|
+
"processing_images": len(database.processing_targets),
|
|
562
|
+
"reco_threshold": database.reco_threshold,
|
|
563
|
+
"request_quota": database.request_quota,
|
|
564
|
+
"request_usage": 0,
|
|
565
|
+
}
|
|
566
|
+
body_json = json_dump(body=body)
|
|
567
|
+
headers = {
|
|
568
|
+
"Connection": "keep-alive",
|
|
569
|
+
"Content-Length": str(object=len(body_json)),
|
|
570
|
+
"Content-Type": "application/json",
|
|
571
|
+
"Date": date,
|
|
572
|
+
"server": "envoy",
|
|
573
|
+
"x-envoy-upstream-service-time": "5",
|
|
574
|
+
"strict-transport-security": "max-age=31536000",
|
|
575
|
+
"x-aws-region": "us-east-2, us-west-2",
|
|
576
|
+
"x-content-type-options": "nosniff",
|
|
577
|
+
}
|
|
578
|
+
return HTTPStatus.OK, headers, body_json
|
|
579
|
+
|
|
580
|
+
@route(path_pattern="/targets", http_methods={HTTPMethod.GET})
|
|
581
|
+
def target_list(self, request: RequestData) -> _ResponseType:
|
|
582
|
+
"""Get a list of all targets.
|
|
583
|
+
|
|
584
|
+
Fake implementation of
|
|
585
|
+
https://developer.vuforia.com/library/web-api/cloud-targets-web-services-api#details-list
|
|
586
|
+
"""
|
|
587
|
+
try:
|
|
588
|
+
run_services_validators(
|
|
589
|
+
request_headers=request.headers,
|
|
590
|
+
request_body=request.body,
|
|
591
|
+
request_method=request.method,
|
|
592
|
+
request_path=request.path,
|
|
593
|
+
databases=self._target_manager.cloud_databases,
|
|
594
|
+
)
|
|
595
|
+
except ValidatorError as exc:
|
|
596
|
+
return exc.status_code, exc.headers, exc.response_text
|
|
597
|
+
|
|
598
|
+
database = get_database_matching_server_keys(
|
|
599
|
+
request_headers=request.headers,
|
|
600
|
+
request_body=request.body,
|
|
601
|
+
request_method=request.method,
|
|
602
|
+
request_path=request.path,
|
|
603
|
+
databases=self._target_manager.cloud_databases,
|
|
604
|
+
)
|
|
605
|
+
|
|
606
|
+
date = email.utils.formatdate(
|
|
607
|
+
timeval=None,
|
|
608
|
+
localtime=False,
|
|
609
|
+
usegmt=True,
|
|
610
|
+
)
|
|
611
|
+
|
|
612
|
+
response_results = [
|
|
613
|
+
target.target_id for target in database.not_deleted_targets
|
|
614
|
+
]
|
|
615
|
+
body = {
|
|
616
|
+
"transaction_id": uuid.uuid4().hex,
|
|
617
|
+
"result_code": ResultCodes.SUCCESS.value,
|
|
618
|
+
"results": response_results,
|
|
619
|
+
}
|
|
620
|
+
body_json = json_dump(body=body)
|
|
621
|
+
headers = {
|
|
622
|
+
"Connection": "keep-alive",
|
|
623
|
+
"Content-Length": str(object=len(body_json)),
|
|
624
|
+
"Content-Type": "application/json",
|
|
625
|
+
"Date": date,
|
|
626
|
+
"server": "envoy",
|
|
627
|
+
"x-envoy-upstream-service-time": "5",
|
|
628
|
+
"strict-transport-security": "max-age=31536000",
|
|
629
|
+
"x-aws-region": "us-east-2, us-west-2",
|
|
630
|
+
"x-content-type-options": "nosniff",
|
|
631
|
+
}
|
|
632
|
+
return HTTPStatus.OK, headers, body_json
|
|
633
|
+
|
|
634
|
+
@route(
|
|
635
|
+
path_pattern=f"/targets/{_TARGET_ID_PATTERN}",
|
|
636
|
+
http_methods={HTTPMethod.GET},
|
|
637
|
+
)
|
|
638
|
+
def get_target(self, request: RequestData) -> _ResponseType:
|
|
639
|
+
"""Get details of a target.
|
|
640
|
+
|
|
641
|
+
Fake implementation of
|
|
642
|
+
https://developer.vuforia.com/library/web-api/cloud-targets-web-services-api#target-record
|
|
643
|
+
"""
|
|
644
|
+
try:
|
|
645
|
+
run_services_validators(
|
|
646
|
+
request_headers=request.headers,
|
|
647
|
+
request_body=request.body,
|
|
648
|
+
request_method=request.method,
|
|
649
|
+
request_path=request.path,
|
|
650
|
+
databases=self._target_manager.cloud_databases,
|
|
651
|
+
)
|
|
652
|
+
except ValidatorError as exc:
|
|
653
|
+
return exc.status_code, exc.headers, exc.response_text
|
|
654
|
+
|
|
655
|
+
database = get_database_matching_server_keys(
|
|
656
|
+
request_headers=request.headers,
|
|
657
|
+
request_body=request.body,
|
|
658
|
+
request_method=request.method,
|
|
659
|
+
request_path=request.path,
|
|
660
|
+
databases=self._target_manager.cloud_databases,
|
|
661
|
+
)
|
|
662
|
+
target_id = request.path.split(sep="/")[-1]
|
|
663
|
+
target = database.get_target(target_id=target_id)
|
|
664
|
+
|
|
665
|
+
width = target.width
|
|
666
|
+
tracking_rating = target.tracking_rating
|
|
667
|
+
reco_rating = target.reco_rating
|
|
668
|
+
target_record = {
|
|
669
|
+
"target_id": target.target_id,
|
|
670
|
+
"active_flag": target.active_flag,
|
|
671
|
+
"name": target.name,
|
|
672
|
+
"width": width,
|
|
673
|
+
"tracking_rating": tracking_rating,
|
|
674
|
+
"reco_rating": reco_rating,
|
|
675
|
+
}
|
|
676
|
+
date = email.utils.formatdate(
|
|
677
|
+
timeval=None,
|
|
678
|
+
localtime=False,
|
|
679
|
+
usegmt=True,
|
|
680
|
+
)
|
|
681
|
+
|
|
682
|
+
body = {
|
|
683
|
+
"result_code": ResultCodes.SUCCESS.value,
|
|
684
|
+
"transaction_id": uuid.uuid4().hex,
|
|
685
|
+
"target_record": target_record,
|
|
686
|
+
"status": target.status,
|
|
687
|
+
}
|
|
688
|
+
body_json = json_dump(body=body)
|
|
689
|
+
headers = {
|
|
690
|
+
"Connection": "keep-alive",
|
|
691
|
+
"Content-Length": str(object=len(body_json)),
|
|
692
|
+
"Content-Type": "application/json",
|
|
693
|
+
"Date": date,
|
|
694
|
+
"server": "envoy",
|
|
695
|
+
"x-envoy-upstream-service-time": "5",
|
|
696
|
+
"strict-transport-security": "max-age=31536000",
|
|
697
|
+
"x-aws-region": "us-east-2, us-west-2",
|
|
698
|
+
"x-content-type-options": "nosniff",
|
|
699
|
+
}
|
|
700
|
+
return HTTPStatus.OK, headers, body_json
|
|
701
|
+
|
|
702
|
+
@route(
|
|
703
|
+
path_pattern=f"/duplicates/{_TARGET_ID_PATTERN}",
|
|
704
|
+
http_methods={HTTPMethod.GET},
|
|
705
|
+
)
|
|
706
|
+
def get_duplicates(self, request: RequestData) -> _ResponseType:
|
|
707
|
+
"""Get targets which may be considered duplicates of a given
|
|
708
|
+
target.
|
|
709
|
+
|
|
710
|
+
Fake implementation of
|
|
711
|
+
https://developer.vuforia.com/library/web-api/cloud-targets-web-services-api#check
|
|
712
|
+
"""
|
|
713
|
+
try:
|
|
714
|
+
run_services_validators(
|
|
715
|
+
request_headers=request.headers,
|
|
716
|
+
request_body=request.body,
|
|
717
|
+
request_method=request.method,
|
|
718
|
+
request_path=request.path,
|
|
719
|
+
databases=self._target_manager.cloud_databases,
|
|
720
|
+
)
|
|
721
|
+
except ValidatorError as exc:
|
|
722
|
+
return exc.status_code, exc.headers, exc.response_text
|
|
723
|
+
|
|
724
|
+
database = get_database_matching_server_keys(
|
|
725
|
+
request_headers=request.headers,
|
|
726
|
+
request_body=request.body,
|
|
727
|
+
request_method=request.method,
|
|
728
|
+
request_path=request.path,
|
|
729
|
+
databases=self._target_manager.cloud_databases,
|
|
730
|
+
)
|
|
731
|
+
target_id = request.path.split(sep="/")[-1]
|
|
732
|
+
target = database.get_target(target_id=target_id)
|
|
733
|
+
|
|
734
|
+
other_targets = database.targets - {target}
|
|
735
|
+
|
|
736
|
+
similar_targets = [
|
|
737
|
+
other.target_id
|
|
738
|
+
for other in other_targets
|
|
739
|
+
if self._duplicate_match_checker(
|
|
740
|
+
first_image_content=target.image_value,
|
|
741
|
+
second_image_content=other.image_value,
|
|
742
|
+
)
|
|
743
|
+
and TargetStatuses.FAILED.value
|
|
744
|
+
not in {target.status, other.status}
|
|
745
|
+
and TargetStatuses.PROCESSING.value != other.status
|
|
746
|
+
and other.active_flag
|
|
747
|
+
]
|
|
748
|
+
|
|
749
|
+
date = email.utils.formatdate(
|
|
750
|
+
timeval=None,
|
|
751
|
+
localtime=False,
|
|
752
|
+
usegmt=True,
|
|
753
|
+
)
|
|
754
|
+
body = {
|
|
755
|
+
"transaction_id": uuid.uuid4().hex,
|
|
756
|
+
"result_code": ResultCodes.SUCCESS.value,
|
|
757
|
+
"similar_targets": similar_targets,
|
|
758
|
+
}
|
|
759
|
+
body_json = json_dump(body=body)
|
|
760
|
+
headers = {
|
|
761
|
+
"Connection": "keep-alive",
|
|
762
|
+
"Content-Length": str(object=len(body_json)),
|
|
763
|
+
"Content-Type": "application/json",
|
|
764
|
+
"Date": date,
|
|
765
|
+
"server": "envoy",
|
|
766
|
+
"x-envoy-upstream-service-time": "5",
|
|
767
|
+
"strict-transport-security": "max-age=31536000",
|
|
768
|
+
"x-aws-region": "us-east-2, us-west-2",
|
|
769
|
+
"x-content-type-options": "nosniff",
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
return HTTPStatus.OK, headers, body_json
|
|
773
|
+
|
|
774
|
+
@route(
|
|
775
|
+
path_pattern=f"/targets/{_TARGET_ID_PATTERN}",
|
|
776
|
+
http_methods={HTTPMethod.PUT},
|
|
777
|
+
)
|
|
778
|
+
def update_target(self, request: RequestData) -> _ResponseType:
|
|
779
|
+
"""Update a target.
|
|
780
|
+
|
|
781
|
+
Fake implementation of
|
|
782
|
+
https://developer.vuforia.com/library/web-api/cloud-targets-web-services-api#update
|
|
783
|
+
"""
|
|
784
|
+
try:
|
|
785
|
+
run_services_validators(
|
|
786
|
+
request_headers=request.headers,
|
|
787
|
+
request_body=request.body,
|
|
788
|
+
request_method=request.method,
|
|
789
|
+
request_path=request.path,
|
|
790
|
+
databases=self._target_manager.cloud_databases,
|
|
791
|
+
)
|
|
792
|
+
except ValidatorError as exc:
|
|
793
|
+
return exc.status_code, exc.headers, exc.response_text
|
|
794
|
+
|
|
795
|
+
database = get_database_matching_server_keys(
|
|
796
|
+
request_headers=request.headers,
|
|
797
|
+
request_body=request.body,
|
|
798
|
+
request_method=request.method,
|
|
799
|
+
request_path=request.path,
|
|
800
|
+
databases=self._target_manager.cloud_databases,
|
|
801
|
+
)
|
|
802
|
+
|
|
803
|
+
target_id = request.path.split(sep="/")[-1]
|
|
804
|
+
target = database.get_target(target_id=target_id)
|
|
805
|
+
|
|
806
|
+
date = email.utils.formatdate(
|
|
807
|
+
timeval=None,
|
|
808
|
+
localtime=False,
|
|
809
|
+
usegmt=True,
|
|
810
|
+
)
|
|
811
|
+
|
|
812
|
+
if target.status != TargetStatuses.SUCCESS.value:
|
|
813
|
+
exception = TargetStatusNotSuccessError()
|
|
814
|
+
return (
|
|
815
|
+
exception.status_code,
|
|
816
|
+
exception.headers,
|
|
817
|
+
exception.response_text,
|
|
818
|
+
)
|
|
819
|
+
|
|
820
|
+
request_json: dict[str, Any] = json.loads(s=request.body)
|
|
821
|
+
name = request_json.get("name", target.name)
|
|
822
|
+
active_flag = request_json.get("active_flag", target.active_flag)
|
|
823
|
+
|
|
824
|
+
if "active_flag" in request_json and active_flag is None:
|
|
825
|
+
fail_exception = FailError(status_code=HTTPStatus.BAD_REQUEST)
|
|
826
|
+
return (
|
|
827
|
+
fail_exception.status_code,
|
|
828
|
+
fail_exception.headers,
|
|
829
|
+
fail_exception.response_text,
|
|
830
|
+
)
|
|
831
|
+
|
|
832
|
+
gmt = ZoneInfo(key="GMT")
|
|
833
|
+
last_modified_date = datetime.datetime.now(tz=gmt)
|
|
834
|
+
|
|
835
|
+
width = request_json.get("width", target.width)
|
|
836
|
+
application_metadata = request_json.get(
|
|
837
|
+
"application_metadata",
|
|
838
|
+
target.application_metadata,
|
|
839
|
+
)
|
|
840
|
+
|
|
841
|
+
image_value = target.image_value
|
|
842
|
+
if "image" in request_json:
|
|
843
|
+
image_value = base64.b64decode(s=request_json["image"])
|
|
844
|
+
|
|
845
|
+
if (
|
|
846
|
+
"application_metadata" in request_json
|
|
847
|
+
and application_metadata is None
|
|
848
|
+
):
|
|
849
|
+
fail_exception = FailError(status_code=HTTPStatus.BAD_REQUEST)
|
|
850
|
+
return (
|
|
851
|
+
fail_exception.status_code,
|
|
852
|
+
fail_exception.headers,
|
|
853
|
+
fail_exception.response_text,
|
|
854
|
+
)
|
|
855
|
+
|
|
856
|
+
# See https://github.com/facebook/pyrefly/issues/1897
|
|
857
|
+
new_target: ImageTarget = copy.replace(
|
|
858
|
+
target, # pyrefly: ignore[bad-argument-type]
|
|
859
|
+
name=name,
|
|
860
|
+
width=width,
|
|
861
|
+
active_flag=active_flag,
|
|
862
|
+
application_metadata=application_metadata,
|
|
863
|
+
image_value=image_value,
|
|
864
|
+
last_modified_date=last_modified_date,
|
|
865
|
+
)
|
|
866
|
+
|
|
867
|
+
database.targets.remove(target)
|
|
868
|
+
database.targets.add(new_target)
|
|
869
|
+
|
|
870
|
+
body = {
|
|
871
|
+
"result_code": ResultCodes.SUCCESS.value,
|
|
872
|
+
"transaction_id": uuid.uuid4().hex,
|
|
873
|
+
}
|
|
874
|
+
body_json = json_dump(body=body)
|
|
875
|
+
headers = {
|
|
876
|
+
"Connection": "keep-alive",
|
|
877
|
+
"Content-Type": "application/json",
|
|
878
|
+
"server": "envoy",
|
|
879
|
+
"Date": date,
|
|
880
|
+
"Content-Length": str(object=len(body_json)),
|
|
881
|
+
"x-envoy-upstream-service-time": "5",
|
|
882
|
+
"strict-transport-security": "max-age=31536000",
|
|
883
|
+
"x-aws-region": "us-east-2, us-west-2",
|
|
884
|
+
"x-content-type-options": "nosniff",
|
|
885
|
+
}
|
|
886
|
+
return HTTPStatus.OK, headers, body_json
|
|
887
|
+
|
|
888
|
+
@route(
|
|
889
|
+
path_pattern=f"/summary/{_TARGET_ID_PATTERN}",
|
|
890
|
+
http_methods={HTTPMethod.GET},
|
|
891
|
+
)
|
|
892
|
+
def target_summary(self, request: RequestData) -> _ResponseType:
|
|
893
|
+
"""Get a summary report for a target.
|
|
894
|
+
|
|
895
|
+
Fake implementation of
|
|
896
|
+
https://developer.vuforia.com/library/web-api/cloud-targets-web-services-api#retrieve-report
|
|
897
|
+
"""
|
|
898
|
+
try:
|
|
899
|
+
run_services_validators(
|
|
900
|
+
request_headers=request.headers,
|
|
901
|
+
request_body=request.body,
|
|
902
|
+
request_method=request.method,
|
|
903
|
+
request_path=request.path,
|
|
904
|
+
databases=self._target_manager.cloud_databases,
|
|
905
|
+
)
|
|
906
|
+
except ValidatorError as exc:
|
|
907
|
+
return exc.status_code, exc.headers, exc.response_text
|
|
908
|
+
|
|
909
|
+
database = get_database_matching_server_keys(
|
|
910
|
+
request_headers=request.headers,
|
|
911
|
+
request_body=request.body,
|
|
912
|
+
request_method=request.method,
|
|
913
|
+
request_path=request.path,
|
|
914
|
+
databases=self._target_manager.cloud_databases,
|
|
915
|
+
)
|
|
916
|
+
target_id = request.path.split(sep="/")[-1]
|
|
917
|
+
target = database.get_target(target_id=target_id)
|
|
918
|
+
|
|
919
|
+
date = email.utils.formatdate(
|
|
920
|
+
timeval=None,
|
|
921
|
+
localtime=False,
|
|
922
|
+
usegmt=True,
|
|
923
|
+
)
|
|
924
|
+
tracking_rating = target.tracking_rating
|
|
925
|
+
total_recos = target.total_recos
|
|
926
|
+
current_month_recos = target.current_month_recos
|
|
927
|
+
previous_month_recos = target.previous_month_recos
|
|
928
|
+
body = {
|
|
929
|
+
"status": target.status,
|
|
930
|
+
"transaction_id": uuid.uuid4().hex,
|
|
931
|
+
"result_code": ResultCodes.SUCCESS.value,
|
|
932
|
+
"database_name": database.database_name,
|
|
933
|
+
"target_name": target.name,
|
|
934
|
+
"upload_date": target.upload_date.strftime(format="%Y-%m-%d"),
|
|
935
|
+
"active_flag": target.active_flag,
|
|
936
|
+
"tracking_rating": tracking_rating,
|
|
937
|
+
"total_recos": total_recos,
|
|
938
|
+
"current_month_recos": current_month_recos,
|
|
939
|
+
"previous_month_recos": previous_month_recos,
|
|
940
|
+
}
|
|
941
|
+
body_json = json_dump(body=body)
|
|
942
|
+
headers = {
|
|
943
|
+
"Connection": "keep-alive",
|
|
944
|
+
"Content-Length": str(object=len(body_json)),
|
|
945
|
+
"Content-Type": "application/json",
|
|
946
|
+
"Date": date,
|
|
947
|
+
"server": "envoy",
|
|
948
|
+
"x-envoy-upstream-service-time": "5",
|
|
949
|
+
"strict-transport-security": "max-age=31536000",
|
|
950
|
+
"x-aws-region": "us-east-2, us-west-2",
|
|
951
|
+
"x-content-type-options": "nosniff",
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
return HTTPStatus.OK, headers, body_json
|