mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-07-04 11:03:50 +01:00
Implement fabric native path
This commit is contained in:
parent
e5609bd91e
commit
0a6306ad40
@ -1,7 +1,7 @@
|
|||||||
import * as Cosmos from "@azure/cosmos";
|
import * as Cosmos from "@azure/cosmos";
|
||||||
import { getAuthorizationTokenUsingResourceTokens } from "Common/getAuthorizationTokenUsingResourceTokens";
|
import { getAuthorizationTokenUsingResourceTokens } from "Common/getAuthorizationTokenUsingResourceTokens";
|
||||||
import { AuthorizationToken } from "Contracts/FabricMessageTypes";
|
import { AuthorizationToken } from "Contracts/FabricMessageTypes";
|
||||||
import { checkDatabaseResourceTokensValidity } from "Platform/Fabric/FabricUtil";
|
import { checkDatabaseResourceTokensValidity, isFabricMirrored } from "Platform/Fabric/FabricUtil";
|
||||||
import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
|
import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
|
||||||
import { useNewPortalBackendEndpoint } from "Utils/EndpointUtils";
|
import { useNewPortalBackendEndpoint } from "Utils/EndpointUtils";
|
||||||
import { AuthType } from "../AuthType";
|
import { AuthType } from "../AuthType";
|
||||||
@ -42,7 +42,7 @@ export const tokenProvider = async (requestInfo: Cosmos.RequestInfo) => {
|
|||||||
return decodeURIComponent(headers.authorization);
|
return decodeURIComponent(headers.authorization);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (configContext.platform === Platform.Fabric) {
|
if (isFabricMirrored()) {
|
||||||
switch (requestInfo.resourceType) {
|
switch (requestInfo.resourceType) {
|
||||||
case Cosmos.ResourceType.conflicts:
|
case Cosmos.ResourceType.conflicts:
|
||||||
case Cosmos.ResourceType.container:
|
case Cosmos.ResourceType.container:
|
||||||
@ -54,8 +54,8 @@ export const tokenProvider = async (requestInfo: Cosmos.RequestInfo) => {
|
|||||||
// User resource tokens
|
// User resource tokens
|
||||||
// TODO userContext.fabricContext.databaseConnectionInfo can be undefined
|
// TODO userContext.fabricContext.databaseConnectionInfo can be undefined
|
||||||
headers[HttpHeaders.msDate] = new Date().toUTCString();
|
headers[HttpHeaders.msDate] = new Date().toUTCString();
|
||||||
const resourceTokens = userContext.fabricContext.databaseConnectionInfo.resourceTokens;
|
const resourceTokens = userContext.fabricContext.mirroredConnectionInfo.resourceTokens;
|
||||||
checkDatabaseResourceTokensValidity(userContext.fabricContext.databaseConnectionInfo.resourceTokensTimestamp);
|
checkDatabaseResourceTokensValidity(userContext.fabricContext.mirroredConnectionInfo.resourceTokensTimestamp);
|
||||||
return getAuthorizationTokenUsingResourceTokens(resourceTokens, requestInfo.path, requestInfo.resourceId);
|
return getAuthorizationTokenUsingResourceTokens(resourceTokens, requestInfo.path, requestInfo.resourceId);
|
||||||
|
|
||||||
case Cosmos.ResourceType.none:
|
case Cosmos.ResourceType.none:
|
||||||
@ -66,7 +66,7 @@ export const tokenProvider = async (requestInfo: Cosmos.RequestInfo) => {
|
|||||||
// For now, these operations aren't used, so fetching the authorization token is commented out.
|
// For now, these operations aren't used, so fetching the authorization token is commented out.
|
||||||
// This provider must return a real token to pass validation by the client, so we return the cached resource token
|
// This provider must return a real token to pass validation by the client, so we return the cached resource token
|
||||||
// (which is a valid token, but won't work for these operations).
|
// (which is a valid token, but won't work for these operations).
|
||||||
const resourceTokens2 = userContext.fabricContext.databaseConnectionInfo.resourceTokens;
|
const resourceTokens2 = userContext.fabricContext.mirroredConnectionInfo.resourceTokens;
|
||||||
return getAuthorizationTokenUsingResourceTokens(resourceTokens2, requestInfo.path, requestInfo.resourceId);
|
return getAuthorizationTokenUsingResourceTokens(resourceTokens2, requestInfo.path, requestInfo.resourceId);
|
||||||
|
|
||||||
/* ************** TODO: Uncomment this code if we need to support these operations **************
|
/* ************** TODO: Uncomment this code if we need to support these operations **************
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { ContainerResponse } from "@azure/cosmos";
|
import { ContainerResponse } from "@azure/cosmos";
|
||||||
import { Queries } from "Common/Constants";
|
import { Queries } from "Common/Constants";
|
||||||
import { Platform, configContext } from "ConfigContext";
|
import { isFabricMirrored } from "Platform/Fabric/FabricUtil";
|
||||||
import { AuthType } from "../../AuthType";
|
import { AuthType } from "../../AuthType";
|
||||||
import * as DataModels from "../../Contracts/DataModels";
|
import * as DataModels from "../../Contracts/DataModels";
|
||||||
import { userContext } from "../../UserContext";
|
import { userContext } from "../../UserContext";
|
||||||
@ -16,15 +16,11 @@ import { handleError } from "../ErrorHandlingUtils";
|
|||||||
export async function readCollections(databaseId: string): Promise<DataModels.Collection[]> {
|
export async function readCollections(databaseId: string): Promise<DataModels.Collection[]> {
|
||||||
const clearMessage = logConsoleProgress(`Querying containers for database ${databaseId}`);
|
const clearMessage = logConsoleProgress(`Querying containers for database ${databaseId}`);
|
||||||
|
|
||||||
if (
|
if (isFabricMirrored() && userContext.fabricContext?.mirroredConnectionInfo.databaseId === databaseId) {
|
||||||
configContext.platform === Platform.Fabric &&
|
|
||||||
userContext.fabricContext &&
|
|
||||||
userContext.fabricContext.databaseConnectionInfo.databaseId === databaseId
|
|
||||||
) {
|
|
||||||
const collections: DataModels.Collection[] = [];
|
const collections: DataModels.Collection[] = [];
|
||||||
const promises: Promise<ContainerResponse>[] = [];
|
const promises: Promise<ContainerResponse>[] = [];
|
||||||
|
|
||||||
for (const collectionResourceId in userContext.fabricContext.databaseConnectionInfo.resourceTokens) {
|
for (const collectionResourceId in userContext.fabricContext.mirroredConnectionInfo.resourceTokens) {
|
||||||
// Dictionary key looks like this: dbs/SampleDB/colls/Container
|
// Dictionary key looks like this: dbs/SampleDB/colls/Container
|
||||||
const resourceIdObj = collectionResourceId.split("/");
|
const resourceIdObj = collectionResourceId.split("/");
|
||||||
const tokenDatabaseId = resourceIdObj[1];
|
const tokenDatabaseId = resourceIdObj[1];
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Platform, configContext } from "ConfigContext";
|
import { isFabricMirrored } from "Platform/Fabric/FabricUtil";
|
||||||
import { AuthType } from "../../AuthType";
|
import { AuthType } from "../../AuthType";
|
||||||
import { Offer, ReadDatabaseOfferParams } from "../../Contracts/DataModels";
|
import { Offer, ReadDatabaseOfferParams } from "../../Contracts/DataModels";
|
||||||
import { userContext } from "../../UserContext";
|
import { userContext } from "../../UserContext";
|
||||||
@ -11,7 +11,7 @@ import { handleError } from "../ErrorHandlingUtils";
|
|||||||
import { readOfferWithSDK } from "./readOfferWithSDK";
|
import { readOfferWithSDK } from "./readOfferWithSDK";
|
||||||
|
|
||||||
export const readDatabaseOffer = async (params: ReadDatabaseOfferParams): Promise<Offer> => {
|
export const readDatabaseOffer = async (params: ReadDatabaseOfferParams): Promise<Offer> => {
|
||||||
if (configContext.platform === Platform.Fabric) {
|
if (isFabricMirrored()) {
|
||||||
// TODO This works, but is very slow, because it requests the token, so we skip for now
|
// TODO This works, but is very slow, because it requests the token, so we skip for now
|
||||||
console.error("Skiping readDatabaseOffer for Fabric");
|
console.error("Skiping readDatabaseOffer for Fabric");
|
||||||
return undefined;
|
return undefined;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Platform, configContext } from "ConfigContext";
|
import { isFabricMirrored } from "Platform/Fabric/FabricUtil";
|
||||||
import { AuthType } from "../../AuthType";
|
import { AuthType } from "../../AuthType";
|
||||||
import * as DataModels from "../../Contracts/DataModels";
|
import * as DataModels from "../../Contracts/DataModels";
|
||||||
import { userContext } from "../../UserContext";
|
import { userContext } from "../../UserContext";
|
||||||
@ -14,8 +14,8 @@ export async function readDatabases(): Promise<DataModels.Database[]> {
|
|||||||
let databases: DataModels.Database[];
|
let databases: DataModels.Database[];
|
||||||
const clearMessage = logConsoleProgress(`Querying databases`);
|
const clearMessage = logConsoleProgress(`Querying databases`);
|
||||||
|
|
||||||
if (configContext.platform === Platform.Fabric && userContext.fabricContext?.databaseConnectionInfo.resourceTokens) {
|
if (isFabricMirrored() && userContext.fabricContext?.mirroredConnectionInfo.resourceTokens) {
|
||||||
const tokensData = userContext.fabricContext.databaseConnectionInfo;
|
const tokensData = userContext.fabricContext.mirroredConnectionInfo;
|
||||||
|
|
||||||
const databaseIdsSet = new Set<string>(); // databaseId
|
const databaseIdsSet = new Set<string>(); // databaseId
|
||||||
|
|
||||||
|
@ -1,47 +1,9 @@
|
|||||||
import { AuthorizationToken } from "Contracts/FabricMessageTypes";
|
import { AuthorizationToken } from "./FabricMessageTypes";
|
||||||
|
|
||||||
// This is the version of these messages
|
// This is the version of these messages
|
||||||
export const FABRIC_RPC_VERSION = "2";
|
export const FABRIC_RPC_VERSION = "2";
|
||||||
|
|
||||||
// Fabric to Data Explorer
|
// Fabric to Data Explorer
|
||||||
|
|
||||||
// TODO Deprecated. Remove this section once DE is updated
|
|
||||||
export type FabricMessageV1 =
|
|
||||||
| {
|
|
||||||
type: "newContainer";
|
|
||||||
databaseName: string;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
type: "initialize";
|
|
||||||
message: {
|
|
||||||
endpoint: string | undefined;
|
|
||||||
databaseId: string | undefined;
|
|
||||||
resourceTokens: unknown | undefined;
|
|
||||||
resourceTokensTimestamp: number | undefined;
|
|
||||||
error: string | undefined;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
type: "authorizationToken";
|
|
||||||
message: {
|
|
||||||
id: string;
|
|
||||||
error: string | undefined;
|
|
||||||
data: AuthorizationToken | undefined;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
type: "allResourceTokens";
|
|
||||||
message: {
|
|
||||||
id: string;
|
|
||||||
error: string | undefined;
|
|
||||||
endpoint: string | undefined;
|
|
||||||
databaseId: string | undefined;
|
|
||||||
resourceTokens: unknown | undefined;
|
|
||||||
resourceTokensTimestamp: number | undefined;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
// -----------------------------
|
|
||||||
|
|
||||||
export type FabricMessageV2 =
|
export type FabricMessageV2 =
|
||||||
| {
|
| {
|
||||||
type: "newContainer";
|
type: "newContainer";
|
||||||
@ -54,6 +16,11 @@ export type FabricMessageV2 =
|
|||||||
message: {
|
message: {
|
||||||
connectionId: string;
|
connectionId: string;
|
||||||
isVisible: boolean;
|
isVisible: boolean;
|
||||||
|
isReadOnly: boolean;
|
||||||
|
artifactType: CosmosDbArtifactType;
|
||||||
|
|
||||||
|
// For Native artifacts
|
||||||
|
nativeConnectionInfo?: FabricNativeDatabaseConnectionInfo;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
@ -69,7 +36,7 @@ export type FabricMessageV2 =
|
|||||||
message: {
|
message: {
|
||||||
id: string;
|
id: string;
|
||||||
error: string | undefined;
|
error: string | undefined;
|
||||||
data: FabricDatabaseConnectionInfo | undefined;
|
data: FabricMirroredDatabaseConnectionInfo | undefined;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
@ -79,17 +46,29 @@ export type FabricMessageV2 =
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export type CosmosDBTokenResponse = {
|
export enum CosmosDbArtifactType {
|
||||||
|
MIRRORED = "MIRRORED",
|
||||||
|
NATIVE = "NATIVE",
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FabricNativeDatabaseConnectionInfo {
|
||||||
|
accessToken: string;
|
||||||
|
accountName: string;
|
||||||
|
databaseName: string;
|
||||||
|
connectionString: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CosmosDBTokenResponse {
|
||||||
token: string;
|
token: string;
|
||||||
date: string;
|
date: string;
|
||||||
};
|
}
|
||||||
|
|
||||||
export type CosmosDBConnectionInfoResponse = {
|
export interface CosmosDBConnectionInfoResponse {
|
||||||
endpoint: string;
|
endpoint: string;
|
||||||
databaseId: string;
|
databaseId: string;
|
||||||
resourceTokens: { [resourceId: string]: string };
|
resourceTokens: Record<string, string>;
|
||||||
};
|
}
|
||||||
|
|
||||||
export interface FabricDatabaseConnectionInfo extends CosmosDBConnectionInfoResponse {
|
export interface FabricMirroredDatabaseConnectionInfo extends CosmosDBConnectionInfoResponse {
|
||||||
resourceTokensTimestamp: number;
|
resourceTokensTimestamp: number;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
|
import { configContext, Platform } from "ConfigContext";
|
||||||
import { TreeNodeMenuItem } from "Explorer/Controls/TreeComponent/TreeNodeComponent";
|
import { TreeNodeMenuItem } from "Explorer/Controls/TreeComponent/TreeNodeComponent";
|
||||||
import { useDatabases } from "Explorer/useDatabases";
|
import { useDatabases } from "Explorer/useDatabases";
|
||||||
|
import { isFabricMirrored } from "Platform/Fabric/FabricUtil";
|
||||||
import { Action } from "Shared/Telemetry/TelemetryConstants";
|
import { Action } from "Shared/Telemetry/TelemetryConstants";
|
||||||
import { traceOpen } from "Shared/Telemetry/TelemetryProcessor";
|
import { traceOpen } from "Shared/Telemetry/TelemetryProcessor";
|
||||||
import { ReactTabKind, useTabs } from "hooks/useTabs";
|
import { ReactTabKind, useTabs } from "hooks/useTabs";
|
||||||
@ -19,7 +21,6 @@ import * as ViewModels from "../Contracts/ViewModels";
|
|||||||
import { userContext } from "../UserContext";
|
import { userContext } from "../UserContext";
|
||||||
import { getCollectionName, getDatabaseName } from "../Utils/APITypeUtils";
|
import { getCollectionName, getDatabaseName } from "../Utils/APITypeUtils";
|
||||||
import { useSidePanel } from "../hooks/useSidePanel";
|
import { useSidePanel } from "../hooks/useSidePanel";
|
||||||
import { Platform, configContext } from "./../ConfigContext";
|
|
||||||
import Explorer from "./Explorer";
|
import Explorer from "./Explorer";
|
||||||
import { useNotebook } from "./Notebook/useNotebook";
|
import { useNotebook } from "./Notebook/useNotebook";
|
||||||
import { DeleteCollectionConfirmationPane } from "./Panes/DeleteCollectionConfirmationPane/DeleteCollectionConfirmationPane";
|
import { DeleteCollectionConfirmationPane } from "./Panes/DeleteCollectionConfirmationPane/DeleteCollectionConfirmationPane";
|
||||||
@ -41,7 +42,7 @@ export interface DatabaseContextMenuButtonParams {
|
|||||||
* New resource tree (in ReactJS)
|
* New resource tree (in ReactJS)
|
||||||
*/
|
*/
|
||||||
export const createDatabaseContextMenu = (container: Explorer, databaseId: string): TreeNodeMenuItem[] => {
|
export const createDatabaseContextMenu = (container: Explorer, databaseId: string): TreeNodeMenuItem[] => {
|
||||||
if (configContext.platform === Platform.Fabric && userContext.fabricContext?.isReadOnly) {
|
if (isFabricMirrored() && userContext.fabricContext?.isReadOnly) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ import { MessageTypes } from "Contracts/ExplorerContracts";
|
|||||||
import { useDataPlaneRbac } from "Explorer/Panes/SettingsPane/SettingsPane";
|
import { useDataPlaneRbac } from "Explorer/Panes/SettingsPane/SettingsPane";
|
||||||
import { getCopilotEnabled, isCopilotFeatureRegistered } from "Explorer/QueryCopilot/Shared/QueryCopilotClient";
|
import { getCopilotEnabled, isCopilotFeatureRegistered } from "Explorer/QueryCopilot/Shared/QueryCopilotClient";
|
||||||
import { IGalleryItem } from "Juno/JunoClient";
|
import { IGalleryItem } from "Juno/JunoClient";
|
||||||
import { scheduleRefreshDatabaseResourceToken } from "Platform/Fabric/FabricUtil";
|
import { isFabricMirrored, scheduleRefreshDatabaseResourceToken } from "Platform/Fabric/FabricUtil";
|
||||||
import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
|
import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
|
||||||
import { acquireMsalTokenForAccount } from "Utils/AuthorizationUtils";
|
import { acquireMsalTokenForAccount } from "Utils/AuthorizationUtils";
|
||||||
import { allowedNotebookServerUrls, validateEndpoint } from "Utils/EndpointUtils";
|
import { allowedNotebookServerUrls, validateEndpoint } from "Utils/EndpointUtils";
|
||||||
@ -378,7 +378,7 @@ export default class Explorer {
|
|||||||
};
|
};
|
||||||
|
|
||||||
public onRefreshResourcesClick = async (): Promise<void> => {
|
public onRefreshResourcesClick = async (): Promise<void> => {
|
||||||
if (configContext.platform === Platform.Fabric) {
|
if (isFabricMirrored()) {
|
||||||
scheduleRefreshDatabaseResourceToken(true).then(() => this.refreshAllDatabases());
|
scheduleRefreshDatabaseResourceToken(true).then(() => this.refreshAllDatabases());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// TODO convert this file to an action registry in order to have actions and their handlers be more tightly coupled.
|
// TODO convert this file to an action registry in order to have actions and their handlers be more tightly coupled.
|
||||||
import { configContext, Platform } from "ConfigContext";
|
|
||||||
import { useDatabases } from "Explorer/useDatabases";
|
import { useDatabases } from "Explorer/useDatabases";
|
||||||
|
import { isFabricMirrored } from "Platform/Fabric/FabricUtil";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { ActionContracts } from "../../Contracts/ExplorerContracts";
|
import { ActionContracts } from "../../Contracts/ExplorerContracts";
|
||||||
import * as ViewModels from "../../Contracts/ViewModels";
|
import * as ViewModels from "../../Contracts/ViewModels";
|
||||||
@ -58,9 +58,9 @@ function openCollectionTab(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
configContext.platform === Platform.Fabric &&
|
isFabricMirrored() &&
|
||||||
!(
|
!(
|
||||||
// whitelist the tab kinds that are allowed to be opened in Fabric
|
// whitelist the tab kinds that are allowed to be opened in Fabric mirrored
|
||||||
(
|
(
|
||||||
action.tabKind === ActionContracts.TabKind.SQLDocuments ||
|
action.tabKind === ActionContracts.TabKind.SQLDocuments ||
|
||||||
action.tabKind === ActionContracts.TabKind.SQLQuery
|
action.tabKind === ActionContracts.TabKind.SQLQuery
|
||||||
|
@ -28,6 +28,7 @@ import {
|
|||||||
import { VectorEmbeddingPoliciesComponent } from "Explorer/Controls/VectorSearch/VectorEmbeddingPoliciesComponent";
|
import { VectorEmbeddingPoliciesComponent } from "Explorer/Controls/VectorSearch/VectorEmbeddingPoliciesComponent";
|
||||||
import { useSidePanel } from "hooks/useSidePanel";
|
import { useSidePanel } from "hooks/useSidePanel";
|
||||||
import { useTeachingBubble } from "hooks/useTeachingBubble";
|
import { useTeachingBubble } from "hooks/useTeachingBubble";
|
||||||
|
import { isFabricNative } from "Platform/Fabric/FabricUtil";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { CollectionCreation } from "Shared/Constants";
|
import { CollectionCreation } from "Shared/Constants";
|
||||||
import { Action } from "Shared/Telemetry/TelemetryConstants";
|
import { Action } from "Shared/Telemetry/TelemetryConstants";
|
||||||
@ -284,6 +285,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="panelMainContent">
|
<div className="panelMainContent">
|
||||||
|
{!(isFabricNative() && this.props.databaseId !== undefined) && (
|
||||||
<Stack hidden={userContext.apiType === "Tables"}>
|
<Stack hidden={userContext.apiType === "Tables"}>
|
||||||
<Stack horizontal>
|
<Stack horizontal>
|
||||||
<span className="mandatoryStar">* </span>
|
<span className="mandatoryStar">* </span>
|
||||||
@ -428,6 +430,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
)}
|
)}
|
||||||
<Separator className="panelSeparator" />
|
<Separator className="panelSeparator" />
|
||||||
</Stack>
|
</Stack>
|
||||||
|
)}
|
||||||
|
|
||||||
<Stack>
|
<Stack>
|
||||||
<Stack horizontal>
|
<Stack horizontal>
|
||||||
@ -666,7 +669,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
{userContext.apiType === "SQL" && (
|
{!isFabricNative() && userContext.apiType === "SQL" && (
|
||||||
<Stack className="panelGroupSpacing">
|
<Stack className="panelGroupSpacing">
|
||||||
<DefaultButton
|
<DefaultButton
|
||||||
styles={{ root: { padding: 0, width: 200, height: 30 }, label: { fontSize: 12 } }}
|
styles={{ root: { padding: 0, width: 200, height: 30 }, label: { fontSize: 12 } }}
|
||||||
@ -747,7 +750,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{userContext.apiType === "SQL" && (
|
{!isFabricNative() && userContext.apiType === "SQL" && (
|
||||||
<Stack>
|
<Stack>
|
||||||
<Stack horizontal>
|
<Stack horizontal>
|
||||||
<Text className="panelTextBold" variant="small">
|
<Text className="panelTextBold" variant="small">
|
||||||
@ -936,7 +939,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
</CollapsibleSectionComponent>
|
</CollapsibleSectionComponent>
|
||||||
</Stack>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
{userContext.apiType !== "Tables" && (
|
{!isFabricNative() && userContext.apiType !== "Tables" && (
|
||||||
<CollapsibleSectionComponent
|
<CollapsibleSectionComponent
|
||||||
title="Advanced"
|
title="Advanced"
|
||||||
isExpandedByDefault={false}
|
isExpandedByDefault={false}
|
||||||
@ -1255,7 +1258,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
private shouldShowCollectionThroughputInput(): boolean {
|
private shouldShowCollectionThroughputInput(): boolean {
|
||||||
if (isServerlessAccount()) {
|
if (isFabricNative() || isServerlessAccount()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1281,7 +1284,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private shouldShowAnalyticalStoreOptions(): boolean {
|
private shouldShowAnalyticalStoreOptions(): boolean {
|
||||||
if (configContext.platform === Platform.Emulator) {
|
if (isFabricNative() || configContext.platform === Platform.Emulator) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
|
makeStyles,
|
||||||
Menu,
|
Menu,
|
||||||
MenuButton,
|
MenuButton,
|
||||||
MenuButtonProps,
|
MenuButtonProps,
|
||||||
@ -7,13 +8,12 @@ import {
|
|||||||
MenuList,
|
MenuList,
|
||||||
MenuPopover,
|
MenuPopover,
|
||||||
MenuTrigger,
|
MenuTrigger,
|
||||||
SplitButton,
|
|
||||||
makeStyles,
|
|
||||||
mergeClasses,
|
mergeClasses,
|
||||||
shorthands,
|
shorthands,
|
||||||
|
SplitButton,
|
||||||
} from "@fluentui/react-components";
|
} from "@fluentui/react-components";
|
||||||
import { Add16Regular, ArrowSync12Regular, ChevronLeft12Regular, ChevronRight12Regular } from "@fluentui/react-icons";
|
import { Add16Regular, ArrowSync12Regular, ChevronLeft12Regular, ChevronRight12Regular } from "@fluentui/react-icons";
|
||||||
import { Platform, configContext } from "ConfigContext";
|
import { configContext, Platform } from "ConfigContext";
|
||||||
import Explorer from "Explorer/Explorer";
|
import Explorer from "Explorer/Explorer";
|
||||||
import { AddDatabasePanel } from "Explorer/Panes/AddDatabasePanel/AddDatabasePanel";
|
import { AddDatabasePanel } from "Explorer/Panes/AddDatabasePanel/AddDatabasePanel";
|
||||||
import { Tabs } from "Explorer/Tabs/Tabs";
|
import { Tabs } from "Explorer/Tabs/Tabs";
|
||||||
@ -21,6 +21,7 @@ import { CosmosFluentProvider, cosmosShorthands, tokens } from "Explorer/Theme/T
|
|||||||
import { ResourceTree } from "Explorer/Tree/ResourceTree";
|
import { ResourceTree } from "Explorer/Tree/ResourceTree";
|
||||||
import { useDatabases } from "Explorer/useDatabases";
|
import { useDatabases } from "Explorer/useDatabases";
|
||||||
import { KeyboardAction, KeyboardActionGroup, KeyboardActionHandler, useKeyboardActionGroup } from "KeyboardShortcuts";
|
import { KeyboardAction, KeyboardActionGroup, KeyboardActionHandler, useKeyboardActionGroup } from "KeyboardShortcuts";
|
||||||
|
import { isFabricMirrored, isFabricNative } from "Platform/Fabric/FabricUtil";
|
||||||
import { userContext } from "UserContext";
|
import { userContext } from "UserContext";
|
||||||
import { getCollectionName, getDatabaseName } from "Utils/APITypeUtils";
|
import { getCollectionName, getDatabaseName } from "Utils/APITypeUtils";
|
||||||
import { Allotment, AllotmentHandle } from "allotment";
|
import { Allotment, AllotmentHandle } from "allotment";
|
||||||
@ -120,11 +121,7 @@ const GlobalCommands: React.FC<GlobalCommandsProps> = ({ explorer }) => {
|
|||||||
const [globalCommandButton, setGlobalCommandButton] = useState<HTMLElement | null>(null);
|
const [globalCommandButton, setGlobalCommandButton] = useState<HTMLElement | null>(null);
|
||||||
|
|
||||||
const actions = useMemo<GlobalCommand[]>(() => {
|
const actions = useMemo<GlobalCommand[]>(() => {
|
||||||
if (
|
if (isFabricMirrored() || userContext.apiType === "Postgres" || userContext.apiType === "VCoreMongo") {
|
||||||
configContext.platform === Platform.Fabric ||
|
|
||||||
userContext.apiType === "Postgres" ||
|
|
||||||
userContext.apiType === "VCoreMongo"
|
|
||||||
) {
|
|
||||||
// No Global Commands for these API types.
|
// 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.
|
// In fact, no sidebar for Postgres or VCoreMongo at all, but just in case, we check here anyway.
|
||||||
return [];
|
return [];
|
||||||
@ -135,12 +132,17 @@ const GlobalCommands: React.FC<GlobalCommandsProps> = ({ explorer }) => {
|
|||||||
id: "new_collection",
|
id: "new_collection",
|
||||||
label: `New ${getCollectionName()}`,
|
label: `New ${getCollectionName()}`,
|
||||||
icon: <Add16Regular />,
|
icon: <Add16Regular />,
|
||||||
onClick: () => explorer.onNewCollectionClicked(),
|
onClick: () => {
|
||||||
|
const databaseId = isFabricNative()
|
||||||
|
? userContext.fabricContext?.nativeConnectionInfo?.databaseName
|
||||||
|
: undefined;
|
||||||
|
explorer.onNewCollectionClicked({ databaseId });
|
||||||
|
},
|
||||||
keyboardAction: KeyboardAction.NEW_COLLECTION,
|
keyboardAction: KeyboardAction.NEW_COLLECTION,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
if (userContext.apiType !== "Tables") {
|
if (configContext.platform !== Platform.Fabric && userContext.apiType !== "Tables") {
|
||||||
actions.push({
|
actions.push({
|
||||||
id: "new_database",
|
id: "new_database",
|
||||||
label: `New ${getDatabaseName()}`,
|
label: `New ${getDatabaseName()}`,
|
||||||
@ -276,7 +278,7 @@ export const SidebarContainer: React.FC<SidebarProps> = ({ explorer }) => {
|
|||||||
}, [setLoading]);
|
}, [setLoading]);
|
||||||
|
|
||||||
const hasGlobalCommands = !(
|
const hasGlobalCommands = !(
|
||||||
configContext.platform === Platform.Fabric ||
|
isFabricMirrored() ||
|
||||||
userContext.apiType === "Postgres" ||
|
userContext.apiType === "Postgres" ||
|
||||||
userContext.apiType === "VCoreMongo"
|
userContext.apiType === "VCoreMongo"
|
||||||
);
|
);
|
||||||
|
175
src/Explorer/SplashScreen/FabricHome.tsx
Normal file
175
src/Explorer/SplashScreen/FabricHome.tsx
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
/**
|
||||||
|
* Accordion top class
|
||||||
|
*/
|
||||||
|
import { Link, makeStyles, tokens } from "@fluentui/react-components";
|
||||||
|
import { DocumentAddRegular, LinkMultipleRegular } from "@fluentui/react-icons";
|
||||||
|
import { isFabricNative } from "Platform/Fabric/FabricUtil";
|
||||||
|
import * as React from "react";
|
||||||
|
import { userContext } from "UserContext";
|
||||||
|
import CosmosDbBlackIcon from "../../../images/CosmosDB_black.svg";
|
||||||
|
import LinkIcon from "../../../images/Link_blue.svg";
|
||||||
|
import Explorer from "../Explorer";
|
||||||
|
|
||||||
|
export interface SplashScreenProps {
|
||||||
|
explorer: Explorer;
|
||||||
|
}
|
||||||
|
|
||||||
|
const useStyles = makeStyles({
|
||||||
|
homeContainer: {
|
||||||
|
width: "100%",
|
||||||
|
alignContent: "center",
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
textAlign: "center",
|
||||||
|
fontSize: "20px",
|
||||||
|
fontWeight: "bold",
|
||||||
|
},
|
||||||
|
buttonsContainer: {
|
||||||
|
width: "584px",
|
||||||
|
margin: "auto",
|
||||||
|
display: "grid",
|
||||||
|
padding: "16px",
|
||||||
|
gridTemplateColumns: "repeat(3, 1fr)",
|
||||||
|
gap: "10px",
|
||||||
|
gridAutoRows: "minmax(184px, auto)",
|
||||||
|
},
|
||||||
|
one: {
|
||||||
|
gridColumn: "1 / 3",
|
||||||
|
gridRow: "1 / 3",
|
||||||
|
"& svg": {
|
||||||
|
width: "48px",
|
||||||
|
height: "48px",
|
||||||
|
margin: "auto",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
two: {
|
||||||
|
gridColumn: "3",
|
||||||
|
gridRow: "1",
|
||||||
|
"& img": {
|
||||||
|
width: "32px",
|
||||||
|
height: "32px",
|
||||||
|
margin: "auto",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
three: {
|
||||||
|
gridColumn: "3",
|
||||||
|
gridRow: "2",
|
||||||
|
"& svg": {
|
||||||
|
width: "32px",
|
||||||
|
height: "32px",
|
||||||
|
margin: "auto",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
buttonContainer: {
|
||||||
|
height: "100%",
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
border: "1px solid #e0e0e0",
|
||||||
|
cursor: "pointer",
|
||||||
|
"&:hover": {
|
||||||
|
backgroundColor: tokens.colorNeutralBackground1Hover,
|
||||||
|
"border-color": tokens.colorNeutralStroke1Hover,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
buttonUpperPart: {
|
||||||
|
textAlign: "center",
|
||||||
|
flexGrow: 1,
|
||||||
|
display: "flex",
|
||||||
|
backgroundColor: "#e3f7ef",
|
||||||
|
},
|
||||||
|
buttonLowerPart: {
|
||||||
|
borderTop: "1px solid #e0e0e0",
|
||||||
|
height: "76px",
|
||||||
|
padding: "8px",
|
||||||
|
"> div:nth-child(1)": {
|
||||||
|
fontWeight: "bold",
|
||||||
|
},
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
justifyContent: "center",
|
||||||
|
},
|
||||||
|
footer: {
|
||||||
|
textAlign: "center",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
interface FabricHomeScreenButtonProps {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
icon: JSX.Element;
|
||||||
|
onClick?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FabricHomeScreenButton: React.FC<FabricHomeScreenButtonProps & { className: string }> = ({
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
icon,
|
||||||
|
className,
|
||||||
|
onClick,
|
||||||
|
}) => {
|
||||||
|
const styles = useStyles();
|
||||||
|
|
||||||
|
// TODO Make this a11y copmliant: aria-label for icon
|
||||||
|
return (
|
||||||
|
<div role="button" className={`${styles.buttonContainer} ${className}`} onClick={onClick}>
|
||||||
|
<div className={styles.buttonUpperPart}>{icon}</div>
|
||||||
|
<div className={styles.buttonLowerPart}>
|
||||||
|
<div>{title}</div>
|
||||||
|
<div>{description}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const FabricHomeScreen: React.FC<SplashScreenProps> = (props: SplashScreenProps) => {
|
||||||
|
const styles = useStyles();
|
||||||
|
const getSplashScreenButtons = (): JSX.Element => {
|
||||||
|
const buttons: FabricHomeScreenButtonProps[] = [
|
||||||
|
{
|
||||||
|
title: "New container",
|
||||||
|
description: "Create a destination container to store your data",
|
||||||
|
icon: <DocumentAddRegular />,
|
||||||
|
onClick: () => {
|
||||||
|
const databaseId = isFabricNative()
|
||||||
|
? userContext.fabricContext?.nativeConnectionInfo?.databaseName
|
||||||
|
: undefined;
|
||||||
|
props.explorer.onNewCollectionClicked({ databaseId });
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Sample data",
|
||||||
|
description: "Automatically load sample data in your database",
|
||||||
|
icon: <img src={CosmosDbBlackIcon} />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "App development",
|
||||||
|
description: "Start here to use an SDK to build your apps",
|
||||||
|
icon: <LinkMultipleRegular />,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.buttonsContainer}>
|
||||||
|
<FabricHomeScreenButton className={styles.one} {...buttons[0]} />
|
||||||
|
<FabricHomeScreenButton className={styles.two} {...buttons[1]} />
|
||||||
|
<FabricHomeScreenButton className={styles.three} {...buttons[2]} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const title = "Build your database";
|
||||||
|
return (
|
||||||
|
<div className={styles.homeContainer}>
|
||||||
|
<div className={styles.title} role="heading" aria-label={title}>
|
||||||
|
{title}
|
||||||
|
</div>
|
||||||
|
{getSplashScreenButtons()}
|
||||||
|
<div className={styles.footer}>
|
||||||
|
Need help?{" "}
|
||||||
|
<Link href="https://cosmos.azure.com/docs" target="_blank">
|
||||||
|
Learn more <img src={LinkIcon} alt="Learn more" />
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@ -2,6 +2,7 @@ import { FeedResponse, ItemDefinition, Resource } from "@azure/cosmos";
|
|||||||
import { waitFor } from "@testing-library/react";
|
import { waitFor } from "@testing-library/react";
|
||||||
import { deleteDocuments } from "Common/dataAccess/deleteDocument";
|
import { deleteDocuments } from "Common/dataAccess/deleteDocument";
|
||||||
import { Platform, updateConfigContext } from "ConfigContext";
|
import { Platform, updateConfigContext } from "ConfigContext";
|
||||||
|
import { CosmosDbArtifactType } from "Contracts/FabricMessagesContract";
|
||||||
import { useDialog } from "Explorer/Controls/Dialog";
|
import { useDialog } from "Explorer/Controls/Dialog";
|
||||||
import { EditorReactProps } from "Explorer/Controls/Editor/EditorReact";
|
import { EditorReactProps } from "Explorer/Controls/Editor/EditorReact";
|
||||||
import { ProgressModalDialog } from "Explorer/Controls/ProgressModalDialog";
|
import { ProgressModalDialog } from "Explorer/Controls/ProgressModalDialog";
|
||||||
@ -71,7 +72,7 @@ jest.mock("Explorer/Controls/Editor/EditorReact", () => ({
|
|||||||
|
|
||||||
const mockDialogState = {
|
const mockDialogState = {
|
||||||
showOkCancelModalDialog: jest.fn((title: string, subText: string, okLabel: string, onOk: () => void) => onOk()),
|
showOkCancelModalDialog: jest.fn((title: string, subText: string, okLabel: string, onOk: () => void) => onOk()),
|
||||||
showOkModalDialog: () => {},
|
showOkModalDialog: () => { },
|
||||||
};
|
};
|
||||||
|
|
||||||
jest.mock("Explorer/Controls/Dialog", () => ({
|
jest.mock("Explorer/Controls/Dialog", () => ({
|
||||||
@ -342,7 +343,9 @@ describe("Documents tab (noSql API)", () => {
|
|||||||
updateUserContext({
|
updateUserContext({
|
||||||
fabricContext: {
|
fabricContext: {
|
||||||
connectionId: "test",
|
connectionId: "test",
|
||||||
databaseConnectionInfo: undefined,
|
mirroredConnectionInfo: undefined,
|
||||||
|
nativeConnectionInfo: undefined,
|
||||||
|
artifactType: CosmosDbArtifactType.MIRRORED,
|
||||||
isReadOnly: true,
|
isReadOnly: true,
|
||||||
isVisible: true,
|
isVisible: true,
|
||||||
},
|
},
|
||||||
|
@ -6,6 +6,7 @@ import { CollectionTabKind } from "Contracts/ViewModels";
|
|||||||
import Explorer from "Explorer/Explorer";
|
import Explorer from "Explorer/Explorer";
|
||||||
import { useCommandBar } from "Explorer/Menus/CommandBar/CommandBarComponentAdapter";
|
import { useCommandBar } from "Explorer/Menus/CommandBar/CommandBarComponentAdapter";
|
||||||
import { QueryCopilotTab } from "Explorer/QueryCopilot/QueryCopilotTab";
|
import { QueryCopilotTab } from "Explorer/QueryCopilot/QueryCopilotTab";
|
||||||
|
import { FabricHomeScreen } from "Explorer/SplashScreen/FabricHome";
|
||||||
import { SplashScreen } from "Explorer/SplashScreen/SplashScreen";
|
import { SplashScreen } from "Explorer/SplashScreen/SplashScreen";
|
||||||
import { ConnectTab } from "Explorer/Tabs/ConnectTab";
|
import { ConnectTab } from "Explorer/Tabs/ConnectTab";
|
||||||
import { PostgresConnectTab } from "Explorer/Tabs/PostgresConnectTab";
|
import { PostgresConnectTab } from "Explorer/Tabs/PostgresConnectTab";
|
||||||
@ -14,6 +15,7 @@ import { VcoreMongoConnectTab } from "Explorer/Tabs/VCoreMongoConnectTab";
|
|||||||
import { VcoreMongoQuickstartTab } from "Explorer/Tabs/VCoreMongoQuickstartTab";
|
import { VcoreMongoQuickstartTab } from "Explorer/Tabs/VCoreMongoQuickstartTab";
|
||||||
import { LayoutConstants } from "Explorer/Theme/ThemeUtil";
|
import { LayoutConstants } from "Explorer/Theme/ThemeUtil";
|
||||||
import { KeyboardAction, KeyboardActionGroup, useKeyboardActionGroup } from "KeyboardShortcuts";
|
import { KeyboardAction, KeyboardActionGroup, useKeyboardActionGroup } from "KeyboardShortcuts";
|
||||||
|
import { isFabricNative } from "Platform/Fabric/FabricUtil";
|
||||||
import { userContext } from "UserContext";
|
import { userContext } from "UserContext";
|
||||||
import { CassandraProxyOutboundIPs, MongoProxyOutboundIPs, PortalBackendIPs } from "Utils/EndpointUtils";
|
import { CassandraProxyOutboundIPs, MongoProxyOutboundIPs, PortalBackendIPs } from "Utils/EndpointUtils";
|
||||||
import { useTeachingBubble } from "hooks/useTeachingBubble";
|
import { useTeachingBubble } from "hooks/useTeachingBubble";
|
||||||
@ -301,7 +303,11 @@ const getReactTabContent = (activeReactTab: ReactTabKind, explorer: Explorer): J
|
|||||||
<ConnectTab />
|
<ConnectTab />
|
||||||
);
|
);
|
||||||
case ReactTabKind.Home:
|
case ReactTabKind.Home:
|
||||||
|
if (isFabricNative()) {
|
||||||
|
return <FabricHomeScreen explorer={explorer} />;
|
||||||
|
} else {
|
||||||
return <SplashScreen explorer={explorer} />;
|
return <SplashScreen explorer={explorer} />;
|
||||||
|
}
|
||||||
case ReactTabKind.Quickstart:
|
case ReactTabKind.Quickstart:
|
||||||
return userContext.apiType === "VCoreMongo" ? (
|
return userContext.apiType === "VCoreMongo" ? (
|
||||||
<VcoreMongoQuickstartTab explorer={explorer} />
|
<VcoreMongoQuickstartTab explorer={explorer} />
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { Resource, StoredProcedureDefinition, TriggerDefinition, UserDefinedFunctionDefinition } from "@azure/cosmos";
|
import { Resource, StoredProcedureDefinition, TriggerDefinition, UserDefinedFunctionDefinition } from "@azure/cosmos";
|
||||||
import { useNotebook } from "Explorer/Notebook/useNotebook";
|
import { useNotebook } from "Explorer/Notebook/useNotebook";
|
||||||
import { DocumentsTabV2 } from "Explorer/Tabs/DocumentsTabV2/DocumentsTabV2";
|
import { DocumentsTabV2 } from "Explorer/Tabs/DocumentsTabV2/DocumentsTabV2";
|
||||||
|
import { isFabricMirrored } from "Platform/Fabric/FabricUtil";
|
||||||
import * as ko from "knockout";
|
import * as ko from "knockout";
|
||||||
import * as _ from "underscore";
|
import * as _ from "underscore";
|
||||||
import * as Constants from "../../Common/Constants";
|
import * as Constants from "../../Common/Constants";
|
||||||
@ -34,7 +35,6 @@ import QueryTablesTab from "../Tabs/QueryTablesTab";
|
|||||||
import { CollectionSettingsTabV2 } from "../Tabs/SettingsTabV2";
|
import { CollectionSettingsTabV2 } from "../Tabs/SettingsTabV2";
|
||||||
import { useDatabases } from "../useDatabases";
|
import { useDatabases } from "../useDatabases";
|
||||||
import { useSelectedNode } from "../useSelectedNode";
|
import { useSelectedNode } from "../useSelectedNode";
|
||||||
import { Platform, configContext } from "./../../ConfigContext";
|
|
||||||
import ConflictId from "./ConflictId";
|
import ConflictId from "./ConflictId";
|
||||||
import DocumentId from "./DocumentId";
|
import DocumentId from "./DocumentId";
|
||||||
import StoredProcedure from "./StoredProcedure";
|
import StoredProcedure from "./StoredProcedure";
|
||||||
@ -210,7 +210,7 @@ export default class Collection implements ViewModels.Collection {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const showScriptsMenus: boolean =
|
const showScriptsMenus: boolean =
|
||||||
configContext.platform != Platform.Fabric && (userContext.apiType === "SQL" || userContext.apiType === "Gremlin");
|
!isFabricMirrored() && (userContext.apiType === "SQL" || userContext.apiType === "Gremlin");
|
||||||
this.showStoredProcedures = ko.observable<boolean>(showScriptsMenus);
|
this.showStoredProcedures = ko.observable<boolean>(showScriptsMenus);
|
||||||
this.showTriggers = ko.observable<boolean>(showScriptsMenus);
|
this.showTriggers = ko.observable<boolean>(showScriptsMenus);
|
||||||
this.showUserDefinedFunctions = ko.observable<boolean>(showScriptsMenus);
|
this.showUserDefinedFunctions = ko.observable<boolean>(showScriptsMenus);
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { Tree, TreeItemValue, TreeOpenChangeData, TreeOpenChangeEvent } from "@fluentui/react-components";
|
import { Tree, TreeItemValue, TreeOpenChangeData, TreeOpenChangeEvent } from "@fluentui/react-components";
|
||||||
import { Home16Regular } from "@fluentui/react-icons";
|
import { Home16Regular } from "@fluentui/react-icons";
|
||||||
import { AuthType } from "AuthType";
|
import { AuthType } from "AuthType";
|
||||||
import { Platform, configContext } from "ConfigContext";
|
|
||||||
import { useTreeStyles } from "Explorer/Controls/TreeComponent/Styles";
|
import { useTreeStyles } from "Explorer/Controls/TreeComponent/Styles";
|
||||||
import { TreeNode, TreeNodeComponent } from "Explorer/Controls/TreeComponent/TreeNodeComponent";
|
import { TreeNode, TreeNodeComponent } from "Explorer/Controls/TreeComponent/TreeNodeComponent";
|
||||||
import {
|
import {
|
||||||
@ -11,6 +10,7 @@ import {
|
|||||||
} from "Explorer/Tree/treeNodeUtil";
|
} from "Explorer/Tree/treeNodeUtil";
|
||||||
import { useDatabases } from "Explorer/useDatabases";
|
import { useDatabases } from "Explorer/useDatabases";
|
||||||
import { useSelectedNode } from "Explorer/useSelectedNode";
|
import { useSelectedNode } from "Explorer/useSelectedNode";
|
||||||
|
import { isFabricMirrored } from "Platform/Fabric/FabricUtil";
|
||||||
import { userContext } from "UserContext";
|
import { userContext } from "UserContext";
|
||||||
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||||
import { ReactTabKind, useTabs } from "hooks/useTabs";
|
import { ReactTabKind, useTabs } from "hooks/useTabs";
|
||||||
@ -76,8 +76,7 @@ export const ResourceTree: React.FC<ResourceTreeProps> = ({ explorer }: Resource
|
|||||||
: [];
|
: [];
|
||||||
}, [isSampleDataEnabled, sampleDataResourceTokenCollection]);
|
}, [isSampleDataEnabled, sampleDataResourceTokenCollection]);
|
||||||
|
|
||||||
const headerNodes: TreeNode[] =
|
const headerNodes: TreeNode[] = isFabricMirrored()
|
||||||
configContext.platform === Platform.Fabric
|
|
||||||
? []
|
? []
|
||||||
: [
|
: [
|
||||||
{
|
{
|
||||||
|
@ -6,6 +6,7 @@ import StoredProcedure from "Explorer/Tree/StoredProcedure";
|
|||||||
import Trigger from "Explorer/Tree/Trigger";
|
import Trigger from "Explorer/Tree/Trigger";
|
||||||
import UserDefinedFunction from "Explorer/Tree/UserDefinedFunction";
|
import UserDefinedFunction from "Explorer/Tree/UserDefinedFunction";
|
||||||
import { useDatabases } from "Explorer/useDatabases";
|
import { useDatabases } from "Explorer/useDatabases";
|
||||||
|
import { isFabricMirrored } from "Platform/Fabric/FabricUtil";
|
||||||
import { getItemName } from "Utils/APITypeUtils";
|
import { getItemName } from "Utils/APITypeUtils";
|
||||||
import { isServerlessAccount } from "Utils/CapabilityUtils";
|
import { isServerlessAccount } from "Utils/CapabilityUtils";
|
||||||
import { useTabs } from "hooks/useTabs";
|
import { useTabs } from "hooks/useTabs";
|
||||||
@ -22,9 +23,7 @@ import { useNotebook } from "../Notebook/useNotebook";
|
|||||||
import { useSelectedNode } from "../useSelectedNode";
|
import { useSelectedNode } from "../useSelectedNode";
|
||||||
|
|
||||||
export const shouldShowScriptNodes = (): boolean => {
|
export const shouldShowScriptNodes = (): boolean => {
|
||||||
return (
|
return !isFabricMirrored() && (userContext.apiType === "SQL" || userContext.apiType === "Gremlin");
|
||||||
configContext.platform !== Platform.Fabric && (userContext.apiType === "SQL" || userContext.apiType === "Gremlin")
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const TreeDatabaseIcon = <DatabaseRegular fontSize={16} />;
|
const TreeDatabaseIcon = <DatabaseRegular fontSize={16} />;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { sendCachedDataMessage } from "Common/MessageHandler";
|
import { sendCachedDataMessage } from "Common/MessageHandler";
|
||||||
|
import { configContext, Platform } from "ConfigContext";
|
||||||
import { FabricMessageTypes } from "Contracts/FabricMessageTypes";
|
import { FabricMessageTypes } from "Contracts/FabricMessageTypes";
|
||||||
import { FabricDatabaseConnectionInfo } from "Contracts/FabricMessagesContract";
|
import { CosmosDbArtifactType, FabricMirroredDatabaseConnectionInfo } from "Contracts/FabricMessagesContract";
|
||||||
import { updateUserContext, userContext } from "UserContext";
|
import { updateUserContext, userContext } from "UserContext";
|
||||||
import { logConsoleError } from "Utils/NotificationConsoleUtils";
|
import { logConsoleError } from "Utils/NotificationConsoleUtils";
|
||||||
|
|
||||||
@ -18,7 +19,7 @@ const requestDatabaseResourceTokens = async (): Promise<void> => {
|
|||||||
|
|
||||||
lastRequestTimestamp = Date.now();
|
lastRequestTimestamp = Date.now();
|
||||||
try {
|
try {
|
||||||
const fabricDatabaseConnectionInfo = await sendCachedDataMessage<FabricDatabaseConnectionInfo>(
|
const fabricDatabaseConnectionInfo = await sendCachedDataMessage<FabricMirroredDatabaseConnectionInfo>(
|
||||||
FabricMessageTypes.GetAllResourceTokens,
|
FabricMessageTypes.GetAllResourceTokens,
|
||||||
[],
|
[],
|
||||||
userContext.fabricContext.connectionId,
|
userContext.fabricContext.connectionId,
|
||||||
@ -31,7 +32,7 @@ const requestDatabaseResourceTokens = async (): Promise<void> => {
|
|||||||
updateUserContext({
|
updateUserContext({
|
||||||
fabricContext: {
|
fabricContext: {
|
||||||
...userContext.fabricContext,
|
...userContext.fabricContext,
|
||||||
databaseConnectionInfo: fabricDatabaseConnectionInfo,
|
mirroredConnectionInfo: fabricDatabaseConnectionInfo,
|
||||||
isReadOnly: true,
|
isReadOnly: true,
|
||||||
},
|
},
|
||||||
databaseAccount: { ...userContext.databaseAccount },
|
databaseAccount: { ...userContext.databaseAccount },
|
||||||
@ -71,3 +72,10 @@ export const checkDatabaseResourceTokensValidity = (tokenTimestamp: number): voi
|
|||||||
scheduleRefreshDatabaseResourceToken(true);
|
scheduleRefreshDatabaseResourceToken(true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const isFabricMirrored = (): boolean =>
|
||||||
|
configContext.platform === Platform.Fabric &&
|
||||||
|
userContext.fabricContext?.artifactType === CosmosDbArtifactType.MIRRORED;
|
||||||
|
|
||||||
|
export const isFabricNative = (): boolean =>
|
||||||
|
configContext.platform === Platform.Fabric && userContext.fabricContext?.artifactType === CosmosDbArtifactType.NATIVE;
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
import { FabricDatabaseConnectionInfo } from "Contracts/FabricMessagesContract";
|
import {
|
||||||
|
CosmosDbArtifactType,
|
||||||
|
FabricMirroredDatabaseConnectionInfo,
|
||||||
|
FabricNativeDatabaseConnectionInfo,
|
||||||
|
} from "Contracts/FabricMessagesContract";
|
||||||
import { ParsedResourceTokenConnectionString } from "Platform/Hosted/Helpers/ResourceTokenUtils";
|
import { ParsedResourceTokenConnectionString } from "Platform/Hosted/Helpers/ResourceTokenUtils";
|
||||||
import { Action } from "Shared/Telemetry/TelemetryConstants";
|
import { Action } from "Shared/Telemetry/TelemetryConstants";
|
||||||
import { traceOpen } from "Shared/Telemetry/TelemetryProcessor";
|
import { traceOpen } from "Shared/Telemetry/TelemetryProcessor";
|
||||||
@ -49,9 +53,11 @@ export interface VCoreMongoConnectionParams {
|
|||||||
|
|
||||||
interface FabricContext {
|
interface FabricContext {
|
||||||
connectionId: string;
|
connectionId: string;
|
||||||
databaseConnectionInfo: FabricDatabaseConnectionInfo | undefined;
|
|
||||||
isReadOnly: boolean;
|
isReadOnly: boolean;
|
||||||
isVisible: boolean;
|
isVisible: boolean;
|
||||||
|
artifactType: CosmosDbArtifactType;
|
||||||
|
mirroredConnectionInfo: FabricMirroredDatabaseConnectionInfo | undefined;
|
||||||
|
nativeConnectionInfo: FabricNativeDatabaseConnectionInfo | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AdminFeedbackControlPolicy =
|
export type AdminFeedbackControlPolicy =
|
||||||
|
@ -2,7 +2,12 @@ import * as Constants from "Common/Constants";
|
|||||||
import { createUri } from "Common/UrlUtility";
|
import { createUri } from "Common/UrlUtility";
|
||||||
import { DATA_EXPLORER_RPC_VERSION } from "Contracts/DataExplorerMessagesContract";
|
import { DATA_EXPLORER_RPC_VERSION } from "Contracts/DataExplorerMessagesContract";
|
||||||
import { FabricMessageTypes } from "Contracts/FabricMessageTypes";
|
import { FabricMessageTypes } from "Contracts/FabricMessageTypes";
|
||||||
import { FABRIC_RPC_VERSION, FabricMessageV2 } from "Contracts/FabricMessagesContract";
|
import {
|
||||||
|
CosmosDbArtifactType,
|
||||||
|
FABRIC_RPC_VERSION,
|
||||||
|
FabricMessageV2,
|
||||||
|
FabricNativeDatabaseConnectionInfo,
|
||||||
|
} from "Contracts/FabricMessagesContract";
|
||||||
import Explorer from "Explorer/Explorer";
|
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 { useSelectedNode } from "Explorer/useSelectedNode";
|
||||||
@ -23,7 +28,7 @@ import { AccountKind, Flights } from "../Common/Constants";
|
|||||||
import { normalizeArmEndpoint } from "../Common/EnvironmentUtility";
|
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 { handleCachedDataMessage, sendMessage, sendReadyMessage } from "../Common/MessageHandler";
|
||||||
import { Platform, configContext, updateConfigContext } from "../ConfigContext";
|
import { configContext, Platform, updateConfigContext } from "../ConfigContext";
|
||||||
import { ActionType, DataExplorerAction, TabKind } from "../Contracts/ActionContracts";
|
import { ActionType, DataExplorerAction, TabKind } from "../Contracts/ActionContracts";
|
||||||
import { MessageTypes } from "../Contracts/ExplorerContracts";
|
import { MessageTypes } from "../Contracts/ExplorerContracts";
|
||||||
import { DataExplorerInputsFrame } from "../Contracts/ViewModels";
|
import { DataExplorerInputsFrame } from "../Contracts/ViewModels";
|
||||||
@ -85,9 +90,7 @@ export function useKnockoutExplorer(platform: Platform): Explorer {
|
|||||||
await updateContextForSampleData(explorer);
|
await updateContextForSampleData(explorer);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (userContext.features.restoreTabs) {
|
|
||||||
restoreOpenTabs();
|
restoreOpenTabs();
|
||||||
}
|
|
||||||
|
|
||||||
setExplorer(explorer);
|
setExplorer(explorer);
|
||||||
}
|
}
|
||||||
@ -138,12 +141,17 @@ async function configureFabric(): Promise<Explorer> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
explorer = createExplorerFabric(data.message);
|
explorer = createExplorerFabric(data.message);
|
||||||
|
|
||||||
|
if (data.message.artifactType === CosmosDbArtifactType.MIRRORED) {
|
||||||
await scheduleRefreshDatabaseResourceToken(true);
|
await scheduleRefreshDatabaseResourceToken(true);
|
||||||
|
}
|
||||||
|
|
||||||
resolve(explorer);
|
resolve(explorer);
|
||||||
await explorer.refreshAllDatabases();
|
await explorer.refreshAllDatabases();
|
||||||
if (userContext.fabricContext.isVisible) {
|
|
||||||
|
if (userContext.fabricContext.isVisible && userContext.fabricContext.mirroredConnectionInfo?.databaseId) {
|
||||||
firstContainerOpened = true;
|
firstContainerOpened = true;
|
||||||
openFirstContainer(explorer, userContext.fabricContext.databaseConnectionInfo.databaseId);
|
openFirstContainer(explorer, userContext.fabricContext.mirroredConnectionInfo.databaseId);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -160,10 +168,10 @@ async function configureFabric(): Promise<Explorer> {
|
|||||||
if (
|
if (
|
||||||
userContext.fabricContext.isVisible &&
|
userContext.fabricContext.isVisible &&
|
||||||
!firstContainerOpened &&
|
!firstContainerOpened &&
|
||||||
userContext?.fabricContext?.databaseConnectionInfo?.databaseId !== undefined
|
userContext?.fabricContext?.mirroredConnectionInfo?.databaseId !== undefined
|
||||||
) {
|
) {
|
||||||
firstContainerOpened = true;
|
firstContainerOpened = true;
|
||||||
openFirstContainer(explorer, userContext.fabricContext.databaseConnectionInfo.databaseId);
|
openFirstContainer(explorer, userContext.fabricContext.mirroredConnectionInfo.databaseId);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -418,15 +426,29 @@ function configureHostedWithResourceToken(config: ResourceToken): Explorer {
|
|||||||
return explorer;
|
return explorer;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createExplorerFabric(params: { connectionId: string; isVisible: boolean }): Explorer {
|
const createExplorerFabric = (params: {
|
||||||
|
connectionId: string;
|
||||||
|
isVisible: boolean;
|
||||||
|
isReadOnly?: boolean;
|
||||||
|
artifactType?: CosmosDbArtifactType;
|
||||||
|
nativeConnectionInfo?: FabricNativeDatabaseConnectionInfo;
|
||||||
|
}): Explorer => {
|
||||||
|
const artifactType = params.artifactType ?? CosmosDbArtifactType.MIRRORED;
|
||||||
|
|
||||||
updateUserContext({
|
updateUserContext({
|
||||||
fabricContext: {
|
fabricContext: {
|
||||||
connectionId: params.connectionId,
|
connectionId: params.connectionId,
|
||||||
databaseConnectionInfo: undefined,
|
mirroredConnectionInfo: undefined,
|
||||||
isReadOnly: true,
|
isReadOnly: params.isReadOnly ?? true,
|
||||||
isVisible: params.isVisible ?? true,
|
isVisible: params.isVisible ?? true,
|
||||||
|
artifactType,
|
||||||
|
nativeConnectionInfo: params.nativeConnectionInfo,
|
||||||
},
|
},
|
||||||
authType: AuthType.ConnectionString,
|
});
|
||||||
|
|
||||||
|
if (artifactType === CosmosDbArtifactType.MIRRORED) {
|
||||||
|
updateUserContext({
|
||||||
|
authType: AuthType.ConnectionString, // TODO: will need its own type and Mirroring could be using AAD
|
||||||
databaseAccount: {
|
databaseAccount: {
|
||||||
id: "",
|
id: "",
|
||||||
location: "",
|
location: "",
|
||||||
@ -438,10 +460,28 @@ function createExplorerFabric(params: { connectionId: string; isVisible: boolean
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
useTabs.getState().closeAllTabs();
|
} else if (artifactType === CosmosDbArtifactType.NATIVE) {
|
||||||
|
updateUserContext({
|
||||||
|
databaseAccount: {
|
||||||
|
id: "",
|
||||||
|
location: "",
|
||||||
|
type: "",
|
||||||
|
name: params.nativeConnectionInfo.accountName,
|
||||||
|
kind: AccountKind.Default,
|
||||||
|
properties: {
|
||||||
|
documentEndpoint: params.nativeConnectionInfo.connectionString, // TODO: verify that <artifactid>.sql.cosmos.fabric.microsoft.com is passed to the client as account endpoint
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// For legacy reasons lots of code expects a connection string login to look and act like an encrypted token login
|
||||||
|
authType: AuthType.EncryptedToken,
|
||||||
|
accessToken: params.nativeConnectionInfo.accessToken,
|
||||||
|
masterKey: undefined,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const explorer = new Explorer();
|
const explorer = new Explorer();
|
||||||
return explorer;
|
return explorer;
|
||||||
}
|
};
|
||||||
|
|
||||||
function configureWithEncryptedToken(config: EncryptedToken): Explorer {
|
function configureWithEncryptedToken(config: EncryptedToken): Explorer {
|
||||||
const apiExperience = DefaultExperienceUtility.getDefaultExperienceFromApiKind(config.encryptedTokenMetadata.apiKind);
|
const apiExperience = DefaultExperienceUtility.getDefaultExperienceFromApiKind(config.encryptedTokenMetadata.apiKind);
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import { clamp } from "@fluentui/react";
|
import { clamp } from "@fluentui/react";
|
||||||
|
import { Platform } from "ConfigContext";
|
||||||
import { OpenTab } from "Contracts/ActionContracts";
|
import { OpenTab } from "Contracts/ActionContracts";
|
||||||
import { useSelectedNode } from "Explorer/useSelectedNode";
|
import { useSelectedNode } from "Explorer/useSelectedNode";
|
||||||
|
import { isFabricMirrored } from "Platform/Fabric/FabricUtil";
|
||||||
import {
|
import {
|
||||||
AppStateComponentNames,
|
AppStateComponentNames,
|
||||||
OPEN_TABS_SUBCOMPONENT_NAME,
|
OPEN_TABS_SUBCOMPONENT_NAME,
|
||||||
@ -11,7 +13,6 @@ import * as ViewModels from "../Contracts/ViewModels";
|
|||||||
import { CollectionTabKind } from "../Contracts/ViewModels";
|
import { CollectionTabKind } from "../Contracts/ViewModels";
|
||||||
import NotebookTabV2 from "../Explorer/Tabs/NotebookV2Tab";
|
import NotebookTabV2 from "../Explorer/Tabs/NotebookV2Tab";
|
||||||
import TabsBase from "../Explorer/Tabs/TabsBase";
|
import TabsBase from "../Explorer/Tabs/TabsBase";
|
||||||
import { Platform, configContext } from "./../ConfigContext";
|
|
||||||
|
|
||||||
export interface TabsState {
|
export interface TabsState {
|
||||||
openedTabs: TabsBase[];
|
openedTabs: TabsBase[];
|
||||||
@ -122,7 +123,7 @@ export const useTabs: UseStore<TabsState> = create((set, get) => ({
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
if (updatedTabs.length === 0 && configContext.platform !== Platform.Fabric) {
|
if (updatedTabs.length === 0 && !isFabricMirrored()) {
|
||||||
set({ activeTab: undefined, activeReactTab: undefined });
|
set({ activeTab: undefined, activeReactTab: undefined });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,7 +163,7 @@ export const useTabs: UseStore<TabsState> = create((set, get) => ({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (get().openedTabs.length === 0 && configContext.platform !== Platform.Fabric) {
|
if (get().openedTabs.length === 0 && !isFabricMirrored()) {
|
||||||
set({ activeTab: undefined, activeReactTab: undefined });
|
set({ activeTab: undefined, activeReactTab: undefined });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user