Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Open App Store & Notify App Install Completed Capability #2626

Merged
merged 41 commits into from
Nov 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
e701069
enable open app store experience and add unit test file
Charlz-hang Oct 25, 2024
f26df0a
changefile
YinHang2515 Oct 25, 2024
4369b13
Merge branch 'main' of https://github.com/YinHang2515/microsoft-teams…
YinHang2515 Oct 25, 2024
7241ad4
update unit tes file
YinHang2515 Oct 25, 2024
ab74890
update teams-test-app StoreApis
YinHang2515 Oct 25, 2024
3d54a7d
1. eliminate some parameters of openStoreExperience & fix by comment
YinHang2515 Oct 29, 2024
d22e192
update isSupported
YinHang2515 Oct 29, 2024
6460cd5
otherAppStateChange file change migration @zhitao
YinHang2515 Oct 29, 2024
26d7ec1
Merge branch 'main' of https://github.com/YinHang2515/microsoft-teams…
YinHang2515 Oct 29, 2024
d6162a9
Merge branch 'main' into user/hangyin/openstore
TrevorJoelHarris Oct 31, 2024
771e269
Merge branch 'main' into user/hangyin/openstore
YinHang2515 Nov 1, 2024
c8a0c88
Update change/@microsoft-teams-js-07829a41-a760-48a1-a793-df938ea61c1…
YinHang2515 Nov 3, 2024
a237e02
Update packages/teams-js/src/private/store.ts
YinHang2515 Nov 5, 2024
480da43
Update packages/teams-js/src/private/store.ts
YinHang2515 Nov 5, 2024
4bfecf9
add unit test for notifyInstall & update error thrown code
YinHang2515 Nov 5, 2024
c6e2840
Merge branch 'user/hangyin/openstore' of https://github.com/YinHang25…
YinHang2515 Nov 5, 2024
9c115af
append 'hostSdkVersion' parameter in e2e test data
YinHang2515 Nov 5, 2024
16bf34c
Merge branch 'OfficeDev:main' into user/hangyin/openstore
YinHang2515 Nov 5, 2024
9a8a3c1
Update packages/teams-js/src/private/store.ts
TrevorJoelHarris Nov 6, 2024
9c5c143
Update packages/teams-js/src/private/store.ts
YinHang2515 Nov 7, 2024
3b77978
update e2e test
YinHang2515 Nov 8, 2024
a348f72
update e2e test for notifyAppInstall
YinHang2515 Nov 10, 2024
4fea1c6
Merge branch 'main' of https://github.com/YinHang2515/microsoft-teams…
YinHang2515 Nov 10, 2024
4b38dd6
add collectionId parameter & modify related test files
YinHang2515 Nov 13, 2024
796984a
add new error message for invalid dialog type & update test file
YinHang2515 Nov 14, 2024
44e67a8
try catch error in test app
YinHang2515 Nov 14, 2024
4c2ed6e
Merge branch 'main' of https://github.com/YinHang2515/microsoft-teams…
YinHang2515 Nov 14, 2024
2d7ee17
Merge branch 'main' into user/hangyin/openstore
TrevorJoelHarris Nov 14, 2024
eccfeb3
parse open store params json to hub sdk to eliminate redundant knowle…
YinHang2515 Nov 15, 2024
7cf5baf
Merge branch 'user/hangyin/openstore' of https://github.com/YinHang25…
YinHang2515 Nov 15, 2024
07711ef
update e2e test file
YinHang2515 Nov 15, 2024
59bff47
rename notifyAppInstall with notifyInstallCompleted
YinHang2515 Nov 15, 2024
9e9e396
update name
YinHang2515 Nov 15, 2024
ba2d407
Merge branch 'main' into user/hangyin/openstore
TrevorJoelHarris Nov 15, 2024
01d5cfc
parse parameters to hub sdk
YinHang2515 Nov 16, 2024
af61aba
Merge branch 'user/hangyin/openstore' of https://github.com/YinHang25…
YinHang2515 Nov 16, 2024
32eb34b
add test cases to store e2e & update notify install title
YinHang2515 Nov 19, 2024
7b7924c
update store e2e
YinHang2515 Nov 19, 2024
f67f2d3
Merge branch 'main' into user/hangyin/openstore
YinHang2515 Nov 20, 2024
f238ddb
Merge branch 'main' into user/hangyin/openstore
TrevorJoelHarris Nov 20, 2024
de9401c
Merge branch 'main' into user/hangyin/openstore
TrevorJoelHarris Nov 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions apps/teams-test-app/e2e-test-data/otherAppStateChange.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,22 @@
"type": "registerAndRaiseEvent",
"boxSelector": "#box_otherAppStateChange_registerInstallHandler",
"eventName": "otherApp.install",
"eventData":
{
"appIds": ["123", "456"]
},
"eventData": {
"appIds": ["123", "456"]
},
"expectedTestAppValue": "received"
},
{
"title": "unregisterAppInstallationHandler - Sends but not processed if app is not approved",
"type": "callResponse",
"boxSelector": "#box_otherAppStateChange_unregisterInstallHandler",
"expectedTestAppValue": "received"
},
{
"title": "notifyInstallCompleted - Success",
"type": "callResponse",
"boxSelector": "#box_otherAppStateChange_notifyInstallCompleted",
"expectedTestAppValue": "notified"
}
]
}
118 changes: 118 additions & 0 deletions apps/teams-test-app/e2e-test-data/store.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
{
"name": "Store",
"platforms": "Web",
"checkIsSupported": {
"domElementName": "checkCapabilityStore"
},
"version": ">2.31.0",
"hostSdkVersion": {
"web": ">4.5.0"
},
"testCases": [
{
"title": "openStoreExperience API Call Full Store - Success",
"type": "callResponse",
"boxSelector": "#box_storeOpen",
"inputValue": {
"dialogType": "fullstore"
},
"expectedAlertValue": "openStoreExperience called with ##JSON_INPUT_VALUE##"
},
{
"title": "openStoreExperience API Call In-Context-Store - Success",
"type": "callResponse",
"boxSelector": "#box_storeOpen",
"inputValue": {
"dialogType": "ics"
},
"expectedAlertValue": "openStoreExperience called with ##JSON_INPUT_VALUE##"
},
{
"title": "openStoreExperience API Call App Detail - Success",
"type": "callResponse",
"boxSelector": "#box_storeOpen",
"inputValue": {
"dialogType": "appdetail",
"appId": "1542629c-01b3-4a6d-8f76-1938b779e48d"
},
"expectedAlertValue": "openStoreExperience called with ##JSON_INPUT_VALUE##"
},
{
"title": "openStoreExperience API Call App Detail With Invalid AppId - Fail",
"type": "callResponse",
"boxSelector": "#box_storeOpen",
"inputValue": {
"dialogType": "appdetail",
"appId": "123"
},
"expectedTestAppValue": "Error: Error: Potential app id (123) is invalid; its length 3 is not within the length limits (4-256)."
},
{
"title": "openStoreExperience API Call App Detail With Empty AppId - Fail",
"type": "callResponse",
"boxSelector": "#box_storeOpen",
"inputValue": {
"dialogType": "appdetail",
"appId": ""
},
"expectedTestAppValue": "Error: Error: Potential app id () is invalid; its length 0 is not within the length limits (4-256)."
},
{
"title": "openStoreExperience API Call App Detail Without AppId - Fail",
"type": "callResponse",
"boxSelector": "#box_storeOpen",
"inputValue": {
"dialogType": "appdetail"
},
"expectedTestAppValue": "Error: Error: No App Id present, but AppId needed to open AppDetail store"
},
{
"title": "openStoreExperience API Call Specific Store - Success",
"type": "callResponse",
"boxSelector": "#box_storeOpen",
"inputValue": {
"dialogType": "specificstore",
"collectionId": "copilotplugins"
},
"expectedAlertValue": "openStoreExperience called with ##JSON_INPUT_VALUE##"
},
{
"title": "openStoreExperience API Call Specific Store With Empty CollectionId - Fail",
"type": "callResponse",
"boxSelector": "#box_storeOpen",
"inputValue": {
"dialogType": "specificstore",
"collectionId": ""
},
"expectedTestAppValue": "Error: Error: No Collection Id present, but CollectionId needed to open a store specific to a collection"
},
{
"title": "openStoreExperience API Call Specific Store Without CollectionId - Fail",
"type": "callResponse",
"boxSelector": "#box_storeOpen",
"inputValue": {
"dialogType": "specificstore"
},
"expectedTestAppValue": "Error: Error: No Collection Id present, but CollectionId needed to open a store specific to a collection"
},
{
"title": "openStoreExperience API Call With Invalid Dialog Type - Fail",
"type": "callResponse",
"boxSelector": "#box_storeOpen",
"inputValue": {
"dialogType": "123"
},
"expectedTestAppValue": "Error: Error: Invalid store dialog type, but type needed to specify store to open"
},
{
"title": "openStoreExperience API Call Without Permission - Fail",
"type": "callResponse",
"boxSelector": "#box_storeOpen",
"inputValue": {
"dialogType": "fullstore"
},
"testUrlParams": [["appDefOverrides", "{\"isFullTrustApp\": false, \"isMicrosoftOwned\": false}"]],
"expectedTestAppValue": "Error: Error: 500, message: App does not have the required permissions for this operation"
}
]
}
13 changes: 12 additions & 1 deletion apps/teams-test-app/src/components/OtherAppStateChangeAPIs.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { otherAppStateChange } from '@microsoft/teams-js';
import { AppId, otherAppStateChange } from '@microsoft/teams-js';
import React, { ReactElement } from 'react';

