Wednesday, June 17, 2020

AWS Connect Chat/Lex Bot - Web Client via API-Gateway, Lambda


Summary

This post is a continuation my previous on the topic web chat client integration to AWS Connect.  In this post, I utilize more of the AWS suite to implement the same chat client.  Specifically, I put together a pure cloud architecture with CloudFront providing CDN services, S3 providing static web hosting, API-Gateway providing REST API call proxying to Lambda, and finally Lambda providing the direct SDK integration with Connect.

Architecture

Below is a diagram depicting what was discussed above.  A static HTML/Javascript site is hosted on S3.  That site is front-ended by CloudFront.  The Javascript client application makes REST calls to API-Gateway which proxies those calls to a Lambda function.  The Lambda function in turn is proxying those calls to the appropriate AWS SDK calls to Connect.

 

Web Client Architecture

The diagram below depicts the client architecture.  All SDK calls to AWS Connect are abstracted to REST calls into API-Gateway + Lambda.

 

Code Snippets

Client POST/connect call

 async _connect() {  
  try {
   const body = {
    DisplayName: this.displayName,
    ParticipantToken: this.participantToken
   };
   const response = await fetch(API_URL, {
    method: 'POST',
    headers: {
     'Content-Type': 'application/json'
    },
    body: JSON.stringify(body)
   });
   
   const json = await response.json();
   if (response.ok) {
    this.participantToken = json.ParticipantToken;
    const diff = Math.abs(new Date() - Date.parse(json.Expiration));
    this.refreshTimer = setTimeout(this._connect, diff - 5000); //refresh the websocket
    this.connectionToken = json.ConnectionToken;
    this._subscribe(json.Url);
   }
   else {
    throw new Error(JSON.stringify(json));
   }
  }
  catch(err) {
   console.log(err);
  }
 }

Corresponding Lambda Proxy

exports.handler = async (event) => {
 let resp, body;
 try {
  AWS.config.region = process.env.REGION; 
  AWS.config.credentials = new AWS.Credentials(process.env.ACCESS_KEY_ID, 
   process.env.SECRET_ACCESS_KEY);

  switch (event.path) {
   case '/connectChat': 
    switch (event.httpMethod) {
     case 'POST':
      body = JSON.parse(event.body);
      resp = await connect(body.DisplayName, body.ParticipantToken);
      return {
       headers: {'Access-Control-Allow-Origin': '*'}, 
       statusCode : 200,
       body : JSON.stringify(resp)
      } 
async function connect(displayName, token) {
 let sdk, params, response, participantToken;

 if (token) {
  participantToken = token;
 } 
 else {
  sdk = new AWS.Connect();
  params = {
    ContactFlowId: process.env.FLOW_ID,
    InstanceId: process.env.INSTANCE_ID,
    ParticipantDetails: {DisplayName: displayName}
  };
  response = await sdk.startChatContact(params).promise();
  participantToken = response.ParticipantToken;
 }

 sdk = new AWS.ConnectParticipant();
 params = {
  ParticipantToken: participantToken,
  Type: ['WEBSOCKET', 'CONNECTION_CREDENTIALS']
 };  
 response = await sdk.createParticipantConnection(params).promise();
 const expiration = response.Websocket.ConnectionExpiry;
 const connectionToken = response.ConnectionCredentials.ConnectionToken;
 const url = response.Websocket.Url;

 const retVal = {
  ParticipantToken : participantToken,
  Expiration : expiration,
  ConnectionToken : connectionToken,
  Url : url
 };

 return retVal;
}

Source

https://github.com/joeywhelan/awsConnectAPIGwyClient

Copyright ©1993-2024 Joey E Whelan, All rights reserved.