https://github.com/antoine-dh/iop-rest-client-framework
https://github.com/antoine-dh/iop-rest-client-framework
intersystems-iris
Last synced: 5 months ago
JSON representation
- Host: GitHub
- URL: https://github.com/antoine-dh/iop-rest-client-framework
- Owner: Antoine-dh
- License: mit
- Created: 2024-09-22T19:27:14.000Z (over 1 year ago)
- Default Branch: master
- Last Pushed: 2024-09-25T10:00:22.000Z (over 1 year ago)
- Last Synced: 2025-04-25T21:30:03.670Z (about 1 year ago)
- Topics: intersystems-iris
- Language: Python
- Homepage: https://openexchange.intersystems.com/package/IOP-REST-Client-Framework-1
- Size: 35.2 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# iop-rest-client-framework
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](https://github.com/grongierisc/interoperability-embedded-python)) with minimal code required
## Features
- Define your API client in an elegant and declarative syntax, inspired by libraries such as [Retrofit](https://square.github.io/retrofit/) or [FastAPI](https://fastapi.tiangolo.com/)
- Full interoperability with either native IRIS [EnsLib.HTTP.OutboundAdapter](https://docs.intersystems.com/irislatest/csp/documatic/%25CSP.Documatic.cls?LIBRARY=ENSLIB&CLASSNAME=EnsLib.HTTP.OutboundAdapter) or python [requests](https://requests.readthedocs.io/en/latest/) as your HTTP backend
- Automatically generate your dataclass models and API methods by providing an [OpenAPI specification](https://swagger.io/specification/)
- Generated code is made to be human-readable with minimal boilerplate code to be easily editable if adjustments needs to be made
## Dependencies
- [iris-pex-embedded-python](https://github.com/grongierisc/interoperability-embedded-python) (IOP)
- [uplink](https://pypi.org/project/uplink/) - This project uses Uplink as a "frontend" for the API client definition
- [datamodel-code-generator](https://pypi.org/project/datamodel-code-generator/) + [pydantic](https://pypi.org/project/pydantic/) - Used to generate Pydantic dataclasses from OpenAPI specs
- [prance](https://pypi.org/project/prance/) - Used to parse and validate OpenAPI specs
## Example
### api\.py
This is all the code you need to define your models and API endpoints
```python
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
```
### bo\.py
Simple Business Operation that uses a EnsLib.HTTP.OutboundAdapter by default and passes it to our HTTP client
```python
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())
```
### bs\.py
```python
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)
```
## Demo
The demo can be found inside `src/python/demo/` and is a full implementation of the [Petstore OpenAPI spec](https://petstore3.swagger.io/) based on the above example
### Presentation video
[See the demo in action on Youtube](https://www.youtube.com/watch?v=MtK2eNVG0Gc)
*(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](https://uplink.readthedocs.io/en/stable/) 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](https://github.com/grongierisc/interoperability-embedded-python?tab=readme-ov-file#61241-settingpy-file) for more information
## Prerequisites
Make sure you have [git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) and [Docker](https://www.docker.com/products/docker-desktop) installed.
## Installation
### With Docker
Clone/git pull the repo into any local directory
```sh
git clone https://github.com/Antoine-dh/iop-rest-client-framework
```
Open the terminal in this directory and run:
```sh
docker compose build
```
Run the IRIS container with your project:
```sh
docker compose up -d
```
### With ZPM
```objectscript
zpm "install iop-rest-client-framework"
```
## Usage
### Code generation
If running Docker, open a bash terminal inside the container
```sh
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
```sh
python -m iop_rest
```
For this demo:
```sh
python -m iop_rest PetstoreClient ./misc/petstore.openapi.yaml ./src/python/demo/generated/
```
## Issues and technical limitations
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](https://github.com/grongierisc/interoperability-embedded-python/issues/25)
- Models' schemas inside OpenAPI specs are expected to be defined in `#/components/schemas/`, and responses to be `$ref`s to thoses schemas