mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2026-01-31 15:44:11 +00:00
Compare commits
16 Commits
2262594
...
users/artr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6270358d9c | ||
|
|
9f7783f3f9 | ||
|
|
daa26d289b | ||
|
|
879cb08949 | ||
|
|
92f43c28a7 | ||
|
|
5f0c7bcea2 | ||
|
|
fa55d528ad | ||
|
|
c873fed7aa | ||
|
|
7ec5290293 | ||
|
|
4005128211 | ||
|
|
38d13cc74e | ||
|
|
4ab93a5a32 | ||
|
|
44e25c0769 | ||
|
|
8ea8f0230f | ||
|
|
655b998b84 | ||
|
|
9e2f6a9c89 |
@@ -230,7 +230,7 @@ input::-webkit-inner-spin-button {
|
|||||||
.advanced-options-panel .advanced-options .select .select-options-link {
|
.advanced-options-panel .advanced-options .select .select-options-link {
|
||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
outline: none;
|
padding: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.query-panel .row .column-headers .Field {
|
.query-panel .row .column-headers .Field {
|
||||||
|
|||||||
8059
package-lock.json
generated
8059
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -137,7 +137,7 @@ export const TableEntity: FunctionComponent<TableEntityProps> = ({
|
|||||||
/>
|
/>
|
||||||
{!isEntityValueDisable && (
|
{!isEntityValueDisable && (
|
||||||
<TooltipHost content="Edit property" id="editTooltip">
|
<TooltipHost content="Edit property" id="editTooltip">
|
||||||
<div tabIndex={0}>
|
<div>
|
||||||
<Image
|
<Image
|
||||||
{...imageProps}
|
{...imageProps}
|
||||||
src={EditIcon}
|
src={EditIcon}
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import {
|
import {
|
||||||
allowedAadEndpoints,
|
allowedAadEndpoints,
|
||||||
allowedArcadiaEndpoints,
|
allowedArcadiaEndpoints,
|
||||||
allowedArmEndpoints,
|
|
||||||
allowedBackendEndpoints,
|
|
||||||
allowedEmulatorEndpoints,
|
allowedEmulatorEndpoints,
|
||||||
allowedGraphEndpoints,
|
allowedGraphEndpoints,
|
||||||
allowedHostedExplorerEndpoints,
|
allowedHostedExplorerEndpoints,
|
||||||
allowedJunoOrigins,
|
allowedJunoOrigins,
|
||||||
allowedMongoBackendEndpoints,
|
allowedMongoBackendEndpoints,
|
||||||
allowedMsalRedirectEndpoints,
|
allowedMsalRedirectEndpoints,
|
||||||
|
defaultAllowedArmEndpoints,
|
||||||
|
defaultAllowedBackendEndpoints,
|
||||||
validateEndpoint,
|
validateEndpoint,
|
||||||
} from "Utils/EndpointValidation";
|
} from "Utils/EndpointValidation";
|
||||||
|
|
||||||
@@ -20,6 +20,8 @@ export enum Platform {
|
|||||||
|
|
||||||
export interface ConfigContext {
|
export interface ConfigContext {
|
||||||
platform: Platform;
|
platform: Platform;
|
||||||
|
allowedArmEndpoints: ReadonlyArray<string>;
|
||||||
|
allowedBackendEndpoints: ReadonlyArray<string>;
|
||||||
allowedParentFrameOrigins: ReadonlyArray<string>;
|
allowedParentFrameOrigins: ReadonlyArray<string>;
|
||||||
gitSha?: string;
|
gitSha?: string;
|
||||||
proxyPath?: string;
|
proxyPath?: string;
|
||||||
@@ -49,6 +51,8 @@ export interface ConfigContext {
|
|||||||
// Default configuration
|
// Default configuration
|
||||||
let configContext: Readonly<ConfigContext> = {
|
let configContext: Readonly<ConfigContext> = {
|
||||||
platform: Platform.Portal,
|
platform: Platform.Portal,
|
||||||
|
allowedArmEndpoints: defaultAllowedArmEndpoints,
|
||||||
|
allowedBackendEndpoints: defaultAllowedBackendEndpoints,
|
||||||
allowedParentFrameOrigins: [
|
allowedParentFrameOrigins: [
|
||||||
`^https:\\/\\/cosmos\\.azure\\.(com|cn|us)$`,
|
`^https:\\/\\/cosmos\\.azure\\.(com|cn|us)$`,
|
||||||
`^https:\\/\\/[\\.\\w]*portal\\.azure\\.(com|cn|us)$`,
|
`^https:\\/\\/[\\.\\w]*portal\\.azure\\.(com|cn|us)$`,
|
||||||
@@ -77,7 +81,7 @@ let configContext: Readonly<ConfigContext> = {
|
|||||||
|
|
||||||
export function resetConfigContext(): void {
|
export function resetConfigContext(): void {
|
||||||
if (process.env.NODE_ENV !== "test") {
|
if (process.env.NODE_ENV !== "test") {
|
||||||
throw new Error("resetConfigContext can only becalled in a test environment");
|
throw new Error("resetConfigContext can only be called in a test environment");
|
||||||
}
|
}
|
||||||
configContext = {} as ConfigContext;
|
configContext = {} as ConfigContext;
|
||||||
}
|
}
|
||||||
@@ -87,7 +91,7 @@ export function updateConfigContext(newContext: Partial<ConfigContext>): void {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!validateEndpoint(newContext.ARM_ENDPOINT, allowedArmEndpoints)) {
|
if (!validateEndpoint(newContext.ARM_ENDPOINT, configContext.allowedArmEndpoints || defaultAllowedArmEndpoints)) {
|
||||||
delete newContext.ARM_ENDPOINT;
|
delete newContext.ARM_ENDPOINT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,7 +111,12 @@ export function updateConfigContext(newContext: Partial<ConfigContext>): void {
|
|||||||
delete newContext.ARCADIA_ENDPOINT;
|
delete newContext.ARCADIA_ENDPOINT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!validateEndpoint(newContext.BACKEND_ENDPOINT, allowedBackendEndpoints)) {
|
if (
|
||||||
|
!validateEndpoint(
|
||||||
|
newContext.BACKEND_ENDPOINT,
|
||||||
|
configContext.allowedBackendEndpoints || defaultAllowedBackendEndpoints
|
||||||
|
)
|
||||||
|
) {
|
||||||
delete newContext.BACKEND_ENDPOINT;
|
delete newContext.BACKEND_ENDPOINT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,7 +139,7 @@ export function updateConfigContext(newContext: Partial<ConfigContext>): void {
|
|||||||
Object.assign(configContext, newContext);
|
Object.assign(configContext, newContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Injected for local develpment. These will be removed in the production bundle by webpack
|
// Injected for local development. These will be removed in the production bundle by webpack
|
||||||
if (process.env.NODE_ENV === "development") {
|
if (process.env.NODE_ENV === "development") {
|
||||||
const port: string = process.env.PORT || "1234";
|
const port: string = process.env.PORT || "1234";
|
||||||
updateConfigContext({
|
updateConfigContext({
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { Action } from "Shared/Telemetry/TelemetryConstants";
|
||||||
|
import { traceOpen } from "Shared/Telemetry/TelemetryProcessor";
|
||||||
import { ReactTabKind, useTabs } from "hooks/useTabs";
|
import { ReactTabKind, useTabs } from "hooks/useTabs";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import AddCollectionIcon from "../../images/AddCollection.svg";
|
import AddCollectionIcon from "../../images/AddCollection.svg";
|
||||||
@@ -146,7 +148,10 @@ export const createSampleCollectionContextMenuButton = (): TreeNodeMenuItem[] =>
|
|||||||
if (userContext.apiType === "SQL") {
|
if (userContext.apiType === "SQL") {
|
||||||
items.push({
|
items.push({
|
||||||
iconSrc: AddSqlQueryIcon,
|
iconSrc: AddSqlQueryIcon,
|
||||||
onClick: () => useTabs.getState().openAndActivateReactTab(ReactTabKind.QueryCopilot),
|
onClick: () => {
|
||||||
|
useTabs.getState().openAndActivateReactTab(ReactTabKind.QueryCopilot);
|
||||||
|
traceOpen(Action.OpenQueryCopilotFromNewQuery, { apiType: userContext.apiType });
|
||||||
|
},
|
||||||
label: "New SQL Query",
|
label: "New SQL Query",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,18 +5,18 @@ import DiscardIcon from "../../../../images/discard.svg";
|
|||||||
import SaveIcon from "../../../../images/save-cosmos.svg";
|
import SaveIcon from "../../../../images/save-cosmos.svg";
|
||||||
import { AuthType } from "../../../AuthType";
|
import { AuthType } from "../../../AuthType";
|
||||||
import * as Constants from "../../../Common/Constants";
|
import * as Constants from "../../../Common/Constants";
|
||||||
|
import { getErrorMessage, getErrorStack } from "../../../Common/ErrorHandlingUtils";
|
||||||
import { getIndexTransformationProgress } from "../../../Common/dataAccess/getIndexTransformationProgress";
|
import { getIndexTransformationProgress } from "../../../Common/dataAccess/getIndexTransformationProgress";
|
||||||
import { readMongoDBCollectionThroughRP } from "../../../Common/dataAccess/readMongoDBCollection";
|
import { readMongoDBCollectionThroughRP } from "../../../Common/dataAccess/readMongoDBCollection";
|
||||||
import { updateCollection } from "../../../Common/dataAccess/updateCollection";
|
import { updateCollection } from "../../../Common/dataAccess/updateCollection";
|
||||||
import { updateOffer } from "../../../Common/dataAccess/updateOffer";
|
import { updateOffer } from "../../../Common/dataAccess/updateOffer";
|
||||||
import { getErrorMessage, getErrorStack } from "../../../Common/ErrorHandlingUtils";
|
|
||||||
import * as DataModels from "../../../Contracts/DataModels";
|
import * as DataModels from "../../../Contracts/DataModels";
|
||||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||||
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
|
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
|
||||||
import { trace, traceFailure, traceStart, traceSuccess } from "../../../Shared/Telemetry/TelemetryProcessor";
|
import { trace, traceFailure, traceStart, traceSuccess } from "../../../Shared/Telemetry/TelemetryProcessor";
|
||||||
import { userContext } from "../../../UserContext";
|
import { userContext } from "../../../UserContext";
|
||||||
import { MongoDBCollectionResource, MongoIndex } from "../../../Utils/arm/generatedClients/cosmos/types";
|
|
||||||
import * as AutoPilotUtils from "../../../Utils/AutoPilotUtils";
|
import * as AutoPilotUtils from "../../../Utils/AutoPilotUtils";
|
||||||
|
import { MongoDBCollectionResource, MongoIndex } from "../../../Utils/arm/generatedClients/cosmos/types";
|
||||||
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
|
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
|
||||||
import { useCommandBar } from "../../Menus/CommandBar/CommandBarComponentAdapter";
|
import { useCommandBar } from "../../Menus/CommandBar/CommandBarComponentAdapter";
|
||||||
import { SettingsTabV2 } from "../../Tabs/SettingsTabV2";
|
import { SettingsTabV2 } from "../../Tabs/SettingsTabV2";
|
||||||
@@ -37,15 +37,15 @@ import {
|
|||||||
AddMongoIndexProps,
|
AddMongoIndexProps,
|
||||||
ChangeFeedPolicyState,
|
ChangeFeedPolicyState,
|
||||||
GeospatialConfigType,
|
GeospatialConfigType,
|
||||||
|
MongoIndexTypes,
|
||||||
|
SettingsV2TabTypes,
|
||||||
|
TtlType,
|
||||||
getMongoNotification,
|
getMongoNotification,
|
||||||
getTabTitle,
|
getTabTitle,
|
||||||
hasDatabaseSharedThroughput,
|
hasDatabaseSharedThroughput,
|
||||||
isDirty,
|
isDirty,
|
||||||
MongoIndexTypes,
|
|
||||||
parseConflictResolutionMode,
|
parseConflictResolutionMode,
|
||||||
parseConflictResolutionProcedure,
|
parseConflictResolutionProcedure,
|
||||||
SettingsV2TabTypes,
|
|
||||||
TtlType,
|
|
||||||
} from "./SettingsUtils";
|
} from "./SettingsUtils";
|
||||||
|
|
||||||
interface SettingsV2TabInfo {
|
interface SettingsV2TabInfo {
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { Link } from "@fluentui/react/lib/Link";
|
import { Link } from "@fluentui/react/lib/Link";
|
||||||
import { isPublicInternetAccessAllowed } from "Common/DatabaseAccountUtility";
|
import { isPublicInternetAccessAllowed } from "Common/DatabaseAccountUtility";
|
||||||
|
import { sendMessage } from "Common/MessageHandler";
|
||||||
|
import { Platform } from "ConfigContext";
|
||||||
|
import { MessageTypes } from "Contracts/ExplorerContracts";
|
||||||
import { IGalleryItem } from "Juno/JunoClient";
|
import { IGalleryItem } from "Juno/JunoClient";
|
||||||
import { allowedNotebookServerUrls, validateEndpoint } from "Utils/EndpointValidation";
|
import { allowedNotebookServerUrls, validateEndpoint } from "Utils/EndpointValidation";
|
||||||
import * as ko from "knockout";
|
import * as ko from "knockout";
|
||||||
@@ -23,7 +26,7 @@ import { PhoenixClient } from "../Phoenix/PhoenixClient";
|
|||||||
import * as ExplorerSettings from "../Shared/ExplorerSettings";
|
import * as ExplorerSettings from "../Shared/ExplorerSettings";
|
||||||
import { Action, ActionModifiers } from "../Shared/Telemetry/TelemetryConstants";
|
import { Action, ActionModifiers } from "../Shared/Telemetry/TelemetryConstants";
|
||||||
import * as TelemetryProcessor from "../Shared/Telemetry/TelemetryProcessor";
|
import * as TelemetryProcessor from "../Shared/Telemetry/TelemetryProcessor";
|
||||||
import { userContext } from "../UserContext";
|
import { isAccountNewerThanThresholdInMs, userContext } from "../UserContext";
|
||||||
import { getCollectionName, getUploadName } from "../Utils/APITypeUtils";
|
import { getCollectionName, getUploadName } from "../Utils/APITypeUtils";
|
||||||
import { stringToBlob } from "../Utils/BlobUtils";
|
import { stringToBlob } from "../Utils/BlobUtils";
|
||||||
import { isCapabilityEnabled } from "../Utils/CapabilityUtils";
|
import { isCapabilityEnabled } from "../Utils/CapabilityUtils";
|
||||||
@@ -258,6 +261,45 @@ export default class Explorer {
|
|||||||
// TODO: return result
|
// 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 isAccountNewerThanNinetyDays = isAccountNewerThanThresholdInMs(
|
||||||
|
userContext.databaseAccount?.systemData?.createdAt || "",
|
||||||
|
NINETY_DAYS_IN_MS
|
||||||
|
);
|
||||||
|
|
||||||
|
// Try Cosmos DB subscription - survey shown to random 25% of users at day 1 in Data Explorer.
|
||||||
|
if (userContext.isTryCosmosDBSubscription) {
|
||||||
|
if (
|
||||||
|
isAccountNewerThanThresholdInMs(userContext.databaseAccount?.systemData?.createdAt || "", ONE_DAY_IN_MS) &&
|
||||||
|
this.getRandomInt(100) < 25
|
||||||
|
) {
|
||||||
|
sendMessage({ type: MessageTypes.DisplayNPSSurvey });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// An existing account is lesser than 90 days old. For existing account show to random 10 % of users in Data Explorer.
|
||||||
|
if (isAccountNewerThanNinetyDays) {
|
||||||
|
if (this.getRandomInt(100) < 10) {
|
||||||
|
sendMessage({ type: MessageTypes.DisplayNPSSurvey });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// An existing account is greater than 90 days. For existing account show to random 25 % of users in Data Explorer.
|
||||||
|
if (this.getRandomInt(100) < 25) {
|
||||||
|
sendMessage({ type: MessageTypes.DisplayNPSSurvey });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async refreshDatabaseForResourceToken(): Promise<void> {
|
public async refreshDatabaseForResourceToken(): Promise<void> {
|
||||||
const databaseId = userContext.parsedResourceToken?.databaseId;
|
const databaseId = userContext.parsedResourceToken?.databaseId;
|
||||||
const collectionId = userContext.parsedResourceToken?.collectionId;
|
const collectionId = userContext.parsedResourceToken?.collectionId;
|
||||||
|
|||||||
@@ -4,16 +4,21 @@
|
|||||||
* and update any knockout observables passed from the parent.
|
* and update any knockout observables passed from the parent.
|
||||||
*/
|
*/
|
||||||
import { CommandBar as FluentCommandBar, ICommandBarItemProps } from "@fluentui/react";
|
import { CommandBar as FluentCommandBar, ICommandBarItemProps } from "@fluentui/react";
|
||||||
|
import { AuthType } from "AuthType";
|
||||||
import { useNotebook } from "Explorer/Notebook/useNotebook";
|
import { useNotebook } from "Explorer/Notebook/useNotebook";
|
||||||
import * as React from "react";
|
|
||||||
import { userContext } from "UserContext";
|
import { userContext } from "UserContext";
|
||||||
|
import * as React from "react";
|
||||||
import create, { UseStore } from "zustand";
|
import create, { UseStore } from "zustand";
|
||||||
import { ConnectionStatusType, StyleConstants } from "../../../Common/Constants";
|
import { ConnectionStatusType, StyleConstants } from "../../../Common/Constants";
|
||||||
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
|
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
|
||||||
import Explorer from "../../Explorer";
|
import Explorer from "../../Explorer";
|
||||||
import { useSelectedNode } from "../../useSelectedNode";
|
import { useSelectedNode } from "../../useSelectedNode";
|
||||||
import * as CommandBarComponentButtonFactory from "./CommandBarComponentButtonFactory";
|
|
||||||
import * as CommandBarUtil from "./CommandBarUtil";
|
import * as CommandBarUtil from "./CommandBarUtil";
|
||||||
|
import * as createContextCommandBarButtons from "./CommandButtonsFactories/createContextCommandBarButtons";
|
||||||
|
import * as createControlCommandBarButtons from "./CommandButtonsFactories/createControlCommandBarButtons";
|
||||||
|
import * as createPostgreButtons from "./CommandButtonsFactories/createPostgreButtons";
|
||||||
|
import * as createStaticCommandBarButtons from "./CommandButtonsFactories/createStaticCommandBarButtons";
|
||||||
|
import * as createStaticCommandBarButtonsForResourceToken from "./CommandButtonsFactories/createStaticCommandBarButtonsForResourceToken";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
container: Explorer;
|
container: Explorer;
|
||||||
@@ -35,7 +40,7 @@ export const CommandBar: React.FC<Props> = ({ container }: Props) => {
|
|||||||
const backgroundColor = StyleConstants.BaseLight;
|
const backgroundColor = StyleConstants.BaseLight;
|
||||||
|
|
||||||
if (userContext.apiType === "Postgres") {
|
if (userContext.apiType === "Postgres") {
|
||||||
const buttons = CommandBarComponentButtonFactory.createPostgreButtons(container);
|
const buttons = createPostgreButtons.createPostgreButtons(container);
|
||||||
return (
|
return (
|
||||||
<div className="commandBarContainer">
|
<div className="commandBarContainer">
|
||||||
<FluentCommandBar
|
<FluentCommandBar
|
||||||
@@ -50,11 +55,18 @@ export const CommandBar: React.FC<Props> = ({ container }: Props) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const staticButtons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(container, selectedNodeState);
|
const staticButtons =
|
||||||
|
userContext.authType === AuthType.ResourceToken
|
||||||
|
? createStaticCommandBarButtonsForResourceToken.createStaticCommandBarButtonsForResourceToken(
|
||||||
|
container,
|
||||||
|
selectedNodeState
|
||||||
|
)
|
||||||
|
: createStaticCommandBarButtons.createStaticCommandBarButtons(container, selectedNodeState);
|
||||||
|
|
||||||
const contextButtons = (buttons || []).concat(
|
const contextButtons = (buttons || []).concat(
|
||||||
CommandBarComponentButtonFactory.createContextCommandBarButtons(container, selectedNodeState)
|
createContextCommandBarButtons.createContextCommandBarButtons(container, selectedNodeState)
|
||||||
);
|
);
|
||||||
const controlButtons = CommandBarComponentButtonFactory.createControlCommandBarButtons(container);
|
const controlButtons = createControlCommandBarButtons.createControlCommandBarButtons(container);
|
||||||
|
|
||||||
const uiFabricStaticButtons = CommandBarUtil.convertButton(staticButtons, backgroundColor);
|
const uiFabricStaticButtons = CommandBarUtil.convertButton(staticButtons, backgroundColor);
|
||||||
if (buttons && buttons.length > 0) {
|
if (buttons && buttons.length > 0) {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import NotebookManager from "../../Notebook/NotebookManager";
|
|||||||
import { useNotebook } from "../../Notebook/useNotebook";
|
import { useNotebook } from "../../Notebook/useNotebook";
|
||||||
import { useDatabases } from "../../useDatabases";
|
import { useDatabases } from "../../useDatabases";
|
||||||
import { useSelectedNode } from "../../useSelectedNode";
|
import { useSelectedNode } from "../../useSelectedNode";
|
||||||
import * as CommandBarComponentButtonFactory from "./CommandBarComponentButtonFactory";
|
import * as createStaticCommandBarButtons from "./CommandButtonsFactories/createStaticCommandBarButtons";
|
||||||
|
|
||||||
describe("CommandBarComponentButtonFactory tests", () => {
|
describe("CommandBarComponentButtonFactory tests", () => {
|
||||||
let mockExplorer: Explorer;
|
let mockExplorer: Explorer;
|
||||||
@@ -32,7 +32,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("Button should be visible", () => {
|
it("Button should be visible", () => {
|
||||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
const buttons = createStaticCommandBarButtons.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||||
const enableAzureSynapseLinkBtn = buttons.find(
|
const enableAzureSynapseLinkBtn = buttons.find(
|
||||||
(button) => button.commandButtonLabel === enableAzureSynapseLinkBtnLabel
|
(button) => button.commandButtonLabel === enableAzureSynapseLinkBtnLabel
|
||||||
);
|
);
|
||||||
@@ -48,7 +48,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||||||
} as DatabaseAccount,
|
} as DatabaseAccount,
|
||||||
});
|
});
|
||||||
|
|
||||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
const buttons = createStaticCommandBarButtons.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||||
const enableAzureSynapseLinkBtn = buttons.find(
|
const enableAzureSynapseLinkBtn = buttons.find(
|
||||||
(button) => button.commandButtonLabel === enableAzureSynapseLinkBtnLabel
|
(button) => button.commandButtonLabel === enableAzureSynapseLinkBtnLabel
|
||||||
);
|
);
|
||||||
@@ -64,7 +64,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||||||
} as DatabaseAccount,
|
} as DatabaseAccount,
|
||||||
});
|
});
|
||||||
|
|
||||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
const buttons = createStaticCommandBarButtons.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||||
const enableAzureSynapseLinkBtn = buttons.find(
|
const enableAzureSynapseLinkBtn = buttons.find(
|
||||||
(button) => button.commandButtonLabel === enableAzureSynapseLinkBtnLabel
|
(button) => button.commandButtonLabel === enableAzureSynapseLinkBtnLabel
|
||||||
);
|
);
|
||||||
@@ -100,7 +100,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||||||
useNotebook.getState().setIsNotebookEnabled(true);
|
useNotebook.getState().setIsNotebookEnabled(true);
|
||||||
useNotebook.getState().setIsNotebooksEnabledForAccount(true);
|
useNotebook.getState().setIsNotebooksEnabledForAccount(true);
|
||||||
|
|
||||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
const buttons = createStaticCommandBarButtons.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||||
const enableNotebookBtn = buttons.find((button) => button.commandButtonLabel === enableNotebookBtnLabel);
|
const enableNotebookBtn = buttons.find((button) => button.commandButtonLabel === enableNotebookBtnLabel);
|
||||||
expect(enableNotebookBtn).toBeUndefined();
|
expect(enableNotebookBtn).toBeUndefined();
|
||||||
});
|
});
|
||||||
@@ -110,7 +110,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||||||
portalEnv: "mooncake",
|
portalEnv: "mooncake",
|
||||||
});
|
});
|
||||||
|
|
||||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
const buttons = createStaticCommandBarButtons.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||||
const enableNotebookBtn = buttons.find((button) => button.commandButtonLabel === enableNotebookBtnLabel);
|
const enableNotebookBtn = buttons.find((button) => button.commandButtonLabel === enableNotebookBtnLabel);
|
||||||
expect(enableNotebookBtn).toBeUndefined();
|
expect(enableNotebookBtn).toBeUndefined();
|
||||||
});
|
});
|
||||||
@@ -118,7 +118,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||||||
it("Notebooks is not enabled but is available - button should be shown and enabled", () => {
|
it("Notebooks is not enabled but is available - button should be shown and enabled", () => {
|
||||||
useNotebook.getState().setIsNotebooksEnabledForAccount(true);
|
useNotebook.getState().setIsNotebooksEnabledForAccount(true);
|
||||||
|
|
||||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
const buttons = createStaticCommandBarButtons.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||||
const enableNotebookBtn = buttons.find((button) => button.commandButtonLabel === enableNotebookBtnLabel);
|
const enableNotebookBtn = buttons.find((button) => button.commandButtonLabel === enableNotebookBtnLabel);
|
||||||
|
|
||||||
//TODO: modify once notebooks are available
|
//TODO: modify once notebooks are available
|
||||||
@@ -129,7 +129,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("Notebooks is not enabled and is unavailable - button should be shown and disabled", () => {
|
it("Notebooks is not enabled and is unavailable - button should be shown and disabled", () => {
|
||||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
const buttons = createStaticCommandBarButtons.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||||
const enableNotebookBtn = buttons.find((button) => button.commandButtonLabel === enableNotebookBtnLabel);
|
const enableNotebookBtn = buttons.find((button) => button.commandButtonLabel === enableNotebookBtnLabel);
|
||||||
|
|
||||||
//TODO: modify once notebooks are available
|
//TODO: modify once notebooks are available
|
||||||
@@ -180,7 +180,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||||||
updateUserContext({
|
updateUserContext({
|
||||||
apiType: "SQL",
|
apiType: "SQL",
|
||||||
});
|
});
|
||||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
const buttons = createStaticCommandBarButtons.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||||
const openMongoShellBtn = buttons.find((button) => button.commandButtonLabel === openMongoShellBtnLabel);
|
const openMongoShellBtn = buttons.find((button) => button.commandButtonLabel === openMongoShellBtnLabel);
|
||||||
expect(openMongoShellBtn).toBeUndefined();
|
expect(openMongoShellBtn).toBeUndefined();
|
||||||
});
|
});
|
||||||
@@ -190,13 +190,13 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||||||
portalEnv: "mooncake",
|
portalEnv: "mooncake",
|
||||||
});
|
});
|
||||||
|
|
||||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
const buttons = createStaticCommandBarButtons.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||||
const openMongoShellBtn = buttons.find((button) => button.commandButtonLabel === openMongoShellBtnLabel);
|
const openMongoShellBtn = buttons.find((button) => button.commandButtonLabel === openMongoShellBtnLabel);
|
||||||
expect(openMongoShellBtn).toBeUndefined();
|
expect(openMongoShellBtn).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Notebooks is not enabled and is unavailable - button should be hidden", () => {
|
it("Notebooks is not enabled and is unavailable - button should be hidden", () => {
|
||||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
const buttons = createStaticCommandBarButtons.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||||
const openMongoShellBtn = buttons.find((button) => button.commandButtonLabel === openMongoShellBtnLabel);
|
const openMongoShellBtn = buttons.find((button) => button.commandButtonLabel === openMongoShellBtnLabel);
|
||||||
expect(openMongoShellBtn).toBeUndefined();
|
expect(openMongoShellBtn).toBeUndefined();
|
||||||
});
|
});
|
||||||
@@ -204,7 +204,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||||||
it("Notebooks is not enabled and is available - button should be hidden", () => {
|
it("Notebooks is not enabled and is available - button should be hidden", () => {
|
||||||
useNotebook.getState().setIsNotebooksEnabledForAccount(true);
|
useNotebook.getState().setIsNotebooksEnabledForAccount(true);
|
||||||
|
|
||||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
const buttons = createStaticCommandBarButtons.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||||
const openMongoShellBtn = buttons.find((button) => button.commandButtonLabel === openMongoShellBtnLabel);
|
const openMongoShellBtn = buttons.find((button) => button.commandButtonLabel === openMongoShellBtnLabel);
|
||||||
expect(openMongoShellBtn).toBeUndefined();
|
expect(openMongoShellBtn).toBeUndefined();
|
||||||
});
|
});
|
||||||
@@ -212,7 +212,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||||||
it("Notebooks is enabled and is unavailable - button should be shown and enabled", () => {
|
it("Notebooks is enabled and is unavailable - button should be shown and enabled", () => {
|
||||||
useNotebook.getState().setIsNotebookEnabled(true);
|
useNotebook.getState().setIsNotebookEnabled(true);
|
||||||
|
|
||||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
const buttons = createStaticCommandBarButtons.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||||
const openMongoShellBtn = buttons.find((button) => button.commandButtonLabel === openMongoShellBtnLabel);
|
const openMongoShellBtn = buttons.find((button) => button.commandButtonLabel === openMongoShellBtnLabel);
|
||||||
expect(openMongoShellBtn).toBeDefined();
|
expect(openMongoShellBtn).toBeDefined();
|
||||||
|
|
||||||
@@ -226,7 +226,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||||||
useNotebook.getState().setIsNotebookEnabled(true);
|
useNotebook.getState().setIsNotebookEnabled(true);
|
||||||
useNotebook.getState().setIsNotebooksEnabledForAccount(true);
|
useNotebook.getState().setIsNotebooksEnabledForAccount(true);
|
||||||
|
|
||||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
const buttons = createStaticCommandBarButtons.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||||
const openMongoShellBtn = buttons.find((button) => button.commandButtonLabel === openMongoShellBtnLabel);
|
const openMongoShellBtn = buttons.find((button) => button.commandButtonLabel === openMongoShellBtnLabel);
|
||||||
expect(openMongoShellBtn).toBeDefined();
|
expect(openMongoShellBtn).toBeDefined();
|
||||||
|
|
||||||
@@ -241,7 +241,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||||||
useNotebook.getState().setIsNotebooksEnabledForAccount(true);
|
useNotebook.getState().setIsNotebooksEnabledForAccount(true);
|
||||||
useNotebook.getState().setIsShellEnabled(false);
|
useNotebook.getState().setIsShellEnabled(false);
|
||||||
|
|
||||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
const buttons = createStaticCommandBarButtons.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||||
const openMongoShellBtn = buttons.find((button) => button.commandButtonLabel === openMongoShellBtnLabel);
|
const openMongoShellBtn = buttons.find((button) => button.commandButtonLabel === openMongoShellBtnLabel);
|
||||||
expect(openMongoShellBtn).toBeUndefined();
|
expect(openMongoShellBtn).toBeUndefined();
|
||||||
});
|
});
|
||||||
@@ -285,7 +285,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||||||
},
|
},
|
||||||
} as DatabaseAccount,
|
} as DatabaseAccount,
|
||||||
});
|
});
|
||||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
const buttons = createStaticCommandBarButtons.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||||
const openCassandraShellBtn = buttons.find((button) => button.commandButtonLabel === openCassandraShellBtnLabel);
|
const openCassandraShellBtn = buttons.find((button) => button.commandButtonLabel === openCassandraShellBtnLabel);
|
||||||
expect(openCassandraShellBtn).toBeUndefined();
|
expect(openCassandraShellBtn).toBeUndefined();
|
||||||
});
|
});
|
||||||
@@ -295,13 +295,13 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||||||
portalEnv: "mooncake",
|
portalEnv: "mooncake",
|
||||||
});
|
});
|
||||||
|
|
||||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
const buttons = createStaticCommandBarButtons.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||||
const openCassandraShellBtn = buttons.find((button) => button.commandButtonLabel === openCassandraShellBtnLabel);
|
const openCassandraShellBtn = buttons.find((button) => button.commandButtonLabel === openCassandraShellBtnLabel);
|
||||||
expect(openCassandraShellBtn).toBeUndefined();
|
expect(openCassandraShellBtn).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Notebooks is not enabled and is unavailable - button should be shown and disabled", () => {
|
it("Notebooks is not enabled and is unavailable - button should be shown and disabled", () => {
|
||||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
const buttons = createStaticCommandBarButtons.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||||
const openCassandraShellBtn = buttons.find((button) => button.commandButtonLabel === openCassandraShellBtnLabel);
|
const openCassandraShellBtn = buttons.find((button) => button.commandButtonLabel === openCassandraShellBtnLabel);
|
||||||
expect(openCassandraShellBtn).toBeUndefined();
|
expect(openCassandraShellBtn).toBeUndefined();
|
||||||
});
|
});
|
||||||
@@ -309,7 +309,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||||||
it("Notebooks is not enabled and is available - button should be shown and enabled", () => {
|
it("Notebooks is not enabled and is available - button should be shown and enabled", () => {
|
||||||
useNotebook.getState().setIsNotebooksEnabledForAccount(true);
|
useNotebook.getState().setIsNotebooksEnabledForAccount(true);
|
||||||
|
|
||||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
const buttons = createStaticCommandBarButtons.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||||
const openCassandraShellBtn = buttons.find((button) => button.commandButtonLabel === openCassandraShellBtnLabel);
|
const openCassandraShellBtn = buttons.find((button) => button.commandButtonLabel === openCassandraShellBtnLabel);
|
||||||
expect(openCassandraShellBtn).toBeUndefined();
|
expect(openCassandraShellBtn).toBeUndefined();
|
||||||
});
|
});
|
||||||
@@ -317,7 +317,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||||||
it("Notebooks is enabled and is unavailable - button should be shown and enabled", () => {
|
it("Notebooks is enabled and is unavailable - button should be shown and enabled", () => {
|
||||||
useNotebook.getState().setIsNotebookEnabled(true);
|
useNotebook.getState().setIsNotebookEnabled(true);
|
||||||
|
|
||||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
const buttons = createStaticCommandBarButtons.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||||
const openCassandraShellBtn = buttons.find((button) => button.commandButtonLabel === openCassandraShellBtnLabel);
|
const openCassandraShellBtn = buttons.find((button) => button.commandButtonLabel === openCassandraShellBtnLabel);
|
||||||
|
|
||||||
expect(openCassandraShellBtn).toBeDefined();
|
expect(openCassandraShellBtn).toBeDefined();
|
||||||
@@ -332,7 +332,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||||||
useNotebook.getState().setIsNotebookEnabled(true);
|
useNotebook.getState().setIsNotebookEnabled(true);
|
||||||
useNotebook.getState().setIsNotebooksEnabledForAccount(true);
|
useNotebook.getState().setIsNotebooksEnabledForAccount(true);
|
||||||
|
|
||||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
const buttons = createStaticCommandBarButtons.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||||
const openCassandraShellBtn = buttons.find((button) => button.commandButtonLabel === openCassandraShellBtnLabel);
|
const openCassandraShellBtn = buttons.find((button) => button.commandButtonLabel === openCassandraShellBtnLabel);
|
||||||
expect(openCassandraShellBtn).toBeDefined();
|
expect(openCassandraShellBtn).toBeDefined();
|
||||||
|
|
||||||
@@ -370,7 +370,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||||||
it("Notebooks is enabled and GitHubOAuthService is not logged in - connect to github button should be visible", () => {
|
it("Notebooks is enabled and GitHubOAuthService is not logged in - connect to github button should be visible", () => {
|
||||||
useNotebook.getState().setIsNotebookEnabled(true);
|
useNotebook.getState().setIsNotebookEnabled(true);
|
||||||
|
|
||||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
const buttons = createStaticCommandBarButtons.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||||
const connectToGitHubBtn = buttons.find((button) => button.commandButtonLabel === connectToGitHubBtnLabel);
|
const connectToGitHubBtn = buttons.find((button) => button.commandButtonLabel === connectToGitHubBtnLabel);
|
||||||
expect(connectToGitHubBtn).toBeDefined();
|
expect(connectToGitHubBtn).toBeDefined();
|
||||||
});
|
});
|
||||||
@@ -379,7 +379,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||||||
useNotebook.getState().setIsNotebookEnabled(true);
|
useNotebook.getState().setIsNotebookEnabled(true);
|
||||||
mockExplorer.notebookManager.gitHubOAuthService.isLoggedIn = jest.fn().mockReturnValue(true);
|
mockExplorer.notebookManager.gitHubOAuthService.isLoggedIn = jest.fn().mockReturnValue(true);
|
||||||
|
|
||||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
const buttons = createStaticCommandBarButtons.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||||
const manageGitHubSettingsBtn = buttons.find(
|
const manageGitHubSettingsBtn = buttons.find(
|
||||||
(button) => button.commandButtonLabel === manageGitHubSettingsBtnLabel
|
(button) => button.commandButtonLabel === manageGitHubSettingsBtnLabel
|
||||||
);
|
);
|
||||||
@@ -387,7 +387,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("Notebooks is not enabled - connect to github and manage github settings buttons should be hidden", () => {
|
it("Notebooks is not enabled - connect to github and manage github settings buttons should be hidden", () => {
|
||||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
const buttons = createStaticCommandBarButtons.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||||
|
|
||||||
const connectToGitHubBtn = buttons.find((button) => button.commandButtonLabel === connectToGitHubBtnLabel);
|
const connectToGitHubBtn = buttons.find((button) => button.commandButtonLabel === connectToGitHubBtnLabel);
|
||||||
expect(connectToGitHubBtn).toBeUndefined();
|
expect(connectToGitHubBtn).toBeUndefined();
|
||||||
@@ -419,7 +419,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||||||
kind: "DocumentDB",
|
kind: "DocumentDB",
|
||||||
} as DatabaseAccount,
|
} as DatabaseAccount,
|
||||||
});
|
});
|
||||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
const buttons = createStaticCommandBarButtons.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||||
expect(buttons.length).toBe(2);
|
expect(buttons.length).toBe(2);
|
||||||
expect(buttons[0].commandButtonLabel).toBe("New SQL Query");
|
expect(buttons[0].commandButtonLabel).toBe("New SQL Query");
|
||||||
expect(buttons[0].disabled).toBe(false);
|
expect(buttons[0].disabled).toBe(false);
|
||||||
|
|||||||
@@ -1,633 +0,0 @@
|
|||||||
import { ReactTabKind, useTabs } from "hooks/useTabs";
|
|
||||||
import * as React from "react";
|
|
||||||
import AddCollectionIcon from "../../../../images/AddCollection.svg";
|
|
||||||
import AddDatabaseIcon from "../../../../images/AddDatabase.svg";
|
|
||||||
import AddSqlQueryIcon from "../../../../images/AddSqlQuery_16x16.svg";
|
|
||||||
import AddStoredProcedureIcon from "../../../../images/AddStoredProcedure.svg";
|
|
||||||
import AddTriggerIcon from "../../../../images/AddTrigger.svg";
|
|
||||||
import AddUdfIcon from "../../../../images/AddUdf.svg";
|
|
||||||
import BrowseQueriesIcon from "../../../../images/BrowseQuery.svg";
|
|
||||||
import CosmosTerminalIcon from "../../../../images/Cosmos-Terminal.svg";
|
|
||||||
import FeedbackIcon from "../../../../images/Feedback-Command.svg";
|
|
||||||
import HostedTerminalIcon from "../../../../images/Hosted-Terminal.svg";
|
|
||||||
import OpenQueryFromDiskIcon from "../../../../images/OpenQueryFromDisk.svg";
|
|
||||||
import GitHubIcon from "../../../../images/github.svg";
|
|
||||||
import NewNotebookIcon from "../../../../images/notebook/Notebook-new.svg";
|
|
||||||
import ResetWorkspaceIcon from "../../../../images/notebook/Notebook-reset-workspace.svg";
|
|
||||||
import OpenInTabIcon from "../../../../images/open-in-tab.svg";
|
|
||||||
import SettingsIcon from "../../../../images/settings_15x15.svg";
|
|
||||||
import SynapseIcon from "../../../../images/synapse-link.svg";
|
|
||||||
import { AuthType } from "../../../AuthType";
|
|
||||||
import * as Constants from "../../../Common/Constants";
|
|
||||||
import { Platform, configContext } from "../../../ConfigContext";
|
|
||||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
|
||||||
import { JunoClient } from "../../../Juno/JunoClient";
|
|
||||||
import { userContext } from "../../../UserContext";
|
|
||||||
import { getCollectionName, getDatabaseName } from "../../../Utils/APITypeUtils";
|
|
||||||
import { isRunningOnNationalCloud } from "../../../Utils/CloudUtils";
|
|
||||||
import { useSidePanel } from "../../../hooks/useSidePanel";
|
|
||||||
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
|
|
||||||
import Explorer from "../../Explorer";
|
|
||||||
import { useNotebook } from "../../Notebook/useNotebook";
|
|
||||||
import { OpenFullScreen } from "../../OpenFullScreen";
|
|
||||||
import { AddDatabasePanel } from "../../Panes/AddDatabasePanel/AddDatabasePanel";
|
|
||||||
import { BrowseQueriesPane } from "../../Panes/BrowseQueriesPane/BrowseQueriesPane";
|
|
||||||
import { GitHubReposPanel } from "../../Panes/GitHubReposPanel/GitHubReposPanel";
|
|
||||||
import { LoadQueryPane } from "../../Panes/LoadQueryPane/LoadQueryPane";
|
|
||||||
import { SettingsPane } from "../../Panes/SettingsPane/SettingsPane";
|
|
||||||
import { useDatabases } from "../../useDatabases";
|
|
||||||
import { SelectedNodeState, useSelectedNode } from "../../useSelectedNode";
|
|
||||||
|
|
||||||
let counter = 0;
|
|
||||||
|
|
||||||
export function createStaticCommandBarButtons(
|
|
||||||
container: Explorer,
|
|
||||||
selectedNodeState: SelectedNodeState
|
|
||||||
): CommandButtonComponentProps[] {
|
|
||||||
if (userContext.authType === AuthType.ResourceToken) {
|
|
||||||
return createStaticCommandBarButtonsForResourceToken(container, selectedNodeState);
|
|
||||||
}
|
|
||||||
|
|
||||||
const newCollectionBtn = createNewCollectionGroup(container);
|
|
||||||
const buttons: CommandButtonComponentProps[] = [];
|
|
||||||
|
|
||||||
buttons.push(newCollectionBtn);
|
|
||||||
if (userContext.apiType !== "Tables" && userContext.apiType !== "Cassandra") {
|
|
||||||
const addSynapseLink = createOpenSynapseLinkDialogButton(container);
|
|
||||||
|
|
||||||
if (addSynapseLink) {
|
|
||||||
buttons.push(createDivider());
|
|
||||||
buttons.push(addSynapseLink);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (userContext.apiType !== "Tables") {
|
|
||||||
newCollectionBtn.children = [createNewCollectionGroup(container)];
|
|
||||||
const newDatabaseBtn = createNewDatabase(container);
|
|
||||||
newCollectionBtn.children.push(newDatabaseBtn);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (useNotebook.getState().isNotebookEnabled) {
|
|
||||||
buttons.push(createDivider());
|
|
||||||
const notebookButtons: CommandButtonComponentProps[] = [];
|
|
||||||
|
|
||||||
const newNotebookButton = createNewNotebookButton(container);
|
|
||||||
newNotebookButton.children = [createNewNotebookButton(container), createuploadNotebookButton(container)];
|
|
||||||
notebookButtons.push(newNotebookButton);
|
|
||||||
|
|
||||||
if (container.notebookManager?.gitHubOAuthService) {
|
|
||||||
notebookButtons.push(createManageGitHubAccountButton(container));
|
|
||||||
}
|
|
||||||
if (useNotebook.getState().isPhoenixFeatures && configContext.isTerminalEnabled) {
|
|
||||||
notebookButtons.push(createOpenTerminalButton(container));
|
|
||||||
}
|
|
||||||
if (useNotebook.getState().isPhoenixNotebooks && selectedNodeState.isConnectedToContainer()) {
|
|
||||||
notebookButtons.push(createNotebookWorkspaceResetButton(container));
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
(userContext.apiType === "Mongo" &&
|
|
||||||
useNotebook.getState().isShellEnabled &&
|
|
||||||
selectedNodeState.isDatabaseNodeOrNoneSelected()) ||
|
|
||||||
userContext.apiType === "Cassandra"
|
|
||||||
) {
|
|
||||||
notebookButtons.push(createDivider());
|
|
||||||
if (userContext.apiType === "Cassandra") {
|
|
||||||
notebookButtons.push(createOpenCassandraTerminalButton(container));
|
|
||||||
} else {
|
|
||||||
notebookButtons.push(createOpenMongoTerminalButton(container));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
notebookButtons.forEach((btn) => {
|
|
||||||
if (btn.commandButtonLabel.indexOf("Cassandra") !== -1) {
|
|
||||||
if (!useNotebook.getState().isPhoenixFeatures) {
|
|
||||||
applyNotebooksTemporarilyDownStyle(btn, Constants.Notebook.cassandraShellTemporarilyDownMsg);
|
|
||||||
}
|
|
||||||
} else if (btn.commandButtonLabel.indexOf("Mongo") !== -1) {
|
|
||||||
if (!useNotebook.getState().isPhoenixFeatures) {
|
|
||||||
applyNotebooksTemporarilyDownStyle(btn, Constants.Notebook.mongoShellTemporarilyDownMsg);
|
|
||||||
}
|
|
||||||
} else if (btn.commandButtonLabel.indexOf("Open Terminal") !== -1) {
|
|
||||||
if (!useNotebook.getState().isPhoenixFeatures) {
|
|
||||||
applyNotebooksTemporarilyDownStyle(btn, Constants.Notebook.temporarilyDownMsg);
|
|
||||||
}
|
|
||||||
} else if (!useNotebook.getState().isPhoenixNotebooks) {
|
|
||||||
applyNotebooksTemporarilyDownStyle(btn, Constants.Notebook.temporarilyDownMsg);
|
|
||||||
}
|
|
||||||
buttons.push(btn);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!selectedNodeState.isDatabaseNodeOrNoneSelected()) {
|
|
||||||
const isQuerySupported = userContext.apiType === "SQL" || userContext.apiType === "Gremlin";
|
|
||||||
|
|
||||||
if (isQuerySupported) {
|
|
||||||
buttons.push(createDivider());
|
|
||||||
const newSqlQueryBtn = createNewSQLQueryButton(selectedNodeState);
|
|
||||||
buttons.push(newSqlQueryBtn);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isQuerySupported && selectedNodeState.findSelectedCollection()) {
|
|
||||||
const openQueryBtn = createOpenQueryButton(container);
|
|
||||||
openQueryBtn.children = [createOpenQueryButton(container), createOpenQueryFromDiskButton()];
|
|
||||||
buttons.push(openQueryBtn);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (areScriptsSupported()) {
|
|
||||||
const label = "New Stored Procedure";
|
|
||||||
const newStoredProcedureBtn: CommandButtonComponentProps = {
|
|
||||||
iconSrc: AddStoredProcedureIcon,
|
|
||||||
iconAlt: label,
|
|
||||||
onCommandClick: () => {
|
|
||||||
const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection();
|
|
||||||
selectedCollection && selectedCollection.onNewStoredProcedureClick(selectedCollection);
|
|
||||||
},
|
|
||||||
commandButtonLabel: label,
|
|
||||||
ariaLabel: label,
|
|
||||||
hasPopup: true,
|
|
||||||
disabled:
|
|
||||||
useSelectedNode.getState().isQueryCopilotCollectionSelected() ||
|
|
||||||
selectedNodeState.isDatabaseNodeOrNoneSelected(),
|
|
||||||
};
|
|
||||||
|
|
||||||
newStoredProcedureBtn.children = createScriptCommandButtons(selectedNodeState);
|
|
||||||
buttons.push(newStoredProcedureBtn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return buttons;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createContextCommandBarButtons(
|
|
||||||
container: Explorer,
|
|
||||||
selectedNodeState: SelectedNodeState
|
|
||||||
): CommandButtonComponentProps[] {
|
|
||||||
const buttons: CommandButtonComponentProps[] = [];
|
|
||||||
|
|
||||||
if (!selectedNodeState.isDatabaseNodeOrNoneSelected() && userContext.apiType === "Mongo") {
|
|
||||||
const label = useNotebook.getState().isShellEnabled ? "Open Mongo Shell" : "New Shell";
|
|
||||||
const newMongoShellBtn: CommandButtonComponentProps = {
|
|
||||||
iconSrc: HostedTerminalIcon,
|
|
||||||
iconAlt: label,
|
|
||||||
onCommandClick: () => {
|
|
||||||
const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection();
|
|
||||||
if (useNotebook.getState().isShellEnabled) {
|
|
||||||
container.openNotebookTerminal(ViewModels.TerminalKind.Mongo);
|
|
||||||
} else {
|
|
||||||
selectedCollection && selectedCollection.onNewMongoShellClick();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
commandButtonLabel: label,
|
|
||||||
ariaLabel: label,
|
|
||||||
hasPopup: true,
|
|
||||||
};
|
|
||||||
buttons.push(newMongoShellBtn);
|
|
||||||
}
|
|
||||||
|
|
||||||
return buttons;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createControlCommandBarButtons(container: Explorer): CommandButtonComponentProps[] {
|
|
||||||
const buttons: CommandButtonComponentProps[] = [
|
|
||||||
{
|
|
||||||
iconSrc: SettingsIcon,
|
|
||||||
iconAlt: "Settings",
|
|
||||||
onCommandClick: () => useSidePanel.getState().openSidePanel("Settings", <SettingsPane />),
|
|
||||||
commandButtonLabel: undefined,
|
|
||||||
ariaLabel: "Settings",
|
|
||||||
tooltipText: "Settings",
|
|
||||||
hasPopup: true,
|
|
||||||
disabled: false,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const showOpenFullScreen =
|
|
||||||
configContext.platform === Platform.Portal && !isRunningOnNationalCloud() && userContext.apiType !== "Gremlin";
|
|
||||||
|
|
||||||
if (showOpenFullScreen) {
|
|
||||||
const label = "Open Full Screen";
|
|
||||||
const fullScreenButton: CommandButtonComponentProps = {
|
|
||||||
id: "openFullScreenBtn",
|
|
||||||
iconSrc: OpenInTabIcon,
|
|
||||||
iconAlt: label,
|
|
||||||
onCommandClick: () => {
|
|
||||||
useSidePanel.getState().openSidePanel("Open Full Screen", <OpenFullScreen />);
|
|
||||||
},
|
|
||||||
commandButtonLabel: undefined,
|
|
||||||
ariaLabel: label,
|
|
||||||
tooltipText: label,
|
|
||||||
hasPopup: false,
|
|
||||||
disabled: !showOpenFullScreen,
|
|
||||||
className: "OpenFullScreen",
|
|
||||||
};
|
|
||||||
buttons.push(fullScreenButton);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (configContext.platform !== Platform.Emulator) {
|
|
||||||
const label = "Feedback";
|
|
||||||
const feedbackButtonOptions: CommandButtonComponentProps = {
|
|
||||||
iconSrc: FeedbackIcon,
|
|
||||||
iconAlt: label,
|
|
||||||
onCommandClick: () => container.provideFeedbackEmail(),
|
|
||||||
commandButtonLabel: undefined,
|
|
||||||
ariaLabel: label,
|
|
||||||
tooltipText: label,
|
|
||||||
hasPopup: false,
|
|
||||||
disabled: false,
|
|
||||||
};
|
|
||||||
buttons.push(feedbackButtonOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
return buttons;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createDivider(): CommandButtonComponentProps {
|
|
||||||
const label = `divider${counter++}`;
|
|
||||||
return {
|
|
||||||
isDivider: true,
|
|
||||||
commandButtonLabel: label,
|
|
||||||
hasPopup: false,
|
|
||||||
iconSrc: undefined,
|
|
||||||
iconAlt: undefined,
|
|
||||||
onCommandClick: undefined,
|
|
||||||
ariaLabel: label,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function areScriptsSupported(): boolean {
|
|
||||||
return userContext.apiType === "SQL" || userContext.apiType === "Gremlin";
|
|
||||||
}
|
|
||||||
|
|
||||||
function createNewCollectionGroup(container: Explorer): CommandButtonComponentProps {
|
|
||||||
const label = `New ${getCollectionName()}`;
|
|
||||||
return {
|
|
||||||
iconSrc: AddCollectionIcon,
|
|
||||||
iconAlt: label,
|
|
||||||
onCommandClick: () => container.onNewCollectionClicked(),
|
|
||||||
commandButtonLabel: label,
|
|
||||||
ariaLabel: label,
|
|
||||||
hasPopup: true,
|
|
||||||
id: "createNewContainerCommandButton",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function createOpenSynapseLinkDialogButton(container: Explorer): CommandButtonComponentProps {
|
|
||||||
if (configContext.platform === Platform.Emulator) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (userContext?.databaseAccount?.properties?.enableAnalyticalStorage) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
const capabilities = userContext?.databaseAccount?.properties?.capabilities || [];
|
|
||||||
if (capabilities.some((capability) => capability.name === Constants.CapabilityNames.EnableStorageAnalytics)) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
const label = "Enable Azure Synapse Link";
|
|
||||||
return {
|
|
||||||
iconSrc: SynapseIcon,
|
|
||||||
iconAlt: label,
|
|
||||||
onCommandClick: () => container.openEnableSynapseLinkDialog(),
|
|
||||||
commandButtonLabel: label,
|
|
||||||
hasPopup: false,
|
|
||||||
disabled:
|
|
||||||
useSelectedNode.getState().isQueryCopilotCollectionSelected() || useNotebook.getState().isSynapseLinkUpdating,
|
|
||||||
ariaLabel: label,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function createNewDatabase(container: Explorer): CommandButtonComponentProps {
|
|
||||||
const label = "New " + getDatabaseName();
|
|
||||||
return {
|
|
||||||
iconSrc: AddDatabaseIcon,
|
|
||||||
iconAlt: label,
|
|
||||||
onCommandClick: async () => {
|
|
||||||
const throughputCap = userContext.databaseAccount?.properties.capacity?.totalThroughputLimit;
|
|
||||||
if (throughputCap && throughputCap !== -1) {
|
|
||||||
await useDatabases.getState().loadAllOffers();
|
|
||||||
}
|
|
||||||
useSidePanel.getState().openSidePanel("New " + getDatabaseName(), <AddDatabasePanel explorer={container} />);
|
|
||||||
},
|
|
||||||
commandButtonLabel: label,
|
|
||||||
ariaLabel: label,
|
|
||||||
hasPopup: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function createNewSQLQueryButton(selectedNodeState: SelectedNodeState): CommandButtonComponentProps {
|
|
||||||
if (userContext.apiType === "SQL" || userContext.apiType === "Gremlin") {
|
|
||||||
const label = "New SQL Query";
|
|
||||||
return {
|
|
||||||
id: "newQueryBtn",
|
|
||||||
iconSrc: AddSqlQueryIcon,
|
|
||||||
iconAlt: label,
|
|
||||||
onCommandClick: () => {
|
|
||||||
if (useSelectedNode.getState().isQueryCopilotCollectionSelected()) {
|
|
||||||
useTabs.getState().openAndActivateReactTab(ReactTabKind.QueryCopilot);
|
|
||||||
} else {
|
|
||||||
const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection();
|
|
||||||
selectedCollection && selectedCollection.onNewQueryClick(selectedCollection);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
commandButtonLabel: label,
|
|
||||||
ariaLabel: label,
|
|
||||||
hasPopup: true,
|
|
||||||
disabled: selectedNodeState.isDatabaseNodeOrNoneSelected(),
|
|
||||||
};
|
|
||||||
} else if (userContext.apiType === "Mongo") {
|
|
||||||
const label = "New Query";
|
|
||||||
return {
|
|
||||||
id: "newQueryBtn",
|
|
||||||
iconSrc: AddSqlQueryIcon,
|
|
||||||
iconAlt: label,
|
|
||||||
onCommandClick: () => {
|
|
||||||
const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection();
|
|
||||||
selectedCollection && selectedCollection.onNewMongoQueryClick(selectedCollection);
|
|
||||||
},
|
|
||||||
commandButtonLabel: label,
|
|
||||||
ariaLabel: label,
|
|
||||||
hasPopup: true,
|
|
||||||
disabled: selectedNodeState.isDatabaseNodeOrNoneSelected(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createScriptCommandButtons(selectedNodeState: SelectedNodeState): CommandButtonComponentProps[] {
|
|
||||||
const buttons: CommandButtonComponentProps[] = [];
|
|
||||||
|
|
||||||
const shouldEnableScriptsCommands: boolean =
|
|
||||||
!selectedNodeState.isDatabaseNodeOrNoneSelected() && areScriptsSupported();
|
|
||||||
|
|
||||||
if (shouldEnableScriptsCommands) {
|
|
||||||
const label = "New Stored Procedure";
|
|
||||||
const newStoredProcedureBtn: CommandButtonComponentProps = {
|
|
||||||
iconSrc: AddStoredProcedureIcon,
|
|
||||||
iconAlt: label,
|
|
||||||
onCommandClick: () => {
|
|
||||||
const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection();
|
|
||||||
selectedCollection && selectedCollection.onNewStoredProcedureClick(selectedCollection);
|
|
||||||
},
|
|
||||||
commandButtonLabel: label,
|
|
||||||
ariaLabel: label,
|
|
||||||
hasPopup: true,
|
|
||||||
disabled:
|
|
||||||
useSelectedNode.getState().isQueryCopilotCollectionSelected() ||
|
|
||||||
selectedNodeState.isDatabaseNodeOrNoneSelected(),
|
|
||||||
};
|
|
||||||
buttons.push(newStoredProcedureBtn);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shouldEnableScriptsCommands) {
|
|
||||||
const label = "New UDF";
|
|
||||||
const newUserDefinedFunctionBtn: CommandButtonComponentProps = {
|
|
||||||
iconSrc: AddUdfIcon,
|
|
||||||
iconAlt: label,
|
|
||||||
onCommandClick: () => {
|
|
||||||
const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection();
|
|
||||||
selectedCollection && selectedCollection.onNewUserDefinedFunctionClick(selectedCollection);
|
|
||||||
},
|
|
||||||
commandButtonLabel: label,
|
|
||||||
ariaLabel: label,
|
|
||||||
hasPopup: true,
|
|
||||||
disabled:
|
|
||||||
useSelectedNode.getState().isQueryCopilotCollectionSelected() ||
|
|
||||||
selectedNodeState.isDatabaseNodeOrNoneSelected(),
|
|
||||||
};
|
|
||||||
buttons.push(newUserDefinedFunctionBtn);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shouldEnableScriptsCommands) {
|
|
||||||
const label = "New Trigger";
|
|
||||||
const newTriggerBtn: CommandButtonComponentProps = {
|
|
||||||
iconSrc: AddTriggerIcon,
|
|
||||||
iconAlt: label,
|
|
||||||
onCommandClick: () => {
|
|
||||||
const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection();
|
|
||||||
selectedCollection && selectedCollection.onNewTriggerClick(selectedCollection);
|
|
||||||
},
|
|
||||||
commandButtonLabel: label,
|
|
||||||
ariaLabel: label,
|
|
||||||
hasPopup: true,
|
|
||||||
disabled:
|
|
||||||
useSelectedNode.getState().isQueryCopilotCollectionSelected() ||
|
|
||||||
selectedNodeState.isDatabaseNodeOrNoneSelected(),
|
|
||||||
};
|
|
||||||
buttons.push(newTriggerBtn);
|
|
||||||
}
|
|
||||||
|
|
||||||
return buttons;
|
|
||||||
}
|
|
||||||
|
|
||||||
function applyNotebooksTemporarilyDownStyle(buttonProps: CommandButtonComponentProps, tooltip: string): void {
|
|
||||||
if (!buttonProps.isDivider) {
|
|
||||||
buttonProps.disabled = true;
|
|
||||||
buttonProps.tooltipText = tooltip;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function createNewNotebookButton(container: Explorer): CommandButtonComponentProps {
|
|
||||||
const label = "New Notebook";
|
|
||||||
return {
|
|
||||||
id: "newNotebookBtn",
|
|
||||||
iconSrc: NewNotebookIcon,
|
|
||||||
iconAlt: label,
|
|
||||||
onCommandClick: () => container.onNewNotebookClicked(),
|
|
||||||
commandButtonLabel: label,
|
|
||||||
hasPopup: false,
|
|
||||||
disabled: useSelectedNode.getState().isQueryCopilotCollectionSelected(),
|
|
||||||
ariaLabel: label,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function createuploadNotebookButton(container: Explorer): CommandButtonComponentProps {
|
|
||||||
const label = "Upload to Notebook Server";
|
|
||||||
return {
|
|
||||||
iconSrc: NewNotebookIcon,
|
|
||||||
iconAlt: label,
|
|
||||||
onCommandClick: () => container.openUploadFilePanel(),
|
|
||||||
commandButtonLabel: label,
|
|
||||||
hasPopup: false,
|
|
||||||
disabled: useSelectedNode.getState().isQueryCopilotCollectionSelected(),
|
|
||||||
ariaLabel: label,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function createOpenQueryButton(container: Explorer): CommandButtonComponentProps {
|
|
||||||
const label = "Open Query";
|
|
||||||
return {
|
|
||||||
iconSrc: BrowseQueriesIcon,
|
|
||||||
iconAlt: label,
|
|
||||||
onCommandClick: () =>
|
|
||||||
useSidePanel.getState().openSidePanel("Open Saved Queries", <BrowseQueriesPane explorer={container} />),
|
|
||||||
commandButtonLabel: label,
|
|
||||||
ariaLabel: label,
|
|
||||||
hasPopup: true,
|
|
||||||
disabled: useSelectedNode.getState().isQueryCopilotCollectionSelected(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function createOpenQueryFromDiskButton(): CommandButtonComponentProps {
|
|
||||||
const label = "Open Query From Disk";
|
|
||||||
return {
|
|
||||||
iconSrc: OpenQueryFromDiskIcon,
|
|
||||||
iconAlt: label,
|
|
||||||
onCommandClick: () => useSidePanel.getState().openSidePanel("Load Query", <LoadQueryPane />),
|
|
||||||
commandButtonLabel: label,
|
|
||||||
ariaLabel: label,
|
|
||||||
hasPopup: true,
|
|
||||||
disabled: useSelectedNode.getState().isQueryCopilotCollectionSelected(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function createOpenTerminalButton(container: Explorer): CommandButtonComponentProps {
|
|
||||||
const label = "Open Terminal";
|
|
||||||
return {
|
|
||||||
iconSrc: CosmosTerminalIcon,
|
|
||||||
iconAlt: label,
|
|
||||||
onCommandClick: () => container.openNotebookTerminal(ViewModels.TerminalKind.Default),
|
|
||||||
commandButtonLabel: label,
|
|
||||||
hasPopup: false,
|
|
||||||
disabled: useSelectedNode.getState().isQueryCopilotCollectionSelected(),
|
|
||||||
ariaLabel: label,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function createOpenMongoTerminalButton(container: Explorer): CommandButtonComponentProps {
|
|
||||||
const label = "Open Mongo Shell";
|
|
||||||
const tooltip =
|
|
||||||
"This feature is not yet available in your account's region. View supported regions here: https://aka.ms/cosmos-enable-notebooks.";
|
|
||||||
const disableButton =
|
|
||||||
!useNotebook.getState().isNotebooksEnabledForAccount && !useNotebook.getState().isNotebookEnabled;
|
|
||||||
return {
|
|
||||||
iconSrc: HostedTerminalIcon,
|
|
||||||
iconAlt: label,
|
|
||||||
onCommandClick: () => {
|
|
||||||
if (useNotebook.getState().isNotebookEnabled) {
|
|
||||||
container.openNotebookTerminal(ViewModels.TerminalKind.Mongo);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
commandButtonLabel: label,
|
|
||||||
hasPopup: false,
|
|
||||||
disabled: disableButton,
|
|
||||||
ariaLabel: label,
|
|
||||||
tooltipText: !disableButton ? "" : tooltip,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function createOpenCassandraTerminalButton(container: Explorer): CommandButtonComponentProps {
|
|
||||||
const label = "Open Cassandra Shell";
|
|
||||||
const tooltip =
|
|
||||||
"This feature is not yet available in your account's region. View supported regions here: https://aka.ms/cosmos-enable-notebooks.";
|
|
||||||
const disableButton =
|
|
||||||
!useNotebook.getState().isNotebooksEnabledForAccount && !useNotebook.getState().isNotebookEnabled;
|
|
||||||
return {
|
|
||||||
iconSrc: HostedTerminalIcon,
|
|
||||||
iconAlt: label,
|
|
||||||
onCommandClick: () => {
|
|
||||||
if (useNotebook.getState().isNotebookEnabled) {
|
|
||||||
container.openNotebookTerminal(ViewModels.TerminalKind.Cassandra);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
commandButtonLabel: label,
|
|
||||||
hasPopup: false,
|
|
||||||
disabled: disableButton,
|
|
||||||
ariaLabel: label,
|
|
||||||
tooltipText: !disableButton ? "" : tooltip,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function createOpenPsqlTerminalButton(container: Explorer): CommandButtonComponentProps {
|
|
||||||
const label = "Open PSQL Shell";
|
|
||||||
const disableButton =
|
|
||||||
(!useNotebook.getState().isNotebooksEnabledForAccount && !useNotebook.getState().isNotebookEnabled) ||
|
|
||||||
useSelectedNode.getState().isQueryCopilotCollectionSelected();
|
|
||||||
return {
|
|
||||||
iconSrc: HostedTerminalIcon,
|
|
||||||
iconAlt: label,
|
|
||||||
onCommandClick: () => {
|
|
||||||
if (useNotebook.getState().isNotebookEnabled) {
|
|
||||||
container.openNotebookTerminal(ViewModels.TerminalKind.Postgres);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
commandButtonLabel: label,
|
|
||||||
hasPopup: false,
|
|
||||||
disabled: disableButton,
|
|
||||||
ariaLabel: label,
|
|
||||||
tooltipText: !disableButton
|
|
||||||
? ""
|
|
||||||
: "This feature is not yet available in your account's region. View supported regions here: https://aka.ms/cosmos-enable-notebooks.",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function createNotebookWorkspaceResetButton(container: Explorer): CommandButtonComponentProps {
|
|
||||||
const label = "Reset Workspace";
|
|
||||||
return {
|
|
||||||
iconSrc: ResetWorkspaceIcon,
|
|
||||||
iconAlt: label,
|
|
||||||
onCommandClick: () => container.resetNotebookWorkspace(),
|
|
||||||
commandButtonLabel: label,
|
|
||||||
hasPopup: false,
|
|
||||||
disabled: useSelectedNode.getState().isQueryCopilotCollectionSelected(),
|
|
||||||
ariaLabel: label,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function createManageGitHubAccountButton(container: Explorer): CommandButtonComponentProps {
|
|
||||||
const connectedToGitHub: boolean = container.notebookManager?.gitHubOAuthService.isLoggedIn();
|
|
||||||
const label = connectedToGitHub ? "Manage GitHub settings" : "Connect to GitHub";
|
|
||||||
const junoClient = new JunoClient();
|
|
||||||
return {
|
|
||||||
iconSrc: GitHubIcon,
|
|
||||||
iconAlt: label,
|
|
||||||
onCommandClick: () => {
|
|
||||||
useSidePanel
|
|
||||||
.getState()
|
|
||||||
.openSidePanel(
|
|
||||||
label,
|
|
||||||
<GitHubReposPanel
|
|
||||||
explorer={container}
|
|
||||||
gitHubClientProp={container.notebookManager.gitHubClient}
|
|
||||||
junoClientProp={junoClient}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
commandButtonLabel: label,
|
|
||||||
hasPopup: false,
|
|
||||||
disabled: useSelectedNode.getState().isQueryCopilotCollectionSelected(),
|
|
||||||
ariaLabel: label,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function createStaticCommandBarButtonsForResourceToken(
|
|
||||||
container: Explorer,
|
|
||||||
selectedNodeState: SelectedNodeState
|
|
||||||
): CommandButtonComponentProps[] {
|
|
||||||
const newSqlQueryBtn = createNewSQLQueryButton(selectedNodeState);
|
|
||||||
const openQueryBtn = createOpenQueryButton(container);
|
|
||||||
|
|
||||||
const resourceTokenCollection: ViewModels.CollectionBase = useDatabases.getState().resourceTokenCollection;
|
|
||||||
const isResourceTokenCollectionNodeSelected: boolean =
|
|
||||||
resourceTokenCollection?.id() === selectedNodeState.selectedNode?.id();
|
|
||||||
newSqlQueryBtn.disabled = !isResourceTokenCollectionNodeSelected;
|
|
||||||
newSqlQueryBtn.onCommandClick = () => {
|
|
||||||
const resourceTokenCollection: ViewModels.CollectionBase = useDatabases.getState().resourceTokenCollection;
|
|
||||||
resourceTokenCollection && resourceTokenCollection.onNewQueryClick(resourceTokenCollection, undefined);
|
|
||||||
};
|
|
||||||
|
|
||||||
openQueryBtn.disabled = !isResourceTokenCollectionNodeSelected;
|
|
||||||
if (!openQueryBtn.disabled) {
|
|
||||||
openQueryBtn.children = [createOpenQueryButton(container), createOpenQueryFromDiskButton()];
|
|
||||||
}
|
|
||||||
|
|
||||||
return [newSqlQueryBtn, openQueryBtn];
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createPostgreButtons(container: Explorer): CommandButtonComponentProps[] {
|
|
||||||
const openPostgreShellBtn = createOpenPsqlTerminalButton(container);
|
|
||||||
|
|
||||||
return [openPostgreShellBtn];
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import { CommandButtonComponentProps } from "../../../Controls/CommandButton/CommandButtonComponent";
|
||||||
|
|
||||||
|
export function applyNotebooksTemporarilyDownStyle(buttonProps: CommandButtonComponentProps, tooltip: string): void {
|
||||||
|
if (!buttonProps.isDivider) {
|
||||||
|
buttonProps.disabled = true;
|
||||||
|
buttonProps.tooltipText = tooltip;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { userContext } from "../../../../UserContext";
|
||||||
|
|
||||||
|
export function areScriptsSupported(): boolean {
|
||||||
|
return userContext.apiType === "SQL" || userContext.apiType === "Gremlin";
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import * as ViewModels from "../../../../Contracts/ViewModels";
|
||||||
|
import { userContext } from "../../../../UserContext";
|
||||||
|
import HostedTerminalIcon from "../../../../images/Hosted-Terminal.svg";
|
||||||
|
import { CommandButtonComponentProps } from "../../../Controls/CommandButton/CommandButtonComponent";
|
||||||
|
import Explorer from "../../../Explorer";
|
||||||
|
import { useNotebook } from "../../../Notebook/useNotebook";
|
||||||
|
import { SelectedNodeState } from "../../../useSelectedNode";
|
||||||
|
|
||||||
|
export function createContextCommandBarButtons(
|
||||||
|
container: Explorer,
|
||||||
|
selectedNodeState: SelectedNodeState
|
||||||
|
): CommandButtonComponentProps[] {
|
||||||
|
if (selectedNodeState.isDatabaseNodeOrNoneSelected()) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userContext.apiType !== "Mongo") {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const label = useNotebook.getState().isShellEnabled ? "Open Mongo Shell" : "New Shell";
|
||||||
|
|
||||||
|
const newMongoShellBtn: CommandButtonComponentProps = {
|
||||||
|
iconSrc: HostedTerminalIcon,
|
||||||
|
iconAlt: label,
|
||||||
|
onCommandClick: () => {
|
||||||
|
const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection();
|
||||||
|
if (useNotebook.getState().isShellEnabled) {
|
||||||
|
container.openNotebookTerminal(ViewModels.TerminalKind.Mongo);
|
||||||
|
} else {
|
||||||
|
selectedCollection && selectedCollection.onNewMongoShellClick();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
commandButtonLabel: label,
|
||||||
|
ariaLabel: label,
|
||||||
|
hasPopup: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
return [newMongoShellBtn];
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import { Platform, configContext } from "../../../../ConfigContext";
|
||||||
|
import { userContext } from "../../../../UserContext";
|
||||||
|
import { isRunningOnNationalCloud } from "../../../../Utils/CloudUtils";
|
||||||
|
import { useSidePanel } from "../../../../hooks/useSidePanel";
|
||||||
|
import FeedbackIcon from "../../../../images/Feedback-Command.svg";
|
||||||
|
import OpenInTabIcon from "../../../../images/open-in-tab.svg";
|
||||||
|
import SettingsIcon from "../../../../images/settings_15x15.svg";
|
||||||
|
import { CommandButtonComponentProps } from "../../../Controls/CommandButton/CommandButtonComponent";
|
||||||
|
import Explorer from "../../../Explorer";
|
||||||
|
import { OpenFullScreen } from "../../../OpenFullScreen";
|
||||||
|
import { SettingsPane } from "../../../Panes/SettingsPane/SettingsPane";
|
||||||
|
|
||||||
|
export function createControlCommandBarButtons(container: Explorer): CommandButtonComponentProps[] {
|
||||||
|
const buttons: CommandButtonComponentProps[] = [
|
||||||
|
{
|
||||||
|
iconSrc: SettingsIcon,
|
||||||
|
iconAlt: "Settings",
|
||||||
|
onCommandClick: () => useSidePanel.getState().openSidePanel("Settings", <SettingsPane />),
|
||||||
|
commandButtonLabel: undefined,
|
||||||
|
ariaLabel: "Settings",
|
||||||
|
tooltipText: "Settings",
|
||||||
|
hasPopup: true,
|
||||||
|
disabled: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const showOpenFullScreen =
|
||||||
|
configContext.platform === Platform.Portal && !isRunningOnNationalCloud() && userContext.apiType !== "Gremlin";
|
||||||
|
|
||||||
|
if (showOpenFullScreen) {
|
||||||
|
const label = "Open Full Screen";
|
||||||
|
const fullScreenButton: CommandButtonComponentProps = {
|
||||||
|
id: "openFullScreenBtn",
|
||||||
|
iconSrc: OpenInTabIcon,
|
||||||
|
iconAlt: label,
|
||||||
|
onCommandClick: () => {
|
||||||
|
useSidePanel.getState().openSidePanel("Open Full Screen", <OpenFullScreen />);
|
||||||
|
},
|
||||||
|
commandButtonLabel: undefined,
|
||||||
|
ariaLabel: label,
|
||||||
|
tooltipText: label,
|
||||||
|
hasPopup: false,
|
||||||
|
disabled: !showOpenFullScreen,
|
||||||
|
className: "OpenFullScreen",
|
||||||
|
};
|
||||||
|
buttons.push(fullScreenButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (configContext.platform !== Platform.Emulator) {
|
||||||
|
const label = "Feedback";
|
||||||
|
const feedbackButtonOptions: CommandButtonComponentProps = {
|
||||||
|
iconSrc: FeedbackIcon,
|
||||||
|
iconAlt: label,
|
||||||
|
onCommandClick: () => container.provideFeedbackEmail(),
|
||||||
|
commandButtonLabel: undefined,
|
||||||
|
ariaLabel: label,
|
||||||
|
tooltipText: label,
|
||||||
|
hasPopup: false,
|
||||||
|
disabled: false,
|
||||||
|
};
|
||||||
|
buttons.push(feedbackButtonOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
return buttons;
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { CommandButtonComponentProps } from "../../../Controls/CommandButton/CommandButtonComponent";
|
||||||
|
|
||||||
|
let counter = 0;
|
||||||
|
export function createDivider(): CommandButtonComponentProps {
|
||||||
|
const label = `divider${counter++}`;
|
||||||
|
return {
|
||||||
|
isDivider: true,
|
||||||
|
commandButtonLabel: label,
|
||||||
|
hasPopup: false,
|
||||||
|
iconSrc: undefined,
|
||||||
|
iconAlt: undefined,
|
||||||
|
onCommandClick: undefined,
|
||||||
|
ariaLabel: label,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import { JunoClient } from "../../../../Juno/JunoClient";
|
||||||
|
import { useSidePanel } from "../../../../hooks/useSidePanel";
|
||||||
|
import GitHubIcon from "../../../../images/github.svg";
|
||||||
|
import { CommandButtonComponentProps } from "../../../Controls/CommandButton/CommandButtonComponent";
|
||||||
|
import Explorer from "../../../Explorer";
|
||||||
|
import { GitHubReposPanel } from "../../../Panes/GitHubReposPanel/GitHubReposPanel";
|
||||||
|
import { useSelectedNode } from "../../../useSelectedNode";
|
||||||
|
|
||||||
|
export function createManageGitHubAccountButton(container: Explorer): CommandButtonComponentProps {
|
||||||
|
const connectedToGitHub: boolean = container.notebookManager?.gitHubOAuthService.isLoggedIn();
|
||||||
|
const label = connectedToGitHub ? "Manage GitHub settings" : "Connect to GitHub";
|
||||||
|
const junoClient = new JunoClient();
|
||||||
|
return {
|
||||||
|
iconSrc: GitHubIcon,
|
||||||
|
iconAlt: label,
|
||||||
|
onCommandClick: () => {
|
||||||
|
useSidePanel
|
||||||
|
.getState()
|
||||||
|
.openSidePanel(
|
||||||
|
label,
|
||||||
|
<GitHubReposPanel
|
||||||
|
explorer={container}
|
||||||
|
gitHubClientProp={container.notebookManager.gitHubClient}
|
||||||
|
junoClientProp={junoClient}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
commandButtonLabel: label,
|
||||||
|
hasPopup: false,
|
||||||
|
disabled: useSelectedNode.getState().isQueryCopilotCollectionSelected(),
|
||||||
|
ariaLabel: label,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { getCollectionName } from "../../../../Utils/APITypeUtils";
|
||||||
|
import AddCollectionIcon from "../../../../images/AddCollection.svg";
|
||||||
|
import { CommandButtonComponentProps } from "../../../Controls/CommandButton/CommandButtonComponent";
|
||||||
|
import Explorer from "../../../Explorer";
|
||||||
|
|
||||||
|
export function createNewCollectionGroup(container: Explorer): CommandButtonComponentProps {
|
||||||
|
const label = `New ${getCollectionName()}`;
|
||||||
|
return {
|
||||||
|
iconSrc: AddCollectionIcon,
|
||||||
|
iconAlt: label,
|
||||||
|
onCommandClick: () => container.onNewCollectionClicked(),
|
||||||
|
commandButtonLabel: label,
|
||||||
|
ariaLabel: label,
|
||||||
|
hasPopup: true,
|
||||||
|
id: "createNewContainerCommandButton",
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import { userContext } from "../../../../UserContext";
|
||||||
|
import { getDatabaseName } from "../../../../Utils/APITypeUtils";
|
||||||
|
import { useSidePanel } from "../../../../hooks/useSidePanel";
|
||||||
|
import AddDatabaseIcon from "../../../../images/AddDatabase.svg";
|
||||||
|
import { CommandButtonComponentProps } from "../../../Controls/CommandButton/CommandButtonComponent";
|
||||||
|
import Explorer from "../../../Explorer";
|
||||||
|
import { AddDatabasePanel } from "../../../Panes/AddDatabasePanel/AddDatabasePanel";
|
||||||
|
import { useDatabases } from "../../../useDatabases";
|
||||||
|
|
||||||
|
export function createNewDatabase(container: Explorer): CommandButtonComponentProps {
|
||||||
|
const label = "New " + getDatabaseName();
|
||||||
|
return {
|
||||||
|
iconSrc: AddDatabaseIcon,
|
||||||
|
iconAlt: label,
|
||||||
|
onCommandClick: async () => {
|
||||||
|
const throughputCap = userContext.databaseAccount?.properties.capacity?.totalThroughputLimit;
|
||||||
|
if (throughputCap && throughputCap !== -1) {
|
||||||
|
await useDatabases.getState().loadAllOffers();
|
||||||
|
}
|
||||||
|
useSidePanel.getState().openSidePanel("New " + getDatabaseName(), <AddDatabasePanel explorer={container} />);
|
||||||
|
},
|
||||||
|
commandButtonLabel: label,
|
||||||
|
ariaLabel: label,
|
||||||
|
hasPopup: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import NewNotebookIcon from "../../../../images/notebook/Notebook-new.svg";
|
||||||
|
import { CommandButtonComponentProps } from "../../../Controls/CommandButton/CommandButtonComponent";
|
||||||
|
import Explorer from "../../../Explorer";
|
||||||
|
import { useSelectedNode } from "../../../useSelectedNode";
|
||||||
|
|
||||||
|
export function createNewNotebookButton(container: Explorer): CommandButtonComponentProps {
|
||||||
|
const label = "New Notebook";
|
||||||
|
return {
|
||||||
|
id: "newNotebookBtn",
|
||||||
|
iconSrc: NewNotebookIcon,
|
||||||
|
iconAlt: label,
|
||||||
|
onCommandClick: () => container.onNewNotebookClicked(),
|
||||||
|
commandButtonLabel: label,
|
||||||
|
hasPopup: false,
|
||||||
|
disabled: useSelectedNode.getState().isQueryCopilotCollectionSelected(),
|
||||||
|
ariaLabel: label,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
import { Action } from "Shared/Telemetry/TelemetryConstants";
|
||||||
|
import { traceOpen } from "Shared/Telemetry/TelemetryProcessor";
|
||||||
|
import { ReactTabKind, useTabs } from "hooks/useTabs";
|
||||||
|
import * as ViewModels from "../../../../Contracts/ViewModels";
|
||||||
|
import { userContext } from "../../../../UserContext";
|
||||||
|
import AddSqlQueryIcon from "../../../../images/AddSqlQuery_16x16.svg";
|
||||||
|
import { CommandButtonComponentProps } from "../../../Controls/CommandButton/CommandButtonComponent";
|
||||||
|
import { SelectedNodeState, useSelectedNode } from "../../../useSelectedNode";
|
||||||
|
|
||||||
|
export function createNewSQLQueryButton(selectedNodeState: SelectedNodeState): CommandButtonComponentProps {
|
||||||
|
if (userContext.apiType === "SQL" || userContext.apiType === "Gremlin") {
|
||||||
|
const label = "New SQL Query";
|
||||||
|
return {
|
||||||
|
id: "newQueryBtn",
|
||||||
|
iconSrc: AddSqlQueryIcon,
|
||||||
|
iconAlt: label,
|
||||||
|
onCommandClick: () => {
|
||||||
|
if (useSelectedNode.getState().isQueryCopilotCollectionSelected()) {
|
||||||
|
useTabs.getState().openAndActivateReactTab(ReactTabKind.QueryCopilot);
|
||||||
|
traceOpen(Action.OpenQueryCopilotFromNewQuery, { apiType: userContext.apiType });
|
||||||
|
} else {
|
||||||
|
const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection();
|
||||||
|
selectedCollection && selectedCollection.onNewQueryClick(selectedCollection);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
commandButtonLabel: label,
|
||||||
|
ariaLabel: label,
|
||||||
|
hasPopup: true,
|
||||||
|
disabled: selectedNodeState.isDatabaseNodeOrNoneSelected(),
|
||||||
|
};
|
||||||
|
} else if (userContext.apiType === "Mongo") {
|
||||||
|
const label = "New Query";
|
||||||
|
return {
|
||||||
|
id: "newQueryBtn",
|
||||||
|
iconSrc: AddSqlQueryIcon,
|
||||||
|
iconAlt: label,
|
||||||
|
onCommandClick: () => {
|
||||||
|
const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection();
|
||||||
|
selectedCollection && selectedCollection.onNewMongoQueryClick(selectedCollection);
|
||||||
|
},
|
||||||
|
commandButtonLabel: label,
|
||||||
|
ariaLabel: label,
|
||||||
|
hasPopup: true,
|
||||||
|
disabled: selectedNodeState.isDatabaseNodeOrNoneSelected(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import ResetWorkspaceIcon from "../../../../images/notebook/Notebook-reset-workspace.svg";
|
||||||
|
import { CommandButtonComponentProps } from "../../../Controls/CommandButton/CommandButtonComponent";
|
||||||
|
import Explorer from "../../../Explorer";
|
||||||
|
import { useSelectedNode } from "../../../useSelectedNode";
|
||||||
|
|
||||||
|
export function createNotebookWorkspaceResetButton(container: Explorer): CommandButtonComponentProps {
|
||||||
|
const label = "Reset Workspace";
|
||||||
|
return {
|
||||||
|
iconSrc: ResetWorkspaceIcon,
|
||||||
|
iconAlt: label,
|
||||||
|
onCommandClick: () => container.resetNotebookWorkspace(),
|
||||||
|
commandButtonLabel: label,
|
||||||
|
hasPopup: false,
|
||||||
|
disabled: useSelectedNode.getState().isQueryCopilotCollectionSelected(),
|
||||||
|
ariaLabel: label,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import * as ViewModels from "../../../../Contracts/ViewModels";
|
||||||
|
import HostedTerminalIcon from "../../../../images/Hosted-Terminal.svg";
|
||||||
|
import { CommandButtonComponentProps } from "../../../Controls/CommandButton/CommandButtonComponent";
|
||||||
|
import Explorer from "../../../Explorer";
|
||||||
|
import { useNotebook } from "../../../Notebook/useNotebook";
|
||||||
|
|
||||||
|
export function createOpenCassandraTerminalButton(container: Explorer): CommandButtonComponentProps {
|
||||||
|
const label = "Open Cassandra Shell";
|
||||||
|
const tooltip =
|
||||||
|
"This feature is not yet available in your account's region. View supported regions here: https://aka.ms/cosmos-enable-notebooks.";
|
||||||
|
const disableButton =
|
||||||
|
!useNotebook.getState().isNotebooksEnabledForAccount && !useNotebook.getState().isNotebookEnabled;
|
||||||
|
return {
|
||||||
|
iconSrc: HostedTerminalIcon,
|
||||||
|
iconAlt: label,
|
||||||
|
onCommandClick: () => {
|
||||||
|
if (useNotebook.getState().isNotebookEnabled) {
|
||||||
|
container.openNotebookTerminal(ViewModels.TerminalKind.Cassandra);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
commandButtonLabel: label,
|
||||||
|
hasPopup: false,
|
||||||
|
disabled: disableButton,
|
||||||
|
ariaLabel: label,
|
||||||
|
tooltipText: !disableButton ? "" : tooltip,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import * as ViewModels from "../../../../Contracts/ViewModels";
|
||||||
|
import HostedTerminalIcon from "../../../../images/Hosted-Terminal.svg";
|
||||||
|
import { CommandButtonComponentProps } from "../../../Controls/CommandButton/CommandButtonComponent";
|
||||||
|
import Explorer from "../../../Explorer";
|
||||||
|
import { useNotebook } from "../../../Notebook/useNotebook";
|
||||||
|
|
||||||
|
export function createOpenMongoTerminalButton(container: Explorer): CommandButtonComponentProps {
|
||||||
|
const label = "Open Mongo Shell";
|
||||||
|
const tooltip =
|
||||||
|
"This feature is not yet available in your account's region. View supported regions here: https://aka.ms/cosmos-enable-notebooks.";
|
||||||
|
const disableButton =
|
||||||
|
!useNotebook.getState().isNotebooksEnabledForAccount && !useNotebook.getState().isNotebookEnabled;
|
||||||
|
return {
|
||||||
|
iconSrc: HostedTerminalIcon,
|
||||||
|
iconAlt: label,
|
||||||
|
onCommandClick: () => {
|
||||||
|
if (useNotebook.getState().isNotebookEnabled) {
|
||||||
|
container.openNotebookTerminal(ViewModels.TerminalKind.Mongo);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
commandButtonLabel: label,
|
||||||
|
hasPopup: false,
|
||||||
|
disabled: disableButton,
|
||||||
|
ariaLabel: label,
|
||||||
|
tooltipText: !disableButton ? "" : tooltip,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import * as ViewModels from "../../../../Contracts/ViewModels";
|
||||||
|
import HostedTerminalIcon from "../../../../images/Hosted-Terminal.svg";
|
||||||
|
import { CommandButtonComponentProps } from "../../../Controls/CommandButton/CommandButtonComponent";
|
||||||
|
import Explorer from "../../../Explorer";
|
||||||
|
import { useNotebook } from "../../../Notebook/useNotebook";
|
||||||
|
import { useSelectedNode } from "../../../useSelectedNode";
|
||||||
|
|
||||||
|
export function createOpenPsqlTerminalButton(container: Explorer): CommandButtonComponentProps {
|
||||||
|
const label = "Open PSQL Shell";
|
||||||
|
const disableButton =
|
||||||
|
(!useNotebook.getState().isNotebooksEnabledForAccount && !useNotebook.getState().isNotebookEnabled) ||
|
||||||
|
useSelectedNode.getState().isQueryCopilotCollectionSelected();
|
||||||
|
return {
|
||||||
|
iconSrc: HostedTerminalIcon,
|
||||||
|
iconAlt: label,
|
||||||
|
onCommandClick: () => {
|
||||||
|
if (useNotebook.getState().isNotebookEnabled) {
|
||||||
|
container.openNotebookTerminal(ViewModels.TerminalKind.Postgres);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
commandButtonLabel: label,
|
||||||
|
hasPopup: false,
|
||||||
|
disabled: disableButton,
|
||||||
|
ariaLabel: label,
|
||||||
|
tooltipText: !disableButton
|
||||||
|
? ""
|
||||||
|
: "This feature is not yet available in your account's region. View supported regions here: https://aka.ms/cosmos-enable-notebooks.",
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import { useSidePanel } from "../../../../hooks/useSidePanel";
|
||||||
|
import BrowseQueriesIcon from "../../../../images/BrowseQuery.svg";
|
||||||
|
import { CommandButtonComponentProps } from "../../../Controls/CommandButton/CommandButtonComponent";
|
||||||
|
import Explorer from "../../../Explorer";
|
||||||
|
import { BrowseQueriesPane } from "../../../Panes/BrowseQueriesPane/BrowseQueriesPane";
|
||||||
|
import { useSelectedNode } from "../../../useSelectedNode";
|
||||||
|
|
||||||
|
export function createOpenQueryButton(container: Explorer): CommandButtonComponentProps {
|
||||||
|
const label = "Open Query";
|
||||||
|
return {
|
||||||
|
iconSrc: BrowseQueriesIcon,
|
||||||
|
iconAlt: label,
|
||||||
|
onCommandClick: () =>
|
||||||
|
useSidePanel.getState().openSidePanel("Open Saved Queries", <BrowseQueriesPane explorer={container} />),
|
||||||
|
commandButtonLabel: label,
|
||||||
|
ariaLabel: label,
|
||||||
|
hasPopup: true,
|
||||||
|
disabled: useSelectedNode.getState().isQueryCopilotCollectionSelected(),
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import { useSidePanel } from "../../../../hooks/useSidePanel";
|
||||||
|
import OpenQueryFromDiskIcon from "../../../../images/OpenQueryFromDisk.svg";
|
||||||
|
import { CommandButtonComponentProps } from "../../../Controls/CommandButton/CommandButtonComponent";
|
||||||
|
import { LoadQueryPane } from "../../../Panes/LoadQueryPane/LoadQueryPane";
|
||||||
|
import { useSelectedNode } from "../../../useSelectedNode";
|
||||||
|
|
||||||
|
export function createOpenQueryFromDiskButton(): CommandButtonComponentProps {
|
||||||
|
const label = "Open Query From Disk";
|
||||||
|
return {
|
||||||
|
iconSrc: OpenQueryFromDiskIcon,
|
||||||
|
iconAlt: label,
|
||||||
|
onCommandClick: () => useSidePanel.getState().openSidePanel("Load Query", <LoadQueryPane />),
|
||||||
|
commandButtonLabel: label,
|
||||||
|
ariaLabel: label,
|
||||||
|
hasPopup: true,
|
||||||
|
disabled: useSelectedNode.getState().isQueryCopilotCollectionSelected(),
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import * as Constants from "../../../../Common/Constants";
|
||||||
|
import { Platform, configContext } from "../../../../ConfigContext";
|
||||||
|
import { userContext } from "../../../../UserContext";
|
||||||
|
import SynapseIcon from "../../../../images/synapse-link.svg";
|
||||||
|
import { CommandButtonComponentProps } from "../../../Controls/CommandButton/CommandButtonComponent";
|
||||||
|
import Explorer from "../../../Explorer";
|
||||||
|
import { useNotebook } from "../../../Notebook/useNotebook";
|
||||||
|
import { useSelectedNode } from "../../../useSelectedNode";
|
||||||
|
|
||||||
|
export function createOpenSynapseLinkDialogButton(container: Explorer): CommandButtonComponentProps {
|
||||||
|
if (configContext.platform === Platform.Emulator) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userContext?.databaseAccount?.properties?.enableAnalyticalStorage) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const capabilities = userContext?.databaseAccount?.properties?.capabilities || [];
|
||||||
|
if (capabilities.some((capability) => capability.name === Constants.CapabilityNames.EnableStorageAnalytics)) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const label = "Enable Azure Synapse Link";
|
||||||
|
return {
|
||||||
|
iconSrc: SynapseIcon,
|
||||||
|
iconAlt: label,
|
||||||
|
onCommandClick: () => container.openEnableSynapseLinkDialog(),
|
||||||
|
commandButtonLabel: label,
|
||||||
|
hasPopup: false,
|
||||||
|
disabled:
|
||||||
|
useSelectedNode.getState().isQueryCopilotCollectionSelected() || useNotebook.getState().isSynapseLinkUpdating,
|
||||||
|
ariaLabel: label,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import * as ViewModels from "../../../../Contracts/ViewModels";
|
||||||
|
import CosmosTerminalIcon from "../../../../images/Cosmos-Terminal.svg";
|
||||||
|
import { CommandButtonComponentProps } from "../../../Controls/CommandButton/CommandButtonComponent";
|
||||||
|
import Explorer from "../../../Explorer";
|
||||||
|
import { useSelectedNode } from "../../../useSelectedNode";
|
||||||
|
|
||||||
|
export function createOpenTerminalButton(container: Explorer): CommandButtonComponentProps {
|
||||||
|
const label = "Open Terminal";
|
||||||
|
return {
|
||||||
|
iconSrc: CosmosTerminalIcon,
|
||||||
|
iconAlt: label,
|
||||||
|
onCommandClick: () => container.openNotebookTerminal(ViewModels.TerminalKind.Default),
|
||||||
|
commandButtonLabel: label,
|
||||||
|
hasPopup: false,
|
||||||
|
disabled: useSelectedNode.getState().isQueryCopilotCollectionSelected(),
|
||||||
|
ariaLabel: label,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { CommandButtonComponentProps } from "../../../Controls/CommandButton/CommandButtonComponent";
|
||||||
|
import Explorer from "../../../Explorer";
|
||||||
|
import { createOpenPsqlTerminalButton } from "./createOpenPsqlTerminalButton";
|
||||||
|
|
||||||
|
export function createPostgreButtons(container: Explorer): CommandButtonComponentProps[] {
|
||||||
|
const openPostgreShellBtn = createOpenPsqlTerminalButton(container);
|
||||||
|
|
||||||
|
return [openPostgreShellBtn];
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
import { CommandButtonComponentProps } from "Explorer/Controls/CommandButton/CommandButtonComponent";
|
||||||
|
import { SelectedNodeState, useSelectedNode } from "Explorer/useSelectedNode";
|
||||||
|
import * as ViewModels from "../../../../Contracts/ViewModels";
|
||||||
|
import AddStoredProcedureIcon from "../../../../images/AddStoredProcedure.svg";
|
||||||
|
import AddTriggerIcon from "../../../../images/AddTrigger.svg";
|
||||||
|
import AddUdfIcon from "../../../../images/AddUdf.svg";
|
||||||
|
import { areScriptsSupported } from "./areScriptsSupported";
|
||||||
|
|
||||||
|
export function createScriptCommandButtons(selectedNodeState: SelectedNodeState): CommandButtonComponentProps[] {
|
||||||
|
const buttons: CommandButtonComponentProps[] = [];
|
||||||
|
|
||||||
|
const shouldEnableScriptsCommands: boolean =
|
||||||
|
!selectedNodeState.isDatabaseNodeOrNoneSelected() && areScriptsSupported();
|
||||||
|
|
||||||
|
if (shouldEnableScriptsCommands) {
|
||||||
|
const label = "New Stored Procedure";
|
||||||
|
const newStoredProcedureBtn: CommandButtonComponentProps = {
|
||||||
|
iconSrc: AddStoredProcedureIcon,
|
||||||
|
iconAlt: label,
|
||||||
|
onCommandClick: () => {
|
||||||
|
const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection();
|
||||||
|
selectedCollection && selectedCollection.onNewStoredProcedureClick(selectedCollection);
|
||||||
|
},
|
||||||
|
commandButtonLabel: label,
|
||||||
|
ariaLabel: label,
|
||||||
|
hasPopup: true,
|
||||||
|
disabled:
|
||||||
|
useSelectedNode.getState().isQueryCopilotCollectionSelected() ||
|
||||||
|
selectedNodeState.isDatabaseNodeOrNoneSelected(),
|
||||||
|
};
|
||||||
|
buttons.push(newStoredProcedureBtn);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldEnableScriptsCommands) {
|
||||||
|
const label = "New UDF";
|
||||||
|
const newUserDefinedFunctionBtn: CommandButtonComponentProps = {
|
||||||
|
iconSrc: AddUdfIcon,
|
||||||
|
iconAlt: label,
|
||||||
|
onCommandClick: () => {
|
||||||
|
const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection();
|
||||||
|
selectedCollection && selectedCollection.onNewUserDefinedFunctionClick(selectedCollection);
|
||||||
|
},
|
||||||
|
commandButtonLabel: label,
|
||||||
|
ariaLabel: label,
|
||||||
|
hasPopup: true,
|
||||||
|
disabled:
|
||||||
|
useSelectedNode.getState().isQueryCopilotCollectionSelected() ||
|
||||||
|
selectedNodeState.isDatabaseNodeOrNoneSelected(),
|
||||||
|
};
|
||||||
|
buttons.push(newUserDefinedFunctionBtn);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldEnableScriptsCommands) {
|
||||||
|
const label = "New Trigger";
|
||||||
|
const newTriggerBtn: CommandButtonComponentProps = {
|
||||||
|
iconSrc: AddTriggerIcon,
|
||||||
|
iconAlt: label,
|
||||||
|
onCommandClick: () => {
|
||||||
|
const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection();
|
||||||
|
selectedCollection && selectedCollection.onNewTriggerClick(selectedCollection);
|
||||||
|
},
|
||||||
|
commandButtonLabel: label,
|
||||||
|
ariaLabel: label,
|
||||||
|
hasPopup: true,
|
||||||
|
disabled:
|
||||||
|
useSelectedNode.getState().isQueryCopilotCollectionSelected() ||
|
||||||
|
selectedNodeState.isDatabaseNodeOrNoneSelected(),
|
||||||
|
};
|
||||||
|
buttons.push(newTriggerBtn);
|
||||||
|
}
|
||||||
|
|
||||||
|
return buttons;
|
||||||
|
}
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
import { createScriptCommandButtons } from "Explorer/Menus/CommandBar/CommandButtonsFactories/createScriptCommandButtons";
|
||||||
|
import * as Constants from "../../../../Common/Constants";
|
||||||
|
import { configContext } from "../../../../ConfigContext";
|
||||||
|
import * as ViewModels from "../../../../Contracts/ViewModels";
|
||||||
|
import { userContext } from "../../../../UserContext";
|
||||||
|
import AddStoredProcedureIcon from "../../../../images/AddStoredProcedure.svg";
|
||||||
|
import { CommandButtonComponentProps } from "../../../Controls/CommandButton/CommandButtonComponent";
|
||||||
|
import Explorer from "../../../Explorer";
|
||||||
|
import { useNotebook } from "../../../Notebook/useNotebook";
|
||||||
|
import { SelectedNodeState, useSelectedNode } from "../../../useSelectedNode";
|
||||||
|
import { applyNotebooksTemporarilyDownStyle } from "./applyNotebooksTemporarilyDownStyle";
|
||||||
|
import { areScriptsSupported } from "./areScriptsSupported";
|
||||||
|
import { createDivider } from "./createDivider";
|
||||||
|
import { createManageGitHubAccountButton } from "./createManageGitHubAccountButton";
|
||||||
|
import { createNewCollectionGroup } from "./createNewCollectionGroup";
|
||||||
|
import { createNewDatabase } from "./createNewDatabase";
|
||||||
|
import { createNewNotebookButton } from "./createNewNotebookButton";
|
||||||
|
import { createNewSQLQueryButton } from "./createNewSQLQueryButton";
|
||||||
|
import { createNotebookWorkspaceResetButton } from "./createNotebookWorkspaceResetButton";
|
||||||
|
import { createOpenCassandraTerminalButton } from "./createOpenCassandraTerminalButton";
|
||||||
|
import { createOpenMongoTerminalButton } from "./createOpenMongoTerminalButton";
|
||||||
|
import { createOpenQueryButton } from "./createOpenQueryButton";
|
||||||
|
import { createOpenQueryFromDiskButton } from "./createOpenQueryFromDiskButton";
|
||||||
|
import { createOpenSynapseLinkDialogButton } from "./createOpenSynapseLinkDialogButton";
|
||||||
|
import { createOpenTerminalButton } from "./createOpenTerminalButton";
|
||||||
|
import { createuploadNotebookButton } from "./createuploadNotebookButton";
|
||||||
|
|
||||||
|
export function createStaticCommandBarButtons(
|
||||||
|
container: Explorer,
|
||||||
|
selectedNodeState: SelectedNodeState
|
||||||
|
): CommandButtonComponentProps[] {
|
||||||
|
const newCollectionBtn = createNewCollectionGroup(container);
|
||||||
|
const buttons: CommandButtonComponentProps[] = [];
|
||||||
|
|
||||||
|
buttons.push(newCollectionBtn);
|
||||||
|
if (userContext.apiType !== "Tables" && userContext.apiType !== "Cassandra") {
|
||||||
|
const addSynapseLink = createOpenSynapseLinkDialogButton(container);
|
||||||
|
|
||||||
|
if (addSynapseLink) {
|
||||||
|
buttons.push(createDivider());
|
||||||
|
buttons.push(addSynapseLink);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userContext.apiType !== "Tables") {
|
||||||
|
newCollectionBtn.children = [createNewCollectionGroup(container)];
|
||||||
|
const newDatabaseBtn = createNewDatabase(container);
|
||||||
|
newCollectionBtn.children.push(newDatabaseBtn);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (useNotebook.getState().isNotebookEnabled) {
|
||||||
|
buttons.push(createDivider());
|
||||||
|
const notebookButtons: CommandButtonComponentProps[] = [];
|
||||||
|
|
||||||
|
const newNotebookButton = createNewNotebookButton(container);
|
||||||
|
newNotebookButton.children = [createNewNotebookButton(container), createuploadNotebookButton(container)];
|
||||||
|
notebookButtons.push(newNotebookButton);
|
||||||
|
|
||||||
|
if (container.notebookManager?.gitHubOAuthService) {
|
||||||
|
notebookButtons.push(createManageGitHubAccountButton(container));
|
||||||
|
}
|
||||||
|
if (useNotebook.getState().isPhoenixFeatures && configContext.isTerminalEnabled) {
|
||||||
|
notebookButtons.push(createOpenTerminalButton(container));
|
||||||
|
}
|
||||||
|
if (useNotebook.getState().isPhoenixNotebooks && selectedNodeState.isConnectedToContainer()) {
|
||||||
|
notebookButtons.push(createNotebookWorkspaceResetButton(container));
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
(userContext.apiType === "Mongo" &&
|
||||||
|
useNotebook.getState().isShellEnabled &&
|
||||||
|
selectedNodeState.isDatabaseNodeOrNoneSelected()) ||
|
||||||
|
userContext.apiType === "Cassandra"
|
||||||
|
) {
|
||||||
|
notebookButtons.push(createDivider());
|
||||||
|
if (userContext.apiType === "Cassandra") {
|
||||||
|
notebookButtons.push(createOpenCassandraTerminalButton(container));
|
||||||
|
} else {
|
||||||
|
notebookButtons.push(createOpenMongoTerminalButton(container));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
notebookButtons.forEach((btn) => {
|
||||||
|
if (btn.commandButtonLabel.indexOf("Cassandra") !== -1) {
|
||||||
|
if (!useNotebook.getState().isPhoenixFeatures) {
|
||||||
|
applyNotebooksTemporarilyDownStyle(btn, Constants.Notebook.cassandraShellTemporarilyDownMsg);
|
||||||
|
}
|
||||||
|
} else if (btn.commandButtonLabel.indexOf("Mongo") !== -1) {
|
||||||
|
if (!useNotebook.getState().isPhoenixFeatures) {
|
||||||
|
applyNotebooksTemporarilyDownStyle(btn, Constants.Notebook.mongoShellTemporarilyDownMsg);
|
||||||
|
}
|
||||||
|
} else if (btn.commandButtonLabel.indexOf("Open Terminal") !== -1) {
|
||||||
|
if (!useNotebook.getState().isPhoenixFeatures) {
|
||||||
|
applyNotebooksTemporarilyDownStyle(btn, Constants.Notebook.temporarilyDownMsg);
|
||||||
|
}
|
||||||
|
} else if (!useNotebook.getState().isPhoenixNotebooks) {
|
||||||
|
applyNotebooksTemporarilyDownStyle(btn, Constants.Notebook.temporarilyDownMsg);
|
||||||
|
}
|
||||||
|
buttons.push(btn);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!selectedNodeState.isDatabaseNodeOrNoneSelected()) {
|
||||||
|
const isQuerySupported = userContext.apiType === "SQL" || userContext.apiType === "Gremlin";
|
||||||
|
|
||||||
|
if (isQuerySupported) {
|
||||||
|
buttons.push(createDivider());
|
||||||
|
const newSqlQueryBtn = createNewSQLQueryButton(selectedNodeState);
|
||||||
|
buttons.push(newSqlQueryBtn);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isQuerySupported && selectedNodeState.findSelectedCollection()) {
|
||||||
|
const openQueryBtn = createOpenQueryButton(container);
|
||||||
|
openQueryBtn.children = [createOpenQueryButton(container), createOpenQueryFromDiskButton()];
|
||||||
|
buttons.push(openQueryBtn);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (areScriptsSupported()) {
|
||||||
|
const label = "New Stored Procedure";
|
||||||
|
const newStoredProcedureBtn: CommandButtonComponentProps = {
|
||||||
|
iconSrc: AddStoredProcedureIcon,
|
||||||
|
iconAlt: label,
|
||||||
|
onCommandClick: () => {
|
||||||
|
const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection();
|
||||||
|
selectedCollection && selectedCollection.onNewStoredProcedureClick(selectedCollection);
|
||||||
|
},
|
||||||
|
commandButtonLabel: label,
|
||||||
|
ariaLabel: label,
|
||||||
|
hasPopup: true,
|
||||||
|
disabled:
|
||||||
|
useSelectedNode.getState().isQueryCopilotCollectionSelected() ||
|
||||||
|
selectedNodeState.isDatabaseNodeOrNoneSelected(),
|
||||||
|
};
|
||||||
|
|
||||||
|
newStoredProcedureBtn.children = createScriptCommandButtons(selectedNodeState);
|
||||||
|
buttons.push(newStoredProcedureBtn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buttons;
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import * as ViewModels from "../../../../Contracts/ViewModels";
|
||||||
|
import { CommandButtonComponentProps } from "../../../Controls/CommandButton/CommandButtonComponent";
|
||||||
|
import Explorer from "../../../Explorer";
|
||||||
|
import { useDatabases } from "../../../useDatabases";
|
||||||
|
import { SelectedNodeState } from "../../../useSelectedNode";
|
||||||
|
import { createNewSQLQueryButton } from "./createNewSQLQueryButton";
|
||||||
|
import { createOpenQueryButton } from "./createOpenQueryButton";
|
||||||
|
import { createOpenQueryFromDiskButton } from "./createOpenQueryFromDiskButton";
|
||||||
|
|
||||||
|
export function createStaticCommandBarButtonsForResourceToken(
|
||||||
|
container: Explorer,
|
||||||
|
selectedNodeState: SelectedNodeState
|
||||||
|
): CommandButtonComponentProps[] {
|
||||||
|
const newSqlQueryBtn = createNewSQLQueryButton(selectedNodeState);
|
||||||
|
const openQueryBtn = createOpenQueryButton(container);
|
||||||
|
|
||||||
|
const resourceTokenCollection: ViewModels.CollectionBase = useDatabases.getState().resourceTokenCollection;
|
||||||
|
const isResourceTokenCollectionNodeSelected: boolean =
|
||||||
|
resourceTokenCollection?.id() === selectedNodeState.selectedNode?.id();
|
||||||
|
newSqlQueryBtn.disabled = !isResourceTokenCollectionNodeSelected;
|
||||||
|
newSqlQueryBtn.onCommandClick = () => {
|
||||||
|
const resourceTokenCollection: ViewModels.CollectionBase = useDatabases.getState().resourceTokenCollection;
|
||||||
|
resourceTokenCollection && resourceTokenCollection.onNewQueryClick(resourceTokenCollection, undefined);
|
||||||
|
};
|
||||||
|
|
||||||
|
openQueryBtn.disabled = !isResourceTokenCollectionNodeSelected;
|
||||||
|
if (!openQueryBtn.disabled) {
|
||||||
|
openQueryBtn.children = [createOpenQueryButton(container), createOpenQueryFromDiskButton()];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [newSqlQueryBtn, openQueryBtn];
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import NewNotebookIcon from "../../../../images/notebook/Notebook-new.svg";
|
||||||
|
import { CommandButtonComponentProps } from "../../../Controls/CommandButton/CommandButtonComponent";
|
||||||
|
import Explorer from "../../../Explorer";
|
||||||
|
import { useSelectedNode } from "../../../useSelectedNode";
|
||||||
|
|
||||||
|
export function createuploadNotebookButton(container: Explorer): CommandButtonComponentProps {
|
||||||
|
const label = "Upload to Notebook Server";
|
||||||
|
return {
|
||||||
|
iconSrc: NewNotebookIcon,
|
||||||
|
iconAlt: label,
|
||||||
|
onCommandClick: () => container.openUploadFilePanel(),
|
||||||
|
commandButtonLabel: label,
|
||||||
|
hasPopup: false,
|
||||||
|
disabled: useSelectedNode.getState().isQueryCopilotCollectionSelected(),
|
||||||
|
ariaLabel: label,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1425,6 +1425,10 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
this.setState({ isExecuting: false });
|
this.setState({ isExecuting: false });
|
||||||
TelemetryProcessor.traceSuccess(Action.CreateCollection, telemetryData, startKey);
|
TelemetryProcessor.traceSuccess(Action.CreateCollection, telemetryData, startKey);
|
||||||
useSidePanel.getState().closeSidePanel();
|
useSidePanel.getState().closeSidePanel();
|
||||||
|
// open NPS Survey Dialog once the collection is created
|
||||||
|
if (userContext.features.enableNPSSurvey) {
|
||||||
|
this.props.explorer.openNPSSurveyDialog();
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorMessage: string = getErrorMessage(error);
|
const errorMessage: string = getErrorMessage(error);
|
||||||
this.setState({ isExecuting: false, errorMessage, showErrorDetails: true });
|
this.setState({ isExecuting: false, errorMessage, showErrorDetails: true });
|
||||||
|
|||||||
@@ -112,6 +112,9 @@
|
|||||||
margin-top: 28px;
|
margin-top: 28px;
|
||||||
margin-left: 4px !important;
|
margin-left: 4px !important;
|
||||||
}
|
}
|
||||||
|
.addRemoveIcon [alt="editEntity"]:focus,.addRemoveIconLabel [alt="editEntity"]:focus{
|
||||||
|
border:1px dashed #605E5C
|
||||||
|
}
|
||||||
.addNewParamStyle {
|
.addNewParamStyle {
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
margin-left: 5px !important;
|
margin-left: 5px !important;
|
||||||
|
|||||||
@@ -78,11 +78,13 @@ export const QueryCopilotFeedbackModal: React.FC = (): JSX.Element => {
|
|||||||
}}
|
}}
|
||||||
></ChoiceGroup>
|
></ChoiceGroup>
|
||||||
<Text style={{ fontSize: 12, marginBottom: 14 }}>
|
<Text style={{ fontSize: 12, marginBottom: 14 }}>
|
||||||
By pressing submit, your feedback will be used to improve Microsoft products and services. IT admins for your
|
By pressing submit, your feedback will be used to improve Microsoft products and services. Please see the{" "}
|
||||||
organization will be able to view and manage your feedback data.{" "}
|
{
|
||||||
<Link href="" target="_blank">
|
<Link href="https://privacy.microsoft.com/privacystatement" target="_blank">
|
||||||
Privacy statement
|
Privacy statement
|
||||||
</Link>
|
</Link>
|
||||||
|
}{" "}
|
||||||
|
for more information.
|
||||||
</Text>
|
</Text>
|
||||||
{likeQuery && (
|
{likeQuery && (
|
||||||
<Checkbox
|
<Checkbox
|
||||||
|
|||||||
@@ -42,8 +42,8 @@ export const WelcomeModal = ({ visible }: { visible: boolean }): JSX.Element =>
|
|||||||
</Stack>
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
<Stack horizontalAlign="center">
|
<Stack horizontalAlign="center">
|
||||||
<Stack.Item align="center">
|
<Stack.Item align="center" style={{ textAlign: "center" }}>
|
||||||
<Text className="title bold">Welcome to Copilot in CosmosDB</Text>
|
<Text className="title bold">Welcome to Query Copilot for Azure Cosmos DB (Private Preview)</Text>
|
||||||
</Stack.Item>
|
</Stack.Item>
|
||||||
<Stack.Item align="center" className="text">
|
<Stack.Item align="center" className="text">
|
||||||
<Stack horizontal>
|
<Stack horizontal>
|
||||||
@@ -52,7 +52,7 @@ export const WelcomeModal = ({ visible }: { visible: boolean }): JSX.Element =>
|
|||||||
</StackItem>
|
</StackItem>
|
||||||
<StackItem align="start">
|
<StackItem align="start">
|
||||||
<Text className="bold">
|
<Text className="bold">
|
||||||
Let copilot do the work for you
|
Let Query Copilot do the work for you
|
||||||
<br />
|
<br />
|
||||||
</Text>
|
</Text>
|
||||||
</StackItem>
|
</StackItem>
|
||||||
@@ -60,7 +60,7 @@ export const WelcomeModal = ({ visible }: { visible: boolean }): JSX.Element =>
|
|||||||
<Text>
|
<Text>
|
||||||
Ask Copilot to generate a query by describing the query in your words.
|
Ask Copilot to generate a query by describing the query in your words.
|
||||||
<br />
|
<br />
|
||||||
<Link href="">Learn more</Link>
|
<Link href="http://aka.ms/cdb-copilot-learn-more">Learn more</Link>
|
||||||
</Text>
|
</Text>
|
||||||
</Stack.Item>
|
</Stack.Item>
|
||||||
<Stack.Item align="center" className="text">
|
<Stack.Item align="center" className="text">
|
||||||
@@ -78,7 +78,7 @@ export const WelcomeModal = ({ visible }: { visible: boolean }): JSX.Element =>
|
|||||||
<Text>
|
<Text>
|
||||||
AI-generated content can have mistakes. Make sure it’s accurate and appropriate before using it.
|
AI-generated content can have mistakes. Make sure it’s accurate and appropriate before using it.
|
||||||
<br />
|
<br />
|
||||||
<Link href="">Read preview terms</Link>
|
<Link href="http://aka.ms/cdb-copilot-preview-terms">Read preview terms</Link>
|
||||||
</Text>
|
</Text>
|
||||||
</Stack.Item>
|
</Stack.Item>
|
||||||
<Stack.Item align="center" className="text">
|
<Stack.Item align="center" className="text">
|
||||||
@@ -88,15 +88,16 @@ export const WelcomeModal = ({ visible }: { visible: boolean }): JSX.Element =>
|
|||||||
</StackItem>
|
</StackItem>
|
||||||
<StackItem align="start">
|
<StackItem align="start">
|
||||||
<Text className="bold">
|
<Text className="bold">
|
||||||
Copilot currently works only a sample database
|
Query Copilot works on a sample database.
|
||||||
<br />
|
<br />
|
||||||
</Text>
|
</Text>
|
||||||
</StackItem>
|
</StackItem>
|
||||||
</Stack>
|
</Stack>
|
||||||
<Text>
|
<Text>
|
||||||
Copilot is setup on a sample database we have configured for you at no cost
|
While in Private Preview, Query Copilot is setup to work on sample database we have configured for you
|
||||||
|
at no cost.
|
||||||
<br />
|
<br />
|
||||||
<Link href="">Learn more</Link>
|
<Link href="http://aka.ms/cdb-copilot-learn-more">Learn more</Link>
|
||||||
</Text>
|
</Text>
|
||||||
</Stack.Item>
|
</Stack.Item>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@@ -118,14 +118,16 @@ exports[`Query Copilot Feedback Modal snapshot test shoud render and match snaps
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
By pressing submit, your feedback will be used to improve Microsoft products and services. IT admins for your organization will be able to view and manage your feedback data.
|
By pressing submit, your feedback will be used to improve Microsoft products and services. Please see the
|
||||||
|
|
||||||
<StyledLinkBase
|
<StyledLinkBase
|
||||||
href=""
|
href="https://privacy.microsoft.com/privacystatement"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
Privacy statement
|
Privacy statement
|
||||||
</StyledLinkBase>
|
</StyledLinkBase>
|
||||||
|
|
||||||
|
for more information.
|
||||||
</Text>
|
</Text>
|
||||||
<Stack
|
<Stack
|
||||||
horizontal={true}
|
horizontal={true}
|
||||||
@@ -271,14 +273,16 @@ exports[`Query Copilot Feedback Modal snapshot test should cancel submission 1`]
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
By pressing submit, your feedback will be used to improve Microsoft products and services. IT admins for your organization will be able to view and manage your feedback data.
|
By pressing submit, your feedback will be used to improve Microsoft products and services. Please see the
|
||||||
|
|
||||||
<StyledLinkBase
|
<StyledLinkBase
|
||||||
href=""
|
href="https://privacy.microsoft.com/privacystatement"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
Privacy statement
|
Privacy statement
|
||||||
</StyledLinkBase>
|
</StyledLinkBase>
|
||||||
|
|
||||||
|
for more information.
|
||||||
</Text>
|
</Text>
|
||||||
<Stack
|
<Stack
|
||||||
horizontal={true}
|
horizontal={true}
|
||||||
@@ -424,14 +428,16 @@ exports[`Query Copilot Feedback Modal snapshot test should close on cancel click
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
By pressing submit, your feedback will be used to improve Microsoft products and services. IT admins for your organization will be able to view and manage your feedback data.
|
By pressing submit, your feedback will be used to improve Microsoft products and services. Please see the
|
||||||
|
|
||||||
<StyledLinkBase
|
<StyledLinkBase
|
||||||
href=""
|
href="https://privacy.microsoft.com/privacystatement"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
Privacy statement
|
Privacy statement
|
||||||
</StyledLinkBase>
|
</StyledLinkBase>
|
||||||
|
|
||||||
|
for more information.
|
||||||
</Text>
|
</Text>
|
||||||
<Stack
|
<Stack
|
||||||
horizontal={true}
|
horizontal={true}
|
||||||
@@ -577,14 +583,16 @@ exports[`Query Copilot Feedback Modal snapshot test should get user unput 1`] =
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
By pressing submit, your feedback will be used to improve Microsoft products and services. IT admins for your organization will be able to view and manage your feedback data.
|
By pressing submit, your feedback will be used to improve Microsoft products and services. Please see the
|
||||||
|
|
||||||
<StyledLinkBase
|
<StyledLinkBase
|
||||||
href=""
|
href="https://privacy.microsoft.com/privacystatement"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
Privacy statement
|
Privacy statement
|
||||||
</StyledLinkBase>
|
</StyledLinkBase>
|
||||||
|
|
||||||
|
for more information.
|
||||||
</Text>
|
</Text>
|
||||||
<Stack
|
<Stack
|
||||||
horizontal={true}
|
horizontal={true}
|
||||||
@@ -730,14 +738,16 @@ exports[`Query Copilot Feedback Modal snapshot test should not render dont show
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
By pressing submit, your feedback will be used to improve Microsoft products and services. IT admins for your organization will be able to view and manage your feedback data.
|
By pressing submit, your feedback will be used to improve Microsoft products and services. Please see the
|
||||||
|
|
||||||
<StyledLinkBase
|
<StyledLinkBase
|
||||||
href=""
|
href="https://privacy.microsoft.com/privacystatement"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
Privacy statement
|
Privacy statement
|
||||||
</StyledLinkBase>
|
</StyledLinkBase>
|
||||||
|
|
||||||
|
for more information.
|
||||||
</Text>
|
</Text>
|
||||||
<Stack
|
<Stack
|
||||||
horizontal={true}
|
horizontal={true}
|
||||||
@@ -883,14 +893,16 @@ exports[`Query Copilot Feedback Modal snapshot test should record user contact c
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
By pressing submit, your feedback will be used to improve Microsoft products and services. IT admins for your organization will be able to view and manage your feedback data.
|
By pressing submit, your feedback will be used to improve Microsoft products and services. Please see the
|
||||||
|
|
||||||
<StyledLinkBase
|
<StyledLinkBase
|
||||||
href=""
|
href="https://privacy.microsoft.com/privacystatement"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
Privacy statement
|
Privacy statement
|
||||||
</StyledLinkBase>
|
</StyledLinkBase>
|
||||||
|
|
||||||
|
for more information.
|
||||||
</Text>
|
</Text>
|
||||||
<Stack
|
<Stack
|
||||||
horizontal={true}
|
horizontal={true}
|
||||||
@@ -1036,14 +1048,16 @@ exports[`Query Copilot Feedback Modal snapshot test should record user contact c
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
By pressing submit, your feedback will be used to improve Microsoft products and services. IT admins for your organization will be able to view and manage your feedback data.
|
By pressing submit, your feedback will be used to improve Microsoft products and services. Please see the
|
||||||
|
|
||||||
<StyledLinkBase
|
<StyledLinkBase
|
||||||
href=""
|
href="https://privacy.microsoft.com/privacystatement"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
Privacy statement
|
Privacy statement
|
||||||
</StyledLinkBase>
|
</StyledLinkBase>
|
||||||
|
|
||||||
|
for more information.
|
||||||
</Text>
|
</Text>
|
||||||
<Stack
|
<Stack
|
||||||
horizontal={true}
|
horizontal={true}
|
||||||
@@ -1189,14 +1203,16 @@ exports[`Query Copilot Feedback Modal snapshot test should render dont show agai
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
By pressing submit, your feedback will be used to improve Microsoft products and services. IT admins for your organization will be able to view and manage your feedback data.
|
By pressing submit, your feedback will be used to improve Microsoft products and services. Please see the
|
||||||
|
|
||||||
<StyledLinkBase
|
<StyledLinkBase
|
||||||
href=""
|
href="https://privacy.microsoft.com/privacystatement"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
Privacy statement
|
Privacy statement
|
||||||
</StyledLinkBase>
|
</StyledLinkBase>
|
||||||
|
|
||||||
|
for more information.
|
||||||
</Text>
|
</Text>
|
||||||
<StyledCheckboxBase
|
<StyledCheckboxBase
|
||||||
checked={true}
|
checked={true}
|
||||||
@@ -1357,14 +1373,16 @@ exports[`Query Copilot Feedback Modal snapshot test should submit submission 1`]
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
By pressing submit, your feedback will be used to improve Microsoft products and services. IT admins for your organization will be able to view and manage your feedback data.
|
By pressing submit, your feedback will be used to improve Microsoft products and services. Please see the
|
||||||
|
|
||||||
<StyledLinkBase
|
<StyledLinkBase
|
||||||
href=""
|
href="https://privacy.microsoft.com/privacystatement"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
Privacy statement
|
Privacy statement
|
||||||
</StyledLinkBase>
|
</StyledLinkBase>
|
||||||
|
|
||||||
|
for more information.
|
||||||
</Text>
|
</Text>
|
||||||
<Stack
|
<Stack
|
||||||
horizontal={true}
|
horizontal={true}
|
||||||
|
|||||||
@@ -60,11 +60,16 @@ exports[`Query Copilot Welcome Modal snapshot test should render when isOpen is
|
|||||||
>
|
>
|
||||||
<StackItem
|
<StackItem
|
||||||
align="center"
|
align="center"
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"textAlign": "center",
|
||||||
|
}
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<Text
|
<Text
|
||||||
className="title bold"
|
className="title bold"
|
||||||
>
|
>
|
||||||
Welcome to Copilot in CosmosDB
|
Welcome to Query Copilot for Azure Cosmos DB (Private Preview)
|
||||||
</Text>
|
</Text>
|
||||||
</StackItem>
|
</StackItem>
|
||||||
<StackItem
|
<StackItem
|
||||||
@@ -88,7 +93,7 @@ exports[`Query Copilot Welcome Modal snapshot test should render when isOpen is
|
|||||||
<Text
|
<Text
|
||||||
className="bold"
|
className="bold"
|
||||||
>
|
>
|
||||||
Let copilot do the work for you
|
Let Query Copilot do the work for you
|
||||||
<br />
|
<br />
|
||||||
</Text>
|
</Text>
|
||||||
</StackItem>
|
</StackItem>
|
||||||
@@ -97,7 +102,7 @@ exports[`Query Copilot Welcome Modal snapshot test should render when isOpen is
|
|||||||
Ask Copilot to generate a query by describing the query in your words.
|
Ask Copilot to generate a query by describing the query in your words.
|
||||||
<br />
|
<br />
|
||||||
<StyledLinkBase
|
<StyledLinkBase
|
||||||
href=""
|
href="http://aka.ms/cdb-copilot-learn-more"
|
||||||
>
|
>
|
||||||
Learn more
|
Learn more
|
||||||
</StyledLinkBase>
|
</StyledLinkBase>
|
||||||
@@ -133,7 +138,7 @@ exports[`Query Copilot Welcome Modal snapshot test should render when isOpen is
|
|||||||
AI-generated content can have mistakes. Make sure it’s accurate and appropriate before using it.
|
AI-generated content can have mistakes. Make sure it’s accurate and appropriate before using it.
|
||||||
<br />
|
<br />
|
||||||
<StyledLinkBase
|
<StyledLinkBase
|
||||||
href=""
|
href="http://aka.ms/cdb-copilot-preview-terms"
|
||||||
>
|
>
|
||||||
Read preview terms
|
Read preview terms
|
||||||
</StyledLinkBase>
|
</StyledLinkBase>
|
||||||
@@ -160,16 +165,16 @@ exports[`Query Copilot Welcome Modal snapshot test should render when isOpen is
|
|||||||
<Text
|
<Text
|
||||||
className="bold"
|
className="bold"
|
||||||
>
|
>
|
||||||
Copilot currently works only a sample database
|
Query Copilot works on a sample database.
|
||||||
<br />
|
<br />
|
||||||
</Text>
|
</Text>
|
||||||
</StackItem>
|
</StackItem>
|
||||||
</Stack>
|
</Stack>
|
||||||
<Text>
|
<Text>
|
||||||
Copilot is setup on a sample database we have configured for you at no cost
|
While in Private Preview, Query Copilot is setup to work on sample database we have configured for you at no cost.
|
||||||
<br />
|
<br />
|
||||||
<StyledLinkBase
|
<StyledLinkBase
|
||||||
href=""
|
href="http://aka.ms/cdb-copilot-learn-more"
|
||||||
>
|
>
|
||||||
Learn more
|
Learn more
|
||||||
</StyledLinkBase>
|
</StyledLinkBase>
|
||||||
|
|||||||
@@ -5,9 +5,7 @@ import { QueryCopilotTab } from "./QueryCopilotTab";
|
|||||||
|
|
||||||
describe("Query copilot tab snapshot test", () => {
|
describe("Query copilot tab snapshot test", () => {
|
||||||
it("should render with initial input", () => {
|
it("should render with initial input", () => {
|
||||||
const wrapper = shallow(
|
const wrapper = shallow(<QueryCopilotTab explorer={new Explorer()} />);
|
||||||
<QueryCopilotTab initialInput="Write a query to return all records in this table" explorer={new Explorer()} />
|
|
||||||
);
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
expect(wrapper).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ import { DeletePopup } from "Explorer/QueryCopilot/Popup/DeletePopup";
|
|||||||
import { querySampleDocuments, submitFeedback } from "Explorer/QueryCopilot/QueryCopilotUtilities";
|
import { querySampleDocuments, submitFeedback } from "Explorer/QueryCopilot/QueryCopilotUtilities";
|
||||||
import { SamplePrompts, SamplePromptsProps } from "Explorer/QueryCopilot/SamplePrompts/SamplePrompts";
|
import { SamplePrompts, SamplePromptsProps } from "Explorer/QueryCopilot/SamplePrompts/SamplePrompts";
|
||||||
import { QueryResultSection } from "Explorer/Tabs/QueryTab/QueryResultSection";
|
import { QueryResultSection } from "Explorer/Tabs/QueryTab/QueryResultSection";
|
||||||
|
import { Action } from "Shared/Telemetry/TelemetryConstants";
|
||||||
|
import { traceFailure, traceStart, traceSuccess } from "Shared/Telemetry/TelemetryProcessor";
|
||||||
import { userContext } from "UserContext";
|
import { userContext } from "UserContext";
|
||||||
import { queryPagesUntilContentPresent } from "Utils/QueryUtils";
|
import { queryPagesUntilContentPresent } from "Utils/QueryUtils";
|
||||||
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||||
@@ -54,7 +56,6 @@ interface SuggestedPrompt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface QueryCopilotTabProps {
|
interface QueryCopilotTabProps {
|
||||||
initialInput: string;
|
|
||||||
explorer: Explorer;
|
explorer: Explorer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,31 +72,50 @@ const promptStyles: IButtonStyles = {
|
|||||||
label: { fontWeight: 400, textAlign: "left", paddingLeft: 8 },
|
label: { fontWeight: 400, textAlign: "left", paddingLeft: 8 },
|
||||||
};
|
};
|
||||||
|
|
||||||
export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({
|
export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({ explorer }: QueryCopilotTabProps): JSX.Element => {
|
||||||
initialInput,
|
|
||||||
explorer,
|
|
||||||
}: QueryCopilotTabProps): JSX.Element => {
|
|
||||||
const hideFeedbackModalForLikedQueries = useQueryCopilot((state) => state.hideFeedbackModalForLikedQueries);
|
|
||||||
const [userPrompt, setUserPrompt] = useState<string>(initialInput || "");
|
|
||||||
const [generatedQuery, setGeneratedQuery] = useState<string>("");
|
|
||||||
const [query, setQuery] = useState<string>("");
|
|
||||||
const [selectedQuery, setSelectedQuery] = useState<string>("");
|
|
||||||
const [isGeneratingQuery, setIsGeneratingQuery] = useState<boolean>(false);
|
|
||||||
const [isExecuting, setIsExecuting] = useState<boolean>(false);
|
|
||||||
const [likeQuery, setLikeQuery] = useState<boolean>();
|
|
||||||
const [dislikeQuery, setDislikeQuery] = useState<boolean>();
|
|
||||||
const [showCallout, setShowCallout] = useState<boolean>(false);
|
|
||||||
const [showSamplePrompts, setShowSamplePrompts] = useState<boolean>(false);
|
|
||||||
const [queryIterator, setQueryIterator] = useState<MinimalQueryIterator>();
|
|
||||||
const [queryResults, setQueryResults] = useState<QueryResults>();
|
|
||||||
const [errorMessage, setErrorMessage] = useState<string>("");
|
|
||||||
const [copilotTeachingBubbleVisible, { toggle: toggleCopilotTeachingBubbleVisible }] = useBoolean(false);
|
const [copilotTeachingBubbleVisible, { toggle: toggleCopilotTeachingBubbleVisible }] = useBoolean(false);
|
||||||
const inputEdited = useRef(false);
|
const inputEdited = useRef(false);
|
||||||
const [isSamplePromptsOpen, setIsSamplePromptsOpen] = useState<boolean>(false);
|
const {
|
||||||
const [showDeletePopup, setShowDeletePopup] = useState<boolean>(false);
|
hideFeedbackModalForLikedQueries,
|
||||||
const [showFeedbackBar, setShowFeedbackBar] = useState<boolean>(false);
|
userPrompt,
|
||||||
const [showCopyPopup, setshowCopyPopup] = useState<boolean>(false);
|
setUserPrompt,
|
||||||
const [showErrorMessageBar, setShowErrorMessageBar] = useState<boolean>(false);
|
generatedQuery,
|
||||||
|
setGeneratedQuery,
|
||||||
|
query,
|
||||||
|
setQuery,
|
||||||
|
selectedQuery,
|
||||||
|
setSelectedQuery,
|
||||||
|
isGeneratingQuery,
|
||||||
|
setIsGeneratingQuery,
|
||||||
|
isExecuting,
|
||||||
|
setIsExecuting,
|
||||||
|
likeQuery,
|
||||||
|
setLikeQuery,
|
||||||
|
dislikeQuery,
|
||||||
|
setDislikeQuery,
|
||||||
|
showCallout,
|
||||||
|
setShowCallout,
|
||||||
|
showSamplePrompts,
|
||||||
|
setShowSamplePrompts,
|
||||||
|
queryIterator,
|
||||||
|
setQueryIterator,
|
||||||
|
queryResults,
|
||||||
|
setQueryResults,
|
||||||
|
errorMessage,
|
||||||
|
setErrorMessage,
|
||||||
|
isSamplePromptsOpen,
|
||||||
|
setIsSamplePromptsOpen,
|
||||||
|
showDeletePopup,
|
||||||
|
setShowDeletePopup,
|
||||||
|
showFeedbackBar,
|
||||||
|
setShowFeedbackBar,
|
||||||
|
showCopyPopup,
|
||||||
|
setshowCopyPopup,
|
||||||
|
showErrorMessageBar,
|
||||||
|
setShowErrorMessageBar,
|
||||||
|
generatedQueryComments,
|
||||||
|
setGeneratedQueryComments,
|
||||||
|
} = useQueryCopilot();
|
||||||
|
|
||||||
const sampleProps: SamplePromptsProps = {
|
const sampleProps: SamplePromptsProps = {
|
||||||
isSamplePromptsOpen: isSamplePromptsOpen,
|
isSamplePromptsOpen: isSamplePromptsOpen,
|
||||||
@@ -170,10 +190,12 @@ export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({
|
|||||||
userPrompt: userPrompt,
|
userPrompt: userPrompt,
|
||||||
};
|
};
|
||||||
setShowDeletePopup(false);
|
setShowDeletePopup(false);
|
||||||
|
useQueryCopilot.getState().refreshCorrelationId();
|
||||||
const response = await fetch("https://copilotorchestrater.azurewebsites.net/generateSQLQuery", {
|
const response = await fetch("https://copilotorchestrater.azurewebsites.net/generateSQLQuery", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"content-type": "application/json",
|
"content-type": "application/json",
|
||||||
|
"x-ms-correlationid": useQueryCopilot.getState().correlationId,
|
||||||
},
|
},
|
||||||
body: JSON.stringify(payload),
|
body: JSON.stringify(payload),
|
||||||
});
|
});
|
||||||
@@ -188,6 +210,7 @@ export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({
|
|||||||
query += generateSQLQueryResponse.sql;
|
query += generateSQLQueryResponse.sql;
|
||||||
setQuery(query);
|
setQuery(query);
|
||||||
setGeneratedQuery(generateSQLQueryResponse.sql);
|
setGeneratedQuery(generateSQLQueryResponse.sql);
|
||||||
|
setGeneratedQueryComments(generateSQLQueryResponse.explanation);
|
||||||
setShowErrorMessageBar(false);
|
setShowErrorMessageBar(false);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -208,6 +231,13 @@ export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onExecuteQueryClick = async (): Promise<void> => {
|
const onExecuteQueryClick = async (): Promise<void> => {
|
||||||
|
traceStart(Action.ExecuteQueryGeneratedFromQueryCopilot, {
|
||||||
|
correlationId: useQueryCopilot.getState().correlationId,
|
||||||
|
userPrompt: userPrompt,
|
||||||
|
generatedQuery: generatedQuery,
|
||||||
|
generatedQueryComments: generatedQueryComments,
|
||||||
|
executedQuery: selectedQuery || query,
|
||||||
|
});
|
||||||
const queryToExecute = selectedQuery || query;
|
const queryToExecute = selectedQuery || query;
|
||||||
const queryIterator = querySampleDocuments(queryToExecute, {
|
const queryIterator = querySampleDocuments(queryToExecute, {
|
||||||
enableCrossPartitionQuery: shouldEnableCrossPartitionKey(),
|
enableCrossPartitionQuery: shouldEnableCrossPartitionKey(),
|
||||||
@@ -233,8 +263,15 @@ export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({
|
|||||||
setQueryResults(queryResults);
|
setQueryResults(queryResults);
|
||||||
setErrorMessage("");
|
setErrorMessage("");
|
||||||
setShowErrorMessageBar(false);
|
setShowErrorMessageBar(false);
|
||||||
|
traceSuccess(Action.ExecuteQueryGeneratedFromQueryCopilot, {
|
||||||
|
correlationId: useQueryCopilot.getState().correlationId,
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorMessage = getErrorMessage(error);
|
const errorMessage = getErrorMessage(error);
|
||||||
|
traceFailure(Action.ExecuteQueryGeneratedFromQueryCopilot, {
|
||||||
|
correlationId: useQueryCopilot.getState().correlationId,
|
||||||
|
errorMessage: errorMessage,
|
||||||
|
});
|
||||||
setErrorMessage(errorMessage);
|
setErrorMessage(errorMessage);
|
||||||
handleError(errorMessage, "executeQueryCopilotTab");
|
handleError(errorMessage, "executeQueryCopilotTab");
|
||||||
useTabs.getState().setIsQueryErrorThrown(true);
|
useTabs.getState().setIsQueryErrorThrown(true);
|
||||||
@@ -247,7 +284,7 @@ export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({
|
|||||||
|
|
||||||
const getCommandbarButtons = (): CommandButtonComponentProps[] => {
|
const getCommandbarButtons = (): CommandButtonComponentProps[] => {
|
||||||
const executeQueryBtnLabel = selectedQuery ? "Execute Selection" : "Execute Query";
|
const executeQueryBtnLabel = selectedQuery ? "Execute Selection" : "Execute Query";
|
||||||
const executeQueryBtn = {
|
const executeQueryBtn: CommandButtonComponentProps = {
|
||||||
iconSrc: ExecuteQueryIcon,
|
iconSrc: ExecuteQueryIcon,
|
||||||
iconAlt: executeQueryBtnLabel,
|
iconAlt: executeQueryBtnLabel,
|
||||||
onCommandClick: () => onExecuteQueryClick(),
|
onCommandClick: () => onExecuteQueryClick(),
|
||||||
@@ -257,7 +294,7 @@ export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({
|
|||||||
disabled: query?.trim() === "",
|
disabled: query?.trim() === "",
|
||||||
};
|
};
|
||||||
|
|
||||||
const saveQueryBtn = {
|
const saveQueryBtn: CommandButtonComponentProps = {
|
||||||
iconSrc: SaveQueryIcon,
|
iconSrc: SaveQueryIcon,
|
||||||
iconAlt: "Save Query",
|
iconAlt: "Save Query",
|
||||||
onCommandClick: () =>
|
onCommandClick: () =>
|
||||||
@@ -269,7 +306,7 @@ export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Sample Prompts temporary disabled due current design
|
// Sample Prompts temporary disabled due current design
|
||||||
// const samplePromptsBtn = {
|
// const samplePromptsBtn: CommandButtonComponentProps = {
|
||||||
// iconSrc: SamplePromptsIcon,
|
// iconSrc: SamplePromptsIcon,
|
||||||
// iconAlt: "Sample Prompts",
|
// iconAlt: "Sample Prompts",
|
||||||
// onCommandClick: () => setIsSamplePromptsOpen(true),
|
// onCommandClick: () => setIsSamplePromptsOpen(true),
|
||||||
@@ -281,9 +318,10 @@ export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({
|
|||||||
return [executeQueryBtn, saveQueryBtn];
|
return [executeQueryBtn, saveQueryBtn];
|
||||||
};
|
};
|
||||||
const showTeachingBubble = (): void => {
|
const showTeachingBubble = (): void => {
|
||||||
if (!inputEdited.current) {
|
const shouldShowTeachingBubble = !inputEdited.current && userPrompt.trim() === "";
|
||||||
|
if (shouldShowTeachingBubble) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (!inputEdited.current) {
|
if (shouldShowTeachingBubble) {
|
||||||
toggleCopilotTeachingBubbleVisible();
|
toggleCopilotTeachingBubbleVisible();
|
||||||
inputEdited.current = true;
|
inputEdited.current = true;
|
||||||
}
|
}
|
||||||
@@ -297,21 +335,24 @@ export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({
|
|||||||
setShowCallout(false);
|
setShowCallout(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const startGenerateQueryProcess = () => {
|
||||||
|
updateHistories();
|
||||||
|
generateSQLQuery();
|
||||||
|
resetButtonState();
|
||||||
|
};
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
useCommandBar.getState().setContextButtons(getCommandbarButtons());
|
useCommandBar.getState().setContextButtons(getCommandbarButtons());
|
||||||
}, [query, selectedQuery]);
|
}, [query, selectedQuery]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (initialInput) {
|
|
||||||
generateSQLQuery();
|
|
||||||
}
|
|
||||||
showTeachingBubble();
|
showTeachingBubble();
|
||||||
useTabs.getState().setIsQueryErrorThrown(false);
|
useTabs.getState().setIsQueryErrorThrown(false);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack className="tab-pane" style={{ padding: 24, width: "100%" }}>
|
<Stack className="tab-pane" style={{ padding: 24, width: "100%" }}>
|
||||||
<div style={{ overflowY: "auto", height: "100%" }}>
|
<div style={isGeneratingQuery ? { height: "100%" } : { overflowY: "auto", height: "100%" }}>
|
||||||
<Stack horizontal verticalAlign="center">
|
<Stack horizontal verticalAlign="center">
|
||||||
<Image src={CopilotIcon} />
|
<Image src={CopilotIcon} />
|
||||||
<Text style={{ marginLeft: 8, fontWeight: 600, fontSize: 16 }}>Copilot</Text>
|
<Text style={{ marginLeft: 8, fontWeight: 600, fontSize: 16 }}>Copilot</Text>
|
||||||
@@ -325,6 +366,11 @@ export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({
|
|||||||
inputEdited.current = true;
|
inputEdited.current = true;
|
||||||
setShowSamplePrompts(true);
|
setShowSamplePrompts(true);
|
||||||
}}
|
}}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === "Enter") {
|
||||||
|
startGenerateQueryProcess();
|
||||||
|
}
|
||||||
|
}}
|
||||||
style={{ lineHeight: 30 }}
|
style={{ lineHeight: 30 }}
|
||||||
styles={{ root: { width: "95%" } }}
|
styles={{ root: { width: "95%" } }}
|
||||||
disabled={isGeneratingQuery}
|
disabled={isGeneratingQuery}
|
||||||
@@ -357,11 +403,7 @@ export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({
|
|||||||
iconProps={{ iconName: "Send" }}
|
iconProps={{ iconName: "Send" }}
|
||||||
disabled={isGeneratingQuery || !userPrompt.trim()}
|
disabled={isGeneratingQuery || !userPrompt.trim()}
|
||||||
style={{ marginLeft: 8 }}
|
style={{ marginLeft: 8 }}
|
||||||
onClick={() => {
|
onClick={() => startGenerateQueryProcess()}
|
||||||
updateHistories();
|
|
||||||
generateSQLQuery();
|
|
||||||
resetButtonState();
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
{isGeneratingQuery && <Spinner style={{ marginLeft: 8 }} />}
|
{isGeneratingQuery && <Spinner style={{ marginLeft: 8 }} />}
|
||||||
{showSamplePrompts && (
|
{showSamplePrompts && (
|
||||||
@@ -453,7 +495,7 @@ export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Learn about{" "}
|
Learn about{" "}
|
||||||
<Link target="_blank" href="">
|
<Link target="_blank" href="http://aka.ms/cdb-copilot-writing">
|
||||||
writing effective prompts
|
writing effective prompts
|
||||||
</Link>
|
</Link>
|
||||||
</Text>
|
</Text>
|
||||||
@@ -467,7 +509,7 @@ export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({
|
|||||||
<Stack style={{ marginTop: 8, marginBottom: 24 }}>
|
<Stack style={{ marginTop: 8, marginBottom: 24 }}>
|
||||||
<Text style={{ fontSize: 12 }}>
|
<Text style={{ fontSize: 12 }}>
|
||||||
AI-generated content can have mistakes. Make sure it's accurate and appropriate before using it.{" "}
|
AI-generated content can have mistakes. Make sure it's accurate and appropriate before using it.{" "}
|
||||||
<Link href="" target="_blank">
|
<Link href="http://aka.ms/cdb-copilot-preview-terms" target="_blank">
|
||||||
Read preview terms
|
Read preview terms
|
||||||
</Link>
|
</Link>
|
||||||
{showErrorMessageBar && (
|
{showErrorMessageBar && (
|
||||||
@@ -490,7 +532,12 @@ export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({
|
|||||||
target="#likeBtn"
|
target="#likeBtn"
|
||||||
onDismiss={() => {
|
onDismiss={() => {
|
||||||
setShowCallout(false);
|
setShowCallout(false);
|
||||||
submitFeedback({ generatedQuery, likeQuery, description: "", userPrompt: userPrompt });
|
submitFeedback({
|
||||||
|
generatedQuery: generatedQuery,
|
||||||
|
likeQuery: likeQuery,
|
||||||
|
description: "",
|
||||||
|
userPrompt: userPrompt,
|
||||||
|
});
|
||||||
}}
|
}}
|
||||||
directionalHint={DirectionalHint.topCenter}
|
directionalHint={DirectionalHint.topCenter}
|
||||||
>
|
>
|
||||||
|
|||||||
225
src/Explorer/QueryCopilot/QueryCopilotUtilities.test.tsx
Normal file
225
src/Explorer/QueryCopilot/QueryCopilotUtilities.test.tsx
Normal file
@@ -0,0 +1,225 @@
|
|||||||
|
import { FeedOptions } from "@azure/cosmos";
|
||||||
|
import { QueryCopilotSampleContainerSchema } from "Common/Constants";
|
||||||
|
import { handleError } from "Common/ErrorHandlingUtils";
|
||||||
|
import { sampleDataClient } from "Common/SampleDataClient";
|
||||||
|
import * as commonUtils from "Common/dataAccess/queryDocuments";
|
||||||
|
import DocumentId from "Explorer/Tree/DocumentId";
|
||||||
|
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||||
|
import { querySampleDocuments, readSampleDocument, submitFeedback } from "./QueryCopilotUtilities";
|
||||||
|
jest.mock("Explorer/Tree/DocumentId", () => {
|
||||||
|
return jest.fn().mockImplementation(() => {
|
||||||
|
return {
|
||||||
|
id: jest.fn(),
|
||||||
|
loadDocument: jest.fn(),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
jest.mock("Utils/NotificationConsoleUtils", () => ({
|
||||||
|
logConsoleProgress: jest.fn(),
|
||||||
|
logConsoleError: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock("@azure/cosmos", () => ({
|
||||||
|
FeedOptions: jest.fn(),
|
||||||
|
QueryIterator: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock("Common/ErrorHandlingUtils", () => ({
|
||||||
|
handleError: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock("Utils/NotificationConsoleUtils", () => ({
|
||||||
|
logConsoleProgress: jest.fn().mockReturnValue((): void => undefined),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock("Common/dataAccess/queryDocuments", () => ({
|
||||||
|
getCommonQueryOptions: jest.fn((options) => options),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock("Common/SampleDataClient");
|
||||||
|
|
||||||
|
jest.mock("node-fetch");
|
||||||
|
|
||||||
|
describe("QueryCopilotUtilities", () => {
|
||||||
|
beforeEach(() => jest.clearAllMocks());
|
||||||
|
describe("submitFeedback", () => {
|
||||||
|
const payload = {
|
||||||
|
like: "like",
|
||||||
|
generatedSql: "GeneratedQuery",
|
||||||
|
userPrompt: "UserPrompt",
|
||||||
|
description: "Description",
|
||||||
|
contact: "Contact",
|
||||||
|
containerSchema: QueryCopilotSampleContainerSchema,
|
||||||
|
};
|
||||||
|
|
||||||
|
it("should call fetch with the payload with like", async () => {
|
||||||
|
const mockFetch = jest.fn().mockResolvedValueOnce({});
|
||||||
|
|
||||||
|
globalThis.fetch = mockFetch;
|
||||||
|
useQueryCopilot.getState().refreshCorrelationId();
|
||||||
|
|
||||||
|
await submitFeedback({
|
||||||
|
likeQuery: true,
|
||||||
|
generatedQuery: "GeneratedQuery",
|
||||||
|
userPrompt: "UserPrompt",
|
||||||
|
description: "Description",
|
||||||
|
contact: "Contact",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(mockFetch).toHaveBeenCalledWith(
|
||||||
|
"https://copilotorchestrater.azurewebsites.net/feedback",
|
||||||
|
expect.objectContaining({
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"content-type": "application/json",
|
||||||
|
"x-ms-correlationid": useQueryCopilot.getState().correlationId,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const actualBody = JSON.parse(mockFetch.mock.calls[0][1].body);
|
||||||
|
expect(actualBody).toEqual(payload);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should call fetch with the payload with unlike and empty parameters", async () => {
|
||||||
|
payload.like = "dislike";
|
||||||
|
payload.description = "";
|
||||||
|
payload.contact = "";
|
||||||
|
const mockFetch = jest.fn().mockResolvedValueOnce({});
|
||||||
|
|
||||||
|
globalThis.fetch = mockFetch;
|
||||||
|
useQueryCopilot.getState().refreshCorrelationId();
|
||||||
|
|
||||||
|
await submitFeedback({
|
||||||
|
likeQuery: false,
|
||||||
|
generatedQuery: "GeneratedQuery",
|
||||||
|
userPrompt: "UserPrompt",
|
||||||
|
description: undefined,
|
||||||
|
contact: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(mockFetch).toHaveBeenCalledWith(
|
||||||
|
"https://copilotorchestrater.azurewebsites.net/feedback",
|
||||||
|
expect.objectContaining({
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"content-type": "application/json",
|
||||||
|
"x-ms-correlationid": useQueryCopilot.getState().correlationId,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const actualBody = JSON.parse(mockFetch.mock.calls[0][1].body);
|
||||||
|
expect(actualBody).toEqual(payload);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle errors and call handleError", async () => {
|
||||||
|
globalThis.fetch = jest.fn().mockRejectedValueOnce(new Error("Mock error"));
|
||||||
|
|
||||||
|
await submitFeedback({
|
||||||
|
likeQuery: true,
|
||||||
|
generatedQuery: "GeneratedQuery",
|
||||||
|
userPrompt: "UserPrompt",
|
||||||
|
description: "Description",
|
||||||
|
contact: "Contact",
|
||||||
|
}).catch((error) => {
|
||||||
|
expect(error.message).toEqual("Mock error");
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(handleError).toHaveBeenCalledWith(new Error("Mock error"), expect.any(String));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("querySampleDocuments", () => {
|
||||||
|
(sampleDataClient as jest.Mock).mockReturnValue({
|
||||||
|
database: jest.fn().mockReturnValue({
|
||||||
|
container: jest.fn().mockReturnValue({
|
||||||
|
items: {
|
||||||
|
query: jest.fn().mockReturnValue([]),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
it("calls getCommonQueryOptions with the provided options", () => {
|
||||||
|
const query = "sample query";
|
||||||
|
const options: FeedOptions = { maxItemCount: 10 };
|
||||||
|
|
||||||
|
querySampleDocuments(query, options);
|
||||||
|
|
||||||
|
expect(commonUtils.getCommonQueryOptions).toHaveBeenCalledWith(options);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns the result of items.query method", () => {
|
||||||
|
const query = "sample query";
|
||||||
|
const options: FeedOptions = { maxItemCount: 10 };
|
||||||
|
const mockResult = [
|
||||||
|
{ id: 1, name: "Document 1" },
|
||||||
|
{ id: 2, name: "Document 2" },
|
||||||
|
];
|
||||||
|
|
||||||
|
// Mock the items.query method to return the mockResult
|
||||||
|
(sampleDataClient().database("CopilotSampleDb").container("SampleContainer").items
|
||||||
|
.query as jest.Mock).mockReturnValue(mockResult);
|
||||||
|
|
||||||
|
const result = querySampleDocuments(query, options);
|
||||||
|
|
||||||
|
expect(result).toEqual(mockResult);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("readSampleDocument", () => {
|
||||||
|
it("should call the read method with the correct parameters", async () => {
|
||||||
|
(sampleDataClient as jest.Mock).mockReturnValue({
|
||||||
|
database: jest.fn().mockReturnValue({
|
||||||
|
container: jest.fn().mockReturnValue({
|
||||||
|
item: jest.fn().mockReturnValue({
|
||||||
|
read: jest.fn().mockResolvedValue({
|
||||||
|
resource: {},
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const documentId = new DocumentId(null, "DocumentId", []);
|
||||||
|
const expectedResponse = {};
|
||||||
|
|
||||||
|
const result = await readSampleDocument(documentId);
|
||||||
|
|
||||||
|
expect(sampleDataClient).toHaveBeenCalled();
|
||||||
|
expect(sampleDataClient().database).toHaveBeenCalledWith("CopilotSampleDb");
|
||||||
|
expect(sampleDataClient().database("CopilotSampleDb").container).toHaveBeenCalledWith("SampleContainer");
|
||||||
|
expect(
|
||||||
|
sampleDataClient().database("CopilotSampleDb").container("SampleContainer").item("DocumentId", undefined).read
|
||||||
|
).toHaveBeenCalled();
|
||||||
|
expect(result).toEqual(expectedResponse);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle an error and re-throw it", async () => {
|
||||||
|
(sampleDataClient as jest.Mock).mockReturnValue({
|
||||||
|
database: jest.fn().mockReturnValue({
|
||||||
|
container: jest.fn().mockReturnValue({
|
||||||
|
item: jest.fn().mockReturnValue({
|
||||||
|
read: jest.fn().mockRejectedValue(new Error("Mock error")),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const errorMock = new Error("Mock error");
|
||||||
|
const documentId = new DocumentId(null, "DocumentId", []);
|
||||||
|
|
||||||
|
await expect(readSampleDocument(documentId)).rejects.toStrictEqual(errorMock);
|
||||||
|
|
||||||
|
expect(sampleDataClient).toHaveBeenCalled();
|
||||||
|
expect(sampleDataClient().database).toHaveBeenCalledWith("CopilotSampleDb");
|
||||||
|
expect(sampleDataClient().database("CopilotSampleDb").container).toHaveBeenCalledWith("SampleContainer");
|
||||||
|
expect(
|
||||||
|
sampleDataClient().database("CopilotSampleDb").container("SampleContainer").item("DocumentId", undefined).read
|
||||||
|
).toHaveBeenCalled();
|
||||||
|
expect(handleError).toHaveBeenCalledWith(errorMock, "ReadDocument", expect.any(String));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -10,6 +10,7 @@ import { getPartitionKeyValue } from "Common/dataAccess/getPartitionKeyValue";
|
|||||||
import { getCommonQueryOptions } from "Common/dataAccess/queryDocuments";
|
import { getCommonQueryOptions } from "Common/dataAccess/queryDocuments";
|
||||||
import DocumentId from "Explorer/Tree/DocumentId";
|
import DocumentId from "Explorer/Tree/DocumentId";
|
||||||
import { logConsoleProgress } from "Utils/NotificationConsoleUtils";
|
import { logConsoleProgress } from "Utils/NotificationConsoleUtils";
|
||||||
|
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||||
|
|
||||||
interface FeedbackParams {
|
interface FeedbackParams {
|
||||||
likeQuery: boolean;
|
likeQuery: boolean;
|
||||||
@@ -35,6 +36,7 @@ export const submitFeedback = async (params: FeedbackParams): Promise<void> => {
|
|||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"content-type": "application/json",
|
"content-type": "application/json",
|
||||||
|
"x-ms-correlationid": useQueryCopilot.getState().correlationId,
|
||||||
},
|
},
|
||||||
body: JSON.stringify(payload),
|
body: JSON.stringify(payload),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ exports[`Query copilot tab snapshot test should render with initial input 1`] =
|
|||||||
id="naturalLanguageInput"
|
id="naturalLanguageInput"
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
onClick={[Function]}
|
onClick={[Function]}
|
||||||
|
onKeyDown={[Function]}
|
||||||
style={
|
style={
|
||||||
Object {
|
Object {
|
||||||
"lineHeight": 30,
|
"lineHeight": 30,
|
||||||
@@ -66,10 +67,10 @@ exports[`Query copilot tab snapshot test should render with initial input 1`] =
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
value="Write a query to return all records in this table"
|
value=""
|
||||||
/>
|
/>
|
||||||
<CustomizedIconButton
|
<CustomizedIconButton
|
||||||
disabled={false}
|
disabled={true}
|
||||||
iconProps={
|
iconProps={
|
||||||
Object {
|
Object {
|
||||||
"iconName": "Send",
|
"iconName": "Send",
|
||||||
@@ -101,7 +102,7 @@ exports[`Query copilot tab snapshot test should render with initial input 1`] =
|
|||||||
AI-generated content can have mistakes. Make sure it's accurate and appropriate before using it.
|
AI-generated content can have mistakes. Make sure it's accurate and appropriate before using it.
|
||||||
|
|
||||||
<StyledLinkBase
|
<StyledLinkBase
|
||||||
href=""
|
href="http://aka.ms/cdb-copilot-preview-terms"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
Read preview terms
|
Read preview terms
|
||||||
|
|||||||
@@ -97,6 +97,12 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
|
|||||||
() => this.setState({}),
|
() => this.setState({}),
|
||||||
(state) => state.showResetPasswordBubble
|
(state) => state.showResetPasswordBubble
|
||||||
),
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dispose: useDatabases.subscribe(
|
||||||
|
() => this.setState({}),
|
||||||
|
(state) => state.sampleDataResourceTokenCollection
|
||||||
|
),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -107,7 +113,11 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
private getSplashScreenButtons = (): JSX.Element => {
|
private getSplashScreenButtons = (): JSX.Element => {
|
||||||
if (userContext.features.enableCopilot && userContext.apiType === "SQL") {
|
if (
|
||||||
|
useDatabases.getState().sampleDataResourceTokenCollection &&
|
||||||
|
userContext.features.enableCopilot &&
|
||||||
|
userContext.apiType === "SQL"
|
||||||
|
) {
|
||||||
return (
|
return (
|
||||||
<Stack style={{ width: "66%", cursor: "pointer", margin: "40px auto" }} tokens={{ childrenGap: 16 }}>
|
<Stack style={{ width: "66%", cursor: "pointer", margin: "40px auto" }} tokens={{ childrenGap: 16 }}>
|
||||||
<Stack horizontal tokens={{ childrenGap: 16 }}>
|
<Stack horizontal tokens={{ childrenGap: 16 }}>
|
||||||
@@ -137,7 +147,10 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
|
|||||||
description={
|
description={
|
||||||
"Copilot is your AI buddy that helps you write Azure Cosmos DB queries like a pro. Try it using our sample data set now!"
|
"Copilot is your AI buddy that helps you write Azure Cosmos DB queries like a pro. Try it using our sample data set now!"
|
||||||
}
|
}
|
||||||
onClick={() => useTabs.getState().openAndActivateReactTab(ReactTabKind.QueryCopilot)}
|
onClick={() => {
|
||||||
|
useTabs.getState().openAndActivateReactTab(ReactTabKind.QueryCopilot);
|
||||||
|
traceOpen(Action.OpenQueryCopilotFromSplashScreen, { apiType: userContext.apiType });
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<SplashScreenButton
|
<SplashScreenButton
|
||||||
imgSrc={ConnectIcon}
|
imgSrc={ConnectIcon}
|
||||||
@@ -246,8 +259,9 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
|
|||||||
<form className="connectExplorerFormContainer">
|
<form className="connectExplorerFormContainer">
|
||||||
<div className="splashScreenContainer">
|
<div className="splashScreenContainer">
|
||||||
<div className="splashScreen">
|
<div className="splashScreen">
|
||||||
<div
|
<h1
|
||||||
className="title"
|
className="title"
|
||||||
|
role="heading"
|
||||||
aria-label={
|
aria-label={
|
||||||
userContext.apiType === "Postgres"
|
userContext.apiType === "Postgres"
|
||||||
? "Welcome to Azure Cosmos DB for PostgreSQL"
|
? "Welcome to Azure Cosmos DB for PostgreSQL"
|
||||||
@@ -258,7 +272,7 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
|
|||||||
? "Welcome to Azure Cosmos DB for PostgreSQL"
|
? "Welcome to Azure Cosmos DB for PostgreSQL"
|
||||||
: "Welcome to Azure Cosmos DB"}
|
: "Welcome to Azure Cosmos DB"}
|
||||||
<FeaturePanelLauncher />
|
<FeaturePanelLauncher />
|
||||||
</div>
|
</h1>
|
||||||
<div className="subtitle">
|
<div className="subtitle">
|
||||||
{userContext.apiType === "Postgres"
|
{userContext.apiType === "Postgres"
|
||||||
? "Get started with our sample datasets, documentation, and additional tools."
|
? "Get started with our sample datasets, documentation, and additional tools."
|
||||||
|
|||||||
@@ -6,16 +6,16 @@ import DiscardIcon from "../../../images/discard.svg";
|
|||||||
import SaveIcon from "../../../images/save-cosmos.svg";
|
import SaveIcon from "../../../images/save-cosmos.svg";
|
||||||
import * as Constants from "../../Common/Constants";
|
import * as Constants from "../../Common/Constants";
|
||||||
import { DocumentsGridMetrics, KeyCodes } from "../../Common/Constants";
|
import { DocumentsGridMetrics, KeyCodes } from "../../Common/Constants";
|
||||||
import { createDocument } from "../../Common/dataAccess/createDocument";
|
|
||||||
import { deleteConflict } from "../../Common/dataAccess/deleteConflict";
|
|
||||||
import { deleteDocument } from "../../Common/dataAccess/deleteDocument";
|
|
||||||
import { queryConflicts } from "../../Common/dataAccess/queryConflicts";
|
|
||||||
import { updateDocument } from "../../Common/dataAccess/updateDocument";
|
|
||||||
import editable from "../../Common/EditableUtility";
|
import editable from "../../Common/EditableUtility";
|
||||||
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
|
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
|
||||||
import * as HeadersUtility from "../../Common/HeadersUtility";
|
import * as HeadersUtility from "../../Common/HeadersUtility";
|
||||||
import { MinimalQueryIterator } from "../../Common/IteratorUtilities";
|
import { MinimalQueryIterator } from "../../Common/IteratorUtilities";
|
||||||
import { Splitter, SplitterBounds, SplitterDirection } from "../../Common/Splitter";
|
import { Splitter, SplitterBounds, SplitterDirection } from "../../Common/Splitter";
|
||||||
|
import { createDocument } from "../../Common/dataAccess/createDocument";
|
||||||
|
import { deleteConflict } from "../../Common/dataAccess/deleteConflict";
|
||||||
|
import { deleteDocument } from "../../Common/dataAccess/deleteDocument";
|
||||||
|
import { queryConflicts } from "../../Common/dataAccess/queryConflicts";
|
||||||
|
import { updateDocument } from "../../Common/dataAccess/updateDocument";
|
||||||
import * as DataModels from "../../Contracts/DataModels";
|
import * as DataModels from "../../Contracts/DataModels";
|
||||||
import * as ViewModels from "../../Contracts/ViewModels";
|
import * as ViewModels from "../../Contracts/ViewModels";
|
||||||
import { Action } from "../../Shared/Telemetry/TelemetryConstants";
|
import { Action } from "../../Shared/Telemetry/TelemetryConstants";
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { stringifyNotebook, toJS } from "@nteract/commutable";
|
import { stringifyNotebook, toJS } from "@nteract/commutable";
|
||||||
|
import * as createDivider from "Explorer/Menus/CommandBar/CommandButtonsFactories/createDivider";
|
||||||
|
import { userContext } from "UserContext";
|
||||||
import * as ko from "knockout";
|
import * as ko from "knockout";
|
||||||
import * as Q from "q";
|
import * as Q from "q";
|
||||||
import { userContext } from "UserContext";
|
|
||||||
import ClearAllOutputsIcon from "../../../images/notebook/Notebook-clear-all-outputs.svg";
|
import ClearAllOutputsIcon from "../../../images/notebook/Notebook-clear-all-outputs.svg";
|
||||||
import CopyIcon from "../../../images/notebook/Notebook-copy.svg";
|
import CopyIcon from "../../../images/notebook/Notebook-copy.svg";
|
||||||
import CutIcon from "../../../images/notebook/Notebook-cut.svg";
|
import CutIcon from "../../../images/notebook/Notebook-cut.svg";
|
||||||
@@ -12,17 +13,16 @@ import RunAllIcon from "../../../images/notebook/Notebook-run-all.svg";
|
|||||||
import RunIcon from "../../../images/notebook/Notebook-run.svg";
|
import RunIcon from "../../../images/notebook/Notebook-run.svg";
|
||||||
import { default as InterruptKernelIcon, default as KillKernelIcon } from "../../../images/notebook/Notebook-stop.svg";
|
import { default as InterruptKernelIcon, default as KillKernelIcon } from "../../../images/notebook/Notebook-stop.svg";
|
||||||
import SaveIcon from "../../../images/save-cosmos.svg";
|
import SaveIcon from "../../../images/save-cosmos.svg";
|
||||||
import { useNotebookSnapshotStore } from "../../hooks/useNotebookSnapshotStore";
|
|
||||||
import { Action, ActionModifiers, Source } from "../../Shared/Telemetry/TelemetryConstants";
|
import { Action, ActionModifiers, Source } from "../../Shared/Telemetry/TelemetryConstants";
|
||||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||||
import * as NotebookConfigurationUtils from "../../Utils/NotebookConfigurationUtils";
|
import * as NotebookConfigurationUtils from "../../Utils/NotebookConfigurationUtils";
|
||||||
import { logConsoleInfo } from "../../Utils/NotificationConsoleUtils";
|
import { logConsoleInfo } from "../../Utils/NotificationConsoleUtils";
|
||||||
|
import { useNotebookSnapshotStore } from "../../hooks/useNotebookSnapshotStore";
|
||||||
import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent";
|
import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent";
|
||||||
import { useDialog } from "../Controls/Dialog";
|
import { useDialog } from "../Controls/Dialog";
|
||||||
import * as CommandBarComponentButtonFactory from "../Menus/CommandBar/CommandBarComponentButtonFactory";
|
|
||||||
import { KernelSpecsDisplay } from "../Notebook/NotebookClientV2";
|
import { KernelSpecsDisplay } from "../Notebook/NotebookClientV2";
|
||||||
import * as CdbActions from "../Notebook/NotebookComponent/actions";
|
|
||||||
import { NotebookComponentAdapter } from "../Notebook/NotebookComponent/NotebookComponentAdapter";
|
import { NotebookComponentAdapter } from "../Notebook/NotebookComponent/NotebookComponentAdapter";
|
||||||
|
import * as CdbActions from "../Notebook/NotebookComponent/actions";
|
||||||
import { CdbAppState, SnapshotRequest } from "../Notebook/NotebookComponent/types";
|
import { CdbAppState, SnapshotRequest } from "../Notebook/NotebookComponent/types";
|
||||||
import { NotebookContentItem } from "../Notebook/NotebookContentItem";
|
import { NotebookContentItem } from "../Notebook/NotebookContentItem";
|
||||||
import { NotebookUtil } from "../Notebook/NotebookUtil";
|
import { NotebookUtil } from "../Notebook/NotebookUtil";
|
||||||
@@ -118,7 +118,7 @@ export default class NotebookTabV2 extends NotebookTabBase {
|
|||||||
const cellMarkdownType = "markdown";
|
const cellMarkdownType = "markdown";
|
||||||
const cellRawType = "raw";
|
const cellRawType = "raw";
|
||||||
|
|
||||||
const saveButtonChildren = [];
|
const saveButtonChildren: CommandButtonComponentProps[] = [];
|
||||||
if (this.container.notebookManager?.gitHubOAuthService.isLoggedIn()) {
|
if (this.container.notebookManager?.gitHubOAuthService.isLoggedIn()) {
|
||||||
saveButtonChildren.push({
|
saveButtonChildren.push({
|
||||||
iconName: copyToLabel,
|
iconName: copyToLabel,
|
||||||
@@ -272,7 +272,7 @@ export default class NotebookTabV2 extends NotebookTabBase {
|
|||||||
hasPopup: false,
|
hasPopup: false,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
},
|
},
|
||||||
CommandBarComponentButtonFactory.createDivider(),
|
createDivider.createDivider(),
|
||||||
{
|
{
|
||||||
iconSrc: null,
|
iconSrc: null,
|
||||||
iconAlt: null,
|
iconAlt: null,
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import ExecuteQueryIcon from "../../../images/ExecuteQuery.svg";
|
|||||||
import QueryBuilderIcon from "../../../images/Query-Builder.svg";
|
import QueryBuilderIcon from "../../../images/Query-Builder.svg";
|
||||||
import QueryTextIcon from "../../../images/Query-Text.svg";
|
import QueryTextIcon from "../../../images/Query-Text.svg";
|
||||||
import * as ViewModels from "../../Contracts/ViewModels";
|
import * as ViewModels from "../../Contracts/ViewModels";
|
||||||
import { useSidePanel } from "../../hooks/useSidePanel";
|
|
||||||
import { userContext } from "../../UserContext";
|
import { userContext } from "../../UserContext";
|
||||||
|
import { useSidePanel } from "../../hooks/useSidePanel";
|
||||||
import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent";
|
import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent";
|
||||||
import Explorer from "../Explorer";
|
import Explorer from "../Explorer";
|
||||||
import { AddTableEntityPanel } from "../Panes/Tables/AddTableEntityPanel";
|
import { AddTableEntityPanel } from "../Panes/Tables/AddTableEntityPanel";
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { Resource, StoredProcedureDefinition } from "@azure/cosmos";
|
import { Resource, StoredProcedureDefinition } from "@azure/cosmos";
|
||||||
import { Pivot, PivotItem } from "@fluentui/react";
|
import { Pivot, PivotItem } from "@fluentui/react";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import DiscardIcon from "../../../../images/discard.svg";
|
|
||||||
import ExecuteQueryIcon from "../../../../images/ExecuteQuery.svg";
|
import ExecuteQueryIcon from "../../../../images/ExecuteQuery.svg";
|
||||||
|
import DiscardIcon from "../../../../images/discard.svg";
|
||||||
import SaveIcon from "../../../../images/save-cosmos.svg";
|
import SaveIcon from "../../../../images/save-cosmos.svg";
|
||||||
import { NormalizedEventKey } from "../../../Common/Constants";
|
import { NormalizedEventKey } from "../../../Common/Constants";
|
||||||
import { createStoredProcedure } from "../../../Common/dataAccess/createStoredProcedure";
|
import { createStoredProcedure } from "../../../Common/dataAccess/createStoredProcedure";
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { ConnectTab } from "Explorer/Tabs/ConnectTab";
|
|||||||
import { PostgresConnectTab } from "Explorer/Tabs/PostgresConnectTab";
|
import { PostgresConnectTab } from "Explorer/Tabs/PostgresConnectTab";
|
||||||
import { QuickstartTab } from "Explorer/Tabs/QuickstartTab";
|
import { QuickstartTab } from "Explorer/Tabs/QuickstartTab";
|
||||||
import { userContext } from "UserContext";
|
import { userContext } from "UserContext";
|
||||||
|
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||||
import { useTeachingBubble } from "hooks/useTeachingBubble";
|
import { useTeachingBubble } from "hooks/useTeachingBubble";
|
||||||
import ko from "knockout";
|
import ko from "knockout";
|
||||||
import React, { MutableRefObject, useEffect, useRef, useState } from "react";
|
import React, { MutableRefObject, useEffect, useRef, useState } from "react";
|
||||||
@@ -158,6 +159,7 @@ const CloseButton = ({
|
|||||||
onClick={(event: React.MouseEvent<HTMLSpanElement, MouseEvent>) => {
|
onClick={(event: React.MouseEvent<HTMLSpanElement, MouseEvent>) => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
tab ? tab.onCloseTabButtonClick() : useTabs.getState().closeReactTab(tabKind);
|
tab ? tab.onCloseTabButtonClick() : useTabs.getState().closeReactTab(tabKind);
|
||||||
|
tabKind === ReactTabKind.QueryCopilot && useQueryCopilot.getState().resetQueryCopilotStates();
|
||||||
}}
|
}}
|
||||||
tabIndex={active ? 0 : undefined}
|
tabIndex={active ? 0 : undefined}
|
||||||
onKeyPress={({ nativeEvent: e }) => tab.onKeyPressClose(undefined, e)}
|
onKeyPress={({ nativeEvent: e }) => tab.onKeyPressClose(undefined, e)}
|
||||||
@@ -251,7 +253,7 @@ const getReactTabContent = (activeReactTab: ReactTabKind, explorer: Explorer): J
|
|||||||
case ReactTabKind.Quickstart:
|
case ReactTabKind.Quickstart:
|
||||||
return <QuickstartTab explorer={explorer} />;
|
return <QuickstartTab explorer={explorer} />;
|
||||||
case ReactTabKind.QueryCopilot:
|
case ReactTabKind.QueryCopilot:
|
||||||
return <QueryCopilotTab initialInput={useTabs.getState().queryCopilotTabInitialInput} explorer={explorer} />;
|
return <QueryCopilotTab explorer={explorer} />;
|
||||||
default:
|
default:
|
||||||
throw Error(`Unsupported tab kind ${ReactTabKind[activeReactTab]}`);
|
throw Error(`Unsupported tab kind ${ReactTabKind[activeReactTab]}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ import React, { Component } from "react";
|
|||||||
import DiscardIcon from "../../../images/discard.svg";
|
import DiscardIcon from "../../../images/discard.svg";
|
||||||
import SaveIcon from "../../../images/save-cosmos.svg";
|
import SaveIcon from "../../../images/save-cosmos.svg";
|
||||||
import * as Constants from "../../Common/Constants";
|
import * as Constants from "../../Common/Constants";
|
||||||
|
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
|
||||||
import { createTrigger } from "../../Common/dataAccess/createTrigger";
|
import { createTrigger } from "../../Common/dataAccess/createTrigger";
|
||||||
import { updateTrigger } from "../../Common/dataAccess/updateTrigger";
|
import { updateTrigger } from "../../Common/dataAccess/updateTrigger";
|
||||||
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
|
|
||||||
import * as ViewModels from "../../Contracts/ViewModels";
|
import * as ViewModels from "../../Contracts/ViewModels";
|
||||||
import { Action } from "../../Shared/Telemetry/TelemetryConstants";
|
import { Action } from "../../Shared/Telemetry/TelemetryConstants";
|
||||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ import React, { Component } from "react";
|
|||||||
import DiscardIcon from "../../../images/discard.svg";
|
import DiscardIcon from "../../../images/discard.svg";
|
||||||
import SaveIcon from "../../../images/save-cosmos.svg";
|
import SaveIcon from "../../../images/save-cosmos.svg";
|
||||||
import * as Constants from "../../Common/Constants";
|
import * as Constants from "../../Common/Constants";
|
||||||
|
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
|
||||||
import { createUserDefinedFunction } from "../../Common/dataAccess/createUserDefinedFunction";
|
import { createUserDefinedFunction } from "../../Common/dataAccess/createUserDefinedFunction";
|
||||||
import { updateUserDefinedFunction } from "../../Common/dataAccess/updateUserDefinedFunction";
|
import { updateUserDefinedFunction } from "../../Common/dataAccess/updateUserDefinedFunction";
|
||||||
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
|
|
||||||
import * as ViewModels from "../../Contracts/ViewModels";
|
import * as ViewModels from "../../Contracts/ViewModels";
|
||||||
import { Action } from "../../Shared/Telemetry/TelemetryConstants";
|
import { Action } from "../../Shared/Telemetry/TelemetryConstants";
|
||||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export const SampleDataTree = ({
|
|||||||
const buildSampleDataTree = (): TreeNode => {
|
const buildSampleDataTree = (): TreeNode => {
|
||||||
const updatedSampleTree: TreeNode = {
|
const updatedSampleTree: TreeNode = {
|
||||||
label: sampleDataResourceTokenCollection.databaseId,
|
label: sampleDataResourceTokenCollection.databaseId,
|
||||||
isExpanded: true,
|
isExpanded: false,
|
||||||
iconSrc: CosmosDBIcon,
|
iconSrc: CosmosDBIcon,
|
||||||
className: "databaseHeader",
|
className: "databaseHeader",
|
||||||
children: [
|
children: [
|
||||||
@@ -47,6 +47,7 @@ export const SampleDataTree = ({
|
|||||||
{
|
{
|
||||||
label: "Items",
|
label: "Items",
|
||||||
onClick: () => sampleDataResourceTokenCollection.onDocumentDBDocumentsClick(),
|
onClick: () => sampleDataResourceTokenCollection.onDocumentDBDocumentsClick(),
|
||||||
|
contextMenu: ResourceTreeContextMenuButtonFactory.createSampleCollectionContextMenuButton(),
|
||||||
isSelected: () =>
|
isSelected: () =>
|
||||||
useSelectedNode
|
useSelectedNode
|
||||||
.getState()
|
.getState()
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { CommandButtonComponent } from "../../../Explorer/Controls/CommandButton/CommandButtonComponent";
|
|
||||||
import FeedbackIcon from "../../../../images/Feedback.svg";
|
import FeedbackIcon from "../../../../images/Feedback.svg";
|
||||||
|
import { CommandButtonComponent } from "../../../Explorer/Controls/CommandButton/CommandButtonComponent";
|
||||||
|
|
||||||
export const FeedbackCommandButton: React.FunctionComponent = () => {
|
export const FeedbackCommandButton: React.FunctionComponent = () => {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ export type Features = {
|
|||||||
readonly enableLegacyMongoShellV2Debug: boolean;
|
readonly enableLegacyMongoShellV2Debug: boolean;
|
||||||
readonly loadLegacyMongoShellFromBE: boolean;
|
readonly loadLegacyMongoShellFromBE: boolean;
|
||||||
readonly enableCopilot: boolean;
|
readonly enableCopilot: boolean;
|
||||||
|
readonly enableNPSSurvey: boolean;
|
||||||
|
|
||||||
// can be set via both flight and feature flag
|
// can be set via both flight and feature flag
|
||||||
autoscaleDefault: boolean;
|
autoscaleDefault: boolean;
|
||||||
@@ -104,6 +105,7 @@ export function extractFeatures(given = new URLSearchParams(window.location.sear
|
|||||||
enableLegacyMongoShellV2Debug: "true" === get("enablelegacymongoshellv2debug"),
|
enableLegacyMongoShellV2Debug: "true" === get("enablelegacymongoshellv2debug"),
|
||||||
loadLegacyMongoShellFromBE: "true" === get("loadlegacymongoshellfrombe"),
|
loadLegacyMongoShellFromBE: "true" === get("loadlegacymongoshellfrombe"),
|
||||||
enableCopilot: "true" === get("enablecopilot"),
|
enableCopilot: "true" === get("enablecopilot"),
|
||||||
|
enableNPSSurvey: "true" === get("enablenpssurvey"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -131,6 +131,9 @@ export enum Action {
|
|||||||
LaunchUITour,
|
LaunchUITour,
|
||||||
CancelUITour,
|
CancelUITour,
|
||||||
CompleteUITour,
|
CompleteUITour,
|
||||||
|
OpenQueryCopilotFromSplashScreen,
|
||||||
|
OpenQueryCopilotFromNewQuery,
|
||||||
|
ExecuteQueryGeneratedFromQueryCopilot,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ActionModifiers = {
|
export const ActionModifiers = {
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ const userContext: UserContext = {
|
|||||||
collectionCreationDefaults: CollectionCreationDefaults,
|
collectionCreationDefaults: CollectionCreationDefaults,
|
||||||
};
|
};
|
||||||
|
|
||||||
function isAccountNewerThanThresholdInMs(createdAt: string, threshold: number) {
|
export function isAccountNewerThanThresholdInMs(createdAt: string, threshold: number) {
|
||||||
let createdAtMs: number = Date.parse(createdAt);
|
let createdAtMs: number = Date.parse(createdAt);
|
||||||
if (isNaN(createdAtMs)) {
|
if (isNaN(createdAtMs)) {
|
||||||
createdAtMs = 0;
|
createdAtMs = 0;
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ function validateEndpointInternal(
|
|||||||
return valid;
|
return valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const allowedArmEndpoints: ReadonlyArray<string> = [
|
export const defaultAllowedArmEndpoints: ReadonlyArray<string> = [
|
||||||
"https://management.azure.com",
|
"https://management.azure.com",
|
||||||
"https://management.usgovcloudapi.net",
|
"https://management.usgovcloudapi.net",
|
||||||
"https://management.chinacloudapi.cn",
|
"https://management.chinacloudapi.cn",
|
||||||
@@ -46,7 +46,7 @@ export const allowedArmEndpoints: ReadonlyArray<string> = [
|
|||||||
|
|
||||||
export const allowedAadEndpoints: ReadonlyArray<string> = ["https://login.microsoftonline.com/"];
|
export const allowedAadEndpoints: ReadonlyArray<string> = ["https://login.microsoftonline.com/"];
|
||||||
|
|
||||||
export const allowedBackendEndpoints: ReadonlyArray<string> = [
|
export const defaultAllowedBackendEndpoints: ReadonlyArray<string> = [
|
||||||
"https://main.documentdb.ext.azure.com",
|
"https://main.documentdb.ext.azure.com",
|
||||||
"https://main.documentdb.ext.azure.cn",
|
"https://main.documentdb.ext.azure.cn",
|
||||||
"https://main.documentdb.ext.azure.us",
|
"https://main.documentdb.ext.azure.us",
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
import { MinimalQueryIterator } from "Common/IteratorUtilities";
|
||||||
|
import { QueryResults } from "Contracts/ViewModels";
|
||||||
|
import { guid } from "Explorer/Tables/Utilities";
|
||||||
import create, { UseStore } from "zustand";
|
import create, { UseStore } from "zustand";
|
||||||
|
|
||||||
interface QueryCopilotState {
|
interface QueryCopilotState {
|
||||||
@@ -6,20 +9,127 @@ interface QueryCopilotState {
|
|||||||
userPrompt: string;
|
userPrompt: string;
|
||||||
showFeedbackModal: boolean;
|
showFeedbackModal: boolean;
|
||||||
hideFeedbackModalForLikedQueries: boolean;
|
hideFeedbackModalForLikedQueries: boolean;
|
||||||
|
correlationId: string;
|
||||||
|
query: string;
|
||||||
|
selectedQuery: string;
|
||||||
|
isGeneratingQuery: boolean;
|
||||||
|
isExecuting: boolean;
|
||||||
|
dislikeQuery: boolean | undefined;
|
||||||
|
showCallout: boolean;
|
||||||
|
showSamplePrompts: boolean;
|
||||||
|
queryIterator: MinimalQueryIterator | undefined;
|
||||||
|
queryResults: QueryResults | undefined;
|
||||||
|
errorMessage: string;
|
||||||
|
isSamplePromptsOpen: boolean;
|
||||||
|
showDeletePopup: boolean;
|
||||||
|
showFeedbackBar: boolean;
|
||||||
|
showCopyPopup: boolean;
|
||||||
|
showErrorMessageBar: boolean;
|
||||||
|
generatedQueryComments: string;
|
||||||
|
|
||||||
openFeedbackModal: (generatedQuery: string, likeQuery: boolean, userPrompt: string) => void;
|
openFeedbackModal: (generatedQuery: string, likeQuery: boolean, userPrompt: string) => void;
|
||||||
closeFeedbackModal: () => void;
|
closeFeedbackModal: () => void;
|
||||||
setHideFeedbackModalForLikedQueries: (hideFeedbackModalForLikedQueries: boolean) => void;
|
setHideFeedbackModalForLikedQueries: (hideFeedbackModalForLikedQueries: boolean) => void;
|
||||||
|
refreshCorrelationId: () => void;
|
||||||
|
setUserPrompt: (userPrompt: string) => void;
|
||||||
|
setQuery: (query: string) => void;
|
||||||
|
setGeneratedQuery: (generatedQuery: string) => void;
|
||||||
|
setSelectedQuery: (selectedQuery: string) => void;
|
||||||
|
setIsGeneratingQuery: (isGeneratingQuery: boolean) => void;
|
||||||
|
setIsExecuting: (isExecuting: boolean) => void;
|
||||||
|
setLikeQuery: (likeQuery: boolean) => void;
|
||||||
|
setDislikeQuery: (dislikeQuery: boolean | undefined) => void;
|
||||||
|
setShowCallout: (showCallout: boolean) => void;
|
||||||
|
setShowSamplePrompts: (showSamplePrompts: boolean) => void;
|
||||||
|
setQueryIterator: (queryIterator: MinimalQueryIterator | undefined) => void;
|
||||||
|
setQueryResults: (queryResults: QueryResults | undefined) => void;
|
||||||
|
setErrorMessage: (errorMessage: string) => void;
|
||||||
|
setIsSamplePromptsOpen: (isSamplePromptsOpen: boolean) => void;
|
||||||
|
setShowDeletePopup: (showDeletePopup: boolean) => void;
|
||||||
|
setShowFeedbackBar: (showFeedbackBar: boolean) => void;
|
||||||
|
setshowCopyPopup: (showCopyPopup: boolean) => void;
|
||||||
|
setShowErrorMessageBar: (showErrorMessageBar: boolean) => void;
|
||||||
|
setGeneratedQueryComments: (generatedQueryComments: string) => void;
|
||||||
|
resetQueryCopilotStates: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useQueryCopilot: UseStore<QueryCopilotState> = create((set) => ({
|
type QueryCopilotStore = UseStore<QueryCopilotState>;
|
||||||
|
|
||||||
|
export const useQueryCopilot: QueryCopilotStore = create((set) => ({
|
||||||
generatedQuery: "",
|
generatedQuery: "",
|
||||||
likeQuery: false,
|
likeQuery: false,
|
||||||
userPrompt: "",
|
userPrompt: "",
|
||||||
showFeedbackModal: false,
|
showFeedbackModal: false,
|
||||||
hideFeedbackModalForLikedQueries: false,
|
hideFeedbackModalForLikedQueries: false,
|
||||||
|
correlationId: "",
|
||||||
|
query: "",
|
||||||
|
selectedQuery: "",
|
||||||
|
isGeneratingQuery: false,
|
||||||
|
isExecuting: false,
|
||||||
|
dislikeQuery: undefined,
|
||||||
|
showCallout: false,
|
||||||
|
showSamplePrompts: false,
|
||||||
|
queryIterator: undefined,
|
||||||
|
queryResults: undefined,
|
||||||
|
errorMessage: "",
|
||||||
|
isSamplePromptsOpen: false,
|
||||||
|
showDeletePopup: false,
|
||||||
|
showFeedbackBar: false,
|
||||||
|
showCopyPopup: false,
|
||||||
|
showErrorMessageBar: false,
|
||||||
|
generatedQueryComments: "",
|
||||||
|
|
||||||
openFeedbackModal: (generatedQuery: string, likeQuery: boolean, userPrompt: string) =>
|
openFeedbackModal: (generatedQuery: string, likeQuery: boolean, userPrompt: string) =>
|
||||||
set({ generatedQuery, likeQuery, userPrompt, showFeedbackModal: true }),
|
set({ generatedQuery, likeQuery, userPrompt, showFeedbackModal: true }),
|
||||||
closeFeedbackModal: () => set({ generatedQuery: "", likeQuery: false, userPrompt: "", showFeedbackModal: false }),
|
closeFeedbackModal: () => set({ generatedQuery: "", likeQuery: false, userPrompt: "", showFeedbackModal: false }),
|
||||||
setHideFeedbackModalForLikedQueries: (hideFeedbackModalForLikedQueries: boolean) =>
|
setHideFeedbackModalForLikedQueries: (hideFeedbackModalForLikedQueries: boolean) =>
|
||||||
set({ hideFeedbackModalForLikedQueries }),
|
set({ hideFeedbackModalForLikedQueries }),
|
||||||
|
refreshCorrelationId: () => set({ correlationId: guid() }),
|
||||||
|
setUserPrompt: (userPrompt: string) => set({ userPrompt }),
|
||||||
|
setQuery: (query: string) => set({ query }),
|
||||||
|
setGeneratedQuery: (generatedQuery: string) => set({ generatedQuery }),
|
||||||
|
setSelectedQuery: (selectedQuery: string) => set({ selectedQuery }),
|
||||||
|
setIsGeneratingQuery: (isGeneratingQuery: boolean) => set({ isGeneratingQuery }),
|
||||||
|
setIsExecuting: (isExecuting: boolean) => set({ isExecuting }),
|
||||||
|
setLikeQuery: (likeQuery: boolean) => set({ likeQuery }),
|
||||||
|
setDislikeQuery: (dislikeQuery: boolean | undefined) => set({ dislikeQuery }),
|
||||||
|
setShowCallout: (showCallout: boolean) => set({ showCallout }),
|
||||||
|
setShowSamplePrompts: (showSamplePrompts: boolean) => set({ showSamplePrompts }),
|
||||||
|
setQueryIterator: (queryIterator: MinimalQueryIterator | undefined) => set({ queryIterator }),
|
||||||
|
setQueryResults: (queryResults: QueryResults | undefined) => set({ queryResults }),
|
||||||
|
setErrorMessage: (errorMessage: string) => set({ errorMessage }),
|
||||||
|
setIsSamplePromptsOpen: (isSamplePromptsOpen: boolean) => set({ isSamplePromptsOpen }),
|
||||||
|
setShowDeletePopup: (showDeletePopup: boolean) => set({ showDeletePopup }),
|
||||||
|
setShowFeedbackBar: (showFeedbackBar: boolean) => set({ showFeedbackBar }),
|
||||||
|
setshowCopyPopup: (showCopyPopup: boolean) => set({ showCopyPopup }),
|
||||||
|
setShowErrorMessageBar: (showErrorMessageBar: boolean) => set({ showErrorMessageBar }),
|
||||||
|
setGeneratedQueryComments: (generatedQueryComments: string) => set({ generatedQueryComments }),
|
||||||
|
|
||||||
|
resetQueryCopilotStates: () => {
|
||||||
|
set((state) => ({
|
||||||
|
...state,
|
||||||
|
generatedQuery: "",
|
||||||
|
likeQuery: false,
|
||||||
|
userPrompt: "",
|
||||||
|
showFeedbackModal: false,
|
||||||
|
hideFeedbackModalForLikedQueries: false,
|
||||||
|
correlationId: "",
|
||||||
|
query: "",
|
||||||
|
selectedQuery: "",
|
||||||
|
isGeneratingQuery: false,
|
||||||
|
isExecuting: false,
|
||||||
|
dislikeQuery: undefined,
|
||||||
|
showCallout: false,
|
||||||
|
showSamplePrompts: false,
|
||||||
|
queryIterator: undefined,
|
||||||
|
queryResults: undefined,
|
||||||
|
errorMessage: "",
|
||||||
|
isSamplePromptsOpen: false,
|
||||||
|
showDeletePopup: false,
|
||||||
|
showFeedbackBar: false,
|
||||||
|
showCopyPopup: false,
|
||||||
|
showErrorMessageBar: false,
|
||||||
|
generatedQueryComments: "",
|
||||||
|
}));
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
|
|||||||
Reference in New Issue
Block a user