robotframework-openapitools 1.0.0b1__tar.gz → 1.0.0b3__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 (37) hide show
  1. {robotframework_openapitools-1.0.0b1 → robotframework_openapitools-1.0.0b3}/PKG-INFO +97 -1
  2. robotframework_openapitools-1.0.0b3/docs/README.md +102 -0
  3. {robotframework_openapitools-1.0.0b1 → robotframework_openapitools-1.0.0b3}/pyproject.toml +1 -1
  4. {robotframework_openapitools-1.0.0b1 → robotframework_openapitools-1.0.0b3}/src/OpenApiDriver/openapidriver.libspec +2 -2
  5. {robotframework_openapitools-1.0.0b1 → robotframework_openapitools-1.0.0b3}/src/OpenApiLibCore/openapi_libcore.libspec +32 -36
  6. {robotframework_openapitools-1.0.0b1 → robotframework_openapitools-1.0.0b3}/src/OpenApiLibCore/openapi_libcore.py +2 -9
  7. {robotframework_openapitools-1.0.0b1 → robotframework_openapitools-1.0.0b3}/src/OpenApiLibCore/parameter_utils.py +3 -1
  8. {robotframework_openapitools-1.0.0b1 → robotframework_openapitools-1.0.0b3}/src/OpenApiLibCore/request_data.py +6 -0
  9. {robotframework_openapitools-1.0.0b1 → robotframework_openapitools-1.0.0b3}/src/OpenApiLibCore/validation.py +5 -5
  10. {robotframework_openapitools-1.0.0b1 → robotframework_openapitools-1.0.0b3}/src/openapi_libgen/spec_parser.py +20 -5
  11. {robotframework_openapitools-1.0.0b1 → robotframework_openapitools-1.0.0b3}/src/openapi_libgen/templates/library.jinja +1 -1
  12. robotframework_openapitools-1.0.0b1/docs/README.md +0 -6
  13. {robotframework_openapitools-1.0.0b1 → robotframework_openapitools-1.0.0b3}/LICENSE +0 -0
  14. {robotframework_openapitools-1.0.0b1 → robotframework_openapitools-1.0.0b3}/src/OpenApiDriver/__init__.py +0 -0
  15. {robotframework_openapitools-1.0.0b1 → robotframework_openapitools-1.0.0b3}/src/OpenApiDriver/openapi_executors.py +0 -0
  16. {robotframework_openapitools-1.0.0b1 → robotframework_openapitools-1.0.0b3}/src/OpenApiDriver/openapi_reader.py +0 -0
  17. {robotframework_openapitools-1.0.0b1 → robotframework_openapitools-1.0.0b3}/src/OpenApiDriver/openapidriver.py +0 -0
  18. {robotframework_openapitools-1.0.0b1 → robotframework_openapitools-1.0.0b3}/src/OpenApiDriver/py.typed +0 -0
  19. {robotframework_openapitools-1.0.0b1 → robotframework_openapitools-1.0.0b3}/src/OpenApiLibCore/__init__.py +0 -0
  20. {robotframework_openapitools-1.0.0b1 → robotframework_openapitools-1.0.0b3}/src/OpenApiLibCore/annotations.py +0 -0
  21. {robotframework_openapitools-1.0.0b1 → robotframework_openapitools-1.0.0b3}/src/OpenApiLibCore/data_generation/__init__.py +0 -0
  22. {robotframework_openapitools-1.0.0b1 → robotframework_openapitools-1.0.0b3}/src/OpenApiLibCore/data_generation/body_data_generation.py +0 -0
  23. {robotframework_openapitools-1.0.0b1 → robotframework_openapitools-1.0.0b3}/src/OpenApiLibCore/data_generation/data_generation_core.py +0 -0
  24. {robotframework_openapitools-1.0.0b1 → robotframework_openapitools-1.0.0b3}/src/OpenApiLibCore/data_invalidation.py +0 -0
  25. {robotframework_openapitools-1.0.0b1 → robotframework_openapitools-1.0.0b3}/src/OpenApiLibCore/dto_base.py +0 -0
  26. {robotframework_openapitools-1.0.0b1 → robotframework_openapitools-1.0.0b3}/src/OpenApiLibCore/dto_utils.py +0 -0
  27. {robotframework_openapitools-1.0.0b1 → robotframework_openapitools-1.0.0b3}/src/OpenApiLibCore/oas_cache.py +0 -0
  28. {robotframework_openapitools-1.0.0b1 → robotframework_openapitools-1.0.0b3}/src/OpenApiLibCore/path_functions.py +0 -0
  29. {robotframework_openapitools-1.0.0b1 → robotframework_openapitools-1.0.0b3}/src/OpenApiLibCore/path_invalidation.py +0 -0
  30. {robotframework_openapitools-1.0.0b1 → robotframework_openapitools-1.0.0b3}/src/OpenApiLibCore/protocols.py +0 -0
  31. {robotframework_openapitools-1.0.0b1 → robotframework_openapitools-1.0.0b3}/src/OpenApiLibCore/py.typed +0 -0
  32. {robotframework_openapitools-1.0.0b1 → robotframework_openapitools-1.0.0b3}/src/OpenApiLibCore/resource_relations.py +0 -0
  33. {robotframework_openapitools-1.0.0b1 → robotframework_openapitools-1.0.0b3}/src/OpenApiLibCore/value_utils.py +0 -0
  34. {robotframework_openapitools-1.0.0b1 → robotframework_openapitools-1.0.0b3}/src/openapi_libgen/__init__.py +0 -0
  35. {robotframework_openapitools-1.0.0b1 → robotframework_openapitools-1.0.0b3}/src/openapi_libgen/command_line.py +0 -0
  36. {robotframework_openapitools-1.0.0b1 → robotframework_openapitools-1.0.0b3}/src/openapi_libgen/parsing_utils.py +0 -0
  37. {robotframework_openapitools-1.0.0b1 → robotframework_openapitools-1.0.0b3}/src/openapi_libgen/templates/__init__.jinja +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: robotframework-openapitools
