Skip to content

Commit

Permalink
address comments
Browse files Browse the repository at this point in the history
  • Loading branch information
MengyiGong committed Sep 20, 2024
1 parent 1199c21 commit 2c71f5c
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 54 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { externalAppCardActions, IAdaptiveCardActionSubmit } from '@microsoft/teams-js';
import { AppId, externalAppCardActionsForCEA, IAdaptiveCardActionSubmit } from '@microsoft/teams-js';
import React from 'react';

import { ApiWithoutInput, ApiWithTextInput } from '../utils';
Expand All @@ -9,12 +9,15 @@ const CheckExternalAppCardActionsForCEACapability = (): React.ReactElement =>
name: 'checkExternalAppCardActionsForCEACapability',
title: 'Check External App Card Actions For CEA Capability',
onClick: async () =>
`External App Card Actions For CEA module ${externalAppCardActions.isSupported() ? 'is' : 'is not'} supported`,
`External App Card Actions For CEA module ${
externalAppCardActionsForCEA.isSupported() ? 'is' : 'is not'
} supported`,
});

const CECProcessActionSubmit = (): React.ReactElement =>
ApiWithTextInput<{
appId: string;
appId: AppId;
conversationId: string;
actionSubmitPayload: IAdaptiveCardActionSubmit;
}>({
name: 'processActionSubmitForCEA',
Expand All @@ -29,7 +32,11 @@ const CECProcessActionSubmit = (): React.ReactElement =>
}
},
submit: async (input) => {
await externalAppCardActions.processActionSubmit(input.appId, input.actionSubmitPayload);
await externalAppCardActionsForCEA.processActionSubmit(
input.appId,
input.conversationId,
input.actionSubmitPayload,
);
return 'Completed';
},
},
Expand All @@ -44,9 +51,9 @@ const CECProcessActionSubmit = (): React.ReactElement =>

