@connectifi/agent-web
TypeScript icon, indicating that this package has built-in type declarations

2.1.3-beta.3 • Public • Published

connectifi-agent-web

This module provides a basic implementation of a Connectifi Agent for all web clients including desktop integration platforms, electronjs, and vanilla browsers. It will bind to any Connectifi interop service endpoint and expose a scoped FDC3 api. By default, the Agent will generate UX for standard interop functions such as color picking and intent resolution. It can also be run headless.

instalation

Install the module in your app:

npm i @connectifi/agent-web

Include it in your bundle

import {createAgent} from '@connectifi/agent-web';

To bypass bundling, you can also load the Agent from unpkg.com and use directly as a script module:

  <script type="module" >

    import { createAgent } from "https://unpkg.com/@connectifi/agent-web/dist/main.js";

            createAgent(
                  `https://[ME].connectifi-interop.com`,
                  `$[appName]@$[directoryName]`,
                )
                .then((fdc3) => {
                    // do fdc3 things...
                });
    </script>

usage

The module exposes a single function: createAgent which returns a Promise that resolves with a standard FDC3 2.0 API (DesktopAgent) after successfully connecting to the specified interop service. The function takes 3 arguments:

  • interopHost - the URL of the Connectifi service the app is targeting
  • appId - the FDC3 appId for the app. Note: this must be in the format of appName@directoryName (see security model below)
  • config - optional configuration for the Agent including the following props:
    • logger - a callback function for logging output from agent
    • headless - boolean flag to run the Agent in headless mode (it will just make a connection and expose the FDC3 API)
    • props - a collection of properties to configue the UI for the agent
      • logoSrc - override the logo image

Note: If you need an FDC3 1.2 intereface, you can use the 1.3.2 version of this module.

