Skip to content
This repository has been archived by the owner on Jul 4, 2024. It is now read-only.

Commit

Permalink
Mojaloop Oracle: Added new module that translates from G2P Connect to…
Browse files Browse the repository at this point in the history
… ML Oracle API
  • Loading branch information
lalithkota committed Nov 25, 2023
1 parent 6538bc5 commit d099676
Show file tree
Hide file tree
Showing 15 changed files with 201 additions and 24 deletions.
2 changes: 1 addition & 1 deletion api-docs/generated/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -2330,4 +2330,4 @@
}
}
}
}
}
4 changes: 3 additions & 1 deletion main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
# ruff: noqa: I001

from social_payments_account_registry.app import Initializer as SparInitializer
from openg2p_fastapi_auth.app import Initializer as AuthInitializer
from spar_mapper_g2p_connect.app import Initializer as SparG2pConnectMapperInitializer
from spar_mojaloop_als_oracle.app import Initializer as MojaloopApiInitializer
from openg2p_common_g2pconnect_id_mapper.app import (
Initializer as G2pConnectMapperInitializer,
)
from openg2p_fastapi_auth.app import Initializer as AuthInitializer
from openg2p_fastapi_common.ping import PingInitializer

# from spar_mapper_g2p_connect.app import Initializer as SparG2PConnectMapperInitializer
Expand All @@ -16,6 +17,7 @@
AuthInitializer()
G2pConnectMapperInitializer()
SparG2pConnectMapperInitializer()
MojaloopApiInitializer()
PingInitializer()

main_init.main()
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from typing import List
from typing import Dict, List

import parse
from openg2p_fastapi_common.service import BaseService

from ..models.key_value import KeyValuePair
Expand All @@ -16,6 +17,12 @@ def construct(self, values: List[KeyValuePair], strategy: str):
**{key_value.key: key_value.value for key_value in values}
)

def deconstruct(self, value: str, strategy: str) -> Dict:
parse_res = parse.parse(strategy, value)
if parse_res:
return parse_res.named
return None

