From 9c1bbcdca7eba0e98bc5687edf03520ee167356b Mon Sep 17 00:00:00 2001
From: amitjoshi <amitjoshi@microsoft.com>
Date: Wed, 20 Nov 2024 15:47:59 +0530
Subject: [PATCH] Add environment list retrieval and refactor BAP endpoint
 logic

---
 src/common/services/BAPService.ts         | 29 ++-------
 src/common/services/Constants.ts          |  1 +
 src/common/services/TelemetryConstants.ts |  2 +
 src/common/utilities/Utils.ts             | 73 +++++++++++++++++++++++
 4 files changed, 81 insertions(+), 24 deletions(-)

diff --git a/src/common/services/BAPService.ts b/src/common/services/BAPService.ts
index fb26251aa..bcfb51310 100644
--- a/src/common/services/BAPService.ts
+++ b/src/common/services/BAPService.ts
@@ -5,9 +5,10 @@
 
 import { ITelemetry } from "../OneDSLoggerTelemetry/telemetry/ITelemetry";
 import { bapServiceAuthentication, getCommonHeaders } from "./AuthenticationProvider";
-import { VSCODE_EXTENSION_GET_BAP_ENDPOINT_UNSUPPORTED_REGION, VSCODE_EXTENSION_GET_CROSS_GEO_DATA_MOVEMENT_ENABLED_FLAG_COMPLETED, VSCODE_EXTENSION_GET_CROSS_GEO_DATA_MOVEMENT_ENABLED_FLAG_FAILED } from "./TelemetryConstants";
+import { VSCODE_EXTENSION_GET_CROSS_GEO_DATA_MOVEMENT_ENABLED_FLAG_COMPLETED, VSCODE_EXTENSION_GET_CROSS_GEO_DATA_MOVEMENT_ENABLED_FLAG_FAILED } from "./TelemetryConstants";
 import { ServiceEndpointCategory, BAP_API_VERSION, BAP_SERVICE_COPILOT_CROSS_GEO_FLAG_RELATIVE_URL, BAP_SERVICE_ENDPOINT } from "./Constants";
 import { sendTelemetryEvent } from "../copilot/telemetry/copilotTelemetry";
