clariteia / api_gateway_common Goto Github PK
View Code? Open in Web Editor NEWLicense: MIT License
License: MIT License
the Common library must support an HTTP Client for Microservices interfaces
METH_GET = "GET"
METH_DELETE = "DELETE"
METH_PATCH = "PATCH"
METH_POST = "POST"
METH_PUT = "PUT"
class ClientHttpBase:
def __init__(self, **kwargs):
pass
async def get(self, url, params: dict = None, **kwargs: Any):
return await self._triger_request(METH_GET, url, params, **kwargs)
async def post(self, url, params: dict = None, data: Any = None, **kwargs: Any):
return await self._triger_request(METH_POST, url, params, data, **kwargs)
async def put(self, url, params: dict = None, data: Any = None, **kwargs: Any):
return await self._triger_request(METH_PUT, url, params, data, **kwargs)
async def patch(self, url, params: dict = None, data: Any = None, **kwargs: Any):
return await self._triger_request(METH_PATCH, url, params, data, **kwargs)
async def delete(self, url, params: dict = None, data: Any = None, **kwargs: Any):
return await self._triger_request(METH_DELETE, url, params, data, **kwargs)
async def _triger_request(self, method, url, params, data: Any = None, **kwargs: Any):
async with aiohttp.ClientSession() as session:
async with session.request(method=method, url=url, params=params, data=data, **kwargs) as resp:
return resp
Example of MinosConfig:
service:
name: Order
rest:
host: localhost
port: 8900
endpoints:
- name: AddOrder
route: /order
method: POST
controller: minos.services.OrderService
action: add_order
discovery:
host: localhost
port: 8080
path: /discover
endpoints:
- name: Discover
route: /discover
method: GET
controller: minos.services.DiscoveryService
action: discover
- name: Subscribe
route: /subscribe
method: POST
controller: minos.services.DiscoveryService
action: subscribe
- name: Unsubscribe
route: /unsubscribe
method: POST
controller: minos.services.DiscoveryService
action: unsubscribe
- name: SystemHealth
route: /system/health
method: GET
controller: minos.services.DiscoveryService
action: system_health
db:
host: localhost
port: 6379
password: None
def import_module(module: str) -> t.Type:
"""Import the given module from a package"""
try:
if "." in module:
parts = module.split(".")
name = ".".join(parts[:-1])
module_ref = importlib.import_module(name)
kallable = getattr(module_ref, parts[-1])
if not six.callable(kallable):
raise TypeError("The module is not callable")
return kallable
except ImportError as e:
raise MinosImportException("Error importing Package")
def classname(cls: t.Type) -> str:
"""Compute the given class full name.
:param cls: Target class.
:return: An string object.
"""
return f"{cls.__module__}.{cls.__qualname__}"
Example:
class RestRoutesLoader:
"""
Rest Interface Handler
Rest Interface for aiohttp web handling.
"""
__slots__ = "_config", "_app"
def __init__(self, config: MinosConfig, app: web.Application = web.Application()):
self._config = config
self._app = app
self.load_routes()
def load_routes(self):
"""Load routes from config file."""
for item in self._config.discovery.endpoints:
callable_f = self.class_resolver(item.controller, item.action)
self._app.router.add_route(item.method, item.route, callable_f)
@staticmethod
def class_resolver(controller: str, action: str):
"""Load controller class and action method.
:param controller: Controller string. Example: "tests.service.CommandTestService.CommandService"
:param action: Config instance. Example: "get_order"
:return: A class method callable instance.
"""
object_class = import_module(controller)
instance_class = object_class()
class_method = getattr(instance_class, action)
return class_method
def get_app(self):
"""Return rest application instance.
:return: A `web.Application` instance.
"""
return self._app
cors:
active: true
Currently, the package is named as "api_gateway_common"
in pyproject.toml
. That name is used as the package name on Pypi
.
To avoid future confusion and keep all the packages developed for the minos
framework, I propose to replace it by something like "minos_api_gateway_common"
Example of main class:
class RESTService(AIOHTTPService):
"""
Rest Interface
Expose REST Interface handler using aiomisc AIOHTTPService.
"""
def __init__(self, address: str, port: int, endpoints: dict, app: web.Application = web.Application(), **kwds: t.Any):
address = address
port = port
super().__init__(address=address, port=port, **kwds)
self._endpoints = endpoints
self.rest_interface = RestRoutesLoader(endpoints=endpoints, app=app)
async def create_application(self):
return self.rest_interface.get_app() # pragma: no cover
Example of extend and create custom service:
class TestRestService(RESTService):
def __init__(self, address: str, port: int, endpoints: dict, **kwds: t.Any):
super().__init__(address=address, port=port, endpoints=endpoints, **kwds)
config = MinosConfig(self.CONFIG_FILE_PATH)
rest_interface = TestRestService(address=config.rest.connection.host, port=config.rest.connection.port, endpoints=config.rest.endpoints)
---
enabled: true
exclude:
- "**/*.md"
- ".idea/**/*"
- "docs/**/*"
- "**/*.in"
- "Makefile"
- ".github/workflows/**/*"
restylers:
- name: black
image: restyled/restyler-black:v19.10b0
command:
- black
arguments: ["--line-length", "120"]
include:
- "**/*.py"
interpreters:
- python
- name: isort
image: restyled/restyler-isort:v5.8.0
command:
- isort
arguments: []
include:
- "**/*.py"
interpreters:
- python
In order to avoid that unexpected files are committed, I propose to extend the excluded files adding the following lines:
# lmdb database
*.mdb
# Sphinx Api Documentation
docs/api
This issue was inspired by the following Pull Request: Clariteia/minos_microservice_networks#137
This issue is a clone of: Clariteia/minos-pypackage#12
RestRoutesLoader must load controller and action passing MinosConfig as default:
class RestRoutesLoader:
...
def resolve_callable(self, controller: str, action: str):
"""Load controller class and action method.
:param controller: Controller string. Example: "tests.service.CommandTestService.CommandService"
:param action: Config instance. Example: "get_order"
:return: A class method callable instance.
"""
object_class = import_module(controller)
instance_class = object_class()
class_method = getattr(instance_class, action)
partial = functools.partial(class_method, config=self._config)
return partial
...
Problem:
Current Implementation:
class ClientHttp(ClientHttpBase):
...
async def _trigger_request(self, method: str, url: str, params, data: Any = None, **kwargs: Any):
"""Trigger the request.
:param method: HTTP method.
:param url: Url to call.
:param params: Params to send on URL.
:param data: Data to send in body.
:param kwargs: Additional named arguments.
:return: A `_RequestContextManager` instance.
"""
async with aiohttp.ClientSession() as session:
async with session.request(method=method, url=url, params=params, data=data, **kwargs) as resp:
return resp
....
Solution:
class ClientHttp(ClientHttpBase):
"""HTTP Client aiohttp."""
async def __aenter__(self):
self.session = aiohttp.ClientSession()
return self.session
async def __aexit__(self, exc_type, exc_val, exc_tb):
await self.session.close()
async def _trigger_request(self, method: str, url: str, params, data: Any = None, **kwargs: Any):
"""Trigger the request.
:param method: HTTP method.
:param url: Url to call.
:param params: Params to send on URL.
:param data: Data to send in body.
:param kwargs: Additional named arguments.
:return: A `_RequestContextManager` instance.
"""
async with self.session.request(method=method, url=url, params=params, data=data, **kwargs) as response:
return response
Use:
async with ClientHttp() as client:
request = await client.get(url="http://httpbin.org/get")
self.assertEqual(request.status, 200)
The changes to be applied during the restyling process are:
requirements_dev.txt
file for development by the use of the Poetry
package manager: Clariteia/minos-pypackage#4codecov
config to allow a small degree of tolerance: Clariteia/minos-pypackage#5pre-commit
: Clariteia/minos-pypackage#6restyled
: Clariteia/minos-pypackage#7flake8
at pre-commit
and Github Actions level: Clariteia/minos-pypackage#9MinosConfig error with discovery.path attr.
> self.assertEqual("/discover", discovery.path)
E AttributeError: 'Discovery' object has no attribute 'path'
test_config.py:57: AttributeError
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.