3
- Version: 1.0.0b1
3
+ Version: 1.0.0b3
4
4
  Summary: A set of Robot Framework libraries to test APIs for which the OAS is available.
5
5
  License: Apache License
6
6
  Version 2.0, January 2004
@@ -235,3 +235,99 @@ OpenApiTools is a set of libraries centered around the OpenAPI Specification:
235
235
  - [OpenApiDriver](./driver.md)
236
236
  - [OpenApiLibCore](./libcore.md)
237
237
 
238
+
239
+ ## New in OpenApiTools v1.0.0
240
+
241
+ Inspired by the work Bartlomiej and Mateusz did on #roboswag, I've created a CLI tool that generates a fully functional Robot Framework (Python) library that builds on OpenApiLibCore for automatic request data generation and request execution.
242
+
243
+ ### So how does it work?
244
+ After installing / updating to the v1.0.0 beta (`pip install robotframework-openapitools==1.0.0b3`) , there's a new CLI command available in your (virtual) environment: `generate-library`. You'll have to provide a path to the openapi spec (json or yaml, can be a url or path to the file), provide a path to where to generate the library and (optionally) a name for the library:
245
+
246
+ ```
247
+ $ generate-library
248
+ Please provide a source for the generation:
249
+ $ http://127.0.0.1:8000/openapi.json
250
+ Please provide a path to where the library will be generated:
251
+ $ ./generated
252
+ Please provide a name for the library [default: FastAPI]:
253
+ $ My Generated Library
254
+ generated/MyGeneratedLibrary/__init__.py created
255
+ generated/MyGeneratedLibrary/my_generated_library.py created
256
+ ```
257
+
258
+ > Note: there's currently 2 environment variables that are taken into account by the generator; USE_SUMMARY_AS_KEYWORD_NAME can result in nicer keyword names (but: uniqueness is not guaranteed, so you might need to rename some of the generated keywords manually) and EXPAND_BODY_ARGUMENTS is what a recent poll in #api-testing was about.
259
+
260
+ If the location where the library is located is in your Python search path, you'll be able to use it like a regular Robot Framework library (in fact, it's a drop-in replacement for OpenApiLibCore):
261
+
262
+ ```{robot}
263
+ *** Settings ***
264
+ Library MyGeneratedLibrary
265
+ ... source=${ORIGIN}/openapi.json
266
+ ... origin=${ORIGIN}
267
+ ... base_path=${EMPTY}
268
+ ... mappings_path=${ROOT}/tests/user_implemented/custom_user_mappings.py
269
+ Variables ${ROOT}/tests/variables.py
270
+
271
+
272
+ *** Variables ***
273
+ ${ORIGIN}= http://localhost:8000
274
+
275
+
276
+ *** Test Cases ***
277
+ Test Generated Keywords: Get Employees
278
+ ${response}= Get Employees
279
+ Should Be Equal As Integers ${response.status_code} 200
280
+
281
+ Test Generated Keywords: Post Employee
282
+ VAR &{body} name=Robin the Robot
283
+ ${response}= Post Employee body=${body}
284
+ # ${response}= Post Employee name=Robin the Robot
285
+ Should Be Equal As Integers ${response.status_code} 201
286
+ Should Be Equal ${response.json()}[name] Robin the Robot
287
+
288
+ Test Generated Keywords: Get Employee
289
+ ${response}= Get Employee
290
+ Should Be Equal As Integers ${response.status_code} 200
291
+
292
+ Test Generated Keywords: Patch Employee
293
+ ${employee_id}= Get Valid Id For Path path=/employees/{employee_id}
294
+ VAR &{body} date_of_birth=2021-12-31
295
+ ${response}= Patch Employee employee_id=${employee_id} body=${body}
296
+ # ${response}= Patch Employee employee_id=${employee_id} date_of_birth=2021-12-31
297
+ Should Be Equal As Integers ${response.status_code} 403
298
+
299
+ Test Generated Keywords: Post WageGroup
300
+ VAR &{body} hourly_rate=99.99
301
+ ${response}= Post Wagegroup body=${body}
302
+ # ${response}= Post Wagegroup hourly_rate=99.99
303
+ Should Be Equal As Integers ${response.status_code} 201
304
+ Should Be Equal As Numbers ${response.json()}[hourly-rate] 99.99
305
+
306
+ Test Generated Keywords: Get Energy Label
307
+ ${response}= Get Energy Label
308
+ ... zipcode=1111AA
309
+ ... home_number=10
310
+ ... extension=too long to be acceptable
311
+ ... validate_against_schema=${FALSE}
312
+ Should Be Equal As Integers ${response.status_code} 422
313
+
314
+ VAR @{omit} extension
315
+ ${response}= Get Energy Label
316
+ ... zipcode=1111AA
317
+ ... home_number=10
318
+ ... extension=too long to be acceptable
319
+ ... omit_parameters=${omit}
320
+ Should Be Equal As Integers ${response.status_code} 200
321
+ ```
322
+
323
+ ### Contributions and feedback
324
+
325
+ So now I need your feedback! Does the library generator work for your openapi spec? Does the library work / do all the generated keywords work? Please let me know of any issues you run into!
326
+ Things I'd like to address / improve before an official release:
327
+ - parameters with union types (e.g. int or float) are currently annotated as Any.
328
+ - support for lists / arrays is limited (i.e. not supported as body)
329
+ - objects / dicts are currently only typed as dict; I'm looking into TypedDict annotation for better auto-complete / auto-conversion
330
+ - a documentation rework
331
+
332
+ Subscribe to https://app.slack.com/client/T07PJQ9S7/CKK0X68KD for updates
333
+
@@ -0,0 +1,102 @@
1
+ # OpenApiTools for Robot Framework
2
+
3
+ OpenApiTools is a set of libraries centered around the OpenAPI Specification:
4
+
5
+ - [OpenApiDriver](./driver.md)
6
+ - [OpenApiLibCore](./libcore.md)
7
+
8
+
9
+ ## New in OpenApiTools v1.0.0
10
+
11
+ Inspired by the work Bartlomiej and Mateusz did on #roboswag, I've created a CLI tool that generates a fully functional Robot Framework (Python) library that builds on OpenApiLibCore for automatic request data generation and request execution.
12
+
13
+ ### So how does it work?
14
+ After installing / updating to the v1.0.0 beta (`pip install robotframework-openapitools==1.0.0b3`) , there's a new CLI command available in your (virtual) environment: `generate-library`. You'll have to provide a path to the openapi spec (json or yaml, can be a url or path to the file), provide a path to where to generate the library and (optionally) a name for the library:
15
+
16
+ ```
17
+ $ generate-library
18
+ Please provide a source for the generation:
19
+ $ http://127.0.0.1:8000/openapi.json
20
+ Please provide a path to where the library will be generated:
21
+ $ ./generated
22
+ Please provide a name for the library [default: FastAPI]:
23
+ $ My Generated Library
24
+ generated/MyGeneratedLibrary/__init__.py created
25
+ generated/MyGeneratedLibrary/my_generated_library.py created
26
+ ```
27
+
28
+ > Note: there's currently 2 environment variables that are taken into account by the generator; USE_SUMMARY_AS_KEYWORD_NAME can result in nicer keyword names (but: uniqueness is not guaranteed, so you might need to rename some of the generated keywords manually) and EXPAND_BODY_ARGUMENTS is what a recent poll in #api-testing was about.
29
+
30
+ If the location where the library is located is in your Python search path, you'll be able to use it like a regular Robot Framework library (in fact, it's a drop-in replacement for OpenApiLibCore):
31
+
32
+ ```{robot}
33
+ *** Settings ***
34
+ Library MyGeneratedLibrary
35
+ ... source=${ORIGIN}/openapi.json
36
+ ... origin=${ORIGIN}
37
+ ... base_path=${EMPTY}
38
+ ... mappings_path=${ROOT}/tests/user_implemented/custom_user_mappings.py
39
+ Variables ${ROOT}/tests/variables.py
40
+
41
+
42
+ *** Variables ***
43
+ ${ORIGIN}= http://localhost:8000
44
+
45
+
46
+ *** Test Cases ***
47
+ Test Generated Keywords: Get Employees
48
+ ${response}= Get Employees
49
+ Should Be Equal As Integers ${response.status_code} 200
50
+
51
+ Test Generated Keywords: Post Employee
52
+ VAR &{body} name=Robin the Robot
53
+ ${response}= Post Employee body=${body}
54
+ # ${response}= Post Employee name=Robin the Robot
55
+ Should Be Equal As Integers ${response.status_code} 201
56
+ Should Be Equal ${response.json()}[name] Robin the Robot
57
+
58
+ Test Generated Keywords: Get Employee
59
+ ${response}= Get Employee
60
+ Should Be Equal As Integers ${response.status_code} 200
61
+
62
+ Test Generated Keywords: Patch Employee
63
+ ${employee_id}= Get Valid Id For Path path=/employees/{employee_id}
64
+ VAR &{body} date_of_birth=2021-12-31
65
+ ${response}= Patch Employee employee_id=${employee_id} body=${body}
66
+ # ${response}= Patch Employee employee_id=${employee_id} date_of_birth=2021-12-31
67
+ Should Be Equal As Integers ${response.status_code} 403
68
+
69
+ Test Generated Keywords: Post WageGroup
70
+ VAR &{body} hourly_rate=99.99
71
+ ${response}= Post Wagegroup body=${body}
72
+ # ${response}= Post Wagegroup hourly_rate=99.99
73
+ Should Be Equal As Integers ${response.status_code} 201
74
+ Should Be Equal As Numbers ${response.json()}[hourly-rate] 99.99
75
+
76
+ Test Generated Keywords: Get Energy Label
77
+ ${response}= Get Energy Label
78
+ ... zipcode=1111AA
79
+ ... home_number=10
80
+ ... extension=too long to be acceptable
81
+ ... validate_against_schema=${FALSE}
82
+ Should Be Equal As Integers ${response.status_code} 422
83
+
84
+ VAR @{omit} extension
85
+ ${response}= Get Energy Label
86
+ ... zipcode=1111AA
87
+ ... home_number=10
88
+ ... extension=too long to be acceptable
89
+ ... omit_parameters=${omit}
90
+ Should Be Equal As Integers ${response.status_code} 200
91
+ ```
92
+
93
+ ### Contributions and feedback
94
+
95
+ So now I need your feedback! Does the library generator work for your openapi spec? Does the library work / do all the generated keywords work? Please let me know of any issues you run into!
96
+ Things I'd like to address / improve before an official release:
97
+ - parameters with union types (e.g. int or float) are currently annotated as Any.
98
+ - support for lists / arrays is limited (i.e. not supported as body)
99
+ - objects / dicts are currently only typed as dict; I'm looking into TypedDict annotation for better auto-complete / auto-conversion
100
+ - a documentation rework
101
+
102
+ Subscribe to https://app.slack.com/client/T07PJQ9S7/CKK0X68KD for updates
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name="robotframework-openapitools"
3
- version = "1.0.0b1"
3
+ version = "1.0.0b3"
4
4
  description = "A set of Robot Framework libraries to test APIs for which the OAS is available."