async def id_construct(self, id_values: List[KeyValuePair], id_provider_id: int):
id_provider: IdProvider = await IdProvider.get_by_id(id_provider_id)
strategy: FaConstructStrategy = await FaConstructStrategy.get_by_id(
Expand Down
6 changes: 6 additions & 0 deletions spar-mapper-g2p-connect/src/spar_mapper_g2p_connect/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,9 @@ class Settings(G2PConnectMapperSettings, Settings):
model_config = SettingsConfigDict(
env_prefix="spar_g2p_connect_", env_file=".env", extra="allow"
)

callback_api_common_prefix: str = "/internal/callback"

mapper_resolve_sender_url: str = "http://localhost:8000/internal/callback/mapper"
mapper_link_sender_url: str = "http://localhost:8000/internal/callback/mapper"
mapper_update_sender_url: str = "http://localhost:8000/internal/callback/mapper"
2 changes: 1 addition & 1 deletion spar-mojaloop-als-oracle/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "spar-mojaloop-oracle"
name = "spar-mojaloop-als-oracle"
version = "0.1.0"
authors = [
{ name="OpenG2P", email="[email protected]" },
Expand Down
6 changes: 6 additions & 0 deletions spar-mojaloop-als-oracle/src/spar_mojaloop_als_oracle/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,15 @@
from openg2p_fastapi_common.app import Initializer

from .controllers.als_oracle import ALSOracleController
from .exception import ExceptionHandler
from .services.als_oracle_service import MojaloopOracleService


class Initializer(Initializer):
def initialize(self, **kwargs):
# Initialize all Services, Controllers, any utils here.
ExceptionHandler()

MojaloopOracleService()

ALSOracleController().post_init()
11 changes: 6 additions & 5 deletions spar-mojaloop-als-oracle/src/spar_mojaloop_als_oracle/config.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
from openg2p_common_g2pconnect_id_mapper.config import (
Settings as G2PConnectMapperSettings,
)
from typing import Dict

from openg2p_fastapi_common.config import Settings
from pydantic_settings import SettingsConfigDict


class Settings(G2PConnectMapperSettings, Settings):
class Settings(Settings):
model_config = SettingsConfigDict(
env_prefix="spar_mojaloop_als_oracle_", env_file=".env", extra="allow"
env_prefix="spar_ml_oracle_", env_file=".env", extra="allow"
)

type_fa_prefix_map: Dict = {"ACCOUNT_ID": "account:"}
Original file line number Diff line number Diff line change
@@ -1,32 +1,23 @@
import asyncio
import uuid

from starlette.status import HTTP_202_ACCEPTED

from openg2p_fastapi_common.controller import BaseController
from openg2p_common_g2pconnect_id_mapper.service.resolve import MapperResolveService

from ..config import Settings

_config = Settings.get_config()
from ..models.participant import ParticipantsTypeIDGetResponse
from ..services.als_oracle_service import MojaloopOracleService


class ALSOracleController(BaseController):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.mapper_resolve_service = MapperResolveService.get_component()
self.als_oracle = MojaloopOracleService.get_component()

self.router.tags += ["mojaloop-als-oracle"]
self.router.prefix += "/internal/mojaloop"

self.router.add_api_route(
"/participants/{type}/{id}",
self.get_participants,
status_code=HTTP_202_ACCEPTED,
responses={200: {"model": ParticipantsTypeIDGetResponse}},
methods=["GET"],
)

async def get_participants(self, type: str, id: str):
# Perform any extra validations here
async def process_get_participants():
pass
asyncio.create_task(process_get_participants())
return await self.als_oracle.get_participants(type, id)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from openg2p_fastapi_common.errors import BaseAppException


class BaseMojaloopException(BaseAppException):
pass
33 changes: 33 additions & 0 deletions spar-mojaloop-als-oracle/src/spar_mojaloop_als_oracle/exception.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import logging

from fastapi.responses import ORJSONResponse
from openg2p_fastapi_common.component import BaseComponent
from openg2p_fastapi_common.context import app_registry

from .errors import BaseMojaloopException
from .models.error import ErrorInformation, ErrorInformationResponse

_logger = logging.getLogger(__name__)


class ExceptionHandler(BaseComponent):
def __init__(self, name="", **kwargs):
super().__init__(name=name)

app_registry.get().add_exception_handler(
BaseMojaloopException, self.base_mojaloop_exception_handler
)

async def base_mojaloop_exception_handler(
self, request, exc: BaseMojaloopException
):
_logger.exception(f"Received Mojaloop Exception: {exc}")
res = ErrorInformationResponse(
errorInformation=ErrorInformation(
errorCode=exc.code,
errorDescription=exc.message,
),
)
return ORJSONResponse(
content=res.model_dump(), status_code=exc.status_code, headers=exc.headers
)
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from typing import List

from pydantic import BaseModel


class Extension(BaseModel):
key: str
value: str


class ExtensionList(BaseModel):
extension: List[Extension]


class ErrorInformation(BaseModel):
errorCode: str
errorDescription: str
extensionList: ExtensionList = None


class ErrorInformationResponse(BaseModel):
errorInformation: ErrorInformation
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from typing import List

from pydantic import BaseModel


class PartyTypeIdInfo(BaseModel):
fspId: str


class ParticipantsTypeIDGetResponse(BaseModel):
partyList: List[PartyTypeIdInfo]
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
from typing import List

from openg2p_common_g2pconnect_id_mapper.models.common import (
MapperValue,
RequestStatusEnum,
)
from openg2p_common_g2pconnect_id_mapper.service.resolve import MapperResolveService
from openg2p_fastapi_common.service import BaseService
from social_payments_account_registry.models.orm.fa_construct_strategy import (
FaConstructStrategy,
)
from social_payments_account_registry.models.orm.provider import DfspProvider
from social_payments_account_registry.services.construct_service import ConstructService

from ..config import Settings
from ..errors import BaseMojaloopException
from ..models.participant import ParticipantsTypeIDGetResponse, PartyTypeIdInfo

_config = Settings.get_config()


class MojaloopOracleService(BaseService):
def __init__(self, name="", **kwargs):
super().__init__(name=name, **kwargs)

self.mapper_resolve_service = MapperResolveService.get_component()
self.construct_service = ConstructService.get_component()

async def get_participants(self, type: str, id: str):
if type not in _config.type_fa_prefix_map:
# Make this compliant to Mojaloop
raise BaseMojaloopException(
"ML-REQ-100",
"Given type is not supported by this oracle.",
http_status_code=400,
)

fa_prefix = _config.type_fa_prefix_map[type]

response = None
# The Following is only possible if the ID Mapper allows
# seaching through the FAs using the resolve API.
# This is possible in Sunbird's G2P ID Mapper because of a limitation
try:
res = await self.mapper_resolve_service.resolve_request_sync(
[
MapperValue(
fa=f"{fa_prefix}{id}",
),
],
)
res = list(res.refs.values())[0]
if res.status == RequestStatusEnum.succ:
response = res.fa
except Exception as e:
raise BaseMojaloopException(
"ML-REQ-200",
"Given Type and ID combination is invalid or not found in this oracle.",
http_status_code=400,
) from e

if not response:
raise BaseMojaloopException(
"ML-REQ-200",
"Given Type and ID combination is invalid or not found in this oracle.",
http_status_code=400,
)

dfsp_id: str = None

dfsp_providers: List[DfspProvider] = await DfspProvider.get_all()
for dfsp_prov in dfsp_providers:
strategy: FaConstructStrategy = await FaConstructStrategy.get_by_id(
dfsp_prov.strategy_id
)
res = self.construct_service.deconstruct(response, strategy.strategy)
if res and dfsp_prov.code in res.values():
dfsp_id = dfsp_prov.code
break

if not dfsp_id:
raise BaseMojaloopException(
"ML-REQ-300",
"FinancialAddress response is not recognisable by this oracle or by Mojaloop.",
http_status_code=400,
)

return ParticipantsTypeIDGetResponse(
partyList=[
PartyTypeIdInfo(fspId=dfsp_id),
]
)
1 change: 1 addition & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ description = run the tests with pytest
commands = pytest
--cov=social-payments-account-registry
--cov=spar-mapper-g2p-connect
--cov=spar-mojaloop-als-oracle
deps =
git+https://github.com/openg2p/openg2p-fastapi-common.git@develop\#egg=openg2p-fastapi-common\&subdirectory=openg2p-fastapi-common \
git+https://github.com/openg2p/openg2p-fastapi-common.git@develop\#egg=openg2p-fastapi-auth\&subdirectory=openg2p-fastapi-auth \
Expand Down

0 comments on commit d099676

Please sign in to comment.