robotframework-openapitools 0.2.1__tar.gz → 0.2.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. {robotframework_openapitools-0.2.1 → robotframework_openapitools-0.2.2}/PKG-INFO +1 -1
  2. {robotframework_openapitools-0.2.1 → robotframework_openapitools-0.2.2}/pyproject.toml +1 -1
  3. robotframework_openapitools-0.2.2/src/OpenApiDriver/openapi_executors.py +297 -0
  4. {robotframework_openapitools-0.2.1 → robotframework_openapitools-0.2.2}/src/OpenApiDriver/openapidriver.libspec +6 -6
  5. {robotframework_openapitools-0.2.1 → robotframework_openapitools-0.2.2}/src/OpenApiLibCore/__init__.py +54 -48
  6. {robotframework_openapitools-0.2.1 → robotframework_openapitools-0.2.2}/src/OpenApiLibCore/dto_base.py +3 -3
  7. {robotframework_openapitools-0.2.1 → robotframework_openapitools-0.2.2}/src/OpenApiLibCore/openapi_libcore.libspec +111 -16
  8. {robotframework_openapitools-0.2.1 → robotframework_openapitools-0.2.2}/src/OpenApiLibCore/openapi_libcore.py +472 -2
  9. robotframework_openapitools-0.2.1/src/OpenApiDriver/openapi_executors.py +0 -764
  10. {robotframework_openapitools-0.2.1 → robotframework_openapitools-0.2.2}/LICENSE +0 -0
  11. {robotframework_openapitools-0.2.1 → robotframework_openapitools-0.2.2}/docs/README.md +0 -0
  12. {robotframework_openapitools-0.2.1 → robotframework_openapitools-0.2.2}/src/OpenApiDriver/__init__.py +0 -0
  13. {robotframework_openapitools-0.2.1 → robotframework_openapitools-0.2.2}/src/OpenApiDriver/openapi_reader.py +0 -0
  14. {robotframework_openapitools-0.2.1 → robotframework_openapitools-0.2.2}/src/OpenApiDriver/openapidriver.py +0 -0
  15. {robotframework_openapitools-0.2.1 → robotframework_openapitools-0.2.2}/src/OpenApiDriver/py.typed +0 -0
  16. {robotframework_openapitools-0.2.1 → robotframework_openapitools-0.2.2}/src/OpenApiLibCore/dto_utils.py +0 -0
  17. {robotframework_openapitools-0.2.1 → robotframework_openapitools-0.2.2}/src/OpenApiLibCore/oas_cache.py +0 -0
  18. {robotframework_openapitools-0.2.1 → robotframework_openapitools-0.2.2}/src/OpenApiLibCore/py.typed +0 -0
  19. {robotframework_openapitools-0.2.1 → robotframework_openapitools-0.2.2}/src/OpenApiLibCore/value_utils.py +0 -0
  20. {robotframework_openapitools-0.2.1 → robotframework_openapitools-0.2.2}/src/roboswag/__init__.py +0 -0
  21. {robotframework_openapitools-0.2.1 → robotframework_openapitools-0.2.2}/src/roboswag/__main__.py +0 -0
  22. {robotframework_openapitools-0.2.1 → robotframework_openapitools-0.2.2}/src/roboswag/auth.py +0 -0
  23. {robotframework_openapitools-0.2.1 → robotframework_openapitools-0.2.2}/src/roboswag/cli.py +0 -0
  24. {robotframework_openapitools-0.2.1 → robotframework_openapitools-0.2.2}/src/roboswag/core.py +0 -0
  25. {robotframework_openapitools-0.2.1 → robotframework_openapitools-0.2.2}/src/roboswag/generate/__init__.py +0 -0
  26. {robotframework_openapitools-0.2.1 → robotframework_openapitools-0.2.2}/src/roboswag/generate/generate.py +0 -0
  27. {robotframework_openapitools-0.2.1 → robotframework_openapitools-0.2.2}/src/roboswag/generate/models/__init__.py +0 -0
  28. {robotframework_openapitools-0.2.1 → robotframework_openapitools-0.2.2}/src/roboswag/generate/models/api.py +0 -0
  29. {robotframework_openapitools-0.2.1 → robotframework_openapitools-0.2.2}/src/roboswag/generate/models/definition.py +0 -0
  30. {robotframework_openapitools-0.2.1 → robotframework_openapitools-0.2.2}/src/roboswag/generate/models/endpoint.py +0 -0
  31. {robotframework_openapitools-0.2.1 → robotframework_openapitools-0.2.2}/src/roboswag/generate/models/parameter.py +0 -0
  32. {robotframework_openapitools-0.2.1 → robotframework_openapitools-0.2.2}/src/roboswag/generate/models/response.py +0 -0
  33. {robotframework_openapitools-0.2.1 → robotframework_openapitools-0.2.2}/src/roboswag/generate/models/tag.py +0 -0
  34. {robotframework_openapitools-0.2.1 → robotframework_openapitools-0.2.2}/src/roboswag/generate/models/utils.py +0 -0
  35. {robotframework_openapitools-0.2.1 → robotframework_openapitools-0.2.2}/src/roboswag/generate/templates/api_init.jinja +0 -0
  36. {robotframework_openapitools-0.2.1 → robotframework_openapitools-0.2.2}/src/roboswag/generate/templates/models.jinja +0 -0
  37. {robotframework_openapitools-0.2.1 → robotframework_openapitools-0.2.2}/src/roboswag/generate/templates/paths.jinja +0 -0
  38. {robotframework_openapitools-0.2.1 → robotframework_openapitools-0.2.2}/src/roboswag/logger.py +0 -0
  39. {robotframework_openapitools-0.2.1 → robotframework_openapitools-0.2.2}/src/roboswag/validate/__init__.py +0 -0
  40. {robotframework_openapitools-0.2.1 → robotframework_openapitools-0.2.2}/src/roboswag/validate/core.py +0 -0
  41. {robotframework_openapitools-0.2.1 → robotframework_openapitools-0.2.2}/src/roboswag/validate/schema.py +0 -0
  42. {robotframework_openapitools-0.2.1 → robotframework_openapitools-0.2.2}/src/roboswag/validate/text_response.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: robotframework-openapitools