import { ApiWithoutInput } from './utils';
Expand Down Expand Up @@ -39,12 +39,23 @@ const UnregisterAppInstallHandler = (): React.ReactElement =>
},
});

const NotifyInstallCompletedHandler = (): React.ReactElement =>
ApiWithoutInput({
name: 'otherAppStateChange_notifyInstallCompleted',
title: 'Notify Install Completed',
onClick: async () => {
otherAppStateChange.notifyInstallCompleted(new AppId('12345'));
return 'notified';
},
});

const OtherAppStateChangedAPIs = (): ReactElement => (
<>
<ModuleWrapper title="OtherAppStateChanged">
<CheckOtherAppStateChangeCapability />
<RegisterAppInstallHandler />
<UnregisterAppInstallHandler />
<NotifyInstallCompletedHandler />
</ModuleWrapper>
</>
);
Expand Down
61 changes: 61 additions & 0 deletions apps/teams-test-app/src/components/StoreApis.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { AppId, store } from '@microsoft/teams-js';
import { ReactElement } from 'react';
import React from 'react';

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

const StoreAPIs = (): ReactElement => {
const CheckStoreCapability = (): ReactElement =>
ApiWithoutInput({
name: 'checkCapabilityStore',
title: 'Check Capability Store',
onClick: async () => {
if (store.isSupported()) {
return 'Store module is supported';
} else {
return 'Store module is not supported';
}
},
});

const OpenStore = (): ReactElement =>
ApiWithTextInput<{ dialogType: string; appId?: string; collectionId?: string }>({
name: 'storeOpen',
title: 'Store Open',
onClick: {
validateInput: (input) => {
if (input?.dialogType === undefined) {
throw new Error('store type undefined');
}
},
submit: async (input) => {
const appId = input.appId === undefined ? undefined : new AppId(input.appId);
const openStoreParam = {
dialogType: input.dialogType,
appId: appId,
collectionId: input.collectionId,
};
// eslint-disable-next-line no-useless-catch
try {
await store.openStoreExperience(openStoreParam as store.OpenStoreParams);
return 'store opened';
} catch (e) {
throw e;
}
},
},
defaultInput: JSON.stringify({
dialogType: 'appdetail',
appId: '1542629c-01b3-4a6d-8f76-1938b779e48d',
}),
});
return (
<ModuleWrapper title="Store">
<CheckStoreCapability />
<OpenStore />
</ModuleWrapper>
);
};

