Skip to content
This repository was archived by the owner on Dec 25, 2024. It is now read-only.

v2 migration guides #138

Merged
merged 10 commits into from
Mar 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,8 @@ public void processOpts() {
readmeTemplate = "README_onlypackage.hbs";
}
supportingFiles.add(new SupportingFile(readmeTemplate, "", readmePath));
supportingFiles.add(new SupportingFile("migration_2_0_0.hbs", "", "migration_2_0_0.md"));
supportingFiles.add(new SupportingFile("migration_other_python_generators.hbs", "", "migration_other_python_generators.md"));
supportingFiles.add(new SupportingFile("__init__package.hbs", packagePath(), "__init__.py"));

if (!generateSourceCodeOnly) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,94 +19,9 @@ For more information, please visit [{{{infoUrl}}}]({{{infoUrl}}})

Python {{generatorLanguageVersion}}

## Migration from other generators like python and python-legacy

### Changes
1. This generator uses spec case for all (object) property names and parameter names.
- So if the spec has a property name like camelCase, it will use camelCase rather than camel_case
- So you will need to update how you input and read properties to use spec case
2. Endpoint parameters are stored in dictionaries to prevent collisions (explanation below)
- So you will need to update how you pass data in to endpoints
3. Endpoint responses now include the original response, the deserialized response body, and (todo)the deserialized headers
- So you will need to update your code to use response.body to access deserialized data
4. All validated data is instantiated in an instance that subclasses all validated Schema classes and Decimal/str/tuple/frozendict/NoneClass/BoolClass/bytes/io.FileIO
- This means that you can use isinstance to check if a payload validated against a schema class
- This means that no data will be of type None/True/False
- ingested None will subclass NoneClass
- ingested True will subclass BoolClass
- ingested False will subclass BoolClass
- So if you need to check is True/False/None, instead use instance.is_true_()/.is_false_()/.is_none_()
5. All validated class instances are immutable except for ones based on io.File
- This is because if properties were changed after validation, that validation would no longer apply
- So no changing values or property values after a class has been instantiated
6. String + Number types with formats
- String type data is stored as a string and if you need to access types based on its format like date,
date-time, uuid, number etc then you will need to use accessor functions on the instance
- type string + format: See .as_date_, .as_datetime_, .as_decimal_, .as_uuid_
- type number + format: See .as_float_, .as_int_
- this was done because openapi/json-schema defines constraints. string data may be type string with no format
keyword in one schema, and include a format constraint in another schema
- So if you need to access a string format based type, use as_date_/as_datetime_/as_decimal_/as_uuid_
- So if you need to access a number format based type, use as_int_/as_float_
7. Property access on AnyType(type unset) or object(dict) schemas
- Only required keys with valid python names are properties like .someProp and have type hints
- All optional keys may not exist, so properties are not defined for them
- One can access optional values with dict_instance['optionalProp'] and KeyError will be raised if it does not exist
- Use get_item_ if you need a way to always get a value whether or not the key exists
- If the key does not exist, schemas.unset is returned from calling dict_instance.get_item_('optionalProp')
- All required and optional keys have type hints for this method, and @typing.overload is used
- A type hint is also generated for additionalProperties accessed using this method
- So you will need to update you code to use some_instance['optionalProp'] to access optional property
and additionalProperty values
8. The location of the api classes has changed
- Api classes are located in your_package.apis.tags.some_api
- This change was made to eliminate redundant code generation
- Legacy generators generated the same endpoint twice if it had > 1 tag on it
- This generator defines an endpoint in one class, then inherits that class to generate
apis by tags and by paths
- This change reduces code and allows quicker run time if you use the path apis
- path apis are at your_package.apis.paths.some_path
- Those apis will only load their needed models, which is less to load than all of the resources needed in a tag api
- So you will need to update your import paths to the api classes

### Why are Leading and Trailing Underscores in class and method names?
Classes can have arbitrarily named properties set on them
Endpoints can have arbitrary operationId method names set
For those reasons, I use the prefix and suffix _ to greatly reduce the likelihood of collisions
on protected + public classes/methods.

### Object property spec case
This was done because when payloads are ingested, they can be validated against N number of schemas.
If the input signature used a different property name then that has mutated the payload.
So SchemaA and SchemaB must both see the camelCase spec named variable.
Also it is possible to send in two properties, named camelCase and camel_case in the same payload.
That use case should work, so spec case is used.