const CECProcessActionOpenUrl = (): React.ReactElement =>
ApiWithTextInput<{
appId: string;
appId: AppId;
conversationId: string;
url: string;
fromElement?: { name: 'composeExtensions' | 'plugins' };
}>({
name: 'processActionOpenUrlForCEA',
title: 'Process Action Open Url For CEA',
Expand All @@ -60,10 +67,10 @@ const CECProcessActionOpenUrl = (): React.ReactElement =>
}
},
submit: async (input) => {
const result = await externalAppCardActions.processActionOpenUrl(
const result = await externalAppCardActionsForCEA.processActionOpenUrl(
input.appId,
input.conversationId,
new URL(input.url),
input.fromElement,
);
return JSON.stringify(result);
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"type": "minor",
"comment": "Added APIs for externalAppCardActionForCEA capability.",
"comment": "Added APIs for `externalAppCardActionsForCEA` capability.",
"packageName": "@microsoft/teams-js",
"email": "[email protected]",
"dependentChangeType": "patch"
Expand Down
45 changes: 22 additions & 23 deletions packages/teams-js/src/private/externalAppCardActionsForCEA.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { 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';
Expand All @@ -22,28 +23,29 @@ export namespace externalAppCardActionsForCEA {
* @param url The URL to open
* @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 function processActionOpenUrl(appId: string, conversationId: string, url: URL): Promise<ActionOpenUrlType> {
export async function processActionOpenUrl(
appId: AppId,
conversationId: string,
url: URL,
): Promise<ActionOpenUrlType> {
ensureInitialized(runtime, FrameContexts.content);

if (!isSupported()) {
throw errorNotSupportedOnPlatform;
}
validateId(appId, new Error('App id is not valid.'));
validateId(conversationId, new Error('conversation id is not valid.'));
return sendMessageToParentAsync<[ActionOpenUrlError, ActionOpenUrlType]>(
const [error, response] = await sendMessageToParentAsync<[ActionOpenUrlError, ActionOpenUrlType]>(
getApiVersionTag(
externalAppCardActionsTelemetryVersionNumber,
ApiName.ExternalAppCardActionsForCEA_ProcessActionOpenUrl,
),
'externalAppCardActionsForCEA.processActionOpenUrl',
ApiName.ExternalAppCardActionsForCEA_ProcessActionOpenUrl,
[appId, url.href, conversationId],
).then(([error, response]: [ActionOpenUrlError, ActionOpenUrlType]) => {
if (error) {
throw error;
} else {
return response;
}
});
);
if (error) {
throw error;
} else {
return response;
}
}

/**
Expand All @@ -57,30 +59,27 @@ export namespace externalAppCardActionsForCEA {
* @param actionSubmitPayload The Adaptive Card Action.Submit payload
* @returns Promise that resolves when the request is completed and rejects with ActionSubmitError if the request fails
*/
export function processActionSubmit(
appId: string,
export async function processActionSubmit(
appId: AppId,
conversationId: string,
actionSubmitPayload: IAdaptiveCardActionSubmit,
): Promise<void> {
ensureInitialized(runtime, FrameContexts.content);

if (!isSupported()) {
throw errorNotSupportedOnPlatform;
}
validateId(appId, new Error('App id is not valid.'));
validateId(conversationId, new Error('conversation id is not valid.'));
return sendMessageToParentAsync<[boolean, ActionSubmitError]>(
const [wasSuccessful, error] = await sendMessageToParentAsync<[boolean, ActionSubmitError]>(
getApiVersionTag(
externalAppCardActionsTelemetryVersionNumber,
ApiName.ExternalAppCardActionsForCEA_ProcessActionSubmit,
),
'externalAppCardActionsForCEA.processActionSubmit',
ApiName.ExternalAppCardActionsForCEA_ProcessActionSubmit,
[appId, conversationId, actionSubmitPayload],
).then(([wasSuccessful, error]: [boolean, ActionSubmitError]) => {
if (!wasSuccessful) {
throw error;
}
});
);
if (!wasSuccessful) {
throw error;
}
}

/**
Expand Down
73 changes: 51 additions & 22 deletions packages/teams-js/test/private/externalAppCardActionsForCEA.spec.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { errorLibraryNotInitialized } from '../../src/internal/constants';
import { GlobalVars } from '../../src/internal/globalVars';
import { ApiName } from '../../src/internal/telemetry';
import { ExternalAppErrorCode } from '../../src/private/constants';
import { externalAppCardActionsForCEA } from '../../src/private/externalAppCardActionsForCEA';
import { ActionOpenUrlErrorCode, ActionOpenUrlType } from '../../src/private/interfaces';
import { FrameContexts } from '../../src/public';
import { AppId, FrameContexts } from '../../src/public';
import { app } from '../../src/public/app';
import { errorNotSupportedOnPlatform } from '../../src/public/constants';
import { Utils } from '../utils';
Expand All @@ -12,7 +13,7 @@ describe('externalAppCardActionsForCEA', () => {
let utils = new Utils();

// This ID was randomly generated for the purpose of these tests
const testAppId = '01b92759-b43a-4085-ac22-7772d94bb7a9';
const testAppId = new AppId('01b92759-b43a-4085-ac22-7772d94bb7a9');
const testConversationId = '61f7f08d-477b-42b8-9c36-44eabb58eb92';

beforeEach(() => {
Expand All @@ -37,11 +38,16 @@ describe('externalAppCardActionsForCEA', () => {
errorCode: ExternalAppErrorCode.INTERNAL_ERROR,
message: 'testMessage',
};

it('should not allow calls before initialization', async () => {
return expect(() =>
externalAppCardActionsForCEA.processActionSubmit(testAppId, testConversationId, testActionSubmitPayload),
).toThrowError(new Error(errorLibraryNotInitialized));
expect.assertions(1);
try {
await externalAppCardActionsForCEA.processActionSubmit(testAppId, testConversationId, testActionSubmitPayload);
} catch (e) {
expect(e).toEqual(new Error(errorLibraryNotInitialized));
}
});

it('should throw error when externalAppCardActionsForCEA capability is not supported', async () => {
expect.assertions(1);
await utils.initializeWithContext(FrameContexts.content);
Expand All @@ -52,25 +58,30 @@ describe('externalAppCardActionsForCEA', () => {
expect(e).toEqual(errorNotSupportedOnPlatform);
}
});

Object.values(FrameContexts).forEach((frameContext) => {
if (allowedFrameContexts.includes(frameContext)) {
it(`should resolve when called from context - ${frameContext}`, async () => {
expect.assertions(3);
await utils.initializeWithContext(frameContext);
utils.setRuntimeConfig({ apiVersion: 2, supports: { externalAppCardActionsForCEA: {} } });

const promise = externalAppCardActionsForCEA.processActionSubmit(
testAppId,
testConversationId,
testActionSubmitPayload,
);

const message = utils.findMessageByFunc('externalAppCardActionsForCEA.processActionSubmit');
if (message && message.args) {
expect(message).not.toBeNull();
expect(message.args).toEqual([testAppId, testConversationId, testActionSubmitPayload]);
utils.respondToMessage(message, [true, undefined]);
}
return expect(promise).resolves.toBeUndefined();

await expect(promise).resolves.toBeUndefined();
});

it(`should throw error from host when called from context - ${frameContext}`, async () => {
expect.assertions(3);
await utils.initializeWithContext(frameContext);
Expand All @@ -80,25 +91,27 @@ describe('externalAppCardActionsForCEA', () => {
testConversationId,
testActionSubmitPayload,
);
const message = utils.findMessageByFunc('externalAppCardActionsForCEA.processActionSubmit');
const message = utils.findMessageByFunc(ApiName.ExternalAppCardActionsForCEA_ProcessActionSubmit);
if (message && message.args) {
expect(message).not.toBeNull();
expect(message.args).toEqual([testAppId, testConversationId, testActionSubmitPayload]);
utils.respondToMessage(message, false, testError);
}
return expect(promise).rejects.toEqual(testError);
await expect(promise).rejects.toEqual(testError);
});
} else {
it(`should not allow calls from ${frameContext} context`, async () => {
expect.assertions(1);
await utils.initializeWithContext(frameContext);
utils.setRuntimeConfig({ apiVersion: 2, supports: { externalAppCardActionsForCEA: {} } });
return expect(() =>

await expect(
externalAppCardActionsForCEA.processActionSubmit(testAppId, testConversationId, testActionSubmitPayload),
).toThrowError(
).rejects.toThrowError(
new Error(
`This call is only allowed in following contexts: ${JSON.stringify(allowedFrameContexts)}. ` +
`Current context: "${frameContext}".`,
`This call is only allowed in following contexts: ${JSON.stringify(
allowedFrameContexts,
)}. Current context: "${frameContext}".`,
),
);
});
Expand All @@ -114,60 +127,76 @@ describe('externalAppCardActionsForCEA', () => {
message: 'testMessage',
};
const testResponse = ActionOpenUrlType.DeepLinkDialog;

it('should not allow calls before initialization', async () => {
return expect(() =>
externalAppCardActionsForCEA.processActionOpenUrl(testAppId, testConversationId, testUrl),
).toThrowError(new Error(errorLibraryNotInitialized));
expect.assertions(1);
try {
await externalAppCardActionsForCEA.processActionOpenUrl(testAppId, testConversationId, testUrl);
} catch (e) {
expect(e).toEqual(new Error(errorLibraryNotInitialized));
}
});

it('should throw error when externalAppCardActionsForCEA capability is not supported', async () => {
expect.assertions(1);
await utils.initializeWithContext(FrameContexts.content);
utils.setRuntimeConfig({ apiVersion: 2, supports: {} });

try {
await externalAppCardActionsForCEA.processActionOpenUrl(testAppId, testConversationId, testUrl);
} catch (e) {
expect(e).toEqual(errorNotSupportedOnPlatform);
}
});

Object.values(FrameContexts).forEach((frameContext) => {
if (allowedFrameContexts.includes(frameContext)) {
it(`should resolve when called from context - ${frameContext}`, async () => {
expect.assertions(3);
await utils.initializeWithContext(frameContext);
utils.setRuntimeConfig({ apiVersion: 2, supports: { externalAppCardActionsForCEA: {} } });

const promise = externalAppCardActionsForCEA.processActionOpenUrl(testAppId, testConversationId, testUrl);
const message = utils.findMessageByFunc('externalAppCardActionsForCEA.processActionOpenUrl');

const message = utils.findMessageByFunc(ApiName.ExternalAppCardActionsForCEA_ProcessActionOpenUrl);
if (message && message.args) {
expect(message).not.toBeNull();
expect(message.args).toEqual([testAppId, testUrl.href, testConversationId]);
utils.respondToMessage(message, null, testResponse);
}
return expect(promise).resolves.toEqual(testResponse);

await expect(promise).resolves.toEqual(testResponse);
});

it(`should throw error from host when called from context - ${frameContext}`, async () => {
expect.assertions(3);
await utils.initializeWithContext(frameContext);
utils.setRuntimeConfig({ apiVersion: 2, supports: { externalAppCardActionsForCEA: {} } });

const promise = externalAppCardActionsForCEA.processActionOpenUrl(testAppId, testConversationId, testUrl);

const message = utils.findMessageByFunc('externalAppCardActionsForCEA.processActionOpenUrl');
if (message && message.args) {
expect(message).not.toBeNull();
expect(message.args).toEqual([testAppId, testUrl.href, testConversationId]);
utils.respondToMessage(message, testError, null);
}
return expect(promise).rejects.toEqual(testError);

await expect(promise).rejects.toEqual(testError);
});
} else {
it(`should not allow calls from ${frameContext} context`, async () => {
expect.assertions(1);
await utils.initializeWithContext(frameContext);
utils.setRuntimeConfig({ apiVersion: 2, supports: { externalAppCardActionsForCEA: {} } });
return expect(() =>

await expect(
externalAppCardActionsForCEA.processActionOpenUrl(testAppId, testConversationId, testUrl),
).toThrowError(
).rejects.toThrowError(
new Error(
`This call is only allowed in following contexts: ${JSON.stringify(allowedFrameContexts)}. ` +
`Current context: "${frameContext}".`,
`This call is only allowed in following contexts: ${JSON.stringify(
allowedFrameContexts,
)}. Current context: "${frameContext}".`,
),
);
});
Expand Down

0 comments on commit 2c71f5c

Please sign in to comment.