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

fix: avoid cross-geo calls #9613

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions Composer/packages/electron-server/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,11 @@ async function run() {
};
});

ipcMain.handle('deeplink', async (evt, url: string) => {
const deeplink = parseDeepLinkUrl(url);
await getMainWindow()?.webContents.loadURL(getBaseUrl() + deeplink);
});

await main();
setTimeout(() => startApp(signalThatMainWindowIsShowing), 500);
await initApp();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,19 @@ export const PVA_GOV_APP_ID = '9315aedd-209b-43b3-b149-2abff6a95d59';
export const PVA_GCC_HIGH_APP_ID = '69c6e40c-465f-4154-987d-da5cba10734e';

export type PowerVirtualAgentsMetadata = IContentProviderMetadata & {
clusterCategory?:
| 'Dev'
| 'Prv'
| 'Test'
| 'Preprod'
| 'FirstRelease'
| 'Prod'
| 'Gov'
| 'High'
| 'DoD'
| 'Mooncake'
| 'Ex'
| 'Rx';
baseUrl: string;
botId: string;
dialogId?: string;
Expand All @@ -32,22 +45,35 @@ export type PowerVirtualAgentsMetadata = IContentProviderMetadata & {
};

const getAuthCredentials = (baseUrl: string, metadata: PowerVirtualAgentsMetadata) => {
const clusterCategory =
(process.env.COMPOSER_PVA_CLUSTER as typeof metadata.clusterCategory) ?? metadata.clusterCategory;
const url = new URL(baseUrl);
if (url.hostname.includes('.int.') || url.hostname.includes('.ppe.')) {
if (
(clusterCategory && ['Test', 'Preprod', 'Dev'].includes(clusterCategory)) ||
url.hostname.includes('.int.') ||
url.hostname.includes('.ppe.') ||
url.hostname.includes('.test.')
) {
log('Using INT / PPE auth credentials.');
return {
clientId: COMPOSER_1P_APP_ID,
scopes: [`${PVA_TEST_APP_ID}/.default`],
targetResource: PVA_TEST_APP_ID,
};
} else if (url.hostname.includes('gcc.api.powerva.microsoft.us')) {
} else if (
(clusterCategory && ['Gov'].includes(clusterCategory)) ||
url.hostname.includes('gcc.api.powerva.microsoft.us')
) {
log('Using GCC auth credentials.');
return {
clientId: COMPOSER_1P_APP_ID,
scopes: [`${PVA_GOV_APP_ID}/.default`],
targetResource: PVA_GOV_APP_ID,
};
} else if (url.hostname.includes('high.api.powerva.microsoft.us')) {
} else if (
(clusterCategory && ['High'].includes(clusterCategory)) ||
url.hostname.includes('high.api.powerva.microsoft.us')
) {
log('Using GCC High auth credentials.');
return {
authority: `https://login.microsoftonline.us/${metadata.tenantId}`,
Expand All @@ -56,55 +82,14 @@ const getAuthCredentials = (baseUrl: string, metadata: PowerVirtualAgentsMetadat
targetResource: PVA_GCC_HIGH_APP_ID,
};
}
log('Using PROD auth credentials.');
log(`Using PROD auth credentials.\nCategory: ${clusterCategory}\nURL: ${baseUrl}`);
return {
clientId: COMPOSER_1P_APP_ID,
scopes: [`${PVA_PROD_APP_ID}/.default`],
targetResource: PVA_PROD_APP_ID,
};
};

const getBaseUrl = () => {
const pvaEnv = (process.env.COMPOSER_PVA_ENV || '').toLowerCase();
switch (pvaEnv) {
case 'prod': {
const url = 'https://powerva.microsoft.com/api/botmanagement/v1';
log('PROD env detected, grabbing PVA content from %s', url);
return url;
}

case 'ppe': {
const url = 'https://bots.ppe.customercareintelligence.net/api/botmanagement/v1';
log('PPE env detected, grabbing PVA content from %s', url);
return url;
}

case 'int': {
const url = 'https://bots.int.customercareintelligence.net/api/botmanagement/v1';
log('INT env detected, grabbing PVA content from %s', url);
return url;
}

case 'gcc': {
const url = 'https://gcc.api.powerva.microsoft.us/api/botmanagement/v1';
log('GCC env detected, grabbing PVA content from %s', url);
return url;
}

case 'gcc-high': {
const url = 'https://high.api.powerva.microsoft.us/api/botmanagement/v1';
log('GCC High env detected, grabbing PVA content from %s', url);
return url;
}

default: {
const url = 'https://bots.int.customercareintelligence.net/api/botmanagement/v1';
log('No env flag detected, grabbing PVA content from %s', url);
return url;
}
}
};

function prettyPrintError(err: string | Error): string {
if (typeof err === 'string') {
return err;
Expand Down Expand Up @@ -177,7 +162,7 @@ export class PowerVirtualAgentsProvider extends ExternalContentProvider<PowerVir
try {
// login to the 1P app and get an access token
const { baseUrl } = this.metadata;
const authCredentials = getAuthCredentials(baseUrl || getBaseUrl(), this.metadata);
const authCredentials = getAuthCredentials(baseUrl, this.metadata);
const accessToken = await authService.getAccessToken(authCredentials);
if (accessToken === '') {
throw 'User cancelled login flow.';
Expand All @@ -190,7 +175,7 @@ export class PowerVirtualAgentsProvider extends ExternalContentProvider<PowerVir

private getContentUrl(): string {
const { envId, baseUrl, botId } = this.metadata;
return `${baseUrl || getBaseUrl()}/environments/${envId}/bots/${botId}/composer/content?includeTopics=true`;
return `${baseUrl}/environments/${envId}/bots/${botId}/composer/content?includeTopics=true`;
}

private async getRequestHeaders() {
Expand All @@ -206,7 +191,7 @@ export class PowerVirtualAgentsProvider extends ExternalContentProvider<PowerVir
private getDeepLink(): string {
// use metadata (if provided) to create a deep link to a specific dialog / trigger / action etc. after opening bot.
let deepLink = '';
const { dialogId, triggerId, actionId = '' } = this.metadata;
const { dialogId, triggerId, actionId = '', clusterCategory } = this.metadata;

if (dialogId) {
deepLink += `dialogs/${dialogId}`;
Expand All @@ -219,6 +204,9 @@ export class PowerVirtualAgentsProvider extends ExternalContentProvider<PowerVir
`"${actionId}"`
)}]`;
}
if (clusterCategory) {
deepLink += deepLink.includes('?') ? '&' : '?' + `cluster=${clusterCategory}`;
}
// base64 encode to make parsing on the client side easier
return Buffer.from(deepLink, 'utf-8').toString('base64');
}
Expand Down
13 changes: 0 additions & 13 deletions extensions/pvaPublish/src/node/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,6 @@ export const AUTH_CREDENTIALS = {
// electron auth flow
targetResource: 'a522f059-bb65-47c0-8934-7db6e5286414',
},
PPE: {
clientId: COMPOSER_1P_APP_ID,
scopes: ['a522f059-bb65-47c0-8934-7db6e5286414/.default'],
targetResource: 'a522f059-bb65-47c0-8934-7db6e5286414',
},
PROD: {
clientId: COMPOSER_1P_APP_ID,
scopes: ['96ff4394-9197-43aa-b393-6a41652e21f8/.default'],
Expand All @@ -36,11 +31,3 @@ export const AUTH_CREDENTIALS = {
targetResource: '69c6e40c-465f-4154-987d-da5cba10734e',
},
};

export const BASE_URLS = {
INT: 'https://bots.int.customercareintelligence.net/',
PPE: 'https://bots.ppe.customercareintelligence.net/',
PROD: 'https://powerva.microsoft.com/',
GCC: 'https://gcc.api.powerva.microsoft.us/',
GCC_HIGH: 'https://high.api.powerva.microsoft.us/',
};
28 changes: 19 additions & 9 deletions extensions/pvaPublish/src/node/publish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { RequestInit } from 'node-fetch';

import fetch from './fetch';
import { PVAPublishJob, PublishConfig, UserIdentity, PublishState, PublishHistory, PullResponse } from './types';
import { getAuthCredentials, getBaseUrl } from './utils';
import { getAuthCredentials } from './utils';
import { logger } from './logger';
import { API_VERSION } from './constants';

Expand All @@ -35,14 +35,18 @@ export const publish = async (
envId,
tenantId,
deleteMissingDependencies = false, // publish behavior
clusterCategory,
} = config;
const { comment = '' } = metadata;

try {
logger.log('Starting publish to Power Virtual Agents.');
// authenticate with PVA
const base = baseUrl || getBaseUrl();
const creds = getAuthCredentials(base, tenantId);
const base = baseUrl;
if (!base) {
throw new Error('Base URL is not supplied in published target');
}
const creds = getAuthCredentials(base, tenantId, clusterCategory);
const accessToken = await getAccessToken(creds);

// write the .zip to a buffer in memory
Expand Down Expand Up @@ -145,6 +149,7 @@ export const getStatus = async (
botId,
envId,
tenantId,
clusterCategory,
} = config;
const botProjectId = project.id || '';

Expand All @@ -161,8 +166,11 @@ export const getStatus = async (

try {
// authenticate with PVA
const base = baseUrl || getBaseUrl();
const creds = getAuthCredentials(base, tenantId);
const base = baseUrl;
if (!base) {
throw new Error('Base URL is not supplied in published target');
}
const creds = getAuthCredentials(base, tenantId, clusterCategory);
const accessToken = await getAccessToken(creds);

// check the status for the publish job
Expand Down Expand Up @@ -218,12 +226,13 @@ export const history = async (
botId,
envId,
tenantId,
clusterCategory,
} = config;

try {
// authenticate with PVA
const base = baseUrl || getBaseUrl();
const creds = getAuthCredentials(base, tenantId);
const base = baseUrl;
const creds = getAuthCredentials(base, tenantId, clusterCategory);
const accessToken = await getAccessToken(creds);

// get the publish history for the bot
Expand Down Expand Up @@ -253,11 +262,12 @@ export const pull = async (
botId,
envId,
tenantId,
clusterCategory,
} = config;
try {
// authenticate with PVA
const base = baseUrl || getBaseUrl();
const creds = getAuthCredentials(base, tenantId);
const base = baseUrl;
const creds = getAuthCredentials(base, tenantId, clusterCategory);
const accessToken = await getAccessToken(creds);

// fetch zip containing bot content
Expand Down
15 changes: 15 additions & 0 deletions extensions/pvaPublish/src/node/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,20 @@

import { PublishResult } from '@botframework-composer/types';

export type ClusterCategory =
| 'Dev'
| 'Prv'
| 'Test'
| 'Preprod'
| 'FirstRelease'
| 'Prod'
| 'Gov'
| 'High'
| 'DoD'
| 'Mooncake'
| 'Ex'
| 'Rx';

export type PVAPublishJob = {
comment: string;
diagnostics: DiagnosticInfo[];
Expand Down Expand Up @@ -38,6 +52,7 @@ export type PublishConfig = {
fullSettings: any;
profileName: string;
tenantId: string;
clusterCategory: ClusterCategory;
};

export type PublishState =
Expand Down
50 changes: 3 additions & 47 deletions extensions/pvaPublish/src/node/utils.spec.ts
Original file line number Diff line number Diff line change
@@ -1,52 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import { AUTH_CREDENTIALS, BASE_URLS } from './constants';
import { getAuthCredentials, getBaseUrl } from './utils';

describe('should return the proper PVA base URL for the environment', () => {
let envBackup;
beforeAll(() => {
envBackup = { ...process.env };
});

beforeEach(() => {
Object.assign(process.env, { COMPOSER_PVA_PUBLISH_ENV: '' });
});

afterAll(() => {
Object.assign(process.env, envBackup); // restore the platform
});

it('fallback to prod', () => {
expect(getBaseUrl()).toBe(BASE_URLS.PROD);
});

it('int', () => {
Object.assign(process.env, { COMPOSER_PVA_PUBLISH_ENV: 'INT' });
expect(getBaseUrl()).toBe(BASE_URLS.INT);
});

it('ppe', () => {
Object.assign(process.env, { COMPOSER_PVA_PUBLISH_ENV: 'PPE' });
expect(getBaseUrl()).toBe(BASE_URLS.PPE);
});

it('prod', () => {
Object.assign(process.env, { COMPOSER_PVA_PUBLISH_ENV: 'PROD' });
expect(getBaseUrl()).toBe(BASE_URLS.PROD);
});

it('gcc', () => {
Object.assign(process.env, { COMPOSER_PVA_PUBLISH_ENV: 'GCC' });
expect(getBaseUrl()).toBe(BASE_URLS.GCC);
});

it('gcc high', () => {
Object.assign(process.env, { COMPOSER_PVA_PUBLISH_ENV: 'GCC-HIGH' });
expect(getBaseUrl()).toBe(BASE_URLS.GCC_HIGH);
});
});
import { AUTH_CREDENTIALS } from './constants';
import { getAuthCredentials } from './utils';

describe('it should return the proper PVA auth parameters for the base URL', () => {
it('fallback to prod', () => {
Expand All @@ -64,7 +20,7 @@ describe('it should return the proper PVA auth parameters for the base URL', ()

it('ppe', () => {
const url = 'https://bots.ppe.customercareintelligence.net/api/botmanagement/v1';
expect(getAuthCredentials(url)).toEqual(AUTH_CREDENTIALS.PPE);
expect(getAuthCredentials(url)).toEqual(AUTH_CREDENTIALS.INT);
});

it('prod', () => {
Expand Down
Loading