Skip to content
This repository has been archived by the owner on Aug 9, 2024. It is now read-only.

Commit

Permalink
Merge pull request #10 from MarketSquare/add_id_mapping
Browse files Browse the repository at this point in the history
Add id mapping
  • Loading branch information
robinmackaij authored Feb 17, 2023
2 parents 1457e29 + a517b5d commit fb8478d
Show file tree
Hide file tree
Showing 25 changed files with 961 additions and 417 deletions.
9 changes: 5 additions & 4 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
---
---

# OpenApiLibCore for Robot Framework®
# OpenApiLibCore for Robot Framework

The OpenApiLibCore library is a utility library that is meant to simplify creation
of other Robot Framework libraries for API testing based on the information in
an OpenAPI document (also known as Swagger document).
This document explains how to use the OpenApiLibCore library.

For more information about Robot Framework®, see http://robotframework.org.
My RoboCon 2022 talk about OpenApiDriver and OpenApiLibCore can be found [here](https://www.youtube.com/watch?v=7YWZEHxk9Ps)

For more information about Robot Framework, see http://robotframework.org.

---

Expand Down Expand Up @@ -112,7 +114,6 @@ Details about the `mappings_path` variable usage can be found
There are currently a number of limitations to supported API structures, supported
data types and properties. The following list details the most important ones:
- Only JSON request and response bodies are supported.
- The unique identifier for a resource as used in the `paths` section of the
openapi document is expected to be the `id` property on a resource of that type.
- No support for per-endpoint authorization levels.
- Parsing of OAS 3.1 documents is supported by the parsing tools, but runtime behavior is untested.

72 changes: 64 additions & 8 deletions docs/advanced_use.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,17 @@ To take a custom mappings file into use, the absolute path to it has to be passe
Library OpenApiLibCore
... source=http://localhost:8000/openapi.json
... origin=http://localhost:8000
... mappings_path=${root}/tests/custom_user_mappings.py
... mappings_path=${ROOT}/tests/custom_user_mappings.py
...
```

> Note: An absolute path is required.
> In the example above, `${root}` is a global variable that holds the absolute path to the repository root.
> In the example above, `${ROOT}` is a global variable that holds the absolute path to the repository root.
## The custom mappings file
Just like custom Robot Framework libraries, the mappings file has to be implemented in Python.
Since this Python file is imported by the OpenApiLibCore, it has to follow a fixed format (more technically, implement a certain interface).
The bare minimum implementation of a mappings.py file looks like this:
The (almost) bare minimum implementation of a mappings.py file looks like this:

```python
from OpenApiLibCore import (
Expand All @@ -41,6 +41,11 @@ from OpenApiLibCore import (
)


ID_MAPPING = {
"/myspecialendpoint", "special_thing_id",
}


class MyDtoThatDoesNothing(Dto):
@staticmethod
def get_relations():
Expand All @@ -54,24 +59,47 @@ DTO_MAPPING = {

```

There are 3 main parts in this mappings file:
There are 4 main parts in this mappings file:

1. The import section.
Here the classes needed to implement custom mappings are imported.
This section can just be copied without changes.
2. The section defining the mapping Dtos.
2. The `ID_MAPPING` "constant" definition / assignment.
3. The section defining the mapping Dtos.
More on this later.
3. The `DTO_MAPPING` "constant" definition / assignment.
4. The `DTO_MAPPING` "constant" definition / assignment.

## The DTO_MAPPING
When a custom mappings file is used, the OpenApiLibCore will attempt to import it and then import `DTO_MAPPING` from it.

## The ID_MAPPING and DTO_MAPPING
When a custom mappings file is used, the OpenApiLibCore will attempt to import it and then import `DTO_MAPPING` and `ID_MAPPING` from it.
For this reason, the exact same name must be used in a custom mappings file (capitilization matters).

### The ID_MAPPING
The `ID_MAPPING` 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.

The `ID_MAPPING` is used to specify what the unique identifier property of a resource at the given path is, if different from the `default_id_property_name` (see library parameters).

In some situations, the identifier of the resource is not url-safe (e.g. containing a `/`).
To support this type of resource identifier, a transformer can be provided:

```python
def my_transformer(identifier_name: str) -> str:
return identifier_name.replace("/", "_")


ID_MAPPING = {
"/myspecialendpoint": ("special_thing_id", my_transformer),
}

```

### The DTO_MAPPING
The `DTO_MAPPING` is a dictionary with a tuple as its key and a mappings Dto as its value.
The tuple must be in the form `("endpoint_from_the_paths_section", "method_supported_by_the_endpoint")`.
The `endpoint_from_the_paths_section` must be exactly as found in the openapi document.
The `method_supported_by_the_endpoint` must be one of the methods supported by the endpoint and must be in lowercase.


## Dto mapping classes
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.
Each of these classes is designed to handle a relation or constraint commonly seen in REST APIs.
Expand Down Expand Up @@ -249,6 +277,34 @@ DTO_MAPPING = {

So now if an incorrectly formatted date is send a 422 response is expected, but when `2020-02-20` is send the expected repsonse is 403.

In some API implementations, there may be a property that will always return a specific error code if it's value is not valid.
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).
This situation can be handled by use of the special `IGNORE` value (see below for other uses):

```python
class EmployeeDto(Dto):
@staticmethod
def get_relations():
relations = [
PropertyValueConstraint(
property_name="date_of_birth",
values=["1995-03-27", "1980-10-02"],
error_code=403,
invalid_value=IGNORE,
invalid_value_error_code=422,
),
]
return relations

DTO_MAPPING = {
("/employees", "post"): EmployeeDto,
("/employees/${employee_id}", "put"): EmployeeDto,
("/employees/${employee_id}", "patch"): EmployeeDto,
}
```

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).

---

### `PathPropertiesConstraint`
Expand Down
4 changes: 2 additions & 2 deletions docs/openapi_libcore.html

Large diffs are not rendered by default.

Loading

0 comments on commit fb8478d

Please sign in to comment.