robotframework-openapitools 1.0.0b4__py3-none-any.whl → 1.0.1__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 +1 -0
- OpenApiDriver/openapi_executors.py +32 -4
- OpenApiDriver/openapidriver.libspec +183 -80
- OpenApiDriver/openapidriver.py +8 -279
- OpenApiLibCore/__init__.py +26 -0
- OpenApiLibCore/data_generation/__init__.py +0 -0
- OpenApiLibCore/data_generation/body_data_generation.py +4 -4
- OpenApiLibCore/data_generation/data_generation_core.py +18 -45
- OpenApiLibCore/data_invalidation.py +1 -5
- OpenApiLibCore/dto_base.py +31 -22
- OpenApiLibCore/dto_utils.py +31 -3
- OpenApiLibCore/localized_faker.py +0 -0
- OpenApiLibCore/models.py +26 -18
- OpenApiLibCore/openapi_libcore.libspec +229 -121
- OpenApiLibCore/openapi_libcore.py +45 -274
- OpenApiLibCore/parameter_utils.py +3 -3
- OpenApiLibCore/path_functions.py +5 -6
- OpenApiLibCore/path_invalidation.py +5 -7
- OpenApiLibCore/protocols.py +6 -0
- OpenApiLibCore/request_data.py +0 -0
- OpenApiLibCore/resource_relations.py +4 -2
- OpenApiLibCore/validation.py +4 -9
- OpenApiLibCore/value_utils.py +1 -1
- openapi_libgen/__init__.py +3 -0
- openapi_libgen/generator.py +2 -4
- openapi_libgen/parsing_utils.py +9 -5
- openapi_libgen/spec_parser.py +4 -4
- openapitools_docs/__init__.py +0 -0
- openapitools_docs/docstrings.py +891 -0
- openapitools_docs/documentation_generator.py +35 -0
- openapitools_docs/templates/documentation.jinja +209 -0
- {robotframework_openapitools-1.0.0b4.dist-info → robotframework_openapitools-1.0.1.dist-info}/METADATA +16 -98
- robotframework_openapitools-1.0.1.dist-info/RECORD +44 -0
- robotframework_openapitools-1.0.0b4.dist-info/RECORD +0 -40
- {robotframework_openapitools-1.0.0b4.dist-info → robotframework_openapitools-1.0.1.dist-info}/LICENSE +0 -0
- {robotframework_openapitools-1.0.0b4.dist-info → robotframework_openapitools-1.0.1.dist-info}/WHEEL +0 -0
- {robotframework_openapitools-1.0.0b4.dist-info → robotframework_openapitools-1.0.1.dist-info}/entry_points.txt +0 -0
@@ -1,123 +1,3 @@
|
|
1
|
-
"""
|
2
|
-
# OpenApiLibCore for Robot Framework
|
3
|
-
|
4
|
-
The OpenApiLibCore library is a utility library that is meant to simplify creation
|
5
|
-
of other Robot Framework libraries for API testing based on the information in
|
6
|
-
an OpenAPI document (also known as Swagger document).
|
7
|
-
This document explains how to use the OpenApiLibCore library.
|
8
|
-
|
9
|
-
My RoboCon 2022 talk about OpenApiDriver and OpenApiLibCore can be found
|
10
|
-
[here](https://www.youtube.com/watch?v=7YWZEHxk9Ps)
|
11
|
-
|
12
|
-
For more information about Robot Framework, see http://robotframework.org.
|
13
|
-
|
14
|
-
---
|
15
|
-
|
16
|
-
> Note: OpenApiLibCore is still being developed so there are currently
|
17
|
-
restrictions / limitations that you may encounter when using this library to run
|
18
|
-
tests against an API. See [Limitations](#limitations) for details.
|
19
|
-
|
20
|
-
---
|
21
|
-
|
22
|
-
## Installation
|
23
|
-
|
24
|
-
If you already have Python >= 3.8 with pip installed, you can simply run:
|
25
|
-
|
26
|
-
`pip install --upgrade robotframework-openapi-libcore`
|
27
|
-
|
28
|
-
---
|
29
|
-
|
30
|
-
## OpenAPI (aka Swagger)
|
31
|
-
|
32
|
-
The OpenAPI Specification (OAS) defines a standard, language-agnostic interface
|
33
|
-
to RESTful APIs, see https://swagger.io/specification/
|
34
|
-
|
35
|
-
The OpenApiLibCore implements a number of Robot Framework keywords that make it
|
36
|
-
easy to interact with an OpenAPI implementation by using the information in the
|
37
|
-
openapi document (Swagger file), for examply by automatic generation of valid values
|
38
|
-
for requests based on the schema information in the document.
|
39
|
-
|
40
|
-
> Note: OpenApiLibCore is designed for APIs based on the OAS v3
|
41
|
-
The library has not been tested for APIs based on the OAS v2.
|
42
|
-
|
43
|
-
---
|
44
|
-
|
45
|
-
## Getting started
|
46
|
-
|
47
|
-
Before trying to use the keywords exposed by OpenApiLibCore on the target API
|
48
|
-
it's recommended to first ensure that the openapi document for the API is valid
|
49
|
-
under the OpenAPI Specification.
|
50
|
-
|
51
|
-
This can be done using the command line interface of a package that is installed as
|
52
|
-
a prerequisite for OpenApiLibCore.
|
53
|
-
Both a local openapi.json or openapi.yaml file or one hosted by the API server
|
54
|
-
can be checked using the `prance validate <reference_to_file>` shell command:
|
55
|
-
|
56
|
-
```shell
|
57
|
-
prance validate --backend=openapi-spec-validator http://localhost:8000/openapi.json
|
58
|
-
Processing "http://localhost:8000/openapi.json"...
|
59
|
-
-> Resolving external references.
|
60
|
-
Validates OK as OpenAPI 3.0.2!
|
61
|
-
|
62
|
-
prance validate --backend=openapi-spec-validator /tests/files/petstore_openapi.yaml
|
63
|
-
Processing "/tests/files/petstore_openapi.yaml"...
|
64
|
-
-> Resolving external references.
|
65
|
-
Validates OK as OpenAPI 3.0.2!
|
66
|
-
```
|
67
|
-
|
68
|
-
You'll have to change the url or file reference to the location of the openapi
|
69
|
-
document for your API.
|
70
|
-
|
71
|
-
> Note: Although recursion is technically allowed under the OAS, tool support is limited
|
72
|
-
and changing the OAS to not use recursion is recommended.
|
73
|
-
OpenApiLibCore has limited support for parsing OpenAPI documents with
|
74
|
-
recursion in them. See the `recursion_limit` and `recursion_default` parameters.
|
75
|
-
|
76
|
-
If the openapi document passes this validation, the next step is trying to do a test
|
77
|
-
run with a minimal test suite.
|
78
|
-
The example below can be used, with `source`, `origin` and `path` altered to
|
79
|
-
fit your situation.
|
80
|
-
|
81
|
-
``` robotframework
|
82
|
-
*** Settings ***
|
83
|
-
Library OpenApiLibCore
|
84
|
-
... source=http://localhost:8000/openapi.json
|
85
|
-
... origin=http://localhost:8000
|
86
|
-
|
87
|
-
*** Test Cases ***
|
88
|
-
Getting Started
|
89
|
-
${url}= Get Valid Url path=/employees/{employee_id}
|
90
|
-
|
91
|
-
```
|
92
|
-
|
93
|
-
Running the above suite for the first time may result in an error / failed test.
|
94
|
-
You should look at the Robot Framework `log.html` to determine the reasons
|
95
|
-
for the failing tests.
|
96
|
-
Depending on the reasons for the failures, different solutions are possible.
|
97
|
-
|
98
|
-
Details about the OpenApiLibCore library parameters and keywords that you may need can be found
|
99
|
-
[here](https://marketsquare.github.io/robotframework-openapi-libcore/openapi_libcore.html).
|
100
|
-
|
101
|
-
The OpenApiLibCore also support handling of relations between resources within the scope
|
102
|
-
of the API being validated as well as handling dependencies on resources outside the
|
103
|
-
scope of the API. In addition there is support for handling restrictions on the values
|
104
|
-
of parameters and properties.
|
105
|
-
|
106
|
-
Details about the `mappings_path` variable usage can be found
|
107
|
-
[here](https://marketsquare.github.io/robotframework-openapi-libcore/advanced_use.html).
|
108
|
-
|
109
|
-
---
|
110
|
-
|
111
|
-
## Limitations
|
112
|
-
|
113
|
-
There are currently a number of limitations to supported API structures, supported
|
114
|
-
data types and properties. The following list details the most important ones:
|
115
|
-
- Only JSON request and response bodies are supported.
|
116
|
-
- No support for per-endpoint authorization levels.
|
117
|
-
- Parsing of OAS 3.1 documents is supported by the parsing tools, but runtime behavior is untested.
|
118
|
-
|
119
|
-
"""
|
120
|
-
|
121
1
|
import json as _json
|
122
2
|
import sys
|
123
3
|
from collections.abc import Mapping, MutableMapping
|
@@ -140,17 +20,18 @@ from robot.api.exceptions import FatalError
|
|
140
20
|
from robot.libraries.BuiltIn import BuiltIn
|
141
21
|
|
142
22
|
import OpenApiLibCore.data_generation as _data_generation
|
143
|
-
import OpenApiLibCore.data_invalidation as
|
144
|
-
import OpenApiLibCore.path_functions as
|
145
|
-
import OpenApiLibCore.path_invalidation as
|
146
|
-
import OpenApiLibCore.resource_relations as
|
147
|
-
import OpenApiLibCore.validation as
|
23
|
+
import OpenApiLibCore.data_invalidation as _data_invalidation
|
24
|
+
import OpenApiLibCore.path_functions as _path_functions
|
25
|
+
import OpenApiLibCore.path_invalidation as _path_invalidation
|
26
|
+
import OpenApiLibCore.resource_relations as _resource_relations
|
27
|
+
import OpenApiLibCore.validation as _validation
|
148
28
|
from OpenApiLibCore.annotations import JSON
|
149
29
|
from OpenApiLibCore.dto_base import Dto, IdReference
|
150
30
|
from OpenApiLibCore.dto_utils import (
|
151
31
|
DEFAULT_ID_PROPERTY_NAME,
|
152
32
|
get_dto_class,
|
153
33
|
get_id_property_name,
|
34
|
+
get_path_dto_class,
|
154
35
|
)
|
155
36
|
from OpenApiLibCore.localized_faker import FAKE
|
156
37
|
from OpenApiLibCore.models import (
|
@@ -164,27 +45,24 @@ from OpenApiLibCore.parameter_utils import (
|
|
164
45
|
)
|
165
46
|
from OpenApiLibCore.protocols import ResponseValidatorType
|
166
47
|
from OpenApiLibCore.request_data import RequestData, RequestValues
|
48
|
+
from openapitools_docs.docstrings import (
|
49
|
+
OPENAPILIBCORE_INIT_DOCSTRING,
|
50
|
+
OPENAPILIBCORE_LIBRARY_DOCSTRING,
|
51
|
+
)
|
167
52
|
|
168
53
|
run_keyword = BuiltIn().run_keyword
|
169
54
|
default_str_mapping: Mapping[str, str] = MappingProxyType({})
|
170
55
|
default_json_mapping: Mapping[str, JSON] = MappingProxyType({})
|
171
56
|
|
172
57
|
|
173
|
-
@library(scope="SUITE", doc_format="
|
58
|
+
@library(scope="SUITE", doc_format="HTML")
|
174
59
|
class OpenApiLibCore: # pylint: disable=too-many-public-methods
|
175
|
-
"""
|
176
|
-
Main class providing the keywords and core logic to interact with an OpenAPI server.
|
177
|
-
|
178
|
-
Visit the [https://github.com/MarketSquare/robotframework-openapi-libcore | library page]
|
179
|
-
for an introduction.
|
180
|
-
"""
|
181
|
-
|
182
60
|
def __init__( # noqa: PLR0913, pylint: disable=dangerous-default-value
|
183
61
|
self,
|
184
62
|
source: str,
|
185
63
|
origin: str = "",
|
186
64
|
base_path: str = "",
|
187
|
-
response_validation:
|
65
|
+
response_validation: _validation.ValidationLevel = _validation.ValidationLevel.WARN,
|
188
66
|
disable_server_validation: bool = True,
|
189
67
|
mappings_path: str | Path = "",
|
190
68
|
invalid_property_default_response: int = 422,
|
@@ -203,126 +81,6 @@ class OpenApiLibCore: # pylint: disable=too-many-public-methods
|
|
203
81
|
cookies: MutableMapping[str, str] | CookieJar | None = None,
|
204
82
|
proxies: MutableMapping[str, str] | None = None,
|
205
83
|
) -> None:
|
206
|
-
"""
|
207
|
-
== Base parameters ==
|
208
|
-
|
209
|
-
=== source ===
|
210
|
-
An absolute path to an openapi.json or openapi.yaml file or an url to such a file.
|
211
|
-
|
212
|
-
=== origin ===
|
213
|
-
The server (and port) of the target server. E.g. ``https://localhost:8000``
|
214
|
-
|
215
|
-
=== base_path ===
|
216
|
-
The routing between ``origin`` and the paths as found in the ``paths``
|
217
|
-
section in the openapi document.
|
218
|
-
E.g. ``/petshop/v2``.
|
219
|
-
|
220
|
-
== Test case execution ==
|
221
|
-
|
222
|
-
=== response_validation ===
|
223
|
-
By default, a ``WARN`` is logged when the Response received after a Request does not
|
224
|
-
comply with the schema as defined in the openapi document for the given operation. The
|
225
|
-
following values are supported:
|
226
|
-
|
227
|
-
- ``DISABLED``: All Response validation errors will be ignored
|
228
|
-
- ``INFO``: Any Response validation erros will be logged at ``INFO`` level
|
229
|
-
- ``WARN``: Any Response validation erros will be logged at ``WARN`` level
|
230
|
-
- ``STRICT``: The Test Case will fail on any Response validation errors
|
231
|
-
|
232
|
-
=== disable_server_validation ===
|
233
|
-
If enabled by setting this parameter to ``True``, the Response validation will also
|
234
|
-
include possible errors for Requests made to a server address that is not defined in
|
235
|
-
the list of servers in the openapi document. This generally means that if there is a
|
236
|
-
mismatch, every Test Case will raise this error. Note that ``localhost`` and
|
237
|
-
``127.0.0.1`` are not considered the same by Response validation.
|
238
|
-
|
239
|
-
== API-specific configurations ==
|
240
|
-
|
241
|
-
=== mappings_path ===
|
242
|
-
See [https://marketsquare.github.io/robotframework-openapi-libcore/advanced_use.html | this page]
|
243
|
-
for an in-depth explanation.
|
244
|
-
|
245
|
-
=== invalid_property_default_response ===
|
246
|
-
The default response code for requests with a JSON body that does not comply
|
247
|
-
with the schema.
|
248
|
-
Example: a value outside the specified range or a string value
|
249
|
-
for a property defined as integer in the schema.
|
250
|
-
|
251
|
-
=== default_id_property_name ===
|
252
|
-
The default name for the property that identifies a resource (i.e. a unique
|
253
|
-
entity) within the API.
|
254
|
-
The default value for this property name is ``id``.
|
255
|
-
If the target API uses a different name for all the resources within the API,
|
256
|
-
you can configure it globally using this property.
|
257
|
-
|
258
|
-
If different property names are used for the unique identifier for different
|
259
|
-
types of resources, an ``ID_MAPPING`` can be implemented using the ``mappings_path``.
|
260
|
-
|
261
|
-
=== faker_locale ===
|
262
|
-
A locale string or list of locale strings to pass to the Faker library to be
|
263
|
-
used in generation of string data for supported format types.
|
264
|
-
|
265
|
-
=== require_body_for_invalid_url ===
|
266
|
-
When a request is made against an invalid url, this usually is because of a "404" request;
|
267
|
-
a request for a resource that does not exist. Depending on API implementation, when a
|
268
|
-
request with a missing or invalid request body is made on a non-existent resource,
|
269
|
-
either a 404 or a 422 or 400 Response is normally returned. If the API being tested
|
270
|
-
processes the request body before checking if the requested resource exists, set
|
271
|
-
this parameter to True.
|
272
|
-
|
273
|
-
== Parsing parameters ==
|
274
|
-
|
275
|
-
=== recursion_limit ===
|
276
|
-
The recursion depth to which to fully parse recursive references before the
|
277
|
-
`recursion_default` is used to end the recursion.
|
278
|
-
|
279
|
-
=== recursion_default ===
|
280
|
-
The value that is used instead of the referenced schema when the
|
281
|
-
`recursion_limit` has been reached.
|
282
|
-
The default `{}` represents an empty object in JSON.
|
283
|
-
Depending on schema definitions, this may cause schema validation errors.
|
284
|
-
If this is the case, 'None' (``${NONE}`` in Robot Framework) or an empty list
|
285
|
-
can be tried as an alternative.
|
286
|
-
|
287
|
-
== Security-related parameters ==
|
288
|
-
_Note: these parameters are equivalent to those in the ``requests`` library._
|
289
|
-
|
290
|
-
=== username ===
|
291
|
-
The username to be used for Basic Authentication.
|
292
|
-
|
293
|
-
=== password ===
|
294
|
-
The password to be used for Basic Authentication.
|
295
|
-
|
296
|
-
=== security_token ===
|
297
|
-
The token to be used for token based security using the ``Authorization`` header.
|
298
|
-
|
299
|
-
=== auth ===
|
300
|
-
A [https://requests.readthedocs.io/en/latest/api/#authentication | requests ``AuthBase`` instance]
|
301
|
-
to be used for authentication instead of the ``username`` and ``password``.
|
302
|
-
|
303
|
-
=== cert ===
|
304
|
-
The SSL certificate to use with all requests.
|
305
|
-
If string: the path to ssl client cert file (.pem).
|
306
|
-
If tuple: the ('cert', 'key') pair.
|
307
|
-
|
308
|
-
=== verify_tls ===
|
309
|
-
Whether or not to verify the TLS / SSL certificate of the server.
|
310
|
-
If boolean: whether or not to verify the server TLS certificate.
|
311
|
-
If string: path to a CA bundle to use for verification.
|
312
|
-
|
313
|
-
=== extra_headers ===
|
314
|
-
A dictionary with extra / custom headers that will be send with every request.
|
315
|
-
This parameter can be used to send headers that are not documented in the
|
316
|
-
openapi document or to provide an API-key.
|
317
|
-
|
318
|
-
=== cookies ===
|
319
|
-
A dictionary or
|
320
|
-
[https://docs.python.org/3/library/http.cookiejar.html#http.cookiejar.CookieJar | CookieJar object]
|
321
|
-
to send with all requests.
|
322
|
-
|
323
|
-
=== proxies ===
|
324
|
-
A dictionary of 'protocol': 'proxy url' to use for all requests.
|
325
|
-
"""
|
326
84
|
self._source = source
|
327
85
|
self._origin = origin
|
328
86
|
self._base_path = base_path
|
@@ -359,12 +117,18 @@ class OpenApiLibCore: # pylint: disable=too-many-public-methods
|
|
359
117
|
self.get_dto_class = get_dto_class(
|
360
118
|
mappings_module_name=mappings_module_name
|
361
119
|
)
|
120
|
+
self.get_path_dto_class = get_path_dto_class(
|
121
|
+
mappings_module_name=mappings_module_name
|
122
|
+
)
|
362
123
|
self.get_id_property_name = get_id_property_name(
|
363
124
|
mappings_module_name=mappings_module_name
|
364
125
|
)
|
365
126
|
sys.path.pop()
|
366
127
|
else:
|
367
128
|
self.get_dto_class = get_dto_class(mappings_module_name="no mapping")
|
129
|
+
self.get_path_dto_class = get_path_dto_class(
|
130
|
+
mappings_module_name="no mapping"
|
131
|
+
)
|
368
132
|
self.get_id_property_name = get_id_property_name(
|
369
133
|
mappings_module_name="no mapping"
|
370
134
|
)
|
@@ -500,7 +264,7 @@ class OpenApiLibCore: # pylint: disable=too-many-public-methods
|
|
500
264
|
> Note: applicable UniquePropertyValueConstraint and IdReference Relations are
|
501
265
|
considered before changes to `json_data` are made.
|
502
266
|
"""
|
503
|
-
return
|
267
|
+
return _data_invalidation.get_invalid_body_data(
|
504
268
|
url=url,
|
505
269
|
method=method,
|
506
270
|
status_code=status_code,
|
@@ -518,7 +282,7 @@ class OpenApiLibCore: # pylint: disable=too-many-public-methods
|
|
518
282
|
Returns a version of `params, headers` as present on `request_data` that has
|
519
283
|
been modified to cause the provided `status_code`.
|
520
284
|
"""
|
521
|
-
return
|
285
|
+
return _data_invalidation.get_invalidated_parameters(
|
522
286
|
status_code=status_code,
|
523
287
|
request_data=request_data,
|
524
288
|
invalid_property_default_response=self.invalid_property_default_response,
|
@@ -533,7 +297,7 @@ class OpenApiLibCore: # pylint: disable=too-many-public-methods
|
|
533
297
|
returned by the `get_relations` implementation on the `dto` for the given
|
534
298
|
`conflict_status_code`.
|
535
299
|
"""
|
536
|
-
return
|
300
|
+
return _data_invalidation.get_json_data_with_conflict(
|
537
301
|
url=url,
|
538
302
|
base_url=self.base_url,
|
539
303
|
method=method,
|
@@ -555,10 +319,10 @@ class OpenApiLibCore: # pylint: disable=too-many-public-methods
|
|
555
319
|
`PathPropertiesConstraint` Relation can be used. More information can be found
|
556
320
|
[https://marketsquare.github.io/robotframework-openapitools/advanced_use.html | here].
|
557
321
|
"""
|
558
|
-
return
|
322
|
+
return _path_functions.get_valid_url(
|
559
323
|
path=path,
|
560
324
|
base_url=self.base_url,
|
561
|
-
|
325
|
+
get_path_dto_class=self.get_path_dto_class,
|
562
326
|
openapi_spec=self.openapi_spec,
|
563
327
|
)
|
564
328
|
|
@@ -570,7 +334,7 @@ class OpenApiLibCore: # pylint: disable=too-many-public-methods
|
|
570
334
|
To prevent resource conflicts with other test cases, a new resource is created
|
571
335
|
(by a POST operation) if possible.
|
572
336
|
"""
|
573
|
-
return
|
337
|
+
return _path_functions.get_valid_id_for_path(
|
574
338
|
path=path, get_id_property_name=self.get_id_property_name
|
575
339
|
)
|
576
340
|
|
@@ -583,7 +347,7 @@ class OpenApiLibCore: # pylint: disable=too-many-public-methods
|
|
583
347
|
path_parts = path.split("/")
|
584
348
|
# first part will be '' since a path starts with /
|
585
349
|
path_parts.pop(0)
|
586
|
-
parameterized_path =
|
350
|
+
parameterized_path = _path_functions.get_parametrized_path(
|
587
351
|
path=path, openapi_spec=self.openapi_spec
|
588
352
|
)
|
589
353
|
return parameterized_path
|
@@ -594,7 +358,7 @@ class OpenApiLibCore: # pylint: disable=too-many-public-methods
|
|
594
358
|
Perform a GET request on the `url` and return the list of resource
|
595
359
|
`ids` from the response.
|
596
360
|
"""
|
597
|
-
return
|
361
|
+
return _path_functions.get_ids_from_url(
|
598
362
|
url=url, get_id_property_name=self.get_id_property_name
|
599
363
|
)
|
600
364
|
|
@@ -611,13 +375,13 @@ class OpenApiLibCore: # pylint: disable=too-many-public-methods
|
|
611
375
|
on the mapped `path` and `expected_status_code`.
|
612
376
|
If a PathPropertiesConstraint is mapped, the `invalid_value` is returned.
|
613
377
|
|
614
|
-
Raises ValueError if the valid_url cannot be invalidated.
|
378
|
+
Raises: ValueError if the valid_url cannot be invalidated.
|
615
379
|
"""
|
616
|
-
return
|
380
|
+
return _path_invalidation.get_invalidated_url(
|
617
381
|
valid_url=valid_url,
|
618
382
|
path=path,
|
619
383
|
base_url=self.base_url,
|
620
|
-
|
384
|
+
get_path_dto_class=self.get_path_dto_class,
|
621
385
|
expected_status_code=expected_status_code,
|
622
386
|
)
|
623
387
|
|
@@ -629,7 +393,7 @@ class OpenApiLibCore: # pylint: disable=too-many-public-methods
|
|
629
393
|
Ensure that the (right-most) `id` of the resource referenced by the `url`
|
630
394
|
is used by the resource defined by the `resource_relation`.
|
631
395
|
"""
|
632
|
-
|
396
|
+
_resource_relations.ensure_in_use(
|
633
397
|
url=url,
|
634
398
|
base_url=self.base_url,
|
635
399
|
openapi_spec=self.openapi_spec,
|
@@ -700,7 +464,7 @@ class OpenApiLibCore: # pylint: disable=too-many-public-methods
|
|
700
464
|
Response keyword and finally validates, for `DELETE` operations, whether
|
701
465
|
the target resource was indeed deleted (OK response) or not (error responses).
|
702
466
|
"""
|
703
|
-
|
467
|
+
_validation.perform_validated_request(
|
704
468
|
path=path,
|
705
469
|
status_code=status_code,
|
706
470
|
request_values=request_values,
|
@@ -713,7 +477,7 @@ class OpenApiLibCore: # pylint: disable=too-many-public-methods
|
|
713
477
|
Validate the `response` against the OpenAPI Spec that is
|
714
478
|
loaded during library initialization.
|
715
479
|
"""
|
716
|
-
|
480
|
+
_validation.validate_response_using_validator(
|
717
481
|
response=response,
|
718
482
|
response_validator=self.response_validator,
|
719
483
|
)
|
@@ -726,7 +490,7 @@ class OpenApiLibCore: # pylint: disable=too-many-public-methods
|
|
726
490
|
Attempt to GET the resource referenced by the `href` and validate it's equal
|
727
491
|
to the provided `referenced_resource` object / dictionary.
|
728
492
|
"""
|
729
|
-
|
493
|
+
_validation.assert_href_to_resource_is_valid(
|
730
494
|
href=href,
|
731
495
|
origin=self.origin,
|
732
496
|
base_url=self.base_url,
|
@@ -750,7 +514,7 @@ class OpenApiLibCore: # pylint: disable=too-many-public-methods
|
|
750
514
|
- validate that no `original_data` is preserved when performing a PUT operation
|
751
515
|
- validate that a PATCH operation only updates the provided properties
|
752
516
|
"""
|
753
|
-
|
517
|
+
_validation.validate_response(
|
754
518
|
path=path,
|
755
519
|
response=response,
|
756
520
|
response_validator=self.response_validator,
|
@@ -774,7 +538,9 @@ class OpenApiLibCore: # pylint: disable=too-many-public-methods
|
|
774
538
|
In case a PATCH request, validate that only the properties that were patched
|
775
539
|
have changed and that other properties are still at their pre-patch values.
|
776
540
|
"""
|
777
|
-
|
541
|
+
_validation.validate_send_response(
|
542
|
+
response=response, original_data=original_data
|
543
|
+
)
|
778
544
|
|
779
545
|
# endregion
|
780
546
|
|
@@ -844,7 +610,7 @@ class OpenApiLibCore: # pylint: disable=too-many-public-methods
|
|
844
610
|
refstring: str, # pylint: disable=unused-argument
|
845
611
|
recursions: JSON, # pylint: disable=unused-argument
|
846
612
|
) -> JSON:
|
847
|
-
return self._recursion_default
|
613
|
+
return self._recursion_default # pragma: no cover
|
848
614
|
|
849
615
|
try:
|
850
616
|
# Since parsing of the OAS and creating the Spec can take a long time,
|
@@ -892,14 +658,19 @@ class OpenApiLibCore: # pylint: disable=too-many-public-methods
|
|
892
658
|
|
893
659
|
return parser, validation_spec, response_validator
|
894
660
|
|
895
|
-
except ResolutionError as exception:
|
661
|
+
except ResolutionError as exception: # pragma: no cover
|
896
662
|
raise FatalError(
|
897
663
|
f"ResolutionError while trying to load openapi spec: {exception}"
|
898
664
|
) from exception
|
899
|
-
except ValidationError as exception:
|
665
|
+
except ValidationError as exception: # pragma: no cover
|
900
666
|
raise FatalError(
|
901
667
|
f"ValidationError while trying to load openapi spec: {exception}"
|
902
668
|
) from exception
|
903
669
|
|
904
670
|
def read_paths(self) -> dict[str, PathItemObject]:
|
905
671
|
return self.openapi_spec.paths
|
672
|
+
|
673
|
+
__init__.__doc__ = OPENAPILIBCORE_INIT_DOCSTRING
|
674
|
+
|
675
|
+
|
676
|
+
OpenApiLibCore.__doc__ = OPENAPILIBCORE_LIBRARY_DOCSTRING
|
@@ -24,7 +24,7 @@ def get_safe_name_for_oas_name(oas_name: str) -> str:
|
|
24
24
|
PARAMETER_REGISTRY[oas_name] = oas_name
|
25
25
|
return oas_name
|
26
26
|
|
27
|
-
safe_name =
|
27
|
+
safe_name = convert_string_to_python_identifier(oas_name)
|
28
28
|
|
29
29
|
if safe_name not in PARAMETER_REGISTRY:
|
30
30
|
PARAMETER_REGISTRY[safe_name] = oas_name
|
@@ -36,7 +36,7 @@ def get_safe_name_for_oas_name(oas_name: str) -> str:
|
|
36
36
|
# We're dealing with multiple oas_names that convert to the same safe_name.
|
37
37
|
# To resolve this, a more verbose safe_name is generated. This is less user-friendly
|
38
38
|
# but necessary to ensure an one-to-one mapping.
|
39
|
-
verbose_safe_name =
|
39
|
+
verbose_safe_name = convert_string_to_python_identifier(oas_name, verbose=True)
|
40
40
|
if verbose_safe_name not in PARAMETER_REGISTRY:
|
41
41
|
PARAMETER_REGISTRY[verbose_safe_name] = oas_name
|
42
42
|
return verbose_safe_name
|
@@ -46,7 +46,7 @@ def _is_python_safe(name: str) -> bool:
|
|
46
46
|
return name.isidentifier()
|
47
47
|
|
48
48
|
|
49
|
-
def
|
49
|
+
def convert_string_to_python_identifier(string: str, verbose: bool = False) -> str:
|
50
50
|
def _convert_string_to_python_identifier() -> Generator[str, None, None]:
|
51
51
|
string_iterator = iter(string)
|
52
52
|
|
OpenApiLibCore/path_functions.py
CHANGED
@@ -8,9 +8,8 @@ from typing import Any
|
|
8
8
|
from requests import Response
|
9
9
|
from robot.libraries.BuiltIn import BuiltIn
|
10
10
|
|
11
|
-
from OpenApiLibCore.dto_base import PathPropertiesConstraint
|
12
11
|
from OpenApiLibCore.models import OpenApiObject
|
13
|
-
from OpenApiLibCore.protocols import
|
12
|
+
from OpenApiLibCore.protocols import GetIdPropertyNameType, GetPathDtoClassType
|
14
13
|
from OpenApiLibCore.request_data import RequestData
|
15
14
|
|
16
15
|
run_keyword = BuiltIn().run_keyword
|
@@ -64,7 +63,7 @@ def get_parametrized_path(path: str, openapi_spec: OpenApiObject) -> str:
|
|
64
63
|
def get_valid_url(
|
65
64
|
path: str,
|
66
65
|
base_url: str,
|
67
|
-
|
66
|
+
get_path_dto_class: GetPathDtoClassType,
|
68
67
|
openapi_spec: OpenApiObject,
|
69
68
|
) -> str:
|
70
69
|
try:
|
@@ -75,9 +74,9 @@ def get_valid_url(
|
|
75
74
|
raise ValueError(
|
76
75
|
f"{path} not found in paths section of the OpenAPI document."
|
77
76
|
) from None
|
78
|
-
dto_class =
|
79
|
-
relations = dto_class.
|
80
|
-
paths = [p.path for p in relations
|
77
|
+
dto_class = get_path_dto_class(path=path)
|
78
|
+
relations = dto_class.get_path_relations()
|
79
|
+
paths = [p.path for p in relations]
|
81
80
|
if paths:
|
82
81
|
url = f"{base_url}{choice(paths)}"
|
83
82
|
return url
|
@@ -5,8 +5,7 @@ from uuid import uuid4
|
|
5
5
|
|
6
6
|
from robot.libraries.BuiltIn import BuiltIn
|
7
7
|
|
8
|
-
from OpenApiLibCore.
|
9
|
-
from OpenApiLibCore.protocols import GetDtoClassType
|
8
|
+
from OpenApiLibCore.protocols import GetPathDtoClassType
|
10
9
|
|
11
10
|
run_keyword = BuiltIn().run_keyword
|
12
11
|
|
@@ -15,16 +14,15 @@ def get_invalidated_url(
|
|
15
14
|
valid_url: str,
|
16
15
|
path: str,
|
17
16
|
base_url: str,
|
18
|
-
|
17
|
+
get_path_dto_class: GetPathDtoClassType,
|
19
18
|
expected_status_code: int,
|
20
19
|
) -> str:
|
21
|
-
dto_class =
|
22
|
-
relations = dto_class.
|
20
|
+
dto_class = get_path_dto_class(path=path)
|
21
|
+
relations = dto_class.get_path_relations()
|
23
22
|
paths = [
|
24
23
|
p.invalid_value
|
25
24
|
for p in relations
|
26
|
-
if
|
27
|
-
and p.invalid_value_error_code == expected_status_code
|
25
|
+
if p.invalid_value_error_code == expected_status_code
|
28
26
|
]
|
29
27
|
if paths:
|
30
28
|
url = f"{base_url}{choice(paths)}"
|
OpenApiLibCore/protocols.py
CHANGED
@@ -30,3 +30,9 @@ class GetIdPropertyNameType(Protocol):
|
|
30
30
|
) -> tuple[
|
31
31
|
str, Callable[[str], str] | Callable[[int], int]
|
32
32
|
]: ... # pragma: no cover
|
33
|
+
|
34
|
+
|
35
|
+
class GetPathDtoClassType(Protocol):
|
36
|
+
def __init__(self, mappings_module_name: str) -> None: ... # pragma: no cover
|
37
|
+
|
38
|
+
def __call__(self, path: str) -> Type[Dto]: ... # pragma: no cover
|
OpenApiLibCore/request_data.py
CHANGED
File without changes
|
@@ -4,7 +4,7 @@ from requests import Response
|
|
4
4
|
from robot.api import logger
|
5
5
|
from robot.libraries.BuiltIn import BuiltIn
|
6
6
|
|
7
|
-
import OpenApiLibCore.path_functions as
|
7
|
+
import OpenApiLibCore.path_functions as _path_functions
|
8
8
|
from OpenApiLibCore.dto_base import IdReference
|
9
9
|
from OpenApiLibCore.models import OpenApiObject
|
10
10
|
from OpenApiLibCore.request_data import RequestData
|
@@ -22,7 +22,9 @@ def ensure_in_use(
|
|
22
22
|
|
23
23
|
path = url.replace(base_url, "")
|
24
24
|
path_parts = path.split("/")
|
25
|
-
parameterized_path =
|
25
|
+
parameterized_path = _path_functions.get_parametrized_path(
|
26
|
+
path=path, openapi_spec=openapi_spec
|
27
|
+
)
|
26
28
|
parameterized_path_parts = parameterized_path.split("/")
|
27
29
|
for part, param_part in zip(
|
28
30
|
reversed(path_parts), reversed(parameterized_path_parts)
|
OpenApiLibCore/validation.py
CHANGED
@@ -75,7 +75,7 @@ def perform_validated_request(
|
|
75
75
|
f"\nGot: {_json.dumps(response_json, indent=4, sort_keys=True)}"
|
76
76
|
)
|
77
77
|
raise AssertionError(
|
78
|
-
f"Response status_code {response.status_code} was not {status_code}"
|
78
|
+
f"Response status_code {response.status_code} was not {status_code}."
|
79
79
|
)
|
80
80
|
|
81
81
|
run_keyword("validate_response", path, response, original_data)
|
@@ -90,7 +90,7 @@ def perform_validated_request(
|
|
90
90
|
if response.ok:
|
91
91
|
if get_response.ok:
|
92
92
|
raise AssertionError(
|
93
|
-
f"Resource still exists after deletion. Url was {request_values.url}"
|
93
|
+
f"Resource still exists after deletion. Url was {request_values.url}."
|
94
94
|
)
|
95
95
|
# if the path supports GET, 404 is expected, if not 405 is expected
|
96
96
|
if get_response.status_code not in [404, 405]:
|
@@ -102,7 +102,7 @@ def perform_validated_request(
|
|
102
102
|
elif not get_response.ok:
|
103
103
|
raise AssertionError(
|
104
104
|
f"Resource could not be retrieved after failed deletion. "
|
105
|
-
f"Url was {request_values.url}, status_code was {get_response.status_code}"
|
105
|
+
f"Url was {request_values.url}, status_code was {get_response.status_code}."
|
106
106
|
)
|
107
107
|
|
108
108
|
|
@@ -279,11 +279,6 @@ def validate_send_response(
|
|
279
279
|
)
|
280
280
|
return None
|
281
281
|
|
282
|
-
# FIXME: this applies to removed code
|
283
|
-
# POST on /resource_type/{id}/array_item/ will return the updated {id} resource
|
284
|
-
# instead of a newly created resource. In this case, the send_json must be
|
285
|
-
# in the array of the 'array_item' property on {id}
|
286
|
-
|
287
282
|
send_path: str = response.request.path_url
|
288
283
|
response_path = response_data.get("href", None)
|
289
284
|
if response_path and send_path not in response_path:
|
@@ -380,6 +375,6 @@ def _get_response_object(
|
|
380
375
|
path_operations = path_item.get_operations()
|
381
376
|
operation_data = path_operations.get(method)
|
382
377
|
if operation_data is None:
|
383
|
-
raise ValueError(f"method '{method}' not supported for {path}")
|
378
|
+
raise ValueError(f"method '{method}' not supported for {path}.")
|
384
379
|
|
385
380
|
return operation_data.responses[status]
|
OpenApiLibCore/value_utils.py
CHANGED
@@ -98,7 +98,7 @@ def get_invalid_value(
|
|
98
98
|
# Violate min / max values or length if possible
|
99
99
|
try:
|
100
100
|
values_out_of_bounds = value_schema.get_values_out_of_bounds(
|
101
|
-
current_value=current_value
|
101
|
+
current_value=current_value # type: ignore[arg-type]
|
102
102
|
)
|
103
103
|
invalid_values += values_out_of_bounds
|
104
104
|
except ValueError:
|
openapi_libgen/__init__.py
CHANGED