3
- Version: 0.2.1
3
+ Version: 0.2.2
4
4
  Summary: A set of Robot Framework libraries to test APIs for which the OAS is available.
5
5
  Home-page: https://github.com/MarketSquare/robotframework-openapitools
6
6
  License: Apache-2.0
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name="robotframework-openapitools"
3
- version = "0.2.1"
3
+ version = "0.2.2"
4
4
  description = "A set of Robot Framework libraries to test APIs for which the OAS is available."
5
5
  license = "Apache-2.0"
6
6
  authors = [
@@ -0,0 +1,297 @@
1
+ """Module containing the classes to perform automatic OpenAPI contract validation."""
2
+
3
+ from logging import getLogger
4
+ from pathlib import Path
5
+ from random import choice
6
+ from typing import Any, Dict, List, Optional, Tuple, Union
7
+
8
+ from requests import Response
9
+ from requests.auth import AuthBase
10
+ from requests.cookies import RequestsCookieJar as CookieJar
11
+ from robot.api import SkipExecution
12
+ from robot.api.deco import keyword, library
13
+ from robot.libraries.BuiltIn import BuiltIn
14
+
15
+ from OpenApiLibCore import OpenApiLibCore, RequestData, RequestValues, ValidationLevel
16
+
17
+ run_keyword = BuiltIn().run_keyword
18
+
19
+
20
+ logger = getLogger(__name__)
21
+
22
+
23
+ @library(scope="TEST SUITE", doc_format="ROBOT")
24
+ class OpenApiExecutors(OpenApiLibCore): # pylint: disable=too-many-instance-attributes
25
+ """Main class providing the keywords and core logic to perform endpoint validations."""
26
+
27
+ def __init__( # pylint: disable=too-many-arguments
28
+ self,
29
+ source: str,
30
+ origin: str = "",
31
+ base_path: str = "",
32
+ response_validation: ValidationLevel = ValidationLevel.WARN,
33
+ disable_server_validation: bool = True,
34
+ mappings_path: Union[str, Path] = "",
35
+ invalid_property_default_response: int = 422,
36
+ default_id_property_name: str = "id",
37
+ faker_locale: Optional[Union[str, List[str]]] = None,
38
+ require_body_for_invalid_url: bool = False,
39
+ recursion_limit: int = 1,
40
+ recursion_default: Any = {},
41
+ username: str = "",
42
+ password: str = "",
43
+ security_token: str = "",
44
+ auth: Optional[AuthBase] = None,
45
+ cert: Optional[Union[str, Tuple[str, str]]] = None,
46
+ verify_tls: Optional[Union[bool, str]] = True,
47
+ extra_headers: Optional[Dict[str, str]] = None,
48
+ cookies: Optional[Union[Dict[str, str], CookieJar]] = None,
49
+ proxies: Optional[Dict[str, str]] = None,
50
+ ) -> None:
51
+ super().__init__(
52
+ source=source,
53
+ origin=origin,
54
+ base_path=base_path,
55
+ mappings_path=mappings_path,
56
+ default_id_property_name=default_id_property_name,
57
+ faker_locale=faker_locale,
58
+ recursion_limit=recursion_limit,
59
+ recursion_default=recursion_default,
60
+ username=username,
61
+ password=password,
62
+ security_token=security_token,
63
+ auth=auth,
64
+ cert=cert,
65
+ verify_tls=verify_tls,
66
+ extra_headers=extra_headers,
67
+ cookies=cookies,
68
+ proxies=proxies,
69
+ )
70
+ self.response_validation = response_validation
71
+ self.disable_server_validation = disable_server_validation
72
+ self.require_body_for_invalid_url = require_body_for_invalid_url
73
+ self.invalid_property_default_response = invalid_property_default_response
74
+
75
+ @keyword
76
+ def test_unauthorized(self, path: str, method: str) -> None:
77
+ """
78
+ Perform a request for `method` on the `path`, with no authorization.
79
+
80
+ This keyword only passes if the response code is 401: Unauthorized.
81
+
82
+ Any authorization parameters used to initialize the library are
83
+ ignored for this request.
84
+ > Note: No headers or (json) body are send with the request. For security
85
+ reasons, the authorization validation should be checked first.
86
+ """
87
+ url: str = run_keyword("get_valid_url", path, method)
88
+ response = self.session.request(
89
+ method=method,
90
+ url=url,
91
+ verify=False,
92
+ )
93
+ if response.status_code != 401:
94
+ raise AssertionError(f"Response {response.status_code} was not 401.")
95
+
96
+ @keyword
97
+ def test_forbidden(self, path: str, method: str) -> None:
98
+ """
99
+ Perform a request for `method` on the `path`, with the provided authorization.
100
+
101
+ This keyword only passes if the response code is 403: Forbidden.
102
+
103
+ For this keyword to pass, the authorization parameters used to initialize the
104
+ library should grant insufficient access rights to the target endpoint.
105
+ > Note: No headers or (json) body are send with the request. For security
106
+ reasons, the access rights validation should be checked first.
107
+ """
108
+ url: str = run_keyword("get_valid_url", path, method)
109
+ response: Response = run_keyword("authorized_request", url, method)
110
+ if response.status_code != 403:
111
+ raise AssertionError(f"Response {response.status_code} was not 403.")
112
+
113
+ @keyword
114
+ def test_invalid_url(
115
+ self, path: str, method: str, expected_status_code: int = 404
116
+ ) -> None:
117
+ """
118
+ Perform a request for the provided 'path' and 'method' where the url for
119
+ the `path` is invalidated.
120
+
121
+ This keyword will be `SKIPPED` if the path contains no parts that
122
+ can be invalidated.
123
+
124
+ The optional `expected_status_code` parameter (default: 404) can be set to the
125
+ expected status code for APIs that do not return a 404 on invalid urls.
126
+
127
+ > Note: Depending on API design, the url may be validated before or after
128
+ validation of headers, query parameters and / or (json) body. By default, no
129
+ parameters are send with the request. The `require_body_for_invalid_url`
130
+ parameter can be set to `True` if needed.
131
+ """
132
+ valid_url: str = run_keyword("get_valid_url", path, method)
133
+
134
+ if not (url := run_keyword("get_invalidated_url", valid_url)):
135
+ raise SkipExecution(
136
+ f"Path {path} does not contain resource references that "
137
+ f"can be invalidated."
138
+ )
139
+
140
+ params, headers, json_data = None, None, None
141
+ if self.require_body_for_invalid_url:
142
+ request_data = self.get_request_data(method=method, endpoint=path)
143
+ params = request_data.params
144
+ headers = request_data.headers
145
+ dto = request_data.dto
146
+ json_data = dto.as_dict()
147
+ response: Response = run_keyword(
148
+ "authorized_request", url, method, params, headers, json_data
149
+ )
150
+ if response.status_code != expected_status_code:
151
+ raise AssertionError(
152
+ f"Response {response.status_code} was not {expected_status_code}"
153
+ )
154
+
155
+ @keyword
156
+ def test_endpoint(self, path: str, method: str, status_code: int) -> None:
157
+ """
158
+ Validate that performing the `method` operation on `path` results in a
159
+ `status_code` response.
160
+
161
+ This is the main keyword to be used in the `Test Template` keyword when using
162
+ the OpenApiDriver.
163
+
164
+ The keyword calls other keywords to generate the neccesary data to perform
165
+ the desired operation and validate the response against the openapi document.
166
+ """
167
+ json_data: Optional[Dict[str, Any]] = None
168
+ original_data = None
169
+
170
+ url: str = run_keyword("get_valid_url", path, method)
171
+ request_data: RequestData = self.get_request_data(method=method, endpoint=path)
172
+ params = request_data.params
173
+ headers = request_data.headers
174
+ if request_data.has_body:
175
+ json_data = request_data.dto.as_dict()
176
+ # when patching, get the original data to check only patched data has changed
177
+ if method == "PATCH":
178
+ original_data = self.get_original_data(url=url)
179
+ # in case of a status code indicating an error, ensure the error occurs
180
+ if status_code >= 400:
181
+ invalidation_keyword_data = {
182
+ "get_invalid_json_data": [
183
+ "get_invalid_json_data",
184
+ url,
185
+ method,
186
+ status_code,
187
+ request_data,
188
+ ],
189
+ "get_invalidated_parameters": [
190
+ "get_invalidated_parameters",
191
+ status_code,
192
+ request_data,
193
+ ],
194
+ }
195
+ invalidation_keywords = []
196
+
197
+ if request_data.dto.get_relations_for_error_code(status_code):
198
+ invalidation_keywords.append("get_invalid_json_data")
199
+ if request_data.dto.get_parameter_relations_for_error_code(status_code):
200
+ invalidation_keywords.append("get_invalidated_parameters")
201
+ if invalidation_keywords:
202
+ if (
203
+ invalidation_keyword := choice(invalidation_keywords)
204
+ ) == "get_invalid_json_data":
205
+ json_data = run_keyword(
206
+ *invalidation_keyword_data[invalidation_keyword]
207
+ )
208
+ else:
209
+ params, headers = run_keyword(
210
+ *invalidation_keyword_data[invalidation_keyword]
211
+ )
212
+ # if there are no relations to invalide and the status_code is the default
213
+ # response_code for invalid properties, invalidate properties instead
214
+ elif status_code == self.invalid_property_default_response:
215
+ if (
216
+ request_data.params_that_can_be_invalidated
217
+ or request_data.headers_that_can_be_invalidated
218
+ ):
219
+ params, headers = run_keyword(
220
+ *invalidation_keyword_data["get_invalidated_parameters"]
221
+ )
222
+ if request_data.dto_schema:
223
+ json_data = run_keyword(
224
+ *invalidation_keyword_data["get_invalid_json_data"]
225
+ )
226
+ elif request_data.dto_schema:
227
+ json_data = run_keyword(
228
+ *invalidation_keyword_data["get_invalid_json_data"]
229
+ )
230
+ else:
231
+ raise SkipExecution(
232
+ "No properties or parameters can be invalidated."
233
+ )
234
+ else:
235
+ raise AssertionError(
236
+ f"No Dto mapping found to cause status_code {status_code}."
237
+ )
238
+ run_keyword(
239
+ "perform_validated_request",
240
+ path,
241
+ status_code,
242
+ RequestValues(
243
+ url=url,
244
+ method=method,
245
+ params=params,
246
+ headers=headers,
247
+ json_data=json_data,
248
+ ),
249
+ original_data,
250
+ )
251
+ if status_code < 300 and (
252
+ request_data.has_optional_properties
253
+ or request_data.has_optional_params
254
+ or request_data.has_optional_headers
255
+ ):
256
+ logger.info("Performing request without optional properties and parameters")
257
+ url = run_keyword("get_valid_url", path, method)
258
+ request_data = self.get_request_data(method=method, endpoint=path)
259
+ params = request_data.get_required_params()
260
+ headers = request_data.get_required_headers()
261
+ json_data = (
262
+ request_data.get_minimal_body_dict() if request_data.has_body else None
263
+ )
264
+ original_data = None
265
+ if method == "PATCH":
266
+ original_data = self.get_original_data(url=url)
267
+ run_keyword(
268
+ "perform_validated_request",
269
+ path,
270
+ status_code,
271
+ RequestValues(
272
+ url=url,
273
+ method=method,
274
+ params=params,
275
+ headers=headers,
276
+ json_data=json_data,
277
+ ),
278
+ original_data,
279
+ )
280
+
281
+ def get_original_data(self, url: str) -> Optional[Dict[str, Any]]:
282
+ """
283
+ Attempt to GET the current data for the given url and return it.
284
+
285
+ If the GET request fails, None is returned.
286
+ """
287
+ original_data = None
288
+ path = self.get_parameterized_endpoint_from_url(url)
289
+ get_request_data = self.get_request_data(endpoint=path, method="GET")
290
+ get_params = get_request_data.params
291
+ get_headers = get_request_data.headers
292
+ response: Response = run_keyword(
293
+ "authorized_request", url, "GET", get_params, get_headers
294
+ )
295
+ if response.ok:
296
+ original_data = response.json()
297
+ return original_data
@@ -1,6 +1,6 @@
1
1
  <?xml version="1.0" encoding="UTF-8"?>
2
- <keywordspec name="OpenApiDriver" type="LIBRARY" format="HTML" scope="SUITE" generated="2024-04-18T10:17:57+00:00" specversion="5" source="/workspaces/robotframework-openapitools/src/OpenApiDriver/openapidriver.py" lineno="352">
3
- <version>0.2.1</version>
2
+ <keywordspec name="OpenApiDriver" type="LIBRARY" format="HTML" scope="SUITE" generated="2024-05-15T18:47:16+00:00" specversion="5" source="/workspaces/robotframework-openapitools/src/OpenApiDriver/openapidriver.py" lineno="352">
3
+ <version>0.2.2</version>
4
4
  <doc>&lt;p&gt;Visit the &lt;a href="https://github.com/MarketSquare/robotframework-openapidriver"&gt;library page&lt;/a&gt; for an introduction and examples.&lt;/p&gt;</doc>
5
5
  <tags>
6
6
  </tags>
@@ -199,7 +199,7 @@
199
199
  </init>
200
200
  </inits>
201
201
  <keywords>
202
- <kw name="Test Endpoint" source="/workspaces/robotframework-openapitools/src/OpenApiDriver/openapi_executors.py" lineno="175">
202
+ <kw name="Test Endpoint" source="/workspaces/robotframework-openapitools/src/OpenApiDriver/openapi_executors.py" lineno="156">
203
203
  <arguments repr="path: str, method: str, status_code: int">
204
204
  <arg kind="POSITIONAL_OR_NAMED" required="true" repr="path: str">
205
205
  <name>path</name>
@@ -219,7 +219,7 @@
219
219
  &lt;p&gt;The keyword calls other keywords to generate the neccesary data to perform the desired operation and validate the response against the openapi document.&lt;/p&gt;</doc>
220
220
  <shortdoc>Validate that performing the `method` operation on `path` results in a `status_code` response.</shortdoc>
221
221
  </kw>
222
- <kw name="Test Forbidden" source="/workspaces/robotframework-openapitools/src/OpenApiDriver/openapi_executors.py" lineno="116">
222
+ <kw name="Test Forbidden" source="/workspaces/robotframework-openapitools/src/OpenApiDriver/openapi_executors.py" lineno="97">
223
223
  <arguments repr="path: str, method: str">
224
224
  <arg kind="POSITIONAL_OR_NAMED" required="true" repr="path: str">
225
225
  <name>path</name>
@@ -235,7 +235,7 @@
235
235
  &lt;p&gt;For this keyword to pass, the authorization parameters used to initialize the library should grant insufficient access rights to the target endpoint. &amp;gt; Note: No headers or (json) body are send with the request. For security reasons, the access rights validation should be checked first.&lt;/p&gt;</doc>
236
236
  <shortdoc>Perform a request for `method` on the `path`, with the provided authorization.</shortdoc>
237
237
  </kw>
238
- <kw name="Test Invalid Url" source="/workspaces/robotframework-openapitools/src/OpenApiDriver/openapi_executors.py" lineno="133">
238
+ <kw name="Test Invalid Url" source="/workspaces/robotframework-openapitools/src/OpenApiDriver/openapi_executors.py" lineno="114">
239
239
  <arguments repr="path: str, method: str, expected_status_code: int = 404">
240
240
  <arg kind="POSITIONAL_OR_NAMED" required="true" repr="path: str">
241
241
  <name>path</name>
@@ -257,7 +257,7 @@
257
257
  &lt;p&gt;&amp;gt; Note: Depending on API design, the url may be validated before or after validation of headers, query parameters and / or (json) body. By default, no parameters are send with the request. The &lt;span class="name"&gt;require_body_for_invalid_url&lt;/span&gt; parameter can be set to &lt;span class="name"&gt;True&lt;/span&gt; if needed.&lt;/p&gt;</doc>
258
258
  <shortdoc>Perform a request for the provided 'path' and 'method' where the url for the `path` is invalidated.</shortdoc>
259
259
  </kw>
260
- <kw name="Test Unauthorized" source="/workspaces/robotframework-openapitools/src/OpenApiDriver/openapi_executors.py" lineno="95">
260
+ <kw name="Test Unauthorized" source="/workspaces/robotframework-openapitools/src/OpenApiDriver/openapi_executors.py" lineno="76">
261
261
  <arguments repr="path: str, method: str">
262
262
  <arg kind="POSITIONAL_OR_NAMED" required="true" repr="path: str">
263
263
  <name>path</name>
@@ -1,48 +1,54 @@
1
- """
2
- The OpenApiLibCore package is intended to be used as a dependency for other
3
- Robot Framework libraries that facilitate the testing of OpenAPI / Swagger APIs.
4
- The following classes and constants are exposed to be used by the library user:
5
- - OpenApiLibCore: The class to be imported in the Robot Framework library.
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 OpenApiLibCore.dto_base import (
16
- Dto,
17
- IdDependency,
18
- IdReference,
19
- PathPropertiesConstraint,
20
- PropertyValueConstraint,
21
- Relation,
22
- UniquePropertyValueConstraint,
23
- resolve_schema,
24
- )
25
- from OpenApiLibCore.dto_utils import DefaultDto
26
- from OpenApiLibCore.openapi_libcore import OpenApiLibCore, RequestData, RequestValues
27
- from OpenApiLibCore.value_utils import IGNORE
28
-
29
- try:
30
- __version__ = version("robotframework-openapi-libcore")
31
- except Exception: # pragma: no cover
32
- pass
33
-
34
- __all__ = [
35
- "Dto",
36
- "IdDependency",
37
- "IdReference",
38
- "PathPropertiesConstraint",
39
- "PropertyValueConstraint",
40
- "Relation",
41
- "UniquePropertyValueConstraint",
42
- "DefaultDto",
43
- "OpenApiLibCore",
44
- "RequestData",
45
- "RequestValues",
46
- "resolve_schema",
47
- "IGNORE",
48
- ]
1
+ """
2
+ The OpenApiLibCore package is intended to be used as a dependency for other
3
+ Robot Framework libraries that facilitate the testing of OpenAPI / Swagger APIs.
4
+ The following classes and constants are exposed to be used by the library user:
5
+ - OpenApiLibCore: The class to be imported in the Robot Framework library.
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 OpenApiLibCore.dto_base import (
16
+ Dto,
17
+ IdDependency,
18
+ IdReference,
19
+ PathPropertiesConstraint,
20
+ PropertyValueConstraint,
21
+ Relation,
22
+ UniquePropertyValueConstraint,
23
+ resolve_schema,
24
+ )
25
+ from OpenApiLibCore.dto_utils import DefaultDto
26
+ from OpenApiLibCore.openapi_libcore import (
27
+ OpenApiLibCore,
28
+ RequestData,
29
+ RequestValues,
30
+ ValidationLevel,
31
+ )
32
+ from OpenApiLibCore.value_utils import IGNORE
33
+
34
+ try:
35
+ __version__ = version("robotframework-openapi-libcore")
36
+ except Exception: # pragma: no cover
37
+ pass
38
+
39
+ __all__ = [
40
+ "Dto",
41
+ "IdDependency",
42
+ "IdReference",
43
+ "PathPropertiesConstraint",
44
+ "PropertyValueConstraint",
45
+ "Relation",
46
+ "UniquePropertyValueConstraint",
47
+ "DefaultDto",
48
+ "OpenApiLibCore",
49
+ "RequestData",
50
+ "RequestValues",
51
+ "ValidationLevel",
52
+ "resolve_schema",
53
+ "IGNORE",
54
+ ]
@@ -77,9 +77,9 @@ def merge_schemas(first: Dict[str, Any], second: Dict[str, Any]) -> Dict[str, An
77
77
  # if the key holds a list, extend the values (e.g. 'required')
78
78
  merged_schema[key].extend(value)
79
79
  elif value != merged_schema[key]:
80
- logger.warning(
81
- f"key '{key}' with value '{merged_schema[key]}' not "
82
- f"updated to '{value}'"
80
+ logger.debug(
81
+ f"key '{key}' with value '{merged_schema[key]}'"
82
+ f" not updated to '{value}'"
83
83
  )
84
84
  else:
85
85
  merged_schema[key] = value