mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2026-01-07 19:46:53 +00:00
Compare commits
2 Commits
users/sind
...
users/jawe
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
31f245cd43 | ||
|
|
b4890881f0 |
22
.github/workflows/ci.yml
vendored
22
.github/workflows/ci.yml
vendored
@@ -96,14 +96,24 @@ jobs:
|
||||
with:
|
||||
name: dist
|
||||
path: dist/
|
||||
#- name: Upload build to preview blob storage
|
||||
# run: az storage blob upload-batch -d '$web' -s 'dist' --account-name cosmosexplorerpreview --destination-path "${{github.event.pull_request.head.sha || github.sha}}" --account-key="${PREVIEW_STORAGE_KEY}" --overwrite true
|
||||
# env:
|
||||
# PREVIEW_STORAGE_KEY: ${{ secrets.PREVIEW_STORAGE_KEY }}
|
||||
#- name: Upload preview config to blob storage
|
||||
# run: az storage blob upload -c '$web' -f ./preview/config.json --account-name cosmosexplorerpreview --name "${{github.event.pull_request.head.sha || github.sha}}/config.json" --account-key="${PREVIEW_STORAGE_KEY}" --overwrite true
|
||||
# env:
|
||||
# PREVIEW_STORAGE_KEY: ${{ secrets.PREVIEW_STORAGE_KEY }}
|
||||
- name: "Az CLI login"
|
||||
uses: azure/login@v1
|
||||
with:
|
||||
client-id: ${{ secrets.AZURE_CLIENT_ID }}
|
||||
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
||||
subscription-id: ${{ secrets.PREVIEW_SUBSCRIPTION_ID }}
|
||||
- name: Upload build to preview blob storage
|
||||
run: az storage blob upload-batch -d '$web' -s 'dist' --account-name cosmosexplorerpreview --destination-path "${{github.event.pull_request.head.sha || github.sha}}" --account-key="${PREVIEW_STORAGE_KEY}" --overwrite true
|
||||
env:
|
||||
PREVIEW_STORAGE_KEY: ${{ secrets.PREVIEW_STORAGE_KEY }}
|
||||
run: az storage blob upload-batch -d '$web' -s 'dist' --account-name jadepreview --destination-path "${{github.event.pull_request.head.sha || github.sha}}" --overwrite true --auth-mode login
|
||||
- name: Upload preview config to blob storage
|
||||
run: az storage blob upload -c '$web' -f ./preview/config.json --account-name cosmosexplorerpreview --name "${{github.event.pull_request.head.sha || github.sha}}/config.json" --account-key="${PREVIEW_STORAGE_KEY}" --overwrite true
|
||||
env:
|
||||
PREVIEW_STORAGE_KEY: ${{ secrets.PREVIEW_STORAGE_KEY }}
|
||||
run: az storage blob upload -c '$web' -f ./preview/config.json --account-name jadepreview --name "${{github.event.pull_request.head.sha || github.sha}}/config.json" --overwrite true --auth-mode login
|
||||
nuget:
|
||||
name: Publish Nuget
|
||||
if: github.ref == 'refs/heads/master' || contains(github.ref, 'hotfix/') || contains(github.ref, 'release/')
|
||||
|
||||
@@ -8,13 +8,11 @@ import { AuthType } from "../AuthType";
|
||||
import { BackendApi, PriorityLevel } from "../Common/Constants";
|
||||
import * as Logger from "../Common/Logger";
|
||||
import { Platform, configContext } from "../ConfigContext";
|
||||
import { updateUserContext, userContext } from "../UserContext";
|
||||
import { userContext } from "../UserContext";
|
||||
import { logConsoleError } from "../Utils/NotificationConsoleUtils";
|
||||
import * as PriorityBasedExecutionUtils from "../Utils/PriorityBasedExecutionUtils";
|
||||
import { EmulatorMasterKey, HttpHeaders } from "./Constants";
|
||||
import { getErrorMessage } from "./ErrorHandlingUtils";
|
||||
import { runCommand } from "hooks/useDatabaseAccounts";
|
||||
import { acquireTokenWithMsal, getMsalInstance } from "Utils/AuthorizationUtils";
|
||||
|
||||
const _global = typeof self === "undefined" ? window : self;
|
||||
|
||||
@@ -34,42 +32,7 @@ export const tokenProvider = async (requestInfo: Cosmos.RequestInfo) => {
|
||||
return null;
|
||||
}
|
||||
const AUTH_PREFIX = `type=aad&ver=1.0&sig=`;
|
||||
let authorizationToken;
|
||||
|
||||
try {
|
||||
authorizationToken = `${AUTH_PREFIX}${userContext.aadToken}`;
|
||||
} catch (error) {
|
||||
if (error.code === "ExpiredAuthenticationToken") {
|
||||
// Renew the AAD token using runCommand
|
||||
const newToken = await runCommand(async () => {
|
||||
// Implement the logic to acquire a new AAD token
|
||||
const msalInstance = await getMsalInstance();
|
||||
const cachedAccount = msalInstance.getAllAccounts()?.[0];
|
||||
const cachedTenantId = localStorage.getItem("cachedTenantId");
|
||||
|
||||
msalInstance.setActiveAccount(cachedAccount);
|
||||
const newAccessToken = await acquireTokenWithMsal(msalInstance, {
|
||||
authority: `${configContext.AAD_ENDPOINT}${cachedTenantId}`,
|
||||
scopes: [`${configContext.ARM_ENDPOINT}/.default`],
|
||||
});
|
||||
|
||||
// Update user context with the new token
|
||||
updateUserContext({ aadToken: newAccessToken });
|
||||
authorizationToken = `${AUTH_PREFIX}${userContext.aadToken}`;
|
||||
return newAccessToken;
|
||||
});
|
||||
|
||||
// Retry getting the token after renewing
|
||||
const retryResult = await getTokenFromAuthService(verb, resourceType, resourceId);
|
||||
headers[HttpHeaders.msDate] = retryResult.XDate;
|
||||
return decodeURIComponent(retryResult.PrimaryReadWriteToken);
|
||||
} else {
|
||||
console.error('An error occurred:', error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const authorizationToken = `${AUTH_PREFIX}${userContext.aadToken}`;
|
||||
return authorizationToken;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import {
|
||||
MongoProxyEndpoints,
|
||||
PortalBackendEndpoints,
|
||||
} from "Common/Constants";
|
||||
import { userContext } from "UserContext";
|
||||
import {
|
||||
allowedAadEndpoints,
|
||||
allowedArcadiaEndpoints,
|
||||
@@ -39,7 +38,6 @@ export interface ConfigContext {
|
||||
gitSha?: string;
|
||||
proxyPath?: string;
|
||||
AAD_ENDPOINT: string;
|
||||
ENVIRONMENT: string;
|
||||
ARM_AUTH_AREA: string;
|
||||
ARM_ENDPOINT: string;
|
||||
EMULATOR_ENDPOINT?: string;
|
||||
@@ -95,7 +93,7 @@ let configContext: Readonly<ConfigContext> = {
|
||||
], // Webpack injects this at build time
|
||||
gitSha: process.env.GIT_SHA,
|
||||
hostedExplorerURL: "https://cosmos.azure.com/",
|
||||
AAD_ENDPOINT: "",
|
||||
AAD_ENDPOINT: "https://login.microsoftonline.com/",
|
||||
ARM_AUTH_AREA: "https://management.azure.com/",
|
||||
ARM_ENDPOINT: "https://management.azure.com/",
|
||||
ARM_API_VERSION: "2016-06-01",
|
||||
|
||||
@@ -381,7 +381,6 @@ export enum TerminalKind {
|
||||
export interface DataExplorerInputsFrame {
|
||||
databaseAccount: any;
|
||||
subscriptionId?: string;
|
||||
tenantId?: string;
|
||||
resourceGroup?: string;
|
||||
masterKey?: string;
|
||||
hasWriteAccess?: boolean;
|
||||
|
||||
@@ -56,15 +56,13 @@ export const createDatabaseContextMenu = (container: Explorer, databaseId: strin
|
||||
if (userContext.apiType !== "Tables" || userContext.features.enableSDKoperations) {
|
||||
items.push({
|
||||
iconSrc: DeleteDatabaseIcon,
|
||||
onClick: (lastFocusedElement?: React.RefObject<HTMLElement>) => {
|
||||
(useSidePanel.getState().getRef = lastFocusedElement),
|
||||
useSidePanel
|
||||
.getState()
|
||||
.openSidePanel(
|
||||
"Delete " + getDatabaseName(),
|
||||
<DeleteDatabaseConfirmationPanel refreshDatabases={() => container.refreshAllDatabases()} />,
|
||||
);
|
||||
},
|
||||
onClick: () =>
|
||||
useSidePanel
|
||||
.getState()
|
||||
.openSidePanel(
|
||||
"Delete " + getDatabaseName(),
|
||||
<DeleteDatabaseConfirmationPanel refreshDatabases={() => container.refreshAllDatabases()} />,
|
||||
),
|
||||
label: `Delete ${getDatabaseName()}`,
|
||||
styleClass: "deleteDatabaseMenuItem",
|
||||
});
|
||||
@@ -148,15 +146,14 @@ export const createCollectionContextMenuButton = (
|
||||
if (configContext.platform !== Platform.Fabric) {
|
||||
items.push({
|
||||
iconSrc: DeleteCollectionIcon,
|
||||
onClick: (lastFocusedElement?: React.RefObject<HTMLElement>) => {
|
||||
onClick: () => {
|
||||
useSelectedNode.getState().setSelectedNode(selectedCollection);
|
||||
(useSidePanel.getState().getRef = lastFocusedElement),
|
||||
useSidePanel
|
||||
.getState()
|
||||
.openSidePanel(
|
||||
"Delete " + getCollectionName(),
|
||||
<DeleteCollectionConfirmationPane refreshDatabases={() => container.refreshAllDatabases()} />,
|
||||
);
|
||||
useSidePanel
|
||||
.getState()
|
||||
.openSidePanel(
|
||||
"Delete " + getCollectionName(),
|
||||
<DeleteCollectionConfirmationPane refreshDatabases={() => container.refreshAllDatabases()} />,
|
||||
);
|
||||
},
|
||||
label: `Delete ${getCollectionName()}`,
|
||||
styleClass: "deleteCollectionMenuItem",
|
||||
|
||||
@@ -23,7 +23,7 @@ import { useCallback } from "react";
|
||||
|
||||
export interface TreeNodeMenuItem {
|
||||
label: string;
|
||||
onClick: (value?: React.RefObject<HTMLElement>) => void;
|
||||
onClick: () => void;
|
||||
iconSrc?: string;
|
||||
isDisabled?: boolean;
|
||||
styleClass?: string;
|
||||
@@ -74,7 +74,6 @@ export const TreeNodeComponent: React.FC<TreeNodeComponentProps> = ({
|
||||
openItems,
|
||||
}: TreeNodeComponentProps): JSX.Element => {
|
||||
const [isLoading, setIsLoading] = React.useState<boolean>(false);
|
||||
const contextMenuRef = React.useRef<HTMLButtonElement>(null);
|
||||
const treeStyles = useTreeStyles();
|
||||
|
||||
const getSortedChildren = (treeNode: TreeNode): TreeNode[] => {
|
||||
@@ -142,7 +141,7 @@ export const TreeNodeComponent: React.FC<TreeNodeComponentProps> = ({
|
||||
data-test={`TreeNode/ContextMenuItem:${menuItem.label}`}
|
||||
disabled={menuItem.isDisabled}
|
||||
key={menuItem.label}
|
||||
onClick={() => menuItem.onClick(contextMenuRef)}
|
||||
onClick={menuItem.onClick}
|
||||
>
|
||||
{menuItem.label}
|
||||
</MenuItem>
|
||||
@@ -191,7 +190,6 @@ export const TreeNodeComponent: React.FC<TreeNodeComponentProps> = ({
|
||||
className={mergeClasses(treeStyles.actionsButton, shouldShowAsSelected && treeStyles.selectedItem)}
|
||||
data-test="TreeNode/ContextMenuTrigger"
|
||||
appearance="subtle"
|
||||
ref={contextMenuRef}
|
||||
icon={<MoreHorizontal20Regular />}
|
||||
/>
|
||||
</MenuTrigger>
|
||||
|
||||
@@ -1478,14 +1478,14 @@ exports[`TreeNodeComponent renders a node with a menu 1`] = `
|
||||
<MenuList>
|
||||
<MenuItem
|
||||
data-test="TreeNode/ContextMenuItem:enabledItem"
|
||||
onClick={[Function]}
|
||||
onClick={[MockFunction enabledItemClick]}
|
||||
>
|
||||
enabledItem
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
data-test="TreeNode/ContextMenuItem:disabledItem"
|
||||
disabled={true}
|
||||
onClick={[Function]}
|
||||
onClick={[MockFunction disabledItemClick]}
|
||||
>
|
||||
disabledItem
|
||||
</MenuItem>
|
||||
@@ -1518,7 +1518,7 @@ exports[`TreeNodeComponent renders a node with a menu 1`] = `
|
||||
<MenuItem
|
||||
data-test="TreeNode/ContextMenuItem:enabledItem"
|
||||
key="enabledItem"
|
||||
onClick={[Function]}
|
||||
onClick={[MockFunction enabledItemClick]}
|
||||
>
|
||||
enabledItem
|
||||
</MenuItem>
|
||||
@@ -1526,7 +1526,7 @@ exports[`TreeNodeComponent renders a node with a menu 1`] = `
|
||||
data-test="TreeNode/ContextMenuItem:disabledItem"
|
||||
disabled={true}
|
||||
key="disabledItem"
|
||||
onClick={[Function]}
|
||||
onClick={[MockFunction disabledItemClick]}
|
||||
>
|
||||
disabledItem
|
||||
</MenuItem>
|
||||
|
||||
@@ -38,6 +38,7 @@ import {
|
||||
TextSortDescendingRegular,
|
||||
} from "@fluentui/react-icons";
|
||||
import { NormalizedEventKey } from "Common/Constants";
|
||||
import { Environment, getEnvironment } from "Common/EnvironmentUtility";
|
||||
import { TableColumnSelectionPane } from "Explorer/Panes/TableColumnSelectionPane/TableColumnSelectionPane";
|
||||
import {
|
||||
ColumnSizesMap,
|
||||
@@ -227,29 +228,31 @@ export const DocumentsTableComponent: React.FC<IDocumentsTableComponentProps> =
|
||||
<MenuItem key="refresh" icon={<ArrowClockwise16Regular />} onClick={onRefreshTable}>
|
||||
Refresh
|
||||
</MenuItem>
|
||||
<>
|
||||
<MenuItem
|
||||
icon={<TextSortAscendingRegular />}
|
||||
onClick={(e) => onSortClick(e, column.id, "ascending")}
|
||||
>
|
||||
Sort ascending
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
icon={<TextSortDescendingRegular />}
|
||||
onClick={(e) => onSortClick(e, column.id, "descending")}
|
||||
>
|
||||
Sort descending
|
||||
</MenuItem>
|
||||
<MenuItem icon={<ArrowResetRegular />} onClick={(e) => onSortClick(e, undefined, undefined)}>
|
||||
Reset sorting
|
||||
</MenuItem>
|
||||
{!isColumnSelectionDisabled && (
|
||||
<MenuItem key="editcolumns" icon={<EditRegular />} onClick={openColumnSelectionPane}>
|
||||
Edit columns
|
||||
{[Environment.Development, Environment.Mpac].includes(getEnvironment()) && (
|
||||
<>
|
||||
<MenuItem
|
||||
icon={<TextSortAscendingRegular />}
|
||||
onClick={(e) => onSortClick(e, column.id, "ascending")}
|
||||
>
|
||||
Sort ascending
|
||||
</MenuItem>
|
||||
)}
|
||||
<MenuDivider />
|
||||
</>
|
||||
<MenuItem
|
||||
icon={<TextSortDescendingRegular />}
|
||||
onClick={(e) => onSortClick(e, column.id, "descending")}
|
||||
>
|
||||
Sort descending
|
||||
</MenuItem>
|
||||
<MenuItem icon={<ArrowResetRegular />} onClick={(e) => onSortClick(e, undefined, undefined)}>
|
||||
Reset sorting
|
||||
</MenuItem>
|
||||
{!isColumnSelectionDisabled && (
|
||||
<MenuItem key="editcolumns" icon={<EditRegular />} onClick={openColumnSelectionPane}>
|
||||
Edit columns
|
||||
</MenuItem>
|
||||
)}
|
||||
<MenuDivider />
|
||||
</>
|
||||
)}
|
||||
<MenuItem
|
||||
key="keyboardresize"
|
||||
icon={<TableResizeColumnRegular />}
|
||||
@@ -257,24 +260,25 @@ export const DocumentsTableComponent: React.FC<IDocumentsTableComponentProps> =
|
||||
>
|
||||
Resize with left/right arrow keys
|
||||
</MenuItem>
|
||||
{!isColumnSelectionDisabled && (
|
||||
<MenuItem
|
||||
key="remove"
|
||||
icon={<DeleteRegular />}
|
||||
onClick={() => {
|
||||
// Remove column id from selectedColumnIds
|
||||
const index = selectedColumnIds.indexOf(column.id);
|
||||
if (index === -1) {
|
||||
return;
|
||||
}
|
||||
const newSelectedColumnIds = [...selectedColumnIds];
|
||||
newSelectedColumnIds.splice(index, 1);
|
||||
onColumnSelectionChange(newSelectedColumnIds);
|
||||
}}
|
||||
>
|
||||
Remove column
|
||||
</MenuItem>
|
||||
)}
|
||||
{[Environment.Development, Environment.Mpac].includes(getEnvironment()) &&
|
||||
!isColumnSelectionDisabled && (
|
||||
<MenuItem
|
||||
key="remove"
|
||||
icon={<DeleteRegular />}
|
||||
onClick={() => {
|
||||
// Remove column id from selectedColumnIds
|
||||
const index = selectedColumnIds.indexOf(column.id);
|
||||
if (index === -1) {
|
||||
return;
|
||||
}
|
||||
const newSelectedColumnIds = [...selectedColumnIds];
|
||||
newSelectedColumnIds.splice(index, 1);
|
||||
onColumnSelectionChange(newSelectedColumnIds);
|
||||
}}
|
||||
>
|
||||
Remove column
|
||||
</MenuItem>
|
||||
)}
|
||||
</MenuList>
|
||||
</MenuPopover>
|
||||
</Menu>
|
||||
|
||||
@@ -74,13 +74,11 @@ export interface UserContext {
|
||||
readonly authType?: AuthType;
|
||||
readonly masterKey?: string;
|
||||
readonly subscriptionId?: string;
|
||||
readonly tenantId?: string;
|
||||
readonly resourceGroup?: string;
|
||||
readonly databaseAccount?: DatabaseAccount;
|
||||
readonly endpoint?: string;
|
||||
readonly aadToken?: string;
|
||||
readonly accessToken?: string;
|
||||
readonly armToken?: string;
|
||||
readonly authorizationToken?: string;
|
||||
readonly resourceToken?: string;
|
||||
readonly subscriptionType?: SubscriptionType;
|
||||
|
||||
@@ -3,7 +3,6 @@ import { useBoolean } from "@fluentui/react-hooks";
|
||||
import * as React from "react";
|
||||
import { configContext } from "../ConfigContext";
|
||||
import { acquireTokenWithMsal, getMsalInstance } from "../Utils/AuthorizationUtils";
|
||||
import { updateUserContext } from "UserContext";
|
||||
|
||||
const msalInstance = await getMsalInstance();
|
||||
|
||||
@@ -80,7 +79,7 @@ export function useAADAuth(): ReturnType {
|
||||
authority: `${configContext.AAD_ENDPOINT}${tenantId}`,
|
||||
scopes: [`${configContext.ARM_ENDPOINT}/.default`],
|
||||
});
|
||||
updateUserContext({ armToken: armToken});
|
||||
|
||||
setArmToken(armToken);
|
||||
setAuthFailure(null);
|
||||
} catch (error) {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { HttpHeaders } from "Common/Constants";
|
||||
import { QueryRequestOptions, QueryResponse } from "Contracts/AzureResourceGraph";
|
||||
import useSWR from "swr";
|
||||
import { acquireTokenWithMsal, getMsalInstance } from "Utils/AuthorizationUtils";
|
||||
import React from "react";
|
||||
import { updateUserContext, userContext } from "UserContext";
|
||||
import { configContext } from "../ConfigContext";
|
||||
import { DatabaseAccount } from "../Contracts/DataModels";
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
|
||||
interface AccountListResult {
|
||||
@@ -34,10 +34,11 @@ export async function fetchDatabaseAccounts(subscriptionId: string, accessToken:
|
||||
}
|
||||
|
||||
export async function fetchDatabaseAccountsFromGraph(
|
||||
subscriptionId: string
|
||||
subscriptionId: string,
|
||||
accessToken: string,
|
||||
): Promise<DatabaseAccount[]> {
|
||||
const headers = new Headers();
|
||||
const bearer = `Bearer ${userContext.armToken}`;
|
||||
const bearer = `Bearer ${accessToken}`;
|
||||
|
||||
headers.append("Authorization", bearer);
|
||||
headers.append(HttpHeaders.contentType, "application/json");
|
||||
@@ -45,9 +46,8 @@ export async function fetchDatabaseAccountsFromGraph(
|
||||
const apiVersion = "2021-03-01";
|
||||
const managementResourceGraphAPIURL = `${configContext.ARM_ENDPOINT}providers/Microsoft.ResourceGraph/resources?api-version=${apiVersion}`;
|
||||
|
||||
let databaseAccounts: DatabaseAccount[] = [];
|
||||
const databaseAccounts: DatabaseAccount[] = [];
|
||||
let skipToken: string;
|
||||
console.log("Old ARM Token", userContext.armToken);
|
||||
do {
|
||||
const body = {
|
||||
query: databaseAccountsQuery,
|
||||
@@ -74,166 +74,21 @@ export async function fetchDatabaseAccountsFromGraph(
|
||||
if (!response.ok) {
|
||||
throw new Error(await response.text());
|
||||
}
|
||||
|
||||
|
||||
const queryResponse: QueryResponse = (await response.json()) as QueryResponse;
|
||||
skipToken = queryResponse.$skipToken;
|
||||
queryResponse.data?.map((databaseAccount: any) => {
|
||||
databaseAccounts.push(databaseAccount as DatabaseAccount);
|
||||
});
|
||||
|
||||
// else {
|
||||
// try{
|
||||
// console.log("Token expired");
|
||||
// databaseAccounts = await acquireNewTokenAndRetry(body);
|
||||
// }
|
||||
// catch (error) {
|
||||
// throw new Error(error);
|
||||
// }
|
||||
|
||||
//}
|
||||
} while (skipToken);
|
||||
|
||||
return databaseAccounts.sort((a, b) => a.name.localeCompare(b.name));
|
||||
}
|
||||
|
||||
export function useDatabaseAccounts(subscriptionId: string): DatabaseAccount[] | undefined {
|
||||
export function useDatabaseAccounts(subscriptionId: string, armToken: string): DatabaseAccount[] | undefined {
|
||||
const { data } = useSWR(
|
||||
() => ( subscriptionId ? ["databaseAccounts", subscriptionId] : undefined),
|
||||
(_, subscriptionId) => runCommand(fetchDatabaseAccountsFromGraph, subscriptionId),
|
||||
() => (armToken && subscriptionId ? ["databaseAccounts", subscriptionId, armToken] : undefined),
|
||||
(_, subscriptionId, armToken) => fetchDatabaseAccountsFromGraph(subscriptionId, armToken),
|
||||
);
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Define the types for your responses
|
||||
interface DatabaseAccount {
|
||||
name: string;
|
||||
id: string;
|
||||
// Add other relevant fields as per your use case
|
||||
}
|
||||
|
||||
interface Subscription {
|
||||
displayName: string;
|
||||
subscriptionId: string;
|
||||
state: string;
|
||||
}
|
||||
|
||||
interface QueryRequestOptions {
|
||||
$top?: number;
|
||||
$skipToken?: string;
|
||||
$allowPartialScopes?: boolean;
|
||||
}
|
||||
|
||||
// Define the configuration context and headers if not already defined
|
||||
const configContext = {
|
||||
ARM_ENDPOINT: 'https://management.azure.com/',
|
||||
AAD_ENDPOINT: 'https://login.microsoftonline.com/'
|
||||
};
|
||||
|
||||
interface QueryResponse {
|
||||
data?: any[];
|
||||
$skipToken?: string;
|
||||
}
|
||||
|
||||
export async function runCommand<T>(
|
||||
fn: (...args: any[]) => Promise<T>,
|
||||
...args: any[]
|
||||
): Promise<T> {
|
||||
try {
|
||||
// Attempt to execute the function passed as an argument
|
||||
const result = await fn(...args);
|
||||
console.log('Successfully executed function:', result);
|
||||
return result;
|
||||
|
||||
} catch (error) {
|
||||
// Handle any error that is thrown during the execution of the function
|
||||
//(error.code === "ExpiredAuthenticationToken")
|
||||
if(error) {
|
||||
console.log('Creating new token');
|
||||
const msalInstance = await getMsalInstance();
|
||||
|
||||
const cachedAccount = msalInstance.getAllAccounts()?.[0];
|
||||
const cachedTenantId = localStorage.getItem("cachedTenantId");
|
||||
|
||||
|
||||
msalInstance.setActiveAccount(cachedAccount);
|
||||
|
||||
const newAccessToken = await acquireTokenWithMsal(msalInstance, {
|
||||
authority: `${configContext.AAD_ENDPOINT}${cachedTenantId}`,
|
||||
scopes: [`${configContext.ARM_ENDPOINT}/.default`],
|
||||
});
|
||||
|
||||
console.log("Latest ARM Token", userContext.armToken);
|
||||
updateUserContext({armToken: newAccessToken});
|
||||
const result = await fn(...args);
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
console.error('An error occurred:', error.message);
|
||||
throw new error;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Running the functions using runCommand
|
||||
|
||||
const accessToken = 'your-access-token';
|
||||
const subscriptionId = 'your-subscription-id';
|
||||
|
||||
//runCommand(fetchDatabaseAccountsFromGraph, subscriptionId, accessToken);
|
||||
//runCommand(fetchSubscriptionsFromGraph, accessToken);
|
||||
|
||||
async function acquireNewTokenAndRetry(body: any) : Promise<DatabaseAccount[]> {
|
||||
try {
|
||||
const msalInstance = await getMsalInstance();
|
||||
|
||||
const cachedAccount = msalInstance.getAllAccounts()?.[0];
|
||||
const cachedTenantId = localStorage.getItem("cachedTenantId");
|
||||
|
||||
// const [tenantId, setTenantId] = React.useState<string>(cachedTenantId);
|
||||
|
||||
|
||||
msalInstance.setActiveAccount(cachedAccount);
|
||||
|
||||
const newAccessToken = await acquireTokenWithMsal(msalInstance, {
|
||||
authority: `${configContext.AAD_ENDPOINT}${cachedTenantId}`,
|
||||
scopes: [`${configContext.ARM_ENDPOINT}/.default`],
|
||||
});
|
||||
console.log("New ARM Token", newAccessToken);
|
||||
const newBearer = `Bearer ${newAccessToken}`;
|
||||
const newHeaders = new Headers();
|
||||
newHeaders.append("Authorization", newBearer);
|
||||
newHeaders.append(HttpHeaders.contentType, "application/json");
|
||||
const apiVersion = "2021-03-01";
|
||||
const managementResourceGraphAPIURL = `${configContext.ARM_ENDPOINT}providers/Microsoft.ResourceGraph/resources?api-version=${apiVersion}`;
|
||||
|
||||
const databaseAccounts: DatabaseAccount[] = [];
|
||||
let skipToken: string;
|
||||
|
||||
|
||||
// Retry the request with the new token
|
||||
const response = await fetch(managementResourceGraphAPIURL, {
|
||||
method: "POST",
|
||||
headers: newHeaders,
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
// Handle successful response with new token
|
||||
const queryResponse: QueryResponse = await response.json();
|
||||
skipToken = queryResponse.$skipToken;
|
||||
queryResponse.data?.forEach((databaseAccount: any) => {
|
||||
databaseAccounts.push(databaseAccount as DatabaseAccount);
|
||||
});
|
||||
return databaseAccounts;
|
||||
} else {
|
||||
throw new Error(`Failed to fetch data after acquiring new token. Status: ${response.status}, ${await response.text()}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error acquiring new token and retrying:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import { FabricMessageTypes } from "Contracts/FabricMessageTypes";
|
||||
import { FABRIC_RPC_VERSION, FabricMessageV2 } from "Contracts/FabricMessagesContract";
|
||||
import Explorer from "Explorer/Explorer";
|
||||
import { useDataPlaneRbac } from "Explorer/Panes/SettingsPane/SettingsPane";
|
||||
import { useDataPlaneRbac } from "Explorer/Panes/SettingsPane/SettingsPane";
|
||||
import { useSelectedNode } from "Explorer/useSelectedNode";
|
||||
import { scheduleRefreshDatabaseResourceToken } from "Platform/Fabric/FabricUtil";
|
||||
import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
|
||||
@@ -19,7 +18,6 @@ import { AuthType } from "../AuthType";
|
||||
import { AccountKind, Flights } from "../Common/Constants";
|
||||
import { normalizeArmEndpoint } from "../Common/EnvironmentUtility";
|
||||
import * as Logger from "../Common/Logger";
|
||||
import * as Logger from "../Common/Logger";
|
||||
import { handleCachedDataMessage, sendMessage, sendReadyMessage } from "../Common/MessageHandler";
|
||||
import { Platform, configContext, updateConfigContext } from "../ConfigContext";
|
||||
import { ActionType, DataExplorerAction, TabKind } from "../Contracts/ActionContracts";
|
||||
@@ -466,7 +464,6 @@ export async function fetchAndUpdateKeys(subscriptionId: string, resourceGroup:
|
||||
Logger.logInfo(`Fetching keys for ${userContext.apiType} account ${account}`, "Explorer/fetchAndUpdateKeys");
|
||||
let keys;
|
||||
try {
|
||||
keys = await listKeys(subscriptionId, resourceGroup, account);
|
||||
keys = await listKeys(subscriptionId, resourceGroup, account);
|
||||
Logger.logInfo(`Keys fetched for ${userContext.apiType} account ${account}`, "Explorer/fetchAndUpdateKeys");
|
||||
updateUserContext({
|
||||
@@ -490,23 +487,6 @@ export async function fetchAndUpdateKeys(subscriptionId: string, resourceGroup:
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
if (error.code === "AuthorizationFailed") {
|
||||
keys = await getReadOnlyKeys(subscriptionId, resourceGroup, account);
|
||||
Logger.logInfo(
|
||||
`Read only Keys fetched for ${userContext.apiType} account ${account}`,
|
||||
"Explorer/fetchAndUpdateKeys",
|
||||
);
|
||||
updateUserContext({
|
||||
masterKey: keys.primaryReadonlyMasterKey,
|
||||
});
|
||||
} else {
|
||||
logConsoleError(`Error occurred fetching keys for the account." ${error.message}`);
|
||||
Logger.logError(
|
||||
`Error during fetching keys or updating user context: ${error} for ${userContext.apiType} account ${account}`,
|
||||
"Explorer/fetchAndUpdateKeys",
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -713,7 +693,6 @@ function updateContextsFromPortalMessage(inputs: DataExplorerInputsFrame) {
|
||||
databaseAccount,
|
||||
resourceGroup: inputs.resourceGroup,
|
||||
subscriptionId: inputs.subscriptionId,
|
||||
tenantId: inputs.tenantId,
|
||||
subscriptionType: inputs.subscriptionType,
|
||||
quotaId: inputs.quotaId,
|
||||
portalEnv: inputs.serverId as PortalEnv,
|
||||
@@ -834,4 +813,4 @@ async function updateContextForSampleData(explorer: Explorer): Promise<void> {
|
||||
|
||||
interface SampledataconnectionResponse {
|
||||
connectionString: string;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,20 +7,12 @@ export interface SidePanelState {
|
||||
headerText?: string;
|
||||
openSidePanel: (headerText: string, panelContent: JSX.Element, panelWidth?: string, onClose?: () => void) => void;
|
||||
closeSidePanel: () => void;
|
||||
getRef?: React.RefObject<HTMLElement>; // Optional ref for focusing the last element.
|
||||
}
|
||||
|
||||
export const useSidePanel: UseStore<SidePanelState> = create((set) => ({
|
||||
isOpen: false,
|
||||
panelWidth: "440px",
|
||||
openSidePanel: (headerText, panelContent, panelWidth = "440px") =>
|
||||
set((state) => ({ ...state, headerText, panelContent, panelWidth, isOpen: true })),
|
||||
closeSidePanel: () => {
|
||||
const lastFocusedElement = useSidePanel.getState().getRef;
|
||||
set((state) => ({ ...state, isOpen: false }));
|
||||
const timeoutId = setTimeout(() => {
|
||||
lastFocusedElement?.current?.focus();
|
||||
set({ getRef: undefined });
|
||||
}, 300);
|
||||
return () => clearTimeout(timeoutId);
|
||||
},
|
||||
closeSidePanel: () => set((state) => ({ ...state, isOpen: false })),
|
||||
}));
|
||||
|
||||
@@ -3,7 +3,6 @@ import { QueryRequestOptions, QueryResponse } from "Contracts/AzureResourceGraph
|
||||
import useSWR from "swr";
|
||||
import { configContext } from "../ConfigContext";
|
||||
import { Subscription } from "../Contracts/DataModels";
|
||||
import { acquireTokenWithMsal, getMsalInstance } from "Utils/AuthorizationUtils";
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
|
||||
interface SubscriptionListResult {
|
||||
@@ -93,5 +92,3 @@ export function useSubscriptions(armToken: string): Subscription[] | undefined {
|
||||
);
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user