Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

This discovery details the code changes necessary

Table of Contents
minLevel1
maxLevel6
outlinefalse
typelist
printablefalse

This refinement details the necessary changes for the search by phone to work in admin tool , based on the solution n. 1 proposed documented in Search customer by phone in Admin App .

Solution 1

Create a phone lookup record in the commit order workflow. This will make sure that we’ll be able to find the customer searching by his phone as long as he has included it on the order, or registered in his account (verified or not).

Table of Contents
minLevel1
maxLevel6
outlinefalse
typelist
printablefalse

...

There are three possible scenarios:

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.

...

POC

The code below creates lookup records if they don’t exist when the order is committed.

intl-whitelabel-graphql/src/functions/graphql/resolvers/orders.ts

Code Block
languagetypescript
Mutation: {
   async commitOrder(...) {
   ...
    const savePhone = () => {
      const phoneNumber = delivery?.dropoff?.phoneNumber ?? contextUser.details.phoneNumber;
      const { cognitoId } = contextUser;
      if (phoneNumber && cognitoId) {
        providers.users.createPhoneRecord(cognitoId, phoneNumber);
      }
    };
 
    savePhone();
  }
}   

intl-packages/src/user-lookup-by-phone/user-lookup-by-phone.ts

Code Block
  class UserLookupByPhone {
    public async create({ cognitoId, phoneNumber }: { cognitoId: string; phoneNumber: string }) {
      const params = {
        ConditionExpression: INSERT_CONDITION_EXPRESSION,
        Item: {
          pk: `phone#${phoneNumber}`,
          pk2: `phone_user#${cognitoId}`,
          sk: 'v0_UserPhone',
          sk2: 'v0_UserPhoneLookUp',
        },
        TableName: this.tableName,
      };
  
      await this.executeWithRetry(DynamoMethods.put, params);
    }
    
    public async getByUserServiceLookup(
      phoneNumber: string,
    ): Promise<IUserLookupByPhone | undefined> {
      const dynamoSearch = {
        ExpressionAttributeValues: {
          ':pk': `phone#${phoneNumber}`,
          ':sk': 'v0_UserPhone',
        },
        KeyConditionExpression: 'pk = :pk AND begins_with(sk, :sk)',
        TableName: this.tableName,
      };
  
  
      const { Items } = await this.executeWithRetry(DynamoMethods.query, dynamoSearch);
      const Item = Items?.[0];
      return Item ? cast(Item, TUserLookupByPhone) : undefined;
    }
  }
  

  1. Commit an order with delivery.dropoff.phoneNumber +222222

    image-20240118-155822.pngImage Added
  2. Validate record in database

    image-20240118-160259.pngImage Added
  3. Searching phone in support tool

    image-20240118-160837.pngImage Added
  4. Customer details

    image-20240118-160929.pngImage Added

Validate SA market solution

In

Jira Legacy
serverSystem JIRA
serverId255417eb-03fa-3e2f-a6ba-05d325fec50d
keyIPN-31
Saudi Arabia team are implementing the possibility to sign up by phone, which has some overlaps with the changes needed for admin app.

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:

...

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.

  1. Searching by phone

    image-20240118-064400.png
  2. 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
languagetypescript
  public getByPhoneNumber(
    phoneNumber: string,
    searchByUserServiceLookup = false,
  ): Promise<IUserItem | undefined> {
    return searchByUserServiceLookup
      ? this.usersDynamo.getByUserServicePhoneLookup(phoneNumber)
      : this.usersDynamo.getByPhoneNumber(phoneNumber);
  }

...

Code Block
  public async getByUserServiceLookup(
    phoneNumber: string,
  ): Promise<IUserLookupByPhone | undefined> {
    const dynamoSearch = {
      ExpressionAttributeValues: {
        ':pk2': this.createUserServicePk(phoneNumber),
        ':sk2': this.createUserServiceSk(),
      },
      IndexName: 'brand-index-2',
      KeyConditionExpression: 'pk2 = :pk2 AND begins_with(sk2, :sk2)',
      TableName: this.tableName,
    };

    const { Items } = await this.executeWithRetry(DynamoMethods.query, dynamoSearch);
    const Item = Items?.[0];
    return Item ? cast(Item, 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.

...

Code Block
languagetypescript
  public async update(updatedUser: IUser): Promise<void> {
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    const { cognitoId, ...userItem } = updatedUser;

    let updateExpressions = DynamoDB.getUpdateExpressions(userItem);

    updateExpressions = {
      ...updateExpressions,
      ExpressionAttributeValues: {
        ...updateExpressions.ExpressionAttributeValues,
        [':phoneNumber']: updatedUser.details.phoneNumber,
      },
    };

    await this.dynamoClient
      .update({
        ...updateExpressions,
        ConditionExpression: '#details.phoneNumber = :phoneNumber',
        Key: { pk: updatedUser.cognitoId, sk: this.createSk() },
        TableName: this.tableName,
      })
      .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 Service API: Add a isPhoneNumberVerified field to lookupByPhoneNumber interface. This is important to avoid impacting https://rbictg.atlassian.net/wiki/spaces/EGMT/pages/4329078785/Solution+Phone+Number+Authentication+for+BK+SA#Customer-support-tool

  • 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

  1. Include the isVerified field in IUserLookupByPhone Interface: https://github.com/rbilabs/intl-user-service/blob/master/src/users/user-lookup-by-phone.repository.ts#L25-L30

...

  1. Create lookupByPhoneNumber record after creating the user in dynamo inside the pre-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#L135

  2. Update the lookupByPhoneNumber 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#L150

  3. Adapt the get user endpoint to have an phoneNumber query field, that searches by phone: https://github.com/rbilabs/intl-user-service/blob/master/src/users/users.controller.ts#L124

  4. Create 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

This changes are already implemented in this git branch: https://github.com/rbilabs/intl-packages/pull/961

intl-admin-app

  • Create a GraphQL query that uses the searchByPhone method exposed by intl-packages

  • Update the UI to display the search by phone number option

...