amazon-connect / amazon-connect-customer-profiles

Apache License 2.0
9 stars 3 forks source link

Fail to fetch customer profile #10

Open vtrofin opened 11 months ago

vtrofin commented 11 months ago

Hi team,

I'm facing an interesting edge case while trying to retrieve Amazon Connect customer profiles in a React App. This only happens if I log-out and then log back in into my app. Worth mentioning that we're using Cognito as IDP service for Amazon Connect as described in the video call escalation repo.

We're logging out as instructed in the amazon connect docs.

 <button
          className={styles.logoutButton}
          onClick={() => {
            // First signout of Amazon Connect
            fetch(connectLogoutUrl, {
              credentials: "include",
              mode: "no-cors",
            })
              .then(() => {
                const eventBus = connect.core.getEventBus()
                eventBus.trigger(connect.EventType.TERMINATE)
              })
              .then(() => cognitoSignOut())
              .then(() => clearConnectTimestamp())
              .catch((err) => logError(err))
          }}
        >
        Log out
        </button>

It looks like there is an fac error around the time of my request to the customer profile API but the connect streams apparently treats this error as a normal occurrence.

This is how I instantiate the customer profile client:

const customerProfileInstance = `https://<connectInstanceAlias>.my.connect.aws`

useEffect(() => {
    const iframeContainer = ref.current
    setIsCustomerProfileLoading(true)

    const log = connect.getLog()
    log.setEchoLevel(connect.LogLevel.WARN)
    log.setLogLevel(connect.LogLevel.WARN)

    connect.core.initCCP(iframeContainer as HTMLElement, initCCPOptions)

    const customerProfileClient = new connect.CustomerProfilesClient(
      customerProfileInstance,
    )
    // store the customer profile client in state (jotai atom) to be reused across the app
    setCustomerProfileClient(customerProfileClient)

   // agent and contact event handlers below
  // ...
  }, [<dependencies>])

This is the hook that I run to fetch the customer profile:

useEffect(() => {
    if (!customerProfileId) {
      return
    }

    const throwErrorOnDelay = new Promise((_resolve, reject) => {
      setTimeout(
        reject,
        10 * 1000,
        "Delayed response from customer profile API",
      )
    })

    const storeCustomerProfile = async (customerProfileId: string) => {
      try {
        console.log("1. fetching profiles --->")

        const profiles = await Promise.race([
          customerProfileClient.searchProfiles({
            DomainName: config.customerProfilesDomain,
            KeyName: "_profileId",
            Values: [customerProfileId],
            AdditionalSearchKeys: phoneNumber
              ? [
                  {
                    KeyName: "_phone",
                    Values: [phoneNumber],
                  },
                ]
              : undefined,
            LogicalOperator: "OR",
          }),
          throwErrorOnDelay,
        ])
        console.log("2. fetched profiles --->", profiles)

        const {
          data: { Items },
        } = profiles

       // store first profile in the array in state
      } catch (err) {
        console.log("3. error --->", err)
      }
    }

    storeCustomerProfile(customerProfileId)
  }, [
    customerProfileId,
    phoneNumber,
    customerProfileClient,
  ])

I've included a screencast of the exact edge case i'm facing. You can consult it here https://www.loom.com/share/0d236d17144e47e5b1cbb54824bac15f?sid=c58f4c50-76d5-4448-a9d0-7280c399efd0

Below i'll paste the export of my console window. As you can see below the Promise.race ends with throwing an error 10 seconds after not receiving a response for the customer profile search request. Any ideas on how I could fix or mitigate this edge case? As mentioned, I only noticed it after logging out from Amazon connect and then logging back into Amazon Connect.

1. fetching profiles --->
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:40.332Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: [Audio Device Settings] ringtoneType voice successfully set to deviceid default
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:40.332Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: [Audio Device Settings] ringtoneType chat successfully set to deviceid default
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:40.332Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: [Audio Device Settings] ringtoneType task successfully set to deviceid default
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:40.332Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: [Audio Device Settings] ringtoneType queue_callback successfully set to deviceid default
main.ac3aab30.chunk.js:1 

       POST https://development-tokyo-call-center-innovation.my.connect.aws/ccp-v2/fac 400 (Bad Request)
