diff --git a/.circleci/config.yml b/.circleci/config.yml index 25ab3c7..a2fcfe6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -52,7 +52,7 @@ jobs: coverage report -m mkdir ../test-reports coverage html -d ../test-reports -# COVERALLS_REPO_TOKEN=${COVERALLS_REPO_TOKEN} coveralls + COVERALLS_REPO_TOKEN=${COVERALLS_REPO_TOKEN} coveralls - store_test_results: path: test-reports - store_artifacts: diff --git a/metering/handlers/freecall_handler.py b/metering/handlers/freecall_handler.py index f6b9e2f..c765c16 100644 --- a/metering/handlers/freecall_handler.py +++ b/metering/handlers/freecall_handler.py @@ -1,47 +1,45 @@ -import json -import logging - -from constants import StatusCode, StatusMessage, HEADER_POST_RESPONSE +from constants import HEADER_POST_RESPONSE +from constants import StatusCode +from constants import StatusMessage +from logger import get_logger from services import UsageService -from logger import setup_logger -from utils import validate_request, make_response + +from utils import generate_lambda_response +from utils import validate_request usage_service = UsageService() -setup_logger() -logger = logging.getLogger(__name__) +logger = get_logger(__name__) -required_keys = ['organization_id', 'service_id'] +required_keys = ["organization_id", "service_id"] def main(event, context): - if validate_request(required_keys, event['queryStringParameters']): - try: - org_id = event['queryStringParameters']['organization_id'] - service_id = event['queryStringParameters']['service_id'] - username = event['queryStringParameters']['username'] + logger.info("Free call request received") + try: + if validate_request(required_keys, event["queryStringParameters"]): + org_id = event["queryStringParameters"]["organization_id"] + service_id = event["queryStringParameters"]["service_id"] + username = event["queryStringParameters"]["username"] + + logger.info(f"Fetched values from request \n" + f"username: {username} \n" + f"org_id: {org_id} \n" + f"service_id: {service_id} \n") + free_call_details = usage_service.get_free_call_details( username, org_id, service_id) - return_value = make_response( - status_code=StatusCode.SUCCESS_GET_CODE, - header=HEADER_POST_RESPONSE, - body=json.dumps(free_call_details) - ) - - except Exception as e: - logger.error(e) - return_value = make_response( - status_code=StatusCode.SERVER_ERROR_CODE, - header=HEADER_POST_RESPONSE, - body=json.dumps({"error": StatusMessage.SERVER_ERROR_MSG}) - ) - - else: - logger.error(f"Request validation failed for {event['queryStringParameters']}") - return_value = make_response( - status_code=StatusCode.BAD_PARAMETERS_CODE, - header=HEADER_POST_RESPONSE, - body=json.dumps({"error": StatusMessage.BAD_PARAMETER}) - ) - - return return_value + response = free_call_details + status_code = StatusCode.SUCCESS_GET_CODE + else: + logger.error(f"Request validation failed") + logger.info(event) + response = StatusMessage.BAD_PARAMETER + status_code = StatusCode.BAD_PARAMETERS_CODE + except Exception as e: + logger.error("Failed to get free call details") + logger.info(event) + status_code = (StatusCode.SERVER_ERROR_CODE, ) + response = StatusMessage.SERVER_ERROR_MSG + + return generate_lambda_response(status_code, response) diff --git a/metering/handlers/usage_handler.py b/metering/handlers/usage_handler.py index 2e1bc72..6f2a0d7 100644 --- a/metering/handlers/usage_handler.py +++ b/metering/handlers/usage_handler.py @@ -1,44 +1,53 @@ import json import logging -from constants import StatusCode, StatusMessage -from logger import setup_logger +from constants import StatusCode +from constants import StatusMessage +from logger import get_logger from services import UsageService -from utils import validate_request, make_response, usage_record_add_verify_fields + +from utils import generate_lambda_response +from utils import usage_record_add_verify_fields +from utils import validate_request usage_service = UsageService() -setup_logger() -logger = logging.getLogger(__name__) +logger = get_logger(__name__) -required_keys = ["organization_id", "service_id", "username", 'usage_value', 'usage_type', - 'service_method', 'group_id', 'status', 'start_time', 'end_time'] +required_keys = [ + "organization_id", + "service_id", + "username", + "usage_value", + "usage_type", + "service_method", + "group_id", + "status", + "start_time", + "end_time", +] def main(event, context): - request_dict = json.loads(event['body']) - + logger.info("Usage record request received") try: + request_dict = json.loads(event["body"]) if validate_request(required_keys, request_dict): usage_detail_dict = usage_record_add_verify_fields(request_dict) - print(f"usage record after modification: {usage_detail_dict}") + logging.info( + f"usage record after modification: {usage_detail_dict}") usage_service.save_usage_details(usage_detail_dict) - response = make_response( - StatusCode.SUCCESS_GET_CODE, - json.dumps({"status": StatusMessage.SUCCESS_POST_CODE}) - ) + response = "success" + status_code = StatusCode.SUCCESS_GET_CODE else: - logger.error(f'Request validation failed {request_dict}') - response = make_response( - StatusCode.BAD_PARAMETERS_CODE, - json.dumps({"status": StatusMessage.BAD_PARAMETER}) - ) + logger.error(f"Request validation failed") + logger.info(event) + response = StatusMessage.BAD_PARAMETER + status_code = StatusCode.BAD_PARAMETERS_CODE except Exception as e: - logger.error(e) - logger.error(f'failed for request {request_dict}') - response = make_response( - StatusCode.SERVER_ERROR_CODE, - json.dumps({"status": StatusMessage.SERVER_ERROR_MSG}) - ) - - return response + logger.error("Failed to get free call details") + logger.info(event) + status_code = (StatusCode.SERVER_ERROR_CODE, ) + response = StatusMessage.SERVER_ERROR_MSG + + return generate_lambda_response(status_code, response) diff --git a/metering/logger.py b/metering/logger.py index c39d5cb..07a8579 100644 --- a/metering/logger.py +++ b/metering/logger.py @@ -1,12 +1,18 @@ -import logging.config +import logging +import sys +FORMATTER = logging.Formatter("%(asctime)s %(levelname)s %(name)s %(message)s") -def setup_logger(): - logging.basicConfig( - level=logging.INFO, format='%(asctime)s - %(message)s', datefmt='%d-%b-%y %H:%M:%S') +def get_console_handler(): + console_handler = logging.StreamHandler(sys.stdout) + console_handler.setFormatter(FORMATTER) + return console_handler -if __name__ == '__main__': - setup_logger() - logger = logging.getLogger() - logger.info("hi, it is test") + +def get_logger(logger_name): + logger = logging.getLogger(logger_name) + logger.setLevel(logging.INFO) + logger.addHandler(get_console_handler()) + logger.propagate = False + return logger diff --git a/metering/repository/base_repository.py b/metering/repository/base_repository.py index da4211e..fcd6942 100644 --- a/metering/repository/base_repository.py +++ b/metering/repository/base_repository.py @@ -1,17 +1,16 @@ import os +from settings import DB_URL from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker -from settings import DB_URL -engine = create_engine(DB_URL, echo=True) +engine = create_engine(DB_URL, echo=False) Session = sessionmaker(bind=engine) default_session = Session() class BaseRepository(object): - def get_default_session(self, session=None): if not session: return default_session diff --git a/metering/serverless.yml b/metering/serverless.yml index 3c8e133..d2c7c4c 100644 --- a/metering/serverless.yml +++ b/metering/serverless.yml @@ -55,7 +55,7 @@ functions: arn: ${file(./config.${self:provider.stage}.json):SIGN_AUTHORIZER} identitySource: ${file(./config.${self:provider.stage}.json):SIGN_HEADERS} name: verify_authorizer -# resultTtlInSeconds: 100 + # resultTtlInSeconds: 100 cors: origin: ${self:custom.origin.${self:provider.stage}} headers: @@ -77,9 +77,6 @@ functions: subnetIds: - ${file(./config.${self:provider.stage}.json):VPC1} - ${file(./config.${self:provider.stage}.json):VPC2} - # The following are a few example events you can configure - # NOTE: Please make sure to change your handler code to work with those events - # Check the event documentation for details events: - http: path: /usage/freecalls @@ -89,7 +86,7 @@ functions: arn: ${file(./config.${self:provider.stage}.json):SIGN_AUTHORIZER} identitySource: ${file(./config.${self:provider.stage}.json):SIGN_HEADERS} name: freecall_authorizer -# resultTtlInSeconds: 100 + # resultTtlInSeconds: 100 cors: origin: ${self:custom.origin.${self:provider.stage}} headers: @@ -110,9 +107,6 @@ functions: subnetIds: - ${file(./config.${self:provider.stage}.json):VPC1} - ${file(./config.${self:provider.stage}.json):VPC2} - # The following are a few example events you can configure - # NOTE: Please make sure to change your handler code to work with those events - # Check the event documentation for details events: - http: path: /usage @@ -122,7 +116,7 @@ functions: arn: ${file(./config.${self:provider.stage}.json):SIGN_AUTHORIZER} identitySource: ${file(./config.${self:provider.stage}.json):SIGN_HEADERS} name: usage_authorizer -# resultTtlInSeconds: 100 + # resultTtlInSeconds: 100 cors: origin: ${self:custom.origin.${self:provider.stage}} headers: diff --git a/metering/services.py b/metering/services.py index 186d9d3..52504b1 100644 --- a/metering/services.py +++ b/metering/services.py @@ -1,29 +1,42 @@ import json -import logging import boto3 as boto3 - -from settings import CONTRACT_API_ARN, CONTRACT_API_STAGE +from logger import get_logger +from settings import CONTRACT_API_ARN +from settings import CONTRACT_API_STAGE from storage import DatabaseStorage + from utils import is_free_call -logger = logging.getLogger(__name__) +logger = get_logger(__name__) class UsageService(object): storage_service = DatabaseStorage() - def get_free_call_details(self, username, org_id, service_id, group_id=None): + def get_free_call_details(self, + username, + org_id, + service_id, + group_id=None): total_calls, free_calls = self.storage_service.get_usage_details( username, org_id, service_id, group_id) + logger.info( + f"Free calls allowed: {free_calls}, Total calls made: {total_calls}" + ) if not free_calls: free_calls = 0 if not total_calls: total_calls = 0 - response = {"username": username, "org_id": org_id, "service_id": service_id, "total_calls_made": total_calls, - "free_calls_allowed": free_calls} + response = { + "username": username, + "org_id": org_id, + "service_id": service_id, + "total_calls_made": total_calls, + "free_calls_allowed": free_calls, + } logger.info(response) return response @@ -31,35 +44,42 @@ def get_free_call_details(self, username, org_id, service_id, group_id=None): def save_usage_details(self, usage_details_dict): # nedd to introduce entities when we enhance feature to this service right now directly using dicts if not is_free_call(usage_details_dict): - channel_id = usage_details_dict['channel_id'] - group_id = usage_details_dict['group_id'] - user_address = APIUtilityService().get_user_address(group_id, channel_id) - usage_details_dict['user_address'] = user_address + logger.info("Received usage record request for paid call") + channel_id = usage_details_dict["channel_id"] + group_id = usage_details_dict["group_id"] + + user_address = APIUtilityService().get_user_address( + group_id, channel_id) + usage_details_dict["user_address"] = user_address + logger.info( + f"fetched user address from contract api: {user_address}") + self.storage_service.add_usage_data(usage_details_dict) return class APIUtilityService: - def __init__(self): - self.lambda_client = boto3.client('lambda') + self.lambda_client = boto3.client("lambda") def get_user_address(self, group_id, channel_id): lambda_payload = { "httpMethod": "GET", - "requestContext": {"stage": CONTRACT_API_STAGE}, - "path": f"/contract-api/group/{group_id}/channel/{channel_id}" + "requestContext": { + "stage": CONTRACT_API_STAGE + }, + "path": f"/contract-api/group/{group_id}/channel/{channel_id}", } try: + logger.info(f"Calling contract api for user_address") response = self.lambda_client.invoke( FunctionName=CONTRACT_API_ARN, - Payload=json.dumps(lambda_payload) - ) + Payload=json.dumps(lambda_payload)) response_body_raw = json.loads( - response.get('Payload').read())['body'] + response.get("Payload").read())["body"] response_body = json.loads(response_body_raw) - user_address = response_body['data'][0]['sender'] + user_address = response_body["data"][0]["sender"] except Exception as e: print(e) raise Exception("Failed to get user address from marketplace") diff --git a/metering/storage.py b/metering/storage.py index dd51083..fa66074 100644 --- a/metering/storage.py +++ b/metering/storage.py @@ -1,12 +1,16 @@ import logging -from models import UserOrgGroupModel, UsageModel +from constants import PAYMENT_MODE_ESCROW_VALUE +from constants import PAYMENT_MODE_FREECALL_VALUE +from logger import get_logger +from models import UsageModel +from models import UserOrgGroupModel + from repository.org_service_config_repository import OrgServiceRepo from repository.usage_repository import UsageRepository from repository.user_org_group_repository import UserOrgGroupRepository -from constants import PAYMENT_MODE_FREECALL_VALUE, PAYMENT_MODE_ESCROW_VALUE -logger = logging.getLogger(__name__) +logger = get_logger(__name__) class Storage(object): @@ -21,7 +25,6 @@ def get_usage_details(self, username, org_id, service_id, group_id=None): class DatabaseStorage(Storage): - def __init__(self): self.usage_repo = UsageRepository() self.org_service_config_repo = OrgServiceRepo() @@ -29,20 +32,20 @@ def __init__(self): def get_user_org_group_id(self, usage_details): - if usage_details['payment_mode'] == PAYMENT_MODE_FREECALL_VALUE: + if usage_details["payment_mode"] == PAYMENT_MODE_FREECALL_VALUE: user_org_group_id = self.user_org_group_repo.get_user_org_group_id_by_username( - usage_details['username'], - usage_details['organization_id'], - usage_details['service_id'], - usage_details['service_method'] + usage_details["username"], + usage_details["organization_id"], + usage_details["service_id"], + usage_details["service_method"], ) - elif usage_details['payment_mode'] == PAYMENT_MODE_ESCROW_VALUE: + elif usage_details["payment_mode"] == PAYMENT_MODE_ESCROW_VALUE: user_org_group_id = self.user_org_group_repo.get_user_org_group_id_by_user_address( - usage_details['user_address'], - usage_details['organization_id'], - usage_details['service_id'], - usage_details['service_method'], - usage_details['group_id'] + usage_details["user_address"], + usage_details["organization_id"], + usage_details["service_id"], + usage_details["service_method"], + usage_details["group_id"], ) else: raise Exception("Invalid payment mode") @@ -68,50 +71,58 @@ def add_usage_data(self, usage_details): user_name=usage_details["username"], user_address=usage_details["user_address"], service_id=usage_details["service_id"], - resource=usage_details["service_method"] + resource=usage_details["service_method"], ) self.user_org_group_repo.create_item(new_user_org_record) - logger.info(f"Added user org group data\ngroup_id: {usage_details['group_id']}, " - f"org_id: {usage_details['organization_id']}, " - f"user_name: {usage_details['username']}, " - f"user_address: {usage_details['user_address']}, " - f"service_id: {usage_details['service_id']}, " - f"resource {usage_details['service_method']}") + logger.info( + f"Added user org group data\ngroup_id: {usage_details['group_id']}, " + f"org_id: {usage_details['organization_id']}, " + f"user_name: {usage_details['username']}, " + f"user_address: {usage_details['user_address']}, " + f"service_id: {usage_details['service_id']}, " + f"resource {usage_details['service_method']}") user_org_group_id = self.get_user_org_group_id(usage_details) usage_record = UsageModel( - client_type=usage_details['client_type'], - ethereum_json_rpc_endpoint=usage_details['ethereum_json_rpc_endpoint'], - registry_address_key=usage_details['registry_address_key'], + client_type=usage_details["client_type"], + ethereum_json_rpc_endpoint=usage_details[ + "ethereum_json_rpc_endpoint"], + registry_address_key=usage_details["registry_address_key"], user_org_group_id=user_org_group_id, - status=usage_details['status'], - start_time=usage_details['start_time'], - end_time=usage_details['end_time'], - response_time=usage_details['response_time'], - response_code=usage_details['response_code'], - error_message=usage_details['error_message'], - version=usage_details['version'], - channel_id=usage_details['channel_id'], - operation=usage_details['operation'], + status=usage_details["status"], + start_time=usage_details["start_time"], + end_time=usage_details["end_time"], + response_time=usage_details["response_time"], + response_code=usage_details["response_code"], + error_message=usage_details["error_message"], + version=usage_details["version"], + channel_id=usage_details["channel_id"], + operation=usage_details["operation"], group_id=usage_details["group_id"], org_id=usage_details["organization_id"], - usage_type=usage_details['usage_type'], - usage_value=usage_details['usage_value'], - user_details=usage_details['user_details'], + usage_type=usage_details["usage_type"], + usage_value=usage_details["usage_value"], + user_details=usage_details["user_details"], user_name=usage_details["username"], service_id=usage_details["service_id"], resource=usage_details["service_method"], request_id=usage_details["request_id"], - payment_mode=usage_details["payment_mode"] + payment_mode=usage_details["payment_mode"], ) self.usage_repo.create_item(usage_record) logger.info(f"added usage data for {usage_details}") def get_usage_details(self, user_name, org_id, service_id, group_id=None): - optin_time = self.usage_repo.get_optin_time( - user_name, org_id, service_id) + optin_time = self.usage_repo.get_optin_time(user_name, org_id, + service_id) + logger.info(f"opt in for the user{user_name}, \n" + f"org_id: {org_id}, service_id: {service_id} \n" + f"{optin_time.strftime('%Y-%m-%d %H:%M:%S')}") + free_calls = self.org_service_config_repo.get_service_config( org_id, service_id, optin_time) - total_calls = self.usage_repo.get_total_calls( - user_name, org_id, service_id) + + total_calls = self.usage_repo.get_total_calls(user_name, org_id, + service_id) + return total_calls, free_calls diff --git a/metering/utils.py b/metering/utils.py index 3f0ba4e..0a68d57 100644 --- a/metering/utils.py +++ b/metering/utils.py @@ -1,28 +1,30 @@ +import json import logging from constants import PAYMENT_MODE_FREECALL_VALUE -def make_response(status_code, body, header=None): - return { +def generate_lambda_response(status_code, message, headers=None): + response = { "statusCode": status_code, - "headers": header, - "body": body + "body": json.dumps(message), + "headers": { + "Content-Type": + "application/json", + "X-Requested-With": + "*", + "Access-Control-Allow-Headers": + "Access-Control-Allow-Origin, Content-Type,X-Amz-Date,Authorization," + "X-Api-Key,x-requested-with", + "Access-Control-Allow-Origin": + "*", + "Access-Control-Allow-Methods": + "GET,OPTIONS,POST", + }, } - - -def configure_log(logger): - logger.setLevel(logging.INFO) - - # create a file handler - handler = logging.StreamHandler() - handler.setLevel(logging.INFO) - # create a logging format - formatter = logging.Formatter( - '%(asctime)s - %(name)s - %(levelname)s - %(message)s') - handler.setFormatter(formatter) - # add the handlers to the logger - logger.addHandler(handler) + if headers is not None: + response["headers"].update(headers) + return response def validate_request(required_keys, request_body): @@ -37,25 +39,45 @@ def validator_usage(): def is_free_call(usage_details_dict): - if usage_details_dict['payment_mode'] == PAYMENT_MODE_FREECALL_VALUE: + if usage_details_dict["payment_mode"] == PAYMENT_MODE_FREECALL_VALUE: return True return False def usage_record_add_verify_fields(usage_detail_dict): new_required_keys = { - 'usage_type', 'status', 'usage_value', 'start_time', 'end_time', - 'created_at', 'payment_mode', 'group_id', 'registry_address_key', - 'ethereum_json_rpc_endpoint', 'response_time', 'response_code', 'error_message', - 'version', 'client_type', 'user_details', 'channel_id', 'operation', 'user_address', - 'username', 'org_id', 'service_id', 'resource', 'request_id' + "usage_type", + "status", + "usage_value", + "start_time", + "end_time", + "created_at", + "payment_mode", + "group_id", + "registry_address_key", + "ethereum_json_rpc_endpoint", + "response_time", + "response_code", + "error_message", + "version", + "client_type", + "user_details", + "channel_id", + "operation", + "user_address", + "username", + "org_id", + "service_id", + "resource", + "request_id", } for key in new_required_keys: if (key not in usage_detail_dict) or (usage_detail_dict[key] == ""): usage_detail_dict[key] = None - if usage_detail_dict['username'] is not None and usage_detail_dict['user_address'] is None: - usage_detail_dict['payment_mode'] = PAYMENT_MODE_FREECALL_VALUE + if (usage_detail_dict["username"] is not None + and usage_detail_dict["user_address"] is None): + usage_detail_dict["payment_mode"] = PAYMENT_MODE_FREECALL_VALUE else: - usage_detail_dict['payment_mode'] = 'paid' + usage_detail_dict["payment_mode"] = "paid" return usage_detail_dict