For data plane operations, skip ARM for Fabric native. Refine the tests for fabric to make the distinction between mirrored key, mirrored AAD and native. Fix FabricUtil to strict compile.

This commit is contained in:
Laurent Nguyen 2025-03-03 17:05:32 +01:00
parent aeca2ac1f9
commit c1832477cd
13 changed files with 66 additions and 51 deletions

View File

@ -2,7 +2,7 @@ import * as Cosmos from "@azure/cosmos";
import { getAuthorizationTokenUsingResourceTokens } from "Common/getAuthorizationTokenUsingResourceTokens";
import { CosmosDbArtifactType } from "Contracts/FabricMessagesContract";
import { AuthorizationToken } from "Contracts/FabricMessageTypes";
import { checkDatabaseResourceTokensValidity, isFabricMirrored } from "Platform/Fabric/FabricUtil";
import { checkDatabaseResourceTokensValidity, isFabricMirroredKey } from "Platform/Fabric/FabricUtil";
import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
import { AuthType } from "../AuthType";
import { PriorityLevel } from "../Common/Constants";
@ -43,7 +43,7 @@ export const tokenProvider = async (requestInfo: Cosmos.RequestInfo) => {
return decodeURIComponent(headers.authorization);
}
if (isFabricMirrored()) {
if (isFabricMirroredKey()) {
switch (requestInfo.resourceType) {
case Cosmos.ResourceType.conflicts:
case Cosmos.ResourceType.container:

View File

@ -1,10 +1,10 @@
import { Platform, configContext } from "../ConfigContext";
import { isFabric } from "Platform/Fabric/FabricUtil";
// eslint-disable-next-line @typescript-eslint/no-var-requires
export const StyleConstants = require("less-vars-loader!../../less/Common/Constants.less");
export function updateStyles(): void {
if (configContext.platform === Platform.Fabric) {
if (isFabric()) {
StyleConstants.AccentMediumHigh = StyleConstants.FabricAccentMediumHigh;
StyleConstants.AccentMedium = StyleConstants.FabricAccentMedium;
StyleConstants.AccentLight = StyleConstants.FabricAccentLight;

View File

@ -1,7 +1,7 @@
import { ContainerResponse } from "@azure/cosmos";
import { Queries } from "Common/Constants";
import { CosmosDbArtifactType } from "Contracts/FabricMessagesContract";
import { isFabricMirrored } from "Platform/Fabric/FabricUtil";
import { isFabric, isFabricMirroredKey } from "Platform/Fabric/FabricUtil";
import { AuthType } from "../../AuthType";
import * as DataModels from "../../Contracts/DataModels";
import { FabricArtifactInfo, userContext } from "../../UserContext";
@ -17,7 +17,7 @@ import { handleError } from "../ErrorHandlingUtils";
export async function readCollections(databaseId: string): Promise<DataModels.Collection[]> {
const clearMessage = logConsoleProgress(`Querying containers for database ${databaseId}`);
if (isFabricMirrored() && userContext.fabricContext?.databaseName === databaseId) {
if (isFabricMirroredKey() && userContext.fabricContext?.databaseName === databaseId) {
const collections: DataModels.Collection[] = [];
const promises: Promise<ContainerResponse>[] = [];
@ -55,7 +55,8 @@ export async function readCollections(databaseId: string): Promise<DataModels.Co
if (
userContext.authType === AuthType.AAD &&
!userContext.features.enableSDKoperations &&
userContext.apiType !== "Tables"
userContext.apiType !== "Tables" &&
!isFabric()
) {
return await readCollectionsWithARM(databaseId);
}

View File

@ -1,4 +1,4 @@
import { isFabricMirrored } from "Platform/Fabric/FabricUtil";
import { isFabric, isFabricMirroredKey } from "Platform/Fabric/FabricUtil";
import { AuthType } from "../../AuthType";
import { Offer, ReadDatabaseOfferParams } from "../../Contracts/DataModels";
import { userContext } from "../../UserContext";
@ -11,7 +11,7 @@ import { handleError } from "../ErrorHandlingUtils";
import { readOfferWithSDK } from "./readOfferWithSDK";
export const readDatabaseOffer = async (params: ReadDatabaseOfferParams): Promise<Offer> => {
if (isFabricMirrored()) {
if (isFabricMirroredKey()) {
// TODO This works, but is very slow, because it requests the token, so we skip for now
console.error("Skiping readDatabaseOffer for Fabric");
return undefined;
@ -23,7 +23,8 @@ export const readDatabaseOffer = async (params: ReadDatabaseOfferParams): Promis
if (
userContext.authType === AuthType.AAD &&
!userContext.features.enableSDKoperations &&
userContext.apiType !== "Tables"
userContext.apiType !== "Tables" &&
!isFabric()
) {
return await readDatabaseOfferWithARM(params.databaseId);
}

View File

@ -1,5 +1,5 @@
import { CosmosDbArtifactType } from "Contracts/FabricMessagesContract";
import { isFabricMirrored, isFabricNative } from "Platform/Fabric/FabricUtil";
import { isFabric, isFabricMirroredKey, isFabricNative } from "Platform/Fabric/FabricUtil";
import { AuthType } from "../../AuthType";
import * as DataModels from "../../Contracts/DataModels";
import { FabricArtifactInfo, userContext } from "../../UserContext";
@ -16,7 +16,7 @@ export async function readDatabases(): Promise<DataModels.Database[]> {
const clearMessage = logConsoleProgress(`Querying databases`);
if (
isFabricMirrored() &&
isFabricMirroredKey() &&
(userContext.fabricContext?.artifactInfo as FabricArtifactInfo[CosmosDbArtifactType.MIRRORED_KEY]).resourceTokenInfo
.resourceTokens
) {
@ -72,7 +72,8 @@ export async function readDatabases(): Promise<DataModels.Database[]> {
if (
userContext.authType === AuthType.AAD &&
!userContext.features.enableSDKoperations &&
userContext.apiType !== "Tables"
userContext.apiType !== "Tables" &&
!isFabric()
) {
databases = await readDatabasesWithARM();
} else {

View File

@ -1,7 +1,7 @@
import { configContext, Platform } from "ConfigContext";
import { TreeNodeMenuItem } from "Explorer/Controls/TreeComponent/TreeNodeComponent";
import { useDatabases } from "Explorer/useDatabases";
import { isFabricMirrored, isFabricNative } from "Platform/Fabric/FabricUtil";
import { isFabric, isFabricNative } from "Platform/Fabric/FabricUtil";
import { Action } from "Shared/Telemetry/TelemetryConstants";
import { traceOpen } from "Shared/Telemetry/TelemetryProcessor";
import { ReactTabKind, useTabs } from "hooks/useTabs";
@ -42,7 +42,7 @@ export interface DatabaseContextMenuButtonParams {
* New resource tree (in ReactJS)
*/
export const createDatabaseContextMenu = (container: Explorer, databaseId: string): TreeNodeMenuItem[] => {
if (isFabricMirrored() && userContext.fabricContext?.isReadOnly) {
if (isFabric() && userContext.fabricContext?.isReadOnly) {
return undefined;
}

View File

@ -8,7 +8,11 @@ import { MessageTypes } from "Contracts/ExplorerContracts";
import { useDataPlaneRbac } from "Explorer/Panes/SettingsPane/SettingsPane";
import { getCopilotEnabled, isCopilotFeatureRegistered } from "Explorer/QueryCopilot/Shared/QueryCopilotClient";
import { IGalleryItem } from "Juno/JunoClient";
import { isFabricMirrored, scheduleRefreshDatabaseResourceToken } from "Platform/Fabric/FabricUtil";
import {
isFabricMirrored,
isFabricMirroredKey,
scheduleRefreshDatabaseResourceToken,
} from "Platform/Fabric/FabricUtil";
import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
import { acquireMsalTokenForAccount } from "Utils/AuthorizationUtils";
import { allowedNotebookServerUrls, validateEndpoint } from "Utils/EndpointUtils";
@ -351,7 +355,7 @@ export default class Explorer {
};
public onRefreshResourcesClick = async (): Promise<void> => {
if (isFabricMirrored()) {
if (isFabricMirroredKey()) {
scheduleRefreshDatabaseResourceToken(true).then(() => this.refreshAllDatabases());
return;
}

View File

@ -6,12 +6,12 @@
import { CommandBar as FluentCommandBar, ICommandBarItemProps } from "@fluentui/react";
import { useNotebook } from "Explorer/Notebook/useNotebook";
import { KeyboardActionGroup, useKeyboardActionGroup } from "KeyboardShortcuts";
import { isFabric } from "Platform/Fabric/FabricUtil";
import { userContext } from "UserContext";
import * as React from "react";
import create, { UseStore } from "zustand";
import { ConnectionStatusType, PoolIdType } from "../../../Common/Constants";
import { StyleConstants } from "../../../Common/StyleConstants";
import { Platform, configContext } from "../../../ConfigContext";
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
import Explorer from "../../Explorer";
import { useSelectedNode } from "../../useSelectedNode";
@ -93,8 +93,7 @@ export const CommandBar: React.FC<Props> = ({ container }: Props) => {
);
}
const rootStyle =
configContext.platform === Platform.Fabric
const rootStyle = isFabric()
? {
root: {
backgroundColor: "transparent",

View File

@ -21,7 +21,7 @@ import { CosmosFluentProvider, cosmosShorthands, tokens } from "Explorer/Theme/T
import { ResourceTree } from "Explorer/Tree/ResourceTree";
import { useDatabases } from "Explorer/useDatabases";
import { KeyboardAction, KeyboardActionGroup, KeyboardActionHandler, useKeyboardActionGroup } from "KeyboardShortcuts";
import { isFabricMirrored, isFabricNative } from "Platform/Fabric/FabricUtil";
import { isFabric, isFabricMirrored, isFabricNative } from "Platform/Fabric/FabricUtil";
import { userContext } from "UserContext";
import { getCollectionName, getDatabaseName } from "Utils/APITypeUtils";
import { Allotment, AllotmentHandle } from "allotment";
@ -123,7 +123,11 @@ const GlobalCommands: React.FC<GlobalCommandsProps> = ({ explorer }) => {
const primaryFocusableRef = useRef<HTMLButtonElement>(null);
const actions = useMemo<GlobalCommand[]>(() => {
if (isFabricMirrored() || userContext.apiType === "Postgres" || userContext.apiType === "VCoreMongo") {
if (
(isFabric() && userContext.fabricContext?.isReadOnly) ||
userContext.apiType === "Postgres" ||
userContext.apiType === "VCoreMongo"
) {
// No Global Commands for these API types.
// In fact, no sidebar for Postgres or VCoreMongo at all, but just in case, we check here anyway.
return [];

View File

@ -20,7 +20,6 @@ import {
import { queryDocuments } from "Common/dataAccess/queryDocuments";
import { readDocument } from "Common/dataAccess/readDocument";
import { updateDocument } from "Common/dataAccess/updateDocument";
import { Platform, configContext } from "ConfigContext";
import { ActionType, OpenCollectionTab, TabKind } from "Contracts/ActionContracts";
import { CommandButtonComponentProps } from "Explorer/Controls/CommandButton/CommandButtonComponent";
import { useDialog } from "Explorer/Controls/Dialog";
@ -43,6 +42,7 @@ import { usePrevious } from "Explorer/Tabs/DocumentsTabV2/SelectionHelper";
import { CosmosFluentProvider, LayoutConstants, cosmosShorthands, tokens } from "Explorer/Theme/ThemeUtil";
import { useSelectedNode } from "Explorer/useSelectedNode";
import { KeyboardAction, KeyboardActionGroup, useKeyboardActionGroup } from "KeyboardShortcuts";
import { isFabric } from "Platform/Fabric/FabricUtil";
import { QueryConstants } from "Shared/Constants";
import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
import { Action } from "Shared/Telemetry/TelemetryConstants";
@ -344,7 +344,7 @@ export const getTabsButtons = ({
onRevertExistingDocumentClick,
onDeleteExistingDocumentsClick,
}: ButtonsDependencies): CommandButtonComponentProps[] => {
if (configContext.platform === Platform.Fabric && userContext.fabricContext?.isReadOnly) {
if (isFabric() && userContext.fabricContext?.isReadOnly) {
// All the following buttons require write access
return [];
}
@ -2136,8 +2136,7 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
selectedColumnIds={selectedColumnIds}
columnDefinitions={columnDefinitions}
isRowSelectionDisabled={
isBulkDeleteDisabled ||
(configContext.platform === Platform.Fabric && userContext.fabricContext?.isReadOnly)
isBulkDeleteDisabled || (isFabric() && userContext.fabricContext?.isReadOnly)
}
onColumnSelectionChange={onColumnSelectionChange}
defaultColumnSelection={getInitialColumnSelection()}

View File

@ -2,27 +2,31 @@ import { sendCachedDataMessage } from "Common/MessageHandler";
import { configContext, Platform } from "ConfigContext";
import { FabricMessageTypes } from "Contracts/FabricMessageTypes";
import { CosmosDbArtifactType, ResourceTokenInfo } from "Contracts/FabricMessagesContract";
import { updateUserContext, userContext } from "UserContext";
import { FabricArtifactInfo, updateUserContext, userContext } from "UserContext";
import { logConsoleError } from "Utils/NotificationConsoleUtils";
const TOKEN_VALIDITY_MS = (3600 - 600) * 1000; // 1 hour minus 10 minutes to be safe
const DEBOUNCE_DELAY_MS = 1000 * 20; // 20 second
let timeoutId: NodeJS.Timeout;
let timeoutId: NodeJS.Timeout | undefined;
// Prevents multiple parallel requests during DEBOUNCE_DELAY_MS
let lastRequestTimestamp: number = undefined;
let lastRequestTimestamp: number | undefined = undefined;
const requestDatabaseResourceTokens = async (): Promise<void> => {
if (lastRequestTimestamp !== undefined && lastRequestTimestamp + DEBOUNCE_DELAY_MS > Date.now()) {
return;
}
if (!userContext.fabricContext || !userContext.databaseAccount) {
return;
}
lastRequestTimestamp = Date.now();
try {
const resourceTokenInfo = await sendCachedDataMessage<ResourceTokenInfo>(
FabricMessageTypes.GetAllResourceTokens,
[],
userContext.fabricContext.artifactInfo.connectionId,
userContext.fabricContext.artifactInfo?.connectionId,
);
if (!userContext.databaseAccount.properties.documentEndpoint) {
@ -34,7 +38,7 @@ const requestDatabaseResourceTokens = async (): Promise<void> => {
...userContext.fabricContext,
databaseName: resourceTokenInfo.databaseId,
artifactInfo: {
...userContext.fabricContext.artifactInfo,
...(userContext.fabricContext.artifactInfo as FabricArtifactInfo[CosmosDbArtifactType.MIRRORED_KEY]),
resourceTokenInfo,
},
isReadOnly: resourceTokenInfo.isReadOnly ?? userContext.fabricContext.isReadOnly,
@ -43,7 +47,7 @@ const requestDatabaseResourceTokens = async (): Promise<void> => {
});
scheduleRefreshDatabaseResourceToken();
} catch (error) {
logConsoleError(error);
logConsoleError(error as string);
throw error;
} finally {
lastRequestTimestamp = undefined;
@ -77,10 +81,11 @@ export const checkDatabaseResourceTokensValidity = (tokenTimestamp: number): voi
}
};
export const isFabricMirrored = (): boolean =>
configContext.platform === Platform.Fabric &&
(userContext.fabricContext?.artifactType === CosmosDbArtifactType.MIRRORED_KEY ||
userContext.fabricContext?.artifactType === CosmosDbArtifactType.MIRRORED_AAD);
export const isFabric = (): boolean => configContext.platform === Platform.Fabric;
export const isFabricMirroredKey = (): boolean =>
isFabric() && userContext.fabricContext?.artifactType === CosmosDbArtifactType.MIRRORED_KEY;
export const isFabricMirroredAAD = (): boolean =>
isFabric() && userContext.fabricContext?.artifactType === CosmosDbArtifactType.MIRRORED_AAD;
export const isFabricMirrored = (): boolean => isFabricMirroredKey() || isFabricMirroredAAD();
export const isFabricNative = (): boolean =>
configContext.platform === Platform.Fabric && userContext.fabricContext?.artifactType === CosmosDbArtifactType.NATIVE;
isFabric() && userContext.fabricContext?.artifactType === CosmosDbArtifactType.NATIVE;

View File

@ -1,3 +1,4 @@
import { isFabric } from "Platform/Fabric/FabricUtil";
import { Platform, configContext } from "./../ConfigContext";
export const getDataExplorerWindow = (currentWindow: Window): Window | undefined => {
@ -7,7 +8,7 @@ export const getDataExplorerWindow = (currentWindow: Window): Window | undefined
if (currentWindow.parent === currentWindow) {
return undefined;
}
if (configContext.platform === Platform.Fabric && currentWindow.parent.parent === currentWindow.top) {
if (isFabric() && currentWindow.parent.parent === currentWindow.top) {
// in Fabric data explorer is inside an extension iframe, so we have two parent iframes
return currentWindow;
}

View File

@ -54,7 +54,7 @@ export enum ReactTabKind {
export const useTabs: UseStore<TabsState> = create((set, get) => ({
openedTabs: [] as TabsBase[],
openedReactTabs: [ReactTabKind.Home],
activeTab: undefined,
activeTab: undefined as TabsBase,
activeReactTab: ReactTabKind.Home,
queryCopilotTabInitialInput: "",
isTabExecuting: false,