5
5
  authors = [
6
6
  {name = "Robin Mackaij", email = "r.a.mackaij@gmail.com"},
@@ -1,6 +1,6 @@
1
1
  <?xml version="1.0" encoding="UTF-8"?>
2
- <keywordspec name="OpenApiDriver" type="LIBRARY" format="HTML" scope="SUITE" generated="2025-04-01T12:12:33+00:00" specversion="6" source="/workspaces/robotframework-openapitools/src/OpenApiDriver/openapidriver.py" lineno="358">
3
- <version>1.0.0b1</version>
2
+ <keywordspec name="OpenApiDriver" type="LIBRARY" format="HTML" scope="SUITE" generated="2025-04-10T08:09:09+00:00" specversion="6" source="/workspaces/robotframework-openapitools/src/OpenApiDriver/openapidriver.py" lineno="358">
3
+ <version>1.0.0b2</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>
@@ -1,12 +1,12 @@
1
1
  <?xml version="1.0" encoding="UTF-8"?>
2
- <keywordspec name="OpenApiLibCore" type="LIBRARY" format="HTML" scope="SUITE" generated="2025-04-01T12:12:26+00:00" specversion="6" source="/workspaces/robotframework-openapitools/src/OpenApiLibCore/openapi_libcore.py" lineno="174">
3
- <version>1.0.0b1</version>
2
+ <keywordspec name="OpenApiLibCore" type="LIBRARY" format="HTML" scope="SUITE" generated="2025-04-10T08:09:02+00:00" specversion="6" source="/workspaces/robotframework-openapitools/src/OpenApiLibCore/openapi_libcore.py" lineno="170">
3
+ <version>1.0.0b2</version>
4
4
  <doc>&lt;p&gt;Main class providing the keywords and core logic to interact with an OpenAPI server.&lt;/p&gt;
5
5
  &lt;p&gt;Visit the &lt;a href="https://github.com/MarketSquare/robotframework-openapi-libcore"&gt;library page&lt;/a&gt; for an introduction.&lt;/p&gt;</doc>
6
6
  <tags>
7
7
  </tags>
8
8
  <inits>
9
- <init name="__init__" lineno="182">
9
+ <init name="__init__" lineno="178">
10
10
  <arguments repr="source: str, origin: str = , base_path: str = , response_validation: ValidationLevel = WARN, disable_server_validation: bool = True, mappings_path: str | Path = , invalid_property_default_response: int = 422, default_id_property_name: str = id, faker_locale: str | list[str] = , require_body_for_invalid_url: bool = False, recursion_limit: int = 1, recursion_default: dict[str, JSON] | list[JSON] | str | bytes | int | float | bool | None = {}, username: str = , password: str = , security_token: str = , auth: AuthBase | None = None, cert: str | tuple[str, str] = , verify_tls: bool | str = True, extra_headers: Mapping[str, str] = {}, cookies: MutableMapping[str, str] | RequestsCookieJar | None = None, proxies: MutableMapping[str, str] | None = None">
11
11
  <arg kind="POSITIONAL_OR_NAMED" required="true" repr="source: str">
12
12
  <name>source</name>
@@ -222,7 +222,7 @@
222
222
  </init>
223
223
  </inits>
224
224
  <keywords>
225
- <kw name="Assert Href To Resource Is Valid" lineno="742">
225
+ <kw name="Assert Href To Resource Is Valid" lineno="735">
226
226
  <arguments repr="href: str, referenced_resource: dict[str, dict[str, JSON] | list[JSON] | str | bytes | int | float | bool | None]">
227
227
  <arg kind="POSITIONAL_OR_NAMED" required="true" repr="href: str">
228
228
  <name>href</name>
@@ -253,7 +253,7 @@
253
253
  <doc>&lt;p&gt;Attempt to GET the resource referenced by the &lt;span class="name"&gt;href&lt;/span&gt; and validate it's equal to the provided &lt;span class="name"&gt;referenced_resource&lt;/span&gt; object / dictionary.&lt;/p&gt;</doc>
254
254
  <shortdoc>Attempt to GET the resource referenced by the `href` and validate it's equal to the provided `referenced_resource` object / dictionary.</shortdoc>
255
255
  </kw>
256
- <kw name="Authorized Request" lineno="659">
256
+ <kw name="Authorized Request" lineno="655">
257
257
  <arguments repr="url: str, method: str, params: dict[str, Any] | None = None, headers: dict[str, str] | None = None, json_data: dict[str, JSON] | list[JSON] | str | bytes | int | float | bool | None = None, data: Any | None = None, files: Any | None = None">
258
258
  <arg kind="POSITIONAL_OR_NAMED" required="true" repr="url: str">
259
259
  <name>url</name>
@@ -327,7 +327,7 @@
327
327
  &lt;p&gt;&amp;gt; Note: provided username / password or auth objects take precedence over token based security&lt;/p&gt;</doc>
328
328
  <shortdoc>Perform a request using the security token or authentication set in the library.</shortdoc>
329
329
  </kw>
330
- <kw name="Ensure In Use" lineno="644">
330
+ <kw name="Ensure In Use" lineno="640">
331
331
  <arguments repr="url: str, resource_relation: IdReference">
332
332
  <arg kind="POSITIONAL_OR_NAMED" required="true" repr="url: str">
333
333
  <name>url</name>
@@ -341,7 +341,7 @@
341
341
  <doc>&lt;p&gt;Ensure that the (right-most) &lt;span class="name"&gt;id&lt;/span&gt; of the resource referenced by the &lt;span class="name"&gt;url&lt;/span&gt; is used by the resource defined by the &lt;span class="name"&gt;resource_relation&lt;/span&gt;.&lt;/p&gt;</doc>
342
342
  <shortdoc>Ensure that the (right-most) `id` of the resource referenced by the `url` is used by the resource defined by the `resource_relation`.</shortdoc>
343
343
  </kw>
344
- <kw name="Get Ids From Url" lineno="609">
344
+ <kw name="Get Ids From Url" lineno="605">
345
345
  <arguments repr="url: str">
346
346
  <arg kind="POSITIONAL_OR_NAMED" required="true" repr="url: str">
347
347
  <name>url</name>
@@ -354,7 +354,7 @@
354
354
  <doc>&lt;p&gt;Perform a GET request on the &lt;span class="name"&gt;url&lt;/span&gt; and return the list of resource &lt;span class="name"&gt;ids&lt;/span&gt; from the response.&lt;/p&gt;</doc>
355
355
  <shortdoc>Perform a GET request on the `url` and return the list of resource `ids` from the response.</shortdoc>
356
356
  </kw>
357
- <kw name="Get Invalid Json Data" lineno="506">
357
+ <kw name="Get Invalid Json Data" lineno="502">
358
358
  <arguments repr="url: str, method: str, status_code: int, request_data: RequestData">
359
359
  <arg kind="POSITIONAL_OR_NAMED" required="true" repr="url: str">
360
360
  <name>url</name>
@@ -395,7 +395,7 @@
395
395
  &lt;p&gt;&amp;gt; Note: applicable UniquePropertyValueConstraint and IdReference Relations are considered before changes to &lt;span class="name"&gt;json_data&lt;/span&gt; are made.&lt;/p&gt;</doc>
396
396
  <shortdoc>Return `json_data` based on the `dto` on the `request_data` that will cause the provided `status_code` for the `method` operation on the `url`.</shortdoc>
397
397
  </kw>
398
- <kw name="Get Invalidated Parameters" lineno="529">
398
+ <kw name="Get Invalidated Parameters" lineno="525">
399
399
  <arguments repr="status_code: int, request_data: RequestData">
400
400
  <arg kind="POSITIONAL_OR_NAMED" required="true" repr="status_code: int">
401
401
  <name>status_code</name>
@@ -433,7 +433,7 @@
433
433
  <doc>&lt;p&gt;Returns a version of &lt;span class="name"&gt;params, headers&lt;/span&gt; as present on &lt;span class="name"&gt;request_data&lt;/span&gt; that has been modified to cause the provided &lt;span class="name"&gt;status_code&lt;/span&gt;.&lt;/p&gt;</doc>
434
434
  <shortdoc>Returns a version of `params, headers` as present on `request_data` that has been modified to cause the provided `status_code`.</shortdoc>
435
435
  </kw>
436
- <kw name="Get Invalidated Url" lineno="619">
436
+ <kw name="Get Invalidated Url" lineno="615">
437
437
  <arguments repr="valid_url: str, path: str = , expected_status_code: int = 404">
438
438
  <arg kind="POSITIONAL_OR_NAMED" required="true" repr="valid_url: str">
439
439
  <name>valid_url</name>
@@ -455,7 +455,7 @@
455
455
  &lt;p&gt;Raises ValueError if the valid_url cannot be invalidated.&lt;/p&gt;</doc>
456
456
  <shortdoc>Return an url with all the path parameters in the `valid_url` replaced by a random UUID if no PathPropertiesConstraint is mapped for the `"get"` operation on the mapped `path` and `expected_status_code`. If a PathPropertiesConstraint is mapped, the `invalid_value` is returned.</shortdoc>
457
457
  </kw>
458
- <kw name="Get Json Data For Dto Class" lineno="489">
458
+ <kw name="Get Json Data For Dto Class" lineno="485">
459
459
  <arguments repr="schema: dict[str, dict[str, JSON] | list[JSON] | str | bytes | int | float | bool | None], dto_class: type[Dto], operation_id: str = ">
460
460
  <arg kind="POSITIONAL_OR_NAMED" required="true" repr="schema: dict[str, dict[str, JSON] | list[JSON] | str | bytes | int | float | bool | None]">
461
461
  <name>schema</name>
@@ -508,7 +508,7 @@
508
508
  <doc>&lt;p&gt;Generate valid (json-compatible) data for the &lt;span class="name"&gt;dto_class&lt;/span&gt;.&lt;/p&gt;</doc>
509
509
  <shortdoc>Generate valid (json-compatible) data for the `dto_class`.</shortdoc>
510
510
  </kw>
511
- <kw name="Get Json Data With Conflict" lineno="545">
511
+ <kw name="Get Json Data With Conflict" lineno="541">
512
512
  <arguments repr="url: str, method: str, dto: Dto, conflict_status_code: int">
513
513
  <arg kind="POSITIONAL_OR_NAMED" required="true" repr="url: str">
514
514
  <name>url</name>
@@ -548,7 +548,7 @@
548
548
  <doc>&lt;p&gt;Return &lt;span class="name"&gt;json_data&lt;/span&gt; based on the &lt;span class="name"&gt;UniquePropertyValueConstraint&lt;/span&gt; that must be returned by the &lt;span class="name"&gt;get_relations&lt;/span&gt; implementation on the &lt;span class="name"&gt;dto&lt;/span&gt; for the given &lt;span class="name"&gt;conflict_status_code&lt;/span&gt;.&lt;/p&gt;</doc>
549
549
  <shortdoc>Return `json_data` based on the `UniquePropertyValueConstraint` that must be returned by the `get_relations` implementation on the `dto` for the given `conflict_status_code`.</shortdoc>
550
550
  </kw>
551
- <kw name="Get Parameterized Path From Url" lineno="595">
551
+ <kw name="Get Parameterized Path From Url" lineno="591">
552
552
  <arguments repr="url: str">
553
553
  <arg kind="POSITIONAL_OR_NAMED" required="true" repr="url: str">
554
554
  <name>url</name>
@@ -559,7 +559,7 @@
559
559
  <doc>&lt;p&gt;Return the path as found in the &lt;span class="name"&gt;paths&lt;/span&gt; section based on the given &lt;span class="name"&gt;url&lt;/span&gt;.&lt;/p&gt;</doc>
560
560
  <shortdoc>Return the path as found in the `paths` section based on the given `url`.</shortdoc>
561
561
  </kw>
562
- <kw name="Get Request Data" lineno="478">
562
+ <kw name="Get Request Data" lineno="474">
563
563
  <arguments repr="path: str, method: str">
564
564
  <arg kind="POSITIONAL_OR_NAMED" required="true" repr="path: str">
565
565
  <name>path</name>
@@ -574,7 +574,7 @@
574
574
  <doc>&lt;p&gt;Return an object with valid request data for body, headers and query params.&lt;/p&gt;</doc>
575
575
  <shortdoc>Return an object with valid request data for body, headers and query params.</shortdoc>
576
576
  </kw>
577
- <kw name="Get Request Values" lineno="437">
577
+ <kw name="Get Request Values" lineno="433">
578
578
  <arguments repr="path: str, method: str, overrides: Mapping[str, object] = {}">
579
579
  <arg kind="POSITIONAL_OR_NAMED" required="true" repr="path: str">
580
580
  <name>path</name>
@@ -597,7 +597,7 @@
597
597
  <doc>&lt;p&gt;Return an object with all (valid) request values needed to make a request.&lt;/p&gt;</doc>
598
598
  <shortdoc>Return an object with all (valid) request values needed to make a request.</shortdoc>
599
599
  </kw>
600
- <kw name="Get Valid Id For Path" lineno="583">
600
+ <kw name="Get Valid Id For Path" lineno="579">
601
601
  <arguments repr="path: str">
602
602
  <arg kind="POSITIONAL_OR_NAMED" required="true" repr="path: str">
603
603
  <name>path</name>
@@ -613,7 +613,7 @@
613
613
  &lt;p&gt;To prevent resource conflicts with other test cases, a new resource is created (by a POST operation) if possible.&lt;/p&gt;</doc>
614
614
  <shortdoc>Support keyword that returns the `id` for an existing resource at `path`.</shortdoc>
615
615
  </kw>
616
- <kw name="Get Valid Url" lineno="564">
616
+ <kw name="Get Valid Url" lineno="560">
617
617
  <arguments repr="path: str">
618
618
  <arg kind="POSITIONAL_OR_NAMED" required="true" repr="path: str">
619
619
  <name>path</name>
@@ -626,7 +626,7 @@
626
626
  &lt;p&gt;&amp;gt; Note: if valid ids cannot be retrieved within the scope of the API, the &lt;span class="name"&gt;PathPropertiesConstraint&lt;/span&gt; Relation can be used. More information can be found &lt;a href="https://marketsquare.github.io/robotframework-openapitools/advanced_use.html"&gt;here&lt;/a&gt;.&lt;/p&gt;</doc>
627
627
  <shortdoc>This keyword returns a valid url for the given `path`.</shortdoc>
628
628
  </kw>
629
- <kw name="Perform Validated Request" lineno="708">
629
+ <kw name="Perform Validated Request" lineno="704">
630
630
  <arguments repr="path: str, status_code: int, request_values: RequestValues, original_data: Mapping[str, object] = {}">
631
631
  <arg kind="POSITIONAL_OR_NAMED" required="true" repr="path: str">
632
632
  <name>path</name>
@@ -652,7 +652,7 @@
652
652
  <doc>&lt;p&gt;This keyword first calls the Authorized Request keyword, then the Validate Response keyword and finally validates, for &lt;span class="name"&gt;DELETE&lt;/span&gt; operations, whether the target resource was indeed deleted (OK response) or not (error responses).&lt;/p&gt;</doc>
653
653
  <shortdoc>This keyword first calls the Authorized Request keyword, then the Validate Response keyword and finally validates, for `DELETE` operations, whether the target resource was indeed deleted (OK response) or not (error responses).</shortdoc>
654
654
  </kw>
655
- <kw name="Set Auth" lineno="415">
655
+ <kw name="Set Auth" lineno="411">
656
656
  <arguments repr="auth: AuthBase">
657
657
  <arg kind="POSITIONAL_OR_NAMED" required="true" repr="auth: AuthBase">
658
658
  <name>auth</name>
@@ -663,7 +663,7 @@
663
663
  &lt;p&gt;After calling this keyword, subsequent requests will use the provided &lt;span class="name"&gt;auth&lt;/span&gt; instance.&lt;/p&gt;</doc>
664
664
  <shortdoc>Set the `auth` used for authentication after the library is imported.</shortdoc>
665
665
  </kw>
666
- <kw name="Set Basic Auth" lineno="403">
666
+ <kw name="Set Basic Auth" lineno="399">
667
667
  <arguments repr="username: str, password: str">
668
668
  <arg kind="POSITIONAL_OR_NAMED" required="true" repr="username: str">
669
669
  <name>username</name>
@@ -678,7 +678,7 @@
678
678
  &lt;p&gt;After calling this keyword, subsequent requests will use the provided credentials.&lt;/p&gt;</doc>
679
679
  <shortdoc>Set the `username` and `password` used for basic authentication after the library is imported.</shortdoc>
680
680
  </kw>
681
- <kw name="Set Extra Headers" lineno="425">
681
+ <kw name="Set Extra Headers" lineno="421">
682
682
  <arguments repr="extra_headers: dict[str, str]">
683
683
  <arg kind="POSITIONAL_OR_NAMED" required="true" repr="extra_headers: dict[str, str]">
684
684
  <name>extra_headers</name>
@@ -692,7 +692,7 @@
692
692
  &lt;p&gt;After calling this keyword, subsequent requests will use the provided &lt;span class="name"&gt;extra_headers&lt;/span&gt;.&lt;/p&gt;</doc>
693
693
  <shortdoc>Set the `extra_headers` used in requests after the library is imported.</shortdoc>
694
694
  </kw>
695
- <kw name="Set Origin" lineno="380">
695
+ <kw name="Set Origin" lineno="376">
696
696
  <arguments repr="origin: str">
697
697
  <arg kind="POSITIONAL_OR_NAMED" required="true" repr="origin: str">
698
698
  <name>origin</name>
@@ -704,7 +704,7 @@
704
704
  &lt;p&gt;In combination with OpenApiLibCore, the &lt;span class="name"&gt;origin&lt;/span&gt; can be used at any point to target another server that hosts an API that complies to the same OAS.&lt;/p&gt;</doc>
705
705
  <shortdoc>Set the `origin` after the library is imported.</shortdoc>
706
706
  </kw>
707
- <kw name="Set Security Token" lineno="394">
707
+ <kw name="Set Security Token" lineno="390">
708
708
  <arguments repr="security_token: str">
709
709
  <arg kind="POSITIONAL_OR_NAMED" required="true" repr="security_token: str">
710
710
  <name>security_token</name>
@@ -715,7 +715,7 @@
715
715
  &lt;p&gt;After calling this keyword, subsequent requests will use the provided token.&lt;/p&gt;</doc>
716
716
  <shortdoc>Set the `security_token` after the library is imported.</shortdoc>
717
717
  </kw>
718
- <kw name="Validate Resource Properties" lineno="786">
718
+ <kw name="Validate Resource Properties" lineno="779">
719
719
  <arguments repr="resource: dict[str, dict[str, JSON] | list[JSON] | str | bytes | int | float | bool | None], schema: dict[str, dict[str, JSON] | list[JSON] | str | bytes | int | float | bool | None]">
720
720
  <arg kind="POSITIONAL_OR_NAMED" required="true" repr="resource: dict[str, dict[str, JSON] | list[JSON] | str | bytes | int | float | bool | None]">
721
721
  <name>resource</name>
@@ -763,7 +763,7 @@
763
763
  <doc>&lt;p&gt;Validate that the &lt;span class="name"&gt;resource&lt;/span&gt; does not contain any properties that are not defined in the &lt;span class="name"&gt;schema_properties&lt;/span&gt;.&lt;/p&gt;</doc>
764
764
  <shortdoc>Validate that the `resource` does not contain any properties that are not defined in the `schema_properties`.</shortdoc>
765
765
  </kw>
766
- <kw name="Validate Response" lineno="757">
766
+ <kw name="Validate Response" lineno="750">
767
767
  <arguments repr="path: str, response: Response, original_data: Mapping[str, object] = {}">
768
768
  <arg kind="POSITIONAL_OR_NAMED" required="true" repr="path: str">
769
769
  <name>path</name>
@@ -793,21 +793,17 @@
793
793
  &lt;/ul&gt;</doc>
794
794
  <shortdoc>Validate the `response` by performing the following validations: - validate the `response` against the openapi schema for the `path` - validate that the response does not contain extra properties - validate that a href, if present, refers to the correct resource - validate that the value for a property that is in the response is equal to the property value that was send - validate that no `original_data` is preserved when performing a PUT operation - validate that a PATCH operation only updates the provided properties</shortdoc>
795
795
  </kw>
796
- <kw name="Validate Response Using Validator" lineno="728">
797
- <arguments repr="request: RequestsOpenAPIRequest, response: RequestsOpenAPIResponse">
798
- <arg kind="POSITIONAL_OR_NAMED" required="true" repr="request: RequestsOpenAPIRequest">
799
- <name>request</name>
800
- <type name="RequestsOpenAPIRequest"/>
801
- </arg>
802
- <arg kind="POSITIONAL_OR_NAMED" required="true" repr="response: RequestsOpenAPIResponse">
796
+ <kw name="Validate Response Using Validator" lineno="724">
797
+ <arguments repr="response: Response">
798
+ <arg kind="POSITIONAL_OR_NAMED" required="true" repr="response: Response">
803
799
  <name>response</name>
804
- <type name="RequestsOpenAPIResponse"/>
800
+ <type name="Response"/>
805
801
  </arg>
806
802
  </arguments>
807
- <doc>&lt;p&gt;Validate the &lt;span class="name"&gt;response&lt;/span&gt; for a given &lt;span class="name"&gt;request&lt;/span&gt; against the OpenAPI Spec that is loaded during library initialization.&lt;/p&gt;</doc>
808
- <shortdoc>Validate the `response` for a given `request` against the OpenAPI Spec that is loaded during library initialization.</shortdoc>
803
+ <doc>&lt;p&gt;Validate the &lt;span class="name"&gt;response&lt;/span&gt; against the OpenAPI Spec that is loaded during library initialization.&lt;/p&gt;</doc>
804
+ <shortdoc>Validate the `response` against the OpenAPI Spec that is loaded during library initialization.</shortdoc>
809
805
  </kw>
810
- <kw name="Validate Send Response" lineno="800">
806
+ <kw name="Validate Send Response" lineno="793">
811
807
  <arguments repr="response: Response, original_data: Mapping[str, object] = {}">
812
808
  <arg kind="POSITIONAL_OR_NAMED" required="true" repr="response: Response">
813
809
  <name>response</name>
@@ -128,10 +128,6 @@ from types import MappingProxyType
128
128
  from typing import Any, Generator
129
129
 
130
130
  from openapi_core import Config, OpenAPI, Spec
131
- from openapi_core.contrib.requests import (
132
- RequestsOpenAPIRequest,
133
- RequestsOpenAPIResponse,
134
- )
135
131
  from openapi_core.validation.exceptions import ValidationError
136
132
  from prance import ResolvingParser
137
133
  from prance.util.url import ResolutionError
@@ -725,15 +721,12 @@ class OpenApiLibCore: # pylint: disable=too-many-public-methods
725
721
  )
