robotframework-openapitools 0.3.0__py3-none-any.whl → 1.0.0b1__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.
- OpenApiDriver/__init__.py +44 -41
- OpenApiDriver/openapi_executors.py +48 -42
- OpenApiDriver/openapi_reader.py +115 -116
- OpenApiDriver/openapidriver.libspec +72 -62
- OpenApiDriver/openapidriver.py +25 -19
- OpenApiLibCore/__init__.py +13 -11
- OpenApiLibCore/annotations.py +3 -0
- OpenApiLibCore/data_generation/__init__.py +12 -0
- OpenApiLibCore/data_generation/body_data_generation.py +269 -0
- OpenApiLibCore/data_generation/data_generation_core.py +240 -0
- OpenApiLibCore/data_invalidation.py +281 -0
- OpenApiLibCore/dto_base.py +43 -40
- OpenApiLibCore/dto_utils.py +97 -85
- OpenApiLibCore/oas_cache.py +14 -13
- OpenApiLibCore/openapi_libcore.libspec +361 -188
- OpenApiLibCore/openapi_libcore.py +392 -1645
- OpenApiLibCore/parameter_utils.py +89 -0
- OpenApiLibCore/path_functions.py +215 -0
- OpenApiLibCore/path_invalidation.py +44 -0
- OpenApiLibCore/protocols.py +30 -0
- OpenApiLibCore/request_data.py +275 -0
- OpenApiLibCore/resource_relations.py +54 -0
- OpenApiLibCore/validation.py +497 -0
- OpenApiLibCore/value_utils.py +528 -481
- openapi_libgen/__init__.py +46 -0
- openapi_libgen/command_line.py +87 -0
- openapi_libgen/parsing_utils.py +26 -0
- openapi_libgen/spec_parser.py +212 -0
- openapi_libgen/templates/__init__.jinja +3 -0
- openapi_libgen/templates/library.jinja +30 -0
- robotframework_openapitools-1.0.0b1.dist-info/METADATA +237 -0
- robotframework_openapitools-1.0.0b1.dist-info/RECORD +37 -0
- {robotframework_openapitools-0.3.0.dist-info → robotframework_openapitools-1.0.0b1.dist-info}/WHEEL +1 -1
- robotframework_openapitools-1.0.0b1.dist-info/entry_points.txt +3 -0
- roboswag/__init__.py +0 -9
- roboswag/__main__.py +0 -3
- roboswag/auth.py +0 -44
- roboswag/cli.py +0 -80
- roboswag/core.py +0 -85
- roboswag/generate/__init__.py +0 -1
- roboswag/generate/generate.py +0 -121
- roboswag/generate/models/__init__.py +0 -0
- roboswag/generate/models/api.py +0 -219
- roboswag/generate/models/definition.py +0 -28
- roboswag/generate/models/endpoint.py +0 -68
- roboswag/generate/models/parameter.py +0 -25
- roboswag/generate/models/response.py +0 -8
- roboswag/generate/models/tag.py +0 -16
- roboswag/generate/models/utils.py +0 -60
- roboswag/generate/templates/api_init.jinja +0 -15
- roboswag/generate/templates/models.jinja +0 -7
- roboswag/generate/templates/paths.jinja +0 -68
- roboswag/logger.py +0 -33
- roboswag/validate/__init__.py +0 -6
- roboswag/validate/core.py +0 -3
- roboswag/validate/schema.py +0 -21
- roboswag/validate/text_response.py +0 -14
- robotframework_openapitools-0.3.0.dist-info/METADATA +0 -41
- robotframework_openapitools-0.3.0.dist-info/RECORD +0 -41
- {robotframework_openapitools-0.3.0.dist-info → robotframework_openapitools-1.0.0b1.dist-info}/LICENSE +0 -0
OpenApiDriver/__init__.py
CHANGED
@@ -1,41 +1,44 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
The
|
4
|
-
|
5
|
-
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
from
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
"
|
35
|
-
"
|
36
|
-
"
|
37
|
-
"
|
38
|
-
"
|
39
|
-
"
|
40
|
-
"
|
41
|
-
|
1
|
+
# pylint: disable=invalid-name
|
2
|
+
"""
|
3
|
+
The OpenApiDriver package is intended to be used as a Robot Framework library.
|
4
|
+
The following classes and constants are exposed to be used by the library user:
|
5
|
+
- OpenApiDriver: The class to be used as a Library in the *** Settings *** section
|
6
|
+
- IdDependency, IdReference, PathPropertiesConstraint, PropertyValueConstraint,
|
7
|
+
UniquePropertyValueConstraint: Classes to be subclassed by the library user
|
8
|
+
when implementing a custom mapping module (advanced use).
|
9
|
+
- Dto, Relation: Base classes that can be used for type annotations.
|
10
|
+
- IGNORE: A special constant that can be used as a value in the PropertyValueConstraint.
|
11
|
+
"""
|
12
|
+
|
13
|
+
from importlib.metadata import version
|
14
|
+
|
15
|
+
from OpenApiDriver.openapidriver import OpenApiDriver
|
16
|
+
from OpenApiLibCore.dto_base import (
|
17
|
+
Dto,
|
18
|
+
IdDependency,
|
19
|
+
IdReference,
|
20
|
+
PathPropertiesConstraint,
|
21
|
+
PropertyValueConstraint,
|
22
|
+
ResourceRelation,
|
23
|
+
UniquePropertyValueConstraint,
|
24
|
+
)
|
25
|
+
from OpenApiLibCore.validation import ValidationLevel
|
26
|
+
from OpenApiLibCore.value_utils import IGNORE
|
27
|
+
|
28
|
+
try:
|
29
|
+
__version__ = version("robotframework-openapidriver")
|
30
|
+
except Exception: # pragma: no cover pylint: disable=broad-exception-caught
|
31
|
+
pass
|
32
|
+
|
33
|
+
__all__ = [
|
34
|
+
"IGNORE",
|
35
|
+
"Dto",
|
36
|
+
"IdDependency",
|
37
|
+
"IdReference",
|
38
|
+
"OpenApiDriver",
|
39
|
+
"PathPropertiesConstraint",
|
40
|
+
"PropertyValueConstraint",
|
41
|
+
"ResourceRelation",
|
42
|
+
"UniquePropertyValueConstraint",
|
43
|
+
"ValidationLevel",
|
44
|
+
]
|
@@ -1,52 +1,53 @@
|
|
1
1
|
"""Module containing the classes to perform automatic OpenAPI contract validation."""
|
2
2
|
|
3
|
-
from
|
3
|
+
from collections.abc import Mapping, MutableMapping
|
4
|
+
from http import HTTPStatus
|
4
5
|
from pathlib import Path
|
5
6
|
from random import choice
|
6
|
-
from
|
7
|
+
from types import MappingProxyType
|
7
8
|
|
8
9
|
from requests import Response
|
9
10
|
from requests.auth import AuthBase
|
10
11
|
from requests.cookies import RequestsCookieJar as CookieJar
|
11
|
-
from robot.api import
|
12
|
+
from robot.api import logger
|
12
13
|
from robot.api.deco import keyword, library
|
14
|
+
from robot.api.exceptions import SkipExecution
|
13
15
|
from robot.libraries.BuiltIn import BuiltIn
|
14
16
|
|
15
17
|
from OpenApiLibCore import OpenApiLibCore, RequestData, RequestValues, ValidationLevel
|
18
|
+
from OpenApiLibCore.annotations import JSON
|
16
19
|
|
17
20
|
run_keyword = BuiltIn().run_keyword
|
18
|
-
|
19
|
-
|
20
|
-
logger = getLogger(__name__)
|
21
|
+
default_str_mapping: Mapping[str, str] = MappingProxyType({})
|
21
22
|
|
22
23
|
|
23
24
|
@library(scope="SUITE", doc_format="ROBOT")
|
24
|
-
class OpenApiExecutors(OpenApiLibCore):
|
25
|
+
class OpenApiExecutors(OpenApiLibCore):
|
25
26
|
"""Main class providing the keywords and core logic to perform endpoint validations."""
|
26
27
|
|
27
|
-
def __init__( # pylint: disable=
|
28
|
+
def __init__( # noqa: PLR0913, pylint: disable=dangerous-default-value
|
28
29
|
self,
|
29
30
|
source: str,
|
30
31
|
origin: str = "",
|
31
32
|
base_path: str = "",
|
32
33
|
response_validation: ValidationLevel = ValidationLevel.WARN,
|
33
34
|
disable_server_validation: bool = True,
|
34
|
-
mappings_path:
|
35
|
+
mappings_path: str | Path = "",
|
35
36
|
invalid_property_default_response: int = 422,
|
36
37
|
default_id_property_name: str = "id",
|
37
|
-
faker_locale:
|
38
|
+
faker_locale: str | list[str] = "",
|
38
39
|
require_body_for_invalid_url: bool = False,
|
39
40
|
recursion_limit: int = 1,
|
40
|
-
recursion_default:
|
41
|
+
recursion_default: JSON = {},
|
41
42
|
username: str = "",
|
42
43
|
password: str = "",
|
43
44
|
security_token: str = "",
|
44
|
-
auth:
|
45
|
-
cert:
|
46
|
-
verify_tls:
|
47
|
-
extra_headers:
|
48
|
-
cookies:
|
49
|
-
proxies:
|
45
|
+
auth: AuthBase | None = None,
|
46
|
+
cert: str | tuple[str, str] = "",
|
47
|
+
verify_tls: bool | str = True,
|
48
|
+
extra_headers: Mapping[str, str] = default_str_mapping,
|
49
|
+
cookies: MutableMapping[str, str] | CookieJar | None = None,
|
50
|
+
proxies: MutableMapping[str, str] | None = None,
|
50
51
|
) -> None:
|
51
52
|
super().__init__(
|
52
53
|
source=source,
|
@@ -84,13 +85,13 @@ class OpenApiExecutors(OpenApiLibCore): # pylint: disable=too-many-instance-att
|
|
84
85
|
> Note: No headers or (json) body are send with the request. For security
|
85
86
|
reasons, the authorization validation should be checked first.
|
86
87
|
"""
|
87
|
-
url: str = run_keyword("get_valid_url", path
|
88
|
+
url: str = run_keyword("get_valid_url", path)
|
88
89
|
response = self.session.request(
|
89
90
|
method=method,
|
90
91
|
url=url,
|
91
92
|
verify=False,
|
92
93
|
)
|
93
|
-
if response.status_code !=
|
94
|
+
if response.status_code != int(HTTPStatus.UNAUTHORIZED):
|
94
95
|
raise AssertionError(f"Response {response.status_code} was not 401.")
|
95
96
|
|
96
97
|
@keyword
|
@@ -105,9 +106,9 @@ class OpenApiExecutors(OpenApiLibCore): # pylint: disable=too-many-instance-att
|
|
105
106
|
> Note: No headers or (json) body are send with the request. For security
|
106
107
|
reasons, the access rights validation should be checked first.
|
107
108
|
"""
|
108
|
-
url: str = run_keyword("get_valid_url", path
|
109
|
+
url: str = run_keyword("get_valid_url", path)
|
109
110
|
response: Response = run_keyword("authorized_request", url, method)
|
110
|
-
if response.status_code !=
|
111
|
+
if response.status_code != int(HTTPStatus.FORBIDDEN):
|
111
112
|
raise AssertionError(f"Response {response.status_code} was not 403.")
|
112
113
|
|
113
114
|
@keyword
|
@@ -118,8 +119,9 @@ class OpenApiExecutors(OpenApiLibCore): # pylint: disable=too-many-instance-att
|
|
118
119
|
Perform a request for the provided 'path' and 'method' where the url for
|
119
120
|
the `path` is invalidated.
|
120
121
|
|
121
|
-
This keyword will be `SKIPPED` if the path contains no parts
|
122
|
-
can be invalidated
|
122
|
+
This keyword will be `SKIPPED` if the path contains no parts
|
123
|
+
that can be invalidated and there is no mapping for a
|
124
|
+
PathPropertiesConstraint for the `expected_status_code`.
|
123
125
|
|
124
126
|
The optional `expected_status_code` parameter (default: 404) can be set to the
|
125
127
|
expected status code for APIs that do not return a 404 on invalid urls.
|
@@ -129,9 +131,13 @@ class OpenApiExecutors(OpenApiLibCore): # pylint: disable=too-many-instance-att
|
|
129
131
|
parameters are send with the request. The `require_body_for_invalid_url`
|
130
132
|
parameter can be set to `True` if needed.
|
131
133
|
"""
|
132
|
-
valid_url: str = run_keyword("get_valid_url", path
|
134
|
+
valid_url: str = run_keyword("get_valid_url", path)
|
133
135
|
|
134
|
-
if not (
|
136
|
+
if not (
|
137
|
+
url := run_keyword(
|
138
|
+
"get_invalidated_url", valid_url, path, expected_status_code
|
139
|
+
)
|
140
|
+
):
|
135
141
|
raise SkipExecution(
|
136
142
|
f"Path {path} does not contain resource references that "
|
137
143
|
f"can be invalidated."
|
@@ -139,7 +145,7 @@ class OpenApiExecutors(OpenApiLibCore): # pylint: disable=too-many-instance-att
|
|
139
145
|
|
140
146
|
params, headers, json_data = None, None, None
|
141
147
|
if self.require_body_for_invalid_url:
|
142
|
-
request_data =
|
148
|
+
request_data: RequestData = run_keyword("get_request_data", path, method)
|
143
149
|
params = request_data.params
|
144
150
|
headers = request_data.headers
|
145
151
|
dto = request_data.dto
|
@@ -164,11 +170,11 @@ class OpenApiExecutors(OpenApiLibCore): # pylint: disable=too-many-instance-att
|
|
164
170
|
The keyword calls other keywords to generate the neccesary data to perform
|
165
171
|
the desired operation and validate the response against the openapi document.
|
166
172
|
"""
|
167
|
-
json_data:
|
168
|
-
original_data =
|
173
|
+
json_data: dict[str, JSON] = {}
|
174
|
+
original_data = {}
|
169
175
|
|
170
|
-
url: str = run_keyword("get_valid_url", path
|
171
|
-
request_data: RequestData =
|
176
|
+
url: str = run_keyword("get_valid_url", path)
|
177
|
+
request_data: RequestData = run_keyword("get_request_data", path, method)
|
172
178
|
params = request_data.params
|
173
179
|
headers = request_data.headers
|
174
180
|
if request_data.has_body:
|
@@ -177,7 +183,7 @@ class OpenApiExecutors(OpenApiLibCore): # pylint: disable=too-many-instance-att
|
|
177
183
|
if method == "PATCH":
|
178
184
|
original_data = self.get_original_data(url=url)
|
179
185
|
# in case of a status code indicating an error, ensure the error occurs
|
180
|
-
if status_code >=
|
186
|
+
if status_code >= int(HTTPStatus.BAD_REQUEST):
|
181
187
|
invalidation_keyword_data = {
|
182
188
|
"get_invalid_json_data": [
|
183
189
|
"get_invalid_json_data",
|
@@ -194,7 +200,7 @@ class OpenApiExecutors(OpenApiLibCore): # pylint: disable=too-many-instance-att
|
|
194
200
|
}
|
195
201
|
invalidation_keywords = []
|
196
202
|
|
197
|
-
if request_data.dto.
|
203
|
+
if request_data.dto.get_body_relations_for_error_code(status_code):
|
198
204
|
invalidation_keywords.append("get_invalid_json_data")
|
199
205
|
if request_data.dto.get_parameter_relations_for_error_code(status_code):
|
200
206
|
invalidation_keywords.append("get_invalidated_parameters")
|
@@ -248,20 +254,20 @@ class OpenApiExecutors(OpenApiLibCore): # pylint: disable=too-many-instance-att
|
|
248
254
|
),
|
249
255
|
original_data,
|
250
256
|
)
|
251
|
-
if status_code <
|
257
|
+
if status_code < int(HTTPStatus.MULTIPLE_CHOICES) and (
|
252
258
|
request_data.has_optional_properties
|
253
259
|
or request_data.has_optional_params
|
254
260
|
or request_data.has_optional_headers
|
255
261
|
):
|
256
262
|
logger.info("Performing request without optional properties and parameters")
|
257
|
-
url = run_keyword("get_valid_url", path
|
258
|
-
request_data =
|
263
|
+
url = run_keyword("get_valid_url", path)
|
264
|
+
request_data = run_keyword("get_request_data", path, method)
|
259
265
|
params = request_data.get_required_params()
|
260
266
|
headers = request_data.get_required_headers()
|
261
267
|
json_data = (
|
262
|
-
request_data.get_minimal_body_dict() if request_data.has_body else
|
268
|
+
request_data.get_minimal_body_dict() if request_data.has_body else {}
|
263
269
|
)
|
264
|
-
original_data =
|
270
|
+
original_data = {}
|
265
271
|
if method == "PATCH":
|
266
272
|
original_data = self.get_original_data(url=url)
|
267
273
|
run_keyword(
|
@@ -278,15 +284,15 @@ class OpenApiExecutors(OpenApiLibCore): # pylint: disable=too-many-instance-att
|
|
278
284
|
original_data,
|
279
285
|
)
|
280
286
|
|
281
|
-
def get_original_data(self, url: str) ->
|
287
|
+
def get_original_data(self, url: str) -> dict[str, JSON]:
|
282
288
|
"""
|
283
289
|
Attempt to GET the current data for the given url and return it.
|
284
290
|
|
285
|
-
If the GET request fails,
|
291
|
+
If the GET request fails, an empty dict is returned.
|
286
292
|
"""
|
287
|
-
original_data =
|
288
|
-
path = self.
|
289
|
-
get_request_data =
|
293
|
+
original_data = {}
|
294
|
+
path = self.get_parameterized_path_from_url(url)
|
295
|
+
get_request_data: RequestData = run_keyword("get_request_data", path, "GET")
|
290
296
|
get_params = get_request_data.params
|
291
297
|
get_headers = get_request_data.headers
|
292
298
|
response: Response = run_keyword(
|
OpenApiDriver/openapi_reader.py
CHANGED
@@ -1,116 +1,115 @@
|
|
1
|
-
"""Module holding the OpenApiReader reader_class implementation."""
|
2
|
-
|
3
|
-
from typing import Any
|
4
|
-
|
5
|
-
from DataDriver.AbstractReaderClass import AbstractReaderClass
|
6
|
-
from DataDriver.ReaderConfig import TestCaseData
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
self.
|
17
|
-
self.
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
self.
|
25
|
-
and self.
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
paths
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
#
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
#
|
57
|
-
|
58
|
-
|
59
|
-
response
|
60
|
-
or response in
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
"${
|
72
|
-
"${
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
return [*tags, f"Method: {method.upper()}", f"Response: {response}"]
|
1
|
+
"""Module holding the OpenApiReader reader_class implementation."""
|
2
|
+
|
3
|
+
from typing import Any
|
4
|
+
|
5
|
+
from DataDriver.AbstractReaderClass import AbstractReaderClass
|
6
|
+
from DataDriver.ReaderConfig import TestCaseData
|
7
|
+
|
8
|
+
|
9
|
+
class Test:
|
10
|
+
"""
|
11
|
+
Helper class to support ignoring endpoint responses when generating the test cases.
|
12
|
+
"""
|
13
|
+
|
14
|
+
def __init__(self, path: str, method: str, response: str | int) -> None:
|
15
|
+
self.path = path
|
16
|
+
self.method = method.lower()
|
17
|
+
self.response = str(response)
|
18
|
+
|
19
|
+
def __eq__(self, other: Any) -> bool:
|
20
|
+
if not isinstance(other, type(self)):
|
21
|
+
return False
|
22
|
+
return (
|
23
|
+
self.path == other.path
|
24
|
+
and self.method == other.method
|
25
|
+
and self.response == other.response
|
26
|
+
)
|
27
|
+
|
28
|
+
|
29
|
+
class OpenApiReader(AbstractReaderClass):
|
30
|
+
"""Implementation of the reader_class used by DataDriver."""
|
31
|
+
|
32
|
+
def get_data_from_source(self) -> list[TestCaseData]:
|
33
|
+
test_data: list[TestCaseData] = []
|
34
|
+
|
35
|
+
read_paths_method = getattr(self, "read_paths_method")
|
36
|
+
paths: dict[str, Any] = read_paths_method()
|
37
|
+
self._filter_paths(paths)
|
38
|
+
|
39
|
+
ignored_responses_ = [
|
40
|
+
str(response) for response in getattr(self, "ignored_responses", [])
|
41
|
+
]
|
42
|
+
|
43
|
+
ignored_tests = [Test(*test) for test in getattr(self, "ignored_testcases", [])]
|
44
|
+
|
45
|
+
for path, path_item in paths.items():
|
46
|
+
# by reseversing the items, post/put operations come before get and delete
|
47
|
+
for item_name, item_data in reversed(path_item.items()):
|
48
|
+
# this level of the OAS also contains data that's not related to a
|
49
|
+
# path operation
|
50
|
+
if item_name not in ["get", "put", "post", "delete", "patch"]:
|
51
|
+
continue
|
52
|
+
method, method_data = item_name, item_data
|
53
|
+
tags_from_spec = method_data.get("tags", [])
|
54
|
+
for response in method_data.get("responses"):
|
55
|
+
# 'default' applies to all status codes that are not specified, in
|
56
|
+
# which case we don't know what to expect and therefore can't verify
|
57
|
+
if (
|
58
|
+
response == "default"
|
59
|
+
or response in ignored_responses_
|
60
|
+
or Test(path, method, response) in ignored_tests
|
61
|
+
):
|
62
|
+
continue
|
63
|
+
|
64
|
+
tag_list = _get_tag_list(
|
65
|
+
tags=tags_from_spec, method=method, response=response
|
66
|
+
)
|
67
|
+
test_data.append(
|
68
|
+
TestCaseData(
|
69
|
+
arguments={
|
70
|
+
"${path}": path,
|
71
|
+
"${method}": method.upper(),
|
72
|
+
"${status_code}": response,
|
73
|
+
},
|
74
|
+
tags=tag_list,
|
75
|
+
),
|
76
|
+
)
|
77
|
+
return test_data
|
78
|
+
|
79
|
+
def _filter_paths(self, paths: dict[str, Any]) -> None:
|
80
|
+
def matches_include_pattern(path: str) -> bool:
|
81
|
+
for included_path in included_paths:
|
82
|
+
if path == included_path:
|
83
|
+
return True
|
84
|
+
if included_path.endswith("*"):
|
85
|
+
wildcard_include, _, _ = included_path.partition("*")
|
86
|
+
if path.startswith(wildcard_include):
|
87
|
+
return True
|
88
|
+
return False
|
89
|
+
|
90
|
+
def matches_ignore_pattern(path: str) -> bool:
|
91
|
+
for ignored_path in ignored_paths:
|
92
|
+
if path == ignored_path:
|
93
|
+
return True
|
94
|
+
|
95
|
+
if ignored_path.endswith("*"):
|
96
|
+
wildcard_ignore, _, _ = ignored_path.partition("*")
|
97
|
+
if path.startswith(wildcard_ignore):
|
98
|
+
return True
|
99
|
+
return False
|
100
|
+
|
101
|
+
if included_paths := getattr(self, "included_paths", ()):
|
102
|
+
path_list = list(paths.keys())
|
103
|
+
for path in path_list:
|
104
|
+
if not matches_include_pattern(path):
|
105
|
+
paths.pop(path)
|
106
|
+
|
107
|
+
if ignored_paths := getattr(self, "ignored_paths", ()):
|
108
|
+
path_list = list(paths.keys())
|
109
|
+
for path in path_list:
|
110
|
+
if matches_ignore_pattern(path):
|
111
|
+
paths.pop(path)
|
112
|
+
|
113
|
+
|
114
|
+
def _get_tag_list(tags: list[str], method: str, response: str) -> list[str]:
|
115
|
+
return [*tags, f"Method: {method.upper()}", f"Response: {response}"]
|