isFeatureEnabled @ main.ac3aab30.chunk.js:1
w @ main.ac3aab30.chunk.js:1
handleAgentInit @ main.ac3aab30.chunk.js:1
(anonymous) @ connect-streams-min.e0f9cd8f.js:1
v.trigger @ connect-streams-min.e0f9cd8f.js:1
L.updateAgentData @ connect-streams-min.e0f9cd8f.js:1
(anonymous) @ connect-streams-min.e0f9cd8f.js:1
(anonymous) @ connect-streams-min.e0f9cd8f.js:1
v.trigger @ connect-streams-min.e0f9cd8f.js:1
(anonymous) @ connect-streams-min.e0f9cd8f.js:1
(anonymous) @ connect-streams-min.e0f9cd8f.js:1
v.trigger @ connect-streams-min.e0f9cd8f.js:1
c._dispatchEvent @ connect-streams-min.e0f9cd8f.js:1
(anonymous) @ connect-streams-min.e0f9cd8f.js:1
main.ac3aab30.chunk.js:1 

       POST https://development-tokyo-call-center-innovation.my.connect.aws/ccp-v2/fac 400 (Bad Request)
isFeatureEnabled @ main.ac3aab30.chunk.js:1
w @ main.ac3aab30.chunk.js:1
handleAgentInit @ main.ac3aab30.chunk.js:1
(anonymous) @ connect-streams-min.e0f9cd8f.js:1
v.trigger @ connect-streams-min.e0f9cd8f.js:1
L.updateAgentData @ connect-streams-min.e0f9cd8f.js:1
(anonymous) @ connect-streams-min.e0f9cd8f.js:1
(anonymous) @ connect-streams-min.e0f9cd8f.js:1
v.trigger @ connect-streams-min.e0f9cd8f.js:1
(anonymous) @ connect-streams-min.e0f9cd8f.js:1
(anonymous) @ connect-streams-min.e0f9cd8f.js:1
v.trigger @ connect-streams-min.e0f9cd8f.js:1
c._dispatchEvent @ connect-streams-min.e0f9cd8f.js:1
(anonymous) @ connect-streams-min.e0f9cd8f.js:1
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:40.411Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: getDomainId: no domainId found
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:40.412Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: voiceId domainId not fetched at agent initialization
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:40.433Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: [Device Settings] Call updateDeviceList with getUserMedia
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:40.435Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: Ringtone is ready to play: 
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:40.442Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: received agentId from CCP is c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:40.442Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: c8908bf7-17d8-4e48-95c4-611b729940e9 is NOT ScreenRecording Enabled. Ignoring...
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:40.464Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: clientApp is detected ENABLED. Now trying Connecting to ClientApp...
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:40.464Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: Agent state has changed from oldState init to newState available
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:40.466Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: After INIT, agent has ["c8908bf7-17d8-4e48-95c4-611b729940e9"] in Snapshot, and [] contacts in IDB
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:40.466Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: No contact found to end after init
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:40.482Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: Ringtone is ready to play: 
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:40.483Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: Ringtone is ready to play: 
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:40.485Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: Ringtone is ready to play: 
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:40.568Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: [Device Settings] Call updateDeviceList with getUserMedia
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:40.656Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: [Audio Device Settings] New Audio Input DeviceList is updated to [{"deviceId":"default","kind":"audioinput","label":"Default - MacBook Pro Microphone (Built-in)","groupId":"c76f3d08f0b92091b5c7e9d875c5c67ee38ae5d68b628972c043f34054ceb932"},{"deviceId":"048e85be582177ee28ee60309df262ba3252e5360d33d6b24dcd55c21fa4006b","kind":"audioinput","label":"MacBook Pro Microphone (Built-in)","groupId":"c76f3d08f0b92091b5c7e9d875c5c67ee38ae5d68b628972c043f34054ceb932"}]
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:40.656Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: [Audio Device Settings] New Audio Output DeviceList is updated to [{"deviceId":"default","kind":"audiooutput","label":"Default - MacBook Pro Speakers (Built-in)","groupId":"c76f3d08f0b92091b5c7e9d875c5c67ee38ae5d68b628972c043f34054ceb932"},{"deviceId":"babd4aff814220c54c3b86002ffac0113d9a9c53179bfb41744ab8d45d6dd9d5","kind":"audiooutput","label":"MacBook Pro Speakers (Built-in)","groupId":"c76f3d08f0b92091b5c7e9d875c5c67ee38ae5d68b628972c043f34054ceb932"}]
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:40.656Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: [Audio Device Settings] CCPUI calls Streams API setSpeakerDevice with default
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:40.656Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: [Audio Device Settings] CCPUI calls Streams API setRingerDevice with default
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:40.657Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: [Audio Device Settings] Attempting to set speaker device default
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:40.657Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: [Audio Device Settings] Attempting to set ringer device default
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:40.658Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: [Audio Device Settings] ringtoneType voice successfully set to deviceid default
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:40.658Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: [Audio Device Settings] ringtoneType chat successfully set to deviceid default
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:40.658Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: [Audio Device Settings] ringtoneType task successfully set to deviceid default
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:40.658Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: [Audio Device Settings] ringtoneType queue_callback successfully set to deviceid default
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:40.658Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: [Audio Device Settings] Speaker device default successfully set to speaker audio element
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:39.688Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: Creating a new Websocket connection for CCP
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:39.689Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: SharedWorker initializing with snapshot delay time of 0ms
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:39.690Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: ADVANCED_LOG AMZ_WEB_SOCKET_MANAGER::Initializing Websocket Manager Failure callback registered 
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:39.690Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: ADVANCED_LOG AMZ_WEB_SOCKET_MANAGER::Websocket connection open callback registered 
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:39.690Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: ADVANCED_LOG AMZ_WEB_SOCKET_MANAGER::Websocket connection close callback registered 
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:39.690Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: ADVANCED_LOG AMZ_WEB_SOCKET_MANAGER::Websocket connection gain callback registered 
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:39.690Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: ADVANCED_LOG AMZ_WEB_SOCKET_MANAGER::Websocket connection lost callback registered 
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:39.690Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: ADVANCED_LOG AMZ_WEB_SOCKET_MANAGER::Websocket subscription failure callback registered 
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:39.691Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: ADVANCED_LOG AMZ_WEB_SOCKET_MANAGER::Deep Heartbeat is successful. WebSocketManager has received 200 response from aws/ping 
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:39.691Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: ADVANCED_LOG AMZ_WEB_SOCKET_MANAGER::Deep Heartbeat failed. WebSocketManager does not receive 200 response from aws/ping 
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:39.691Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: ADVANCED_LOG AMZ_WEB_SOCKET_MANAGER::Generic topic failed. 
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:39.691Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: ADVANCED_LOG AMZ_WEB_SOCKET_MANAGER::Reset Websocket state 
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:39.691Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: ADVANCED_LOG AMZ_WEB_SOCKET_MANAGER::Fetching new WebSocket connection configuration 
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:39.874Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: getWebSocketUrl succeeded
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:39.874Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: ADVANCED_LOG AMZ_WEB_SOCKET_MANAGER::Successfully fetched webSocket connection configuration 
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:39.875Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: ADVANCED_LOG AMZ_WEB_SOCKET_MANAGER::Initializing Websocket Manager 
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:39.875Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: Kicking off agent polling
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:39.876Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: Kicking off config polling
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:39.876Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: Kicking off auth token polling
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:40.048Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: ADVANCED_LOG AMZ_WEB_SOCKET_MANAGER::WebSocket connection established! 
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:40.048Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: ADVANCED_LOG AMZ_WEB_SOCKET_MANAGER::Reset Websocket state 
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:39.688Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: Creating a new Websocket connection for CCP
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:39.689Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: SharedWorker initializing with snapshot delay time of 0ms
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:39.690Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: ADVANCED_LOG AMZ_WEB_SOCKET_MANAGER::Initializing Websocket Manager Failure callback registered 
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:39.690Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: ADVANCED_LOG AMZ_WEB_SOCKET_MANAGER::Websocket connection open callback registered 
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:39.690Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: ADVANCED_LOG AMZ_WEB_SOCKET_MANAGER::Websocket connection close callback registered 
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:39.690Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: ADVANCED_LOG AMZ_WEB_SOCKET_MANAGER::Websocket connection gain callback registered 
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:39.690Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: ADVANCED_LOG AMZ_WEB_SOCKET_MANAGER::Websocket connection lost callback registered 
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:39.690Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: ADVANCED_LOG AMZ_WEB_SOCKET_MANAGER::Websocket subscription failure callback registered 
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:39.691Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: ADVANCED_LOG AMZ_WEB_SOCKET_MANAGER::Deep Heartbeat is successful. WebSocketManager has received 200 response from aws/ping 
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:39.691Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: ADVANCED_LOG AMZ_WEB_SOCKET_MANAGER::Deep Heartbeat failed. WebSocketManager does not receive 200 response from aws/ping 
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:39.691Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: ADVANCED_LOG AMZ_WEB_SOCKET_MANAGER::Generic topic failed. 
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:39.691Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: ADVANCED_LOG AMZ_WEB_SOCKET_MANAGER::Reset Websocket state 
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:39.691Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: ADVANCED_LOG AMZ_WEB_SOCKET_MANAGER::Fetching new WebSocket connection configuration 
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:39.874Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: getWebSocketUrl succeeded
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:39.874Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: ADVANCED_LOG AMZ_WEB_SOCKET_MANAGER::Successfully fetched webSocket connection configuration 
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:39.875Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: ADVANCED_LOG AMZ_WEB_SOCKET_MANAGER::Initializing Websocket Manager 
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:39.875Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: Kicking off agent polling
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:39.876Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: Kicking off config polling
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:39.876Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: Kicking off auth token polling
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:40.048Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: ADVANCED_LOG AMZ_WEB_SOCKET_MANAGER::WebSocket connection established! 
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:40.048Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: ADVANCED_LOG AMZ_WEB_SOCKET_MANAGER::Reset Websocket state 
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:40.806Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: [Audio Device Settings] New Audio Input DeviceList is updated to [{"deviceId":"default","kind":"audioinput","label":"Default - MacBook Pro Microphone (Built-in)","groupId":"c76f3d08f0b92091b5c7e9d875c5c67ee38ae5d68b628972c043f34054ceb932"},{"deviceId":"048e85be582177ee28ee60309df262ba3252e5360d33d6b24dcd55c21fa4006b","kind":"audioinput","label":"MacBook Pro Microphone (Built-in)","groupId":"c76f3d08f0b92091b5c7e9d875c5c67ee38ae5d68b628972c043f34054ceb932"}]
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:40.807Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: [Audio Device Settings] New Audio Output DeviceList is updated to [{"deviceId":"default","kind":"audiooutput","label":"Default - MacBook Pro Speakers (Built-in)","groupId":"c76f3d08f0b92091b5c7e9d875c5c67ee38ae5d68b628972c043f34054ceb932"},{"deviceId":"babd4aff814220c54c3b86002ffac0113d9a9c53179bfb41744ab8d45d6dd9d5","kind":"audiooutput","label":"MacBook Pro Speakers (Built-in)","groupId":"c76f3d08f0b92091b5c7e9d875c5c67ee38ae5d68b628972c043f34054ceb932"}]
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:40.807Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: [Audio Device Settings] CCPUI calls Streams API setSpeakerDevice with default
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:40.807Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: [Audio Device Settings] CCPUI calls Streams API setRingerDevice with default
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:40.807Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: [Audio Device Settings] Attempting to set speaker device default
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:40.808Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: [Audio Device Settings] Speaker device default successfully set to speaker audio element
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:40.808Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: [Audio Device Settings] Attempting to set ringer device default
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:40.808Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: [Audio Device Settings] ringtoneType voice successfully set to deviceid default
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:40.808Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: [Audio Device Settings] ringtoneType chat successfully set to deviceid default
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:40.808Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: [Audio Device Settings] ringtoneType task successfully set to deviceid default
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:09:40.808Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]: [Audio Device Settings] ringtoneType queue_callback successfully set to deviceid default
hook.ts:208 3. error ---> Delayed response from customer profile API
error.ts:20 Error: "Delayed response from customer profile API"
    at toError (error.ts:73:12)
    at logError (error.ts:19:23)
    at storeCustomerProfile (hook.ts:211:9)