+import { getBAPEndpoint } from "../utilities/Utils";
 
 export class BAPService {
     public static async getCrossGeoCopilotDataMovementEnabledFlag(serviceEndpointStamp: ServiceEndpointCategory, telemetry: ITelemetry, environmentId: string): Promise<boolean> {
@@ -15,7 +16,7 @@ export class BAPService {
         try {
             const accessToken = await bapServiceAuthentication(telemetry, true);
 
-            const response = await fetch(await BAPService.getBAPEndpoint(serviceEndpointStamp, telemetry, environmentId), {
+            const response = await fetch(await BAPService.getBAPCopilotCrossGeoFlagEndpoint(serviceEndpointStamp, telemetry, environmentId), {
                 method: 'GET',
                 headers: getCommonHeaders(accessToken)
             });
@@ -33,29 +34,9 @@ export class BAPService {
         return false;
     }
 
-    static async getBAPEndpoint(serviceEndpointStamp: ServiceEndpointCategory, telemetry: ITelemetry, environmentId: string): Promise<string> {
+    static async getBAPCopilotCrossGeoFlagEndpoint(serviceEndpointStamp: ServiceEndpointCategory, telemetry: ITelemetry, environmentId: string): Promise<string> {
 
-        let bapEndpoint = "";
-
-        switch (serviceEndpointStamp) {
-            case ServiceEndpointCategory.TEST:
-                bapEndpoint = "https://test.api.bap.microsoft.com";
-                break;
-            case ServiceEndpointCategory.PREPROD:
-                bapEndpoint = "https://preprod.api.bap.microsoft.com";
-                break;
-            case ServiceEndpointCategory.PROD:
-                bapEndpoint = "https://api.bap.microsoft.com";
-                break;
-            // All below endpoints are not supported yet
-            case ServiceEndpointCategory.DOD:
-            case ServiceEndpointCategory.GCC:
-            case ServiceEndpointCategory.HIGH:
-            case ServiceEndpointCategory.MOONCAKE:
-            default:
-                sendTelemetryEvent(telemetry, { eventName: VSCODE_EXTENSION_GET_BAP_ENDPOINT_UNSUPPORTED_REGION, data: serviceEndpointStamp });
-                break;
-        }
+        const bapEndpoint = await getBAPEndpoint(serviceEndpointStamp, telemetry);
 
         return BAP_SERVICE_ENDPOINT.replace('{rootURL}', bapEndpoint) +
             BAP_SERVICE_COPILOT_CROSS_GEO_FLAG_RELATIVE_URL.replace('{environmentID}', environmentId).replace('{apiVersion}', BAP_API_VERSION);
diff --git a/src/common/services/Constants.ts b/src/common/services/Constants.ts
index d41c586b4..791e3cc1c 100644
--- a/src/common/services/Constants.ts
+++ b/src/common/services/Constants.ts
@@ -17,6 +17,7 @@ export const BAP_API_VERSION = '2021-04-01';
 export const BAP_SERVICE_SCOPE_DEFAULT = "https://api.bap.microsoft.com/.default";
 export const BAP_SERVICE_ENDPOINT = `{rootURL}/providers/Microsoft.BusinessAppPlatform/`;
 export const BAP_SERVICE_COPILOT_CROSS_GEO_FLAG_RELATIVE_URL = `scopes/admin/environments/{environmentID}?$expand=properties/copilotPolicies&api-version={apiVersion}`;
+export const BAP_ENVIRONMENT_LIST_URL = `scopes/admin/environments?api-version=2021-04-01&select=name,properties.displayName,properties.linkedEnvironmentMetadata`;
 
 // PPAPI constants
 export const PPAPI_WEBSITES_API_VERSION = '2022-03-01-preview';
diff --git a/src/common/services/TelemetryConstants.ts b/src/common/services/TelemetryConstants.ts
index 4d4d26404..18615c9b2 100644
--- a/src/common/services/TelemetryConstants.ts
+++ b/src/common/services/TelemetryConstants.ts
@@ -24,3 +24,5 @@ export const VSCODE_EXTENSION_GET_PPAPI_WEBSITES_ENDPOINT_UNSUPPORTED_REGION = "
 export const VSCODE_EXTENSION_DECODE_JWT_TOKEN_FAILED = "VSCodeExtensionDecodeJWTTokenFailed";
 export const VSCODE_EXTENSION_PPAPI_GET_WEBSITE_BY_ID_COMPLETED = "VSCodeExtensionPPAPIGetWebsiteByIdCompleted";
 export const VSCODE_EXTENSION_PPAPI_GET_WEBSITE_BY_ID_FAILED = "VSCodeExtensionPPAPIGetWebsiteByIdFailed";
+export const VSCODE_EXTENSION_GET_ENV_LIST_SUCCESS = "VSCodeExtensionGetEnvListSuccess";
+export const VSCODE_EXTENSION_GET_ENV_LIST_FAILED = "VSCodeExtensionGetEnvListFailed";
diff --git a/src/common/utilities/Utils.ts b/src/common/utilities/Utils.ts
index 853f97433..f5cd76c0d 100644
--- a/src/common/utilities/Utils.ts
+++ b/src/common/utilities/Utils.ts
@@ -14,6 +14,9 @@ import { getDisabledOrgList, getDisabledTenantList } from "../copilot/utils/copi
 import { CopilotNotAvailable, CopilotNotAvailableECSConfig } from "../copilot/telemetry/telemetryConstants";
 import path from "path";
 import { oneDSLoggerWrapper } from "../OneDSLoggerTelemetry/oneDSLoggerWrapper";
+import { bapServiceAuthentication } from "../services/AuthenticationProvider";
+import { BAP_ENVIRONMENT_LIST_URL, BAP_SERVICE_ENDPOINT, ServiceEndpointCategory } from "../services/Constants";
+import { VSCODE_EXTENSION_GET_ENV_LIST_SUCCESS, VSCODE_EXTENSION_GET_ENV_LIST_FAILED, VSCODE_EXTENSION_GET_BAP_ENDPOINT_UNSUPPORTED_REGION } from "../services/TelemetryConstants";
 
 export function getSelectedCode(editor: vscode.TextEditor): string {
     if (!editor) {
@@ -320,3 +323,73 @@ export function getECSOrgLocationValue(clusterName: string, clusterNumber: strin
 
     return extractedSubstring;
 }
+
+//API call to get env list for a org
+export async function getEnvList(telemetry: ITelemetry, endpointStamp: ServiceEndpointCategory): Promise<{ envId: string, envDisplayName: string }[]> {
+    const envInfo: { envId: string, envDisplayName: string }[] = [];
+    try {
+        const bapAuthToken = await bapServiceAuthentication(telemetry, true);
+        const bapEndpoint = getBAPEndpoint(endpointStamp, telemetry);
+        const envListEndpoint = `${bapEndpoint}${BAP_ENVIRONMENT_LIST_URL}`;
+
+        const envListResponse = await fetch(envListEndpoint, {
+            method: "GET",
+            headers: {
+                "Authorization": `Bearer ${bapAuthToken}`
+            }
+        });
+
+        if (envListResponse.ok) {
+            const envListJson = await envListResponse.json();
+            envListJson.value.forEach((env: any) => {
+                envInfo.push({
+                    envId: env.properties.linkedEnvironmentMetadata.instanceUrl,
+                    envDisplayName: env.properties.displayName
+                });
+            });
+            sendTelemetryEvent(telemetry, { eventName: VSCODE_EXTENSION_GET_ENV_LIST_SUCCESS });
+            oneDSLoggerWrapper.getLogger().traceInfo(VSCODE_EXTENSION_GET_ENV_LIST_SUCCESS);
+        } else {
+            sendTelemetryEvent(telemetry, {
+                eventName: VSCODE_EXTENSION_GET_ENV_LIST_FAILED,
+                errorMsg: envListResponse.statusText
+            });
+            oneDSLoggerWrapper.getLogger().traceError(VSCODE_EXTENSION_GET_ENV_LIST_FAILED, VSCODE_EXTENSION_GET_ENV_LIST_FAILED, new Error(envListResponse.statusText));
+        }
+    } catch (error) {
+        sendTelemetryEvent(telemetry, {
+            eventName: VSCODE_EXTENSION_GET_ENV_LIST_FAILED,
+            errorMsg: (error as Error).message
+        });
+        oneDSLoggerWrapper.getLogger().traceError(VSCODE_EXTENSION_GET_ENV_LIST_FAILED, VSCODE_EXTENSION_GET_ENV_LIST_FAILED, error as Error);
+    }
+    return envInfo;
+}
+
+
+export function getBAPEndpoint(serviceEndpointStamp: ServiceEndpointCategory, telemetry: ITelemetry): string {
+    let bapEndpoint = "";
+
+    switch (serviceEndpointStamp) {
+        case ServiceEndpointCategory.TEST:
+            bapEndpoint = "https://test.api.bap.microsoft.com";
+            break;
+        case ServiceEndpointCategory.PREPROD:
+            bapEndpoint = "https://preprod.api.bap.microsoft.com";
+            break;
+        case ServiceEndpointCategory.PROD:
+            bapEndpoint = "https://api.bap.microsoft.com";
+            break;
+        // All below endpoints are not supported yet
+        case ServiceEndpointCategory.DOD:
+        case ServiceEndpointCategory.GCC:
+        case ServiceEndpointCategory.HIGH:
+        case ServiceEndpointCategory.MOONCAKE:
+        default:
+            sendTelemetryEvent(telemetry, { eventName: VSCODE_EXTENSION_GET_BAP_ENDPOINT_UNSUPPORTED_REGION, data: serviceEndpointStamp });
+            oneDSLoggerWrapper.getLogger().traceInfo(VSCODE_EXTENSION_GET_BAP_ENDPOINT_UNSUPPORTED_REGION, { data: serviceEndpointStamp });
+            break;
+    }
+
+    return BAP_SERVICE_ENDPOINT.replace('{rootURL}', bapEndpoint)
+}