

# Service implementation patterns
<a name="building-3P-services-implementation-patterns"></a>

third-party services can implement various patterns to extend the agent workspace functionality. The following examples demonstrate some of our key Amazon Connect SDK capabilities:

## Launching an application on startup
<a name="building-3P-services-launching-app-on-startup"></a>

```
import {
  AmazonConnectService,
  ServiceContext,
  ServiceCreatedEvent,
} from "@amazon-connect/app";
import { ContactClient } from "@amazon-connect/contact";

const provider = AmazonConnectService.init({
  onCreate: onCreateHandler,
});

const onCreateHandler = async (event: ServiceCreatedEvent) => {
  const context: ServiceContext = event.context;
  console.log(`${SDK_LOG_PREFIX} Service created: `, context.instanceId);
  await registerEventHandlers();

  const appName = 'TargetAppName';
  console.log(`Launching app in ACW`, { event: contactEvent, appName: appName });
  const appControllerClient = new AppControllerClient(context.getProvider());
    
  // Get list of all applications available to the agent
  const apps: AppConfig[] = await appControllerClient.getAppCatalog();

  // Find your application by name
  const appArn = apps.find(
    (app) => app.name === appName
  )?.arn;

  if (!appArn) {
    throw new Error(`${appName} not found!`);
  }

  await appControllerClient.launchApp(appArn); 
};
```

## Contact event listening with application launching functionality
<a name="building-3P-services-contact-event-listening"></a>

```
import {
  AmazonConnectService,
  ServiceContext,
  ServiceCreatedEvent,
} from "@amazon-connect/app";
import { ContactClient } from "@amazon-connect/contact";

const provider = AmazonConnectService.init({
  onCreate: onCreateHandler,
});

const onCreateHandler = async (event: ServiceCreatedEvent) => {
  const context: ServiceContext = event.context;
  console.log(`${SDK_LOG_PREFIX} Service created: `, context.instanceId);
  await registerEventHandlers();
  return Promise.resolve();
};

async function registerEventHandlers() {
  const contactClient = new ContactClient(provider);

  // Listen for connected contacts
  contactClient.onConnected(async (contactEvent) => {
    console.log(`Contact connected!`, { event: contactEvent });
  });

  // Listen for contacts that have entered After Contact Work (ACW)
  contactClient.onStartingAcw(async (contactEvent) => {
    const appName = 'TargetAppName';
    console.log(`Launching app in ACW`, { event: contactEvent, appName: appName });
    const appControllerClient = new AppControllerClient(provider);
    
    // Get list of all applications available to the agent
    const apps: AppConfig[] = await appControllerClient.getAppCatalog();
    
    // Find your application by name
    const appArn = apps.find(
        (app) => app.name === appName
    )?.arn;
    
    if (!appArn) {
        throw new Error(`${appName} not found!`);
    }
    
    await appControllerClient.launchApp(appArn);   
    
  });
  
  // Listen for contacts that are cleared
  contactClient.onCleared(async (contactEvent) => {
    try {
      console.log(`Contact Cleared:`, { event: contactEvent, type: contactEvent.type });
    } catch (error) {
      console.error(`Error handling incoming contact:`, { event: contactEvent, error: error });
    }
  });

  // This is emitted when the service unsubscribes from the cleared event
  contactClient.offCleared(async (contactEvent) => {
    console.log(`Contact no longer in the incoming state`, { event: contactEvent });
  });
}
```

## Authentication popup functionality
<a name="building-3P-services-authentication-popup"></a>

