diff --git a/apps/teams-test-app/src/App.tsx b/apps/teams-test-app/src/App.tsx index 052b3815e5..4a2f44ebde 100644 --- a/apps/teams-test-app/src/App.tsx +++ b/apps/teams-test-app/src/App.tsx @@ -21,6 +21,10 @@ if (!urlParams.has('customInit') || !urlParams.get('customInit')) { if (isTestBackCompat()) { initialize(undefined, validMessageOrigins); } else { + app.getHostName().then((hostName) => { + console.log('%cHost Name: ', 'background-color: blue', hostName); + }); + console.log('App Initialization'); app.initialize(validMessageOrigins); } } diff --git a/change/@microsoft-teams-js-1ad0d077-794c-4213-aa67-eed6b8c3ec75.json b/change/@microsoft-teams-js-1ad0d077-794c-4213-aa67-eed6b8c3ec75.json new file mode 100644 index 0000000000..dd746c5ab0 --- /dev/null +++ b/change/@microsoft-teams-js-1ad0d077-794c-4213-aa67-eed6b8c3ec75.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "Added a small capability which returns the host name where the app is hosted.", + "packageName": "@microsoft/teams-js", + "email": "shrshinde@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/packages/teams-js/src/internal/communication.ts b/packages/teams-js/src/internal/communication.ts index efe3b5ba91..4b76622bf1 100644 --- a/packages/teams-js/src/internal/communication.ts +++ b/packages/teams-js/src/internal/communication.ts @@ -79,13 +79,9 @@ interface InitializeResponse { } /** - * @internal - * Limited to Microsoft-internal use + * set windows for communication */ -export function initializeCommunication( - validMessageOrigins: string[] | undefined, - apiVersionTag: string, -): Promise { +function setWindows(validMessageOrigins: string[] | undefined): void { // Listen for messages post to our window CommunicationPrivate.messageListener = async (evt: DOMMessageEvent): Promise => await processMessage(evt); @@ -111,10 +107,20 @@ export function initializeCommunication( extendedWindow.onNativeMessage = handleParentMessage; } else { // at this point we weren't able to find a parent to talk to, no way initialization will succeed - return Promise.reject(new Error('Initialization Failed. No Parent window found.')); + throw new Error('Initialization Failed. No Parent window found.'); } } +} +/** + * @internal + * Limited to Microsoft-internal use + */ +export function initializeCommunication( + validMessageOrigins: string[] | undefined, + apiVersionTag: string, +): Promise { + setWindows(validMessageOrigins); try { // Send the initialized message to any origin, because at this point we most likely don't know the origin // of the parent window, and this message contains no data that could pose a security risk. @@ -373,6 +379,33 @@ export function sendNestedAuthRequestToTopWindow(message: string): NestedAppAuth return sendRequestToTargetWindowHelper(targetWindow, request) as NestedAppAuthRequest; } +/** + * @internal + * Limited to Microsoft-internal use + */ +export function sendAndGetHostName(apiVersionTag: string): Promise { + const request = [ + { + id: CommunicationPrivate.nextMessageId++, + timestamp: Date.now(), + func: 'getHostName', + }, + ]; + try { + setWindows(undefined); + Communication.parentOrigin = '*'; + return sendMessageToParentAsync(apiVersionTag, 'getHostName', request); + } catch (error) { + if (error.message === 'Initialization Failed. No Parent window found.') { + return Promise.resolve('App is not running inside iframe.'); + } else { + return Promise.reject(error); + } + } finally { + Communication.parentOrigin = null; + } +} + const sendRequestToTargetWindowHelperLogger = communicationLogger.extend('sendRequestToTargetWindowHelper'); /** @@ -394,7 +427,6 @@ function sendRequestToTargetWindowHelper( } } else { const targetOrigin = getTargetOrigin(targetWindow); - // If the target window isn't closed and we already know its origin, send the message right away; otherwise, // queue the message and send it after the origin is established if (targetWindow && targetOrigin) { diff --git a/packages/teams-js/src/internal/telemetry.ts b/packages/teams-js/src/internal/telemetry.ts index d7049ec8d5..5341ec8b25 100644 --- a/packages/teams-js/src/internal/telemetry.ts +++ b/packages/teams-js/src/internal/telemetry.ts @@ -52,6 +52,7 @@ export const enum ApiVersionNumber { export const enum ApiName { App_GetContext = 'app.getContext', + App_GetHostName = 'app.getHostName', App_Initialize = 'app.initialize', App_NotifyAppLoaded = 'app.notifyAppLoaded', App_NotifyExpectedFailure = 'app.notifyExpectedFailure', diff --git a/packages/teams-js/src/public/app.ts b/packages/teams-js/src/public/app.ts index 0b32527806..acd2b60bae 100644 --- a/packages/teams-js/src/public/app.ts +++ b/packages/teams-js/src/public/app.ts @@ -5,9 +5,11 @@ import { Communication, initializeCommunication, + sendAndGetHostName, sendAndHandleStatusAndReason, sendAndUnwrap, sendMessageToParent, + sendMessageToParentAsync, uninitializeCommunication, } from '../internal/communication'; import { defaultSDKVersionForCompatCheck } from '../internal/constants'; @@ -731,6 +733,22 @@ export namespace app { return GlobalVars.initializeCompleted; } + /** + * Gets the host name where the app is running. If the app is not running in a hosted environment, + * it will return `App is not running inside iframe.` message or error message. + * @returns A promise that resolves to the host name. + */ + export async function getHostName(): Promise { + if (GlobalVars.initializeCompleted) { + return await sendMessageToParentAsync( + getApiVersionTag(appTelemetryVersionNumber, ApiName.App_GetHostName), + 'getHostName', + ); + } else { + return await sendAndGetHostName(getApiVersionTag(appTelemetryVersionNumber, ApiName.App_GetHostName)); + } + } + /** * Gets the Frame Context that the App is running in. See {@link FrameContexts} for the list of possible values. * @returns the Frame Context. diff --git a/packages/teams-js/test/internal/communication.spec.ts b/packages/teams-js/test/internal/communication.spec.ts index 4d15db4b7b..11e1873920 100644 --- a/packages/teams-js/test/internal/communication.spec.ts +++ b/packages/teams-js/test/internal/communication.spec.ts @@ -29,10 +29,11 @@ describe('Testing communication', () => { GlobalVars.isFramelessWindow = false; }); - it('should throw if there is no parent window and no native interface on the current window', async () => { + it('should throw if there is no parent window and no native interface on the current window', () => { app._initialize(undefined); - const initPromise = communication.initializeCommunication(undefined, testApiVersion); - await expect(initPromise).rejects.toThrowError('Initialization Failed. No Parent window found.'); + expect(() => communication.initializeCommunication(undefined, testApiVersion)).toThrowError( + 'Initialization Failed. No Parent window found.', + ); }); it('should receive valid initialize response from parent when there is no parent window but the window has a native interface', async () => { @@ -206,8 +207,9 @@ describe('Testing communication', () => { // In this case, because Communication.currentWindow is being initialized to undefined we fall back to the actual // window object created by jest, which does not have nativeInterface defined on it app._initialize(undefined); - const initPromise = communication.initializeCommunication(undefined, testApiVersion); - await expect(initPromise).rejects.toThrowError('Initialization Failed. No Parent window found.'); + expect(() => communication.initializeCommunication(undefined, testApiVersion)).toThrowError( + 'Initialization Failed. No Parent window found.', + ); }); it('should receive valid initialize response from parent when currentWindow has a parent with postMessage defined', async () => {