import logging
from constants import DATADOG_CREATE_SHELL_AC_APP_NAME, OCEAN_SSM_KEY, DD_CHECK_EVENT_CREATE_SHELL_AC, \
NHS_DISCOUNT_CONFIG, COLLEAGUE_DISCOUNT_CONFIG
from pymorri.api.morrisons.customer import Customer
from pymorri.util.application_dependencies import ApplicationDependencies
from pymorri.api.osp.customer import OSPGetCustomer, OSPCustomer
from pymorri.util import ResponseBuilder
from pymorri.util.aws_utils import publish_event_to_sns, get_event_from_sns
from pymorri.util.serverless_utils import get_or_exception
from pymorri.util.datadog_utils import post_datadog_event_to_sns
from pymorri.exceptions import MoriException
from pymorri.util.general_utils import build_response_or_throw
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
deps = None
def _prepare_deps():
global deps
if not deps:
deps = ApplicationDependencies(
config_file=COLLEAGUE_DISCOUNT_CONFIG,
ssm_param_store_name=OCEAN_SSM_KEY,
)
[docs]def prepare_osp_payload(customer_id, customer_detail):
"""
This function receives customer_id and customer details from Morrisons Customer v2 API
and prepares a payload to be sent to OSP to create OSP customer
Args:
customer_id (str) - required: customerId
customer_detail (dict) - required: customer details
Example:
.. code-block:: python
"customerId": "343434",
"customer_detail": {
'sourceSystemCustomerId': '039490436575890469032',
'salutation': 'Mrs',
'firstName': 'Lindsey',
'lastName': 'Bradford',
'email': 'lindsey.bradford@morrisonsplc.co.uk',
'birthDate': 'string',
'gender': 'string',
'phoneNumber': '+7777123456',
'mobilePhone': 'string',
'address1': 'string',
'address2': 'string',
'address3': 'string',
'postcode': None,
'city': 'string',
'country': 'string',
'county': 'string',
'billingAddress1': 'string',
'billingAddress2': 'string',
'billingAddress3': 'string',
'billingPostcode': 'string',
'billingCity': 'string',
'billingCountry': 'string',
'billingCounty': 'string',
'marketingOptIn': 'string',
'thirdPartyOptIn': 'string',
'onlineAccountStatus': 'string',
'householdSize': 'string',
'dietaryPreference': 'string',
'voucherChannelPreference': 'string',
'saveVouchers': 'string',
'emailOptIn': 'string',
'mailOptIn': 'string',
'phoneOptIn': 'string',
'smsOptIn': 'string',
'researchOptIn': 'string'
}
Returns:
osp_customer_payload (dict):
Example:
.. code-block:: python
osp_customer_payload = {
"details": {
"attributes": [
{ "name": "firstName", "value": "John" },
{ "name": "lastName", "value": "Doe" },
{ "name": "title", "value": "Mr" },
],
"emailAddress": "arif@examole.com",
"fullName": f"{first_name} {last_name}",
"preferredLanguage": "en-gb",
"preferredName": f"{first_name} {last_name}"
},
"retailerBannerIds": [ "morrisons" ],
"retailerCustomerId": customer_id
}
"""
first_name = get_or_exception(customer_detail, "firstName")
last_name = get_or_exception(customer_detail, "lastName")
email_address = get_or_exception(customer_detail, "email")
preferred_name = f"{first_name} {last_name}"
attributes_1 = { "name": "firstName", "value": first_name}
attributes_2 = { "name": "lastName", "value": last_name}
attributes_list = list()
attributes_list.append(attributes_1)
attributes_list.append(attributes_2)
salutation = customer_detail.get("salutation")
if salutation:
attributes_list.append({ "name": "title", "value": salutation})
osp_customer_payload = {
"details": {
"attributes": attributes_list,
"emailAddress": email_address,
"fullName": preferred_name,
"preferredLanguage": "en-gb",
"preferredName": preferred_name
},
"retailerBannerIds": ["morrisons"],
"retailerCustomerId": customer_id
}
return osp_customer_payload
[docs]def post_sns_event_for_add_customer_to_group(customer_id, group_id, club_id, club_name, bq_date):
"""
This function will post an event onto sns-euw1-<ENV>>-digital-storepick-events Topic
with message attribute as addCustomerToGroup
Args:
customer_id (str) - required: customerId
group_id (str) - required: group_id to add customer to
club_id (str) - required: club_id to add customer to
club_name (str) - required: club_name to add customer to
Example:
.. code-block:: python
"customerId": "343434"
Returns:
True (bool): on successful posting of event onto sns-euw1-<ENV>>-digital-storepick-events Topic
"""
sns_event = {
"config_file": NHS_DISCOUNT_CONFIG,
"eventType": "addCustomerToGroup",
"eventData": {
"customerId": customer_id,
"groupId": group_id
},
"bq_date": bq_date,
"club_id": club_id,
"club_name": club_name,
}
event_type = "addCustomerToGroup"
publish_event_to_sns(message=sns_event, message_attribute_value=event_type)
return True
[docs]@get_event_from_sns
@build_response_or_throw
def lambda_handler(event, context):
"""
Lambda receives customerId. Then confirms if it is a Morrisons customer. Then proceed to create an OSP account
for the customer. Before doing so it checks whether the customer already exist in OSP. If yes throws error or if no
creates the same
Args:
event (dict) - required: customerId
Example:
.. code-block:: python
event = {
"customerId": "343434",
}
event = {
"Records": [
{
"EventVersion": "1.0",
"EventSubscriptionArn": "arn:aws:sns:us-east-2:123456789012:sns-lambda:21be56ed-a058-49f5-8c98-aedd2564c486",
"EventSource": "aws:sns",
"Sns": {
"SignatureVersion": "1",
"Timestamp": "2019-01-02T12:45:07.000Z",
"Signature": "tcc6faL2yUC6dgZdmrwh1Y4cGa/ebXEkAi6RibDsvpi+tE/1+82j...65r==",
"SigningCertUrl": "https://sns.us-east-2.amazonaws.com/SimpleNotificationService-ac565b8b1a6c5d002d285f9598aa1d9b.pem",
"MessageId": "95df01b4-ee98-5cb9-9903-4c221d41eb5e",
"Message": "{ \"eventType\": \"addCustomerToOcean\", \"eventData\": { \"customerId\": \"1234567\", \"groupId\": \"1111-2222\" }, \"config_file\": \"storepick.json\", \"bq_date\": \"2020-11-03T16:58:53.265025\", \"club_id\": \"6\", \"club_name\": \"Colleague\" }",
"MessageAttributes": {
"eventType": {
"Type": "String",
"Value": "logRowToBq"
}
},
"Type": "Notification",
"UnsubscribeUrl": "https://sns.us-east-2.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-east-2:123456789012:test-lambda:21be56ed-a058-49f5-8c98-aedd2564c486",
"TopicArn": "arn:aws:sns:us-east-2:123456789012:sns-lambda",
"Subject": "TestInvoke"
}
}
]
}
Returns:
event (dict): response from the lambda
Example:
.. code-block:: python
event = {
'status': 200,
'message': 'Shell account creation completed successfully',
'results': {},
'headers': {'content-type': 'application/json'}
}
.. code-block:: python
event = {
'status': 404,
'message': 'HTTP 404 - Morrisons customer not found for id 1014860340',
'results': {},
'headers': {'content-type': 'application/json'}
}
.. code-block:: python
event = {
'status': 500,
'message': 'Error getting Morrisons customer record for id 1014860340',
'results': {},
'headers': {'content-type': 'application/json'}
}
.. code-block:: python
event = {
'status': 500,
'message': 'Customer id 1014860340 already exist in OSP',
'results': {},
'headers': {'content-type': 'application/json'}
}
Raises:
MoriException: MoriException thrown when customerId not found in Morrisons service
MoriException: MoriException thrown when customerId trying to find customer in OSP
MoriException: MoriException thrown when customerId already exist in OSP
MoriException: MoriException thrown when customerId trying to create customer in OSP
"""
################################################################################
# need to populate the global deps (which is cached deliberately outside this
# main handler
################################################################################
_prepare_deps()
global deps
# update event in order this function could send STATUS data to DataDog API
# from build_response_or_throw
event.update({
"DATADOG_APP_NAME": DATADOG_CREATE_SHELL_AC_APP_NAME,
"DD_CHECK_METRIC_NAME": DD_CHECK_EVENT_CREATE_SHELL_AC,
})
event_data = get_or_exception(event, "eventData")
customer_id = get_or_exception(event_data, "customerId")
#below code is processed only for status_code == 404
customer = Customer(configuration=deps.get_config(), ssm=deps.get_ssm_params())
customer_response = customer.request(customer_id)
is_osp_customer_exist = True
try:
osp_customer = OSPGetCustomer(configuration=deps.get_config(), ssm=deps.get_ssm_params())
_ = osp_customer.request(customer_id)
except MoriException as exc:
if exc.http_status_code == 404:
is_osp_customer_exist = False
else:
raise exc
if is_osp_customer_exist:
logger.warning(f"not creating shell account in OSP for customer id: {customer_id} - customer already exists")
post_datadog_event_to_sns(application_name=DATADOG_CREATE_SHELL_AC_APP_NAME, metric_type="count",
metric_name="ocean_shell_account_existing_ospcustomer", metric_value="1")
else:
osp_customer_payload = prepare_osp_payload(customer_id, customer_response.get("payload"))
try:
#creating customer in OSP
osp_customer = OSPCustomer(configuration=deps.get_config(), ssm=deps.get_ssm_params())
osp_customer.request(payload=osp_customer_payload)
except MoriException as exc:
post_datadog_event_to_sns(application_name=DATADOG_CREATE_SHELL_AC_APP_NAME, metric_type="count",
metric_name="ocean_shell_account_create_fail", metric_value="1")
raise exc
post_datadog_event_to_sns(application_name=DATADOG_CREATE_SHELL_AC_APP_NAME, metric_type="count",
metric_name="ocean_shell_account_create_success", metric_value="1")
logger.info(f"successfully created shell account for customer id... {customer_id}")
group_id = get_or_exception(event_data, "groupId")
club_id = get_or_exception(event, "club_id")
club_name = get_or_exception(event, "club_name")
bq_date = get_or_exception(event, "bq_date")
post_sns_event_for_add_customer_to_group(customer_id, group_id, club_id, club_name, bq_date)
event['response_message'] = "Shell account creation completed successfully"
return event
if __name__ == "__main__":
#customerId for various scenarios
#happy_ scenario => "customerId": "1023421151"
#mori cust not found => "customerId": "123456789"
#customer exist in OSP => "customerId": "1025974228"
# this is the happy_ scenario
event = {
"Records": [
{
"Sns": {
"Message": "{ \"eventType\": \"addCustomerToOcean\", \"eventData\": { \"customerId\": \"1023421151\", \"groupId\": \"1234-1234\" }, \"config_file\": \"storepick.json\", \"bq_date\": \"2020-11-03T16:58:53.265025\", \"club_id\": \"6\", \"club_name\": \"Colleague\" }",
}
}
]
}
lambda_handler(event, None)