export default StoreAPIs;
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 @@ -60,6 +60,7 @@ import SecondaryBrowserAPIs from '../components/SecondaryBrowserAPIs';
import SharingAPIs from '../components/SharingAPIs';
import StageViewAPIs from '../components/StageViewAPIs';
import StageViewSelfAPIs from '../components/StageViewSelfAPIs';
import StoreAPIs from '../components/StoreApis';
import TeamsCoreAPIs from '../components/TeamsCoreAPIs';
import ThirdPartyCloudStorageAPIs from '../components/ThirdPartyCloudStorageAPIs';
import CookieAccessComponent from '../components/ThirdPatryCookies';
Expand Down Expand Up @@ -163,6 +164,7 @@ export const TestApp: React.FC = () => {
{ name: 'VideoAPIs', component: <VideoAPIs /> },
{ name: 'VideoExAPIs', component: <VideoExAPIs /> },
{ name: 'VisualMediaAPIs', component: <VisualMediaAPIs /> },
{ name: 'StoreAPIs', component: <StoreAPIs /> },
],
[],
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "minor",
"comment": "Added `store` capability that will enable user to open several types of app store dialogs.. The capability is still awaiting support in one or most host applications. To track availability of this capability across different hosts see https://aka.ms/capmatrix",
"packageName": "@microsoft/teams-js",
"email": "yt6520143@163.com",
"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 @@ -233,6 +233,7 @@ export const enum ApiName {
Notifications_ShowNotification = 'notifications.showNotification',
OtherAppStateChange_Install = 'otherApp.install',
OtherAppStateChange_UnregisterInstall = 'otherApp.unregisterInstall',
OtherAppStateChange_NotifyInstallCompleted = 'otherApp.notifyInstallCompleted',
Pages_AppButton_OnClick = 'pages.appButton.onClick',
Pages_AppButton_OnHoverEnter = 'pages.appButton.onHoverEnter',
Pages_AppButton_OnHoverLeave = 'pages.appButton.onHoverLeave',
Expand Down Expand Up @@ -322,6 +323,7 @@ export const enum ApiName {
Sharing_ShareWebContent = 'sharing.shareWebContent',
StageView_Open = 'stageView.open',
StageView_Self_Close = 'stageView.self.close',
Store_Open = 'store.open',
Tasks_StartTask = 'tasks.startTask',
Tasks_SubmitTask = 'tasks.submitTask',
Tasks_UpdateTask = 'tasks.updateTask',
Expand Down
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 @@ -38,3 +38,4 @@ export * as appEntity from './appEntity';
export * as teams from './teams/teams';
export * as videoEffectsEx from './videoEffectsEx';
export * as hostEntity from './hostEntity/hostEntity';
export * as store from './store';
27 changes: 26 additions & 1 deletion packages/teams-js/src/private/otherAppStateChange.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@
* when another application has been installed.
*/

import { sendMessageToParent } from '../internal/communication';
import { callFunctionInHost, sendMessageToParent } from '../internal/communication';
import { registerHandler, removeHandler } from '../internal/handlers';
import { ensureInitialized } from '../internal/internalAPIs';
import { ApiName, ApiVersionNumber, getApiVersionTag } from '../internal/telemetry';
import { isNullOrUndefined } from '../internal/typeCheckUtilities';
import { AppId } from '../public/appId';
import { ErrorCode } from '../public/interfaces';
import { runtime } from '../public/runtime';

Expand Down Expand Up @@ -112,6 +113,30 @@ export function unregisterAppInstallationHandler(): void {
removeHandler(ApiName.OtherAppStateChange_Install);
}

/**
* @hidden
* @beta
* @internal
* Limited to Microsoft-internal use
*
* This function should be called by the Store App to notify the host that the
* app with the given appId has been installed.
*
* @throws Error if {@link app.initialize} has not successfully completed or if the platform
* does not support the otherAppStateChange capability.
*/
export function notifyInstallCompleted(appId: AppId): Promise<void> {
if (!isSupported()) {
throw new Error(ErrorCode.NOT_SUPPORTED_ON_PLATFORM.toString());
}

return callFunctionInHost(
ApiName.OtherAppStateChange_NotifyInstallCompleted,
[appId.toString()],
getApiVersionTag(otherAppStateChangeTelemetryVersionNumber, ApiName.OtherAppStateChange_NotifyInstallCompleted),
);
}

/**
* Checks if the otherAppStateChange capability is supported by the host
* @returns boolean to represent whether the otherAppStateChange capability is supported
Expand Down
Loading
Loading