`;
-exports[`TreeNodeComponent renders sorted children, expanded, leaves and parents separated 1`] = `
+exports[`LegacyTreeNodeComponent renders sorted children, expanded, leaves and parents separated 1`] = `
-
-
-
-
`;
-exports[`TreeNodeComponent renders unsorted children by default 1`] = `
+exports[`LegacyTreeNodeComponent renders unsorted children by default 1`] = `
-
-
+
+ }
+ style={
+ Object {
+ "backgroundColor": undefined,
+ }
+ }
+ >
+ rootLabel
+
+
+`;
+
+exports[`TreeNodeComponent fully renders a tree 1`] = `
+
+
+ ,
+ },
+ "isActionsVisible": false,
+ "isAsideVisible": false,
+ "itemType": "branch",
+ "layoutRef": Object {
+ "current":
+
+
+
+
+
+ rootLabel
+
+
,
+ },
+ "open": false,
+ "selectionRef": Object {
+ "current": null,
+ },
+ "subtreeRef": Object {
+ "current": null,
+ },
+ "treeItemRef": Object {
+ "current":
+
+
+
+
+
+
+ rootLabel
+
+
+
+
+
+
+
+
+
+
+ child1Label
+
+
+
+
+
+
+
+
+
+
+ child2LoadingLabel
+
+
+
+
+
+
+
+
+
+
+ child3ExpandingLabel
+
+
+
+
+
,
+ },
+ "value": "root",
+ }
+ }
+ >
+
+ }
+ style={
+ Object {
+ "backgroundColor": undefined,
+ }
+ }
+ >
+
+
+
+
+
+
+ rootLabel
+
+
+
+
+
+
+
+
+
+ ,
+ },
+ "isActionsVisible": false,
+ "isAsideVisible": false,
+ "itemType": "branch",
+ "layoutRef": Object {
+ "current":
+
+
+
+
+
+ child1Label
+
+
,
+ },
+ "open": false,
+ "selectionRef": Object {
+ "current": null,
+ },
+ "subtreeRef": Object {
+ "current": null,
+ },
+ "treeItemRef": Object {
+ "current":
+
+
+
+
+
+
+ child1Label
+
+
+
,
+ },
+ "value": "root/child1Label",
+ }
+ }
+ >
+
+ }
+ style={
+ Object {
+ "backgroundColor": undefined,
+ }
+ }
+ >
+
+
+
+
+
+
+ child1Label
+
+
+
+
+
+
+
+
+
+
+
+
+ ,
+ },
+ "isActionsVisible": false,
+ "isAsideVisible": false,
+ "itemType": "branch",
+ "layoutRef": Object {
+ "current":
+
+
+
+
+
+ child2LoadingLabel
+
+
,
+ },
+ "open": false,
+ "selectionRef": Object {
+ "current": null,
+ },
+ "subtreeRef": Object {
+ "current": null,
+ },
+ "treeItemRef": Object {
+ "current":
+
+
+
+
+
+
+ child2LoadingLabel
+
+
+
,
+ },
+ "value": "root/child2LoadingLabel",
+ }
+ }
+ >
+
+ }
+ style={
+ Object {
+ "backgroundColor": undefined,
+ }
+ }
+ >
+
+
+
+
+
+
+ child2LoadingLabel
+
+
+
+
+
+
+
+
+
+ ,
+ },
+ "isActionsVisible": false,
+ "isAsideVisible": false,
+ "itemType": "leaf",
+ "layoutRef": Object {
+ "current":
+
+
+
+
+
+ child3ExpandingLabel
+
+
,
+ },
+ "open": false,
+ "selectionRef": Object {
+ "current": null,
+ },
+ "subtreeRef": Object {
+ "current": null,
+ },
+ "treeItemRef": Object {
+ "current":
+
+
+
+
+
+
+ child3ExpandingLabel
+
+
+
,
+ },
+ "value": "root/child3ExpandingLabel",
+ }
+ }
+ >
+
+ }
+ style={
+ Object {
+ "backgroundColor": undefined,
+ }
+ }
+ >
+
+
+
+
+
+ child3ExpandingLabel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
+
+exports[`TreeNodeComponent renders a loading spinner if the node is loading: loaded 1`] = `
+
+
+ }
+ style={
+ Object {
+ "backgroundColor": undefined,
+ }
+ }
+ >
+ rootLabel
+
+
+`;
+
+exports[`TreeNodeComponent renders a loading spinner if the node is loading: loading 1`] = `
+
+
+ }
+ iconBefore={
+
+ }
+ style={
+ Object {
+ "backgroundColor": undefined,
+ }
+ }
+ >
+ rootLabel
+
+
+`;
+
+exports[`TreeNodeComponent renders a node as expandable if it has empty, but defined, children array 1`] = `
+
+
+ }
+ style={
+ Object {
+ "backgroundColor": undefined,
+ }
+ }
+ >
+ rootLabel
+
+
+`;
+
+exports[`TreeNodeComponent renders a node with a menu 1`] = `
+
+ }
+ className="rootClass"
+ data-test="TreeNode:root"
+ iconBefore={
+
+ }
+ style={
+ Object {
+ "backgroundColor": undefined,
+ }
+ }
+ >
+ rootLabel
+
+
+
+
+
+
+
+
+
+
+`;
+
+exports[`TreeNodeComponent renders a single node 1`] = `
+
+
+ }
+ style={
+ Object {
+ "backgroundColor": undefined,
+ }
+ }
+ >
+ rootLabel
+
+
+`;
+
+exports[`TreeNodeComponent renders an icon if the node has one 1`] = `
+
+
+ }
+ style={
+ Object {
+ "backgroundColor": undefined,
+ }
+ }
+ >
+ rootLabel
+
+
+`;
+
+exports[`TreeNodeComponent renders selected parent node as selected if no descendant nodes are selected 1`] = `
+
+
+ }
+ style={
+ Object {
+ "backgroundColor": "var(--colorNeutralBackground1Selected)",
+ }
+ }
+ >
+ rootLabel
+
+
+
+
+
+
+`;
+
+exports[`TreeNodeComponent renders selected parent node as unselected if any descendant node is selected 1`] = `
+
+
+ }
+ style={
+ Object {
+ "backgroundColor": undefined,
+ }
+ }
+ >
+ rootLabel
+
+
+
+
+
+
+`;
+
+exports[`TreeNodeComponent renders single selected leaf node as selected 1`] = `
+
+
+ }
+ style={
+ Object {
+ "backgroundColor": "var(--colorNeutralBackground1Selected)",
+ }
+ }
+ >
+ rootLabel
+
+
+`;
diff --git a/src/Explorer/Controls/TreeComponent2/TreeNode2Component.tsx b/src/Explorer/Controls/TreeComponent2/TreeNode2Component.tsx
deleted file mode 100644
index 14c66db12..000000000
--- a/src/Explorer/Controls/TreeComponent2/TreeNode2Component.tsx
+++ /dev/null
@@ -1,147 +0,0 @@
-import {
- Button,
- Menu,
- MenuItem,
- MenuList,
- MenuPopover,
- MenuTrigger,
- Spinner,
- Tree,
- TreeItem,
- TreeItemLayout,
- TreeOpenChangeData,
- TreeOpenChangeEvent,
-} from "@fluentui/react-components";
-import { MoreHorizontal20Regular } from "@fluentui/react-icons";
-import { tokens } from "@fluentui/react-theme";
-import * as React from "react";
-
-export interface TreeNode2MenuItem {
- label: string;
- onClick: () => void;
- iconSrc?: string;
- isDisabled?: boolean;
- styleClass?: string;
-}
-
-export interface TreeNode2 {
- label: string;
- id?: string;
- children?: TreeNode2[];
- contextMenu?: TreeNode2MenuItem[];
- iconSrc?: string;
- isExpanded?: boolean;
- className?: string;
- isAlphaSorted?: boolean;
- // data?: any; // Piece of data corresponding to this node
- timestamp?: number;
- isLeavesParentsSeparate?: boolean; // Display parents together first, then leaves
- isLoading?: boolean;
- isScrollable?: boolean;
- isSelected?: () => boolean;
- onClick?: () => void; // Only if a leaf, other click will expand/collapse
- onExpanded?: () => Promise
;
- onCollapsed?: () => void;
- onContextMenuOpen?: () => void;
-}
-
-export interface TreeNode2ComponentProps {
- node: TreeNode2;
- className?: string;
- treeNodeId: string;
-}
-
-const getTreeIcon = (iconSrc: string): JSX.Element => ;
-
-export const TreeNode2Component: React.FC = ({
- node,
- treeNodeId,
-}: TreeNode2ComponentProps): JSX.Element => {
- const [isLoading, setIsLoading] = React.useState(false);
-
- const getSortedChildren = (treeNode: TreeNode2): TreeNode2[] => {
- if (!treeNode || !treeNode.children) {
- return undefined;
- }
-
- const compareFct = (a: TreeNode2, b: TreeNode2) => a.label.localeCompare(b.label);
-
- let unsortedChildren;
- if (treeNode.isLeavesParentsSeparate) {
- // Separate parents and leave
- const parents: TreeNode2[] = treeNode.children.filter((node) => node.children);
- const leaves: TreeNode2[] = treeNode.children.filter((node) => !node.children);
-
- if (treeNode.isAlphaSorted) {
- parents.sort(compareFct);
- leaves.sort(compareFct);
- }
-
- unsortedChildren = parents.concat(leaves);
- } else {
- unsortedChildren = treeNode.isAlphaSorted ? treeNode.children.sort(compareFct) : treeNode.children;
- }
-
- return unsortedChildren;
- };
-
- const onOpenChange = (_: TreeOpenChangeEvent, data: TreeOpenChangeData) => {
- if (!node.isExpanded && data.open && node.onExpanded) {
- // Catch the transition non-expanded to expanded
- setIsLoading(true);
- node.onExpanded?.().then(() => setIsLoading(false));
- } else if (node.isExpanded && !data.open && node.onCollapsed) {
- // Catch the transition expanded to non-expanded
- node.onCollapsed?.();
- }
- };
-
- return (
-
-
-
- } />
-
-
-
- {node.contextMenu.map((menuItem) => (
-
- ))}
-
-
-
- )
- }
- expandIcon={isLoading ? : undefined}
- iconBefore={node.iconSrc && getTreeIcon(node.iconSrc)}
- style={{
- backgroundColor: node.isSelected && node.isSelected() ? tokens.colorNeutralBackground1Selected : undefined,
- }}
- >
- node.onClick?.()}>{node.label}
-
- {!node.isLoading && node.children?.length > 0 && (
-
- {getSortedChildren(node).map((childNode: TreeNode2) => (
-
- ))}
-
- )}
-
- );
-};
diff --git a/src/Explorer/Explorer.tsx b/src/Explorer/Explorer.tsx
index f159c6ac1..bb198e6c3 100644
--- a/src/Explorer/Explorer.tsx
+++ b/src/Explorer/Explorer.tsx
@@ -3,10 +3,11 @@ import { isPublicInternetAccessAllowed } from "Common/DatabaseAccountUtility";
import { sendMessage } from "Common/MessageHandler";
import { Platform, configContext } from "ConfigContext";
import { MessageTypes } from "Contracts/ExplorerContracts";
-import { getCopilotEnabled } from "Explorer/QueryCopilot/Shared/QueryCopilotClient";
+import { getCopilotEnabled, isCopilotFeatureRegistered } from "Explorer/QueryCopilot/Shared/QueryCopilotClient";
import { IGalleryItem } from "Juno/JunoClient";
-import { requestDatabaseResourceTokens } from "Platform/Fabric/FabricUtil";
-import { allowedNotebookServerUrls, validateEndpoint } from "Utils/EndpointValidation";
+import { scheduleRefreshDatabaseResourceToken } from "Platform/Fabric/FabricUtil";
+import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
+import { allowedNotebookServerUrls, validateEndpoint } from "Utils/EndpointUtils";
import { useQueryCopilot } from "hooks/useQueryCopilot";
import * as ko from "knockout";
import React from "react";
@@ -37,7 +38,6 @@ import { fromContentUri, toRawContentUri } from "../Utils/GitHubUtils";
import * as NotificationConsoleUtils from "../Utils/NotificationConsoleUtils";
import { logConsoleError, logConsoleInfo, logConsoleProgress } from "../Utils/NotificationConsoleUtils";
import { update } from "../Utils/arm/generatedClients/cosmos/databaseAccounts";
-import { listByDatabaseAccount } from "../Utils/arm/generatedClients/cosmosNotebooks/notebookWorkspaces";
import { useSidePanel } from "../hooks/useSidePanel";
import { useTabs } from "../hooks/useTabs";
import "./ComponentRegisterer";
@@ -55,7 +55,6 @@ import { AddCollectionPanel } from "./Panes/AddCollectionPanel";
import { CassandraAddCollectionPane } from "./Panes/CassandraAddCollectionPane/CassandraAddCollectionPane";
import { ExecuteSprocParamsPane } from "./Panes/ExecuteSprocParamsPane/ExecuteSprocParamsPane";
import { StringInputPane } from "./Panes/StringInputPane/StringInputPane";
-import { UploadFilePane } from "./Panes/UploadFilePane/UploadFilePane";
import { UploadItemsPane } from "./Panes/UploadItemsPane/UploadItemsPane";
import { CassandraAPIDataClient, TableDataClient, TablesAPIDataClient } from "./Tables/TableDataClient";
import NotebookV2Tab, { NotebookTabOptions } from "./Tabs/NotebookV2Tab";
@@ -137,14 +136,6 @@ export default class Explorer {
this.isTabsContentExpanded = ko.observable(false);
- document.addEventListener(
- "contextmenu",
- (e) => {
- e.preventDefault();
- },
- false,
- );
-
$(() => {
$(document.body).click(() => $(".commandDropdownContainer").hide());
});
@@ -264,61 +255,43 @@ export default class Explorer {
// TODO: return result
}
- private getRandomInt(max: number) {
- return Math.floor(Math.random() * max);
- }
-
public openNPSSurveyDialog(): void {
if (!Platform.Portal) {
return;
}
- const NINETY_DAYS_IN_MS = 7776000000;
const ONE_DAY_IN_MS = 86400000;
- const THREE_DAYS_IN_MS = 259200000;
- const isAccountNewerThanNinetyDays = isAccountNewerThanThresholdInMs(
- userContext.databaseAccount?.systemData?.createdAt || "",
- NINETY_DAYS_IN_MS,
- );
- const lastSubmitted: string = localStorage.getItem("lastSubmitted");
-
- if (lastSubmitted !== null) {
- let lastSubmittedDate: number = parseInt(lastSubmitted);
- if (isNaN(lastSubmittedDate)) {
- lastSubmittedDate = 0;
- }
-
- const nowMs: number = Date.now();
- const millisecsSinceLastSubmitted = nowMs - lastSubmittedDate;
- if (millisecsSinceLastSubmitted < NINETY_DAYS_IN_MS) {
- return;
- }
- }
+ const SEVEN_DAYS_IN_MS = 604800000;
// Try Cosmos DB subscription - survey shown to 100% of users at day 1 in Data Explorer.
if (userContext.isTryCosmosDBSubscription) {
if (isAccountNewerThanThresholdInMs(userContext.databaseAccount?.systemData?.createdAt || "", ONE_DAY_IN_MS)) {
- this.sendNPSMessage();
+ Logger.logInfo(
+ `Sending message to Portal to check if NPS Survey can be displayed in Try Cosmos DB ${userContext.apiType}`,
+ "Explorer/openNPSSurveyDialog",
+ );
+ sendMessage({ type: MessageTypes.DisplayNPSSurvey });
}
} else {
- // An existing account is older than 3 days but less than 90 days old. For existing account show to 100% of users in Data Explorer.
+ // Show survey when an existing account is older than 7 days
if (
- !isAccountNewerThanThresholdInMs(userContext.databaseAccount?.systemData?.createdAt || "", THREE_DAYS_IN_MS) &&
- isAccountNewerThanNinetyDays
+ !isAccountNewerThanThresholdInMs(userContext.databaseAccount?.systemData?.createdAt || "", SEVEN_DAYS_IN_MS)
) {
- this.sendNPSMessage();
- } else {
- // An existing account is greater than 90 days. For existing account show to random 33% of users in Data Explorer.
- if (this.getRandomInt(100) < 33) {
- this.sendNPSMessage();
- }
+ Logger.logInfo(
+ `Sending message to Portal to check if NPS Survey can be displayed for existing ${userContext.apiType} account older than 7 days`,
+ "Explorer/openNPSSurveyDialog",
+ );
+ sendMessage({ type: MessageTypes.DisplayNPSSurvey });
}
}
}
- private sendNPSMessage() {
- sendMessage({ type: MessageTypes.DisplayNPSSurvey });
- localStorage.setItem("lastSubmitted", Date.now().toString());
+ public async openCESCVAFeedbackBlade(): Promise {
+ sendMessage({ type: MessageTypes.OpenCESCVAFeedbackBlade });
+ Logger.logInfo(
+ `CES CVA Feedback logging current date when survey is shown ${Date.now().toString()}`,
+ "Explorer/openCESCVAFeedbackBlade",
+ );
}
public async refreshDatabaseForResourceToken(): Promise {
@@ -383,9 +356,7 @@ export default class Explorer {
public onRefreshResourcesClick = (): void => {
if (configContext.platform === Platform.Fabric) {
- // Requesting the tokens will trigger a refresh of the databases
- // TODO: Once the id is returned from Fabric, we can await this call and then refresh the databases here
- requestDatabaseResourceTokens();
+ scheduleRefreshDatabaseResourceToken(true).then(() => this.refreshAllDatabases());
return;
}
@@ -529,104 +500,6 @@ export default class Explorer {
.then((memoryUsageInfo) => useNotebook.getState().setMemoryUsageInfo(memoryUsageInfo));
}
- public resetNotebookWorkspace(): void {
- if (!useNotebook.getState().isNotebookEnabled || !this.notebookManager?.notebookClient) {
- handleError(
- "Attempt to reset notebook workspace, but notebook is not enabled",
- "Explorer/resetNotebookWorkspace",
- );
- return;
- }
- const dialogContent = useNotebook.getState().isPhoenixNotebooks
- ? "Notebooks saved in the temporary workspace will be deleted. Do you want to proceed?"
- : "This lets you keep your notebook files and the workspace will be restored to default. Proceed anyway?";
-
- const resetConfirmationDialogProps: DialogProps = {
- isModal: true,
- title: "Reset Workspace",
- subText: dialogContent,
- primaryButtonText: "OK",
- secondaryButtonText: "Cancel",
- onPrimaryButtonClick: this._resetNotebookWorkspace,
- onSecondaryButtonClick: () => useDialog.getState().closeDialog(),
- };
- useDialog.getState().openDialog(resetConfirmationDialogProps);
- }
-
- private async _containsDefaultNotebookWorkspace(databaseAccount: DataModels.DatabaseAccount): Promise {
- if (!databaseAccount) {
- return false;
- }
- try {
- const { value: workspaces } = await listByDatabaseAccount(
- userContext.subscriptionId,
- userContext.resourceGroup,
- userContext.databaseAccount.name,
- );
- return workspaces && workspaces.length > 0 && workspaces.some((workspace) => workspace.name === "default");
- } catch (error) {
- Logger.logError(getErrorMessage(error), "Explorer/_containsDefaultNotebookWorkspace");
- return false;
- }
- }
-
- private _resetNotebookWorkspace = async () => {
- useDialog.getState().closeDialog();
- const clearInProgressMessage = logConsoleProgress("Resetting notebook workspace");
- let connectionStatus: ContainerConnectionInfo;
- try {
- const notebookServerInfo = useNotebook.getState().notebookServerInfo;
- if (!notebookServerInfo || !notebookServerInfo.notebookServerEndpoint) {
- const error = "No server endpoint detected";
- Logger.logError(error, "NotebookContainerClient/resetWorkspace");
- logConsoleError(error);
- return;
- }
- TelemetryProcessor.traceStart(Action.PhoenixResetWorkspace, {
- dataExplorerArea: Areas.Notebook,
- });
- if (useNotebook.getState().isPhoenixNotebooks) {
- useTabs.getState().closeAllNotebookTabs(true);
- connectionStatus = {
- status: ConnectionStatusType.Connecting,
- };
- useNotebook.getState().setConnectionInfo(connectionStatus);
- }
- const connectionInfo = await this.notebookManager?.notebookClient.resetWorkspace();
- if (connectionInfo?.status !== HttpStatusCodes.OK) {
- throw new Error(`Reset Workspace: Received status code- ${connectionInfo?.status}`);
- }
- if (!connectionInfo?.data?.phoenixServiceUrl) {
- throw new Error(`Reset Workspace: PhoenixServiceUrl is invalid!`);
- }
- if (useNotebook.getState().isPhoenixNotebooks) {
- await this.setNotebookInfo(true, connectionInfo, connectionStatus);
- useNotebook.getState().setIsRefreshed(!useNotebook.getState().isRefreshed);
- }
- logConsoleInfo("Successfully reset notebook workspace");
- TelemetryProcessor.traceSuccess(Action.PhoenixResetWorkspace, {
- dataExplorerArea: Areas.Notebook,
- });
- } catch (error) {
- logConsoleError(`Failed to reset notebook workspace: ${error}`);
- TelemetryProcessor.traceFailure(Action.PhoenixResetWorkspace, {
- dataExplorerArea: Areas.Notebook,
- error: getErrorMessage(error),
- errorStack: getErrorStack(error),
- });
- if (useNotebook.getState().isPhoenixNotebooks) {
- connectionStatus = {
- status: ConnectionStatusType.Failed,
- };
- useNotebook.getState().resetContainerConnection(connectionStatus);
- useNotebook.getState().setIsRefreshed(!useNotebook.getState().isRefreshed);
- }
- throw error;
- } finally {
- clearInProgressMessage();
- }
- };
-
private getDeltaDatabases(
updatedDatabaseList: DataModels.Database[],
databases: ViewModels.Database[],
@@ -1029,92 +902,6 @@ export default class Explorer {
);
}
- /**
- * This creates a new notebook file, then opens the notebook
- */
- public async onNewNotebookClicked(parent?: NotebookContentItem, isGithubTree?: boolean): Promise {
- if (!useNotebook.getState().isNotebookEnabled || !this.notebookManager?.notebookContentClient) {
- const error = "Attempt to create new notebook, but notebook is not enabled";
- handleError(error, "Explorer/onNewNotebookClicked");
- throw new Error(error);
- }
- if (useNotebook.getState().isPhoenixNotebooks) {
- if (isGithubTree) {
- await this.allocateContainer(PoolIdType.DefaultPoolId);
- parent = parent || this.resourceTree.myNotebooksContentRoot;
- this.createNewNoteBook(parent, isGithubTree);
- } else {
- useDialog.getState().showOkCancelModalDialog(
- Notebook.newNotebookModalTitle,
- undefined,
- "Create",
- async () => {
- await this.allocateContainer(PoolIdType.DefaultPoolId);
- parent = parent || this.resourceTree.myNotebooksContentRoot;
- this.createNewNoteBook(parent, isGithubTree);
- },
- "Cancel",
- undefined,
- this.getNewNoteWarningText(),
- );
- }
- } else {
- parent = parent || this.resourceTree.myNotebooksContentRoot;
- this.createNewNoteBook(parent, isGithubTree);
- }
- }
-
- private getNewNoteWarningText(): JSX.Element {
- return (
- <>
- {Notebook.newNotebookModalContent1}
-
-
- {Notebook.newNotebookModalContent2}
-
- {Notebook.learnMore}
-
-
- >
- );
- }
-
- private createNewNoteBook(parent?: NotebookContentItem, isGithubTree?: boolean): void {
- const clearInProgressMessage = logConsoleProgress(`Creating new notebook in ${parent.path}`);
- const startKey: number = TelemetryProcessor.traceStart(Action.CreateNewNotebook, {
- dataExplorerArea: Constants.Areas.Notebook,
- });
-
- this.notebookManager?.notebookContentClient
- .createNewNotebookFile(parent, isGithubTree)
- .then((newFile: NotebookContentItem) => {
- logConsoleInfo(`Successfully created: ${newFile.name}`);
- TelemetryProcessor.traceSuccess(
- Action.CreateNewNotebook,
- {
- dataExplorerArea: Constants.Areas.Notebook,
- },
- startKey,
- );
- return this.openNotebook(newFile);
- })
- .then(() => this.resourceTree.triggerRender())
- .catch((error) => {
- const errorMessage = `Failed to create a new notebook: ${getErrorMessage(error)}`;
- logConsoleError(errorMessage);
- TelemetryProcessor.traceFailure(
- Action.CreateNewNotebook,
- {
- dataExplorerArea: Constants.Areas.Notebook,
- error: errorMessage,
- errorStack: getErrorStack(error),
- },
- startKey,
- );
- })
- .finally(clearInProgressMessage);
- }
-
// TODO: Delete this function when ResourceTreeAdapter is removed.
public async refreshContentItem(item: NotebookContentItem): Promise {
if (!useNotebook.getState().isNotebookEnabled || !this.notebookManager?.notebookContentClient) {
@@ -1149,10 +936,6 @@ export default class Explorer {
let title: string;
switch (kind) {
- case ViewModels.TerminalKind.Default:
- title = "Terminal";
- break;
-
case ViewModels.TerminalKind.Mongo:
title = "Mongo Shell";
break;
@@ -1306,36 +1089,6 @@ export default class Explorer {
.openSidePanel("Input parameters", );
}
- public openUploadFilePanel(parent?: NotebookContentItem): void {
- if (useNotebook.getState().isPhoenixNotebooks) {
- useDialog.getState().showOkCancelModalDialog(
- Notebook.newNotebookUploadModalTitle,
- undefined,
- "Upload",
- async () => {
- await this.allocateContainer(PoolIdType.DefaultPoolId);
- parent = parent || this.resourceTree.myNotebooksContentRoot;
- this.uploadFilePanel(parent);
- },
- "Cancel",
- undefined,
- this.getNewNoteWarningText(),
- );
- } else {
- parent = parent || this.resourceTree.myNotebooksContentRoot;
- this.uploadFilePanel(parent);
- }
- }
-
- private uploadFilePanel(parent?: NotebookContentItem): void {
- useSidePanel
- .getState()
- .openSidePanel(
- "Upload file to notebook server",
- this.uploadFile(name, content, parent)} />,
- );
- }
-
public getDownloadModalConent(fileName: string): JSX.Element {
if (useNotebook.getState().isPhoenixNotebooks) {
return (
@@ -1389,27 +1142,40 @@ export default class Explorer {
if (userContext.apiType !== "SQL" || !userContext.subscriptionId) {
return;
}
- const copilotEnabled = await getCopilotEnabled();
- useQueryCopilot.getState().setCopilotEnabled(copilotEnabled);
- useQueryCopilot.getState().setCopilotUserDBEnabled(copilotEnabled);
+ const copilotEnabledPromise = getCopilotEnabled();
+ const copilotUserDBEnabledPromise = isCopilotFeatureRegistered(userContext.subscriptionId);
+ const [copilotEnabled, copilotUserDBEnabled] = await Promise.all([
+ copilotEnabledPromise,
+ copilotUserDBEnabledPromise,
+ ]);
+ const copilotSampleDBEnabled = LocalStorageUtility.getEntryString(StorageKey.CopilotSampleDBEnabled) === "true";
+ useQueryCopilot.getState().setCopilotEnabled(copilotEnabled && copilotUserDBEnabled);
+ useQueryCopilot.getState().setCopilotUserDBEnabled(copilotUserDBEnabled);
+ useQueryCopilot
+ .getState()
+ .setCopilotSampleDBEnabled(copilotEnabled && copilotUserDBEnabled && copilotSampleDBEnabled);
}
public async refreshSampleData(): Promise {
- if (!userContext.sampleDataConnectionInfo) {
+ try {
+ if (!userContext.sampleDataConnectionInfo) {
+ return;
+ }
+ const collection: DataModels.Collection = await readSampleCollection();
+ if (!collection) {
+ return;
+ }
+
+ const databaseId = userContext.sampleDataConnectionInfo?.databaseId;
+ if (!databaseId) {
+ return;
+ }
+
+ const sampleDataResourceTokenCollection = new ResourceTokenCollection(this, databaseId, collection, true);
+ useDatabases.setState({ sampleDataResourceTokenCollection });
+ } catch (error) {
+ Logger.logError(getErrorMessage(error), "Explorer");
return;
}
-
- const collection: DataModels.Collection = await readSampleCollection();
- if (!collection) {
- return;
- }
-
- const databaseId = userContext.sampleDataConnectionInfo?.databaseId;
- if (!databaseId) {
- return;
- }
-
- const sampleDataResourceTokenCollection = new ResourceTokenCollection(this, databaseId, collection, true);
- useDatabases.setState({ sampleDataResourceTokenCollection });
}
}
diff --git a/src/Explorer/Graph/GraphExplorerComponent/GraphUtil.ts b/src/Explorer/Graph/GraphExplorerComponent/GraphUtil.ts
index 3be076000..c16e9aa30 100644
--- a/src/Explorer/Graph/GraphExplorerComponent/GraphUtil.ts
+++ b/src/Explorer/Graph/GraphExplorerComponent/GraphUtil.ts
@@ -162,7 +162,7 @@ export const addRootChildToGraph = (
* @param value
*/
export const escapeDoubleQuotes = (value: string): string => {
- return value === undefined ? value : value.replace(/"/g, '\\"');
+ return value === undefined ? value : value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
};
/**
@@ -186,5 +186,5 @@ export const getQuotedPropValue = (ip: ViewModels.InputPropertyValue): string =>
* @param value
*/
export const escapeSingleQuotes = (value: string): string => {
- return value === undefined ? value : value.replace(/'/g, "\\'");
+ return value === undefined ? value : value.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
};
diff --git a/src/Explorer/Graph/GraphExplorerComponent/NodePropertiesComponent.tsx b/src/Explorer/Graph/GraphExplorerComponent/NodePropertiesComponent.tsx
index 6b0472649..f67425696 100644
--- a/src/Explorer/Graph/GraphExplorerComponent/NodePropertiesComponent.tsx
+++ b/src/Explorer/Graph/GraphExplorerComponent/NodePropertiesComponent.tsx
@@ -349,7 +349,7 @@ export class NodePropertiesComponent extends React.Component<
onActivated={this.setIsDeleteConfirm.bind(this, true)}
aria-label="Delete this vertex"
>
-
+
);
} else {
@@ -406,7 +406,7 @@ export class NodePropertiesComponent extends React.Component<
aria-label="Edit properties"
onActivated={expandClickHandler}
>
-
+
)}
diff --git a/src/Explorer/Graph/NewVertexComponent/NewVertexComponent.tsx b/src/Explorer/Graph/NewVertexComponent/NewVertexComponent.tsx
index de357989a..6b20cfcb0 100644
--- a/src/Explorer/Graph/NewVertexComponent/NewVertexComponent.tsx
+++ b/src/Explorer/Graph/NewVertexComponent/NewVertexComponent.tsx
@@ -184,12 +184,18 @@ export const NewVertexComponent: FunctionComponent = (
className="rightPaneTrashIcon rightPaneBtns"
tabIndex={0}
role="button"
+ aria-label={`Delete ${data.key}`}
onClick={(event: React.MouseEvent) => removeNewVertexProperty(event, index)}
onKeyPress={(event: React.KeyboardEvent) =>
removeNewVertexPropertyKeyPress(event, index)
}
>
-
+