Compare commits
11 Commits
philip/exp
...
lash/cic-c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1b93d83e56
|
||
|
|
dc17cec2d2
|
||
|
|
5177945a7d
|
||
|
|
5d4fe04987 | ||
|
|
9fac3e9e53 | ||
|
|
792ab1e2fd | ||
|
|
54f54b6fb8 | ||
|
|
4612a4fa13
|
||
|
|
7ada95c6c1
|
||
|
|
4d2ae5023c | ||
|
|
a40a3102e4
|
@@ -20,5 +20,5 @@ TRANSITIONS=/usr/src/cic-ussd/transitions/
|
||||
|
||||
[client]
|
||||
host =
|
||||
port =
|
||||
port =
|
||||
ssl =
|
||||
|
||||
@@ -8,12 +8,12 @@ from cic_types.processor import generate_metadata_pointer
|
||||
|
||||
# local imports
|
||||
from cic_ussd.chain import Chain
|
||||
from cic_ussd.db.models.account import Account
|
||||
from cic_ussd.db.models.user import User
|
||||
from cic_ussd.metadata import blockchain_address_to_metadata_pointer
|
||||
from cic_ussd.redis import get_cached_data
|
||||
|
||||
|
||||
def define_account_tx_metadata(user: Account):
|
||||
def define_account_tx_metadata(user: User):
|
||||
# get sender metadata
|
||||
identifier = blockchain_address_to_metadata_pointer(
|
||||
blockchain_address=user.blockchain_address
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Create account table
|
||||
"""Create user table
|
||||
|
||||
Revision ID: f289e8510444
|
||||
Revises:
|
||||
@@ -17,7 +17,7 @@ depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.create_table('account',
|
||||
op.create_table('user',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('blockchain_address', sa.String(), nullable=False),
|
||||
sa.Column('phone_number', sa.String(), nullable=False),
|
||||
@@ -29,11 +29,11 @@ def upgrade():
|
||||
sa.Column('updated', sa.DateTime(), nullable=False),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_index(op.f('ix_account_phone_number'), 'account', ['phone_number'], unique=True)
|
||||
op.create_index(op.f('ix_account_blockchain_address'), 'account', ['blockchain_address'], unique=True)
|
||||
op.create_index(op.f('ix_user_phone_number'), 'user', ['phone_number'], unique=True)
|
||||
op.create_index(op.f('ix_user_blockchain_address'), 'user', ['blockchain_address'], unique=True)
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.drop_index(op.f('ix_account_blockchain_address'), table_name='account')
|
||||
op.drop_index(op.f('ix_account_phone_number'), table_name='account')
|
||||
op.drop_table('account')
|
||||
op.drop_index(op.f('ix_user_blockchain_address'), table_name='user')
|
||||
op.drop_index(op.f('ix_user_phone_number'), table_name='user')
|
||||
op.drop_table('user')
|
||||
|
||||
@@ -16,12 +16,12 @@ class AccountStatus(IntEnum):
|
||||
RESET = 4
|
||||
|
||||
|
||||
class Account(SessionBase):
|
||||
class User(SessionBase):
|
||||
"""
|
||||
This class defines a user record along with functions responsible for hashing the user's corresponding password and
|
||||
subsequently verifying a password's validity given an input to compare against the persisted hash.
|
||||
"""
|
||||
__tablename__ = 'account'
|
||||
__tablename__ = 'user'
|
||||
|
||||
blockchain_address = Column(String)
|
||||
phone_number = Column(String)
|
||||
@@ -38,7 +38,7 @@ class Account(SessionBase):
|
||||
self.account_status = AccountStatus.PENDING.value
|
||||
|
||||
def __repr__(self):
|
||||
return f'<Account: {self.blockchain_address}>'
|
||||
return f'<User: {self.blockchain_address}>'
|
||||
|
||||
def create_password(self, password):
|
||||
"""This method takes a password value and hashes the value before assigning it to the corresponding
|
||||
@@ -10,7 +10,7 @@ from tinydb.table import Document
|
||||
from typing import Optional
|
||||
|
||||
# local imports
|
||||
from cic_ussd.db.models.account import Account
|
||||
from cic_ussd.db.models.user import User
|
||||
from cic_ussd.db.models.ussd_session import UssdSession
|
||||
from cic_ussd.db.models.task_tracker import TaskTracker
|
||||
from cic_ussd.menu.ussd_menu import UssdMenu
|
||||
@@ -143,10 +143,10 @@ def get_account_status(phone_number) -> str:
|
||||
:return: The user account status.
|
||||
:rtype: str
|
||||
"""
|
||||
user = Account.session.query(Account).filter_by(phone_number=phone_number).first()
|
||||
user = User.session.query(User).filter_by(phone_number=phone_number).first()
|
||||
status = user.get_account_status()
|
||||
Account.session.add(user)
|
||||
Account.session.commit()
|
||||
User.session.add(user)
|
||||
User.session.commit()
|
||||
|
||||
return status
|
||||
|
||||
@@ -269,12 +269,12 @@ def cache_account_creation_task_id(phone_number: str, task_id: str):
|
||||
redis_cache.persist(name=task_id)
|
||||
|
||||
|
||||
def process_current_menu(ussd_session: Optional[dict], user: Account, user_input: str) -> Document:
|
||||
def process_current_menu(ussd_session: Optional[dict], user: User, user_input: str) -> Document:
|
||||
"""This function checks user input and returns a corresponding ussd menu
|
||||
:param ussd_session: An in db ussd session object.
|
||||
:type ussd_session: UssdSession
|
||||
:param user: A user object.
|
||||
:type user: Account
|
||||
:type user: User
|
||||
:param user_input: The user's input.
|
||||
:type user_input: str
|
||||
:return: An in memory ussd menu object.
|
||||
@@ -324,7 +324,7 @@ def process_menu_interaction_requests(chain_str: str,
|
||||
|
||||
else:
|
||||
# get user
|
||||
user = Account.session.query(Account).filter_by(phone_number=phone_number).first()
|
||||
user = User.session.query(User).filter_by(phone_number=phone_number).first()
|
||||
|
||||
# find any existing ussd session
|
||||
existing_ussd_session = UssdSession.session.query(UssdSession).filter_by(
|
||||
@@ -390,10 +390,10 @@ def reset_pin(phone_number: str) -> str:
|
||||
:return: The status of the pin reset.
|
||||
:rtype: str
|
||||
"""
|
||||
user = Account.session.query(Account).filter_by(phone_number=phone_number).first()
|
||||
user = User.session.query(User).filter_by(phone_number=phone_number).first()
|
||||
user.reset_account_pin()
|
||||
Account.session.add(user)
|
||||
Account.session.commit()
|
||||
User.session.add(user)
|
||||
User.session.commit()
|
||||
|
||||
response = f'Pin reset for user {phone_number} is successful!'
|
||||
return response
|
||||
|
||||
@@ -5,7 +5,7 @@ from typing import Optional
|
||||
import phonenumbers
|
||||
|
||||
# local imports
|
||||
from cic_ussd.db.models.account import Account
|
||||
from cic_ussd.db.models.user import User
|
||||
|
||||
|
||||
def process_phone_number(phone_number: str, region: str):
|
||||
@@ -30,14 +30,14 @@ def process_phone_number(phone_number: str, region: str):
|
||||
return parsed_phone_number
|
||||
|
||||
|
||||
def get_user_by_phone_number(phone_number: str) -> Optional[Account]:
|
||||
def get_user_by_phone_number(phone_number: str) -> Optional[User]:
|
||||
"""This function queries the database for a user based on the provided phone number.
|
||||
:param phone_number: A valid phone number.
|
||||
:type phone_number: str
|
||||
:return: A user object matching a given phone number
|
||||
:rtype: Account|None
|
||||
:rtype: User|None
|
||||
"""
|
||||
# consider adding region to user's metadata
|
||||
phone_number = process_phone_number(phone_number=phone_number, region='KE')
|
||||
user = Account.session.query(Account).filter_by(phone_number=phone_number).first()
|
||||
user = User.session.query(User).filter_by(phone_number=phone_number).first()
|
||||
return user
|
||||
|
||||
@@ -13,7 +13,7 @@ from tinydb.table import Document
|
||||
from cic_ussd.account import define_account_tx_metadata, retrieve_account_statement
|
||||
from cic_ussd.balance import BalanceManager, compute_operational_balance, get_cached_operational_balance
|
||||
from cic_ussd.chain import Chain
|
||||
from cic_ussd.db.models.account import AccountStatus, Account
|
||||
from cic_ussd.db.models.user import AccountStatus, User
|
||||
from cic_ussd.db.models.ussd_session import UssdSession
|
||||
from cic_ussd.error import MetadataNotFoundError
|
||||
from cic_ussd.menu.ussd_menu import UssdMenu
|
||||
@@ -28,13 +28,13 @@ from cic_types.models.person import generate_metadata_pointer, get_contact_data_
|
||||
logg = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def process_pin_authorization(display_key: str, user: Account, **kwargs) -> str:
|
||||
def process_pin_authorization(display_key: str, user: User, **kwargs) -> str:
|
||||
"""
|
||||
This method provides translation for all ussd menu entries that follow the pin authorization pattern.
|
||||
:param display_key: The path in the translation files defining an appropriate ussd response
|
||||
:type display_key: str
|
||||
:param user: The user in a running USSD session.
|
||||
:type user: Account
|
||||
:type user: User
|
||||
:param kwargs: Any additional information required by the text values in the internationalization files.
|
||||
:type kwargs
|
||||
:return: A string value corresponding the ussd menu's text value.
|
||||
@@ -55,13 +55,13 @@ def process_pin_authorization(display_key: str, user: Account, **kwargs) -> str:
|
||||
)
|
||||
|
||||
|
||||
def process_exit_insufficient_balance(display_key: str, user: Account, ussd_session: dict):
|
||||
def process_exit_insufficient_balance(display_key: str, user: User, ussd_session: dict):
|
||||
"""This function processes the exit menu letting users their account balance is insufficient to perform a specific
|
||||
transaction.
|
||||
:param display_key: The path in the translation files defining an appropriate ussd response
|
||||
:type display_key: str
|
||||
:param user: The user requesting access to the ussd menu.
|
||||
:type user: Account
|
||||
:type user: User
|
||||
:param ussd_session: A JSON serialized in-memory ussd session object
|
||||
:type ussd_session: dict
|
||||
:return: Corresponding translation text response
|
||||
@@ -71,7 +71,7 @@ def process_exit_insufficient_balance(display_key: str, user: Account, ussd_sess
|
||||
operational_balance = get_cached_operational_balance(blockchain_address=user.blockchain_address)
|
||||
|
||||
# compile response data
|
||||
user_input = ussd_session.get('session_data').get('transaction_amount')
|
||||
user_input = ussd_session.get('user_input').split('*')[-1]
|
||||
transaction_amount = to_wei(value=int(user_input))
|
||||
token_symbol = 'SRF'
|
||||
|
||||
@@ -86,16 +86,16 @@ def process_exit_insufficient_balance(display_key: str, user: Account, ussd_sess
|
||||
amount=from_wei(transaction_amount),
|
||||
token_symbol=token_symbol,
|
||||
recipient_information=tx_recipient_information,
|
||||
token_balance=operational_balance,
|
||||
token_balance=operational_balance
|
||||
)
|
||||
|
||||
|
||||
def process_exit_successful_transaction(display_key: str, user: Account, ussd_session: dict):
|
||||
def process_exit_successful_transaction(display_key: str, user: User, ussd_session: dict):
|
||||
"""This function processes the exit menu after a successful initiation for a transfer of tokens.
|
||||
:param display_key: The path in the translation files defining an appropriate ussd response
|
||||
:type display_key: str
|
||||
:param user: The user requesting access to the ussd menu.
|
||||
:type user: Account
|
||||
:type user: User
|
||||
:param ussd_session: A JSON serialized in-memory ussd session object
|
||||
:type ussd_session: dict
|
||||
:return: Corresponding translation text response
|
||||
@@ -118,11 +118,11 @@ def process_exit_successful_transaction(display_key: str, user: Account, ussd_se
|
||||
)
|
||||
|
||||
|
||||
def process_transaction_pin_authorization(user: Account, display_key: str, ussd_session: dict):
|
||||
def process_transaction_pin_authorization(user: User, display_key: str, ussd_session: dict):
|
||||
"""This function processes pin authorization where making a transaction is concerned. It constructs a
|
||||
pre-transaction response menu that shows the details of the transaction.
|
||||
:param user: The user requesting access to the ussd menu.
|
||||
:type user: Account
|
||||
:type user: User
|
||||
:param display_key: The path in the translation files defining an appropriate ussd response
|
||||
:type display_key: str
|
||||
:param ussd_session: The USSD session determining what user data needs to be extracted and added to the menu's
|
||||
@@ -151,7 +151,7 @@ def process_transaction_pin_authorization(user: Account, display_key: str, ussd_
|
||||
)
|
||||
|
||||
|
||||
def process_account_balances(user: Account, display_key: str, ussd_session: dict):
|
||||
def process_account_balances(user: User, display_key: str, ussd_session: dict):
|
||||
"""
|
||||
:param user:
|
||||
:type user:
|
||||
@@ -205,7 +205,7 @@ def format_transactions(transactions: list, preferred_language: str):
|
||||
return formatted_transactions
|
||||
|
||||
|
||||
def process_display_user_metadata(user: Account, display_key: str):
|
||||
def process_display_user_metadata(user: User, display_key: str):
|
||||
"""
|
||||
:param user:
|
||||
:type user:
|
||||
@@ -238,7 +238,7 @@ def process_display_user_metadata(user: Account, display_key: str):
|
||||
raise MetadataNotFoundError(f'Expected person metadata but found none in cache for key: {key}')
|
||||
|
||||
|
||||
def process_account_statement(user: Account, display_key: str, ussd_session: dict):
|
||||
def process_account_statement(user: User, display_key: str, ussd_session: dict):
|
||||
"""
|
||||
:param user:
|
||||
:type user:
|
||||
@@ -301,12 +301,12 @@ def process_account_statement(user: Account, display_key: str, ussd_session: dic
|
||||
)
|
||||
|
||||
|
||||
def process_start_menu(display_key: str, user: Account):
|
||||
def process_start_menu(display_key: str, user: User):
|
||||
"""This function gets data on an account's balance and token in order to append it to the start of the start menu's
|
||||
title. It passes said arguments to the translation function and returns the appropriate corresponding text from the
|
||||
translation files.
|
||||
:param user: The user requesting access to the ussd menu.
|
||||
:type user: Account
|
||||
:type user: User
|
||||
:param display_key: The path in the translation files defining an appropriate ussd response
|
||||
:type display_key: str
|
||||
:return: Corresponding translation text response
|
||||
@@ -361,13 +361,13 @@ def retrieve_most_recent_ussd_session(phone_number: str) -> UssdSession:
|
||||
return last_ussd_session
|
||||
|
||||
|
||||
def process_request(user_input: str, user: Account, ussd_session: Optional[dict] = None) -> Document:
|
||||
def process_request(user_input: str, user: User, ussd_session: Optional[dict] = None) -> Document:
|
||||
"""This function assesses a request based on the user from the request comes, the session_id and the user's
|
||||
input. It determines whether the request translates to a return to an existing session by checking whether the
|
||||
provided session id exists in the database or whether the creation of a new ussd session object is warranted.
|
||||
It then returns the appropriate ussd menu text values.
|
||||
:param user: The user requesting access to the ussd menu.
|
||||
:type user: Account
|
||||
:type user: User
|
||||
:param user_input: The value a user enters in the ussd menu.
|
||||
:type user_input: str
|
||||
:param ussd_session: A JSON serialized in-memory ussd session object
|
||||
@@ -401,11 +401,7 @@ def process_request(user_input: str, user: Account, ussd_session: Optional[dict]
|
||||
'exit_invalid_pin',
|
||||
'exit_invalid_new_pin',
|
||||
'exit_pin_mismatch',
|
||||
'exit_invalid_request',
|
||||
"exit_insufficient_balance",
|
||||
"exit_successful_transaction",
|
||||
"help",
|
||||
"complete"
|
||||
'exit_invalid_request'
|
||||
] and person_metadata is not None:
|
||||
return UssdMenu.find_by_name(name='start')
|
||||
else:
|
||||
@@ -419,14 +415,14 @@ def process_request(user_input: str, user: Account, ussd_session: Optional[dict]
|
||||
return UssdMenu.find_by_name(name='initial_pin_entry')
|
||||
|
||||
|
||||
def next_state(ussd_session: dict, user: Account, user_input: str) -> str:
|
||||
def next_state(ussd_session: dict, user: User, user_input: str) -> str:
|
||||
"""This function navigates the state machine based on the ussd session object and user inputs it receives.
|
||||
It checks the user input and provides the successive state in the state machine. It then updates the session's
|
||||
state attribute with the new state.
|
||||
:param ussd_session: A JSON serialized in-memory ussd session object
|
||||
:type ussd_session: dict
|
||||
:param user: The user requesting access to the ussd menu.
|
||||
:type user: Account
|
||||
:type user: User
|
||||
:param user_input: The value a user enters in the ussd menu.
|
||||
:type user_input: str
|
||||
:return: A string value corresponding the successive give a specific state in the state machine.
|
||||
@@ -442,7 +438,7 @@ def custom_display_text(
|
||||
display_key: str,
|
||||
menu_name: str,
|
||||
ussd_session: dict,
|
||||
user: Account) -> str:
|
||||
user: User) -> str:
|
||||
"""This function extracts the appropriate session data based on the current menu name. It then inserts them as
|
||||
keywords in the i18n function.
|
||||
:param display_key: The path in the translation files defining an appropriate ussd response
|
||||
@@ -450,7 +446,7 @@ def custom_display_text(
|
||||
:param menu_name: The name by which a specific menu can be identified.
|
||||
:type menu_name: str
|
||||
:param user: The user in a running USSD session.
|
||||
:type user: Account
|
||||
:type user: User
|
||||
:param ussd_session: A JSON serialized in-memory ussd session object
|
||||
:type ussd_session: dict
|
||||
:return: A string value corresponding the ussd menu's text value.
|
||||
|
||||
@@ -10,7 +10,7 @@ from urllib.parse import urlparse, parse_qs
|
||||
from sqlalchemy import desc
|
||||
|
||||
# local imports
|
||||
from cic_ussd.db.models.account import AccountStatus, Account
|
||||
from cic_ussd.db.models.user import AccountStatus, User
|
||||
from cic_ussd.operations import get_account_status, reset_pin
|
||||
from cic_ussd.validator import check_known_user
|
||||
|
||||
@@ -123,9 +123,9 @@ def process_locked_accounts_requests(env: dict) -> tuple:
|
||||
else:
|
||||
limit = r[1]
|
||||
|
||||
locked_accounts = Account.session.query(Account.blockchain_address).filter(
|
||||
Account.account_status == AccountStatus.LOCKED.value,
|
||||
Account.failed_pin_attempts >= 3).order_by(desc(Account.updated)).offset(offset).limit(limit).all()
|
||||
locked_accounts = User.session.query(User.blockchain_address).filter(
|
||||
User.account_status == AccountStatus.LOCKED.value,
|
||||
User.failed_pin_attempts >= 3).order_by(desc(User.updated)).offset(offset).limit(limit).all()
|
||||
|
||||
# convert lists to scalar blockchain addresses
|
||||
locked_accounts = [blockchain_address for (blockchain_address, ) in locked_accounts]
|
||||
|
||||
@@ -5,12 +5,12 @@ from typing import Tuple
|
||||
# third-party imports
|
||||
|
||||
# local imports
|
||||
from cic_ussd.db.models.account import Account
|
||||
from cic_ussd.db.models.user import User
|
||||
|
||||
logg = logging.getLogger(__file__)
|
||||
|
||||
|
||||
def process_mini_statement_request(state_machine_data: Tuple[str, dict, Account]):
|
||||
def process_mini_statement_request(state_machine_data: Tuple[str, dict, User]):
|
||||
"""This function compiles a brief statement of a user's last three inbound and outbound transactions and send the
|
||||
same as a message on their selected avenue for notification.
|
||||
:param state_machine_data: A tuple containing user input, a ussd session and user object.
|
||||
|
||||
@@ -6,10 +6,10 @@ ussd menu facilitating the return of appropriate menu responses based on said us
|
||||
from typing import Tuple
|
||||
|
||||
# local imports
|
||||
from cic_ussd.db.models.account import Account
|
||||
from cic_ussd.db.models.user import User
|
||||
|
||||
|
||||
def menu_one_selected(state_machine_data: Tuple[str, dict, Account]) -> bool:
|
||||
def menu_one_selected(state_machine_data: Tuple[str, dict, User]) -> bool:
|
||||
"""This function checks that user input matches a string with value '1'
|
||||
:param state_machine_data: A tuple containing user input, a ussd session and user object.
|
||||
:type state_machine_data: str
|
||||
@@ -20,7 +20,7 @@ def menu_one_selected(state_machine_data: Tuple[str, dict, Account]) -> bool:
|
||||
return user_input == '1'
|
||||
|
||||
|
||||
def menu_two_selected(state_machine_data: Tuple[str, dict, Account]) -> bool:
|
||||
def menu_two_selected(state_machine_data: Tuple[str, dict, User]) -> bool:
|
||||
"""This function checks that user input matches a string with value '2'
|
||||
:param state_machine_data: A tuple containing user input, a ussd session and user object.
|
||||
:type state_machine_data: tuple
|
||||
@@ -31,7 +31,7 @@ def menu_two_selected(state_machine_data: Tuple[str, dict, Account]) -> bool:
|
||||
return user_input == '2'
|
||||
|
||||
|
||||
def menu_three_selected(state_machine_data: Tuple[str, dict, Account]) -> bool:
|
||||
def menu_three_selected(state_machine_data: Tuple[str, dict, User]) -> bool:
|
||||
"""This function checks that user input matches a string with value '3'
|
||||
:param state_machine_data: A tuple containing user input, a ussd session and user object.
|
||||
:type state_machine_data: tuple
|
||||
@@ -42,7 +42,7 @@ def menu_three_selected(state_machine_data: Tuple[str, dict, Account]) -> bool:
|
||||
return user_input == '3'
|
||||
|
||||
|
||||
def menu_four_selected(state_machine_data: Tuple[str, dict, Account]) -> bool:
|
||||
def menu_four_selected(state_machine_data: Tuple[str, dict, User]) -> bool:
|
||||
"""
|
||||
This function checks that user input matches a string with value '4'
|
||||
:param state_machine_data: A tuple containing user input, a ussd session and user object.
|
||||
@@ -54,7 +54,7 @@ def menu_four_selected(state_machine_data: Tuple[str, dict, Account]) -> bool:
|
||||
return user_input == '4'
|
||||
|
||||
|
||||
def menu_five_selected(state_machine_data: Tuple[str, dict, Account]) -> bool:
|
||||
def menu_five_selected(state_machine_data: Tuple[str, dict, User]) -> bool:
|
||||
"""
|
||||
This function checks that user input matches a string with value '5'
|
||||
:param state_machine_data: A tuple containing user input, a ussd session and user object.
|
||||
@@ -66,7 +66,7 @@ def menu_five_selected(state_machine_data: Tuple[str, dict, Account]) -> bool:
|
||||
return user_input == '5'
|
||||
|
||||
|
||||
def menu_zero_zero_selected(state_machine_data: Tuple[str, dict, Account]) -> bool:
|
||||
def menu_zero_zero_selected(state_machine_data: Tuple[str, dict, User]) -> bool:
|
||||
"""
|
||||
This function checks that user input matches a string with value '00'
|
||||
:param state_machine_data: A tuple containing user input, a ussd session and user object.
|
||||
@@ -78,7 +78,7 @@ def menu_zero_zero_selected(state_machine_data: Tuple[str, dict, Account]) -> bo
|
||||
return user_input == '00'
|
||||
|
||||
|
||||
def menu_ninety_nine_selected(state_machine_data: Tuple[str, dict, Account]) -> bool:
|
||||
def menu_ninety_nine_selected(state_machine_data: Tuple[str, dict, User]) -> bool:
|
||||
"""
|
||||
This function checks that user input matches a string with value '99'
|
||||
:param state_machine_data: A tuple containing user input, a ussd session and user object.
|
||||
|
||||
@@ -12,7 +12,7 @@ from typing import Tuple
|
||||
import bcrypt
|
||||
|
||||
# local imports
|
||||
from cic_ussd.db.models.account import AccountStatus, Account
|
||||
from cic_ussd.db.models.user import AccountStatus, User
|
||||
from cic_ussd.encoder import PasswordEncoder, create_password_hash
|
||||
from cic_ussd.operations import persist_session_to_db_task, create_or_update_session
|
||||
from cic_ussd.redis import InMemoryStore
|
||||
@@ -21,7 +21,7 @@ from cic_ussd.redis import InMemoryStore
|
||||
logg = logging.getLogger(__file__)
|
||||
|
||||
|
||||
def is_valid_pin(state_machine_data: Tuple[str, dict, Account]) -> bool:
|
||||
def is_valid_pin(state_machine_data: Tuple[str, dict, User]) -> bool:
|
||||
"""This function checks a pin's validity by ensuring it has a length of for characters and the characters are
|
||||
numeric.
|
||||
:param state_machine_data: A tuple containing user input, a ussd session and user object.
|
||||
@@ -37,7 +37,7 @@ def is_valid_pin(state_machine_data: Tuple[str, dict, Account]) -> bool:
|
||||
return pin_is_valid
|
||||
|
||||
|
||||
def is_authorized_pin(state_machine_data: Tuple[str, dict, Account]) -> bool:
|
||||
def is_authorized_pin(state_machine_data: Tuple[str, dict, User]) -> bool:
|
||||
"""This function checks whether the user input confirming a specific pin matches the initial pin entered.
|
||||
:param state_machine_data: A tuple containing user input, a ussd session and user object.
|
||||
:type state_machine_data: tuple
|
||||
@@ -45,18 +45,10 @@ def is_authorized_pin(state_machine_data: Tuple[str, dict, Account]) -> bool:
|
||||
:rtype: bool
|
||||
"""
|
||||
user_input, ussd_session, user = state_machine_data
|
||||
pin_validity = user.verify_password(password=user_input)
|
||||
if pin_validity is True:
|
||||
return user.verify_password(password=user_input)
|
||||
else:
|
||||
# bump number for failed attempts
|
||||
user.failed_pin_attempts += 1
|
||||
Account.session.add(user)
|
||||
Account.session.commit()
|
||||
return pin_validity
|
||||
return user.verify_password(password=user_input)
|
||||
|
||||
|
||||
def is_locked_account(state_machine_data: Tuple[str, dict, Account]) -> bool:
|
||||
def is_locked_account(state_machine_data: Tuple[str, dict, User]) -> bool:
|
||||
"""This function checks whether a user's account is locked due to too many failed attempts.
|
||||
:param state_machine_data: A tuple containing user input, a ussd session and user object.
|
||||
:type state_machine_data: tuple
|
||||
@@ -67,7 +59,7 @@ def is_locked_account(state_machine_data: Tuple[str, dict, Account]) -> bool:
|
||||
return user.get_account_status() == AccountStatus.LOCKED.name
|
||||
|
||||
|
||||
def save_initial_pin_to_session_data(state_machine_data: Tuple[str, dict, Account]):
|
||||
def save_initial_pin_to_session_data(state_machine_data: Tuple[str, dict, User]):
|
||||
"""This function hashes a pin and stores it in session data.
|
||||
:param state_machine_data: A tuple containing user input, a ussd session and user object.
|
||||
:type state_machine_data: tuple
|
||||
@@ -102,7 +94,7 @@ def save_initial_pin_to_session_data(state_machine_data: Tuple[str, dict, Accoun
|
||||
persist_session_to_db_task(external_session_id=external_session_id, queue='cic-ussd')
|
||||
|
||||
|
||||
def pins_match(state_machine_data: Tuple[str, dict, Account]) -> bool:
|
||||
def pins_match(state_machine_data: Tuple[str, dict, User]) -> bool:
|
||||
"""This function checks whether the user input confirming a specific pin matches the initial pin entered.
|
||||
:param state_machine_data: A tuple containing user input, a ussd session and user object.
|
||||
:type state_machine_data: tuple
|
||||
@@ -116,7 +108,7 @@ def pins_match(state_machine_data: Tuple[str, dict, Account]) -> bool:
|
||||
return bcrypt.checkpw(user_input.encode(), initial_pin)
|
||||
|
||||
|
||||
def complete_pin_change(state_machine_data: Tuple[str, dict, Account]):
|
||||
def complete_pin_change(state_machine_data: Tuple[str, dict, User]):
|
||||
"""This function persists the user's pin to the database
|
||||
:param state_machine_data: A tuple containing user input, a ussd session and user object.
|
||||
:type state_machine_data: tuple
|
||||
@@ -124,11 +116,11 @@ def complete_pin_change(state_machine_data: Tuple[str, dict, Account]):
|
||||
user_input, ussd_session, user = state_machine_data
|
||||
password_hash = ussd_session.get('session_data').get('initial_pin')
|
||||
user.password_hash = password_hash
|
||||
Account.session.add(user)
|
||||
Account.session.commit()
|
||||
User.session.add(user)
|
||||
User.session.commit()
|
||||
|
||||
|
||||
def is_blocked_pin(state_machine_data: Tuple[str, dict, Account]) -> bool:
|
||||
def is_blocked_pin(state_machine_data: Tuple[str, dict, User]) -> bool:
|
||||
"""This function checks whether the user input confirming a specific pin matches the initial pin entered.
|
||||
:param state_machine_data: A tuple containing user input, a ussd session and user object.
|
||||
:type state_machine_data: tuple
|
||||
@@ -139,7 +131,7 @@ def is_blocked_pin(state_machine_data: Tuple[str, dict, Account]) -> bool:
|
||||
return user.get_account_status() == AccountStatus.LOCKED.name
|
||||
|
||||
|
||||
def is_valid_new_pin(state_machine_data: Tuple[str, dict, Account]) -> bool:
|
||||
def is_valid_new_pin(state_machine_data: Tuple[str, dict, User]) -> bool:
|
||||
"""This function checks whether the user's new pin is a valid pin and that it isn't the same as the old one.
|
||||
:param state_machine_data: A tuple containing user input, a ussd session and user object.
|
||||
:type state_machine_data: tuple
|
||||
|
||||
@@ -3,21 +3,21 @@ import logging
|
||||
from typing import Tuple
|
||||
|
||||
# local imports
|
||||
from cic_ussd.db.models.account import Account
|
||||
from cic_ussd.db.models.user import User
|
||||
|
||||
logg = logging.getLogger()
|
||||
|
||||
|
||||
def send_terms_to_user_if_required(state_machine_data: Tuple[str, dict, Account]):
|
||||
def send_terms_to_user_if_required(state_machine_data: Tuple[str, dict, User]):
|
||||
user_input, ussd_session, user = state_machine_data
|
||||
logg.debug('Requires integration to cic-notify.')
|
||||
|
||||
|
||||
def process_mini_statement_request(state_machine_data: Tuple[str, dict, Account]):
|
||||
def process_mini_statement_request(state_machine_data: Tuple[str, dict, User]):
|
||||
user_input, ussd_session, user = state_machine_data
|
||||
logg.debug('Requires integration to cic-notify.')
|
||||
|
||||
|
||||
def upsell_unregistered_recipient(state_machine_data: Tuple[str, dict, Account]):
|
||||
def upsell_unregistered_recipient(state_machine_data: Tuple[str, dict, User]):
|
||||
user_input, ussd_session, user = state_machine_data
|
||||
logg.debug('Requires integration to cic-notify.')
|
||||
@@ -9,7 +9,7 @@ import celery
|
||||
# local imports
|
||||
from cic_ussd.balance import BalanceManager, compute_operational_balance
|
||||
from cic_ussd.chain import Chain
|
||||
from cic_ussd.db.models.account import AccountStatus, Account
|
||||
from cic_ussd.db.models.user import AccountStatus, User
|
||||
from cic_ussd.operations import save_to_in_memory_ussd_session_data
|
||||
from cic_ussd.phone_number import get_user_by_phone_number
|
||||
from cic_ussd.redis import create_cached_data_key, get_cached_data
|
||||
@@ -19,7 +19,7 @@ from cic_ussd.transactions import OutgoingTransactionProcessor
|
||||
logg = logging.getLogger(__file__)
|
||||
|
||||
|
||||
def is_valid_recipient(state_machine_data: Tuple[str, dict, Account]) -> bool:
|
||||
def is_valid_recipient(state_machine_data: Tuple[str, dict, User]) -> bool:
|
||||
"""This function checks that a user exists, is not the initiator of the transaction, has an active account status
|
||||
and is authorized to perform standard transactions.
|
||||
:param state_machine_data: A tuple containing user input, a ussd session and user object.
|
||||
@@ -34,7 +34,7 @@ def is_valid_recipient(state_machine_data: Tuple[str, dict, Account]) -> bool:
|
||||
return is_not_initiator and has_active_account_status and recipient is not None
|
||||
|
||||
|
||||
def is_valid_transaction_amount(state_machine_data: Tuple[str, dict, Account]) -> bool:
|
||||
def is_valid_transaction_amount(state_machine_data: Tuple[str, dict, User]) -> bool:
|
||||
"""This function checks that the transaction amount provided is valid as per the criteria for the transaction
|
||||
being attempted.
|
||||
:param state_machine_data: A tuple containing user input, a ussd session and user object.
|
||||
@@ -49,7 +49,7 @@ def is_valid_transaction_amount(state_machine_data: Tuple[str, dict, Account]) -
|
||||
return False
|
||||
|
||||
|
||||
def has_sufficient_balance(state_machine_data: Tuple[str, dict, Account]) -> bool:
|
||||
def has_sufficient_balance(state_machine_data: Tuple[str, dict, User]) -> bool:
|
||||
"""This function checks that the transaction amount provided is valid as per the criteria for the transaction
|
||||
being attempted.
|
||||
:param state_machine_data: A tuple containing user input, a ussd session and user object.
|
||||
@@ -72,7 +72,7 @@ def has_sufficient_balance(state_machine_data: Tuple[str, dict, Account]) -> boo
|
||||
return int(user_input) <= operational_balance
|
||||
|
||||
|
||||
def save_recipient_phone_to_session_data(state_machine_data: Tuple[str, dict, Account]):
|
||||
def save_recipient_phone_to_session_data(state_machine_data: Tuple[str, dict, User]):
|
||||
"""This function saves the phone number corresponding the intended recipients blockchain account.
|
||||
:param state_machine_data: A tuple containing user input, a ussd session and user object.
|
||||
:type state_machine_data: str
|
||||
@@ -85,7 +85,7 @@ def save_recipient_phone_to_session_data(state_machine_data: Tuple[str, dict, Ac
|
||||
save_to_in_memory_ussd_session_data(queue='cic-ussd', session_data=session_data, ussd_session=ussd_session)
|
||||
|
||||
|
||||
def retrieve_recipient_metadata(state_machine_data: Tuple[str, dict, Account]):
|
||||
def retrieve_recipient_metadata(state_machine_data: Tuple[str, dict, User]):
|
||||
"""
|
||||
:param state_machine_data:
|
||||
:type state_machine_data:
|
||||
@@ -104,7 +104,7 @@ def retrieve_recipient_metadata(state_machine_data: Tuple[str, dict, Account]):
|
||||
s_query_person_metadata.apply_async(queue='cic-ussd')
|
||||
|
||||
|
||||
def save_transaction_amount_to_session_data(state_machine_data: Tuple[str, dict, Account]):
|
||||
def save_transaction_amount_to_session_data(state_machine_data: Tuple[str, dict, User]):
|
||||
"""This function saves the phone number corresponding the intended recipients blockchain account.
|
||||
:param state_machine_data: A tuple containing user input, a ussd session and user object.
|
||||
:type state_machine_data: str
|
||||
@@ -117,7 +117,7 @@ def save_transaction_amount_to_session_data(state_machine_data: Tuple[str, dict,
|
||||
save_to_in_memory_ussd_session_data(queue='cic-ussd', session_data=session_data, ussd_session=ussd_session)
|
||||
|
||||
|
||||
def process_transaction_request(state_machine_data: Tuple[str, dict, Account]):
|
||||
def process_transaction_request(state_machine_data: Tuple[str, dict, User]):
|
||||
"""This function saves the phone number corresponding the intended recipients blockchain account.
|
||||
:param state_machine_data: A tuple containing user input, a ussd session and user object.
|
||||
:type state_machine_data: str
|
||||
|
||||
@@ -10,7 +10,7 @@ from cic_types.models.person import generate_vcard_from_contact_data, manage_ide
|
||||
|
||||
# local imports
|
||||
from cic_ussd.chain import Chain
|
||||
from cic_ussd.db.models.account import Account
|
||||
from cic_ussd.db.models.user import User
|
||||
from cic_ussd.error import MetadataNotFoundError
|
||||
from cic_ussd.metadata import blockchain_address_to_metadata_pointer
|
||||
from cic_ussd.operations import save_to_in_memory_ussd_session_data
|
||||
@@ -19,40 +19,40 @@ from cic_ussd.redis import get_cached_data
|
||||
logg = logging.getLogger(__file__)
|
||||
|
||||
|
||||
def change_preferred_language_to_en(state_machine_data: Tuple[str, dict, Account]):
|
||||
def change_preferred_language_to_en(state_machine_data: Tuple[str, dict, User]):
|
||||
"""This function changes the user's preferred language to english.
|
||||
:param state_machine_data: A tuple containing user input, a ussd session and user object.
|
||||
:type state_machine_data: tuple
|
||||
"""
|
||||
user_input, ussd_session, user = state_machine_data
|
||||
user.preferred_language = 'en'
|
||||
Account.session.add(user)
|
||||
Account.session.commit()
|
||||
User.session.add(user)
|
||||
User.session.commit()
|
||||
|
||||
|
||||
def change_preferred_language_to_sw(state_machine_data: Tuple[str, dict, Account]):
|
||||
def change_preferred_language_to_sw(state_machine_data: Tuple[str, dict, User]):
|
||||
"""This function changes the user's preferred language to swahili.
|
||||
:param state_machine_data: A tuple containing user input, a ussd session and user object.
|
||||
:type state_machine_data: tuple
|
||||
"""
|
||||
user_input, ussd_session, user = state_machine_data
|
||||
user.preferred_language = 'sw'
|
||||
Account.session.add(user)
|
||||
Account.session.commit()
|
||||
User.session.add(user)
|
||||
User.session.commit()
|
||||
|
||||
|
||||
def update_account_status_to_active(state_machine_data: Tuple[str, dict, Account]):
|
||||
def update_account_status_to_active(state_machine_data: Tuple[str, dict, User]):
|
||||
"""This function sets user's account to active.
|
||||
:param state_machine_data: A tuple containing user input, a ussd session and user object.
|
||||
:type state_machine_data: tuple
|
||||
"""
|
||||
user_input, ussd_session, user = state_machine_data
|
||||
user.activate_account()
|
||||
Account.session.add(user)
|
||||
Account.session.commit()
|
||||
User.session.add(user)
|
||||
User.session.commit()
|
||||
|
||||
|
||||
def process_gender_user_input(user: Account, user_input: str):
|
||||
def process_gender_user_input(user: User, user_input: str):
|
||||
"""
|
||||
:param user:
|
||||
:type user:
|
||||
@@ -74,7 +74,7 @@ def process_gender_user_input(user: Account, user_input: str):
|
||||
return gender
|
||||
|
||||
|
||||
def save_metadata_attribute_to_session_data(state_machine_data: Tuple[str, dict, Account]):
|
||||
def save_metadata_attribute_to_session_data(state_machine_data: Tuple[str, dict, User]):
|
||||
"""This function saves first name data to the ussd session in the redis cache.
|
||||
:param state_machine_data: A tuple containing user input, a ussd session and user object.
|
||||
:type state_machine_data: tuple
|
||||
@@ -109,7 +109,7 @@ def save_metadata_attribute_to_session_data(state_machine_data: Tuple[str, dict,
|
||||
save_to_in_memory_ussd_session_data(queue='cic-ussd', session_data=session_data, ussd_session=ussd_session)
|
||||
|
||||
|
||||
def format_user_metadata(metadata: dict, user: Account):
|
||||
def format_user_metadata(metadata: dict, user: User):
|
||||
"""
|
||||
:param metadata:
|
||||
:type metadata:
|
||||
@@ -150,7 +150,7 @@ def format_user_metadata(metadata: dict, user: Account):
|
||||
}
|
||||
|
||||
|
||||
def save_complete_user_metadata(state_machine_data: Tuple[str, dict, Account]):
|
||||
def save_complete_user_metadata(state_machine_data: Tuple[str, dict, User]):
|
||||
"""This function persists elements of the user metadata stored in session data
|
||||
:param state_machine_data: A tuple containing user input, a ussd session and user object.
|
||||
:type state_machine_data: tuple
|
||||
@@ -171,7 +171,7 @@ def save_complete_user_metadata(state_machine_data: Tuple[str, dict, Account]):
|
||||
s_create_person_metadata.apply_async(queue='cic-ussd')
|
||||
|
||||
|
||||
def edit_user_metadata_attribute(state_machine_data: Tuple[str, dict, Account]):
|
||||
def edit_user_metadata_attribute(state_machine_data: Tuple[str, dict, User]):
|
||||
user_input, ussd_session, user = state_machine_data
|
||||
blockchain_address = user.blockchain_address
|
||||
key = generate_metadata_pointer(
|
||||
@@ -218,7 +218,7 @@ def edit_user_metadata_attribute(state_machine_data: Tuple[str, dict, Account]):
|
||||
s_edit_person_metadata.apply_async(queue='cic-ussd')
|
||||
|
||||
|
||||
def get_user_metadata(state_machine_data: Tuple[str, dict, Account]):
|
||||
def get_user_metadata(state_machine_data: Tuple[str, dict, User]):
|
||||
user_input, ussd_session, user = state_machine_data
|
||||
blockchain_address = user.blockchain_address
|
||||
s_get_user_metadata = celery.signature(
|
||||
|
||||
@@ -7,14 +7,14 @@ from typing import Tuple
|
||||
from cic_types.models.person import generate_metadata_pointer
|
||||
|
||||
# local imports
|
||||
from cic_ussd.db.models.account import Account
|
||||
from cic_ussd.db.models.user import User
|
||||
from cic_ussd.metadata import blockchain_address_to_metadata_pointer
|
||||
from cic_ussd.redis import get_cached_data
|
||||
|
||||
logg = logging.getLogger()
|
||||
|
||||
|
||||
def has_cached_user_metadata(state_machine_data: Tuple[str, dict, Account]):
|
||||
def has_cached_user_metadata(state_machine_data: Tuple[str, dict, User]):
|
||||
"""This function checks whether the attributes of the user's metadata constituting a profile are filled out.
|
||||
:param state_machine_data: A tuple containing user input, a ussd session and user object.
|
||||
:type state_machine_data: str
|
||||
@@ -29,7 +29,7 @@ def has_cached_user_metadata(state_machine_data: Tuple[str, dict, Account]):
|
||||
return user_metadata is not None
|
||||
|
||||
|
||||
def is_valid_name(state_machine_data: Tuple[str, dict, Account]):
|
||||
def is_valid_name(state_machine_data: Tuple[str, dict, User]):
|
||||
"""This function checks that a user provided name is valid
|
||||
:param state_machine_data: A tuple containing user input, a ussd session and user object.
|
||||
:type state_machine_data: str
|
||||
@@ -43,7 +43,7 @@ def is_valid_name(state_machine_data: Tuple[str, dict, Account]):
|
||||
return False
|
||||
|
||||
|
||||
def is_valid_gender_selection(state_machine_data: Tuple[str, dict, Account]):
|
||||
def is_valid_gender_selection(state_machine_data: Tuple[str, dict, User]):
|
||||
"""
|
||||
:param state_machine_data:
|
||||
:type state_machine_data:
|
||||
|
||||
@@ -13,7 +13,7 @@ class UssdStateMachine(Machine):
|
||||
"""This class describes a finite state machine responsible for maintaining all the states that describe the ussd
|
||||
menu as well as providing a means for navigating through these states based on different user inputs.
|
||||
It defines different helper functions that co-ordinate with the stakeholder components of the ussd menu: i.e the
|
||||
Account, UssdSession, UssdMenu to facilitate user interaction with ussd menu.
|
||||
User, UssdSession, UssdMenu to facilitate user interaction with ussd menu.
|
||||
:cvar states: A list of pre-defined states.
|
||||
:type states: list
|
||||
:cvar transitions: A list of pre-defined transitions.
|
||||
|
||||
@@ -9,7 +9,7 @@ import celery
|
||||
# local imports
|
||||
from cic_ussd.conversions import from_wei
|
||||
from cic_ussd.db.models.base import SessionBase
|
||||
from cic_ussd.db.models.account import Account
|
||||
from cic_ussd.db.models.user import User
|
||||
from cic_ussd.account import define_account_tx_metadata
|
||||
from cic_ussd.error import ActionDataNotFoundError
|
||||
from cic_ussd.redis import InMemoryStore, cache_data, create_cached_data_key
|
||||
@@ -49,7 +49,7 @@ def process_account_creation_callback(self, result: str, url: str, status_code:
|
||||
phone_number = account_creation_data.get('phone_number')
|
||||
|
||||
# create user
|
||||
user = Account(blockchain_address=result, phone_number=phone_number)
|
||||
user = User(blockchain_address=result, phone_number=phone_number)
|
||||
session.add(user)
|
||||
session.commit()
|
||||
session.close()
|
||||
@@ -87,8 +87,8 @@ def process_incoming_transfer_callback(result: dict, param: str, status_code: in
|
||||
value = result.get('destination_token_value')
|
||||
|
||||
# try to find users in system
|
||||
recipient_user = session.query(Account).filter_by(blockchain_address=recipient_blockchain_address).first()
|
||||
sender_user = session.query(Account).filter_by(blockchain_address=sender_blockchain_address).first()
|
||||
recipient_user = session.query(User).filter_by(blockchain_address=recipient_blockchain_address).first()
|
||||
sender_user = session.query(User).filter_by(blockchain_address=sender_blockchain_address).first()
|
||||
|
||||
# check whether recipient is in the system
|
||||
if not recipient_user:
|
||||
@@ -188,8 +188,8 @@ def process_statement_callback(result, param: str, status_code: int):
|
||||
processed_transaction = {}
|
||||
|
||||
# check if sender is in the system
|
||||
sender: Account = session.query(Account).filter_by(blockchain_address=sender_blockchain_address).first()
|
||||
owner: Account = session.query(Account).filter_by(blockchain_address=param).first()
|
||||
sender: User = session.query(User).filter_by(blockchain_address=sender_blockchain_address).first()
|
||||
owner: User = session.query(User).filter_by(blockchain_address=param).first()
|
||||
if sender:
|
||||
processed_transaction['sender_phone_number'] = sender.phone_number
|
||||
|
||||
@@ -205,7 +205,7 @@ def process_statement_callback(result, param: str, status_code: int):
|
||||
processed_transaction['sender_phone_number'] = 'GRASSROOTS ECONOMICS'
|
||||
|
||||
# check if recipient is in the system
|
||||
recipient: Account = session.query(Account).filter_by(blockchain_address=recipient_address).first()
|
||||
recipient: User = session.query(User).filter_by(blockchain_address=recipient_address).first()
|
||||
if recipient:
|
||||
processed_transaction['recipient_phone_number'] = recipient.phone_number
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ from typing import Optional
|
||||
def translation_for(key: str, preferred_language: Optional[str] = None, **kwargs) -> str:
|
||||
"""
|
||||
Translates text mapped to a specific YAML key into the user's set preferred language.
|
||||
:param preferred_language: Account's preferred language in which to view the ussd menu.
|
||||
:param preferred_language: User's preferred language in which to view the ussd menu.
|
||||
:type preferred_language str
|
||||
:param key: Key to a specific YAML test entry
|
||||
:type key: str
|
||||
|
||||
@@ -8,7 +8,7 @@ import ipaddress
|
||||
from confini import Config
|
||||
|
||||
# local imports
|
||||
from cic_ussd.db.models.account import Account
|
||||
from cic_ussd.db.models.user import User
|
||||
|
||||
logg = logging.getLogger(__file__)
|
||||
|
||||
@@ -68,7 +68,7 @@ def check_known_user(phone: str):
|
||||
:return: Is known phone number
|
||||
:rtype: boolean
|
||||
"""
|
||||
user = Account.session.query(Account).filter_by(phone_number=phone).first()
|
||||
user = User.session.query(User).filter_by(phone_number=phone).first()
|
||||
return user is not None
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# standard imports
|
||||
import semver
|
||||
|
||||
version = (0, 3, 0, 'alpha.9')
|
||||
version = (0, 3, 0, 'alpha.8')
|
||||
|
||||
version_object = semver.VersionInfo(
|
||||
major=version[0],
|
||||
|
||||
@@ -8,7 +8,7 @@ WORKDIR /usr/src
|
||||
ARG pip_extra_index_url_flag='--extra-index-url https://pip.grassrootseconomics.net:8433'
|
||||
|
||||
RUN apt-get update && \
|
||||
apt install -y gcc gnupg libpq-dev wget make g++ gnupg bash procps git python-pycurl libcurl4-openssl-dev libssl-dev
|
||||
apt install -y gcc gnupg libpq-dev wget make g++ gnupg bash procps git
|
||||
|
||||
# create secrets directory
|
||||
RUN mkdir -vp pgp/keys
|
||||
|
||||
@@ -1,281 +0,0 @@
|
||||
- config:
|
||||
- testset: "account tests"
|
||||
|
||||
- test:
|
||||
- group: "accounts"
|
||||
- name: "create account [EN]"
|
||||
- url: "/"
|
||||
- method: "POST"
|
||||
- body: '{"serviceCode": "*483*46#", "phoneNumber": "+254712345678", "sessionId": "AT_Idjhfuvelw64ffbweiy73nd5vnek020", "text": ""}'
|
||||
- headers: {'Content-Type': 'application/json'}
|
||||
- expected_status: [200]
|
||||
- validators:
|
||||
- compare: {"header": "content-type", "comparator": "str_eq", "expected":"text/plain"}
|
||||
- compare: {"header": "content-length", "comparator": "str_eq", "expected":"175"}
|
||||
- compare: {"raw_body":"", "comparator":"contains", expected: "END Your account is being created. You will receive an SMS when your account is ready.\nAkaunti yako ya Sarafu inatayarishwa. Utapokea ujumbe wa SMS akaunti yako ikiwa tayari.\n" }
|
||||
|
||||
- test:
|
||||
- group: "accounts"
|
||||
- name: "create account [SW]"
|
||||
- url: "/"
|
||||
- method: "POST"
|
||||
- delay: 2
|
||||
- body: '{"serviceCode": "*483*46#", "phoneNumber": "+254712345679", "sessionId": "AT_Idjhfuvelw64ffbweiy73nd5vnek021", "text": ""}'
|
||||
- headers: {'Content-Type': 'application/json'}
|
||||
- expected_status: [200]
|
||||
- validators:
|
||||
- compare: {"header": "content-type", "comparator": "str_eq", "expected":"text/plain"}
|
||||
- compare: {"header": "content-length", "comparator": "str_eq", "expected":"175"}
|
||||
- compare: {"raw_body":"", "comparator":"contains", expected: "END Your account is being created. You will receive an SMS when your account is ready.\nAkaunti yako ya Sarafu inatayarishwa. Utapokea ujumbe wa SMS akaunti yako ikiwa tayari.\n" }
|
||||
|
||||
- test:
|
||||
- group: "accounts"
|
||||
- name: "select preferred language prompt [EN]"
|
||||
- url: "/"
|
||||
- method: "POST"
|
||||
- delay: 5 # delay to allow celery tasks to run to create account
|
||||
- body: '{"serviceCode": "*483*46#", "phoneNumber": "+254712345678", "sessionId": "AT_Idjhfuvelw64ffbweiy73nd5vnek022", "text": ""}'
|
||||
- headers: {'Content-Type': 'application/json'}
|
||||
- expected_status: [200]
|
||||
- validators:
|
||||
- compare: {"header": "content-type", "comparator": "str_eq", "expected":"text/plain"}
|
||||
- compare: {"header": "content-length", "comparator": "str_eq", "expected":"53"}
|
||||
- compare: {"raw_body":"", "comparator":"str_eq", expected: "CON Welcome to Sarafu\n1. English\n2. Kiswahili\n3. Help"}
|
||||
|
||||
- test:
|
||||
- group: "accounts"
|
||||
- name: "select preferred language prompt [SW]"
|
||||
- url: "/"
|
||||
- method: "POST"
|
||||
- delay: 5 # delay to allow celery tasks to run to create account
|
||||
- body: '{"serviceCode": "*483*46#", "phoneNumber": "+254712345679", "sessionId": "AT_Idjhfuvelw64ffbweiy73nd5vnek023", "text": ""}'
|
||||
- headers: {'Content-Type': 'application/json'}
|
||||
- expected_status: [200]
|
||||
- validators:
|
||||
- compare: {"header": "content-type", "comparator": "str_eq", "expected":"text/plain"}
|
||||
- compare: {"header": "content-length", "comparator": "str_eq", "expected":"53"}
|
||||
- compare: {"raw_body":"", "comparator":"str_eq", expected: "CON Welcome to Sarafu\n1. English\n2. Kiswahili\n3. Help"}
|
||||
|
||||
- test:
|
||||
- group: "accounts"
|
||||
- name: "pin entry [EN]"
|
||||
- url: "/"
|
||||
- delay: 2 # delay
|
||||
- method: "POST"
|
||||
- body: '{"serviceCode": "*483*46#", "phoneNumber": "+254712345678", "sessionId": "AT_Idjhfuvelw64ffbweiy73nd5vnek022", "text": "1"}'
|
||||
- headers: {'Content-Type': 'application/json'}
|
||||
- expected_status: [200]
|
||||
- validators:
|
||||
- compare: {"header": "content-type", "comparator": "str_eq", "expected":"text/plain"}
|
||||
- compare: {"header": "content-length", "comparator": "str_eq", "expected":"54"}
|
||||
- compare: {"raw_body":"", "comparator":"str_eq", expected: "CON Please enter a PIN to manage your account.\n0. Back"}
|
||||
|
||||
- test:
|
||||
- group: "accounts"
|
||||
- name: "pin entry [SW]"
|
||||
- url: "/"
|
||||
- delay: 2 # delay
|
||||
- method: "POST"
|
||||
- body: '{"serviceCode": "*483*46#", "phoneNumber": "+254712345679", "sessionId": "AT_Idjhfuvelw64ffbweiy73nd5vnek023", "text": "2"}'
|
||||
- headers: {'Content-Type': 'application/json'}
|
||||
- expected_status: [200]
|
||||
- validators:
|
||||
- compare: {"header": "content-type", "comparator": "str_eq", "expected":"text/plain"}
|
||||
- compare: {"header": "content-length", "comparator": "str_eq", "expected":"59"}
|
||||
- compare: {"raw_body":"", "comparator":"str_eq", expected: "CON Tafadhali weka PIN ili kudhibiti akaunti yako.\n0. Nyuma"}
|
||||
|
||||
- test:
|
||||
- group: "accounts"
|
||||
- name: "pin entry confirmation [EN]"
|
||||
- url: "/"
|
||||
- delay: 2 # delay
|
||||
- method: "POST"
|
||||
- body: '{"serviceCode": "*483*46#", "phoneNumber": "+254712345678", "sessionId": "AT_Idjhfuvelw64ffbweiy73nd5vnek022", "text": "1*0000"}'
|
||||
- headers: {'Content-Type': 'application/json'}
|
||||
- expected_status: [200]
|
||||
- validators:
|
||||
- compare: {"header": "content-type", "comparator": "str_eq", "expected":"text/plain"}
|
||||
- compare: {"header": "content-length", "comparator": "str_eq", "expected":"32"}
|
||||
- compare: {"raw_body":"", "comparator":"str_eq", expected: "CON Enter your PIN again\n0. Back"}
|
||||
|
||||
- test:
|
||||
- group: "accounts"
|
||||
- name: "pin entry confirmation [SW]"
|
||||
- url: "/"
|
||||
- delay: 2 # delay
|
||||
- method: "POST"
|
||||
- body: '{"serviceCode": "*483*46#", "phoneNumber": "+254712345679", "sessionId": "AT_Idjhfuvelw64ffbweiy73nd5vnek023", "text": "2*1111"}'
|
||||
- headers: {'Content-Type': 'application/json'}
|
||||
- expected_status: [200]
|
||||
- validators:
|
||||
- compare: {"header": "content-type", "comparator": "str_eq", "expected":"text/plain"}
|
||||
- compare: {"header": "content-length", "comparator": "str_eq", "expected":"31"}
|
||||
- compare: {"raw_body":"", "comparator":"str_eq", expected: "CON Weka PIN yako tena\n0. Nyuma"}
|
||||
|
||||
- test:
|
||||
- group: "accounts"
|
||||
- name: "given names entry[EN]"
|
||||
- url: "/"
|
||||
- delay: 3 # delay
|
||||
- method: "POST"
|
||||
- body: '{"serviceCode": "*483*46#", "phoneNumber": "+254712345678", "sessionId": "AT_Idjhfuvelw64ffbweiy73nd5vnek022", "text": "1*0000*0000"}'
|
||||
- headers: {'Content-Type': 'application/json'}
|
||||
- expected_status: [200]
|
||||
- validators:
|
||||
- compare: {"header": "content-type", "comparator": "str_eq", "expected":"text/plain"}
|
||||
- compare: {"header": "content-length", "comparator": "str_eq", "expected":"28"}
|
||||
- compare: {"raw_body":"", "comparator":"str_eq", expected: "CON Enter first name\n0. Back"}
|
||||
|
||||
- test:
|
||||
- group: "accounts"
|
||||
- name: "given names entry[SW]"
|
||||
- url: "/"
|
||||
- delay: 3 # delay
|
||||
- method: "POST"
|
||||
- body: '{"serviceCode": "*483*46#", "phoneNumber": "+254712345679", "sessionId": "AT_Idjhfuvelw64ffbweiy73nd5vnek023", "text": "2*1111*1111"}'
|
||||
- headers: {'Content-Type': 'application/json'}
|
||||
- expected_status: [200]
|
||||
- validators:
|
||||
- compare: {"header": "content-type", "comparator": "str_eq", "expected":"text/plain"}
|
||||
- compare: {"header": "content-length", "comparator": "str_eq", "expected":"37"}
|
||||
- compare: {"raw_body":"", "comparator":"str_eq", expected: "CON Weka jina lako la kwanza\n0. Nyuma"}
|
||||
|
||||
- test:
|
||||
- group: "accounts"
|
||||
- name: "family name entry[EN]"
|
||||
- url: "/"
|
||||
- delay: 2 # delay
|
||||
- method: "POST"
|
||||
- body: '{"serviceCode": "*483*46#", "phoneNumber": "+254712345678", "sessionId": "AT_Idjhfuvelw64ffbweiy73nd5vnek022", "text": "1*0000*0000*Kimani"}'
|
||||
- headers: {'Content-Type': 'application/json'}
|
||||
- expected_status: [200]
|
||||
- validators:
|
||||
- compare: {"header": "content-type", "comparator": "str_eq", "expected":"text/plain"}
|
||||
- compare: {"header": "content-length", "comparator": "str_eq", "expected":"27"}
|
||||
- compare: {"raw_body":"", "comparator":"str_eq", expected: "CON Enter last name\n0. Back"}
|
||||
|
||||
- test:
|
||||
- group: "accounts"
|
||||
- name: "family name entry[SW]"
|
||||
- url: "/"
|
||||
- delay: 2 # delay
|
||||
- method: "POST"
|
||||
- body: '{"serviceCode": "*483*46#", "phoneNumber": "+254712345679", "sessionId": "AT_Idjhfuvelw64ffbweiy73nd5vnek023", "text": "2*1111*1111*Chebet"}'
|
||||
- headers: {'Content-Type': 'application/json'}
|
||||
- expected_status: [200]
|
||||
- validators:
|
||||
- compare: {"header": "content-type", "comparator": "str_eq", "expected":"text/plain"}
|
||||
- compare: {"header": "content-length", "comparator": "str_eq", "expected":"37"}
|
||||
- compare: {"raw_body":"", "comparator":"str_eq", expected: "CON Weka jina lako la mwisho\n0. Nyuma"}
|
||||
|
||||
- test:
|
||||
- group: "accounts"
|
||||
- name: "gender selection[EN]"
|
||||
- url: "/"
|
||||
- delay: 2 # delay
|
||||
- method: "POST"
|
||||
- body: '{"serviceCode": "*483*46#", "phoneNumber": "+254712345678", "sessionId": "AT_Idjhfuvelw64ffbweiy73nd5vnek022", "text": "1*0000*0000*Kimani*Omollo"}'
|
||||
- headers: {'Content-Type': 'application/json'}
|
||||
- expected_status: [200]
|
||||
- validators:
|
||||
- compare: {"header": "content-type", "comparator": "str_eq", "expected":"text/plain"}
|
||||
- compare: {"header": "content-length", "comparator": "str_eq", "expected":"42"}
|
||||
- compare: {"raw_body":"", "comparator":"str_eq", expected: "CON Enter gender\n1. Male\n2. Female\n0. Back"}
|
||||
|
||||
- test:
|
||||
- group: "accounts"
|
||||
- name: "gender selection[SW]"
|
||||
- url: "/"
|
||||
- delay: 2 # delay
|
||||
- method: "POST"
|
||||
- body: '{"serviceCode": "*483*46#", "phoneNumber": "+254712345679", "sessionId": "AT_Idjhfuvelw64ffbweiy73nd5vnek023", "text": "2*1111*1111*Chebet*Musau"}'
|
||||
- headers: {'Content-Type': 'application/json'}
|
||||
- expected_status: [200]
|
||||
- validators:
|
||||
- compare: {"header": "content-type", "comparator": "str_eq", "expected":"text/plain"}
|
||||
- compare: {"header": "content-length", "comparator": "str_eq", "expected":"53"}
|
||||
- compare: {"raw_body":"", "comparator":"str_eq", expected: "CON Weka jinsia yako\n1. Mwanaume\n2. Mwanamke\n0. Nyuma"}
|
||||
|
||||
- test:
|
||||
- group: "accounts"
|
||||
- name: "location entry[EN]"
|
||||
- url: "/"
|
||||
- delay: 2 # delay
|
||||
- method: "POST"
|
||||
- body: '{"serviceCode": "*483*46#", "phoneNumber": "+254712345678", "sessionId": "AT_Idjhfuvelw64ffbweiy73nd5vnek022", "text": "1*0000*0000*Kimani*Omollo*1"}'
|
||||
- headers: {'Content-Type': 'application/json'}
|
||||
- expected_status: [200]
|
||||
- validators:
|
||||
- compare: {"header": "content-type", "comparator": "str_eq", "expected":"text/plain"}
|
||||
- compare: {"header": "content-length", "comparator": "str_eq", "expected":"26"}
|
||||
- compare: {"raw_body":"", "comparator":"str_eq", expected: "CON Enter location\n0. Back"}
|
||||
|
||||
- test:
|
||||
- group: "accounts"
|
||||
- name: "location entry[SW]"
|
||||
- url: "/"
|
||||
- delay: 2 # delay
|
||||
- method: "POST"
|
||||
- body: '{"serviceCode": "*483*46#", "phoneNumber": "+254712345679", "sessionId": "AT_Idjhfuvelw64ffbweiy73nd5vnek023", "text": "2*1111*1111*Chebet*Musau*2"}'
|
||||
- headers: {'Content-Type': 'application/json'}
|
||||
- expected_status: [200]
|
||||
- validators:
|
||||
- compare: {"header": "content-type", "comparator": "str_eq", "expected":"text/plain"}
|
||||
- compare: {"header": "content-length", "comparator": "str_eq", "expected":"27"}
|
||||
- compare: {"raw_body":"", "comparator":"str_eq", expected: "CON Weka eneo lako\n0. Nyuma"}
|
||||
|
||||
- test:
|
||||
- group: "accounts"
|
||||
- name: "product entry[EN]"
|
||||
- url: "/"
|
||||
- delay: 2 # delay
|
||||
- method: "POST"
|
||||
- body: '{"serviceCode": "*483*46#", "phoneNumber": "+254712345678", "sessionId": "AT_Idjhfuvelw64ffbweiy73nd5vnek022", "text": "1*0000*0000*Kimani*Omollo*1*Kangemi"}'
|
||||
- headers: {'Content-Type': 'application/json'}
|
||||
- expected_status: [200]
|
||||
- validators:
|
||||
- compare: {"header": "content-type", "comparator": "str_eq", "expected":"text/plain"}
|
||||
- compare: {"header": "content-length", "comparator": "str_eq", "expected":"55"}
|
||||
- compare: {"raw_body":"", "comparator":"str_eq", expected: "CON Please enter a product or service you offer\n0. Back"}
|
||||
|
||||
- test:
|
||||
- group: "accounts"
|
||||
- name: "product entry[SW]"
|
||||
- url: "/"
|
||||
- delay: 2 # delay
|
||||
- method: "POST"
|
||||
- body: '{"serviceCode": "*483*46#", "phoneNumber": "+254712345679", "sessionId": "AT_Idjhfuvelw64ffbweiy73nd5vnek023", "text": "2*1111*1111*Chebet*Musau*2*Chebarbar"}'
|
||||
- headers: {'Content-Type': 'application/json'}
|
||||
- expected_status: [200]
|
||||
- validators:
|
||||
- compare: {"header": "content-type", "comparator": "str_eq", "expected":"text/plain"}
|
||||
- compare: {"header": "content-length", "comparator": "str_eq", "expected":"52"}
|
||||
- compare: {"raw_body":"", "comparator":"str_eq", expected: "CON Tafadhali weka bidhaa ama huduma unauza\n0. Nyuma"}
|
||||
|
||||
- test:
|
||||
- group: "accounts"
|
||||
- name: "start menu[EN]"
|
||||
- url: "/"
|
||||
- delay: 2 # delay
|
||||
- method: "POST"
|
||||
- body: '{"serviceCode": "*483*46#", "phoneNumber": "+254712345678", "sessionId": "AT_Idjhfuvelw64ffbweiy73nd5vnek022", "text": "1*0000*0000*Kimani*Omollo*1*Kangemi*Potatoes"}'
|
||||
- headers: {'Content-Type': 'application/json'}
|
||||
- expected_status: [200]
|
||||
- validators:
|
||||
- compare: {"header": "content-type", "comparator": "str_eq", "expected":"text/plain"}
|
||||
- compare: {"header": "content-length", "comparator": "str_eq", "expected":"51"}
|
||||
- compare: {"raw_body":"", "comparator":"str_eq", expected: "CON Balance 50.00 SRF\n1. Send\n2. My Account\n3. Help"}
|
||||
|
||||
- test:
|
||||
- group: "accounts"
|
||||
- name: "start menu[EN]"
|
||||
- url: "/"
|
||||
- delay: 2 # delay
|
||||
- method: "POST"
|
||||
- body: '{"serviceCode": "*483*46#", "phoneNumber": "+254712345679", "sessionId": "AT_Idjhfuvelw64ffbweiy73nd5vnek023", "text": "2*1111*1111*Chebet*Musau*2*Musau*Mandazi"}'
|
||||
- headers: {'Content-Type': 'application/json'}
|
||||
- expected_status: [200]
|
||||
- validators:
|
||||
- compare: {"header": "content-type", "comparator": "str_eq", "expected":"text/plain"}
|
||||
- compare: {"header": "content-length", "comparator": "str_eq", "expected":"56"}
|
||||
- compare: {"raw_body":"", "comparator":"str_eq", expected: "CON Salio 50.00 SRF\n1. Tuma\n2. Akaunti yangu\n3. Usaidizi"}
|
||||
@@ -1,9 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ -z "$TEST_SERVER_URL" ];
|
||||
then
|
||||
echo "The test server url is not set !"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
pyresttest "$TEST_SERVER_URL" ./test_suite.yml --log debug
|
||||
@@ -1,2 +0,0 @@
|
||||
- import: account_tests.yml
|
||||
- import: transaction_tests.yml
|
||||
@@ -1,285 +0,0 @@
|
||||
- config:
|
||||
- testset: "transaction tests"
|
||||
|
||||
- test:
|
||||
- group: "transactions"
|
||||
- name: "send tokens[EN]"
|
||||
- url: "/"
|
||||
- delay: "3"
|
||||
- method: "POST"
|
||||
- body: '{"serviceCode": "*483*46#", "phoneNumber": "+254712345678", "sessionId": "AT_Idjhfuvelw64ffbweiy73nd5vnek024", "text": ""}'
|
||||
- headers: { 'Content-Type': 'application/json' }
|
||||
- expected_status: [ 200 ]
|
||||
- validators:
|
||||
- compare: { "header": "content-type", "comparator": "str_eq", "expected": "text/plain" }
|
||||
- compare: { "header": "content-length", "comparator": "str_eq", "expected": "51" }
|
||||
- compare: { "raw_body": "", "comparator": "str_eq", expected: "CON Balance 50.00 SRF\n1. Send\n2. My Account\n3. Help" }
|
||||
|
||||
- test:
|
||||
- group: "transactions"
|
||||
- name: "send tokens[SW]"
|
||||
- url: "/"
|
||||
- delay: "3"
|
||||
- method: "POST"
|
||||
- body: '{"serviceCode": "*483*46#", "phoneNumber": "+254712345679", "sessionId": "AT_Idjhfuvelw64ffbweiy73nd5vnek025", "text": ""}'
|
||||
- headers: { 'Content-Type': 'application/json' }
|
||||
- expected_status: [ 200 ]
|
||||
- validators:
|
||||
- compare: { "header": "content-type", "comparator": "str_eq", "expected": "text/plain" }
|
||||
- compare: {"header": "content-length", "comparator": "str_eq", "expected":"56"}
|
||||
- compare: {"raw_body":"", "comparator":"str_eq", expected: "CON Salio 50.00 SRF\n1. Tuma\n2. Akaunti yangu\n3. Usaidizi"}
|
||||
|
||||
- test:
|
||||
- group: "transactions"
|
||||
- name: "enter recipients phone number[EN]"
|
||||
- url: "/"
|
||||
- delay: "2"
|
||||
- method: "POST"
|
||||
- body: '{"serviceCode": "*483*46#", "phoneNumber": "+254712345678", "sessionId": "AT_Idjhfuvelw64ffbweiy73nd5vnek024", "text": "1"}'
|
||||
- headers: { 'Content-Type': 'application/json' }
|
||||
- expected_status: [ 200 ]
|
||||
- validators:
|
||||
- compare: { "header": "content-type", "comparator": "str_eq", "expected": "text/plain" }
|
||||
- compare: { "header": "content-length", "comparator": "str_eq", "expected": "30" }
|
||||
- compare: { "raw_body": "", "comparator": "str_eq", expected: "CON Enter phone number\n0. Back" }
|
||||
|
||||
|
||||
- test:
|
||||
- group: "transactions"
|
||||
- name: "enter recipients phone number[SW]"
|
||||
- url: "/"
|
||||
- delay: "2"
|
||||
- method: "POST"
|
||||
- body: '{"serviceCode": "*483*46#", "phoneNumber": "+254712345679", "sessionId": "AT_Idjhfuvelw64ffbweiy73nd5vnek025", "text": "1"}'
|
||||
- headers: { 'Content-Type': 'application/json' }
|
||||
- expected_status: [ 200 ]
|
||||
- validators:
|
||||
- compare: { "header": "content-type", "comparator": "str_eq", "expected": "text/plain" }
|
||||
- compare: {"header": "content-length", "comparator": "str_eq", "expected":"33"}
|
||||
- compare: {"raw_body":"", "comparator":"str_eq", expected: "CON Weka nambari ya simu\n0. Nyuma"}
|
||||
|
||||
- test:
|
||||
- group: "transactions"
|
||||
- name: "enter token amount[EN]"
|
||||
- url: "/"
|
||||
- delay: "2"
|
||||
- method: "POST"
|
||||
- body: '{"serviceCode": "*483*46#", "phoneNumber": "+254712345678", "sessionId": "AT_Idjhfuvelw64ffbweiy73nd5vnek024", "text": "1*0712345679"}'
|
||||
- headers: { 'Content-Type': 'application/json' }
|
||||
- expected_status: [ 200 ]
|
||||
- validators:
|
||||
- compare: { "header": "content-type", "comparator": "str_eq", "expected": "text/plain" }
|
||||
- compare: { "header": "content-length", "comparator": "str_eq", "expected": "24" }
|
||||
- compare: { "raw_body": "", "comparator": "str_eq", expected: "CON Enter amount\n0. Back" }
|
||||
|
||||
- test:
|
||||
- group: "transactions"
|
||||
- name: "enter token amount[SW]"
|
||||
- url: "/"
|
||||
- delay: "2"
|
||||
- method: "POST"
|
||||
- body: '{"serviceCode": "*483*46#", "phoneNumber": "+254712345679", "sessionId": "AT_Idjhfuvelw64ffbweiy73nd5vnek025", "text": "1*0712345678"}'
|
||||
- headers: { 'Content-Type': 'application/json' }
|
||||
- expected_status: [ 200 ]
|
||||
- validators:
|
||||
- compare: { "header": "content-type", "comparator": "str_eq", "expected": "text/plain" }
|
||||
- compare: {"header": "content-length", "comparator": "str_eq", "expected":"25"}
|
||||
- compare: {"raw_body":"", "comparator":"str_eq", expected: "CON Weka kiwango\n0. Nyuma"}
|
||||
|
||||
- test:
|
||||
- group: "transactions"
|
||||
- name: "transaction pin authorization[EN]"
|
||||
- url: "/"
|
||||
- delay: "2"
|
||||
- method: "POST"
|
||||
- body: '{"serviceCode": "*483*46#", "phoneNumber": "+254712345678", "sessionId": "AT_Idjhfuvelw64ffbweiy73nd5vnek024", "text": "1*0712345679*15"}'
|
||||
- headers: { 'Content-Type': 'application/json' }
|
||||
- expected_status: [ 200 ]
|
||||
- validators:
|
||||
- compare: { "header": "content-type", "comparator": "str_eq", "expected": "text/plain" }
|
||||
- compare: { "header": "content-length", "comparator": "str_eq", "expected": "129" }
|
||||
- compare: { "raw_body": "", "comparator": "str_eq", expected: "CON Chebet Musau +254712345679 will receive 15.00 SRF from Kimani Omollo +254712345678.\nPlease enter your PIN to confirm.\n0. Back" }
|
||||
|
||||
- test:
|
||||
- group: "transactions"
|
||||
- name: "transaction pin authorization[SW]"
|
||||
- url: "/"
|
||||
- delay: "2"
|
||||
- method: "POST"
|
||||
- body: '{"serviceCode": "*483*46#", "phoneNumber": "+254712345679", "sessionId": "AT_Idjhfuvelw64ffbweiy73nd5vnek025", "text": "1*0712345678*18"}'
|
||||
- headers: { 'Content-Type': 'application/json' }
|
||||
- expected_status: [ 200 ]
|
||||
- validators:
|
||||
- compare: { "header": "content-type", "comparator": "str_eq", "expected": "text/plain" }
|
||||
- compare: {"header": "content-length", "comparator": "str_eq", "expected":"148"}
|
||||
- compare: {"raw_body":"", "comparator":"str_eq", expected: "CON Kimani Omollo +254712345678 atapokea 18.00 SRF kutoka kwa Chebet Musau +254712345679.\nTafadhali weka nambari yako ya siri kudhibitisha.\n0. Nyuma"}
|
||||
|
||||
|
||||
- test:
|
||||
- group: "transactions"
|
||||
- name: "transaction pin authorization-invalid-pin[EN]"
|
||||
- url: "/"
|
||||
- delay: "2"
|
||||
- method: "POST"
|
||||
- body: '{"serviceCode": "*483*46#", "phoneNumber": "+254712345678", "sessionId": "AT_Idjhfuvelw64ffbweiy73nd5vnek024", "text": "1*0712345679*15*6987"}'
|
||||
- headers: { 'Content-Type': 'application/json' }
|
||||
- expected_status: [ 200 ]
|
||||
- validators:
|
||||
- compare: { "header": "content-type", "comparator": "str_eq", "expected": "text/plain" }
|
||||
- compare: { "header": "content-length", "comparator": "str_eq", "expected": "65" }
|
||||
- compare: { "raw_body": "", "comparator": "str_eq", expected: "CON Please enter your PIN. You have 2 attempts remaining.\n0. Back" }
|
||||
|
||||
- test:
|
||||
- group: "transactions"
|
||||
- name: "transaction pin authorization-invalid-pin[SW]"
|
||||
- url: "/"
|
||||
- delay: "2"
|
||||
- method: "POST"
|
||||
- body: '{"serviceCode": "*483*46#", "phoneNumber": "+254712345679", "sessionId": "AT_Idjhfuvelw64ffbweiy73nd5vnek025", "text": "1*0712345678*18*7845"}'
|
||||
- headers: { 'Content-Type': 'application/json' }
|
||||
- expected_status: [ 200 ]
|
||||
- validators:
|
||||
- compare: { "header": "content-type", "comparator": "str_eq", "expected": "text/plain" }
|
||||
- compare: {"header": "content-length", "comparator": "str_eq", "expected":"62"}
|
||||
- compare: {"raw_body":"", "comparator":"str_eq", expected: "CON Weka nambari ya siri. Una majaribio 2 yaliyobaki.\n0. Nyuma"}
|
||||
|
||||
- test:
|
||||
- group: "transactions"
|
||||
- name: "transaction pin authorization-valid-pin[EN]"
|
||||
- url: "/"
|
||||
- delay: "2"
|
||||
- method: "POST"
|
||||
- body: '{"serviceCode": "*483*46#", "phoneNumber": "+254712345678", "sessionId": "AT_Idjhfuvelw64ffbweiy73nd5vnek024", "text": "1*0712345679*15*6987*0000"}'
|
||||
- headers: { 'Content-Type': 'application/json' }
|
||||
- expected_status: [ 200 ]
|
||||
- validators:
|
||||
- compare: { "header": "content-type", "comparator": "str_eq", "expected": "text/plain" }
|
||||
- compare: { "header": "content-length", "comparator": "str_eq", "expected": "133" }
|
||||
- compare: { "raw_body": "", "comparator": "str_eq", expected: "CON Your request has been sent. Chebet Musau +254712345679 will receive 15.00 SRF from Kimani Omollo +254712345678.\n00. Back\n99. Exit" }
|
||||
|
||||
|
||||
- test:
|
||||
- group: "transactions"
|
||||
- name: "transaction pin authorization-valid-pin[SW]"
|
||||
- url: "/"
|
||||
- delay: "2"
|
||||
- method: "POST"
|
||||
- body: '{"serviceCode": "*483*46#", "phoneNumber": "+254712345679", "sessionId": "AT_Idjhfuvelw64ffbweiy73nd5vnek025", "text": "1*0712345678*18*7845*1111"}'
|
||||
- headers: { 'Content-Type': 'application/json' }
|
||||
- expected_status: [ 200 ]
|
||||
- validators:
|
||||
- compare: { "header": "content-type", "comparator": "str_eq", "expected": "text/plain" }
|
||||
- compare: {"header": "content-length", "comparator": "str_eq", "expected":"131"}
|
||||
- compare: {"raw_body":"", "comparator":"str_eq", expected: "CON Ombi lako limetumwa. Kimani Omollo +254712345678 atapokea 18.00 SRF kutoka kwa Chebet Musau +254712345679.\n00. Nyuma\n99. Ondoka"}
|
||||
|
||||
- test:
|
||||
- group: "transactions"
|
||||
- name: "send tokens-2[EN]"
|
||||
- url: "/"
|
||||
- delay: "3"
|
||||
- method: "POST"
|
||||
- body: '{"serviceCode": "*483*46#", "phoneNumber": "+254712345678", "sessionId": "AT_Idjhfuvelw64ffbweiy73nd5vnek026", "text": ""}'
|
||||
- headers: { 'Content-Type': 'application/json' }
|
||||
- expected_status: [ 200 ]
|
||||
- validators:
|
||||
- compare: { "header": "content-type", "comparator": "str_eq", "expected": "text/plain" }
|
||||
- compare: { "header": "content-length", "comparator": "str_eq", "expected": "51" }
|
||||
- compare: { "raw_body": "", "comparator": "str_eq", expected: "CON Balance 53.00 SRF\n1. Send\n2. My Account\n3. Help" }
|
||||
|
||||
- test:
|
||||
- group: "transactions"
|
||||
- name: "send tokens-2[SW]"
|
||||
- url: "/"
|
||||
- delay: "3"
|
||||
- method: "POST"
|
||||
- body: '{"serviceCode": "*483*46#", "phoneNumber": "+254712345679", "sessionId": "AT_Idjhfuvelw64ffbweiy73nd5vnek027", "text": ""}'
|
||||
- headers: { 'Content-Type': 'application/json' }
|
||||
- expected_status: [ 200 ]
|
||||
- validators:
|
||||
- compare: { "header": "content-type", "comparator": "str_eq", "expected": "text/plain" }
|
||||
- compare: {"header": "content-length", "comparator": "str_eq", "expected":"56"}
|
||||
- compare: {"raw_body":"", "comparator":"str_eq", expected: "CON Salio 47.00 SRF\n1. Tuma\n2. Akaunti yangu\n3. Usaidizi"}
|
||||
|
||||
- test:
|
||||
- group: "transactions"
|
||||
- name: "enter recipients phone number-2[EN]"
|
||||
- url: "/"
|
||||
- delay: "2"
|
||||
- method: "POST"
|
||||
- body: '{"serviceCode": "*483*46#", "phoneNumber": "+254712345678", "sessionId": "AT_Idjhfuvelw64ffbweiy73nd5vnek026", "text": "1"}'
|
||||
- headers: { 'Content-Type': 'application/json' }
|
||||
- expected_status: [ 200 ]
|
||||
- validators:
|
||||
- compare: { "header": "content-type", "comparator": "str_eq", "expected": "text/plain" }
|
||||
- compare: { "header": "content-length", "comparator": "str_eq", "expected": "30" }
|
||||
- compare: { "raw_body": "", "comparator": "str_eq", expected: "CON Enter phone number\n0. Back" }
|
||||
|
||||
- test:
|
||||
- group: "transactions"
|
||||
- name: "enter recipients phone number-2[SW]"
|
||||
- url: "/"
|
||||
- delay: "2"
|
||||
- method: "POST"
|
||||
- body: '{"serviceCode": "*483*46#", "phoneNumber": "+254712345679", "sessionId": "AT_Idjhfuvelw64ffbweiy73nd5vnek027", "text": "1"}'
|
||||
- headers: { 'Content-Type': 'application/json' }
|
||||
- expected_status: [ 200 ]
|
||||
- validators:
|
||||
- compare: { "header": "content-type", "comparator": "str_eq", "expected": "text/plain" }
|
||||
- compare: {"header": "content-length", "comparator": "str_eq", "expected":"33"}
|
||||
- compare: {"raw_body":"", "comparator":"str_eq", expected: "CON Weka nambari ya simu\n0. Nyuma"}
|
||||
|
||||
- test:
|
||||
- group: "transactions"
|
||||
- name: "enter token amount-2[EN]"
|
||||
- url: "/"
|
||||
- delay: "2"
|
||||
- method: "POST"
|
||||
- body: '{"serviceCode": "*483*46#", "phoneNumber": "+254712345678", "sessionId": "AT_Idjhfuvelw64ffbweiy73nd5vnek026", "text": "1*0712345679"}'
|
||||
- headers: { 'Content-Type': 'application/json' }
|
||||
- expected_status: [ 200 ]
|
||||
- validators:
|
||||
- compare: { "header": "content-type", "comparator": "str_eq", "expected": "text/plain" }
|
||||
- compare: { "header": "content-length", "comparator": "str_eq", "expected": "24" }
|
||||
- compare: { "raw_body": "", "comparator": "str_eq", expected: "CON Enter amount\n0. Back" }
|
||||
|
||||
- test:
|
||||
- group: "transactions"
|
||||
- name: "enter token amount-2[SW]"
|
||||
- url: "/"
|
||||
- delay: "2"
|
||||
- method: "POST"
|
||||
- body: '{"serviceCode": "*483*46#", "phoneNumber": "+254712345679", "sessionId": "AT_Idjhfuvelw64ffbweiy73nd5vnek027", "text": "1*0712345678"}'
|
||||
- headers: { 'Content-Type': 'application/json' }
|
||||
- expected_status: [ 200 ]
|
||||
- validators:
|
||||
- compare: { "header": "content-type", "comparator": "str_eq", "expected": "text/plain" }
|
||||
- compare: {"header": "content-length", "comparator": "str_eq", "expected":"25"}
|
||||
- compare: {"raw_body":"", "comparator":"str_eq", expected: "CON Weka kiwango\n0. Nyuma"}
|
||||
|
||||
- test:
|
||||
- group: "transactions"
|
||||
- name: "transaction pin authorization-2[EN]"
|
||||
- url: "/"
|
||||
- delay: "2"
|
||||
- method: "POST"
|
||||
- body: '{"serviceCode": "*483*46#", "phoneNumber": "+254712345678", "sessionId": "AT_Idjhfuvelw64ffbweiy73nd5vnek026", "text": "1*0712345679*850"}'
|
||||
- headers: { 'Content-Type': 'application/json' }
|
||||
- expected_status: [ 200 ]
|
||||
- validators:
|
||||
- compare: { "header": "content-type", "comparator": "str_eq", "expected": "text/plain" }
|
||||
- compare: { "header": "content-length", "comparator": "str_eq", "expected": "156" }
|
||||
- compare: { "raw_body": "", "comparator": "str_eq", expected: "CON Payment of 850.00 SRF to Chebet Musau +254712345679 has failed due to insufficient balance.\nYour Sarafu-Network balances is: 53.00 SRF\n00. Back\n99. Exit"}
|
||||
|
||||
- test:
|
||||
- group: "transactions"
|
||||
- name: "transaction pin authorization-2[SW]"
|
||||
- url: "/"
|
||||
- delay: "2"
|
||||
- method: "POST"
|
||||
- body: '{"serviceCode": "*483*46#", "phoneNumber": "+254712345679", "sessionId": "AT_Idjhfuvelw64ffbweiy73nd5vnek027", "text": "1*0712345678*1800"}'
|
||||
- headers: { 'Content-Type': 'application/json' }
|
||||
- expected_status: [ 200 ]
|
||||
- validators:
|
||||
- compare: { "header": "content-type", "comparator": "str_eq", "expected": "text/plain" }
|
||||
- compare: {"header": "content-length", "comparator": "str_eq", "expected":"186"}
|
||||
- compare: {"raw_body":"", "comparator":"str_eq", expected: "CON Malipo ya 1800.00 SRF kwa Kimani Omollo +254712345678 halijakamilika kwa sababu salio lako haitoshi.\nAkaunti yako ya Sarafu-Network ina salio ifuatayo: 47.00 SRF\n00. Nyuma\n99. Ondoka"}
|
||||
@@ -2,4 +2,3 @@ cic_base[full_graph]~=0.1.2a68
|
||||
cic-eth~=0.11.0b3
|
||||
cic-notify~=0.4.0a4
|
||||
cic-types~=0.1.0a10
|
||||
pyresttest==1.7.1
|
||||
@@ -4,19 +4,19 @@
|
||||
import pytest
|
||||
|
||||
# platform imports
|
||||
from cic_ussd.db.models.account import Account
|
||||
from cic_ussd.db.models.user import User
|
||||
|
||||
|
||||
def test_user(init_database, set_fernet_key):
|
||||
user = Account(blockchain_address='0x417f5962fc52dc33ff0689659b25848680dec6dcedc6785b03d1df60fc6d5c51',
|
||||
phone_number='+254700000000')
|
||||
user = User(blockchain_address='0x417f5962fc52dc33ff0689659b25848680dec6dcedc6785b03d1df60fc6d5c51',
|
||||
phone_number='+254700000000')
|
||||
user.create_password('0000')
|
||||
|
||||
session = Account.session
|
||||
session = User.session
|
||||
session.add(user)
|
||||
session.commit()
|
||||
|
||||
queried_user = session.query(Account).get(1)
|
||||
queried_user = session.query(User).get(1)
|
||||
assert queried_user.blockchain_address == '0x417f5962fc52dc33ff0689659b25848680dec6dcedc6785b03d1df60fc6d5c51'
|
||||
assert queried_user.phone_number == '+254700000000'
|
||||
assert queried_user.failed_pin_attempts == 0
|
||||
@@ -25,7 +25,7 @@ def test_user(init_database, set_fernet_key):
|
||||
|
||||
def test_user_state_transition(create_pending_user):
|
||||
user = create_pending_user
|
||||
session = Account.session
|
||||
session = User.session
|
||||
|
||||
assert user.get_account_status() == 'PENDING'
|
||||
user.activate_account()
|
||||
|
||||
@@ -8,7 +8,7 @@ import celery
|
||||
import pytest
|
||||
|
||||
# local imports
|
||||
from cic_ussd.db.models.account import Account
|
||||
from cic_ussd.db.models.user import User
|
||||
from cic_ussd.error import ActionDataNotFoundError
|
||||
from cic_ussd.conversions import from_wei
|
||||
|
||||
@@ -29,7 +29,7 @@ def test_successful_process_account_creation_callback_task(account_creation_acti
|
||||
# WARNING: [THE SETTING OF THE ROOT ID IS A HACK AND SHOULD BE REVIEWED OR IMPROVED]
|
||||
mocked_task_request.root_id = task_id
|
||||
|
||||
user = init_database.query(Account).filter_by(phone_number=phone_number).first()
|
||||
user = init_database.query(User).filter_by(phone_number=phone_number).first()
|
||||
assert user is None
|
||||
|
||||
redis_cache = init_redis_cache
|
||||
@@ -48,7 +48,7 @@ def test_successful_process_account_creation_callback_task(account_creation_acti
|
||||
)
|
||||
s_process_callback_request.apply_async().get()
|
||||
|
||||
user = init_database.query(Account).filter_by(phone_number=phone_number).first()
|
||||
user = init_database.query(User).filter_by(phone_number=phone_number).first()
|
||||
assert user.blockchain_address == result
|
||||
|
||||
action_data = redis_cache.get(task_id)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import json
|
||||
|
||||
# local imports
|
||||
from cic_ussd.db.models.account import Account
|
||||
from cic_ussd.db.models.user import User
|
||||
from cic_ussd.requests import (get_query_parameters,
|
||||
get_request_endpoint,
|
||||
get_request_method,
|
||||
@@ -58,8 +58,8 @@ def test_process_locked_accounts_requests(create_locked_accounts, valid_locked_a
|
||||
assert len(locked_account_addresses) == 10
|
||||
|
||||
# check that blockchain addresses are ordered by most recently accessed
|
||||
user_1 = Account.session.query(Account).filter_by(blockchain_address=locked_account_addresses[2]).first()
|
||||
user_2 = Account.session.query(Account).filter_by(blockchain_address=locked_account_addresses[7]).first()
|
||||
user_1 = User.session.query(User).filter_by(blockchain_address=locked_account_addresses[2]).first()
|
||||
user_2 = User.session.query(User).filter_by(blockchain_address=locked_account_addresses[7]).first()
|
||||
|
||||
assert user_1.updated > user_2.updated
|
||||
|
||||
|
||||
14
apps/cic-ussd/tests/fixtures/user.py
vendored
14
apps/cic-ussd/tests/fixtures/user.py
vendored
@@ -9,7 +9,7 @@ from cic_types.models.person import generate_metadata_pointer
|
||||
from faker import Faker
|
||||
|
||||
# local imports
|
||||
from cic_ussd.db.models.account import AccountStatus, Account
|
||||
from cic_ussd.db.models.user import AccountStatus, User
|
||||
from cic_ussd.redis import cache_data
|
||||
from cic_ussd.metadata import blockchain_address_to_metadata_pointer
|
||||
|
||||
@@ -19,7 +19,7 @@ fake = Faker()
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def create_activated_user(init_database, set_fernet_key):
|
||||
user = Account(
|
||||
user = User(
|
||||
blockchain_address='0xFD9c5aD15C72C6F60f1a119A608931226674243f',
|
||||
phone_number='+25498765432'
|
||||
)
|
||||
@@ -33,7 +33,7 @@ def create_activated_user(init_database, set_fernet_key):
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def create_valid_tx_recipient(init_database, set_fernet_key):
|
||||
user = Account(
|
||||
user = User(
|
||||
blockchain_address='0xd6204101012270Bf2558EDcFEd595938d1847bf0',
|
||||
phone_number='+25498765432'
|
||||
)
|
||||
@@ -47,7 +47,7 @@ def create_valid_tx_recipient(init_database, set_fernet_key):
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def create_valid_tx_sender(init_database, set_fernet_key):
|
||||
user = Account(
|
||||
user = User(
|
||||
blockchain_address='0xd6204101012270Bf2558EDcFEd595938d1847bf1',
|
||||
phone_number='+25498765433'
|
||||
)
|
||||
@@ -61,7 +61,7 @@ def create_valid_tx_sender(init_database, set_fernet_key):
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def create_pending_user(init_database, set_fernet_key):
|
||||
user = Account(
|
||||
user = User(
|
||||
blockchain_address='0x0ebdea8612c1b05d952c036859266c7f2cfcd6a29842d9c6cce3b9f1ba427588',
|
||||
phone_number='+25498765432'
|
||||
)
|
||||
@@ -72,7 +72,7 @@ def create_pending_user(init_database, set_fernet_key):
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def create_pin_blocked_user(init_database, set_fernet_key):
|
||||
user = Account(
|
||||
user = User(
|
||||
blockchain_address='0x0ebdea8612c1b05d952c036859266c7f2cfcd6a29842d9c6cce3b9f1ba427588',
|
||||
phone_number='+25498765432'
|
||||
)
|
||||
@@ -90,7 +90,7 @@ def create_locked_accounts(init_database, set_fernet_key):
|
||||
blockchain_address = str(uuid.uuid4())
|
||||
phone_number = fake.phone_number()
|
||||
pin = f'{randint(1000, 9999)}'
|
||||
user = Account(phone_number=phone_number, blockchain_address=blockchain_address)
|
||||
user = User(phone_number=phone_number, blockchain_address=blockchain_address)
|
||||
user.create_password(password=pin)
|
||||
user.failed_pin_attempts = 3
|
||||
user.account_status = AccountStatus.LOCKED.value
|
||||
|
||||
@@ -154,8 +154,8 @@ en:
|
||||
00. Back
|
||||
99. Exit
|
||||
exit_insufficient_balance: |-
|
||||
CON Payment of %{amount} %{token_symbol} to %{recipient_information} has failed due to insufficient balance.
|
||||
Your Sarafu-Network balances is: %{token_balance} %{token_symbol}
|
||||
CON Payment of %{amount} %{token_symbol} to %{recipient_information} has failed due to insufficent balance.
|
||||
Your Sarafu-Network balances is: %{token_balance}
|
||||
00. Back
|
||||
99. Exit
|
||||
help: |-
|
||||
|
||||
@@ -155,7 +155,7 @@ sw:
|
||||
99. Ondoka
|
||||
exit_insufficient_balance: |-
|
||||
CON Malipo ya %{amount} %{token_symbol} kwa %{recipient_information} halijakamilika kwa sababu salio lako haitoshi.
|
||||
Akaunti yako ya Sarafu-Network ina salio ifuatayo: %{token_balance} %{token_symbol}
|
||||
Akaunti yako ya Sarafu-Network ina salio ifuatayo: %{token_balance}
|
||||
00. Nyuma
|
||||
99. Ondoka
|
||||
help: |-
|
||||
|
||||
@@ -60,19 +60,16 @@ ARG pip_extra_index_url=https://pip.grassrootseconomics.net:8433
|
||||
ARG cic_base_version=0.1.2a77
|
||||
ARG cic_eth_version=0.11.0b6
|
||||
ARG sarafu_faucet_version=0.0.2a28
|
||||
ARG sarafu_token_version==0.0.1a6
|
||||
ARG cic_contracts_version=0.0.2a2
|
||||
RUN pip install --user --extra-index-url $pip_extra_index_url cic-base[full_graph]==$cic_base_version \
|
||||
cic-eth==$cic_eth_version \
|
||||
cic-contracts==$cic_contracts_version \
|
||||
sarafu-faucet==$sarafu_faucet_version \
|
||||
sarafu-token==$sarafu_token_version
|
||||
sarafu-faucet==$sarafu_faucet_version
|
||||
|
||||
FROM python:3.8.6-slim-buster as runtime-image
|
||||
|
||||
RUN apt-get update
|
||||
RUN apt-get install -y --no-install-recommends gnupg libpq-dev
|
||||
RUN apt-get install -y --no-install-recommends jq
|
||||
RUN apt-get install -y --no-install-recommends gnupg libpq-dev
|
||||
|
||||
COPY --from=compile-image /usr/local/bin/ /usr/local/bin/
|
||||
COPY --from=compile-image /usr/local/etc/cic/ /usr/local/etc/cic/
|
||||
|
||||
@@ -2,112 +2,82 @@
|
||||
|
||||
set -a
|
||||
|
||||
CIC_CHAIN_SPEC=${CIC_CHAIN_SPEC:-evm:bloxberg:8995}
|
||||
DEV_TOKEN_TYPE=${DEV_TOKEN_TYPE:-giftable}
|
||||
DEV_ETH_ACCOUNT_CONTRACT_DEPLOYER=0xEb3907eCad74a0013c259D5874AE7f22DcBcC95C
|
||||
DEV_ETH_ACCOUNT_RESERVE_MINTER=${DEV_ETH_ACCOUNT_RESERVE_MINTER:-$DEV_ETH_ACCOUNT_CONTRACT_DEPLOYER}
|
||||
DEV_ETH_ACCOUNT_ACCOUNTS_INDEX_WRITER=${DEV_ETH_ACCOUNT_RESERVE_MINTER:-$DEV_ETH_ACCOUNT_CONTRACT_DEPLOYER}
|
||||
DEV_RESERVE_AMOUNT=${DEV_ETH_RESERVE_AMOUNT:-""10000000000000000000000000000000000}
|
||||
DEV_FAUCET_AMOUNT=${DEV_FAUCET_AMOUNT:-0}
|
||||
DEV_ETH_KEYSTORE_FILE=${DEV_ETH_KEYSTORE_FILE:-`realpath ./keystore/UTC--2021-01-08T17-18-44.521011372Z--eb3907ecad74a0013c259d5874ae7f22dcbcc95c`}
|
||||
|
||||
set -e
|
||||
|
||||
DEV_ETH_ACCOUNT_CONTRACT_DEPLOYER=`eth-checksum $(cat $DEV_ETH_KEYSTORE_FILE | jq -r .address)`
|
||||
|
||||
if [ ! -z $DEV_ETH_GAS_PRICE ]; then
|
||||
gas_price_arg="--gas-price $DEV_ETH_GAS_PRICE"
|
||||
>&2 echo using static gas price $DEV_ETH_GAS_PRICE
|
||||
fi
|
||||
|
||||
if [[ $DEV_TOKEN_TYPE != 'giftable' && $DEV_TOKEN_TYPE != 'sarafu' ]]; then
|
||||
echo $DEV_TOKEN_TYPE
|
||||
>&2 echo DEV_TOKEN_TYPE must be one of [giftable,sarafu]
|
||||
exit 1
|
||||
fi
|
||||
faucet_amount=${DEV_FAUCET_AMOUNT:-0}
|
||||
keystore_file=$(realpath ./keystore/UTC--2021-01-08T17-18-44.521011372Z--eb3907ecad74a0013c259d5874ae7f22dcbcc95c)
|
||||
|
||||
echo "environment:"
|
||||
printenv
|
||||
echo \n
|
||||
|
||||
echo "using wallet address '$DEV_ETH_ACCOUNT_CONTRACT_DEPLOYER' from keystore file $DEV_ETH_KEYSTORE_FILE"
|
||||
|
||||
# This is a grassroots team convention for building the Bancor contracts using the bancor protocol repository truffle setup
|
||||
# Running this in docker-internal dev container (built from Docker folder in this repo) will write a
|
||||
# source-able env file to CIC_DATA_DIR. Services dependent on these contracts can mount this file OR
|
||||
# define these parameters at runtime
|
||||
# pushd /usr/src
|
||||
|
||||
if [ -z $CIC_DATA_DIR ]; then
|
||||
CIC_DATA_DIR=`mktemp -d`
|
||||
fi
|
||||
>&2 echo using data dir $CIC_DATA_DIR
|
||||
|
||||
init_level_file=${CIC_DATA_DIR}/.init
|
||||
if [ ! -f ${CIC_DATA_DIR}/.init ]; then
|
||||
echo "Creating .init file..."
|
||||
mkdir -p $CIC_DATA_DIR
|
||||
touch $CIC_DATA_DIR/.init
|
||||
touch /tmp/cic/config/.init
|
||||
# touch $init_level_file
|
||||
fi
|
||||
echo -n 1 > $init_level_file
|
||||
|
||||
# Abort on any error (including if wait-for-it fails).
|
||||
set -e
|
||||
|
||||
# Wait for the backend to be up, if we know where it is.
|
||||
if [[ -n "${ETH_PROVIDER}" ]]; then
|
||||
echo "waiting for ${ETH_PROVIDER}..."
|
||||
./wait-for-it.sh "${ETH_PROVIDER_HOST}:${ETH_PROVIDER_PORT}"
|
||||
|
||||
if [ ! -z "$DEV_USE_DOCKER_WAIT_SCRIPT" ]; then
|
||||
echo "waiting for ${ETH_PROVIDER}..."
|
||||
./wait-for-it.sh "${ETH_PROVIDER_HOST}:${ETH_PROVIDER_PORT}"
|
||||
fi
|
||||
DEV_RESERVE_ADDRESS=`giftable-token-deploy -p $ETH_PROVIDER -y $keystore_file -i $CIC_CHAIN_SPEC -v -w --name "Sarafu" --symbol "SRF" --decimals 6`
|
||||
giftable-token-gift -p $ETH_PROVIDER -y $keystore_file -i $CIC_CHAIN_SPEC -v -w -a $DEV_RESERVE_ADDRESS $DEV_RESERVE_AMOUNT
|
||||
|
||||
if [ $DEV_TOKEN_TYPE == 'giftable' ]; then
|
||||
>&2 echo "deploying 'giftable token'"
|
||||
DEV_RESERVE_ADDRESS=`giftable-token-deploy $gas_price_arg -p $ETH_PROVIDER -y $DEV_ETH_KEYSTORE_FILE -i $CIC_CHAIN_SPEC -vv -w --name "Giftable Token" --symbol "GFT" --decimals 6 -vv`
|
||||
else
|
||||
>&2 echo "deploying 'sarafu' token'"
|
||||
DEV_RESERVE_ADDRESS=`sarafu-token-deploy $gas_price_arg -p $ETH_PROVIDER -y $DEV_ETH_KEYSTORE_FILE -i $CIC_CHAIN_SPEC -vv -w --name "Sarafu" --decimals 6 -vv SRF $DEV_SARAFU_DEMURRAGE_LEVEL`
|
||||
fi
|
||||
giftable-token-gift $gas_price_arg -p $ETH_PROVIDER -y $DEV_ETH_KEYSTORE_FILE -i $CIC_CHAIN_SPEC -vv -w -a $DEV_RESERVE_ADDRESS $DEV_RESERVE_AMOUNT
|
||||
|
||||
#BANCOR_REGISTRY_ADDRESS=`cic-bancor-deploy $gas_price_arg --bancor-dir /usr/local/share/cic/bancor -z $DEV_ETH_RESERVE_ADDRESS -p $ETH_PROVIDER -o $DEV_ETH_ACCOUNT_CONTRACT_DEPLOYER`
|
||||
#BANCOR_REGISTRY_ADDRESS=`cic-bancor-deploy --bancor-dir /usr/local/share/cic/bancor -z $DEV_ETH_RESERVE_ADDRESS -p $ETH_PROVIDER -o $DEV_ETH_ACCOUNT_CONTRACT_DEPLOYER`
|
||||
|
||||
>&2 echo "deploy account index contract"
|
||||
DEV_ACCOUNT_INDEX_ADDRESS=`eth-accounts-index-deploy $gas_price_arg -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -y $DEV_ETH_KEYSTORE_FILE -vv -w`
|
||||
DEV_ACCOUNT_INDEX_ADDRESS=`eth-accounts-index-deploy -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -y $keystore_file -vv -w`
|
||||
>&2 echo "add deployer address as account index writer"
|
||||
eth-accounts-index-writer $gas_price_arg -y $DEV_ETH_KEYSTORE_FILE -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -a $DEV_ACCOUNT_INDEX_ADDRESS -ww -vv $debug $DEV_ETH_ACCOUNT_CONTRACT_DEPLOYER
|
||||
eth-accounts-index-writer -y $keystore_file -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -a $DEV_ACCOUNT_INDEX_ADDRESS -ww $debug $DEV_ETH_ACCOUNT_CONTRACT_DEPLOYER
|
||||
|
||||
CIC_REGISTRY_ADDRESS=`eth-contract-registry-deploy $gas_price_arg -i $CIC_CHAIN_SPEC -y $DEV_ETH_KEYSTORE_FILE --identifier BancorRegistry --identifier AccountRegistry --identifier TokenRegistry --identifier AddressDeclarator --identifier Faucet --identifier TransferAuthorization -p $ETH_PROVIDER -vv -w`
|
||||
eth-contract-registry-set $gas_price_arg -w -y $DEV_ETH_KEYSTORE_FILE -r $CIC_REGISTRY_ADDRESS -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -vv ContractRegistry $CIC_REGISTRY_ADDRESS
|
||||
eth-contract-registry-set $gas_price_arg -w -y $DEV_ETH_KEYSTORE_FILE -r $CIC_REGISTRY_ADDRESS -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -vv AccountRegistry $DEV_ACCOUNT_INDEX_ADDRESS
|
||||
CIC_REGISTRY_ADDRESS=`eth-contract-registry-deploy -i $CIC_CHAIN_SPEC -y $keystore_file --identifier BancorRegistry --identifier AccountRegistry --identifier TokenRegistry --identifier AddressDeclarator --identifier Faucet --identifier TransferAuthorization -p $ETH_PROVIDER -vv -w`
|
||||
eth-contract-registry-set -w -y $keystore_file -r $CIC_REGISTRY_ADDRESS -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -vv ContractRegistry $CIC_REGISTRY_ADDRESS
|
||||
#cic-registry-set -r $CIC_REGISTRY_ADDRESS -i $CIC_CHAIN_SPEC -k BancorRegistry -p $ETH_PROVIDER $BANCOR_REGISTRY_ADDRESS -vv
|
||||
eth-contract-registry-set -w -y $keystore_file -r $CIC_REGISTRY_ADDRESS -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -vv AccountRegistry $DEV_ACCOUNT_INDEX_ADDRESS
|
||||
|
||||
# Deploy address declarator registry
|
||||
>&2 echo "deploy address declarator contract"
|
||||
declarator_description=0x546869732069732074686520434943206e6574776f726b000000000000000000
|
||||
DEV_DECLARATOR_ADDRESS=`eth-address-declarator-deploy -y $DEV_ETH_KEYSTORE_FILE -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -w -vv $declarator_description`
|
||||
eth-contract-registry-set $gas_price_arg -w -y $DEV_ETH_KEYSTORE_FILE -r $CIC_REGISTRY_ADDRESS -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -vv AddressDeclarator $DEV_DECLARATOR_ADDRESS
|
||||
DEV_DECLARATOR_ADDRESS=`eth-address-declarator-deploy -y $keystore_file -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -w -v $declarator_description`
|
||||
eth-contract-registry-set -w -y $keystore_file -r $CIC_REGISTRY_ADDRESS -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -vv AddressDeclarator $DEV_DECLARATOR_ADDRESS
|
||||
|
||||
# Deploy transfer authorization contact
|
||||
>&2 echo "deploy address declarator contract"
|
||||
DEV_TRANSFER_AUTHORIZATION_ADDRESS=`erc20-transfer-auth-deploy $gas_price_arg -y $DEV_ETH_KEYSTORE_FILE -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -w -vv`
|
||||
eth-contract-registry-set $gas_price_arg -w -y $DEV_ETH_KEYSTORE_FILE -r $CIC_REGISTRY_ADDRESS -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -vv TransferAuthorization $DEV_TRANSFER_AUTHORIZATION_ADDRESS
|
||||
DEV_TRANSFER_AUTHORIZATION_ADDRESS=`erc20-transfer-auth-deploy -y $keystore_file -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -w -v`
|
||||
eth-contract-registry-set -w -y $keystore_file -r $CIC_REGISTRY_ADDRESS -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -vv TransferAuthorization $DEV_TRANSFER_AUTHORIZATION_ADDRESS
|
||||
|
||||
# Deploy token index contract
|
||||
>&2 echo "deploy token index contract"
|
||||
DEV_TOKEN_INDEX_ADDRESS=`eth-token-index-deploy $gas_price_arg -y $DEV_ETH_KEYSTORE_FILE -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -w -vv`
|
||||
eth-contract-registry-set $gas_price_arg -w -y $DEV_ETH_KEYSTORE_FILE -r $CIC_REGISTRY_ADDRESS -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -vv TokenRegistry $DEV_TOKEN_INDEX_ADDRESS
|
||||
DEV_TOKEN_INDEX_ADDRESS=`eth-token-index-deploy -y $keystore_file -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -w -v`
|
||||
eth-contract-registry-set -w -y $keystore_file -r $CIC_REGISTRY_ADDRESS -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -vv TokenRegistry $DEV_TOKEN_INDEX_ADDRESS
|
||||
>&2 echo "add reserve token to token index"
|
||||
eth-token-index-add $gas_price_arg -w -y $DEV_ETH_KEYSTORE_FILE -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -vv -a $DEV_TOKEN_INDEX_ADDRESS $DEV_RESERVE_ADDRESS
|
||||
eth-token-index-add -w -y $keystore_file -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -vv -a $DEV_TOKEN_INDEX_ADDRESS $DEV_RESERVE_ADDRESS
|
||||
|
||||
# Sarafu faucet contract
|
||||
>&2 echo "deploy token faucet contract"
|
||||
DEV_FAUCET_ADDRESS=`sarafu-faucet-deploy $gas_price_arg -y $DEV_ETH_KEYSTORE_FILE -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -w -vv --account-index-address $DEV_ACCOUNT_INDEX_ADDRESS $DEV_RESERVE_ADDRESS`
|
||||
eth-contract-registry-set $gas_price_arg -w -y $DEV_ETH_KEYSTORE_FILE -r $CIC_REGISTRY_ADDRESS -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -vv Faucet $DEV_FAUCET_ADDRESS
|
||||
DEV_FAUCET_ADDRESS=`sarafu-faucet-deploy -y $keystore_file -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -w -v --account-index-address $DEV_ACCOUNT_INDEX_ADDRESS $DEV_RESERVE_ADDRESS`
|
||||
eth-contract-registry-set -w -y $keystore_file -r $CIC_REGISTRY_ADDRESS -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -vv Faucet $DEV_FAUCET_ADDRESS
|
||||
>&2 echo "set faucet as token minter"
|
||||
giftable-token-minter $gas_price_arg -w -y $DEV_ETH_KEYSTORE_FILE -a $DEV_RESERVE_ADDRESS -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -vv $DEV_FAUCET_ADDRESS
|
||||
giftable-token-minter -w -y $keystore_file -a $DEV_RESERVE_ADDRESS -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -vv $DEV_FAUCET_ADDRESS
|
||||
|
||||
>&2 echo "set token faucet amount"
|
||||
sarafu-faucet-set $gas_price_arg -y $DEV_ETH_KEYSTORE_FILE -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -a $DEV_FAUCET_ADDRESS -vv $DEV_FAUCET_AMOUNT
|
||||
sarafu-faucet-set -y $keystore_file -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -a $DEV_FAUCET_ADDRESS $faucet_amount
|
||||
|
||||
|
||||
else
|
||||
|
||||
@@ -24,7 +24,7 @@ variables:
|
||||
IMAGE_TAG_BASE: $CI_REGISTRY_IMAGE/$APP_NAME:$CI_COMMIT_BRANCH-$CI_COMMIT_SHORT_SHA
|
||||
LATEST_TAG: $CI_REGISTRY_IMAGE/$APP_NAME:latest
|
||||
script:
|
||||
- export IMAGE_TAG="$IMAGE_TAG_BASE-$(date +%s)"
|
||||
- export IMAGE_TAG="$IMAGE_TAG_BASE-$(date +%F.%H%M%S)"
|
||||
- mkdir -p /kaniko/.docker
|
||||
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > "/kaniko/.docker/config.json"
|
||||
# - /kaniko/executor --context $CONTEXT --dockerfile $DOCKERFILE_PATH $KANIKO_CACHE_ARGS --destination $IMAGE_TAG
|
||||
|
||||
@@ -85,7 +85,6 @@ services:
|
||||
# ETH_PROVIDER should be broken out into host/port but cic-eth expects this
|
||||
ETH_PROVIDER: http://eth:8545
|
||||
# And these two are for wait-for-it (could parse this)
|
||||
DEV_USE_DOCKER_WAIT_SCRIPT: 1
|
||||
ETH_PROVIDER_HOST: eth
|
||||
ETH_PROVIDER_PORT: 8545
|
||||
CIC_CHAIN_SPEC: ${CIC_CHAIN_SPEC:-evm:bloxberg:8996}
|
||||
@@ -104,9 +103,6 @@ services:
|
||||
DEV_PIP_EXTRA_INDEX_URL: ${DEV_PIP_EXTRA_INDEX_URL:-https://pip.grassrootseconomics.net:8433}
|
||||
RUN_MASK: ${RUN_MASK:-0} # bit flags; 1: contract migrations 2: seed data
|
||||
DEV_FAUCET_AMOUNT: ${DEV_FAUCET_AMOUNT:-0}
|
||||
DEV_TOKEN_TYPE: ${DEV_TOKEN_TYPE:-giftable}
|
||||
DEV_SARAFU_DEMURRAGE_LEVEL: ${DEV_SARAFU_DEMURRAGE_LEVEL:-196454828847045000000000000000000}
|
||||
DEV_ETH_GAS_PRICE: ${DEV_ETH_GAS_PRICE:-1}
|
||||
command: ["./run_job.sh"]
|
||||
#command: ["./reset.sh"]
|
||||
depends_on:
|
||||
|
||||
Reference in New Issue
Block a user