robotframework-openapitools 1.0.0b5__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 +25 -1
- OpenApiDriver/openapidriver.libspec +183 -80
- OpenApiDriver/openapidriver.py +8 -279
- OpenApiLibCore/__init__.py +26 -0
- OpenApiLibCore/openapi_libcore.libspec +229 -121
- OpenApiLibCore/openapi_libcore.py +10 -248
- openapi_libgen/__init__.py +3 -0
- openapi_libgen/generator.py +0 -2
- 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.0b5.dist-info → robotframework_openapitools-1.0.1.dist-info}/METADATA +16 -98
- {robotframework_openapitools-1.0.0b5.dist-info → robotframework_openapitools-1.0.1.dist-info}/RECORD +18 -14
- {robotframework_openapitools-1.0.0b5.dist-info → robotframework_openapitools-1.0.1.dist-info}/LICENSE +0 -0
- {robotframework_openapitools-1.0.0b5.dist-info → robotframework_openapitools-1.0.1.dist-info}/WHEEL +0 -0
- {robotframework_openapitools-1.0.0b5.dist-info → robotframework_openapitools-1.0.1.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,891 @@
|
|
1
|
+
GENERAL_INTRODUCTION = """
|
2
|
+
My RoboCon 2022 talk about OpenApiDriver and OpenApiLibCore can be found
|
3
|
+
<a href="https://www.youtube.com/watch?v=7YWZEHxk9Ps" target="_blank">here</a>.
|
4
|
+
|
5
|
+
For more information about Robot Framework, see http://robotframework.org.
|
6
|
+
|
7
|
+
<hr>
|
8
|
+
|
9
|
+
<h2>Installation</h2>
|
10
|
+
|
11
|
+
If you already have Python >= 3.10 with pip installed, you can simply run:
|
12
|
+
|
13
|
+
<div class="code-block"><pre><code class="language-shell">pip install --upgrade robotframework-openapitools</code></pre></div>
|
14
|
+
|
15
|
+
<hr>
|
16
|
+
|
17
|
+
<h2>OpenAPI (aka Swagger)</h2>
|
18
|
+
|
19
|
+
The OpenAPI Specification (OAS) defines a standard, language-agnostic interface
|
20
|
+
to RESTful APIs, see https://www.openapis.org/
|
21
|
+
|
22
|
+
The OpenApiTools libraries implement a number of Robot Framework keywords that make it
|
23
|
+
easy to interact with an OpenAPI implementation by using the information in the
|
24
|
+
OpenAPI document (previously: Swagger file), for examply by automatic generation of
|
25
|
+
valid values for requests based on the schema information in the document.
|
26
|
+
|
27
|
+
<blockquote><i>
|
28
|
+
Note: The OpenApiTools libraries are designed for APIs based on the OAS v3.
|
29
|
+
The libraries have not been tested for APIs based on the OAS v2.
|
30
|
+
</i></blockquote>
|
31
|
+
|
32
|
+
<hr>
|
33
|
+
|
34
|
+
<h2>Getting started</h2>
|
35
|
+
|
36
|
+
Before trying to use the keywords exposed by any of the libraries from OpenApiTools on
|
37
|
+
the target API, it's recommended to first ensure that the OpenAPI document for the API
|
38
|
+
is valid under the OpenAPI Specification.
|
39
|
+
|
40
|
+
This can be done using the command line interface of the <code>prance</code> package
|
41
|
+
that is installed as a prerequisite for OpenApiTools.
|
42
|
+
Both a local openapi.json or openapi.yaml file or one hosted by the API server
|
43
|
+
can be checked using the <code>prance validate <reference_to_file></code> shell command:
|
44
|
+
|
45
|
+
<div class="code-block"><pre><code class="language-shell">
|
46
|
+
(.venv) prance validate --backend=openapi-spec-validator http://localhost:8000/openapi.json
|
47
|
+
|
48
|
+
Processing "http://localhost:8000/openapi.json"...
|
49
|
+
-> Resolving external references.
|
50
|
+
Validates OK as OpenAPI 3.0.2!
|
51
|
+
|
52
|
+
(.venv) prance validate --backend=openapi-spec-validator /tests/files/petstore_openapi.yaml
|
53
|
+
|
54
|
+
Processing "/tests/files/petstore_openapi.yaml"...
|
55
|
+
-> Resolving external references.
|
56
|
+
Validates OK as OpenAPI 3.0.2!
|
57
|
+
|
58
|
+
</code></pre></div>
|
59
|
+
|
60
|
+
You'll have to change the url or file reference to the location of the openapi
|
61
|
+
document for your API.
|
62
|
+
|
63
|
+
<blockquote><i>
|
64
|
+
Note: Although recursion is technically allowed under the OAS, tool
|
65
|
+
support is limited and changing the OAS to not use recursion is recommended.
|
66
|
+
</i></blockquote>
|
67
|
+
|
68
|
+
Support for parsing OpenAPI documents with recursion in them is limited.
|
69
|
+
See the <code>recursion_limit</code> and <code>recursion_default</code> library
|
70
|
+
parameters for the available options.
|
71
|
+
|
72
|
+
If the OpenAPI document passes this validation, the next step is trying to do a test
|
73
|
+
run with a minimal test suite.
|
74
|
+
The examples below can be used with <code>source</code>, <code>origin</code>
|
75
|
+
and <code>path</code> altered to fit your situation.
|
76
|
+
|
77
|
+
<div class="code-block"><pre><code class="language-robotframework">
|
78
|
+
*** Settings ***
|
79
|
+
Library OpenApiDriver
|
80
|
+
... source=http://localhost:8000/openapi.json
|
81
|
+
... origin=http://localhost:8000
|
82
|
+
Test Template Validate Using Test Endpoint Keyword
|
83
|
+
|
84
|
+
*** Test Cases ***
|
85
|
+
Test Endpoint for ${method} on ${path} where ${status_code} is expected
|
86
|
+
|
87
|
+
*** Keywords ***
|
88
|
+
Validate Using Test Endpoint Keyword
|
89
|
+
[Arguments] ${path} ${method} ${status_code}
|
90
|
+
Test Endpoint
|
91
|
+
... path=${path} method=${method} status_code=${status_code}
|
92
|
+
|
93
|
+
</code></pre></div>
|
94
|
+
|
95
|
+
<div class="code-block"><pre><code class="language-robotframework">
|
96
|
+
*** Settings ***
|
97
|
+
Library OpenApiLibCore
|
98
|
+
... source=http://localhost:8000/openapi.json
|
99
|
+
... origin=http://localhost:8000
|
100
|
+
|
101
|
+
*** Test Cases ***
|
102
|
+
Getting Started
|
103
|
+
${url}= Get Valid Url path=/employees/{employee_id}
|
104
|
+
|
105
|
+
</code></pre></div>
|
106
|
+
|
107
|
+
Running the above tests for the first time may result in an error / failed test.
|
108
|
+
You should look at the Robot Framework <code>log.html</code> to determine the reasons
|
109
|
+
for the failing tests.
|
110
|
+
Depending on the reasons for the failures, different solutions are possible.
|
111
|
+
|
112
|
+
The OpenApiTools libraries also support handling of relations between resources within
|
113
|
+
the scope of the API being validated as well as handling dependencies on resources
|
114
|
+
outside the scope of the API. In addition there is support for handling restrictions
|
115
|
+
on the values of parameters and properties.
|
116
|
+
This is supported by the <code>mappings_path</code> library parameter.
|
117
|
+
Details about the <code>mappings_path</code> parameter usage can be
|
118
|
+
found under the Advanced Use tab.
|
119
|
+
|
120
|
+
<hr>
|
121
|
+
|
122
|
+
<h2>Limitations</h2>
|
123
|
+
|
124
|
+
There are currently a number of limitations to supported API structures, supported data
|
125
|
+
types and properties.
|
126
|
+
The following list details the most important ones.
|
127
|
+
- Only JSON request and response bodies are supported.
|
128
|
+
- No support for per-path authorization levels (only simple 401 / 403 validation).
|
129
|
+
|
130
|
+
For all open issues please visit the OpenApiTools <a href="https://github.com/MarketSquare/robotframework-openapitools" target="_blank">GitHub page</a>.
|
131
|
+
"""
|
132
|
+
|
133
|
+
SHARED_PARAMETERS = """
|
134
|
+
<h2>Base parameters</h2>
|
135
|
+
|
136
|
+
<h3>source</h3>
|
137
|
+
An absolute path to an openapi.json or openapi.yaml file or an url to such a file.
|
138
|
+
|
139
|
+
<h3>origin</h3>
|
140
|
+
The server (and port) of the target server. E.g. <code>https://localhost:8000</code>
|
141
|
+
|
142
|
+
<h3>base_path</h3>
|
143
|
+
The routing between <code>origin</code> and the paths as found in the <code>paths</code>
|
144
|
+
section in the openapi document.
|
145
|
+
E.g. <code>/petshop/v2</code>.
|
146
|
+
|
147
|
+
<h2>Test case execution</h2>
|
148
|
+
|
149
|
+
<h3>response_validation</h3>
|
150
|
+
By default, a <code>WARN</code> is logged when the Response received after a Request
|
151
|
+
does not comply with the schema as defined in the OpenAPI document for the given operation.
|
152
|
+
The following values are supported:
|
153
|
+
<ul>
|
154
|
+
<li><code>DISABLED</code>: All Response validation errors will be ignored</li>
|
155
|
+
<li><code>INFO</code>: Any Response validation erros will be logged at <code>INFO</code> level</li>
|
156
|
+
<li><code>WARN</code>: Any Response validation erros will be logged at <code>WARN</code> level</li>
|
157
|
+
<li><code>STRICT</code>: The Test Case will fail on any Response validation errors</li>
|
158
|
+
</ul>
|
159
|
+
|
160
|
+
<h3>disable_server_validation</h3>
|
161
|
+
If enabled by setting this parameter to <code class="language-robotframework">${TRUE}</code>,
|
162
|
+
the Response validation will also include possible errors for Requests made to a server
|
163
|
+
address that is not defined in the list of servers in the OpenAPI document.
|
164
|
+
This generally means that if there is a mismatch, every Test Case will raise this error.
|
165
|
+
Note that <code>localhost</code> and <code>127.0.0.1</code> are not considered the same
|
166
|
+
by Response validation.
|
167
|
+
|
168
|
+
<h2>API-specific configurations</h2>
|
169
|
+
|
170
|
+
<h3>mappings_path</h3>
|
171
|
+
See the Advanced Use tab for an in-depth explanation.
|
172
|
+
|
173
|
+
<h3>invalid_property_default_response</h3>
|
174
|
+
The default response code for requests with a JSON body that does not comply
|
175
|
+
with the schema.
|
176
|
+
Example: a value outside the specified range or a string value
|
177
|
+
for a property defined as integer in the schema.
|
178
|
+
|
179
|
+
<h3>default_id_property_name</h3>
|
180
|
+
The default name for the property that identifies a resource (i.e. a unique
|
181
|
+
entity) within the API.
|
182
|
+
The default value for this property name is <code>id</code>.
|
183
|
+
If the target API uses a different name for all the resources within the API,
|
184
|
+
you can configure it globally using this property.
|
185
|
+
|
186
|
+
If different property names are used for the unique identifier for different
|
187
|
+
types of resources, an <code>ID_MAPPING</code> can be implemented using the <code>mappings_path</code>.
|
188
|
+
|
189
|
+
<h3>faker_locale</h3>
|
190
|
+
A locale string or list of locale strings to pass to the Faker library to be
|
191
|
+
used in generation of string data for supported format types.
|
192
|
+
|
193
|
+
<h3>require_body_for_invalid_url</h3>
|
194
|
+
When a request is made against an invalid url, this usually is because of a "404" request;
|
195
|
+
a request for a resource that does not exist. Depending on API implementation, when a
|
196
|
+
request with a missing or invalid request body is made on a non-existent resource,
|
197
|
+
either a 404 or a 422 or 400 Response is normally returned. If the API being tested
|
198
|
+
processes the request body before checking if the requested resource exists, set
|
199
|
+
this parameter to True.
|
200
|
+
|
201
|
+
<h2>Parsing parameters</h2>
|
202
|
+
|
203
|
+
<h3>recursion_limit</h3>
|
204
|
+
The recursion depth to which to fully parse recursive references before the
|
205
|
+
<code>recursion_default</code> is used to end the recursion.
|
206
|
+
|
207
|
+
<h3>recursion_default</h3>
|
208
|
+
The value that is used instead of the referenced schema when the
|
209
|
+
<code>recursion_limit</code> has been reached.
|
210
|
+
The default <code class="language-python">{}</code> represents an empty object in JSON.
|
211
|
+
Depending on schema definitions, this may cause schema validation errors.
|
212
|
+
If this is the case <code class="language-robotframework">${NONE}</code> or an empty list
|
213
|
+
can be tried as an alternative.
|
214
|
+
|
215
|
+
<h2>Security-related parameters</h2>
|
216
|
+
<blockquote><i>Note: these parameters are equivalent to those in the <code>requests</code> library.</i></blockquote>
|
217
|
+
|
218
|
+
<h3>username</h3>
|
219
|
+
The username to be used for Basic Authentication.
|
220
|
+
|
221
|
+
<h3>password</h3>
|
222
|
+
The password to be used for Basic Authentication.
|
223
|
+
|
224
|
+
<h3>security_token</h3>
|
225
|
+
The token to be used for token based security using the <code>Authorization</code> header.
|
226
|
+
|
227
|
+
<h3>auth</h3>
|
228
|
+
A <a href="https://requests.readthedocs.io/en/latest/api/#authentication" target="_blank">requests <code>AuthBase</code> instance</a> to be used for authentication instead of the <code>username</code> and <code>password</code>.
|
229
|
+
|
230
|
+
<h3>cert</h3>
|
231
|
+
The SSL certificate to use with all requests.
|
232
|
+
If string: the path to ssl client cert file (<code>.pem</code>).
|
233
|
+
If tuple: the <code class="language-python">("cert", "key")</code> pair.
|
234
|
+
|
235
|
+
<h3>verify_tls</h3>
|
236
|
+
Whether or not to verify the TLS / SSL certificate of the server.
|
237
|
+
If boolean: whether or not to verify the server TLS certificate.
|
238
|
+
If string: path to a CA bundle to use for verification.
|
239
|
+
|
240
|
+
<h3>extra_headers</h3>
|
241
|
+
A dictionary with extra / custom headers that will be send with every request.
|
242
|
+
This parameter can be used to send headers that are not documented in the
|
243
|
+
openapi document or to provide an API-key.
|
244
|
+
|
245
|
+
<h3>cookies</h3>
|
246
|
+
A dictionary or
|
247
|
+
<a href="https://docs.python.org/3/library/http.cookiejar.html#http.cookiejar.CookieJar" target="_blank"><code>CookieJar</code> object</a>
|
248
|
+
to send with all requests.
|
249
|
+
|
250
|
+
<h3>proxies</h3>
|
251
|
+
A dictionary of <code>"protocol": "proxy url"</code> to use for all requests.
|
252
|
+
"""
|
253
|
+
|
254
|
+
OPENAPILIBCORE_DOCUMENTATION = """
|
255
|
+
<h1>OpenApiLibCore for Robot Framework</h1>
|
256
|
+
|
257
|
+
The OpenApiLibCore library is a utility library that is meant to simplify creation
|
258
|
+
of other Robot Framework libraries for API testing based on the information in
|
259
|
+
an OpenAPI document.
|
260
|
+
Both OpenApiDriver and libraries generated using OpenApiLibGen rely on OpenApiLibCore
|
261
|
+
and its keywords for their functionality.
|
262
|
+
This document explains how to use the OpenApiLibCore library.
|
263
|
+
|
264
|
+
<blockquote>
|
265
|
+
Keyword documentation for OpenApiLibCore can be found <a href="./openapi_libcore.html" target="_blank">here</a>.
|
266
|
+
</blockquote>
|
267
|
+
|
268
|
+
<h2>Introduction</h2>
|
269
|
+
{general_introduction}
|
270
|
+
|
271
|
+
""".format(general_introduction=GENERAL_INTRODUCTION)
|
272
|
+
|
273
|
+
OPENAPILIBCORE_LIBRARY_DOCSTRING = """
|
274
|
+
The OpenApiLibCore library provides the keywords and core logic to interact with an OpenAPI server.
|
275
|
+
|
276
|
+
Visit the <a href="./index.html" target="_blank">OpenApiTools documentation</a> for an introduction.
|
277
|
+
"""
|
278
|
+
|
279
|
+
OPENAPILIBCORE_INIT_DOCSTRING = SHARED_PARAMETERS
|
280
|
+
|
281
|
+
OPENAPIDRIVER_DOCUMENTATION = """
|
282
|
+
<h1>OpenApiDriver for Robot Framework</h1>
|
283
|
+
|
284
|
+
OpenApiDriver is an extension of the Robot Framework DataDriver library that allows
|
285
|
+
for generation and execution of test cases based on the information in an OpenAPI document.
|
286
|
+
|
287
|
+
For more information about the DataDriver library, see
|
288
|
+
https://github.com/Snooz82/robotframework-datadriver.
|
289
|
+
|
290
|
+
<blockquote>
|
291
|
+
Keyword documentation for OpenApiDriver can be found <a href="./openapidriver.html" target="_blank">here</a>.
|
292
|
+
</blockquote>
|
293
|
+
|
294
|
+
<h2>Introduction</h2>
|
295
|
+
{general_introduction}
|
296
|
+
|
297
|
+
""".format(general_introduction=GENERAL_INTRODUCTION)
|
298
|
+
|
299
|
+
OPENAPIDRIVER_LIBRARY_DOCSTRING = """
|
300
|
+
The OpenApiDriver library provides the keywords and logic for execution of generated test cases based on an OpenAPI document.
|
301
|
+
|
302
|
+
Visit the <a href="./index.html" target="_blank">OpenApiTools documentation</a> for an introduction.
|
303
|
+
"""
|
304
|
+
|
305
|
+
OPENAPIDRIVER_INIT_DOCSTRING = """
|
306
|
+
<h2>Test case generation and execution</h2>
|
307
|
+
|
308
|
+
<h3>included_paths</h3>
|
309
|
+
A list of paths that will be included when generating the test cases.
|
310
|
+
The <code>*</code> character can be used at the end of a partial path to include all paths
|
311
|
+
starting with the partial path (wildcard include).
|
312
|
+
|
313
|
+
<h3>ignored_paths</h3>
|
314
|
+
A list of paths that will be ignored when generating the test cases.
|
315
|
+
The <code>*</code> character can be used at the end of a partial path to ignore all paths
|
316
|
+
starting with the partial path (wildcard ignore).
|
317
|
+
|
318
|
+
<h3>ignored_responses</h3>
|
319
|
+
A list of responses that will be ignored when generating the test cases.
|
320
|
+
|
321
|
+
<h3>ignored_testcases</h3>
|
322
|
+
A list of specific test cases that, if it would be generated, will be ignored.
|
323
|
+
Specific test cases to ignore must be specified as a <code>tuple</code> or
|
324
|
+
<code>list</code> of <code>path</code>, <code>method</code> and <code>response</code>.
|
325
|
+
|
326
|
+
{shared_parameters}
|
327
|
+
""".format(shared_parameters=SHARED_PARAMETERS)
|
328
|
+
|
329
|
+
OPENAPILIBGEN_DOCUMENTATION = """
|
330
|
+
<h1>OpenApiLibGen for Robot Framework</h1>
|
331
|
+
|
332
|
+
OpenApiLibGen is a command-line tool that can be used to generate a Robot Framework
|
333
|
+
library based on an OpenAPI document.
|
334
|
+
|
335
|
+
<hr>
|
336
|
+
|
337
|
+
In a (virtual) environment where <code>robotframework-openapitools</code> is installed
|
338
|
+
the <code>generate-library</code> shell command is availabe:
|
339
|
+
|
340
|
+
<div class="code-block"><pre><code class="language-shell">
|
341
|
+
$ generate-library -h
|
342
|
+
usage: openapi_libgen [-h] [-s SOURCE] [-d DESTINATION] [-n NAME] [--recursion-limit RECURSION_LIMIT]
|
343
|
+
[--recursion-default RECURSION_DEFAULT]
|
344
|
+
|
345
|
+
The OpenApiTools library generator
|
346
|
+
|
347
|
+
options:
|
348
|
+
-h, --help show this help message and exit
|
349
|
+
-s SOURCE, --source SOURCE
|
350
|
+
-d DESTINATION, --destination DESTINATION
|
351
|
+
-n NAME, --name NAME
|
352
|
+
--recursion-limit RECURSION_LIMIT
|
353
|
+
--recursion-default RECURSION_DEFAULT
|
354
|
+
|
355
|
+
Inspired by roboswag. Thank you Bartlomiej and Mateusz for your work!
|
356
|
+
</code></pre></div>
|
357
|
+
|
358
|
+
Generation of your library is an interactive process with a few steps:
|
359
|
+
|
360
|
+
<div class="code-block"><pre><code class="language-shell">
|
361
|
+
$ generate-library
|
362
|
+
Please provide a source for the generation:
|
363
|
+
$ http://127.0.0.1:8000/openapi.json
|
364
|
+
Please provide a path to where the library will be generated:
|
365
|
+
$ ./generated
|
366
|
+
Please provide a name for the library [default: FastAPI]:
|
367
|
+
$ My Generated Library
|
368
|
+
generated/MyGeneratedLibrary/__init__.py created
|
369
|
+
generated/MyGeneratedLibrary/my_generated_library.py created
|
370
|
+
</code></pre></div>
|
371
|
+
|
372
|
+
<div><codeblock>
|
373
|
+
Tip: The name for the library that you provide (or that's taken from the OpenAPI document)
|
374
|
+
will be transformed into a Python-legal module name and a Python-legal class name.
|
375
|
+
This means special characters like <code>-</code> will be removed or replaced.
|
376
|
+
If you used spaces in the desired name, they will be removed from the Library (class) name
|
377
|
+
and the next character will be capitalized while the spaces will be replaced with an
|
378
|
+
underscore in the module name as can be seen in the example above.
|
379
|
+
</codeblock></div>
|
380
|
+
|
381
|
+
<div><pre><codeblock><i>
|
382
|
+
Note: There's currently 2 environment variables that are taken into account by the generator:
|
383
|
+
<code>USE_SUMMARY_AS_KEYWORD_NAME</code> can result in nicer keyword names (but: uniqueness is not guaranteed, so you might need to rename some of the generated keywords manually)
|
384
|
+
<code>EXPAND_BODY_ARGUMENTS</code> changes how body arguments are provided to keywords (either a single body dict or key-value pairs for the body parameters)
|
385
|
+
</i><pre></codeblock></div>
|
386
|
+
|
387
|
+
<hr>
|
388
|
+
|
389
|
+
If the location where the library is located is in your Python search path, you'll be able
|
390
|
+
to use it like a regular Robot Framework library (in fact, it's a drop-in replacement for
|
391
|
+
OpenApiLibCore).
|
392
|
+
|
393
|
+
The generated library has a keyword for every endpoint in the OpenAPI document used to generated it.
|
394
|
+
In addition, all the keywords from OpenApiLibCore are available.
|
395
|
+
<blockquote>
|
396
|
+
Keyword documentation for OpenApiLibCore can be found <a href="./openapi_libcore.html" target="_blank">here</a>.
|
397
|
+
</blockquote>
|
398
|
+
|
399
|
+
The generated library can be used as shown below:
|
400
|
+
|
401
|
+
<div class="code-block"><pre><code class="language-robotframework">
|
402
|
+
*** Settings ***
|
403
|
+
Library MyGeneratedLibrary
|
404
|
+
... source=${ORIGIN}/openapi.json
|
405
|
+
... origin=${ORIGIN}
|
406
|
+
... base_path=${EMPTY}
|
407
|
+
... mappings_path=${ROOT}/tests/user_implemented/custom_user_mappings.py
|
408
|
+
Variables ${ROOT}/tests/variables.py
|
409
|
+
|
410
|
+
|
411
|
+
*** Variables ***
|
412
|
+
${ORIGIN}= http://localhost:8000
|
413
|
+
|
414
|
+
|
415
|
+
*** Test Cases ***
|
416
|
+
Test Generated Keywords: Get Employees
|
417
|
+
${response}= Get Employees
|
418
|
+
Should Be Equal As Integers ${response.status_code} 200
|
419
|
+
|
420
|
+
Test Generated Keywords: Post Employee
|
421
|
+
VAR &{body} name=Robin the Robot
|
422
|
+
${response}= Post Employee body=${body}
|
423
|
+
# ${response}= Post Employee name=Robin the Robot
|
424
|
+
Should Be Equal As Integers ${response.status_code} 201
|
425
|
+
Should Be Equal ${response.json()}[name] Robin the Robot
|
426
|
+
|
427
|
+
Test Generated Keywords: Get Employee
|
428
|
+
${response}= Get Employee
|
429
|
+
Should Be Equal As Integers ${response.status_code} 200
|
430
|
+
|
431
|
+
Test Generated Keywords: Patch Employee
|
432
|
+
${employee_id}= Get Valid Id For Path path=/employees/{employee_id}
|
433
|
+
VAR &{body} date_of_birth=2021-12-31
|
434
|
+
${response}= Patch Employee employee_id=${employee_id} body=${body}
|
435
|
+
# ${response}= Patch Employee employee_id=${employee_id} date_of_birth=2021-12-31
|
436
|
+
Should Be Equal As Integers ${response.status_code} 403
|
437
|
+
|
438
|
+
Test Generated Keywords: Post WageGroup
|
439
|
+
VAR &{body} hourly_rate=99.99
|
440
|
+
${response}= Post Wagegroup body=${body}
|
441
|
+
# ${response}= Post Wagegroup hourly_rate=99.99
|
442
|
+
Should Be Equal As Integers ${response.status_code} 201
|
443
|
+
Should Be Equal As Numbers ${response.json()}[hourly-rate] 99.99
|
444
|
+
|
445
|
+
Test Generated Keywords: Get Energy Label
|
446
|
+
${response}= Get Energy Label
|
447
|
+
... zipcode=1111AA
|
448
|
+
... home_number=10
|
449
|
+
... extension=too long to be acceptable
|
450
|
+
... validate_against_schema=${FALSE}
|
451
|
+
Should Be Equal As Integers ${response.status_code} 422
|
452
|
+
|
453
|
+
VAR @{omit} extension
|
454
|
+
${response}= Get Energy Label
|
455
|
+
... zipcode=1111AA
|
456
|
+
... home_number=10
|
457
|
+
... extension=too long to be acceptable
|
458
|
+
... omit_parameters=${omit}
|
459
|
+
Should Be Equal As Integers ${response.status_code} 200
|
460
|
+
</code></pre></div>
|
461
|
+
"""
|
462
|
+
|
463
|
+
ADVANCED_USE_DOCUMENTATION = """
|
464
|
+
<h1>Advanced use scenario's: using the mappings_path</h1>
|
465
|
+
|
466
|
+
<h2>Introduction</h2>
|
467
|
+
When working with APIs, there are often relations between resources or constraints on values.
|
468
|
+
The property on one resource may refer to the <code>id</code> of another resource.
|
469
|
+
The value for a certain property may have to be unique within a certain scope.
|
470
|
+
Perhaps an url contains parameters that must match values that are defined outside the API itself.
|
471
|
+
|
472
|
+
These types of relations and limitations cannot be described / modeled within the openapi document.
|
473
|
+
To support automatic validation of API endpoints where such relations apply, OpenApiLibCore supports the usage of a custom mappings file.
|
474
|
+
|
475
|
+
<h2>Taking a custom mappings file into use</h2>
|
476
|
+
To take a custom mappings file into use, the absolute path to it has to be passed to OpenApiLibCore as the <code>mappings_path</code> parameter:
|
477
|
+
|
478
|
+
<div class="code-block"><pre><code class="language-robotframework">
|
479
|
+
*** Settings ***
|
480
|
+
Library OpenApiLibCore
|
481
|
+
... source=http://localhost:8000/openapi.json
|
482
|
+
... origin=http://localhost:8000
|
483
|
+
... mappings_path=${ROOT}/tests/custom_user_mappings.py
|
484
|
+
...
|
485
|
+
|
486
|
+
</code></pre></div>
|
487
|
+
|
488
|
+
<blockquote><i>
|
489
|
+
Note: An absolute path is required.
|
490
|
+
In the example above, <code>${ROOT}</code> is a global variable that holds the absolute path to the repository root.
|
491
|
+
</i></blockquote>
|
492
|
+
|
493
|
+
<h2>The custom mappings file</h2>
|
494
|
+
Just like custom Robot Framework libraries, the mappings file has to be implemented in Python.
|
495
|
+
Since this Python file is imported by the OpenApiLibCore, it has to follow a fixed format (more technically, implement a certain interface).
|
496
|
+
The (almost) bare minimum implementation of a mappings.py file looks like this:
|
497
|
+
|
498
|
+
<div class="code-block"><pre><code class="language-python">
|
499
|
+
from OpenApiLibCore import (
|
500
|
+
IGNORE,
|
501
|
+
Dto,
|
502
|
+
IdDependency,
|
503
|
+
IdReference,
|
504
|
+
PathPropertiesConstraint,
|
505
|
+
PropertyValueConstraint,
|
506
|
+
UniquePropertyValueConstraint,
|
507
|
+
)
|
508
|
+
|
509
|
+
|
510
|
+
ID_MAPPING = {
|
511
|
+
"/myspecialpath", "special_thing_id",
|
512
|
+
}
|
513
|
+
|
514
|
+
|
515
|
+
class MyDtoThatDoesNothing(Dto):
|
516
|
+
@staticmethod
|
517
|
+
def get_relations():
|
518
|
+
relations = []
|
519
|
+
return relations
|
520
|
+
|
521
|
+
@staticmethod
|
522
|
+
def get_path_relations():
|
523
|
+
relations = []
|
524
|
+
return relations
|
525
|
+
|
526
|
+
@staticmethod
|
527
|
+
def get_parameter_relations():
|
528
|
+
relations = []
|
529
|
+
return relations
|
530
|
+
|
531
|
+
|
532
|
+
DTO_MAPPING = {
|
533
|
+
("/myspecialpath", "post"): MyDtoThatDoesNothing
|
534
|
+
}
|
535
|
+
|
536
|
+
|
537
|
+
PATH_MAPPING = {
|
538
|
+
"/mypathwithexternalid/{external_id}": MyDtoThatDoesNothing
|
539
|
+
}
|
540
|
+
|
541
|
+
</code></pre></div>
|
542
|
+
|
543
|
+
There are 5 main parts in this mappings file:
|
544
|
+
|
545
|
+
<ol type="1">
|
546
|
+
<li>The import section.
|
547
|
+
Here the classes needed to implement custom mappings are imported.
|
548
|
+
This section can just be copied without changes.</li>
|
549
|
+
<li>The <code class="language-python">ID_MAPPING</code> "constant" definition / assignment.</li>
|
550
|
+
<li>The section defining the mapping Dtos. More on this later.</li>
|
551
|
+
<li>The <code class="language-python">DTO_MAPPING</code> "constant" definition / assignment.</li>
|
552
|
+
<li>The <code class="language-python">PATH_MAPPING</code> "constant" definition / assignment.</li>
|
553
|
+
</ol>
|
554
|
+
|
555
|
+
<h2>The ID_MAPPING, DTO_MAPPING and PATH_MAPPING</h2>
|
556
|
+
When a custom mappings file is used, the OpenApiLibCore will attempt to import it and then import <code>DTO_MAPPING</code>, <code>PATH_MAPPING</code> and <code>ID_MAPPING</code> from it.
|
557
|
+
For this reason, the exact same name must be used in a custom mappings file (capitilization matters).
|
558
|
+
|
559
|
+
<h3>The ID_MAPPING</h3>
|
560
|
+
The <code>ID_MAPPING</code> is a dictionary with a string as its key and either a string or a tuple of string and a callable as its value. The callable must take a string as its argument and return a string.
|
561
|
+
|
562
|
+
The <code>ID_MAPPING</code> is used to specify what the unique identifier property of a resource at the given path is, if different from the <code>default_id_property_name</code> (see library parameters).
|
563
|
+
|
564
|
+
In some situations, the identifier of the resource is not url-safe (e.g. containing a <code class="language-python">/</code>).
|
565
|
+
To support this type of resource identifier, a transformer can be provided:
|
566
|
+
|
567
|
+
<div class="code-block"><pre><code class="language-python">
|
568
|
+
def my_transformer(identifier_name: str) -> str:
|
569
|
+
return identifier_name.replace("/", "_")
|
570
|
+
|
571
|
+
|
572
|
+
ID_MAPPING = {
|
573
|
+
"/myspecialpath": ("special_thing_id", my_transformer),
|
574
|
+
}
|
575
|
+
|
576
|
+
</code></pre></div>
|
577
|
+
|
578
|
+
<h3>The DTO_MAPPING</h3>
|
579
|
+
The <code>DTO_MAPPING</code> is a dictionary with a tuple as its key and a mappings Dto as its value.
|
580
|
+
The tuple must be in the form <code class="language-python">("path_from_the_paths_section", "method_supported_by_the_path")</code>.
|
581
|
+
The <code class="language-python">path_from_the_paths_section</code> must be exactly as found in the openapi document.
|
582
|
+
The <code class="language-python">method_supported_by_the_path</code> must be one of the methods supported by the path and must be in lowercase.
|
583
|
+
|
584
|
+
<h3>The PATH_MAPPING</h3>
|
585
|
+
The <code>PATH_MAPPING</code> is a dictionary with a <code>"path_from_the_paths_section"</code> as its key and a mappings Dto as its value.
|
586
|
+
The <code>path_from_the_paths_section</code> must be exactly as found in the openapi document.
|
587
|
+
|
588
|
+
|
589
|
+
<h2>Dto mapping classes</h2>
|
590
|
+
As can be seen from the import section above, a number of classes are available to deal with relations between resources and / or constraints on properties.
|
591
|
+
Each of these classes is designed to handle a relation or constraint commonly seen in REST APIs.
|
592
|
+
|
593
|
+
To explain the different mapping classes, we'll use the following example:
|
594
|
+
|
595
|
+
<blockquote>
|
596
|
+
Imagine we have an API path <code>/employees</code> where we can create (<code>post</code>) a new Employee resource.
|
597
|
+
The Employee has a number of required properties; name, employee_number, wagegroup_id, and date_of_birth.
|
598
|
+
|
599
|
+
There is also the the <code>/wagegroups</code> path where a Wagegroup resource can be created.
|
600
|
+
This Wagegroup also has a number of required properties: <code>name</code> and <code>hourly rate</code>.
|
601
|
+
</blockquote>
|
602
|
+
|
603
|
+
<hr>
|
604
|
+
|
605
|
+
<h3><code>IdReference</code></h3>
|
606
|
+
<blockquote><u>The value for this propery must the the <code>id</code> of another resource</u></blockquote>
|
607
|
+
|
608
|
+
To add an Employee, a <code>wagegroup_id</code> is required, the <code>id</code> of a Wagegroup resource that is already present in the system.
|
609
|
+
|
610
|
+
Since a typical REST API generates this <code>id</code> for a new resource and returns that <code>id</code> as part of the <code>post</code> response, the required <code>wagegroup_id</code> can be obtained by posting a new Wagegroup.
|
611
|
+
This relation can be implemented as follows:
|
612
|
+
|
613
|
+
<div class="code-block"><pre><code class="language-python">
|
614
|
+
class EmployeeDto(Dto):
|
615
|
+
@staticmethod
|
616
|
+
def get_relations():
|
617
|
+
relations = [
|
618
|
+
IdDependency(
|
619
|
+
property_name="wagegroup_id",
|
620
|
+
get_path="/wagegroups",
|
621
|
+
error_code=451,
|
622
|
+
),
|
623
|
+
]
|
624
|
+
return relations
|
625
|
+
|
626
|
+
DTO_MAPPING = {
|
627
|
+
("/employees", "post"): EmployeeDto
|
628
|
+
}
|
629
|
+
|
630
|
+
</code></pre></div>
|
631
|
+
|
632
|
+
Notice that the <code>get_path</code> of the <code>IdDependency</code> is not named <code>post_path</code> as one might expect.
|
633
|
+
This is deliberate for two reasons:
|
634
|
+
|
635
|
+
1. The purpose is getting an <code>id</code>
|
636
|
+
2. If the <code>post</code> operation is not supported on the provided path, a <code>get</code> operation is performed instead.
|
637
|
+
It is assumed that such a <code>get</code> will yield a list of resources and that each of these resources has an <code>id</code> that is valid for the desired <code>post</code> operation.
|
638
|
+
|
639
|
+
Also note the <code>error_code</code> of the <code>IdDependency</code>.
|
640
|
+
If a <code>post</code> is attempted with a value for the <code>wagegroup_id</code> that does not exist, the API should return an <code>error_code</code> response.
|
641
|
+
This <code>error_code</code> should be described as one of the <code>responses</code> in the openapi document for the <code>post</code> operation of the <code>/employees</code> path.
|
642
|
+
|
643
|
+
<hr>
|
644
|
+
|
645
|
+
<h3><code>IdDependency</code></h3>
|
646
|
+
<blockquote><u>This resource may not be DELETED if another resource refers to it</u></blockquote>
|
647
|
+
|
648
|
+
If an Employee has been added to the system, this Employee refers to the <code>id</code> of a Wagegroup for its required <code>employee_number</code> property.
|
649
|
+
|
650
|
+
Now let's say there is also the <code>/wagegroups/${wagegroup_id}</code> path that supports the <code>delete</code> operation.
|
651
|
+
If the Wagegroup refered to the Employee would be deleted, the Employee would be left with an invalid reference for one of its required properties.
|
652
|
+
To prevent this, an API typically returns an <code>error_code</code> when such a <code>delete</code> operation is attempted on a resource that is refered to in this fashion.
|
653
|
+
This <code>error_code</code> should be described as one of the <code>responses</code> in the openapi document for the <code>delete</code> operation of the <code>/wagegroups/${wagegroup_id}</code> path.
|
654
|
+
|
655
|
+
To verify that the specified <code>error_code</code> indeed occurs when attempting to <code>delete</code> the Wagegroup, we can implement the following dependency:
|
656
|
+
|
657
|
+
<div class="code-block"><pre><code class="language-python">
|
658
|
+
class WagegroupDto(Dto):
|
659
|
+
@staticmethod
|
660
|
+
def get_relations():
|
661
|
+
relations = [
|
662
|
+
IdReference(
|
663
|
+
property_name="wagegroup_id",
|
664
|
+
post_path="/employees",
|
665
|
+
error_code=406,
|
666
|
+
),
|
667
|
+
]
|
668
|
+
return relations
|
669
|
+
|
670
|
+
DTO_MAPPING = {
|
671
|
+
("/wagegroups/{wagegroup_id}", "delete"): WagegroupDto
|
672
|
+
}
|
673
|
+
|
674
|
+
</code></pre></div>
|
675
|
+
|
676
|
+
<hr>
|
677
|
+
|
678
|
+
<h3><code>UniquePropertyValueConstraint</code></h3>
|
679
|
+
<blockquote><u>The value of this property must be unique within its scope</u></blockquote>
|
680
|
+
|
681
|
+
In a lot of systems, there is data that should be unique; an employee number, the email address of an employee, the domain name for the employee, etc.
|
682
|
+
Often those values are automatically generated based on other data, but for some data, an "available value" must be chosen by hand.
|
683
|
+
|
684
|
+
In our example, the required <code>employee_number</code> must be chosen from the "free" numbers.
|
685
|
+
When a number is chosen that is already in use, the API should return the <code>error_code</code> specified in the openapi document for the operation (typically <code>post</code>, <code>put</code> and <code>patch</code>) on the endpoint.
|
686
|
+
|
687
|
+
To verify that the specified <code>error_code</code> occurs when attempting to <code>post</code> an Employee with an <code>employee_number</code> that is already in use, we can implement the following dependency:
|
688
|
+
|
689
|
+
<div class="code-block"><pre><code class="language-python">
|
690
|
+
class EmployeeDto(Dto):
|
691
|
+
@staticmethod
|
692
|
+
def get_relations():
|
693
|
+
relations = [
|
694
|
+
UniquePropertyValueConstraint(
|
695
|
+
property_name="employee_number",
|
696
|
+
value=42,
|
697
|
+
error_code=422,
|
698
|
+
),
|
699
|
+
]
|
700
|
+
return relations
|
701
|
+
|
702
|
+
DTO_MAPPING = {
|
703
|
+
("/employees", "post"): EmployeeDto,
|
704
|
+
("/employees/${employee_id}", "put"): EmployeeDto,
|
705
|
+
("/employees/${employee_id}", "patch"): EmployeeDto,
|
706
|
+
}
|
707
|
+
|
708
|
+
</code></pre></div>
|
709
|
+
|
710
|
+
Note how this example reuses the <code>EmployeeDto</code> to model the uniqueness constraint for all the operations (<code>post</code>, <code>put</code> and <code>patch</code>) that all relate to the same <code>employee_number</code>.
|
711
|
+
|
712
|
+
<hr>
|
713
|
+
|
714
|
+
<h3><code>PropertyValueConstraint</code></h3>
|
715
|
+
<blockquote><u>Use one of these values for this property</u></blockquote>
|
716
|
+
|
717
|
+
The OpenApiLibCore uses the <code>type</code> information in the openapi document to generate random data of the correct type to perform the operations that need it.
|
718
|
+
While this works in many situations (e.g. a random <code>string</code> for a <code>name</code>), there can be additional restrictions to a value that cannot be specified in an openapi document.
|
719
|
+
|
720
|
+
In our example, the <code>date_of_birth</code> must be a string in a specific format, e.g. 1995-03-27.
|
721
|
+
This type of constraint can be modeled as follows:
|
722
|
+
|
723
|
+
<div class="code-block"><pre><code class="language-python">
|
724
|
+
class EmployeeDto(Dto):
|
725
|
+
@staticmethod
|
726
|
+
def get_relations():
|
727
|
+
relations = [
|
728
|
+
PropertyValueConstraint(
|
729
|
+
property_name="date_of_birth",
|
730
|
+
values=["1995-03-27", "1980-10-02"],
|
731
|
+
error_code=422,
|
732
|
+
),
|
733
|
+
]
|
734
|
+
return relations
|
735
|
+
|
736
|
+
DTO_MAPPING = {
|
737
|
+
("/employees", "post"): EmployeeDto,
|
738
|
+
("/employees/${employee_id}", "put"): EmployeeDto,
|
739
|
+
("/employees/${employee_id}", "patch"): EmployeeDto,
|
740
|
+
}
|
741
|
+
|
742
|
+
</code></pre></div>
|
743
|
+
|
744
|
+
Now in addition, there could also be the restriction that the Employee must be 18 years or older.
|
745
|
+
To support additional restrictions like these, the <code class="language-python">PropertyValueConstraint</code> supports two additional properties: <code class="language-python">error_value</code> and <code class="language-python">invalid_value_error_code</code>:
|
746
|
+
|
747
|
+
<div class="code-block"><pre><code class="language-python">
|
748
|
+
class EmployeeDto(Dto):
|
749
|
+
@staticmethod
|
750
|
+
def get_relations():
|
751
|
+
relations = [
|
752
|
+
PropertyValueConstraint(
|
753
|
+
property_name="date_of_birth",
|
754
|
+
values=["1995-03-27", "1980-10-02"],
|
755
|
+
error_code=422,
|
756
|
+
invalid_value="2020-02-20",
|
757
|
+
invalid_value_error_code=403,
|
758
|
+
),
|
759
|
+
]
|
760
|
+
return relations
|
761
|
+
|
762
|
+
DTO_MAPPING = {
|
763
|
+
("/employees", "post"): EmployeeDto,
|
764
|
+
("/employees/${employee_id}", "put"): EmployeeDto,
|
765
|
+
("/employees/${employee_id}", "patch"): EmployeeDto,
|
766
|
+
}
|
767
|
+
|
768
|
+
</code></pre></div>
|
769
|
+
|
770
|
+
So now if an incorrectly formatted date is send a 422 response is expected, but when <code>2020-02-20</code> is send the expected repsonse is 403.
|
771
|
+
|
772
|
+
In some API implementations, there may be a property that will always return a specific error code if it's value is not valid.
|
773
|
+
This means that sending e.g. an invalid type of value will not result in the default error code for the API (typically 422 or 400).
|
774
|
+
This situation can be handled by use of the special <code class="language-python">IGNORE</code> value (see below for other uses):
|
775
|
+
|
776
|
+
<div class="code-block"><pre><code class="language-python">
|
777
|
+
class EmployeeDto(Dto):
|
778
|
+
@staticmethod
|
779
|
+
def get_relations():
|
780
|
+
relations = [
|
781
|
+
PropertyValueConstraint(
|
782
|
+
property_name="date_of_birth",
|
783
|
+
values=["1995-03-27", "1980-10-02"],
|
784
|
+
error_code=403,
|
785
|
+
invalid_value=IGNORE,
|
786
|
+
invalid_value_error_code=422,
|
787
|
+
),
|
788
|
+
]
|
789
|
+
return relations
|
790
|
+
|
791
|
+
DTO_MAPPING = {
|
792
|
+
("/employees", "post"): EmployeeDto,
|
793
|
+
("/employees/${employee_id}", "put"): EmployeeDto,
|
794
|
+
("/employees/${employee_id}", "patch"): EmployeeDto,
|
795
|
+
}
|
796
|
+
|
797
|
+
</code></pre></div>
|
798
|
+
|
799
|
+
Note that while this configuration will prevent failing test cases generated by OpenApiDriver, it does not explicitly check for business logic errors anymore (younger than 18 in this example).
|
800
|
+
|
801
|
+
<hr>
|
802
|
+
|
803
|
+
<h3><code>PathPropertiesConstraint</code></h3>
|
804
|
+
<blockquote><u>Just use this for the <code>path</code></u></blockquote>
|
805
|
+
|
806
|
+
<blockquote><i>
|
807
|
+
Note: The <code class="language-python">PathPropertiesConstraint</code> is only applicable to the <code class="language-python">get_path_relations</code> in a <code class="language-python">Dto</code> and only the <code class="language-python">PATH_MAPPING</code> uses the <code class="language-python">get_path_relations</code>.
|
808
|
+
</i></blockquote>
|
809
|
+
|
810
|
+
To be able to automatically perform endpoint validations, the OpenApiLibCore has to construct the <code>url</code> for the resource from the <code>path</code> as found in the openapi document.
|
811
|
+
Often, such a <code>path</code> contains a reference to a resource id, e.g. <code class="language-python">"/employees/${employee_id}"</code>.
|
812
|
+
When such an <code>id</code> is needed, the OpenApiLibCore tries to obtain a valid <code>id</code> by taking these steps:
|
813
|
+
|
814
|
+
1. Attempt a <code>post</code> on the "parent path" and extract the <code>id</code> from the response.
|
815
|
+
In our example: perform a <code>post</code> request on the <code>/employees</code> path and get the <code>id</code> from the response.
|
816
|
+
2. If 1. fails, perform a <code>get</code> request on the <code>/employees</code> path. It is assumed that this will return a list of Employee objects with an <code>id</code>.
|
817
|
+
One item from the returned list is picked at rondom and its <code>id</code> is used.
|
818
|
+
|
819
|
+
This mechanism relies on the standard REST structure and patterns.
|
820
|
+
|
821
|
+
Unfortunately, this structure / pattern does not apply to every endpoint; not every path parameter refers to a resource id.
|
822
|
+
Imagine we want to extend the API from our example with an endpoint that returns all the Employees that have their birthday at a given date:
|
823
|
+
<code class="language-python">"/birthdays/${month}/${date}"</code>.
|
824
|
+
It should be clear that the OpenApiLibCore won't be able to acquire a valid <code>month</code> and <code>date</code>. The <code class="language-python">PathPropertiesConstraint</code> can be used in this case:
|
825
|
+
|
826
|
+
<div class="code-block"><pre><code class="language-python">
|
827
|
+
class BirthdaysDto(Dto):
|
828
|
+
@staticmethod
|
829
|
+
def get_path_relations():
|
830
|
+
relations = [
|
831
|
+
PathPropertiesConstraint(path="/birthdays/03/27"),
|
832
|
+
]
|
833
|
+
return relations
|
834
|
+
|
835
|
+
PATH_MAPPING = {
|
836
|
+
"/birthdays/{month}/{date}": BirthdaysDto
|
837
|
+
}
|
838
|
+
|
839
|
+
</code></pre></div>
|
840
|
+
|
841
|
+
<hr>
|
842
|
+
|
843
|
+
<h3><code>IGNORE</code></h3>
|
844
|
+
<blockquote><u>Never send this query parameter as part of a request</u></blockquote>
|
845
|
+
|
846
|
+
Some optional query parameters have a range of valid values that depend on one or more path parameters.
|
847
|
+
Since path parameters are part of an url, they cannot be optional or empty so to extend the path parameters with optional parameters, query parameters can be used.
|
848
|
+
|
849
|
+
To illustrate this, let's imagine an API where the energy label for a building can be requested: <code class="language-python">"/energylabel/${zipcode}/${home_number}"</code>.
|
850
|
+
Some addresses however have an address extension, e.g. 1234AB 42 <sup>2.C</sup>.
|
851
|
+
The extension may not be limited to a fixed pattern / range and if an address has an extension, in many cases the address without an extension part is invalid.
|
852
|
+
|
853
|
+
To prevent OpenApiLibCore from generating invalid combinations of path and query parameters in this type of endpoint, the <code class="language-python">IGNORE</code> special value can be used to ensure the related query parameter is never send in a request.
|
854
|
+
|
855
|
+
<div class="code-block"><pre><code class="language-python">
|
856
|
+
class EnergyLabelDto(Dto):
|
857
|
+
@staticmethod
|
858
|
+
def get_parameter_relations():
|
859
|
+
relations = [
|
860
|
+
PropertyValueConstraint(
|
861
|
+
property_name="address_extension",
|
862
|
+
values=[IGNORE],
|
863
|
+
error_code=422,
|
864
|
+
),
|
865
|
+
]
|
866
|
+
return relations
|
867
|
+
|
868
|
+
@staticmethod
|
869
|
+
def get_relations(:
|
870
|
+
relations = [
|
871
|
+
PathPropertiesConstraint(path="/energy_label/1111AA/10"),
|
872
|
+
]
|
873
|
+
return relations
|
874
|
+
|
875
|
+
DTO_MAPPING = {
|
876
|
+
("/energy_label/{zipcode}/{home_number}", "get"): EnergyLabelDto
|
877
|
+
}
|
878
|
+
|
879
|
+
</code></pre></div>
|
880
|
+
|
881
|
+
Note that in this example, the <code class="language-python">get_parameter_relations()</code> method is implemented.
|
882
|
+
This method works mostly the same as the <code class="language-python">get_relations()</code> method but applies to headers and query parameters.
|
883
|
+
|
884
|
+
<hr>
|
885
|
+
|
886
|
+
<h2>Type annotations</h2>
|
887
|
+
|
888
|
+
An additional import to support type annotations is also available: <code class="language-python">Relation</code>.
|
889
|
+
A fully typed example can be found
|
890
|
+
<a href="https://github.com/MarketSquare/robotframework-openapitools/blob/main/tests/user_implemented/custom_user_mappings.py" target="_blank">here</a>.
|
891
|
+
"""
|