This framework is a Proof of Concept that aims to simplify the integration of REST API clients inside IRIS Interoperability-enabled productions using Python (IOP) with minimal code required
- Define your API client in an elegant and declarative syntax, inspired by libraries such as Retrofit or FastAPI
- Full interoperability with either native IRIS EnsLib.HTTP.OutboundAdapter or python requests as your HTTP backend
- Automatically generate your dataclass models and API methods by providing an OpenAPI specification
- Generated code is made to be human-readable with minimal boilerplate code to be easily editable if adjustments needs to be made
- iris-pex-embedded-python (IOP)
- uplink - This project uses Uplink as a "frontend" for the API client definition
- datamodel-code-generator + pydantic - Used to generate Pydantic dataclasses from OpenAPI specs
- prance - Used to parse and validate OpenAPI specs
This is all the code you need to define your models and API endpoints
from iop_rest.api import BaseClientREST
from iop import Message
from pydantic import BaseModel
from uplink.arguments import *
from uplink.commands import *
from uplink.decorators import *
from uplink import returns
@dataclass(init=False)
class Pet(BaseModel, Message):
id: int
name: str
@returns.json
@headers({
"Content-Type": "application/json",
"Accept": "application/json",
"Charset": "utf-8",
})
class PetstoreClient(BaseClientREST):
@json
@post('pet')
def add_pet(self, body: Body(type=Pet)) -> Pet:
pass
@json
@get('pet/{petId}')
def get_pet_by_id(self, pet_id: Path("petId", type=int)) -> Pet:
pass
Simple Business Operation that uses a EnsLib.HTTP.OutboundAdapter by default and passes it to our HTTP client
from iop_rest.bo import BaseRESTOperation
from api import PetstoreClient
# from uplink import RequestsClient
class PetstoreOperation(BaseRESTOperation):
client: PetstoreClient
def init_client(self, adapter):
return PetstoreClient(adapter)
# Alternatively use python requests as the HTTP backend
# but lose the capabilities of the adapter
# return PetstoreClient(adapter, client=RequestsClient())
from iop import BusinessService
from iop_rest.messages import RESTRequest
from api import Pet
class PetstoreService(BusinessService):
@staticmethod
def get_adapter_type():
return "Ens.InboundAdapter"
def on_process_input(self, message_input):
pet = Pet(
id=1,
name="Robert",
)
response: Pet = self.send_request_sync('PetstoreOperation', RESTRequest.create('add_pet', pet))
# do something with response...
self.log_info(response.name)
The demo can be found inside src/python/demo/
and is a full implementation of the Petstore OpenAPI spec based on the above example
See the demo in action on Youtube (Please enable subtitles)
- The
bs.py
file is a simple BusinessService that is used to call periodically the Petstore BusinessOperation with a sample Pet object
- The
bo.py
file is a simple Business Operation that uses a native IRIS EnsLib.HTTP.OutboundAdapter by default and passes it to our HTTP client- You can alternatively choose to use python's requests library as the HTTP backend, however you will lose capabilities offered by IRIS's HTTP OutboundAdapter
- The
generated/api.py
file contains the API definition generated by the iop_rest module, the generated code uses the uplink library at its core, and provides numerous decorators and type-checking abilities to allow for a very elegant and declarative syntax
- The
generated/models.py
file contains the dataclass models generated by the iop_rest module, it uses pydantic's base class to allow for automatic json serialization and deserialization by the uplink library
- The
settings.py
file contains the production definition and settings, see the IOP documentation for more information
Make sure you have git and Docker installed.
Clone/git pull the repo into any local directory
git clone https://github.com/Antoine-dh/iop-rest-client-framework
Open the terminal in this directory and run:
docker compose build
Run the IRIS container with your project:
docker compose up -d
zpm "install iop-rest-client-framework"
If running Docker, open a bash terminal inside the container
docker compose exec iris bash
otherwise, make sure the iop_rest module is installed or accessible by the python interpreter (or just cd src/python
before running the command)
To run the code generation program, use the following command
python -m iop_rest <APIClassName> <path/to/openapi_spec> <path/to/output_folder>
For this demo:
python -m iop_rest PetstoreClient ./misc/petstore.openapi.yaml ./src/python/demo/generated/
Keep in mind this is a proof of concept, and not a finished product, do not use for production.
- For now, only JSON APIs and UTF-8 charset is supported
- Exceptions are not currently handled properly
- Endpoints that returns JSON lists at the root of the HTTP body are not supported, this is due to a technical limitation with IOP messages that needs to be wrapped into a singular class, see issue
- Models' schemas inside OpenAPI specs are expected to be defined in
#/components/schemas/
, and responses to be$ref
s to thoses schemas