Simple creation example

    const fdc3Session = await createAgent('https://dev.connectifi-interop.com, 'myApp@myDirectory');

    //then use the standard FDC3 api
    const listener = await fdc3Session.addContextListener('fdc3.instrument', handleContext);

Example using the logger

const logThat = (...params: any) => {
  console.log('logThat', ...params);
  const paramString = [...params].reduce((acc, p) => {
    acc += ` ${typeof p === 'object' ? JSON.stringify(p) : p}`;
    return acc;
  });
  const newLog = `${new Date().toISOString()} - ${paramString}`;
  setLogs((prevLogs) => [...prevLogs, newLog]);
};

const fdc3Session = await createAgent(
  'https://dev.connectictifi-interop.com',
  'myApp@myDirectory',
  {logger: logThat}
);

//then use the standard FDC3 api...

Example setting a custom logo

const fdc3Session = await createAgent(
  'https://dev.connectictifi-interop.com',
  'myApp@myDirectory',
  {
    props: {
      logoSrc: 'https://mylogo.png',
    },
  }
);

//then use the standard FDC3 api...

note: If you are setting a custom logo, you'll get best results if the logo is:

  • over HTTPS
  • square
  • png format
  • white/light on a transparent background

integrating with existing FDC3 patterns

By scoping of the FDC3 API, the Connectifi Agent provides a superset of typical FDC3 functionality. Supporting the FDC3 global pattern used in Desktop Containers is simple to do as well.

making a fdc3 global object

const fdc3Global = async () => {
  //create the agent and assign to the window
  window.fdc3 = await createAgent(
    'https://dev.connectifi-interop.com',
    'myApp@myDirectory'
  );

  //fire the fdc3Ready event
  document.dispatchEvent(new CustomEvent('fdc3Ready', {}));
};

integrating into a preload

Using the above code, it is simple to then package the global declaration as a preload script in any Electronjs-based project.

bridging with an existing fdc3 global instance

When setting the bridgeGlobal configuration flag to true, the agent will bridge with the globally declared FDC3 object in it's environment. Bridging behavior will:

  • if a Connectifi system/user channel is joined or left, the bridge will join or leave the global system/user channel of the same id (if it exists)
  • if a context is received over the currently joined channel, the bridge will perform an fdc3.broadcast on the global with the same context
  • if raiseIntent or raiseIntentForContext is called on the Connectifi scope, the Agent will also get the relevant intents from the FDC3 global environment and display them in the resolver as 'local' intents.

note: this feature is a work in progress and the nuance of the behavior will continue to evolve along with support for bridging in different FDC3 environments.

customizing the Agent

The Connectifi Agent can be fully customized using the configuration object. passed into the constructor. The AgentConfig interface:

export interface AgentConfig {
  props?: FabProps;
  headless?: boolean;
  logLevel?: LogLevel;
  bridgeGlobal?: boolean;
  handleIntentResolution?: (
    message: IntentResolutionMessage,
    callback: ResolveCallback,
    closeCallback: CloseCallback,
  ) => void;
  handleOpen?: (message: ConnectifiOpenMessage) => void;
  onAuthError?: (directory: string) => void;
  onAppIdentityError?: (directory: string) => void;
  onChannelJoined?: (message: any) => void;
  onChannelLeft?: () => void;
  logger?: (...params: any) => void;
  onWorkingChanged?: (working:boolean) => void;
  onSignedIn?: (username: string) => void;
  onSignedOut?: () => void;
  onConnected?: () => void;
  onDisconnected?: (nextConnect?: number) => void;
  onLoadError?: () => void;
}
  • props
    • These are UI-specific properties that configure the default ‘FAB’ UX provided by the Agent (see UI Props below)
  • headless
    • If set to true - no default UI is rendered by the agent.
  • logLevel
    • logging can be set to ‘debug’, ‘info’, or ‘silent’
  • bridgeGlobal
    • Experimental feature: if set to true - the Agent will attach to the window.fdc3 object (e.g. if running in a desktop container) and join to it for intents resolution and broadcast of context.
  • logger
    • Specify a custom function to output log statements.

UI Props

logoSrc?: string;
position?: ValidPositions;
loginStyle?: LoginStyles;
  • logoSrc - defines a custom location for the logo shown in the FAB
  • position - 'tl', 'ml', 'bl', 'tr', 'mr', or 'br’. Positions the FAB in top, bottom, middle, and right/left/center of the screen.

UI Callbacks

	
  handleIntentResolution?: (
    message: IntentResolutionMessage,
    callback: ResolveCallback,
    closeCallback: CloseCallback,
  ) => void;
  handleOpen?: (message: ConnectifiOpenMessage) => void;
  onAuthError?: (directory: string) => void;
  onAppIdentityError?: (directory: string) => void;
  onChannelJoined?: (message: any) => void;
  onChannelLeft?: () => void;
  logger?: (...params: any) => void;
  onWorkingChanged?: (working:boolean) => void;
  onSignedIn?: (username: string) => void;
  onSignedOut?: () => void;
  onConnected?: () => void;
  onDisconnected?: (nextConnect?: number) => void;
  onLoadError?: () => void;

handleIntentResolution

This is called when an ambiguous list of intent results is pushed to the app for the end user to resolve. The callback will be provided with an IntentResolutionMessage data structure, along with a call back (ResolveCallback) to call when a user has selected on an app to resolve the intent with.

export interface IntentResolutionMessage {
  resolutionType: ResolutionType;
  context: Context;
  data: AppIntentResult | AppIntentResult[];
  bridgeData?: AppIntentResult | AppIntentResult[];
}

The intent resolution message has the following properties:

  • resolutionType - ‘intent-resolver’ or ‘context-resolver’ depending on if the resolution is coming from a raiseIntent or raiseIntentsForContext call.
  • context - the FDC3 context object associated with the intent
  • data - either a single result (’intent-resolver’ type) or a list of results (’context-resolver’). See AppIntentResult below.
  • bridgeData - an optional set of results coming from the bridged fdc3 global context (Desktop Agent) if the bridgeGlobal property is set to true. See AppIntentResult below.

AppIntentResult

export interface AppIntentResult {
  intent: IntentMetadata;
  apps: Array<ConnectifiApp>;
}
export interface ConnectifiApp extends AppMetadata {
  type: AppInstanceType;
  id: string;
  url: string;
  instanceTitle: string;
  intents: Array<DirectoryIntent>;
  proximity: number;
  useragent: string;
  browser?: string;
  device?: string;
  os?: string;
  lastUpdate: number;
}

The AppIntentResult joins up the standard FDC3 IntentMetadata type with an Array of ConnectifiApp types. (i.e. an intent and the app results matching it). The ConnectifiApp type is an extension of the standard FDC3 AppMetadata type that provides some additional properties that be used to help the user disambiguate between applications. For example:

  • proximity - Starting with 0 - a number indicating the relative device and application proximity from the app that initiated the resolver. For example, if the target app is on the same device and running in the same application (e.g. Chrome) as the initiator, proximity is 0. The scoring is meant to aid sorting the list in order of apps closest to furthers from where the user is currently working.
  • browser - indicator of browser type the app is running in
  • device - indicator of the device the app is running on
  • os - indicator of the OS the app is running on
  • useragent - the raw user agent string for the app

onChannelJoined

Called when a channel is joined. Argument is the id of the channel joined.

onChannelLeft

Called when leaveCurrentChannel is executed. I.e. the user leaves the current channel but does not join another.

handleOpen

Called after fdc3.open or an intent is resolved. In this case, the service sends a message to the client requesting it to launch a specific app. The message format is as follows:

export interface ConnectifiOpenMessage {
  name?: string;
  url?: string;
  pendingId: string;
}

Where name is the name of the app in the directory, url is the url to launch (for web applications), and pendingId is a nonce that will allow the newly launched app to retrieve any expected intent/context data. Note: name and url are optional depending on the type of app being launched.

A typical handleOpen looks like this:

const handleOpen = (message : ConnectifiOpenMessage ) => {
     if (data.url && data.pendingId) {
        window.open(data.url, data.pendingId as string);
      }
}

Note the convention here of using the pendingId as the name for the new window - this is a convention picked up by the Agent - which will automatically use the name of the window to retrieve pending state for the new application. Other patterns can and will be used for non-browser clients. Also note, for most browsers, the window.open call must be sufficiently proximate to a end-user click to work. So, if you are building workflows that don’t have an opportunity for the end user to click on something before an app is launched - you will need to build some mechanism into the flow for browser (presenting the user with the link, etc).

onWorkingChanged

Called when the agent starts or stops "working" or "being busy". the boolean argument is the current state.

onSignedIn

Called after the user has successfully signed into the directory. Will be passed the username / identifier as an argument.

onSignedOut

Called after the user is signed out.

onConnected

Called when a socket connection is established.

onDisconnected

Called when the socket connection is dropped. Will be passed the auto-reconnect time (in milliseconds) as an argument.

onAuthError

Called when the user could not connect to the directory because they could not be authenticated. Passed the directory name as an argument.

onAppIdentityError

Called when the user could not connect to the directory because the app was not registered in the directory or the directory could not be found. Passed the directory name as an argument.

onLoadError

Called when the there is a timeout connecting to the service (interop endpoint).

Handling Connection State

Connectifi is designed to run anywhere including mobile devices where connection drop out is a common occurrence. The Agent provides a number of tools to mitigate potential connection issues.

Auto-reconnect

If a connection is dropped, the agent will automatically manage reconnecting. Toast in the default UI will also advise end users of connection state and any reconnect attempts.

Connection Errors

With FDC3 2.0, all FDC3 APIs are async. With Connectifi, any API call where a connection is rejected with an error of type ConnectionError.NoConnectionAvailable. This error can be handled and used to trigger a notification to the user, logging, and/or queueing of critical events.

onConnect / onDisconnect callbacks

These callbacks outlined above can be used to create custom UX around connection state as well as manage reconnect logic.

Security

The agent provides full code isolation and its own secure connection back to its service endpoint. The directory an agent connects to controls the security profile of the interop. There are 3 basic security levels:

  • Open - any app can connect to the end point - this is recommended for development only.
  • App - an app's identity has to be validated and registered to the directory to connect.
  • Strict - an app's identity must be valid and belong to the directory and the user must be authenticated to connect.

This model allows for robust security where counterparties engaged in interop are not vulnerable to spoofing, cross-site-scripting attacks, and data leakage from unauthorized apps gaining access to a desktop bus.

Troubleshooting

Connectifi is fully of the web, so all the same tools and methods you'd use to troubleshoot a webapp apply for the agent. The logger callback can also provide more flexibility for inspecting and diagnosing any issues on the client. Some baseline items to check if you are having issues with the agent are:

  • is the URL to the interop service correct?
  • do you have the correct (case-sensitive) and fully qualified appName@directoryName identifier for the directory you are connecting to?
  • is your app registered with the directory and does the registered origin match the origin you are connecting from?
  • are you using the FDC3 2.0 API in your app?

Next Steps

If you are not yet working with Connectifi, we'd love to hear from you. Please send us a message at info@connectifi.co.

Next up on our roadmap for the Agent is:

  • Native client and language support
  • More improvements for the default UX
  • Greater configurability of the UX

Install

DownloadsWeekly Downloads

230

Version

2.1.3-beta.3

License

Apache-2.0

Unpacked Size

217 kB

Total Files

7

Last publish

Collaborators

  • brian-connectifi
  • as-connectifi
  • nkolba