overrideMethod @ console.js:213
logError @ error.ts:20
storeCustomerProfile @ hook.ts:211
await in storeCustomerProfile (async)
(anonymous) @ hook.ts:215
commitHookEffectListMount @ react-dom.development.js:23150
commitPassiveMountOnFiber @ react-dom.development.js:24926
commitPassiveMountEffects_complete @ react-dom.development.js:24891
commitPassiveMountEffects_begin @ react-dom.development.js:24878
commitPassiveMountEffects @ react-dom.development.js:24866
flushPassiveEffectsImpl @ react-dom.development.js:27039
flushPassiveEffects @ react-dom.development.js:26984
(anonymous) @ react-dom.development.js:26769
workLoop @ scheduler.development.js:266
flushWork @ scheduler.development.js:239
performWorkUntilDeadline @ scheduler.development.js:533
Show 12 more frames
Show less
connect-streams-min.e0f9cd8f.js:1 [2023-11-22T07:10:06.937Z] [INFO] [c89a1e17-4aa6-4a1f-b731-a4a4bdbbbd69]:  Sending check media stream event, is local media stream active: false
greysonevins commented 11 months ago

Hey @vtrofin could you send HAR file or some network information to help us debug this one. I'll try to replicate this issue locally on codesandbox.

If you get rid of the the race does the API ever return?