```
import { AmazonConnectService } from "@amazon-connect/app";
import { AppControllerClient } from "@amazon-connect/app-controller";
import { AppConfig } from "@amazon-connect/workspace-types";

interface IdpMessage {
  type: 'IDP_MESSAGE';
  payload: {
    message: string;
    timestamp: string;
  };
}

const SDK_LOG_PREFIX = '[TestService]';
const APP_NAME = 'TestApp'

const IDP_POPUP_CONFIG = {
  width: 600,
  height: 400,
  title: 'IDP Simulation'
};

let provider;

// Start the authentication service
export function initIDPService() {
  provider = AmazonConnectService.init({
    onCreate: onCreateHandler
  });
}

const onCreateHandler = async () => {
  console.log(`${SDK_LOG_PREFIX} Service running...`);
  
  try {
    // Start the authentication flow
    runIdpFlow();
  } catch(e) {
    // This reports an error in the auth flow but allows the service
    // to continue running.
    console.error(`${SDK_LOG_PREFIX} IDP flow failed:`, error);
  }

  console.log(`${SDK_LOG_PREFIX} Service creation complete`);
};

/**
  * Creates the IDP authentication popup
  * Returns a promise that resolves when authentication is complete
  * or rejects if authentication fails/times out 
  */
export const initIdpSimulator = (): Promise<void> => {
  return new Promise((resolve, reject) => {

    // Clean up event listeners and timers
    const cleanup = () => {
      window.removeEventListener('message', handleIdpMessage);
    };

    // Handle messages received from the IDP popup
    const handleIdpMessage = async (event: MessageEvent<IdpMessage>) => {
      if (event.data.type === 'IDP_MESSAGE') {
        // Implementation for a successful authentication
        console.log(`${SDK_LOG_PREFIX} Message received from IDP:`, event.data.payload);
        
        try {
          // Launch the application after successful authentication
          await launchApp(APP_NAME);
          console.log(`${SDK_LOG_PREFIX} Successfully launched app`);
          resolve();
        } catch (error) {
          console.error(`${SDK_LOG_PREFIX} Failed to launch app:`, error);
          reject(error);
        } finally {
          cleanup();
        }
      }
    };

    // Open the authentication popup window
    const openIdpPopup = () => {
      // https://developer.mozilla.org/en-US/docs/Web/API/Window/open
      const popup = window.open('/popup.html', // URL to your authentication page
        IDP_POPUP_CONFIG.title, 
        `width=${IDP_POPUP_CONFIG.width},height=${IDP_POPUP_CONFIG.height}`
      );
      
      if (!popup) {
        cleanup();
        reject(new Error('Failed to open IDP popup'));
        return;
      }
    };

    // Listen for messages from the popup
    window.addEventListener('message', handleIdpMessage);

    // Open the popup
    openIdpPopup();    
  });
};

/**
  * Manages the complete authentication flow
  * Handles errors and logging 
  */
export const runIdpFlow = async () => {
  try {
    console.log(`${SDK_LOG_PREFIX} Starting IDP flow...`);
    await initIdpSimulator();
    console.log(`${SDK_LOG_PREFIX} IDP flow completed successfully`);
  } catch (error) {
    console.error(`${SDK_LOG_PREFIX} IDP flow failed!`, error);
    throw error;
  }
};

/**
  * Launches the specified application using the AppController
  * Verifies the app exists in the catalog before attempting to launch 
  */
export async function launchApp(targetAppName: string) {
  const appControllerClient = new AppControllerClient(provider);
  
  // Get list of all applications available to the agent
  const apps: AppConfig[] = await appControllerClient.getAppCatalog();
  console.log(`${SDK_LOG_PREFIX} App Catalog: `, apps);
  
  // Find your application by name
  const testAppArn = apps.find(
    (app) => app.name === targetAppName
  )?.arn;
  
  if (!testAppArn) {
    throw new Error(targetAppName + " not found!");
  }
  
  await appControllerClient.launchApp(testAppArn);
}
```

This is the HTML template for the authentication popup:

```
<!DOCTYPE html>
<html>
  <head>
      <title>IDP Simulation</title>
      <style>
          body {
              font-family: Arial, sans-serif;
              padding: 20px;
              background: #f5f5f5;
          }
          .container {
              background: white;
              padding: 20px;
              border-radius: 8px;
              box-shadow: 0 2px 10px rgba(0,0,0,0.1);
          }
          button {
              padding: 10px 20px;
              margin: 5px;
              border: none;
              border-radius: 4px;
              cursor: pointer;
              background: #007bff;
              color: white;
          }
          button:hover {
              background: #0056b3;
          }
      </style>
  </head>
  <body>
      <div class="container">
          <h2>IDP Simulation</h2>
          <p>Click the button below to simulate IDP authentication</p>
          <button id="authButton">Authenticate</button>
      </div>
      <script>
          // When authentication button is clicked, send success message 
          // back to parent window
          document.getElementById('authButton').onclick = function() {
              window.opener.postMessage({
                  type: 'IDP_MESSAGE',
                  payload: {
                      message: 'User authenticated via IDP',
                      timestamp: new Date().toISOString()
                  }
              }, '*');
              window.close();
          };
      </script>
  </body>
</html>
```