### Parameter spec case
Parameters can be included in different locations including:
- query
- path
- header
- cookie

Any of those parameters could use the same parameter names, so if every parameter
was included as an endpoint parameter in a function signature, they would collide.
For that reason, each of those inputs have been separated out into separate typed dictionaries:
- query_params
- path_params
- header_params
- cookie_params

So when updating your code, you will need to pass endpoint parameters in using those
dictionaries.

### Endpoint responses
Endpoint responses have been enriched to now include more information.
Any response reom an endpoint will now include the following properties:
response: urllib3.HTTPResponse
body: typing.Union[Unset, Schema]
headers: typing.Union[Unset, TODO]
Note: response header deserialization has not yet been added
## Migration Guides
- [2.0.0 Migration Guide](migration_2_0_0.md)
- [Migration from Other Python Generators](migration_other_python_generators.md)


## Installation & Usage
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
# Migration v1.X.X to v2.0.0

- [Compatibility note for opeanpi-generator](#compatibility-note-for-opeanpi-generator)
- [Component Generation](#component-generation)
- [Packaging Changes](#packaging-changes)
- [Path Generation](#path-generation)
- [Configuration Info Refactored](#configuration-info-refactored)
- [Servers and Security Generation](#servers-and-security-generation)
- [Java Classes for Openapi Data Refactored](#java-classes-for-openapi-data-refactored)
- [Api Access by Tags and Paths Updated](#api-access-by-tags-and-paths-updated)
- [Some Method/Property/Input/Class Names Updated](#some-methodpropertyinputclass-names-updated)
- [Documentation Updated](#documentation-updated)

## Compatibility note for opeanpi-generator
The v1.0.4 release is nearly identical to the openapi-generator v6.3.0 release

Below is a summary of big changes when updating you code from v1.X.X to 2.0.0

## Component Generation
Update:
Openapi document components in "#/components/x" are now generated in a components/x packages
Ths applies to:
- headers
- parameters
- request_bodies
- responses
- schemas
- security_schemes

The generator now writes a class for each of those generated components.

### Reason
A lot of openapi data is $ref references throughout an openapi document.
With this update, those $ref source locations are generated with file system paths that closely mirror the openapi
document json schema path to that info. $ref definition is then imported in generated python code.
This minimizes the amount of code that is generated, imposes well defined encapsulation and allows templates to be
re-used for many types of data regardless of where the data is in the source openapi document.

### Action
- Update where you are importing models from, models are now component schemas
- File path change: model/pet.py -> components/schema/pet.py

## Packaging changes
Code has been updated to use .toml packaging. Code is now distributed in a src directory

### Reason
These updates follow latest python packaging best practices

### Action
- if you are suppressing generation of any files, you will need to edit those file paths
- File Path Change: package_name -> src/package_name

## Path Generation
If paths contain inline descriptions of parameters, request bodies, responses, security, or servers,
then those are now generated in separate files. Those files are imported into the endpoint code.
File locations closely mirror the openapi data json schema path.

### Reason
Generating those files in paths that closely mirror the json schema paths will allow
the generator to use $ref to any location in the openapi document in the future, not just components.
These could include:
- relative $refs
- a request body $ref referring to the request body in another endpoint

This also allowed the generation code to work the same way regardless of where the header/requestBody etc
is in the source spec.

Note:
Operations are now at
paths/somePath/get/operation.py
and not at
paths/somePath/get/__init__.py
to minimize the amount of memory needed when importing the python package.
The configurations.api_configuration.py file imports all servers defined at:
- openapi document root
- pathItem
- pathItem operation
And if servers were defined in pathItem operations, then every operation that contains a server would be imported
in api_configuration.py. Which could be many many files.

So instead
- operation endpoint definition was moved to paths/somePath/get/operation.py
- paths/somePath/get/__init__.py stays empty and does not need lots of memory when api_configuration.py imports servers
- server information was kept in paths/somePath/get/servers/server_x.py

### Action
- if you are importing any endpoint form its file, update your import
- File path update: paths/somePath/get.py -> paths/somePath/get/operation.py

## Configuration Info Refactored
Configuration information was separated into two classes
- configurations/api_configuration.py ApiConfiguration for:
- server info
- security (auth) info
- logging info
- configurations/schema_configuration.py SchemaConfiguration for:
- disabled openapi/json-schema keywords

### Reason
Schema validation only relies on SchemaConfiguration data and does not need to know about any ApiConfiguration info
General api configuration info was poorly structured in the legacy configuration class which had 13 inputs.
The refactored ApiConfiguration now has 4 inputs which define servers and security info.
Having these separate classes prevents circular imports when the schemas.py file imports its SchemaConfiguration class.

### Action
- When you instantiate ApiClient, update your code to pass in an instance of SchemaConfiguration + ApiConfiguration


## Servers and Security Generation
Servers are now generated as separate files with one class per file wherever they were defined in the openapi document
- servers/server_0.py
- paths/somePath/servers/server_0.py
- paths/somePath/get/servers/server_0.py
Security requirements objects are now generated as separate files with one security requirement object per file
wherever they were defined in the openapi document
- security/security_requirement_object_0.py
- paths/somePath/get/security/security_requirement_object_0.py

### Reason
Server classes now re-use schema validation code to ensure that inputs to server variables are valid.
Generating these separate files minimizes generated code and maximizes code re-use.

### Action
- If endpoints need to use specific servers or security not at index 0, pass security_index_info + server_index_info
into ApiConfiguration when instantiating it
- If you use non-default server variable values, then update your code to pass in server_info into ApiConfiguration

## Java Classes for Openapi Data Refactored
Almost every java class used to store openapi document data at generation time has been re-written or refactored.
The new classes are much shorter and contain only what is needed to generate code and documentation.
This will make it much easier to add new openapi v3.1.0 features like new json schema keywords.
Generator interface methods were also refactored to simplify Java model instantiation and code generation.
Almost all properties are now public static final and are:
- immutable
- publicly accessible to the templates

### Reason
These updates make the code much more maintainable.
The number of properties that have to be maintained is much smaller
- Component Schema: ~100 properties in CodegenModel.java -> ~50 properties in CodegenSchema.java
- PathItem Operation: ~75 properties CodegenOperation: ~25 properties

This will reduce bugs like: why can't I access this property in this template
Because instances are mostly immutable it is very clear where the wrong value is being created/assigned.

### Action
- if you are customizing the python generator, you will need to update your java code

## Api Access By Tags and Paths Updated
Previously, keys togo from tags and paths to apis were enums.
The code was updated to use a TypedDict and uses strings as the keys.

### Reason
Making this change allows type hinting to work for the TypedDict with string keys

### Action
- If you use path_to_api.py or tag_to_api.py, update the key that you use to access the api

## Some Method/Property/Input/Class Names Updated
- is_true_oapg -> is_true_
- is_false_oapg -> is_false_
- is_none_oapg -> is_none_
- as_date_oapg -> as_date_
- as_datetime_oapg -> as_datetime_
- as_decimal_oapg -> as_decimal_
- as_uuid_oapg -> as_uuid_
- as_float_oapg -> as_float_
- as_int_oapg -> as_int_
- get_item_oapg -> get_item_
- from_openapi_data_oapg -> from_openapi_data_
- _verify_typed_dict_inputs_oapg -> _verify_typed_dict_inputs
- _configuration -> configuration_
- _arg -> arg_
- _args -> args_
- MetaOapg -> Schema_
- JsonSchema -> OpenApiSchema

### Reason
Classes can have arbitrarily named properties set on them
Endpoints can have arbitrary operationId method names set
For those reasons, I use the prefix and suffix _ to greatly reduce the likelihood of collisions
on protected + public classes/methods.

### Action
- if you use the above methods/inputs/properties/classes update them to the latest names

## Documentation Updated
- components now have sections in the readme
- models became component schemas
- one file is now generated for each endpoint
- that file now has a table of contents
- heading indentation levels now are indented correctly in descending order
- endpoint now includes server and security info (if security exists)
- servers section added to readme if servers were defined in the openapi document root
- security section added to readme if security was defined in the openapi document root

### Reason
Endpoint documentation had indentation and linking bugs before, this fixes them.
When all endpoints docs were written in one file it was difficult to link ot the correct section.
How to set server and security info was unclear before, the new docs and classes should clarify that.

### Action
- if you link to specific parts of the documentation, update your links
Loading