My one concern with the implementation is that you are mounting the connect client inside a hook and setting to a useState variable and that it is not fully mounted when the API is called.

You may want to immediately invoke the client setup and store it in the window or something. Trying to mount it in a useEffect may run into some issues.

greysonevins commented 11 months ago

@vtrofin we worked on an example to help mitigate this here

For now, by using the onAgent callback, you can re-initialize the CustomerProfilesClient on login which should prevent this issue.

I will add a feature request to the amazon-connect-streams library to re-initailize the Customer Profiles ifame on log out and log in.

vtrofin commented 11 months ago

@greysonevins thank you very much for your response and for taking the time to investigate this. I'll point a few of the implementation decisions below, but it's probably that the client is not properly initialized in my implementation. I'll re-check the client initialization in my codebase.

  1. Initializing the connect ccp client + customer profile client: Our app has both private routes and public routes where the login page is obviously a public route. I decided to initialize the ccp client in the private routes, after I retrieve the amazon connect credentials. As mentioned before, we have an auth flow based on the video-call-escalation example, where we use AWS Cognito as IDP and then use the Cognito access token to retrieve the connect credentials.

But the difference is that only after retrieving the Connect credentials I initialize the ccp client and the customer profile client, in the private route. The reason for this choice was that at the time I was unsure whether the ccp client can be properly initiated and refreshed without having the credentials first.

