Compare commits
5 Commits
master
...
lash/meta-
Author | SHA1 | Date | |
---|---|---|---|
8c453a9190 | |||
b101496402 | |||
|
80dc84eb3a | ||
|
fe07d28193 | ||
|
aed58e62df |
@ -6,6 +6,9 @@ MAX_BODY_LENGTH=1024
|
|||||||
PASSWORD_PEPPER=QYbzKff6NhiQzY3ygl2BkiKOpER8RE/Upqs/5aZWW+I=
|
PASSWORD_PEPPER=QYbzKff6NhiQzY3ygl2BkiKOpER8RE/Upqs/5aZWW+I=
|
||||||
SERVICE_CODE=*483*46#
|
SERVICE_CODE=*483*46#
|
||||||
|
|
||||||
|
[phone_number]
|
||||||
|
REGION=KE
|
||||||
|
|
||||||
[ussd]
|
[ussd]
|
||||||
MENU_FILE=/usr/src/data/ussd_menu.json
|
MENU_FILE=/usr/src/data/ussd_menu.json
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ class ActionDataNotFoundError(OSError):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class UserMetadataNotFoundError(OSError):
|
class MetadataNotFoundError(OSError):
|
||||||
"""Raised when metadata is expected but not available in cache."""
|
"""Raised when metadata is expected but not available in cache."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -31,3 +31,10 @@ class UnsupportedMethodError(OSError):
|
|||||||
class CachedDataNotFoundError(OSError):
|
class CachedDataNotFoundError(OSError):
|
||||||
"""Raised when the method passed to the make request function is unsupported."""
|
"""Raised when the method passed to the make request function is unsupported."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class MetadataStoreError(Exception):
|
||||||
|
"""Raised when metadata storage fails"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@ -3,7 +3,10 @@
|
|||||||
# third-party imports
|
# third-party imports
|
||||||
import requests
|
import requests
|
||||||
from chainlib.eth.address import to_checksum
|
from chainlib.eth.address import to_checksum
|
||||||
from hexathon import add_0x
|
from hexathon import (
|
||||||
|
add_0x,
|
||||||
|
strip_0x,
|
||||||
|
)
|
||||||
|
|
||||||
# local imports
|
# local imports
|
||||||
from cic_ussd.error import UnsupportedMethodError
|
from cic_ussd.error import UnsupportedMethodError
|
||||||
@ -40,4 +43,4 @@ def blockchain_address_to_metadata_pointer(blockchain_address: str):
|
|||||||
:return:
|
:return:
|
||||||
:rtype:
|
:rtype:
|
||||||
"""
|
"""
|
||||||
return bytes.fromhex(blockchain_address[2:])
|
return bytes.fromhex(strip_0x(blockchain_address))
|
||||||
|
47
apps/cic-ussd/cic_ussd/metadata/phone.py
Normal file
47
apps/cic-ussd/cic_ussd/metadata/phone.py
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
# standard imports
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
|
# external imports
|
||||||
|
from cic_types.models.person import generate_metadata_pointer
|
||||||
|
from cic_ussd.metadata import make_request
|
||||||
|
from cic_ussd.metadata.signer import Signer
|
||||||
|
|
||||||
|
# local imports
|
||||||
|
from cic_ussd.error import MetadataStoreError
|
||||||
|
|
||||||
|
logg = logging.getLogger().getChild(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class PhonePointerMetadata:
|
||||||
|
|
||||||
|
base_url = None
|
||||||
|
|
||||||
|
def __init__(self, identifier: bytes, engine: str):
|
||||||
|
"""
|
||||||
|
:param identifier:
|
||||||
|
:type identifier:
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.headers = {
|
||||||
|
'X-CIC-AUTOMERGE': 'server',
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
self.identifier = identifier
|
||||||
|
self.metadata_pointer = generate_metadata_pointer(
|
||||||
|
identifier=self.identifier,
|
||||||
|
cic_type='cic.phone'
|
||||||
|
)
|
||||||
|
if self.base_url:
|
||||||
|
self.url = os.path.join(self.base_url, self.metadata_pointer)
|
||||||
|
self.engine = engine
|
||||||
|
|
||||||
|
|
||||||
|
def create(self, data: str):
|
||||||
|
try:
|
||||||
|
result = make_request(method='POST', url=self.url, data=data, headers=self.headers)
|
||||||
|
metadata = result.content
|
||||||
|
self.edit(data=metadata, engine=self.engine)
|
||||||
|
result.raise_for_status()
|
||||||
|
except requests.exceptions.HTTPError as error:
|
||||||
|
raise MetadataStoreError(error)
|
@ -12,6 +12,7 @@ from cic_ussd.chain import Chain
|
|||||||
from cic_ussd.metadata import make_request
|
from cic_ussd.metadata import make_request
|
||||||
from cic_ussd.metadata.signer import Signer
|
from cic_ussd.metadata.signer import Signer
|
||||||
from cic_ussd.redis import cache_data
|
from cic_ussd.redis import cache_data
|
||||||
|
from cic_ussd.error import MetadataStoreError
|
||||||
|
|
||||||
logg = logging.getLogger()
|
logg = logging.getLogger()
|
||||||
|
|
||||||
@ -28,7 +29,7 @@ class UserMetadata:
|
|||||||
:param identifier:
|
:param identifier:
|
||||||
:type identifier:
|
:type identifier:
|
||||||
"""
|
"""
|
||||||
self. headers = {
|
self.headers = {
|
||||||
'X-CIC-AUTOMERGE': 'server',
|
'X-CIC-AUTOMERGE': 'server',
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
}
|
}
|
||||||
@ -49,7 +50,7 @@ class UserMetadata:
|
|||||||
logg.info(f'Get sign material response status: {result.status_code}')
|
logg.info(f'Get sign material response status: {result.status_code}')
|
||||||
result.raise_for_status()
|
result.raise_for_status()
|
||||||
except requests.exceptions.HTTPError as error:
|
except requests.exceptions.HTTPError as error:
|
||||||
raise RuntimeError(error)
|
raise MetadataStoreError(error)
|
||||||
|
|
||||||
def edit(self, data: bytes, engine: str):
|
def edit(self, data: bytes, engine: str):
|
||||||
"""
|
"""
|
||||||
@ -79,7 +80,7 @@ class UserMetadata:
|
|||||||
logg.info(f'Signed content submission status: {result.status_code}.')
|
logg.info(f'Signed content submission status: {result.status_code}.')
|
||||||
result.raise_for_status()
|
result.raise_for_status()
|
||||||
except requests.exceptions.HTTPError as error:
|
except requests.exceptions.HTTPError as error:
|
||||||
raise RuntimeError(error)
|
raise MetadataStoreError(error)
|
||||||
|
|
||||||
def query(self):
|
def query(self):
|
||||||
result = make_request(method='GET', url=self.url)
|
result = make_request(method='GET', url=self.url)
|
||||||
@ -99,4 +100,4 @@ class UserMetadata:
|
|||||||
logg.info('The data is not available and might need to be added.')
|
logg.info('The data is not available and might need to be added.')
|
||||||
result.raise_for_status()
|
result.raise_for_status()
|
||||||
except requests.exceptions.HTTPError as error:
|
except requests.exceptions.HTTPError as error:
|
||||||
raise RuntimeError(error)
|
raise MetadataNotFoundError(error)
|
||||||
|
@ -15,7 +15,7 @@ from cic_ussd.balance import BalanceManager, compute_operational_balance, get_ca
|
|||||||
from cic_ussd.chain import Chain
|
from cic_ussd.chain import Chain
|
||||||
from cic_ussd.db.models.user import AccountStatus, User
|
from cic_ussd.db.models.user import AccountStatus, User
|
||||||
from cic_ussd.db.models.ussd_session import UssdSession
|
from cic_ussd.db.models.ussd_session import UssdSession
|
||||||
from cic_ussd.error import UserMetadataNotFoundError
|
from cic_ussd.error import MetadataNotFoundError
|
||||||
from cic_ussd.menu.ussd_menu import UssdMenu
|
from cic_ussd.menu.ussd_menu import UssdMenu
|
||||||
from cic_ussd.metadata import blockchain_address_to_metadata_pointer
|
from cic_ussd.metadata import blockchain_address_to_metadata_pointer
|
||||||
from cic_ussd.phone_number import get_user_by_phone_number
|
from cic_ussd.phone_number import get_user_by_phone_number
|
||||||
@ -235,7 +235,7 @@ def process_display_user_metadata(user: User, display_key: str):
|
|||||||
products=products
|
products=products
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
raise UserMetadataNotFoundError(f'Expected user metadata but found none in cache for key: {user.blockchain_address}')
|
raise MetadataNotFoundError(f'Expected user metadata but found none in cache for key: {user.blockchain_address}')
|
||||||
|
|
||||||
|
|
||||||
def process_account_statement(user: User, display_key: str, ussd_session: dict):
|
def process_account_statement(user: User, display_key: str, ussd_session: dict):
|
||||||
|
@ -27,6 +27,7 @@ from cic_ussd.metadata.user import UserMetadata
|
|||||||
from cic_ussd.operations import (define_response_with_content,
|
from cic_ussd.operations import (define_response_with_content,
|
||||||
process_menu_interaction_requests,
|
process_menu_interaction_requests,
|
||||||
define_multilingual_responses)
|
define_multilingual_responses)
|
||||||
|
from cic_ussd.phone_number import process_phone_number
|
||||||
from cic_ussd.redis import InMemoryStore
|
from cic_ussd.redis import InMemoryStore
|
||||||
from cic_ussd.requests import (get_request_endpoint,
|
from cic_ussd.requests import (get_request_endpoint,
|
||||||
get_request_method,
|
get_request_method,
|
||||||
@ -151,6 +152,10 @@ def application(env, start_response):
|
|||||||
external_session_id = post_data.get('sessionId')
|
external_session_id = post_data.get('sessionId')
|
||||||
user_input = post_data.get('text')
|
user_input = post_data.get('text')
|
||||||
|
|
||||||
|
# add validation for phone number
|
||||||
|
if phone_number:
|
||||||
|
phone_number = process_phone_number(phone_number=phone_number, region=config.get('PHONE_NUMBER_REGION'))
|
||||||
|
|
||||||
# validate ip address
|
# validate ip address
|
||||||
if not check_ip(config=config, env=env):
|
if not check_ip(config=config, env=env):
|
||||||
start_response('403 Sneaky, sneaky', errors_headers)
|
start_response('403 Sneaky, sneaky', errors_headers)
|
||||||
|
@ -11,7 +11,7 @@ from cic_types.models.person import generate_vcard_from_contact_data, manage_ide
|
|||||||
# local imports
|
# local imports
|
||||||
from cic_ussd.chain import Chain
|
from cic_ussd.chain import Chain
|
||||||
from cic_ussd.db.models.user import User
|
from cic_ussd.db.models.user import User
|
||||||
from cic_ussd.error import UserMetadataNotFoundError
|
from cic_ussd.error import MetadataNotFoundError
|
||||||
from cic_ussd.metadata import blockchain_address_to_metadata_pointer
|
from cic_ussd.metadata import blockchain_address_to_metadata_pointer
|
||||||
from cic_ussd.operations import save_to_in_memory_ussd_session_data
|
from cic_ussd.operations import save_to_in_memory_ussd_session_data
|
||||||
from cic_ussd.redis import get_cached_data
|
from cic_ussd.redis import get_cached_data
|
||||||
@ -181,7 +181,7 @@ def edit_user_metadata_attribute(state_machine_data: Tuple[str, dict, User]):
|
|||||||
user_metadata = get_cached_data(key=key)
|
user_metadata = get_cached_data(key=key)
|
||||||
|
|
||||||
if not user_metadata:
|
if not user_metadata:
|
||||||
raise UserMetadataNotFoundError(f'Expected user metadata but found none in cache for key: {blockchain_address}')
|
raise MetadataNotFoundError(f'Expected user metadata but found none in cache for key: {blockchain_address}')
|
||||||
|
|
||||||
given_name = ussd_session.get('session_data').get('given_name')
|
given_name = ussd_session.get('session_data').get('given_name')
|
||||||
family_name = ussd_session.get('session_data').get('family_name')
|
family_name = ussd_session.get('session_data').get('family_name')
|
||||||
|
@ -5,6 +5,7 @@ import celery
|
|||||||
import sqlalchemy
|
import sqlalchemy
|
||||||
|
|
||||||
# local imports
|
# local imports
|
||||||
|
from cic_ussd.error import MetadataStoreError
|
||||||
|
|
||||||
|
|
||||||
class CriticalTask(celery.Task):
|
class CriticalTask(celery.Task):
|
||||||
@ -18,3 +19,9 @@ class CriticalSQLAlchemyTask(CriticalTask):
|
|||||||
sqlalchemy.exc.DatabaseError,
|
sqlalchemy.exc.DatabaseError,
|
||||||
sqlalchemy.exc.TimeoutError,
|
sqlalchemy.exc.TimeoutError,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class CriticalMetadataTask(CriticalTask):
|
||||||
|
autoretry_for = (
|
||||||
|
MetadataStoreError,
|
||||||
|
)
|
||||||
|
@ -54,6 +54,18 @@ def process_account_creation_callback(self, result: str, url: str, status_code:
|
|||||||
session.commit()
|
session.commit()
|
||||||
session.close()
|
session.close()
|
||||||
|
|
||||||
|
queue = self.request.delivery_info.get('routing_key')
|
||||||
|
s = celery.signature(
|
||||||
|
'cic_ussd.tasks.metadata.add_phone_pointer',
|
||||||
|
[
|
||||||
|
result,
|
||||||
|
phone_number,
|
||||||
|
'pgp',
|
||||||
|
],
|
||||||
|
queue=queue,
|
||||||
|
)
|
||||||
|
s.apply_async()
|
||||||
|
|
||||||
# expire cache
|
# expire cache
|
||||||
cache.expire(task_id, timedelta(seconds=180))
|
cache.expire(task_id, timedelta(seconds=180))
|
||||||
|
|
||||||
|
@ -8,6 +8,8 @@ import celery
|
|||||||
# local imports
|
# local imports
|
||||||
from cic_ussd.metadata import blockchain_address_to_metadata_pointer
|
from cic_ussd.metadata import blockchain_address_to_metadata_pointer
|
||||||
from cic_ussd.metadata.user import UserMetadata
|
from cic_ussd.metadata.user import UserMetadata
|
||||||
|
from cic_ussd.metadata.phone import PhonePointerMetadata
|
||||||
|
from cic_ussd.tasks.base import CriticalMetadataTask
|
||||||
|
|
||||||
celery_app = celery.current_app
|
celery_app = celery.current_app
|
||||||
logg = logging.getLogger()
|
logg = logging.getLogger()
|
||||||
@ -46,3 +48,10 @@ def edit_user_metadata(blockchain_address: str, data: bytes, engine: str):
|
|||||||
identifier = blockchain_address_to_metadata_pointer(blockchain_address=blockchain_address)
|
identifier = blockchain_address_to_metadata_pointer(blockchain_address=blockchain_address)
|
||||||
user_metadata_client = UserMetadata(identifier=identifier)
|
user_metadata_client = UserMetadata(identifier=identifier)
|
||||||
user_metadata_client.edit(data=data, engine=engine)
|
user_metadata_client.edit(data=data, engine=engine)
|
||||||
|
|
||||||
|
|
||||||
|
@celery_app.task(bind=True, base=CriticalMetadataTask)
|
||||||
|
def add_phone_pointer(blockchain_address: str, phone: str, engine: str):
|
||||||
|
stripped_address = strip_0x(blockchain_address)
|
||||||
|
phone_metadata_client = PhonePointerMetadata(identifier=phone, engine=engine)
|
||||||
|
phone_metadata_client.create(data=stripped_address)
|
||||||
|
Loading…
Reference in New Issue
Block a user