Skip to content

Commit 0bdfa5b

Browse files
authored
Merge pull request #108 from MarketSquare/98_add_keywords_to_simplify_making_requests
98 add keywords to simplify making requests
2 parents 73e671d + 2490cdb commit 0bdfa5b

File tree

5 files changed

+193
-27
lines changed

5 files changed

+193
-27
lines changed

docs/releases.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@
88
- The `Relations` still need to be reworked to align with this change.
99
- Refactored retrieving / loading of the OpenAPI spec.
1010
- This closes [issue #93: SSL error even if cert / verify is set](https://github.com/MarketSquare/robotframework-openapitools/issues/93).
11+
- Added keywords to make it easier to work with the `RequestValues` object:
12+
- `Get Request Values Object` can be used to create a `RequestValues` instance from pre-defined values (where `Get Request Values` generates all values automatically).
13+
- `Perform Authorized Request` is functionally the same as exisiting `Authorized Request` keyword, but it accepts a `RequestValues` instance as argument.
14+
- `Validated Request` is functionally the same as the existing `Perform Validated Request` keyword, but it accepts the data as separate arguments instead of the `RequestValues`.
15+
- `Convert Request Values To Dict` can be used to get a (Python) dict represenation of a `RequestValues` object that can be used with e.g. the Collections keywords for working with dictionaries.
16+
- Thise closes [issue #98: Add keywords to simplify using Authorized Request and Perform Validated Request](https://github.com/MarketSquare/robotframework-openapitools/issues/98).
1117
- Improved handling of `treat_as_mandatory` on a `PropertyValueConstraint`.
1218
- Added support for using `IGNORE` as `invalid_value` on a `PropertyValueConstraint`.
1319

src/OpenApiDriver/openapi_executors.py

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
from OpenApiLibCore import (
2323
OpenApiLibCore,
2424
RequestData,
25-
RequestValues,
2625
ValidationLevel,
2726
)
2827
from OpenApiLibCore.annotations import JSON
@@ -78,7 +77,7 @@ def _run_keyword(
7877

7978
@overload
8079
def _run_keyword(
81-
keyword_name: Literal["perform_validated_request"], *args: object
80+
keyword_name: Literal["validated_request"], *args: object
8281
) -> None: ... # pragma: no cover
8382

8483

@@ -303,16 +302,14 @@ def test_endpoint(self, path: str, method: str, status_code: int) -> None:
303302
f"No relation found to cause status_code {status_code}."
304303
)
305304
_run_keyword(
306-
"perform_validated_request",
305+
"validated_request",
307306
path,
308307
status_code,
309-
RequestValues(
310-
url=url,
311-
method=method,
312-
params=params,
313-
headers=headers,
314-
json_data=json_data,
315-
),
308+
url,
309+
method,
310+
params,
311+
headers,
312+
json_data,
316313
original_data,
317314
)
318315
if status_code < int(HTTPStatus.MULTIPLE_CHOICES) and (
@@ -335,16 +332,14 @@ def test_endpoint(self, path: str, method: str, status_code: int) -> None:
335332
if method == "PATCH":
336333
original_data = self.get_original_data(url=url)
337334
_run_keyword(
338-
"perform_validated_request",
335+
"validated_request",
339336
path,
340337
status_code,
341-
RequestValues(
342-
url=url,
343-
method=method,
344-
params=params,
345-
headers=headers,
346-
json_data=json_data,
347-
),
338+
url,
339+
method,
340+
params,
341+
headers,
342+
json_data,
348343
original_data,
349344
)
350345

src/OpenApiLibCore/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040
"set_auth",
4141
"set_extra_headers",
4242
"get_request_values",
43+
"get_request_values_object",
44+
"convert_request_values_to_dict",
4345
"get_request_data",
4446
"get_invalid_body_data",
4547
"get_invalidated_parameters",
@@ -51,6 +53,8 @@
5153
"get_invalidated_url",
5254
"ensure_in_use",
5355
"authorized_request",
56+
"perform_authorized_request",
57+
"validated_request",
5458
"perform_validated_request",
5559
"validate_response_using_validator",
5660
"assert_href_to_resource_is_valid",

src/OpenApiLibCore/openapi_libcore.py

Lines changed: 124 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,12 @@ def get_request_values(
243243
method: str,
244244
overrides: Mapping[str, JSON] = default_json_mapping,
245245
) -> RequestValues:
246-
"""Return an object with all (valid) request values needed to make a request."""
246+
"""
247+
Return an object with all (valid) request values needed to make a request.
248+
249+
The `overrides` dictionary can be used to pass specific values for parameters
250+
instead of having them be generated automatically.
251+
"""
247252
json_data: JSON = {}
248253

249254
url = _run_keyword("get_valid_url", path)
@@ -454,15 +459,11 @@ def authorized_request( # pylint: disable=too-many-arguments
454459
files: Any = None,
455460
) -> Response:
456461
"""
457-
Perform a request using the security token or authentication set in the library.
462+
See the `Perform Authorized Request` keyword.
458463
459-
`json_data`, `data` and `files` are passed to `requests.request`s `json`,
460-
`data` and `files` parameters unaltered.
461-
See the requests documentation for details:
462-
https://requests.readthedocs.io/en/latest/api/#requests.request
463-
464-
> Note: provided username / password or auth objects take precedence over token
465-
based security
464+
The difference between these keywords is that this keyword accepts separate
465+
arguments where `Perform Validated Request` accepts a `RequestValues` object
466+
(see the `Get Request Values` keyword for details).
466467
"""
467468
headers = deepcopy(headers) if headers else {}
468469
if self.extra_headers:
@@ -489,8 +490,120 @@ def authorized_request( # pylint: disable=too-many-arguments
489490
logger.debug(f"Response text: {response.text}")
490491
return response
491492

493+
@keyword
494+
def perform_authorized_request(
495+
self,
496+
request_values: RequestValues,
497+
data: Any = None,
498+
files: Any = None,
499+
) -> Response:
500+
"""
501+
Perform a request using the security token or authentication set in the library.
502+
503+
`json_data`, `data` and `files` are passed to `requests.request`s `json`,
504+
`data` and `files` parameters unaltered.
505+
See the requests documentation for details:
506+
https://requests.readthedocs.io/en/latest/api/#requests.request
507+
508+
See also `Authorized Request` and `Get Request Values`.
509+
510+
> Note: provided username / password or auth objects take precedence over token
511+
based security
512+
"""
513+
return self.authorized_request(
514+
url=request_values.url,
515+
method=request_values.method,
516+
params=request_values.params,
517+
headers=request_values.headers,
518+
json_data=request_values.json_data,
519+
data=data,
520+
files=files,
521+
)
522+
523+
@keyword
524+
def get_request_values_object(
525+
self,
526+
url: str,
527+
method: str,
528+
params: dict[str, JSON] = {},
529+
headers: dict[str, str] = {},
530+
json_data: JSON = None,
531+
) -> RequestValues:
532+
"""
533+
This keyword can be used to instantiate a RequestValues object that can be used
534+
with the `Perform Authorized Request` and `Perform Validated Request` keywords.
535+
536+
This is a utility keyword that can make certain test cases or keywords more
537+
concise, but logging and debugging information will be more limited.
538+
539+
See also the `Get Request Values` keyword.
540+
"""
541+
# RequestValues has methods that perform mutations on its values, so
542+
# deepcopy to prevent mutation by reference.
543+
params = deepcopy(params) if params else {}
544+
headers = deepcopy(headers) if headers else {}
545+
json_data = deepcopy(json_data)
546+
return RequestValues(
547+
url=url,
548+
method=method,
549+
params=params,
550+
headers=headers,
551+
json_data=json_data,
552+
)
553+
554+
@keyword
555+
def convert_request_values_to_dict(
556+
self, request_values: RequestValues
557+
) -> dict[str, JSON]:
558+
"""Convert a RequestValues object to a dictionary."""
559+
return {
560+
"url": request_values.url,
561+
"method": request_values.method,
562+
"params": request_values.params,
563+
"headers": request_values.headers,
564+
"json_data": request_values.json_data,
565+
}
566+
492567
# endregion
493568
# region: validation keywords
569+
@keyword
570+
def validated_request(
571+
self,
572+
path: str,
573+
status_code: int,
574+
url: str,
575+
method: str,
576+
params: dict[str, JSON] = {},
577+
headers: dict[str, str] = {},
578+
json_data: JSON = None,
579+
original_data: Mapping[str, JSON] = default_json_mapping,
580+
) -> None:
581+
"""
582+
See the `Perform Validated Request` keyword.
583+
584+
The difference between these keywords is that this keyword accepts separate
585+
arguments where `Perform Validated Request` accepts a `RequestValues` object
586+
(see the `Get Request Values` keyword for details).
587+
"""
588+
# RequestValues has methods that perform mutations on its values, so
589+
# deepcopy to prevent mutation by reference.
590+
params = deepcopy(params) if params else {}
591+
headers = deepcopy(headers) if headers else {}
592+
json_data = deepcopy(json_data)
593+
request_values = RequestValues(
594+
url=url,
595+
method=method,
596+
params=params,
597+
headers=headers,
598+
json_data=json_data,
599+
)
600+
_validation.perform_validated_request(
601+
path=path,
602+
status_code=status_code,
603+
request_values=request_values,
604+
original_data=original_data,
605+
)
606+
494607
@keyword
495608
def perform_validated_request(
496609
self,
@@ -503,6 +616,8 @@ def perform_validated_request(
503616
This keyword first calls the Authorized Request keyword, then the Validate
504617
Response keyword and finally validates, for `DELETE` operations, whether
505618
the target resource was indeed deleted (OK response) or not (error responses).
619+
620+
See also `Validated Request` and `Get Request Values`.
506621
"""
507622
_validation.perform_validated_request(
508623
path=path,
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
*** Settings ***
2+
Variables ${ROOT}/tests/variables.py
3+
Library Collections
4+
Library OpenApiLibCore
5+
... source=${ORIGIN}/openapi.json
6+
... origin=${ORIGIN}
7+
... base_path=${EMPTY}
8+
... mappings_path=${ROOT}/tests/user_implemented/custom_user_mappings.py
9+
10+
Test Tags rf7
11+
12+
13+
*** Variables ***
14+
${ORIGIN} http://localhost:8000
15+
16+
17+
*** Test Cases ***
18+
Test Requests Using RequestValues
19+
${request_values}= Get Request Values path=/employees method=POST
20+
${request_values_dict}= Convert Request Values To Dict ${request_values}
21+
22+
${response_using_request_values}= Perform Authorized Request request_values=${request_values}
23+
Should Be Equal As Integers ${response_using_request_values.status_code} 201
24+
25+
${response_using_dict}= Authorized Request &{request_values_dict}
26+
Should Be Equal As Integers ${response_using_dict.status_code} 201
27+
28+
VAR ${response_dict_using_request_values}= ${response_using_request_values.json()}
29+
VAR ${response_dict_using_request_values_dict}= ${response_using_dict.json()}
30+
31+
Should Not Be Equal
32+
... ${response_dict_using_request_values}[identification]
33+
... ${response_dict_using_request_values_dict}[identification]
34+
Should Not Be Equal
35+
... ${response_dict_using_request_values}[employee_number]
36+
... ${response_dict_using_request_values_dict}[employee_number]
37+
38+
Remove From Dictionary ${response_dict_using_request_values} identification employee_number
39+
Remove From Dictionary ${response_dict_using_request_values_dict} identification employee_number
40+
41+
Should Be Equal ${response_dict_using_request_values} ${response_dict_using_request_values_dict}
42+
43+
${request_values_object}= Get Request Values Object &{request_values_dict}
44+
45+
Perform Validated Request path=/employees status_code=201 request_values=${request_values_object}
46+
Validated Request path=/employees status_code=201 &{request_values_dict}

0 commit comments

Comments
 (0)