Skip to content

Commit

Permalink
Added externalAppCardActionsForCEA APIs (#2507)
Browse files Browse the repository at this point in the history
* CEC Card API changes

* address comments

* rename CEC to CEA

* address comments

* address comments

* delete test log

* address comments for adding hidden tag

* Update packages/teams-js/src/private/externalAppCardActionsForCEA.ts

Co-authored-by: Trevor Harris <[email protected]>

* using sendAndUnwrap function

* add beta tag

---------

Co-authored-by: Trevor Harris <[email protected]>
  • Loading branch information
MengyiGong and TrevorJoelHarris committed Sep 25, 2024
1 parent 60eb045 commit 57ea793
Show file tree
Hide file tree
Showing 12 changed files with 516 additions and 52 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { externalAppCardActions } from '@microsoft/teams-js';
import { externalAppCardActions, IAdaptiveCardActionSubmit } from '@microsoft/teams-js';
import React from 'react';

import { ApiWithoutInput, ApiWithTextInput } from '../utils';
Expand All @@ -15,7 +15,7 @@ const CheckExternalAppCardActionsCapability = (): React.ReactElement =>
const ProcessActionSubmit = (): React.ReactElement =>
ApiWithTextInput<{
appId: string;
actionSubmitPayload: externalAppCardActions.IAdaptiveCardActionSubmit;
actionSubmitPayload: IAdaptiveCardActionSubmit;
}>({
name: 'processActionSubmit',
title: 'Process Action Submit',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { AppId, externalAppCardActionsForCEA, IAdaptiveCardActionSubmit } from '@microsoft/teams-js';
import React from 'react';

import { ApiWithoutInput, ApiWithTextInput } from '../utils';
import { ModuleWrapper } from '../utils/ModuleWrapper';

const CheckExternalAppCardActionsForCEACapability = (): React.ReactElement =>
ApiWithoutInput({
name: 'checkExternalAppCardActionsForCEACapability',
title: 'Check External App Card Actions For CEA Capability',
onClick: async () =>
`External App Card Actions For CEA module ${
externalAppCardActionsForCEA.isSupported() ? 'is' : 'is not'
} supported`,
});

const CECProcessActionSubmit = (): React.ReactElement =>
ApiWithTextInput<{
appId: AppId;
conversationId: string;
actionSubmitPayload: IAdaptiveCardActionSubmit;
}>({
name: 'processActionSubmitForCEA',
title: 'Process Action Submit For CEA',
onClick: {
validateInput: (input) => {
if (!input.appId) {
throw new Error('appId is required');
}
if (!input.actionSubmitPayload) {
throw new Error('actionSubmitPayload is required');
}
},
submit: async (input) => {
await externalAppCardActionsForCEA.processActionSubmit(
input.appId,
input.conversationId,
input.actionSubmitPayload,
);
return 'Completed';
},
},
defaultInput: JSON.stringify({
appId: 'b7f8c0a0-6c1d-4a9a-9c0a-2c3f1c0a3b0a',
actionSubmitPayload: {
id: 'submitId',
data: 'data1',
},
}),
});

const CECProcessActionOpenUrl = (): React.ReactElement =>
ApiWithTextInput<{
appId: AppId;
conversationId: string;
url: string;
}>({
name: 'processActionOpenUrlForCEA',
title: 'Process Action Open Url For CEA',
onClick: {
validateInput: (input) => {
if (!input.appId) {
throw new Error('appId is required');
}
if (!input.url) {
throw new Error('url is required');
}
},
submit: async (input) => {
const result = await externalAppCardActionsForCEA.processActionOpenUrl(
input.appId,
input.conversationId,
new URL(input.url),
);
return JSON.stringify(result);
},
},
defaultInput: JSON.stringify({
appId: 'b7f8c0a0-6c1d-4a9a-9c0a-2c3f1c0a3b0a',
url: 'https://www.example.com',
}),
});

const ExternalAppCardActionsForCEAAPIs = (): React.ReactElement => (
<ModuleWrapper title="External App Card Actions For CEA">
<CheckExternalAppCardActionsForCEACapability />
<CECProcessActionSubmit />
<CECProcessActionOpenUrl />
</ModuleWrapper>
);

export default ExternalAppCardActionsForCEAAPIs;
2 changes: 2 additions & 0 deletions apps/teams-test-app/src/pages/TestApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import ChatAPIs from '../components/privateApis/ChatAPIs';
import CopilotAPIs from '../components/privateApis/CopilotAPIs';
import ExternalAppAuthenticationAPIs from '../components/privateApis/ExternalAppAuthenticationAPIs';
import ExternalAppCardActionsAPIs from '../components/privateApis/ExternalAppCardActionsAPIs';
import ExternalAppCardActionsForCEAAPIs from '../components/privateApis/ExternalAppCardActionsForCEAAPIs';
import ExternalAppCommandsAPIs from '../components/privateApis/ExternalAppCommandsAPIs';
import FilesAPIs from '../components/privateApis/FilesAPIs';
import FullTrustAPIs from '../components/privateApis/FullTrustAPIs';
Expand Down Expand Up @@ -114,6 +115,7 @@ export const TestApp: React.FC = () => {
<DialogUrlParentCommunicationAPIs childWindowRef={dialogWindowRef} />
<ExternalAppAuthenticationAPIs />
<ExternalAppCardActionsAPIs />
<ExternalAppCardActionsForCEAAPIs />
<ExternalAppCommandsAPIs />
<FilesAPIs />
<FullTrustAPIs />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "minor",
"comment": "Added APIs for `externalAppCardActionsForCEA` capability.",
"packageName": "@microsoft/teams-js",
"email": "[email protected]",
"dependentChangeType": "patch"
}
2 changes: 2 additions & 0 deletions packages/teams-js/src/internal/telemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ export const enum ApiName {
ExternalAppAuthentication_AuthenticateWithPowerPlatformConnectorPlugins = 'externalAppAuthentication.authenticateWithPowerPlatformConnectorPlugins',
ExternalAppCardActions_ProcessActionOpenUrl = 'externalAppCardActions.processActionOpenUrl',
ExternalAppCardActions_ProcessActionSubmit = 'externalAppCardActions.processActionSubmit',
ExternalAppCardActionsForCEA_ProcessActionOpenUrl = 'externalAppCardActionsForCEA.processActionOpenUrl',
ExternalAppCardActionsForCEA_ProcessActionSubmit = 'externalAppCardActionsForCEA.processActionSubmit',
ExternalAppCommands_ProcessActionCommands = 'externalAppCommands.processActionCommand',
Files_AddCloudStorageFolder = 'files.addCloudStorageFolder',
Files_AddCloudStorageProvider = 'files.addCloudStorageProvider',
Expand Down
50 changes: 1 addition & 49 deletions packages/teams-js/src/private/externalAppCardActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { ApiName, ApiVersionNumber, getApiVersionTag } from '../internal/telemet
import { AppId } from '../public';
import { errorNotSupportedOnPlatform, FrameContexts } from '../public/constants';
import { runtime } from '../public/runtime';
import { ExternalAppErrorCode } from './constants';
import { ActionOpenUrlError, ActionSubmitError, IAdaptiveCardActionSubmit } from './interfaces';

/**
* v2 APIs telemetry file: All of APIs in this capability file should send out API version v2 ONLY
Expand All @@ -31,54 +31,6 @@ export namespace externalAppCardActions {
GenericUrl = 'GenericUrl',
}

/**
* @hidden
* Error that can be thrown from IExternalAppCardActionService.handleActionOpenUrl
*
* @internal
* Limited to Microsoft-internal use
*/
export interface ActionOpenUrlError {
errorCode: ActionOpenUrlErrorCode;
message?: string;
}

/**
* @hidden
* Error codes that can be thrown from IExternalAppCardActionService.handleActionOpenUrl
* @internal
* Limited to Microsoft-internal use
*/
export enum ActionOpenUrlErrorCode {
INTERNAL_ERROR = 'INTERNAL_ERROR', // Generic error
INVALID_LINK = 'INVALID_LINK', // Deep link is invalid
NOT_SUPPORTED = 'NOT_SUPPORTED', // Deep link is not supported
}

/**
* @hidden
* The payload that is used when executing an Adaptive Card Action.Submit
* @internal
* Limited to Microsoft-internal use
*/
export interface IAdaptiveCardActionSubmit {
id: string;
data: string | Record<string, unknown>;
}

/**
*
* @hidden
* Error that can be thrown from IExternalAppCardActionService.handleActionSubmit
*
* @internal
* Limited to Microsoft-internal use
*/
export interface ActionSubmitError {
errorCode: ExternalAppErrorCode;
message?: string;
}

/**
* @beta
* @hidden
Expand Down
108 changes: 108 additions & 0 deletions packages/teams-js/src/private/externalAppCardActionsForCEA.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { sendAndUnwrap, sendMessageToParentAsync } from '../internal/communication';
import { ensureInitialized } from '../internal/internalAPIs';
import { ApiName, ApiVersionNumber, getApiVersionTag } from '../internal/telemetry';
import { validateId } from '../internal/utils';
import { AppId } from '../public';
import { errorNotSupportedOnPlatform, FrameContexts } from '../public/constants';
import { runtime } from '../public/runtime';
import { ActionOpenUrlError, ActionOpenUrlType, ActionSubmitError, IAdaptiveCardActionSubmit } from './interfaces';

/**
* All of APIs in this capability file should send out API version v2 ONLY
*/
const externalAppCardActionsTelemetryVersionNumber: ApiVersionNumber = ApiVersionNumber.V_2;
/**
* @beta
* @hidden
* Namespace to delegate adaptive card action for Custom Engine Agent execution to the host
* @internal
* Limited to Microsoft-internal use
*/
export namespace externalAppCardActionsForCEA {
/**
* @beta
* @hidden
* Delegates an Adaptive Card Action.OpenUrl request to the host for the application with the provided app ID.
* @internal
* Limited to Microsoft-internal use
* @param appId ID of the application the request is intended for. This must be a UUID
* @param conversationId To tell the bot what conversation the calls are coming from
* @param url The URL to open
* @throws Error if the response has not successfully completed
* @returns Promise that resolves to ActionOpenUrlType indicating the type of URL that was opened on success and rejects with ActionOpenUrlError if the request fails
*/
export async function processActionOpenUrl(
appId: AppId,
conversationId: string,
url: URL,
): Promise<ActionOpenUrlType> {
ensureInitialized(runtime, FrameContexts.content);
if (!isSupported()) {
throw errorNotSupportedOnPlatform;
}
validateId(conversationId, new Error('conversation id is not valid.'));
const [error, response] = await sendMessageToParentAsync<[ActionOpenUrlError, ActionOpenUrlType]>(
getApiVersionTag(
externalAppCardActionsTelemetryVersionNumber,
ApiName.ExternalAppCardActionsForCEA_ProcessActionOpenUrl,
),
ApiName.ExternalAppCardActionsForCEA_ProcessActionOpenUrl,
[appId, url.href, conversationId],
);
if (error) {
throw error;
} else {
return response;
}
}

/**
* @beta
* @hidden
* Delegates an Adaptive Card Action.Submit request to the host for the application with the provided app ID
* @internal
* Limited to Microsoft-internal use
* @param appId ID of the application the request is intended for. This must be a UUID
* @param conversationId To tell the bot what conversation the calls are coming from
* @param actionSubmitPayload The Adaptive Card Action.Submit payload
* @throws Error if host notifies of an error
* @returns Promise that resolves when the request is completed and rejects with ActionSubmitError if the request fails
*/
export async function processActionSubmit(
appId: AppId,
conversationId: string,
actionSubmitPayload: IAdaptiveCardActionSubmit,
): Promise<void> {
ensureInitialized(runtime, FrameContexts.content);
if (!isSupported()) {
throw errorNotSupportedOnPlatform;
}
validateId(conversationId, new Error('conversation id is not valid.'));
const error = await sendAndUnwrap<ActionSubmitError | undefined>(
getApiVersionTag(
externalAppCardActionsTelemetryVersionNumber,
ApiName.ExternalAppCardActionsForCEA_ProcessActionSubmit,
),
ApiName.ExternalAppCardActionsForCEA_ProcessActionSubmit,
[appId, conversationId, actionSubmitPayload],
);
if (error) {
throw error;
}
}

/**
* @beta
* @hidden
* Checks if the externalAppCardActionsForCEA capability is supported by the host
* @returns boolean to represent whether externalAppCardActions capability is supported
*
* @throws Error if {@linkcode app.initialize} has not successfully completed
*
* @internal
* Limited to Microsoft-internal use
*/
export function isSupported(): boolean {
return ensureInitialized(runtime) && runtime.supports.externalAppCardActionsForCEA ? true : false;
}
}
1 change: 1 addition & 0 deletions packages/teams-js/src/private/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export { conversations } from './conversations';
export { copilot } from './copilot';
export { externalAppAuthentication } from './externalAppAuthentication';
export { externalAppCardActions } from './externalAppCardActions';
export { externalAppCardActionsForCEA } from './externalAppCardActionsForCEA';
export { externalAppCommands } from './externalAppCommands';
export { files } from './files';
export { meetingRoom } from './meetingRoom';
Expand Down
Loading

0 comments on commit 57ea793

Please sign in to comment.