Table of Contents | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
...
Scenario | Condition | Outcome |
---|---|---|
Delivery order | Phone number is mandatory to close the order | Creates record using the order’s phone number |
Not delivery order but user has a phone added to his account | Phone number is not mandatory | Creates record using the user’s account phone number if it doesn’t exist already |
Not delivery order and user hasn’t added a phone to his account | Phone number is not mandatory | Doesn’t create a record |
Implementation Proposal 1
The sequence diagram below represents one possible implementation of the solution where the responsibility of managing lookup records stay in the intl-users packages.
...
Pros and cons
Pros
Uses existing lookup record structure
Search will work with both phone numbers included in the order or the user account
This solution will be compatible with the Phone OTP
Doesn’t require phone verification
Doesn’t require migration scripts, the search will work as long as the user has made one order after the feature release
Cons
A phone number can only be associated with a single customer, which will be the first one to inform the number on the platform
There isn’t a simple way to delete the link between a user and a phone number recorded in a delivery order. One possibility is allowing customer support to remove the link via the support tool if it is outdated.
Implementation Proposal
Search by phone
The search will be implemented in the users service API, the NPM user package (represented as intl-packages below) exposes the client that admin app will use to make this request.
...
Create phone lookup records
The creation of the lookup records can be either synchronous or asynchronous, the two diagrams below details the different approaches:
asynchronous: since the commit order operation is already slow, this option minimizes possibly making it slower. [PREFERRED]
...
synchronous: simpler implementation
...
POC
The code below creates lookup records if they don’t exist when the order is committed.
...
Commit an order with delivery.dropoff.phoneNumber +222222
Validate record in database
Searching phone in support tool
Customer details
Validate SA market solution
In
Jira Legacy | ||||||
---|---|---|---|---|---|---|
|
Result:
After confirming that SA market is deployed in eu-central-1 AWS Region, I could find user records created in Dynamo indexed by phone:
...
With this information, I was able to validate that the search could work in admin app:
Note |
---|
Since this is just a proof of concept, the example is not working perfectly. For example, the search bar is not showing the customer name and the phone in Customer Details page is wrong but this issues would be fixed during the development. |
Searching by phone
Result
...
Code changes
In this section are listed the changes made for this validation in intl-packages and intl-admin-app repositories.
intl-packages:
users.ts:
Code Block | ||
---|---|---|
| ||
public getByPhoneNumber(
phoneNumber: string,
searchByUserServiceLookup = false,
): Promise<IUserItem | undefined> {
return searchByUserServiceLookup
? this.usersDynamo.getByUserServicePhoneLookup(phoneNumber)
: this.usersDynamo.getByPhoneNumber(phoneNumber);
} |
users-dynamo.ts:
Code Block |
---|
public async getByUserServicePhoneLookup(phoneNumber: string): Promise<IUserItem | undefined> {
const lookupItem = await this.userLookupByPhone.getByUserServiceLookup(phoneNumber);
if (lookupItem) {
if (lookupItem) {
const cognitoId = lookupItem.pk;
const user = await this.getById(cognitoId);
return user;
}
}
return undefined;
} |
user-lookup-by-phone:
...
Solution 2 [Preferred]
Solution 2 uses the same strategy as solution 1, the only difference is that it proposes to update the lookup record structure so that it can link multiple phone numbers to multiple customers. Below is a suggestion of the new record:
Note |
---|
We’ll need to implement checks to prevent the creation of multiple lookup records linking the same phone number and customer |
Code Block |
---|
{
pk: `phone_user#${cognitoId}`,
pk2: `phone#${phoneNumber}`,
sk: `phone#${phoneNumber}`,
sk2: 'phone_user#${cognitoId}',
} |
With these changes, the search by phone will return a list of customers instead of a single one and avoid the problem of manual record deletion that solution 1 requires. However, the support tool front end will have to be adapted so that customer support can choose which one they want to check, given the list of customers associated with that phone number. The front end design will have to be aligned with UI/UX team.
An important point is that customer support can differentiate which customer is the actual owner of the phone number. Phone verification is not enabled in Iberia right now, but the feature is expected to be activated in the following months.
Pros and cons
Pros
Search will work with both phone numbers included in the order or in the user account
Doesn’t require phone verification
This solution will be compatible with the Phone OTP
Doesn’t require migration scripts, the search will work as long as the user has made one order after the feature release
Allows the association of multiple phone numbers with multiple customers
Cons
Require the development of a new page in the support tool front end to select which customer they want to check
Although very unlikely, there is a scenario where a single phone number is linked to many customers and this might make the process of finding the right one cumbersome
POC
SearchByPhone POC:
For this POC I changed the getByUserServiceLookup
to use the proposed structure:
intl-packages/src/user-lookup-by-phone/user-lookup-by-phone.ts
Code Block |
---|
public async getByUserServiceLookup( phoneNumber: string, ): Promise<IUserLookupByPhone[] | undefined> { const dynamoSearch = { ExpressionAttributeValues: { ':pk2': this.createUserServicePk(phoneNumber), `phone_lookup#${phoneNumber}`, ':sk2': this.createUserServiceSk()'createdAt#', }, IndexName: 'brand-index-2', KeyConditionExpression: 'pk2 = :pk2 AND begins_with(sk2, :sk2)', TableName: this.tableName, }; const { Items } = await this.executeWithRetry(DynamoMethods.query, dynamoSearch); return Items const? Item = Items?.[0]; return Item ?.map((item) => cast(Itemitem, TUserLookupByPhone)) : undefined; } |
intl-admin-app
The changes can be found in https://github.com/rbilabs/intl-admin-app/pull/242 .
Validate phone number updates
The user search, either by phone or emails, depends on the pk2 value of the user record in dynamo that necessarily has to have a phone number or an email, for example:
phone number:
...
email:
...
Therefore, we can only search a customer by phone number or email, but not both. Besides this issue, another problem is the update of the phone number. Today the change would only be reflected in user.details.phoneNumber
and not in pk2
(check code below), which leads to the search by phone only working with the first number registered.
...
} |
created 4 lookup records for 2 customers:
...
and was able to find the two customers when searching for the phone number +123123123:
...
Distinguish verified number POC:
To explicitly display which user is verified, we need to:
return phoneVerified in the Customer query from backend
check verified numbers and compare them with the searched text in the result.
intl-admin-app/src/components/layout/universal-search/index.tsx
Code Block | ||
---|---|---|
| ||
export const publicUniversalSearch async= update(updatedUser: IUser): Promise<void>=> { ... // eslint-disable-next-line @typescript-eslint/no-unused-vars return ( const<List> { cognitoId, ...userItem } = updatedUser; ... let updateExpressions = DynamoDB{searchResults.getUpdateExpressionsmap((userItemresult); => { updateExpressions = { const customer = result ...updateExpressions, as CustomerDetails; ExpressionAttributeValues: { const { phoneVerified, phoneNumber } = ...updateExpressions.ExpressionAttributeValues,customer; [':phoneNumber']: updatedUser.details.phoneNumber, },const isVerifiedUser = !!phoneVerified && searchTerm === phoneNumber; }; return ( await this.dynamoClient .update({ <SearchItem ...updateExpressions, item={result} ConditionExpression: '#details.phoneNumber = :phoneNumber', key={result.id} Key: { pk: updatedUser.cognitoId, sk: this.createSk() }, onClick={handleSearchResultSelection} TableName: this.tableName, isVerified={isVerifiedUser}) .promise(); } |
Deprecated
Necessary changes - summary
...
User Service API: Adapt GET user endpoint to also search by phone number
...
User Service API: Adapt the create
method in user service to create a lookupByPhoneNumber
record if a phone number is informed
...
User Service API: Adapt update-user
lambda to update or create a lookupByPhoneNumber
record if the phone number changed
...
User packages: Change getByPhoneNumber
to use User Service API instead of directly accessing the database
...
Admin App Backend: Create a GraphQL query to request customers by phone number
...
Admin App Frontend: Add the option to search customers by phone using the new query
...
DynamoDB: Develop a migration script to create lookupByPhoneNumber
for existing customers
Diagrams
Search customer by phone sequence diagram:
...
Necessary changes - details
intl-user-service
Include theisVerified
field inIUserLookupByPhone
Interface:https://github.com/rbilabs/intl-user-service/blob/master/src/users/user-lookup-by-phone.repository.ts#L25-L30
Code Block |
---|
export interface IUserLookupByPhone {
pk: string;
pk2: string;
sk: string;
sk2: string;
isVerified: boolean;
} |
CreatelookupByPhoneNumber
record after creating the user in dynamo inside thepre-signup
lambda, but only if a phone number is informed:https://github.com/rbilabs/intl-user-service/blob/master/src/cognito-lambdas/pre-signup.service.ts#L135Update thelookupByPhoneNumber
record if an user update is triggered and includes the phone number:https://github.com/rbilabs/intl-user-service/blob/master/src/users/update-comm-pref.service.ts#L150Adapt the get user endpoint to have anphoneNumber
query field, that searches by phone:https://github.com/rbilabs/intl-user-service/blob/master/src/users/users.controller.ts#L124Create a LaunchDarkly Flag and request it via a backend query (https://github.com/rbilabs/intl-admin-app/blob/master/src/remote/queries/launch-darkly.ts)
intl-packages
Create the option to searchlookupByPhone
records created byuser-service
using the endpoint. Currently,getByEmail
already works this way (https://github.com/rbilabs/intl-packages/blob/master/packages/users/src/users.ts#L359).
...
intl-admin-app
Create a GraphQL query that uses thesearchByPhone
method exposed by intl-packagesUpdate the UI to display the search by phone number option
...
/>
);
}
</List>
)
} |
The code alterations above, plus altering SearchItem to change the text if the phone is verified we get:
...
Task breakdown
[intl-user-service]
Refactor user-lookup-by-phone.repository to use both “UserPhone” and “UserPhoneLookUp” records
Since this impacts SA team, we need to ask their review as well
Update the lookup record structure to enable associating multiple phones and users
Develop create lookup records endpoint
Develop find by phone endpoint
[intl-packages]
Develop a method in intl-users that creates lookup records in via the new user endpoint
Update the searchByPhone method to use the new User endpoint
[intl-whitelabel-graphql]
Create lookup records in commitOrder
[intl-fulfillment-service]
Create lookup records in commitOrder
[intl-admin-app]
Update customer query to also search by phone
Include search by phone in search bar
include search by phone in use-universal-search hook
add logic to explicitly show verified numbers