Merge branch 'philip/fix-africastalking-parser' into 'master'

Philip/fix africastalking parser

Closes cic-ussd#41

See merge request grassrootseconomics/cic-internal-integration!140
This commit is contained in:
Philip Wafula 2021-05-11 10:58:00 +00:00
commit 1f668384cc
6 changed files with 43 additions and 30 deletions

View File

@ -4,7 +4,7 @@ LOCALE_FALLBACK=en
LOCALE_PATH=/usr/src/cic-ussd/var/lib/locale/ LOCALE_PATH=/usr/src/cic-ussd/var/lib/locale/
MAX_BODY_LENGTH=1024 MAX_BODY_LENGTH=1024
PASSWORD_PEPPER=QYbzKff6NhiQzY3ygl2BkiKOpER8RE/Upqs/5aZWW+I= PASSWORD_PEPPER=QYbzKff6NhiQzY3ygl2BkiKOpER8RE/Upqs/5aZWW+I=
SERVICE_CODE=*483*46# SERVICE_CODE=*483*46#,*483*061#,*384*96#
[phone_number] [phone_number]
REGION=KE REGION=KE

View File

@ -13,7 +13,7 @@ import argparse
import logging import logging
import urllib import urllib
from xdg.BaseDirectory import xdg_config_home from xdg.BaseDirectory import xdg_config_home
from urllib import request from urllib import parse, request
# third-party imports # third-party imports
from confini import Config from confini import Config
@ -92,9 +92,9 @@ def main():
data['text'] = user_input data['text'] = user_input
req = urllib.request.Request(url) req = urllib.request.Request(url)
data_str = json.dumps(data) urlencoded_data = parse.urlencode(data)
data_bytes = data_str.encode('utf-8') data_bytes = urlencoded_data.encode('utf-8')
req.add_header('Content-Type', 'application/json') req.add_header('Content-Type', 'application/x-www-form-urlencoded')
req.data = data_bytes req.data = data_bytes
response = urllib.request.urlopen(req) response = urllib.request.urlopen(req)
response_data = response.read().decode('utf-8') response_data = response.read().decode('utf-8')

View File

@ -4,6 +4,7 @@
# standard imports # standard imports
import json import json
import logging import logging
from urllib.parse import parse_qs
# third-party imports # third-party imports
import celery import celery
@ -33,8 +34,7 @@ from cic_ussd.requests import (get_request_endpoint,
from cic_ussd.runnable.server_base import exportable_parser, logg from cic_ussd.runnable.server_base import exportable_parser, logg
from cic_ussd.session.ussd_session import UssdSession as InMemoryUssdSession from cic_ussd.session.ussd_session import UssdSession as InMemoryUssdSession
from cic_ussd.state_machine import UssdStateMachine from cic_ussd.state_machine import UssdStateMachine
from cic_ussd.validator import check_ip, check_request_content_length, check_service_code, validate_phone_number, \ from cic_ussd.validator import check_ip, check_request_content_length, validate_phone_number, validate_presence
validate_presence
args = exportable_parser.parse_args() args = exportable_parser.parse_args()
@ -124,6 +124,9 @@ else:
raise InitializationError(f'Default token data for: {chain_str} not found.') raise InitializationError(f'Default token data for: {chain_str} not found.')
valid_service_codes = config.get('APP_SERVICE_CODE').split(",")
def application(env, start_response): def application(env, start_response):
"""Loads python code for application to be accessible over web server """Loads python code for application to be accessible over web server
:param env: Object containing server and request information :param env: Object containing server and request information
@ -139,13 +142,27 @@ def application(env, start_response):
if get_request_method(env=env) == 'POST' and get_request_endpoint(env=env) == '/': if get_request_method(env=env) == 'POST' and get_request_endpoint(env=env) == '/':
# get post data if env.get('CONTENT_TYPE') != 'application/x-www-form-urlencoded':
post_data = json.load(env.get('wsgi.input')) start_response('405 Play by the rules', errors_headers)
return []
service_code = post_data.get('serviceCode') post_data = env.get('wsgi.input').read()
phone_number = post_data.get('phoneNumber') post_data = post_data.decode('utf-8')
external_session_id = post_data.get('sessionId')
user_input = post_data.get('text') try:
post_data = parse_qs(post_data)
except TypeError:
start_response('400 Size matters', errors_headers)
return []
service_code = post_data.get('serviceCode')[0]
phone_number = post_data.get('phoneNumber')[0]
external_session_id = post_data.get('sessionId')[0]
try:
user_input = post_data.get('text')[0]
except TypeError:
user_input = ""
# add validation for phone number # add validation for phone number
if phone_number: if phone_number:
@ -162,14 +179,14 @@ def application(env, start_response):
return [] return []
# validate service code # validate service code
if not check_service_code(code=service_code, config=config): if service_code not in valid_service_codes:
response = define_multilingual_responses( response = define_multilingual_responses(
key='ussd.kenya.invalid_service_code', key='ussd.kenya.invalid_service_code',
locales=['en', 'sw'], locales=['en', 'sw'],
prefix='END', prefix='END',
valid_service_code=config.get('APP_SERVICE_CODE')) valid_service_code=valid_service_codes[0])
response_bytes, headers = define_response_with_content(headers=errors_headers, response=response) response_bytes, headers = define_response_with_content(headers=headers, response=response)
start_response('400 Invalid service code', headers) start_response('200 OK', headers)
return [response_bytes] return [response_bytes]
# validate phone number # validate phone number
@ -192,3 +209,8 @@ def application(env, start_response):
start_response('200 OK,', headers) start_response('200 OK,', headers)
SessionBase.session.close() SessionBase.session.close()
return [response_bytes] return [response_bytes]
else:
start_response('405 Play by the rules', errors_headers)
return []

View File

@ -45,19 +45,6 @@ def check_request_content_length(config: Config, env: dict):
config.get('APP_MAX_BODY_LENGTH')) config.get('APP_MAX_BODY_LENGTH'))
def check_service_code(code: str, config: Config):
"""Checks whether provided code matches expected service code
:param config: A dictionary object containing configuration values
:type config: Config
:param code: Service code passed over request
:type code: str
:return: Service code validity
:rtype: boolean
"""
return code == config.get('APP_SERVICE_CODE')
def check_known_user(phone: str): def check_known_user(phone: str):
""" """
This method attempts to ascertain whether the user already exists and is known to the system. This method attempts to ascertain whether the user already exists and is known to the system.

View File

@ -158,6 +158,8 @@ en:
Your Sarafu-Network balances is: %{token_balance} Your Sarafu-Network balances is: %{token_balance}
00. Back 00. Back
99. Exit 99. Exit
invalid_service_code: |-
Please dial %{valid_service_code} to access Sarafu Network
help: |- help: |-
CON For assistance call %{support_phone} CON For assistance call %{support_phone}
00. Back 00. Back

View File

@ -158,6 +158,8 @@ sw:
Akaunti yako ya Sarafu-Network ina salio ifuatayo: %{token_balance} Akaunti yako ya Sarafu-Network ina salio ifuatayo: %{token_balance}
00. Nyuma 00. Nyuma
99. Ondoka 99. Ondoka
invalid_service_code: |-
Bonyeza %{valid_service_code} kutumia mtandao wa Sarafu
help: |- help: |-
CON Kwa usaidizi piga simu %{support_phone} CON Kwa usaidizi piga simu %{support_phone}
0. Nyuma 0. Nyuma