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,235 @@
|
|
|
1
|
+
"""Validators for target names."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import logging
|
|
5
|
+
from collections.abc import Iterable, Mapping
|
|
6
|
+
from http import HTTPMethod, HTTPStatus
|
|
7
|
+
|
|
8
|
+
from beartype import beartype
|
|
9
|
+
|
|
10
|
+
from mock_vws._database_matchers import (
|
|
11
|
+
AnyDatabase,
|
|
12
|
+
get_database_matching_server_keys,
|
|
13
|
+
)
|
|
14
|
+
from mock_vws._services_validators.exceptions import (
|
|
15
|
+
FailError,
|
|
16
|
+
TargetNameExistError,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
_LOGGER = logging.getLogger(name=__name__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@beartype
|
|
23
|
+
def validate_name_characters_in_range(
|
|
24
|
+
*,
|
|
25
|
+
request_body: bytes,
|
|
26
|
+
request_method: str,
|
|
27
|
+
request_path: str,
|
|
28
|
+
) -> None:
|
|
29
|
+
"""Validate the characters in the name argument given to a VWS
|
|
30
|
+
endpoint.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
request_body: The body of the request.
|
|
34
|
+
request_method: The HTTP method the request is using.
|
|
35
|
+
request_path: The path to the endpoint.
|
|
36
|
+
|
|
37
|
+
Raises:
|
|
38
|
+
FailError: Characters are out of range and the request is trying to
|
|
39
|
+
make a new target.
|
|
40
|
+
TargetNameExistError: Characters are out of range and the request is
|
|
41
|
+
for another endpoint.
|
|
42
|
+
"""
|
|
43
|
+
if not request_body:
|
|
44
|
+
return
|
|
45
|
+
|
|
46
|
+
request_text = request_body.decode()
|
|
47
|
+
if "name" not in json.loads(s=request_text):
|
|
48
|
+
return
|
|
49
|
+
|
|
50
|
+
name = json.loads(s=request_text)["name"]
|
|
51
|
+
|
|
52
|
+
max_character_ord = 65535
|
|
53
|
+
if all(ord(character) <= max_character_ord for character in name):
|
|
54
|
+
return
|
|
55
|
+
|
|
56
|
+
if (request_method, request_path) == (HTTPMethod.POST, "/targets"):
|
|
57
|
+
_LOGGER.warning(msg="Characters are out of range.")
|
|
58
|
+
raise FailError(status_code=HTTPStatus.INTERNAL_SERVER_ERROR)
|
|
59
|
+
|
|
60
|
+
_LOGGER.warning(msg="Characters are out of range.")
|
|
61
|
+
raise TargetNameExistError
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@beartype
|
|
65
|
+
def validate_name_type(*, request_body: bytes) -> None:
|
|
66
|
+
"""Validate the type of the name argument given to a VWS endpoint.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
request_body: The body of the request.
|
|
70
|
+
|
|
71
|
+
Raises:
|
|
72
|
+
FailError: A name is given and it is not a string.
|
|
73
|
+
"""
|
|
74
|
+
if not request_body:
|
|
75
|
+
return
|
|
76
|
+
|
|
77
|
+
request_text = request_body.decode()
|
|
78
|
+
if "name" not in json.loads(s=request_text):
|
|
79
|
+
return
|
|
80
|
+
|
|
81
|
+
name = json.loads(s=request_text)["name"]
|
|
82
|
+
|
|
83
|
+
if isinstance(name, str):
|
|
84
|
+
return
|
|
85
|
+
|
|
86
|
+
_LOGGER.warning(msg="Name is not a string.")
|
|
87
|
+
raise FailError(status_code=HTTPStatus.BAD_REQUEST)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@beartype
|
|
91
|
+
def validate_name_length(*, request_body: bytes) -> None:
|
|
92
|
+
"""Validate the length of the name argument given to a VWS endpoint.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
request_body: The body of the request.
|
|
96
|
+
|
|
97
|
+
Raises:
|
|
98
|
+
FailError: A name is given and it is not a between 1 and 64 characters
|
|
99
|
+
in length.
|
|
100
|
+
"""
|
|
101
|
+
if not request_body:
|
|
102
|
+
return
|
|
103
|
+
|
|
104
|
+
request_text = request_body.decode()
|
|
105
|
+
if "name" not in json.loads(s=request_text):
|
|
106
|
+
return
|
|
107
|
+
|
|
108
|
+
name = json.loads(s=request_text)["name"]
|
|
109
|
+
|
|
110
|
+
max_length = 64
|
|
111
|
+
if name and len(name) <= max_length:
|
|
112
|
+
return
|
|
113
|
+
|
|
114
|
+
_LOGGER.warning(msg="Name is not between 1 and 64 characters in length.")
|
|
115
|
+
raise FailError(status_code=HTTPStatus.BAD_REQUEST)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
@beartype
|
|
119
|
+
def validate_name_does_not_exist_new_target(
|
|
120
|
+
*,
|
|
121
|
+
databases: Iterable[AnyDatabase],
|
|
122
|
+
request_body: bytes,
|
|
123
|
+
request_headers: Mapping[str, str],
|
|
124
|
+
request_method: str,
|
|
125
|
+
request_path: str,
|
|
126
|
+
) -> None:
|
|
127
|
+
"""Validate that the name does not exist for any existing target.
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
databases: All Vuforia databases.
|
|
131
|
+
request_body: The body of the request.
|
|
132
|
+
request_headers: The headers sent with the request.
|
|
133
|
+
request_method: The HTTP method the request is using.
|
|
134
|
+
request_path: The path to the endpoint.
|
|
135
|
+
|
|
136
|
+
Raises:
|
|
137
|
+
TargetNameExistError: The target name already exists.
|
|
138
|
+
"""
|
|
139
|
+
if not request_body:
|
|
140
|
+
return
|
|
141
|
+
|
|
142
|
+
request_text = request_body.decode()
|
|
143
|
+
if "name" not in json.loads(s=request_text):
|
|
144
|
+
return
|
|
145
|
+
|
|
146
|
+
split_path = request_path.split(sep="/")
|
|
147
|
+
|
|
148
|
+
split_path_no_target_id_length = 2
|
|
149
|
+
if len(split_path) != split_path_no_target_id_length:
|
|
150
|
+
return
|
|
151
|
+
|
|
152
|
+
name = json.loads(s=request_text)["name"]
|
|
153
|
+
database = get_database_matching_server_keys(
|
|
154
|
+
request_headers=request_headers,
|
|
155
|
+
request_body=request_body,
|
|
156
|
+
request_method=request_method,
|
|
157
|
+
request_path=request_path,
|
|
158
|
+
databases=databases,
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
matching_name_targets = [
|
|
162
|
+
target
|
|
163
|
+
for target in database.not_deleted_targets
|
|
164
|
+
if target.name == name
|
|
165
|
+
]
|
|
166
|
+
|
|
167
|
+
if not matching_name_targets:
|
|
168
|
+
return
|
|
169
|
+
|
|
170
|
+
_LOGGER.warning(msg="Target name already exists.")
|
|
171
|
+
raise TargetNameExistError
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
@beartype
|
|
175
|
+
def validate_name_does_not_exist_existing_target(
|
|
176
|
+
*,
|
|
177
|
+
request_headers: Mapping[str, str],
|
|
178
|
+
request_body: bytes,
|
|
179
|
+
request_method: str,
|
|
180
|
+
request_path: str,
|
|
181
|
+
databases: Iterable[AnyDatabase],
|
|
182
|
+
) -> None:
|
|
183
|
+
"""Validate that the name does not exist for any existing target apart
|
|
184
|
+
from
|
|
185
|
+
the one being updated.
|
|
186
|
+
|
|
187
|
+
Args:
|
|
188
|
+
databases: All Vuforia databases.
|
|
189
|
+
request_body: The body of the request.
|
|
190
|
+
request_headers: The headers sent with the request.
|
|
191
|
+
request_method: The HTTP method the request is using.
|
|
192
|
+
request_path: The path to the endpoint.
|
|
193
|
+
|
|
194
|
+
Raises:
|
|
195
|
+
TargetNameExistError: The target name is not the same as the name of
|
|
196
|
+
the target being updated but it is the same as another target.
|
|
197
|
+
"""
|
|
198
|
+
if not request_body:
|
|
199
|
+
return
|
|
200
|
+
|
|
201
|
+
request_text = request_body.decode()
|
|
202
|
+
if "name" not in json.loads(s=request_text):
|
|
203
|
+
return
|
|
204
|
+
|
|
205
|
+
split_path = request_path.split(sep="/")
|
|
206
|
+
split_path_no_target_id_length = 2
|
|
207
|
+
if len(split_path) == split_path_no_target_id_length:
|
|
208
|
+
return
|
|
209
|
+
|
|
210
|
+
target_id = split_path[-1]
|
|
211
|
+
|
|
212
|
+
name = json.loads(s=request_text)["name"]
|
|
213
|
+
database = get_database_matching_server_keys(
|
|
214
|
+
request_headers=request_headers,
|
|
215
|
+
request_body=request_body,
|
|
216
|
+
request_method=request_method,
|
|
217
|
+
request_path=request_path,
|
|
218
|
+
databases=databases,
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
matching_name_targets = [
|
|
222
|
+
target
|
|
223
|
+
for target in database.not_deleted_targets
|
|
224
|
+
if target.name == name
|
|
225
|
+
]
|
|
226
|
+
|
|
227
|
+
if not matching_name_targets:
|
|
228
|
+
return
|
|
229
|
+
|
|
230
|
+
(matching_name_target,) = matching_name_targets
|
|
231
|
+
if matching_name_target.target_id == target_id:
|
|
232
|
+
return
|
|
233
|
+
|
|
234
|
+
_LOGGER.warning(msg="Name already exists for another target.")
|
|
235
|
+
raise TargetNameExistError
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""Validators for the project state."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from collections.abc import Iterable, Mapping
|
|
5
|
+
from http import HTTPMethod
|
|
6
|
+
|
|
7
|
+
from beartype import beartype
|
|
8
|
+
|
|
9
|
+
from mock_vws._database_matchers import (
|
|
10
|
+
AnyDatabase,
|
|
11
|
+
get_database_matching_server_keys,
|
|
12
|
+
)
|
|
13
|
+
from mock_vws._services_validators.exceptions import (
|
|
14
|
+
ProjectHasNoAPIAccessError,
|
|
15
|
+
ProjectInactiveError,
|
|
16
|
+
ProjectSuspendedError,
|
|
17
|
+
ValidatorError,
|
|
18
|
+
)
|
|
19
|
+
from mock_vws.database import CloudDatabase, VuMarkDatabase
|
|
20
|
+
from mock_vws.states import States
|
|
21
|
+
|
|
22
|
+
_LOGGER = logging.getLogger(name=__name__)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@beartype
|
|
26
|
+
def validate_project_state(
|
|
27
|
+
*,
|
|
28
|
+
request_path: str,
|
|
29
|
+
request_headers: Mapping[str, str],
|
|
30
|
+
request_body: bytes,
|
|
31
|
+
request_method: str,
|
|
32
|
+
databases: Iterable[AnyDatabase],
|
|
33
|
+
) -> None:
|
|
34
|
+
"""Validate the state of the project.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
request_path: The path of the request.
|
|
38
|
+
request_headers: The headers sent with the request.
|
|
39
|
+
request_body: The body of the request.
|
|
40
|
+
request_method: The HTTP method of the request.
|
|
41
|
+
databases: All Vuforia databases.
|
|
42
|
+
|
|
43
|
+
Raises:
|
|
44
|
+
ProjectInactiveError: The project is inactive and this endpoint does
|
|
45
|
+
not work with inactive projects.
|
|
46
|
+
"""
|
|
47
|
+
database = get_database_matching_server_keys(
|
|
48
|
+
request_headers=request_headers,
|
|
49
|
+
request_body=request_body,
|
|
50
|
+
request_method=request_method,
|
|
51
|
+
request_path=request_path,
|
|
52
|
+
databases=databases,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
state_errors: dict[States, type[ValidatorError]] = {
|
|
56
|
+
States.PROJECT_HAS_NO_API_ACCESS: ProjectHasNoAPIAccessError,
|
|
57
|
+
States.PROJECT_SUSPENDED: ProjectSuspendedError,
|
|
58
|
+
}
|
|
59
|
+
if error := state_errors.get(database.state):
|
|
60
|
+
raise error
|
|
61
|
+
|
|
62
|
+
if database.state != States.PROJECT_INACTIVE:
|
|
63
|
+
return
|
|
64
|
+
|
|
65
|
+
if (
|
|
66
|
+
isinstance(database, CloudDatabase)
|
|
67
|
+
and request_method == HTTPMethod.GET
|
|
68
|
+
and "duplicates" not in request_path
|
|
69
|
+
):
|
|
70
|
+
return
|
|
71
|
+
|
|
72
|
+
if isinstance(database, VuMarkDatabase):
|
|
73
|
+
return
|
|
74
|
+
|
|
75
|
+
_LOGGER.warning(msg="The project is inactive.")
|
|
76
|
+
raise ProjectInactiveError
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""Validators for the VWS request quota.
|
|
2
|
+
|
|
3
|
+
This behavior cannot be verified against the real Vuforia Web Services
|
|
4
|
+
without deliberately exhausting a database's request quota. It implements the
|
|
5
|
+
publicly documented behavior so that users can exercise their application's
|
|
6
|
+
quota-error handling with the mock.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from collections.abc import Iterable, Mapping
|
|
10
|
+
|
|
11
|
+
from beartype import beartype
|
|
12
|
+
|
|
13
|
+
from mock_vws._database_matchers import (
|
|
14
|
+
AnyDatabase,
|
|
15
|
+
get_database_matching_server_keys,
|
|
16
|
+
)
|
|
17
|
+
from mock_vws.database import CloudDatabase
|
|
18
|
+
|
|
19
|
+
from .exceptions import RequestQuotaReachedError
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@beartype
|
|
23
|
+
def validate_request_quota(
|
|
24
|
+
*,
|
|
25
|
+
request_headers: Mapping[str, str],
|
|
26
|
+
request_body: bytes,
|
|
27
|
+
request_method: str,
|
|
28
|
+
request_path: str,
|
|
29
|
+
databases: Iterable[AnyDatabase],
|
|
30
|
+
) -> None:
|
|
31
|
+
"""Raise an error if the matching cloud database has no request
|
|
32
|
+
quota.
|
|
33
|
+
"""
|
|
34
|
+
database = get_database_matching_server_keys(
|
|
35
|
+
request_headers=request_headers,
|
|
36
|
+
request_body=request_body,
|
|
37
|
+
request_method=request_method,
|
|
38
|
+
request_path=request_path,
|
|
39
|
+
databases=databases,
|
|
40
|
+
)
|
|
41
|
+
if isinstance(database, CloudDatabase) and database.request_quota == 0:
|
|
42
|
+
raise RequestQuotaReachedError
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Validators for the VWS target quota."""
|
|
2
|
+
|
|
3
|
+
from collections.abc import Iterable, Mapping
|
|
4
|
+
from http import HTTPMethod
|
|
5
|
+
|
|
6
|
+
from beartype import beartype
|
|
7
|
+
|
|
8
|
+
from mock_vws._database_matchers import (
|
|
9
|
+
AnyDatabase,
|
|
10
|
+
get_database_matching_server_keys,
|
|
11
|
+
)
|
|
12
|
+
from mock_vws.database import CloudDatabase
|
|
13
|
+
|
|
14
|
+
from .exceptions import TargetQuotaReachedError
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@beartype
|
|
18
|
+
def validate_target_quota(
|
|
19
|
+
*,
|
|
20
|
+
request_headers: Mapping[str, str],
|
|
21
|
+
request_body: bytes,
|
|
22
|
+
request_method: str,
|
|
23
|
+
request_path: str,
|
|
24
|
+
databases: Iterable[AnyDatabase],
|
|
25
|
+
) -> None:
|
|
26
|
+
"""Raise an error when adding a target would exceed the quota."""
|
|
27
|
+
if request_method != HTTPMethod.POST or request_path != "/targets":
|
|
28
|
+
return
|
|
29
|
+
|
|
30
|
+
database = get_database_matching_server_keys(
|
|
31
|
+
request_headers=request_headers,
|
|
32
|
+
request_body=request_body,
|
|
33
|
+
request_method=request_method,
|
|
34
|
+
request_path=request_path,
|
|
35
|
+
databases=databases,
|
|
36
|
+
)
|
|
37
|
+
if (
|
|
38
|
+
isinstance(database, CloudDatabase)
|
|
39
|
+
and len(database.not_deleted_targets) >= database.target_quota
|
|
40
|
+
):
|
|
41
|
+
raise TargetQuotaReachedError
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""Validators for given target IDs."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from collections.abc import Iterable, Mapping
|
|
5
|
+
|
|
6
|
+
from beartype import beartype
|
|
7
|
+
|
|
8
|
+
from mock_vws._database_matchers import (
|
|
9
|
+
AnyDatabase,
|
|
10
|
+
get_database_matching_server_keys,
|
|
11
|
+
)
|
|
12
|
+
from mock_vws._services_validators.exceptions import UnknownTargetError
|
|
13
|
+
|
|
14
|
+
_LOGGER = logging.getLogger(name=__name__)
|
|
15
|
+
_TARGETS_WITH_INSTANCE_PATH_LENGTH = 4
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@beartype
|
|
19
|
+
def validate_target_id_exists(
|
|
20
|
+
*,
|
|
21
|
+
request_path: str,
|
|
22
|
+
request_headers: Mapping[str, str],
|
|
23
|
+
request_body: bytes,
|
|
24
|
+
request_method: str,
|
|
25
|
+
databases: Iterable[AnyDatabase],
|
|
26
|
+
) -> None:
|
|
27
|
+
"""Validate that if a target ID is given, it exists in the database
|
|
28
|
+
matching the request.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
request_path: The path of the request.
|
|
32
|
+
request_headers: The headers sent with the request.
|
|
33
|
+
request_body: The body of the request.
|
|
34
|
+
request_method: The HTTP method of the request.
|
|
35
|
+
databases: All Vuforia databases.
|
|
36
|
+
|
|
37
|
+
Raises:
|
|
38
|
+
UnknownTargetError: There are no matching targets for a given target
|
|
39
|
+
ID.
|
|
40
|
+
"""
|
|
41
|
+
split_path = request_path.split(sep="/")
|
|
42
|
+
|
|
43
|
+
request_path_no_target_id_length = 2
|
|
44
|
+
if len(split_path) == request_path_no_target_id_length:
|
|
45
|
+
return
|
|
46
|
+
|
|
47
|
+
target_id = split_path[-1]
|
|
48
|
+
if (
|
|
49
|
+
len(split_path) == _TARGETS_WITH_INSTANCE_PATH_LENGTH
|
|
50
|
+
and split_path[-3] == "targets"
|
|
51
|
+
and split_path[-1] == "instances"
|
|
52
|
+
):
|
|
53
|
+
target_id = split_path[-2]
|
|
54
|
+
database = get_database_matching_server_keys(
|
|
55
|
+
request_headers=request_headers,
|
|
56
|
+
request_body=request_body,
|
|
57
|
+
request_method=request_method,
|
|
58
|
+
request_path=request_path,
|
|
59
|
+
databases=databases,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
matching_targets = [
|
|
63
|
+
target
|
|
64
|
+
for target in database.not_deleted_targets
|
|
65
|
+
if target.target_id == target_id
|
|
66
|
+
]
|
|
67
|
+
if not matching_targets:
|
|
68
|
+
_LOGGER.warning('The target ID "%s" does not exist.', target_id)
|
|
69
|
+
raise UnknownTargetError
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Validators for the width field."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import logging
|
|
5
|
+
from http import HTTPStatus
|
|
6
|
+
|
|
7
|
+
from beartype import beartype
|
|
8
|
+
|
|
9
|
+
from mock_vws._services_validators.exceptions import FailError
|
|
10
|
+
|
|
11
|
+
_LOGGER = logging.getLogger(name=__name__)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@beartype
|
|
15
|
+
def validate_width(*, request_body: bytes) -> None:
|
|
16
|
+
"""Validate the width argument given to a VWS endpoint.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
request_body: The body of the request.
|
|
20
|
+
|
|
21
|
+
Raises:
|
|
22
|
+
FailError: Width is given and is not a positive number.
|
|
23
|
+
"""
|
|
24
|
+
if not request_body:
|
|
25
|
+
return
|
|
26
|
+
|
|
27
|
+
request_text = request_body.decode()
|
|
28
|
+
if "width" not in json.loads(s=request_text):
|
|
29
|
+
return
|
|
30
|
+
|
|
31
|
+
width = json.loads(s=request_text).get("width")
|
|
32
|
+
|
|
33
|
+
width_is_number = isinstance(width, int | float)
|
|
34
|
+
width_positive = width_is_number and width > 0
|
|
35
|
+
|
|
36
|
+
if not width_positive:
|
|
37
|
+
_LOGGER.warning(msg="Width is not a positive number.")
|
|
38
|
+
raise FailError(status_code=HTTPStatus.BAD_REQUEST)
|