Package: minos-discovery-kong
minos-kongSummaryPre-Alpha release, use at your own risk
Minos Kong is a plugin that integrate minos micorservices with Kong API GatewayInstallationInstall the dependency:pipinstallminos-discovery-kongModifyconfig.ymlfile:...discovery:client:minos.plugins.kong.KongDiscoveryClienthost:localhostport:8001...How toThe above configuration is sufficient for the microservice to subscribe on startup and unsubscribe on shutdown.
Therefore, all you would have to do would be to make your requests against:http://localhost:8000/your_endpointKong official documentationOfficial docsYou can get read the official docshere.PostmanYou can get the official postman collection for postmanhere.Konga - Administrative interfaceFor development purposes you can add open-source administrative section by using next docker service:services:...konga:image:pantsel/kongaports:-1337:1337links:-kong:kongcontainer_name:kongaenvironment:-NODE_ENV=productionDecoratorsDecorator@enroutecan support next params:path- route url path.method- HTTP method.authenticated(Optional) - True if route need authentication.authorized_groups(Optional) - Groups which can access to specified route (they must exist in Kong).regex_priority(Optional) - A number used to choose which route resolves a given request when several routes match it using regexes simultaneously. When two routes match the path and have the same regex_priority, the older one (lowest created_at) is used. Note that the priority for non-regex routes is different (longer non-regex routes are matched before shorter ones). Defaults to 0.Example:@enroute.rest.command(f"/users/{{uuid:{UUID_REGEX.pattern}}}/jwt","POST",authenticated=True,authorized_groups=["admin"],regex_priority=2)@enroute.broker.command("GetUserJWT")asyncdeffoo(self,request:Request)->Response:...Route pathIt is important to know that it is best to define routes with a regular expression when it is an id, uuid or similar. This is to avoid collisions with similar routes.
Instead of using:@enroute.rest.command("/users/{uuid}","POST")Use:importreUUID_REGEX=re.compile(r"\w{8}-\w{4}-\w{4}-\w{4}-\w{12}")@enroute.rest.command(f"/users/{{uuid:{UUID_REGEX.pattern}}}","POST")AuthenticationModifyconfig.ymlfile. Add new middleware and modify discovery section:...middleware:...- minos.plugins.kong.middlewarediscovery:connector:minos.networks.DiscoveryConnectorclient:minos.plugins.kong.KongDiscoveryClienthost:localhostauth-type:basic-authport:8001...Currently, there are 2 possible types of authentication:basic-authjwtFor jwt auth type you can specify default token expiration. Example:...middleware:...- minos.plugins.kong.middlewarediscovery:connector:minos.networks.DiscoveryConnectorclient:minos.plugins.kong.KongDiscoveryClienthost:localhostauth-type:jwttoken-exp:60# secondsport:8001...JWT Token creation & refreshExample on how to create and refresh token. You need to store in database or similar the secret and key returned form kong in order to refresh existing token.fromminos.commonimport(UUID_REGEX,NotProvidedException,Config,Inject,)fromminos.cqrsimport(CommandService,)fromminos.networksimport(Request,Response,enroute,)from..aggregatesimport(User,)fromminos.plugins.kongimportKongClientclassUserCommandService(CommandService):"""UserCommandService class."""def__init__(self,*args,**kwargs):super().__init__(*args,**kwargs)self.kong=self._get_kong_client()@staticmethod@Inject()def_get_kong_client(config:Config)->KongClient:"""Get the service name."""ifconfigisNone:raiseNotProvidedException("The config object must be provided.")returnKongClient.from_config(config)@enroute.rest.command(f"/users/{{uuid:{UUID_REGEX.pattern}}}/jwt","POST",authenticated=True,authorized_groups=["admin"],regex_priority=3)@enroute.broker.command("GetUserJWT")asyncdefcreate_user_jwt(self,request:Request)->Response:params=awaitrequest.params()uuid=params["uuid"]user=awaitUser.get(uuid)ifuser.uuid==request.user:token=awaitself.add_jwt_to_consumer(request.headers.get("X-Consumer-ID"))returnResponse({"token":token})else:returnResponse(status=404)asyncdefadd_jwt_to_consumer(self,consumer:str):resp=awaitself.kong.add_jwt_to_consumer(consumer=consumer)res=resp.json()self.key=res['key']self.secret=res['secret']token=awaitself.kong.generate_jwt_token(key=self.key,secret=self.secret)returntoken@enroute.rest.command(f"/users/{{uuid:{UUID_REGEX.pattern}}}/refresh-jwt","POST",authenticated=True,authorized_groups=["admin"],regex_priority=3)@enroute.broker.command("RefreshJWT")asyncdefrefresh_jwt(self,request:Request)->Response:token=awaitself.kong.generate_jwt_token(key=self.key,secret=self.secret)returnResponse({"token":token})For the route to be authenticated with the method specified above, a parameter calledauthenticatedmust be passed:classCategoryCommandService(CommandService):@enroute.rest.command("/categories","POST",authenticated=True,authorized_groups=["super_admin","admin"])@enroute.broker.command("CreateCategory")asyncdefcreate_category(self,request:Request)->Response:try:content=awaitrequest.content()category=awaitCategory.create(**content)returnResponse(category)exceptException:raiseResponseException("An error occurred during category creation.")Ifauthorized_groupsis also specified, this means that ACL will be enabled for that path and only users in the specified group will be allowed access.Example of how to create a user and add them to a group:fromminos.commonimport(NotProvidedException,Config,Inject,)fromminos.cqrsimport(CommandService,)fromminos.networksimport(Request,Response,enroute,)from..aggregatesimport(Role,User,)fromminos.plugins.kongimportKongClientclassUserCommandService(CommandService):"""UserCommandService class."""@enroute.rest.command("/users","POST")@enroute.broker.command("CreateUser")asyncdefcreate_user(self,request:Request)->Response:"""Create a new ``User`` instance.:param request: The ``Request`` instance.:return: A ``Response`` instance."""content=awaitrequest.content()active=Trueif"active"incontent:active=content["active"]user=User(name=content["name"],surname=content["surname"],email=content["email"],telephone=content["telephone"],role=content["role"],active=active,)awaituser.save()kong=KongClient(self._get_kong_url())consumer_raw=awaitkong.create_consumer(username=f"{user.name}{user.surname}",user=user.uuid,tags=[])consumer=consumer_raw.json()basic_auth=awaitkong.add_basic_auth_to_consumer(username=f"{user.name.lower()}_{user.surname.lower()}",password=content["password"],consumer=consumer["id"])acl=awaitkong.add_acl_to_consumer(role=user.role.name.lower(),consumer=consumer["id"])returnResponse(user)@staticmethod@Inject()def_get_kong_url(config:Config)->str:"""Get the service name."""ifconfigisNone:raiseNotProvidedException("The config object must be provided.")returnf"http://{config.get_by_key('discovery.host')}:{config.get_by_key('discovery.port')}"Generate JWT example:fromminos.commonimport(UUID_REGEX,NotProvidedException,Config,Inject,)fromminos.cqrsimport(CommandService,)fromminos.networksimport(Request,Response,enroute,)from..aggregatesimport(Role,User,)fromminos.plugins.kongimportKongClientclassUserCommandService(CommandService):"""UserCommandService class."""@enroute.rest.command(f"/users/{{uuid:{UUID_REGEX.pattern}}}/jwt","POST",authenticated=True,authorized_groups=["admin"])@enroute.broker.command("GetUserJWT")asyncdefcreate_user_jwt(self,request:Request)->Response:params=awaitrequest.params()uuid=params["uuid"]user=awaitUser.get(uuid)ifuser.uuid==request.user:kong=KongClient(self._get_kong_url())jwt=awaitkong.add_jwt_to_consumer(request.headers.get("X-Consumer-ID"))returnResponse(jwt.json())else:returnResponse(status=404)@staticmethod@Inject()def_get_kong_url(config:Config)->str:"""Get the service name."""ifconfigisNone:raiseNotProvidedException("The config object must be provided.")returnf"http://{config.get_by_key('discovery.host')}:{config.get_by_key('discovery.port')}"You can get read the official docshere.DocumentationThe official API Reference is publicly available at theGitHub Pages.Source CodeThe source code of this project is hosted at theGitHub Repository.Getting HelpFor usage questions, the best place to go to isStackOverflow.Discussion and DevelopmentMost development discussions take place over theGitHub Issues. In addition, aGitter channelis available for development-related questions.LicenseThis project is distributed under theMITlicense.