726
722
 
727
723
  @keyword
728
- def validate_response_using_validator(
729
- self, request: RequestsOpenAPIRequest, response: RequestsOpenAPIResponse
730
- ) -> None:
724
+ def validate_response_using_validator(self, response: Response) -> None:
731
725
  """
732
- Validate the `response` for a given `request` against the OpenAPI Spec that is
726
+ Validate the `response` against the OpenAPI Spec that is
733
727
  loaded during library initialization.
734
728
  """
735
729
  val.validate_response_using_validator(
736
- request=request,
737
730
  response=response,
738
731
  response_validator=self.response_validator,
739
732
  )
@@ -7,7 +7,9 @@ from typing import Generator
7
7
 
8
8
  from OpenApiLibCore.annotations import JSON
9
9
 
10
- PARAMETER_REGISTRY: dict[str, str] = {"body": "body"}
10
+ PARAMETER_REGISTRY: dict[str, str] = {
11
+ "body": "body",
12
+ }
11
13
 
12
14
 
13
15
  def get_oas_name_from_safe_name(safe_name: str) -> str:
@@ -40,6 +40,12 @@ class RequestValues:
40
40
  self.override_header_value(name=name, value=value)
41
41
  self.override_param_value(name=name, value=value)
42
42
 
43
+ def remove_parameters(self, parameters: list[str]) -> None:
44
+ for parameter in parameters:
45
+ _ = self.params.pop(parameter, None)
46
+ _ = self.headers.pop(parameter, None)
47
+ _ = self.json_data.pop(parameter, None)
48
+
43
49
 