I'll provide a HAR file to the support team. I'm also wondering whether it's better to just instantiate the ccp client once for the entire app and let it manage the refresh once credentials are available.

  1. The API never returns if i remove the race and I instantiate the customer profile like this

    useEffect(() => {
    
    connect.core.initCCP(iframeContainer as HTMLElement, initCCPOptions)
    
    const customerProfileClient = new connect.CustomerProfilesClient(
    customerProfileInstance,
    )
    
    }, [<dependencies>])

However, if I try using the onInitialized event on the ccp client to instantiate the customer profile client, then I receive a meaningful error about the client not being ready when fetching the customer profile.

useEffect(() => {
  connect.core.initCCP(iframeContainer as HTMLElement, initCCPOptions)

  connect.core.onInitialized(() => {

    // attempt to initiate the customer profile client
    const customerProfileClient = new connect.CustomerProfilesClient(
      customerProfileInstance,
    )
  })

}, [<dependencies>])

The error I receive when trying to fetch the customer profile is below


error.ts:20 Error: {"status":500,"statusText":"Client not ready","data":{}}
    at toError (error.ts:73:12)
    at logError (error.ts:19:23)
    at storeCustomerProfile (hook.ts:212:9)
overrideMethod @ console.js:213
logError @ error.ts:20
storeCustomerProfile @ hook.ts:212
await in storeCustomerProfile (async)
(anonymous) @ hook.ts:222
commitHookEffectListMount @ react-dom.development.js:23150
commitPassiveMountOnFiber @ react-dom.development.js:24926
commitPassiveMountEffects_complete @ react-dom.development.js:24891
commitPassiveMountEffects_begin @ react-dom.development.js:24878
commitPassiveMountEffects @ react-dom.development.js:24866
flushPassiveEffectsImpl @ react-dom.development.js:27039
flushPassiveEffects @ react-dom.development.js:26984
(anonymous) @ react-dom.development.js:26769
workLoop @ scheduler.development.js:266
flushWork @ scheduler.development.js:239
performWorkUntilDeadline @ scheduler.development.js:533
Show 12 more frames
Show less

Again, thank you very much for investigating this issue. I'm looking at your example on how to mitigate the issue and will get back with a reply.

vtrofin commented 11 months ago

@greysonevins thank you for providing me with the example code.

For now, by using the onAgent callback, you can re-initialize the CustomerProfilesClient on login which should prevent this issue.

If I use your approach of searching customer profiles with a click event there is no problem, i get a proper response from the API even when I set the customer profile in state. But in my codebase i must fetch the customer profile with an useEffect hook, every time a new contact is pushed to the agent. This is the main reason i'm getting this error when i log-out and log-in again. And it only happens for that first contact, because the customer profile client is not yet ready.