44
50
  @dataclass
45
51
  class RequestData:
@@ -383,11 +383,12 @@ def validate_send_response(
383
383
 
384
384
 
385
385
  def validate_response_using_validator(
386
- request: RequestsOpenAPIRequest,
387
- response: RequestsOpenAPIResponse,
386
+ response: Response,
388
387
  response_validator: ResponseValidatorType,
389
388
  ) -> None:
390
- response_validator(request=request, response=response)
389
+ openapi_request = RequestsOpenAPIRequest(response.request)
390
+ openapi_response = RequestsOpenAPIResponse(response)
391
+ response_validator(request=openapi_request, response=openapi_response)
391
392
 
392
393
 
393
394
  def _validate_response(
@@ -400,8 +401,7 @@ def _validate_response(
400
401
  ) -> None:
401
402
  try:
402
403
  validate_response_using_validator(
403
- RequestsOpenAPIRequest(response.request),
404
- RequestsOpenAPIResponse(response),
404
+ response=response,
405
405
  response_validator=response_validator,
406
406
  )
407
407
  except (ResponseValidationError, ServerNotFound) as exception:
@@ -13,14 +13,20 @@ KEYWORD_TEMPLATE = r"""@keyword
13
13
  {signature}
14
14
  {body}"""
15
15
 
16
- SIGNATURE_TEMPLATE = r"def {keyword_name}(self{arguments}) -> Response:"
16
+ SIGNATURE_TEMPLATE = r"def {keyword_name}(self{arguments}, validate_against_schema: bool = True) -> Response:"
17
17
 
18
18
  BODY_TEMPLATE_ = r"""overrides = {key: value for key, value in locals().items() if value is not UNSET and key != "self"}
19
+ _ = overrides.pop("validate_against_schema", None)
20
+ omit_list = overrides.pop("omit_parameters", [])
19
21
  body_data = overrides.pop("body", {})
20
22
  overrides.update(body_data)
21
23
  updated_path: str = substitute_path_parameters(path="{path_value}", substitution_dict=overrides)
22
24
  request_values: RequestValues = self.get_request_values(path=f"{updated_path}", method="{method_value}", overrides=overrides)
23
- return self._perform_request(request_values=request_values)"""
25
+ request_values.remove_parameters(parameters=omit_list)
26
+ response = self._perform_request(request_values=request_values)
27
+ if validate_against_schema:
28
+ run_keyword('validate_response_using_validator', response)
29
+ return response"""
24
30
 
25
31
 
26
32
  class BodyTemplate(Template):
@@ -34,11 +40,13 @@ BODY_TEMPLATE = BodyTemplate(BODY_TEMPLATE_)
34
40
  class OperationDetails:
35
41
  path: str
36
42
  method: str
37
- operation_id: str
38
43
  parameters: list[dict[str, Any]]
39
44
  request_body: dict[str, Any]
40
45
  summary: str
41
46
  description: str
47
+ operation_id: str | None = (
48
+ None # operationId MUST be unique within the spec, so no default ""
49
+ )
42
50
 
43
51
 
44
52
  @dataclass
@@ -59,7 +67,7 @@ def get_path_items(paths: dict[str, Any]) -> Generator[OperationDetails, None, N
59
67
  operation_details = OperationDetails(
60
68
  path=path,
61
69
  method=method,
62
- operation_id=method_item["operationId"],
70
+ operation_id=method_item.get("operationId", None),
63
71
  parameters=method_item.get("parameters", []),
64
72
  request_body=method_item.get("requestBody", {}),
65
73
  summary=method_item.get("summary"),
@@ -108,10 +116,14 @@ def get_keyword_signature(operation_details: OperationDetails) -> str:
108
116
  keyword_name = remove_unsafe_characters_from_string(
109
117
  operation_details.summary
110
118
  ).lower()
111
- else:
119
+ elif operation_details.operation_id:
112
120
  keyword_name = remove_unsafe_characters_from_string(
113
121
  operation_details.operation_id
114
122
  ).lower()
123
+ else:
124
+ keyword_name = remove_unsafe_characters_from_string(
125
+ f"{operation_details.method}_{operation_details.path}"
126
+ )
115
127
 
116
128
  parameters = get_parameter_details(operation_details=operation_details)
117
129
  path_parameters = [p for p in parameters if p.type == "path"]
@@ -198,6 +210,9 @@ def get_keyword_signature(operation_details: OperationDetails) -> str:
198
210
  argument = f", {safe_name}: {annotation}"
199
211
  arguments += argument
200
212
 
213
+ if arguments:
214
+ arguments += ", omit_parameters: Iterable[str] = frozenset()"
215
+
201
216
  return SIGNATURE_TEMPLATE.format(keyword_name=keyword_name, arguments=arguments)
202
217
 
203
218
 
@@ -1,5 +1,5 @@
1
1
  # pyright: reportArgumentType=false
2
- from typing import Any
2
+ from typing import Any, Iterable
3
3
 
4
4
  from requests import Response
5
5
  from robot.api.deco import keyword, library
@@ -1,6 +0,0 @@
1
- # OpenApiTools for Robot Framework
2
-
3
- OpenApiTools is a set of libraries centered around the OpenAPI Specification:
4
-
5
- - [OpenApiDriver](./driver.md)
6
- - [OpenApiLibCore](./libcore.md)