Is there any way of knowing when the customer profile client is ready so I only attempt to query after that? I guess as an alternative I could make a loop and retry when I get this error.

Error: {"status":500,"statusText":"Client not ready","data":{}}
bml1g12 commented 11 months ago

I'll provide a HAR file to the support team.

I have just shared this on the associated AWS support case on @vtrofin's behalf

vtrofin commented 11 months ago

In the end I've made my own check for the client to be ready and only make a query once it is ready. If it helps anyone, here's my approach with React:

Initialize the customer profile client only after the ccp iframe is initialized

useEffect(() => {

  connect.core.initCCP(iframeContainer as HTMLElement, initCCPOptions)

  connect.core.onInitialized(() => {

    // Check whether client is ready and if not, initiate it
    isClientReady()
      .then((isReady) => {
        if (!isReady) {
          initiateCustomerProfileClient()
        }
      })
      .catch((err) => {

      })
  })

}, [<dependencies>])

Set up the customer profiles client in Context. Below is the hook called in my Context Provider which retrieves the client and the helper methods to query the client

const useCustomerProfileClientHook = () => {

  const customerProfileInstance = `https://<connect-instance-alias>.my.connect.aws`
  const [client, setClient] = useState<unknown>(null)
  const isClientReadyRef = useRef<boolean>(false)

  const initiateClient = (): void => {
    const customerProfileClient = new connect.CustomerProfilesClient(
      customerProfileInstance,
    )

    setClient(() => customerProfileClient)
  }

  const sleep = (ms: number): Promise<void> =>
    new Promise((resolve) => setTimeout(resolve, ms))

  const throwErrorOnDelay = (delay: number) =>
    new Promise((_, reject) => {
      setTimeout(reject, delay, "delayed-api-response")
    })

  const isClientReady = async (): Promise<boolean> => {
    if (client === null) {
      return false
    }

    if (isClientReadyRef.current) {
      return true
    }

    try {
      const queryOptions = {
        DomainName: <customer-profiles-domain>,
        KeyName: "_phone",
        Values: ["1111"],
      }

      const response = await Promise.race([
        client.searchProfiles(queryOptions),
        throwErrorOnDelay(5 * 1000),
      ])

      if (response?.status === 200) {
        isClientReadyRef.current = true
        return true
      }

      return false
    } catch (err) {
      return false
    }
  }

  const searchProfiles = async (options: OptionsType): Promise<unknown> => {
    let i = 0
    const maxI = 4

    while (i < maxI) {
      if (!(await isClientReady())) {
        i++

        if (i === maxI) {
          break
        }

        await sleep(i * 1000)
        continue
      }

      const profiles = await client.searchProfiles(options)

      return profiles
    }

    throw new Error("client-not-ready")
  }

  return {
    client,
    initiateClient,
    isClientReady,
    searchProfiles,
  }
}

Query the client profile with useEffect on page render

useEffect(() => {
  const getCustomerProfile = async () => {
    const queryOptions = {
      DomainName: config.customerProfilesDomain,
      KeyName: "_phone",
      Values: phoneNumber ? [phoneNumber] : undefined,
    }

     const profiles = await searchProfiles(queryOptions)

  }

  getCustomerProfile()
}, [<dependencies>])

I guess this issue can be closed if there is nothing else to track on the AWS side. I'm wondering whether it would be better to have some sort async initialization for the CustomerProfilesClient class to avoid the edge case when the client is not ready. Thank you for your support!

greysonevins commented 11 months ago

Will cut a feature request for a better mechanism to notify when client is ready.

I believe the new client initialization should help.

After that, checking if the iframe exists before calling the API has helped with our integration tests too if that helps.

Something like:


  const [ready, setReady] = useState(false);
  const intervalRef = useRef();

  useEffect(() => {
    intervalRef.current = setInterval(() => {
      if (
        document.querySelector(
          "iframe[id=AmazonConnectCustomerProfilesClient]",
        ) !== null
      ) {
        setReady(true);
      }
    }, [200]);
    return () => clearInterval(intervalRef.current);
  }, [setReady]);

  useEffect(() => {
    if (ready && intervalRef.current) {
      clearInterval(intervalRef.current);
    }
  }, [ready]);
vtrofin commented 11 months ago

Thank you very much @greysonevins for the help and for the code suggestions!