mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2026-01-05 18:47:41 +00:00
Compare commits
1 Commits
ashleyst/m
...
master_clo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e7680c6c9e |
@@ -2296,17 +2296,6 @@ a:link {
|
|||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.monaco-editor .quick-input-list-label {
|
|
||||||
/* Restore some of Monaco's default styles that are clobbered by our global styles */
|
|
||||||
padding: 0;
|
|
||||||
line-height: 22px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.monaco-editor .quick-input-list .highlight {
|
|
||||||
/* Padding in highlighted text within the quick input list breaks the flow of the text */
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
td a {
|
td a {
|
||||||
color: #393939;
|
color: #393939;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -124,34 +124,6 @@ export enum MongoBackendEndpointType {
|
|||||||
remote,
|
remote,
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum BackendApi {
|
|
||||||
GenerateToken,
|
|
||||||
}
|
|
||||||
|
|
||||||
export class PortalBackendEndpoints {
|
|
||||||
public static readonly Development: string = "https://localhost:7235";
|
|
||||||
public static readonly Mpac: string = "https://cdb-ms-mpac-pbe.cosmos.azure.com";
|
|
||||||
public static readonly Prod: string = "https://cdb-ms-prod-pbe.cosmos.azure.com";
|
|
||||||
public static readonly Fairfax: string = "https://cdb-ff-prod-pbe.cosmos.azure.us";
|
|
||||||
public static readonly Mooncake: string = "https://cdb-mc-prod-pbe.cosmos.azure.cn";
|
|
||||||
}
|
|
||||||
|
|
||||||
export class MongoProxyEndpoints {
|
|
||||||
public static readonly Development: string = "https://localhost:7238";
|
|
||||||
public static readonly Mpac: string = "https://cdb-ms-mpac-mp.cosmos.azure.com";
|
|
||||||
public static readonly Prod: string = "https://cdb-ms-prod-mp.cosmos.azure.com";
|
|
||||||
public static readonly Fairfax: string = "https://cdb-ff-prod-mp.cosmos.azure.us";
|
|
||||||
public static readonly Mooncake: string = "https://cdb-mc-prod-mp.cosmos.azure.cn";
|
|
||||||
}
|
|
||||||
|
|
||||||
export class CassandraProxyEndpoints {
|
|
||||||
public static readonly Development: string = "https://localhost:7240";
|
|
||||||
public static readonly Mpac: string = "https://cdb-ms-mpac-cp.cosmos.azure.com";
|
|
||||||
public static readonly Prod: string = "https://cdb-ms-prod-cp.cosmos.azure.com";
|
|
||||||
public static readonly Fairfax: string = "https://cdb-ff-prod-cp.cosmos.azure.us";
|
|
||||||
public static readonly Mooncake: string = "https://cdb-mc-prod-cp.cosmos.azure.cn";
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: Remove this when new backend is migrated over
|
//TODO: Remove this when new backend is migrated over
|
||||||
export class CassandraBackend {
|
export class CassandraBackend {
|
||||||
public static readonly createOrDeleteApi: string = "api/cassandra/createordelete";
|
public static readonly createOrDeleteApi: string = "api/cassandra/createordelete";
|
||||||
@@ -167,7 +139,7 @@ export class CassandraBackend {
|
|||||||
export class CassandraProxyAPIs {
|
export class CassandraProxyAPIs {
|
||||||
public static readonly createOrDeleteApi: string = "api/cassandra/createordelete";
|
public static readonly createOrDeleteApi: string = "api/cassandra/createordelete";
|
||||||
public static readonly connectionStringCreateOrDeleteApi: string = "api/connectionstring/cassandra/createordelete";
|
public static readonly connectionStringCreateOrDeleteApi: string = "api/connectionstring/cassandra/createordelete";
|
||||||
public static readonly queryApi: string = "api/cassandra";
|
public static readonly queryApi: string = "api/cassandra/postquery";
|
||||||
public static readonly connectionStringQueryApi: string = "api/connectionstring/cassandra";
|
public static readonly connectionStringQueryApi: string = "api/connectionstring/cassandra";
|
||||||
public static readonly keysApi: string = "api/cassandra/keys";
|
public static readonly keysApi: string = "api/cassandra/keys";
|
||||||
public static readonly connectionStringKeysApi: string = "api/connectionstring/cassandra/keys";
|
public static readonly connectionStringKeysApi: string = "api/connectionstring/cassandra/keys";
|
||||||
@@ -474,6 +446,22 @@ export class JunoEndpoints {
|
|||||||
public static readonly Stage = "https://tools-staging.cosmos.azure.com";
|
public static readonly Stage = "https://tools-staging.cosmos.azure.com";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class MongoProxyEndpoints {
|
||||||
|
public static readonly Development: string = "https://localhost:7238";
|
||||||
|
public static readonly Mpac: string = "https://cdb-ms-mpac-mp.cosmos.azure.com";
|
||||||
|
public static readonly Prod: string = "https://cdb-ms-prod-mp.cosmos.azure.com";
|
||||||
|
public static readonly Fairfax: string = "https://cdb-ff-prod-mp.cosmos.azure.us";
|
||||||
|
public static readonly Mooncake: string = "https://cdb-mc-prod-mp.cosmos.azure.cn";
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CassandraProxyEndpoints {
|
||||||
|
public static readonly Development: string = "https://localhost:7240";
|
||||||
|
public static readonly Mpac: string = "https://cdb-ms-mpac-cp.cosmos.azure.com";
|
||||||
|
public static readonly Prod: string = "https://cdb-ms-prod-cp.cosmos.azure.com";
|
||||||
|
public static readonly Fairfax: string = "https://cdb-ff-prod-cp.cosmos.azure.us";
|
||||||
|
public static readonly Mooncake: string = "https://cdb-mc-prod-cp.cosmos.azure.cn";
|
||||||
|
}
|
||||||
|
|
||||||
export class PriorityLevel {
|
export class PriorityLevel {
|
||||||
public static readonly High = "high";
|
public static readonly High = "high";
|
||||||
public static readonly Low = "low";
|
public static readonly Low = "low";
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import * as Cosmos from "@azure/cosmos";
|
import * as Cosmos from "@azure/cosmos";
|
||||||
|
import { sendCachedDataMessage } from "Common/MessageHandler";
|
||||||
import { getAuthorizationTokenUsingResourceTokens } from "Common/getAuthorizationTokenUsingResourceTokens";
|
import { getAuthorizationTokenUsingResourceTokens } from "Common/getAuthorizationTokenUsingResourceTokens";
|
||||||
import { AuthorizationToken } from "Contracts/MessageTypes";
|
import { AuthorizationToken, MessageTypes } from "Contracts/MessageTypes";
|
||||||
import { checkDatabaseResourceTokensValidity } from "Platform/Fabric/FabricUtil";
|
import { checkDatabaseResourceTokensValidity } from "Platform/Fabric/FabricUtil";
|
||||||
import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
|
import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
|
||||||
import { AuthType } from "../AuthType";
|
import { AuthType } from "../AuthType";
|
||||||
@@ -50,23 +51,15 @@ export const tokenProvider = async (requestInfo: Cosmos.RequestInfo) => {
|
|||||||
case Cosmos.ResourceType.offer:
|
case Cosmos.ResourceType.offer:
|
||||||
case Cosmos.ResourceType.user:
|
case Cosmos.ResourceType.user:
|
||||||
case Cosmos.ResourceType.permission:
|
case Cosmos.ResourceType.permission:
|
||||||
// For now, these operations aren't used, so fetching the authorization token is commented out.
|
// User master tokens
|
||||||
// This provider must return a real token to pass validation by the client, so we return the cached resource token
|
const authorizationToken = await sendCachedDataMessage<AuthorizationToken>(
|
||||||
// (which is a valid token, but won't work for these operations).
|
MessageTypes.GetAuthorizationToken,
|
||||||
const resourceTokens2 = userContext.fabricContext.databaseConnectionInfo.resourceTokens;
|
[requestInfo],
|
||||||
return getAuthorizationTokenUsingResourceTokens(resourceTokens2, requestInfo.path, requestInfo.resourceId);
|
userContext.fabricContext.connectionId,
|
||||||
|
);
|
||||||
/* ************** TODO: Uncomment this code if we need to support these operations **************
|
console.log("Response from Fabric: ", authorizationToken);
|
||||||
// User master tokens
|
headers[HttpHeaders.msDate] = authorizationToken.XDate;
|
||||||
const authorizationToken = await sendCachedDataMessage<AuthorizationToken>(
|
return decodeURIComponent(authorizationToken.PrimaryReadWriteToken);
|
||||||
MessageTypes.GetAuthorizationToken,
|
|
||||||
[requestInfo],
|
|
||||||
userContext.fabricContext.connectionId,
|
|
||||||
);
|
|
||||||
console.log("Response from Fabric: ", authorizationToken);
|
|
||||||
headers[HttpHeaders.msDate] = authorizationToken.XDate;
|
|
||||||
return decodeURIComponent(authorizationToken.PrimaryReadWriteToken);
|
|
||||||
***********************************************************************************************/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
import { KeyMap } from "react-hotkeys";
|
|
||||||
|
|
||||||
export const keyMap: KeyMap = {
|
|
||||||
NEW_QUERY: {
|
|
||||||
name: "New Query",
|
|
||||||
sequence: "ctrl+j",
|
|
||||||
action: "keydown",
|
|
||||||
},
|
|
||||||
CANCEL_QUERY: {
|
|
||||||
name: "Cancel Query",
|
|
||||||
sequence: "f8",
|
|
||||||
action: "keydown",
|
|
||||||
},
|
|
||||||
DISCARD: {
|
|
||||||
name: "Discard Changes",
|
|
||||||
sequence: "ctrl+x",
|
|
||||||
action: "keydown"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export type KeyboardShortcutName = keyof typeof keyMap;
|
|
||||||
|
|
||||||
export type KeyboardShortcutHandlers = Partial<{
|
|
||||||
[key in KeyboardShortcutName]: (keyEvent?: KeyboardEvent) => void;
|
|
||||||
}>;
|
|
||||||
@@ -690,7 +690,6 @@ export function getARMCreateCollectionEndpoint(params: DataModels.MongoParameter
|
|||||||
}
|
}
|
||||||
|
|
||||||
function useMongoProxyEndpoint(api: string): boolean {
|
function useMongoProxyEndpoint(api: string): boolean {
|
||||||
const activeMongoProxyEndpoints: string[] = [MongoProxyEndpoints.Development];
|
|
||||||
let canAccessMongoProxy: boolean = userContext.databaseAccount.properties.publicNetworkAccess === "Enabled";
|
let canAccessMongoProxy: boolean = userContext.databaseAccount.properties.publicNetworkAccess === "Enabled";
|
||||||
if (userContext.databaseAccount.properties.ipRules?.length > 0) {
|
if (userContext.databaseAccount.properties.ipRules?.length > 0) {
|
||||||
canAccessMongoProxy = canAccessMongoProxy && configContext.MONGO_PROXY_OUTBOUND_IPS_ALLOWLISTED;
|
canAccessMongoProxy = canAccessMongoProxy && configContext.MONGO_PROXY_OUTBOUND_IPS_ALLOWLISTED;
|
||||||
@@ -699,6 +698,6 @@ function useMongoProxyEndpoint(api: string): boolean {
|
|||||||
return (
|
return (
|
||||||
canAccessMongoProxy &&
|
canAccessMongoProxy &&
|
||||||
configContext.NEW_MONGO_APIS?.includes(api) &&
|
configContext.NEW_MONGO_APIS?.includes(api) &&
|
||||||
activeMongoProxyEndpoints.includes(configContext.MONGO_PROXY_ENDPOINT)
|
[MongoProxyEndpoints.Development, MongoProxyEndpoints.Mpac].includes(configContext.MONGO_PROXY_ENDPOINT)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -122,21 +122,14 @@ const pollDataTransferJobOperation = async (
|
|||||||
|
|
||||||
updateDataTransferJob(body);
|
updateDataTransferJob(body);
|
||||||
|
|
||||||
if (status === "Cancelled") {
|
if (status === "Cancelled" || status === "Failed" || status === "Faulted") {
|
||||||
removeFromPolling(jobName);
|
|
||||||
clearMessage && clearMessage();
|
|
||||||
const cancelMessage = `Data transfer job ${jobName} cancelled`;
|
|
||||||
NotificationConsoleUtils.logConsoleError(cancelMessage);
|
|
||||||
throw new AbortError(cancelMessage);
|
|
||||||
}
|
|
||||||
if (status === "Failed" || status === "Faulted") {
|
|
||||||
removeFromPolling(jobName);
|
removeFromPolling(jobName);
|
||||||
const errorMessage = body?.properties?.error
|
const errorMessage = body?.properties?.error
|
||||||
? JSON.stringify(body?.properties?.error)
|
? JSON.stringify(body?.properties?.error)
|
||||||
: "Operation could not be completed";
|
: "Operation could not be completed";
|
||||||
const error = new Error(errorMessage);
|
const error = new Error(errorMessage);
|
||||||
clearMessage && clearMessage();
|
clearMessage && clearMessage();
|
||||||
NotificationConsoleUtils.logConsoleError(`Data transfer job ${jobName} failed: ${errorMessage}`);
|
NotificationConsoleUtils.logConsoleError(`Data transfer job ${jobName} Failed`);
|
||||||
throw new AbortError(error);
|
throw new AbortError(error);
|
||||||
}
|
}
|
||||||
if (status === "Completed") {
|
if (status === "Completed") {
|
||||||
|
|||||||
@@ -1,10 +1,4 @@
|
|||||||
import {
|
import { CassandraProxyEndpoints, JunoEndpoints, MongoProxyEndpoints } from "Common/Constants";
|
||||||
BackendApi,
|
|
||||||
CassandraProxyEndpoints,
|
|
||||||
JunoEndpoints,
|
|
||||||
MongoProxyEndpoints,
|
|
||||||
PortalBackendEndpoints,
|
|
||||||
} from "Common/Constants";
|
|
||||||
import {
|
import {
|
||||||
allowedAadEndpoints,
|
allowedAadEndpoints,
|
||||||
allowedArcadiaEndpoints,
|
allowedArcadiaEndpoints,
|
||||||
@@ -45,8 +39,6 @@ export interface ConfigContext {
|
|||||||
ARCADIA_ENDPOINT: string;
|
ARCADIA_ENDPOINT: string;
|
||||||
ARCADIA_LIVY_ENDPOINT_DNS_ZONE: string;
|
ARCADIA_LIVY_ENDPOINT_DNS_ZONE: string;
|
||||||
BACKEND_ENDPOINT?: string;
|
BACKEND_ENDPOINT?: string;
|
||||||
PORTAL_BACKEND_ENDPOINT?: string;
|
|
||||||
NEW_BACKEND_APIS?: BackendApi[];
|
|
||||||
MONGO_BACKEND_ENDPOINT?: string;
|
MONGO_BACKEND_ENDPOINT?: string;
|
||||||
MONGO_PROXY_ENDPOINT?: string;
|
MONGO_PROXY_ENDPOINT?: string;
|
||||||
MONGO_PROXY_OUTBOUND_IPS_ALLOWLISTED?: boolean;
|
MONGO_PROXY_OUTBOUND_IPS_ALLOWLISTED?: boolean;
|
||||||
@@ -98,8 +90,6 @@ let configContext: Readonly<ConfigContext> = {
|
|||||||
GITHUB_TEST_ENV_CLIENT_ID: "b63fc8cbf87fd3c6e2eb", // Registered OAuth app: https://github.com/organizations/AzureCosmosDBNotebooks/settings/applications/1777772
|
GITHUB_TEST_ENV_CLIENT_ID: "b63fc8cbf87fd3c6e2eb", // Registered OAuth app: https://github.com/organizations/AzureCosmosDBNotebooks/settings/applications/1777772
|
||||||
JUNO_ENDPOINT: JunoEndpoints.Prod,
|
JUNO_ENDPOINT: JunoEndpoints.Prod,
|
||||||
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com",
|
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com",
|
||||||
PORTAL_BACKEND_ENDPOINT: PortalBackendEndpoints.Prod,
|
|
||||||
NEW_BACKEND_APIS: [BackendApi.GenerateToken],
|
|
||||||
MONGO_PROXY_ENDPOINT: MongoProxyEndpoints.Prod,
|
MONGO_PROXY_ENDPOINT: MongoProxyEndpoints.Prod,
|
||||||
NEW_MONGO_APIS: [
|
NEW_MONGO_APIS: [
|
||||||
// "resourcelist",
|
// "resourcelist",
|
||||||
|
|||||||
@@ -53,7 +53,6 @@ export type FabricMessageV2 =
|
|||||||
id: string;
|
id: string;
|
||||||
message: {
|
message: {
|
||||||
connectionId: string;
|
connectionId: string;
|
||||||
isVisible: boolean;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
@@ -73,7 +72,7 @@ export type FabricMessageV2 =
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
type: "explorerVisible";
|
type: "setToolbarStatus";
|
||||||
message: {
|
message: {
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* React component for Command button component.
|
* React component for Command button component.
|
||||||
*/
|
*/
|
||||||
import { KeyboardShortcutName } from "Common/KeyboardShortcuts";
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import CollapseChevronDownIcon from "../../../../images/QueryBuilder/CollapseChevronDown_16x.png";
|
import CollapseChevronDownIcon from "../../../../images/QueryBuilder/CollapseChevronDown_16x.png";
|
||||||
import { KeyCodes } from "../../../Common/Constants";
|
import { KeyCodes } from "../../../Common/Constants";
|
||||||
@@ -31,7 +30,7 @@ export interface CommandButtonComponentProps {
|
|||||||
/**
|
/**
|
||||||
* Click handler for command button click
|
* Click handler for command button click
|
||||||
*/
|
*/
|
||||||
onCommandClick: (e: React.SyntheticEvent | KeyboardEvent) => void;
|
onCommandClick: (e: React.SyntheticEvent) => void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Label for the button
|
* Label for the button
|
||||||
@@ -108,16 +107,10 @@ export interface CommandButtonComponentProps {
|
|||||||
* Vertical bar to divide buttons
|
* Vertical bar to divide buttons
|
||||||
*/
|
*/
|
||||||
isDivider?: boolean;
|
isDivider?: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Aria-label for the button
|
* Aria-label for the button
|
||||||
*/
|
*/
|
||||||
ariaLabel: string;
|
ariaLabel: string;
|
||||||
|
|
||||||
/**
|
|
||||||
* A keyboard shortcut that can be used to activate this button.
|
|
||||||
*/
|
|
||||||
keyboardShortcut?: KeyboardShortcutName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CommandButtonComponent extends React.Component<CommandButtonComponentProps> {
|
export class CommandButtonComponent extends React.Component<CommandButtonComponentProps> {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Spinner, SpinnerSize } from "@fluentui/react";
|
import { Spinner, SpinnerSize } from "@fluentui/react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { MonacoNamespace, loadMonaco, monaco } from "../../LazyMonaco";
|
import { loadMonaco, monaco } from "../../LazyMonaco";
|
||||||
// import "./EditorReact.less";
|
// import "./EditorReact.less";
|
||||||
|
|
||||||
interface EditorReactStates {
|
interface EditorReactStates {
|
||||||
@@ -21,7 +21,6 @@ export interface EditorReactProps {
|
|||||||
minimap?: monaco.editor.IEditorOptions["minimap"];
|
minimap?: monaco.editor.IEditorOptions["minimap"];
|
||||||
scrollBeyondLastLine?: monaco.editor.IEditorOptions["scrollBeyondLastLine"];
|
scrollBeyondLastLine?: monaco.editor.IEditorOptions["scrollBeyondLastLine"];
|
||||||
monacoContainerStyles?: React.CSSProperties;
|
monacoContainerStyles?: React.CSSProperties;
|
||||||
configureEditor?: (monaco: MonacoNamespace, editor: monaco.editor.IStandaloneCodeEditor) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class EditorReact extends React.Component<EditorReactProps, EditorReactStates> {
|
export class EditorReact extends React.Component<EditorReactProps, EditorReactStates> {
|
||||||
@@ -70,7 +69,7 @@ export class EditorReact extends React.Component<EditorReactProps, EditorReactSt
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected configureEditor(monaco: MonacoNamespace, editor: monaco.editor.IStandaloneCodeEditor) {
|
protected configureEditor(editor: monaco.editor.IStandaloneCodeEditor) {
|
||||||
this.editor = editor;
|
this.editor = editor;
|
||||||
const queryEditorModel = this.editor.getModel();
|
const queryEditorModel = this.editor.getModel();
|
||||||
if (!this.props.isReadOnly && this.props.onContentChanged) {
|
if (!this.props.isReadOnly && this.props.onContentChanged) {
|
||||||
@@ -88,16 +87,12 @@ export class EditorReact extends React.Component<EditorReactProps, EditorReactSt
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.props.configureEditor) {
|
|
||||||
this.props.configureEditor(monaco, this.editor);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create the monaco editor and attach to DOM
|
* Create the monaco editor and attach to DOM
|
||||||
*/
|
*/
|
||||||
private async createEditor(createCallback: (monaco: MonacoNamespace, e: monaco.editor.IStandaloneCodeEditor) => void) {
|
private async createEditor(createCallback: (e: monaco.editor.IStandaloneCodeEditor) => void) {
|
||||||
const options: monaco.editor.IStandaloneEditorConstructionOptions = {
|
const options: monaco.editor.IStandaloneEditorConstructionOptions = {
|
||||||
language: this.props.language,
|
language: this.props.language,
|
||||||
value: this.props.content,
|
value: this.props.content,
|
||||||
@@ -116,7 +111,7 @@ export class EditorReact extends React.Component<EditorReactProps, EditorReactSt
|
|||||||
|
|
||||||
this.rootNode.innerHTML = "";
|
this.rootNode.innerHTML = "";
|
||||||
const monaco = await loadMonaco();
|
const monaco = await loadMonaco();
|
||||||
createCallback(monaco, monaco?.editor?.create(this.rootNode, options));
|
createCallback(monaco?.editor?.create(this.rootNode, options));
|
||||||
|
|
||||||
if (this.rootNode.innerHTML) {
|
if (this.rootNode.innerHTML) {
|
||||||
this.setState({
|
this.setState({
|
||||||
|
|||||||
@@ -754,7 +754,6 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
|||||||
buttons.push({
|
buttons.push({
|
||||||
iconSrc: DiscardIcon,
|
iconSrc: DiscardIcon,
|
||||||
iconAlt: label,
|
iconAlt: label,
|
||||||
keyboardShortcut: "DISCARD",
|
|
||||||
onCommandClick: this.onRevertClick,
|
onCommandClick: this.onRevertClick,
|
||||||
commandButtonLabel: label,
|
commandButtonLabel: label,
|
||||||
ariaLabel: label,
|
ariaLabel: label,
|
||||||
|
|||||||
@@ -136,15 +136,15 @@ export const PartitionKeyComponent: React.FC<PartitionKeyComponentProps> = ({ da
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getPercentageComplete = () => {
|
const getPercentageComplete = () => {
|
||||||
const jobStatus = portalDataTransferJob?.properties?.status;
|
|
||||||
const isCompleted = jobStatus === "Completed";
|
|
||||||
if (isCompleted) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
const processedCount = portalDataTransferJob?.properties?.processedCount;
|
const processedCount = portalDataTransferJob?.properties?.processedCount;
|
||||||
const totalCount = portalDataTransferJob?.properties?.totalCount;
|
const totalCount = portalDataTransferJob?.properties?.totalCount;
|
||||||
const isJobInProgress = isCurrentJobInProgress(portalDataTransferJob);
|
const jobStatus = portalDataTransferJob?.properties?.status;
|
||||||
return isJobInProgress ? (totalCount > 0 ? processedCount / totalCount : null) : 0;
|
const isCancelled = jobStatus === "Cancelled";
|
||||||
|
const isCompleted = jobStatus === "Completed";
|
||||||
|
if (totalCount <= 0 && !isCompleted) {
|
||||||
|
return isCancelled ? 0 : null;
|
||||||
|
}
|
||||||
|
return isCompleted ? 1 : processedCount / totalCount;
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ export const getTabTitle = (tab: SettingsV2TabTypes): string => {
|
|||||||
case SettingsV2TabTypes.IndexingPolicyTab:
|
case SettingsV2TabTypes.IndexingPolicyTab:
|
||||||
return "Indexing Policy";
|
return "Indexing Policy";
|
||||||
case SettingsV2TabTypes.PartitionKeyTab:
|
case SettingsV2TabTypes.PartitionKeyTab:
|
||||||
return "Partition Keys (preview)";
|
return "Partition Keys";
|
||||||
case SettingsV2TabTypes.ComputedPropertiesTab:
|
case SettingsV2TabTypes.ComputedPropertiesTab:
|
||||||
return "Computed Properties (preview)";
|
return "Computed Properties (preview)";
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -207,7 +207,7 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
/>
|
/>
|
||||||
</PivotItem>
|
</PivotItem>
|
||||||
<PivotItem
|
<PivotItem
|
||||||
headerText="Partition Keys (preview)"
|
headerText="Partition Keys"
|
||||||
itemKey="PartitionKeyTab"
|
itemKey="PartitionKeyTab"
|
||||||
key="PartitionKeyTab"
|
key="PartitionKeyTab"
|
||||||
style={
|
style={
|
||||||
|
|||||||
@@ -3,4 +3,3 @@ export type { monaco };
|
|||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||||
export const loadMonaco = () => import(/* webpackChunkName: "lazy-monaco" */ "monaco-editor/esm/vs/editor/editor.api");
|
export const loadMonaco = () => import(/* webpackChunkName: "lazy-monaco" */ "monaco-editor/esm/vs/editor/editor.api");
|
||||||
export type MonacoNamespace = Awaited<ReturnType<typeof loadMonaco>>;
|
|
||||||
|
|||||||
@@ -4,11 +4,9 @@
|
|||||||
* 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 { keyMap } from "Common/KeyboardShortcuts";
|
|
||||||
import { useNotebook } from "Explorer/Notebook/useNotebook";
|
import { useNotebook } from "Explorer/Notebook/useNotebook";
|
||||||
import { userContext } from "UserContext";
|
import { userContext } from "UserContext";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { GlobalHotKeys } from "react-hotkeys";
|
|
||||||
import create, { UseStore } from "zustand";
|
import create, { UseStore } from "zustand";
|
||||||
import { ConnectionStatusType, PoolIdType } from "../../../Common/Constants";
|
import { ConnectionStatusType, PoolIdType } from "../../../Common/Constants";
|
||||||
import { StyleConstants } from "../../../Common/StyleConstants";
|
import { StyleConstants } from "../../../Common/StyleConstants";
|
||||||
@@ -107,13 +105,8 @@ export const CommandBar: React.FC<Props> = ({ container }: Props) => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlers = CommandBarUtil.createKeyboardHandlers(staticButtons.concat(contextButtons).concat(controlButtons));
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="commandBarContainer" style={{ display: isHidden ? "none" : "initial" }}>
|
<div className="commandBarContainer" style={{ display: isHidden ? "none" : "initial" }}>
|
||||||
{/* Handles keyboard shortcuts for command bar buttons when focus is OUTSIDE monaco. Even though it's placed here in the DOM, it hooks keydown on 'document' */}
|
|
||||||
<GlobalHotKeys keyMap={keyMap} handlers={handlers} allowChanges={true} />
|
|
||||||
|
|
||||||
<FluentCommandBar
|
<FluentCommandBar
|
||||||
ariaLabel="Use left and right arrow keys to navigate between commands"
|
ariaLabel="Use left and right arrow keys to navigate between commands"
|
||||||
items={uiFabricStaticButtons.concat(uiFabricTabsButtons)}
|
items={uiFabricStaticButtons.concat(uiFabricTabsButtons)}
|
||||||
|
|||||||
@@ -58,10 +58,10 @@ export function createStaticCommandBarButtons(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (configContext.platform !== Platform.Fabric) {
|
const homeBtn = createHomeButton();
|
||||||
const homeBtn = createHomeButton();
|
buttons.push(homeBtn);
|
||||||
buttons.push(homeBtn);
|
|
||||||
|
|
||||||
|
if (configContext.platform !== Platform.Fabric) {
|
||||||
const newCollectionBtn = createNewCollectionGroup(container);
|
const newCollectionBtn = createNewCollectionGroup(container);
|
||||||
buttons.push(newCollectionBtn);
|
buttons.push(newCollectionBtn);
|
||||||
if (userContext.apiType !== "Tables" && userContext.apiType !== "Cassandra") {
|
if (userContext.apiType !== "Tables" && userContext.apiType !== "Cassandra") {
|
||||||
@@ -240,7 +240,7 @@ export function createControlCommandBarButtons(container: Explorer): CommandButt
|
|||||||
buttons.push(fullScreenButton);
|
buttons.push(fullScreenButton);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (configContext.platform === Platform.Portal) {
|
if (configContext.platform !== Platform.Emulator) {
|
||||||
const label = "Feedback";
|
const label = "Feedback";
|
||||||
const feedbackButtonOptions: CommandButtonComponentProps = {
|
const feedbackButtonOptions: CommandButtonComponentProps = {
|
||||||
iconSrc: FeedbackIcon,
|
iconSrc: FeedbackIcon,
|
||||||
@@ -354,7 +354,6 @@ function createNewSQLQueryButton(selectedNodeState: SelectedNodeState): CommandB
|
|||||||
id: "newQueryBtn",
|
id: "newQueryBtn",
|
||||||
iconSrc: AddSqlQueryIcon,
|
iconSrc: AddSqlQueryIcon,
|
||||||
iconAlt: label,
|
iconAlt: label,
|
||||||
keyboardShortcut: "NEW_QUERY",
|
|
||||||
onCommandClick: () => {
|
onCommandClick: () => {
|
||||||
const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection();
|
const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection();
|
||||||
selectedCollection && selectedCollection.onNewQueryClick(selectedCollection);
|
selectedCollection && selectedCollection.onNewQueryClick(selectedCollection);
|
||||||
@@ -370,7 +369,6 @@ function createNewSQLQueryButton(selectedNodeState: SelectedNodeState): CommandB
|
|||||||
id: "newQueryBtn",
|
id: "newQueryBtn",
|
||||||
iconSrc: AddSqlQueryIcon,
|
iconSrc: AddSqlQueryIcon,
|
||||||
iconAlt: label,
|
iconAlt: label,
|
||||||
keyboardShortcut: "NEW_QUERY",
|
|
||||||
onCommandClick: () => {
|
onCommandClick: () => {
|
||||||
const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection();
|
const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection();
|
||||||
selectedCollection && selectedCollection.onNewMongoQueryClick(selectedCollection);
|
selectedCollection && selectedCollection.onNewMongoQueryClick(selectedCollection);
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import {
|
|||||||
IDropdownOption,
|
IDropdownOption,
|
||||||
IDropdownStyles,
|
IDropdownStyles,
|
||||||
} from "@fluentui/react";
|
} from "@fluentui/react";
|
||||||
import { KeyboardShortcutHandlers } from "Common/KeyboardShortcuts";
|
|
||||||
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import _ from "underscore";
|
import _ from "underscore";
|
||||||
@@ -234,16 +233,3 @@ export const createConnectionStatus = (container: Explorer, poolId: PoolIdType,
|
|||||||
onRender: () => <ConnectionStatus container={container} poolId={poolId} />,
|
onRender: () => <ConnectionStatus container={container} poolId={poolId} />,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createKeyboardHandlers = (buttons: CommandButtonComponentProps[]): KeyboardShortcutHandlers => {
|
|
||||||
const handlers: KeyboardShortcutHandlers = {};
|
|
||||||
buttons.forEach((button) => {
|
|
||||||
if (button.keyboardShortcut) {
|
|
||||||
handlers[button.keyboardShortcut] = (e) => {
|
|
||||||
button.onCommandClick(e);
|
|
||||||
e.preventDefault();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return handlers;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// TODO convert this file to an action registry in order to have actions and their handlers be more tightly coupled.
|
// TODO convert this file to an action registry in order to have actions and their handlers be more tightly coupled.
|
||||||
import { useDatabases } from "Explorer/useDatabases";
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { ActionContracts } from "../../Contracts/ExplorerContracts";
|
import { ActionContracts } from "../../Contracts/ExplorerContracts";
|
||||||
import * as ViewModels from "../../Contracts/ViewModels";
|
import * as ViewModels from "../../Contracts/ViewModels";
|
||||||
@@ -41,112 +40,97 @@ function openCollectionTab(
|
|||||||
databases: ViewModels.Database[],
|
databases: ViewModels.Database[],
|
||||||
initialDatabaseIndex = 0,
|
initialDatabaseIndex = 0,
|
||||||
) {
|
) {
|
||||||
//if databases are not yet loaded, wait until loaded
|
for (let i = initialDatabaseIndex; i < databases.length; i++) {
|
||||||
if (!databases || databases.length === 0) {
|
const database: ViewModels.Database = databases[i];
|
||||||
const databaseActionHandler = (databases: ViewModels.Database[]) => {
|
if (!!action.databaseResourceId && database.id() !== action.databaseResourceId) {
|
||||||
databasesUnsubscription();
|
continue;
|
||||||
openCollectionTab(action, databases, 0);
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
const databasesUnsubscription = useDatabases.subscribe(databaseActionHandler, (state) => state.databases);
|
|
||||||
} else {
|
|
||||||
for (let i = initialDatabaseIndex; i < databases.length; i++) {
|
|
||||||
const database: ViewModels.Database = databases[i];
|
|
||||||
if (!!action.databaseResourceId && database.id() !== action.databaseResourceId) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
//expand database first if not expanded to load the collections
|
|
||||||
if (!database.isDatabaseExpanded?.()) {
|
|
||||||
database.expandDatabase?.();
|
|
||||||
}
|
|
||||||
|
|
||||||
const collectionActionHandler = (collections: ViewModels.Collection[]) => {
|
|
||||||
if (!action.collectionResourceId && collections.length === 0) {
|
|
||||||
subscription.dispose();
|
|
||||||
openCollectionTab(action, databases, ++i);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let j = 0; j < collections.length; j++) {
|
|
||||||
const collection: ViewModels.Collection = collections[j];
|
|
||||||
if (!!action.collectionResourceId && collection.id() !== action.collectionResourceId) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// select the collection
|
|
||||||
collection.expandCollection();
|
|
||||||
|
|
||||||
if (
|
|
||||||
action.tabKind === ActionContracts.TabKind.SQLDocuments ||
|
|
||||||
action.tabKind === ActionContracts.TabKind[ActionContracts.TabKind.SQLDocuments]
|
|
||||||
) {
|
|
||||||
collection.onDocumentDBDocumentsClick();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
action.tabKind === ActionContracts.TabKind.MongoDocuments ||
|
|
||||||
action.tabKind === ActionContracts.TabKind[ActionContracts.TabKind.MongoDocuments]
|
|
||||||
) {
|
|
||||||
collection.onMongoDBDocumentsClick();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
action.tabKind === ActionContracts.TabKind.SchemaAnalyzer ||
|
|
||||||
action.tabKind === ActionContracts.TabKind[ActionContracts.TabKind.SchemaAnalyzer]
|
|
||||||
) {
|
|
||||||
collection.onSchemaAnalyzerClick();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
action.tabKind === ActionContracts.TabKind.TableEntities ||
|
|
||||||
action.tabKind === ActionContracts.TabKind[ActionContracts.TabKind.TableEntities]
|
|
||||||
) {
|
|
||||||
collection.onTableEntitiesClick();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
action.tabKind === ActionContracts.TabKind.Graph ||
|
|
||||||
action.tabKind === ActionContracts.TabKind[ActionContracts.TabKind.Graph]
|
|
||||||
) {
|
|
||||||
collection.onGraphDocumentsClick();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
action.tabKind === ActionContracts.TabKind.SQLQuery ||
|
|
||||||
action.tabKind === ActionContracts.TabKind[ActionContracts.TabKind.SQLQuery]
|
|
||||||
) {
|
|
||||||
collection.onNewQueryClick(
|
|
||||||
collection,
|
|
||||||
undefined,
|
|
||||||
generateQueryText(action as ActionContracts.OpenQueryTab, collection.partitionKeyProperties),
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
action.tabKind === ActionContracts.TabKind.ScaleSettings ||
|
|
||||||
action.tabKind === ActionContracts.TabKind[ActionContracts.TabKind.ScaleSettings]
|
|
||||||
) {
|
|
||||||
collection.onSettingsClick();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
subscription.dispose();
|
|
||||||
};
|
|
||||||
|
|
||||||
const subscription = database.collections.subscribe((collections) => collectionActionHandler(collections));
|
|
||||||
if (database.collections && database.collections() && database.collections().length) {
|
|
||||||
collectionActionHandler(database.collections());
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const collectionActionHandler = (collections: ViewModels.Collection[]) => {
|
||||||
|
if (!action.collectionResourceId && collections.length === 0) {
|
||||||
|
subscription.dispose();
|
||||||
|
openCollectionTab(action, databases, ++i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let j = 0; j < collections.length; j++) {
|
||||||
|
const collection: ViewModels.Collection = collections[j];
|
||||||
|
if (!!action.collectionResourceId && collection.id() !== action.collectionResourceId) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// select the collection
|
||||||
|
collection.expandCollection();
|
||||||
|
|
||||||
|
if (
|
||||||
|
action.tabKind === ActionContracts.TabKind.SQLDocuments ||
|
||||||
|
action.tabKind === ActionContracts.TabKind[ActionContracts.TabKind.SQLDocuments]
|
||||||
|
) {
|
||||||
|
collection.onDocumentDBDocumentsClick();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
action.tabKind === ActionContracts.TabKind.MongoDocuments ||
|
||||||
|
action.tabKind === ActionContracts.TabKind[ActionContracts.TabKind.MongoDocuments]
|
||||||
|
) {
|
||||||
|
collection.onMongoDBDocumentsClick();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
action.tabKind === ActionContracts.TabKind.SchemaAnalyzer ||
|
||||||
|
action.tabKind === ActionContracts.TabKind[ActionContracts.TabKind.SchemaAnalyzer]
|
||||||
|
) {
|
||||||
|
collection.onSchemaAnalyzerClick();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
action.tabKind === ActionContracts.TabKind.TableEntities ||
|
||||||
|
action.tabKind === ActionContracts.TabKind[ActionContracts.TabKind.TableEntities]
|
||||||
|
) {
|
||||||
|
collection.onTableEntitiesClick();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
action.tabKind === ActionContracts.TabKind.Graph ||
|
||||||
|
action.tabKind === ActionContracts.TabKind[ActionContracts.TabKind.Graph]
|
||||||
|
) {
|
||||||
|
collection.onGraphDocumentsClick();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
action.tabKind === ActionContracts.TabKind.SQLQuery ||
|
||||||
|
action.tabKind === ActionContracts.TabKind[ActionContracts.TabKind.SQLQuery]
|
||||||
|
) {
|
||||||
|
collection.onNewQueryClick(
|
||||||
|
collection,
|
||||||
|
undefined,
|
||||||
|
generateQueryText(action as ActionContracts.OpenQueryTab, collection.partitionKeyProperties),
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
action.tabKind === ActionContracts.TabKind.ScaleSettings ||
|
||||||
|
action.tabKind === ActionContracts.TabKind[ActionContracts.TabKind.ScaleSettings]
|
||||||
|
) {
|
||||||
|
collection.onSettingsClick();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
subscription.dispose();
|
||||||
|
};
|
||||||
|
|
||||||
|
const subscription = database.collections.subscribe((collections) => collectionActionHandler(collections));
|
||||||
|
if (database.collections && database.collections() && database.collections().length) {
|
||||||
|
collectionActionHandler(database.collections());
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import {
|
|||||||
Icon,
|
Icon,
|
||||||
IconButton,
|
IconButton,
|
||||||
Link,
|
Link,
|
||||||
MessageBar,
|
|
||||||
Stack,
|
Stack,
|
||||||
Text,
|
Text,
|
||||||
TooltipHost,
|
TooltipHost,
|
||||||
@@ -208,7 +207,6 @@ export const ChangePartitionKeyPane: React.FC<ChangePartitionKeyPaneProps> = ({
|
|||||||
</Stack>
|
</Stack>
|
||||||
{createNewContainer ? (
|
{createNewContainer ? (
|
||||||
<Stack>
|
<Stack>
|
||||||
<MessageBar>All configurations except for unique keys will be copied from the source container</MessageBar>
|
|
||||||
<Stack className="panelGroupSpacing">
|
<Stack className="panelGroupSpacing">
|
||||||
<Stack horizontal>
|
<Stack horizontal>
|
||||||
<span className="mandatoryStar">* </span>
|
<span className="mandatoryStar">* </span>
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ export const QueryCopilotFeedbackModal = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal isOpen={showFeedbackModal} styles={{ main: { borderRadius: 8, maxWidth: 600 } }}>
|
<Modal isOpen={showFeedbackModal}>
|
||||||
<form onSubmit={handleSubmit}>
|
<form onSubmit={handleSubmit}>
|
||||||
<Stack style={{ padding: 24 }}>
|
<Stack style={{ padding: 24 }}>
|
||||||
<Stack horizontal horizontalAlign="space-between">
|
<Stack horizontal horizontalAlign="space-between">
|
||||||
@@ -68,14 +68,9 @@ export const QueryCopilotFeedbackModal = ({
|
|||||||
rows={3}
|
rows={3}
|
||||||
/>
|
/>
|
||||||
<TextField
|
<TextField
|
||||||
styles={{
|
styles={{ root: { marginBottom: 14 } }}
|
||||||
root: { marginBottom: 14 },
|
|
||||||
fieldGroup: { backgroundColor: "#F3F2F1", borderRadius: 4, borderColor: "#D1D1D1" },
|
|
||||||
}}
|
|
||||||
label="Query generated"
|
label="Query generated"
|
||||||
defaultValue={generatedQuery}
|
defaultValue={generatedQuery}
|
||||||
multiline
|
|
||||||
rows={3}
|
|
||||||
readOnly
|
readOnly
|
||||||
/>
|
/>
|
||||||
<Text style={{ fontSize: 12, marginBottom: 14 }}>
|
<Text style={{ fontSize: 12, marginBottom: 14 }}>
|
||||||
|
|||||||
@@ -3,14 +3,6 @@
|
|||||||
exports[`Query Copilot Feedback Modal snapshot test shoud render and match snapshot 1`] = `
|
exports[`Query Copilot Feedback Modal snapshot test shoud render and match snapshot 1`] = `
|
||||||
<Modal
|
<Modal
|
||||||
isOpen={true}
|
isOpen={true}
|
||||||
styles={
|
|
||||||
Object {
|
|
||||||
"main": Object {
|
|
||||||
"borderRadius": 8,
|
|
||||||
"maxWidth": 600,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<form
|
<form
|
||||||
onSubmit={[Function]}
|
onSubmit={[Function]}
|
||||||
@@ -75,16 +67,9 @@ exports[`Query Copilot Feedback Modal snapshot test shoud render and match snaps
|
|||||||
<StyledTextFieldBase
|
<StyledTextFieldBase
|
||||||
defaultValue="test query"
|
defaultValue="test query"
|
||||||
label="Query generated"
|
label="Query generated"
|
||||||
multiline={true}
|
|
||||||
readOnly={true}
|
readOnly={true}
|
||||||
rows={3}
|
|
||||||
styles={
|
styles={
|
||||||
Object {
|
Object {
|
||||||
"fieldGroup": Object {
|
|
||||||
"backgroundColor": "#F3F2F1",
|
|
||||||
"borderColor": "#D1D1D1",
|
|
||||||
"borderRadius": 4,
|
|
||||||
},
|
|
||||||
"root": Object {
|
"root": Object {
|
||||||
"marginBottom": 14,
|
"marginBottom": 14,
|
||||||
},
|
},
|
||||||
@@ -140,14 +125,6 @@ exports[`Query Copilot Feedback Modal snapshot test shoud render and match snaps
|
|||||||
exports[`Query Copilot Feedback Modal snapshot test should cancel submission 1`] = `
|
exports[`Query Copilot Feedback Modal snapshot test should cancel submission 1`] = `
|
||||||
<Modal
|
<Modal
|
||||||
isOpen={false}
|
isOpen={false}
|
||||||
styles={
|
|
||||||
Object {
|
|
||||||
"main": Object {
|
|
||||||
"borderRadius": 8,
|
|
||||||
"maxWidth": 600,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<form
|
<form
|
||||||
onSubmit={[Function]}
|
onSubmit={[Function]}
|
||||||
@@ -212,16 +189,9 @@ exports[`Query Copilot Feedback Modal snapshot test should cancel submission 1`]
|
|||||||
<StyledTextFieldBase
|
<StyledTextFieldBase
|
||||||
defaultValue="test query"
|
defaultValue="test query"
|
||||||
label="Query generated"
|
label="Query generated"
|
||||||
multiline={true}
|
|
||||||
readOnly={true}
|
readOnly={true}
|
||||||
rows={3}
|
|
||||||
styles={
|
styles={
|
||||||
Object {
|
Object {
|
||||||
"fieldGroup": Object {
|
|
||||||
"backgroundColor": "#F3F2F1",
|
|
||||||
"borderColor": "#D1D1D1",
|
|
||||||
"borderRadius": 4,
|
|
||||||
},
|
|
||||||
"root": Object {
|
"root": Object {
|
||||||
"marginBottom": 14,
|
"marginBottom": 14,
|
||||||
},
|
},
|
||||||
@@ -277,14 +247,6 @@ exports[`Query Copilot Feedback Modal snapshot test should cancel submission 1`]
|
|||||||
exports[`Query Copilot Feedback Modal snapshot test should close on cancel click 1`] = `
|
exports[`Query Copilot Feedback Modal snapshot test should close on cancel click 1`] = `
|
||||||
<Modal
|
<Modal
|
||||||
isOpen={false}
|
isOpen={false}
|
||||||
styles={
|
|
||||||
Object {
|
|
||||||
"main": Object {
|
|
||||||
"borderRadius": 8,
|
|
||||||
"maxWidth": 600,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<form
|
<form
|
||||||
onSubmit={[Function]}
|
onSubmit={[Function]}
|
||||||
@@ -349,16 +311,9 @@ exports[`Query Copilot Feedback Modal snapshot test should close on cancel click
|
|||||||
<StyledTextFieldBase
|
<StyledTextFieldBase
|
||||||
defaultValue="test query"
|
defaultValue="test query"
|
||||||
label="Query generated"
|
label="Query generated"
|
||||||
multiline={true}
|
|
||||||
readOnly={true}
|
readOnly={true}
|
||||||
rows={3}
|
|
||||||
styles={
|
styles={
|
||||||
Object {
|
Object {
|
||||||
"fieldGroup": Object {
|
|
||||||
"backgroundColor": "#F3F2F1",
|
|
||||||
"borderColor": "#D1D1D1",
|
|
||||||
"borderRadius": 4,
|
|
||||||
},
|
|
||||||
"root": Object {
|
"root": Object {
|
||||||
"marginBottom": 14,
|
"marginBottom": 14,
|
||||||
},
|
},
|
||||||
@@ -414,14 +369,6 @@ exports[`Query Copilot Feedback Modal snapshot test should close on cancel click
|
|||||||
exports[`Query Copilot Feedback Modal snapshot test should get user unput 1`] = `
|
exports[`Query Copilot Feedback Modal snapshot test should get user unput 1`] = `
|
||||||
<Modal
|
<Modal
|
||||||
isOpen={false}
|
isOpen={false}
|
||||||
styles={
|
|
||||||
Object {
|
|
||||||
"main": Object {
|
|
||||||
"borderRadius": 8,
|
|
||||||
"maxWidth": 600,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<form
|
<form
|
||||||
onSubmit={[Function]}
|
onSubmit={[Function]}
|
||||||
@@ -486,16 +433,9 @@ exports[`Query Copilot Feedback Modal snapshot test should get user unput 1`] =
|
|||||||
<StyledTextFieldBase
|
<StyledTextFieldBase
|
||||||
defaultValue="test query"
|
defaultValue="test query"
|
||||||
label="Query generated"
|
label="Query generated"
|
||||||
multiline={true}
|
|
||||||
readOnly={true}
|
readOnly={true}
|
||||||
rows={3}
|
|
||||||
styles={
|
styles={
|
||||||
Object {
|
Object {
|
||||||
"fieldGroup": Object {
|
|
||||||
"backgroundColor": "#F3F2F1",
|
|
||||||
"borderColor": "#D1D1D1",
|
|
||||||
"borderRadius": 4,
|
|
||||||
},
|
|
||||||
"root": Object {
|
"root": Object {
|
||||||
"marginBottom": 14,
|
"marginBottom": 14,
|
||||||
},
|
},
|
||||||
@@ -551,14 +491,6 @@ exports[`Query Copilot Feedback Modal snapshot test should get user unput 1`] =
|
|||||||
exports[`Query Copilot Feedback Modal snapshot test should not render dont show again button 1`] = `
|
exports[`Query Copilot Feedback Modal snapshot test should not render dont show again button 1`] = `
|
||||||
<Modal
|
<Modal
|
||||||
isOpen={false}
|
isOpen={false}
|
||||||
styles={
|
|
||||||
Object {
|
|
||||||
"main": Object {
|
|
||||||
"borderRadius": 8,
|
|
||||||
"maxWidth": 600,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<form
|
<form
|
||||||
onSubmit={[Function]}
|
onSubmit={[Function]}
|
||||||
@@ -623,16 +555,9 @@ exports[`Query Copilot Feedback Modal snapshot test should not render dont show
|
|||||||
<StyledTextFieldBase
|
<StyledTextFieldBase
|
||||||
defaultValue="test query"
|
defaultValue="test query"
|
||||||
label="Query generated"
|
label="Query generated"
|
||||||
multiline={true}
|
|
||||||
readOnly={true}
|
readOnly={true}
|
||||||
rows={3}
|
|
||||||
styles={
|
styles={
|
||||||
Object {
|
Object {
|
||||||
"fieldGroup": Object {
|
|
||||||
"backgroundColor": "#F3F2F1",
|
|
||||||
"borderColor": "#D1D1D1",
|
|
||||||
"borderRadius": 4,
|
|
||||||
},
|
|
||||||
"root": Object {
|
"root": Object {
|
||||||
"marginBottom": 14,
|
"marginBottom": 14,
|
||||||
},
|
},
|
||||||
@@ -688,14 +613,6 @@ exports[`Query Copilot Feedback Modal snapshot test should not render dont show
|
|||||||
exports[`Query Copilot Feedback Modal snapshot test should render dont show again button and check it 1`] = `
|
exports[`Query Copilot Feedback Modal snapshot test should render dont show again button and check it 1`] = `
|
||||||
<Modal
|
<Modal
|
||||||
isOpen={true}
|
isOpen={true}
|
||||||
styles={
|
|
||||||
Object {
|
|
||||||
"main": Object {
|
|
||||||
"borderRadius": 8,
|
|
||||||
"maxWidth": 600,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<form
|
<form
|
||||||
onSubmit={[Function]}
|
onSubmit={[Function]}
|
||||||
@@ -760,16 +677,9 @@ exports[`Query Copilot Feedback Modal snapshot test should render dont show agai
|
|||||||
<StyledTextFieldBase
|
<StyledTextFieldBase
|
||||||
defaultValue="test query"
|
defaultValue="test query"
|
||||||
label="Query generated"
|
label="Query generated"
|
||||||
multiline={true}
|
|
||||||
readOnly={true}
|
readOnly={true}
|
||||||
rows={3}
|
|
||||||
styles={
|
styles={
|
||||||
Object {
|
Object {
|
||||||
"fieldGroup": Object {
|
|
||||||
"backgroundColor": "#F3F2F1",
|
|
||||||
"borderColor": "#D1D1D1",
|
|
||||||
"borderRadius": 4,
|
|
||||||
},
|
|
||||||
"root": Object {
|
"root": Object {
|
||||||
"marginBottom": 14,
|
"marginBottom": 14,
|
||||||
},
|
},
|
||||||
@@ -840,14 +750,6 @@ exports[`Query Copilot Feedback Modal snapshot test should render dont show agai
|
|||||||
exports[`Query Copilot Feedback Modal snapshot test should submit submission 1`] = `
|
exports[`Query Copilot Feedback Modal snapshot test should submit submission 1`] = `
|
||||||
<Modal
|
<Modal
|
||||||
isOpen={false}
|
isOpen={false}
|
||||||
styles={
|
|
||||||
Object {
|
|
||||||
"main": Object {
|
|
||||||
"borderRadius": 8,
|
|
||||||
"maxWidth": 600,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<form
|
<form
|
||||||
onSubmit={[Function]}
|
onSubmit={[Function]}
|
||||||
@@ -912,16 +814,9 @@ exports[`Query Copilot Feedback Modal snapshot test should submit submission 1`]
|
|||||||
<StyledTextFieldBase
|
<StyledTextFieldBase
|
||||||
defaultValue="test query"
|
defaultValue="test query"
|
||||||
label="Query generated"
|
label="Query generated"
|
||||||
multiline={true}
|
|
||||||
readOnly={true}
|
readOnly={true}
|
||||||
rows={3}
|
|
||||||
styles={
|
styles={
|
||||||
Object {
|
Object {
|
||||||
"fieldGroup": Object {
|
|
||||||
"backgroundColor": "#F3F2F1",
|
|
||||||
"borderColor": "#D1D1D1",
|
|
||||||
"borderRadius": 4,
|
|
||||||
},
|
|
||||||
"root": Object {
|
"root": Object {
|
||||||
"marginBottom": 14,
|
"marginBottom": 14,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ import {
|
|||||||
Link,
|
Link,
|
||||||
MessageBar,
|
MessageBar,
|
||||||
MessageBarType,
|
MessageBarType,
|
||||||
ProgressIndicator,
|
|
||||||
Separator,
|
Separator,
|
||||||
|
Spinner,
|
||||||
Stack,
|
Stack,
|
||||||
TeachingBubble,
|
TeachingBubble,
|
||||||
Text,
|
Text,
|
||||||
@@ -36,6 +36,7 @@ import { userContext } from "UserContext";
|
|||||||
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||||
import React, { useRef, useState } from "react";
|
import React, { useRef, useState } from "react";
|
||||||
import HintIcon from "../../../images/Hint.svg";
|
import HintIcon from "../../../images/Hint.svg";
|
||||||
|
import CopilotIcon from "../../../images/QueryCopilotNewLogo.svg";
|
||||||
import RecentIcon from "../../../images/Recent.svg";
|
import RecentIcon from "../../../images/Recent.svg";
|
||||||
import errorIcon from "../../../images/close-black.svg";
|
import errorIcon from "../../../images/close-black.svg";
|
||||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||||
@@ -214,12 +215,12 @@ export const QueryCopilotPromptbar: React.FC<QueryCopilotPromptProps> = ({
|
|||||||
const generateSQLQueryResponse: GenerateSQLQueryResponse = await response?.json();
|
const generateSQLQueryResponse: GenerateSQLQueryResponse = await response?.json();
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
if (generateSQLQueryResponse?.sql !== "N/A") {
|
if (generateSQLQueryResponse?.sql !== "N/A") {
|
||||||
const queryExplanation = `-- **Explanation of query:** ${
|
let query = `-- **Prompt:** ${userPrompt}\r\n`;
|
||||||
generateSQLQueryResponse.explanation ? generateSQLQueryResponse.explanation : "N/A"
|
if (generateSQLQueryResponse.explanation) {
|
||||||
}\r\n`;
|
query += `-- **Explanation of query:** ${generateSQLQueryResponse.explanation}\r\n`;
|
||||||
const currentGeneratedQuery = queryExplanation + generateSQLQueryResponse.sql;
|
}
|
||||||
const lastQuery = generatedQuery && query ? `${query}\r\n` : "";
|
query += generateSQLQueryResponse.sql;
|
||||||
setQuery(`${lastQuery}${currentGeneratedQuery}`);
|
setQuery(query);
|
||||||
setGeneratedQuery(generateSQLQueryResponse.sql);
|
setGeneratedQuery(generateSQLQueryResponse.sql);
|
||||||
setGeneratedQueryComments(generateSQLQueryResponse.explanation);
|
setGeneratedQueryComments(generateSQLQueryResponse.explanation);
|
||||||
setShowFeedbackBar(true);
|
setShowFeedbackBar(true);
|
||||||
@@ -296,9 +297,9 @@ export const QueryCopilotPromptbar: React.FC<QueryCopilotPromptProps> = ({
|
|||||||
if (isGeneratingQuery === null) {
|
if (isGeneratingQuery === null) {
|
||||||
return " ";
|
return " ";
|
||||||
} else if (isGeneratingQuery) {
|
} else if (isGeneratingQuery) {
|
||||||
return "Content is loading";
|
return "Content is loading!";
|
||||||
} else {
|
} else {
|
||||||
return "Content is updated";
|
return "Content is updated!";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -309,388 +310,12 @@ export const QueryCopilotPromptbar: React.FC<QueryCopilotPromptProps> = ({
|
|||||||
return (
|
return (
|
||||||
<Stack
|
<Stack
|
||||||
className="copilot-prompt-pane"
|
className="copilot-prompt-pane"
|
||||||
styles={{ root: { backgroundColor: "#FAFAFA", padding: "8px" } }}
|
styles={{ root: { backgroundColor: "#FAFAFA", padding: "16px 24px 0px" } }}
|
||||||
id="copilot-textfield-label"
|
id="copilot-textfield-label"
|
||||||
>
|
>
|
||||||
<Stack
|
<Stack horizontal>
|
||||||
horizontal
|
<Image src={CopilotIcon} style={{ width: 24, height: 24 }} alt="Copilot" role="none" />
|
||||||
styles={{
|
<Text style={{ marginLeft: 8, fontWeight: 600, fontSize: 16 }}>Copilot</Text>
|
||||||
root: {
|
|
||||||
width: "100%",
|
|
||||||
borderWidth: 1,
|
|
||||||
borderStyle: "solid",
|
|
||||||
borderColor: "#D1D1D1",
|
|
||||||
borderRadius: 8,
|
|
||||||
boxShadow: "0px 4px 8px 0px #00000024",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Stack style={{ width: "100%" }}>
|
|
||||||
<Stack horizontal verticalAlign="center" style={{ padding: "8px 8px 0px 8px" }}>
|
|
||||||
<TextField
|
|
||||||
id="naturalLanguageInput"
|
|
||||||
value={userPrompt}
|
|
||||||
onChange={handleUserPromptChange}
|
|
||||||
onClick={() => {
|
|
||||||
inputEdited.current = true;
|
|
||||||
setShowSamplePrompts(true);
|
|
||||||
}}
|
|
||||||
onKeyDown={(e) => {
|
|
||||||
if (e.key === "Enter" && userPrompt) {
|
|
||||||
inputEdited.current = true;
|
|
||||||
startGenerateQueryProcess();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
style={{ lineHeight: 30 }}
|
|
||||||
styles={{
|
|
||||||
root: { width: "100%" },
|
|
||||||
suffix: {
|
|
||||||
background: "none",
|
|
||||||
padding: 0,
|
|
||||||
},
|
|
||||||
fieldGroup: {
|
|
||||||
borderRadius: 4,
|
|
||||||
borderColor: "#D1D1D1",
|
|
||||||
"::after": {
|
|
||||||
border: "inherit",
|
|
||||||
borderWidth: 2,
|
|
||||||
borderBottomColor: "#464FEB",
|
|
||||||
borderRadius: 4,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
disabled={isGeneratingQuery}
|
|
||||||
autoComplete="off"
|
|
||||||
placeholder="Ask a question in natural language and we’ll generate the query for you."
|
|
||||||
aria-labelledby="copilot-textfield-label"
|
|
||||||
onRenderSuffix={() => {
|
|
||||||
return (
|
|
||||||
<IconButton
|
|
||||||
iconProps={{ iconName: "Send" }}
|
|
||||||
disabled={isGeneratingQuery || !userPrompt.trim()}
|
|
||||||
style={{ background: "none" }}
|
|
||||||
onClick={() => startGenerateQueryProcess()}
|
|
||||||
aria-label="Send"
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{showPromptTeachingBubble && copilotTeachingBubbleVisible && (
|
|
||||||
<TeachingBubble
|
|
||||||
calloutProps={{ directionalHint: DirectionalHint.bottomCenter }}
|
|
||||||
target="#naturalLanguageInput"
|
|
||||||
hasCloseButton={true}
|
|
||||||
closeButtonAriaLabel="Close"
|
|
||||||
onDismiss={() => toggleCopilotTeachingBubbleVisible(false)}
|
|
||||||
hasSmallHeadline={true}
|
|
||||||
headline="Write a prompt"
|
|
||||||
>
|
|
||||||
Write a prompt here and Copilot will generate the query for you. You can also choose from our{" "}
|
|
||||||
<Link
|
|
||||||
onClick={() => {
|
|
||||||
setShowSamplePrompts(true);
|
|
||||||
toggleCopilotTeachingBubbleVisible(false);
|
|
||||||
}}
|
|
||||||
style={{ color: "white", fontWeight: 600 }}
|
|
||||||
>
|
|
||||||
sample prompts
|
|
||||||
</Link>{" "}
|
|
||||||
or write your own query
|
|
||||||
</TeachingBubble>
|
|
||||||
)}
|
|
||||||
{showSamplePrompts && (
|
|
||||||
<Callout
|
|
||||||
styles={{ root: { minWidth: 400, maxWidth: "70vw" } }}
|
|
||||||
target="#naturalLanguageInput"
|
|
||||||
isBeakVisible={false}
|
|
||||||
onDismiss={() => setShowSamplePrompts(false)}
|
|
||||||
directionalHintFixed={true}
|
|
||||||
directionalHint={DirectionalHint.bottomLeftEdge}
|
|
||||||
alignTargetEdge={true}
|
|
||||||
gapSpace={4}
|
|
||||||
>
|
|
||||||
<Stack>
|
|
||||||
{filteredHistories?.length > 0 && (
|
|
||||||
<Stack>
|
|
||||||
<Text
|
|
||||||
style={{
|
|
||||||
width: "100%",
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: 600,
|
|
||||||
color: "#0078D4",
|
|
||||||
marginLeft: 16,
|
|
||||||
padding: "4px 0",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Recent
|
|
||||||
</Text>
|
|
||||||
{filteredHistories.map((history, i) => (
|
|
||||||
<DefaultButton
|
|
||||||
key={i}
|
|
||||||
onClick={() => {
|
|
||||||
setUserPrompt(history);
|
|
||||||
setShowSamplePrompts(false);
|
|
||||||
inputEdited.current = true;
|
|
||||||
}}
|
|
||||||
onRenderIcon={() => <Image src={RecentIcon} styles={{ root: { overflow: "unset" } }} />}
|
|
||||||
styles={promptStyles}
|
|
||||||
>
|
|
||||||
{history}
|
|
||||||
</DefaultButton>
|
|
||||||
))}
|
|
||||||
</Stack>
|
|
||||||
)}
|
|
||||||
{filteredSuggestedPrompts?.length > 0 && (
|
|
||||||
<Stack>
|
|
||||||
<Text
|
|
||||||
style={{
|
|
||||||
width: "100%",
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: 600,
|
|
||||||
color: "#0078D4",
|
|
||||||
marginLeft: 16,
|
|
||||||
padding: "4px 0",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Suggested Prompts
|
|
||||||
</Text>
|
|
||||||
{filteredSuggestedPrompts.map((prompt) => (
|
|
||||||
<DefaultButton
|
|
||||||
key={prompt.id}
|
|
||||||
onClick={() => {
|
|
||||||
setUserPrompt(prompt.text);
|
|
||||||
setShowSamplePrompts(false);
|
|
||||||
inputEdited.current = true;
|
|
||||||
}}
|
|
||||||
onRenderIcon={() => <Image src={HintIcon} />}
|
|
||||||
styles={promptStyles}
|
|
||||||
>
|
|
||||||
{prompt.text}
|
|
||||||
</DefaultButton>
|
|
||||||
))}
|
|
||||||
</Stack>
|
|
||||||
)}
|
|
||||||
{(filteredHistories?.length > 0 || filteredSuggestedPrompts?.length > 0) && (
|
|
||||||
<Stack>
|
|
||||||
<Separator
|
|
||||||
styles={{
|
|
||||||
root: {
|
|
||||||
selectors: { "::before": { background: "#E1DFDD" } },
|
|
||||||
padding: 0,
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
style={{
|
|
||||||
width: "100%",
|
|
||||||
fontSize: 14,
|
|
||||||
marginLeft: 16,
|
|
||||||
padding: "4px 0",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Learn about{" "}
|
|
||||||
<Link target="_blank" href="https://aka.ms/cdb-copilot-writing">
|
|
||||||
writing effective prompts
|
|
||||||
</Link>
|
|
||||||
</Text>
|
|
||||||
</Stack>
|
|
||||||
)}
|
|
||||||
</Stack>
|
|
||||||
</Callout>
|
|
||||||
)}
|
|
||||||
</Stack>
|
|
||||||
{!isGeneratingQuery && (
|
|
||||||
<Stack style={{ padding: 8 }}>
|
|
||||||
{!showFeedbackBar && (
|
|
||||||
<Text style={{ fontSize: 12 }}>
|
|
||||||
AI-generated content can have mistakes. Make sure it's accurate and appropriate before using it.{" "}
|
|
||||||
<Link href="https://aka.ms/cdb-copilot-preview-terms" target="_blank" style={{ color: "#0072D4" }}>
|
|
||||||
Read preview terms
|
|
||||||
</Link>
|
|
||||||
{showErrorMessageBar && (
|
|
||||||
<MessageBar messageBarType={MessageBarType.error}>
|
|
||||||
{errorMessage ? errorMessage : "We ran into an error and were not able to execute query."}
|
|
||||||
</MessageBar>
|
|
||||||
)}
|
|
||||||
{showInvalidQueryMessageBar && (
|
|
||||||
<MessageBar
|
|
||||||
messageBarType={MessageBarType.info}
|
|
||||||
styles={{ root: { backgroundColor: "#F0F6FF" }, icon: { color: "#015CDA" } }}
|
|
||||||
>
|
|
||||||
We were unable to generate a query based upon the prompt provided. Please modify the prompt and
|
|
||||||
try again. For examples of how to write a good prompt, please read
|
|
||||||
<Link href="https://aka.ms/cdb-copilot-writing" target="_blank">
|
|
||||||
this article.
|
|
||||||
</Link>{" "}
|
|
||||||
Our content guidelines can be found
|
|
||||||
<Link href="https://aka.ms/cdb-query-copilot" target="_blank">
|
|
||||||
here.
|
|
||||||
</Link>
|
|
||||||
</MessageBar>
|
|
||||||
)}
|
|
||||||
</Text>
|
|
||||||
)}
|
|
||||||
{showFeedbackBar && (
|
|
||||||
<Stack horizontal verticalAlign="center" style={{ maxHeight: 20 }}>
|
|
||||||
{userContext.feedbackPolicies?.policyAllowFeedback && (
|
|
||||||
<Stack horizontal verticalAlign="center">
|
|
||||||
<Text style={{ fontSize: 12 }}>Provide feedback</Text>
|
|
||||||
{showCallout && !hideFeedbackModalForLikedQueries && (
|
|
||||||
<Callout
|
|
||||||
role="status"
|
|
||||||
style={{ padding: "6px 12px" }}
|
|
||||||
styles={{
|
|
||||||
root: {
|
|
||||||
borderRadius: 8,
|
|
||||||
},
|
|
||||||
beakCurtain: {
|
|
||||||
borderRadius: 8,
|
|
||||||
},
|
|
||||||
calloutMain: {
|
|
||||||
borderRadius: 8,
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
target="#likeBtn"
|
|
||||||
onDismiss={() => {
|
|
||||||
setShowCallout(false);
|
|
||||||
SubmitFeedback({
|
|
||||||
params: {
|
|
||||||
generatedQuery: generatedQuery,
|
|
||||||
likeQuery: likeQuery,
|
|
||||||
description: "",
|
|
||||||
userPrompt: userPrompt,
|
|
||||||
},
|
|
||||||
explorer,
|
|
||||||
databaseId,
|
|
||||||
containerId,
|
|
||||||
mode: isSampleCopilotActive ? "Sample" : "User",
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
directionalHint={DirectionalHint.topCenter}
|
|
||||||
>
|
|
||||||
<Text>
|
|
||||||
Thank you. Need to give{" "}
|
|
||||||
<Link
|
|
||||||
onClick={() => {
|
|
||||||
setShowCallout(false);
|
|
||||||
openFeedbackModal(generatedQuery, true, userPrompt);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
more feedback?
|
|
||||||
</Link>
|
|
||||||
</Text>
|
|
||||||
</Callout>
|
|
||||||
)}
|
|
||||||
<IconButton
|
|
||||||
id="likeBtn"
|
|
||||||
style={{ marginLeft: 10 }}
|
|
||||||
aria-label="Like"
|
|
||||||
role="toggle"
|
|
||||||
iconProps={{ iconName: likeQuery === true ? "LikeSolid" : "Like" }}
|
|
||||||
onClick={() => {
|
|
||||||
setShowCallout(!likeQuery);
|
|
||||||
setLikeQuery(!likeQuery);
|
|
||||||
if (likeQuery === true) {
|
|
||||||
document.getElementById("likeStatus").innerHTML = "Unpressed";
|
|
||||||
}
|
|
||||||
if (likeQuery === false) {
|
|
||||||
document.getElementById("likeStatus").innerHTML = "Liked";
|
|
||||||
}
|
|
||||||
if (dislikeQuery) {
|
|
||||||
setDislikeQuery(!dislikeQuery);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<IconButton
|
|
||||||
style={{ margin: "0 4px" }}
|
|
||||||
role="toggle"
|
|
||||||
aria-label="Dislike"
|
|
||||||
iconProps={{ iconName: dislikeQuery === true ? "DislikeSolid" : "Dislike" }}
|
|
||||||
onClick={() => {
|
|
||||||
let toggleStatusValue = "Unpressed";
|
|
||||||
if (!dislikeQuery) {
|
|
||||||
openFeedbackModal(generatedQuery, false, userPrompt);
|
|
||||||
setLikeQuery(false);
|
|
||||||
toggleStatusValue = "Disliked";
|
|
||||||
}
|
|
||||||
setDislikeQuery(!dislikeQuery);
|
|
||||||
setShowCallout(false);
|
|
||||||
document.getElementById("likeStatus").innerHTML = toggleStatusValue;
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<span role="status" style={{ position: "absolute", left: "-9999px" }} id="likeStatus"></span>
|
|
||||||
<Separator
|
|
||||||
vertical
|
|
||||||
styles={{
|
|
||||||
root: {
|
|
||||||
"::after": {
|
|
||||||
backgroundColor: "#767676",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Stack>
|
|
||||||
)}
|
|
||||||
<CommandBarButton
|
|
||||||
className="copyQuery"
|
|
||||||
onClick={copyGeneratedCode}
|
|
||||||
iconProps={{ iconName: "Copy" }}
|
|
||||||
style={{ fontSize: 12, transition: "background-color 0.3s ease", height: "100%" }}
|
|
||||||
styles={{
|
|
||||||
root: {
|
|
||||||
backgroundColor: "inherit",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Copy code
|
|
||||||
</CommandBarButton>
|
|
||||||
<CommandBarButton
|
|
||||||
className="deleteQuery"
|
|
||||||
onClick={() => {
|
|
||||||
setShowDeletePopup(true);
|
|
||||||
}}
|
|
||||||
iconProps={{ iconName: "Delete" }}
|
|
||||||
style={{ fontSize: 12, transition: "background-color 0.3s ease", height: "100%" }}
|
|
||||||
styles={{
|
|
||||||
root: {
|
|
||||||
backgroundColor: "inherit",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Clear editor
|
|
||||||
</CommandBarButton>
|
|
||||||
</Stack>
|
|
||||||
)}
|
|
||||||
</Stack>
|
|
||||||
)}
|
|
||||||
{isGeneratingQuery && (
|
|
||||||
<ProgressIndicator
|
|
||||||
label="Thinking..."
|
|
||||||
ariaLabel={getAriaLabel()}
|
|
||||||
barHeight={4}
|
|
||||||
styles={{
|
|
||||||
root: {
|
|
||||||
fontSize: 12,
|
|
||||||
width: "100%",
|
|
||||||
bottom: 0,
|
|
||||||
},
|
|
||||||
itemName: {
|
|
||||||
padding: "0px 8px",
|
|
||||||
},
|
|
||||||
itemProgress: {
|
|
||||||
borderBottomLeftRadius: 8,
|
|
||||||
borderBottomRightRadius: 8,
|
|
||||||
padding: 0,
|
|
||||||
},
|
|
||||||
progressBar: {
|
|
||||||
backgroundImage:
|
|
||||||
"linear-gradient(90deg, rgba(0, 0, 0, 0) 0%, rgb(24, 90, 189) 35%, rgb(71, 207, 250) 70%, rgb(180, 124, 248) 92%, rgba(0, 0, 0, 0))",
|
|
||||||
animationDuration: "5s",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Stack>
|
|
||||||
<IconButton
|
<IconButton
|
||||||
iconProps={{ imageProps: { src: errorIcon } }}
|
iconProps={{ imageProps: { src: errorIcon } }}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -698,10 +323,307 @@ export const QueryCopilotPromptbar: React.FC<QueryCopilotPromptProps> = ({
|
|||||||
clearFeedback();
|
clearFeedback();
|
||||||
resetMessageStates();
|
resetMessageStates();
|
||||||
}}
|
}}
|
||||||
|
styles={{
|
||||||
|
root: {
|
||||||
|
marginLeft: "auto !important",
|
||||||
|
},
|
||||||
|
}}
|
||||||
ariaLabel="Close"
|
ariaLabel="Close"
|
||||||
title="Close copilot"
|
title="Close copilot"
|
||||||
/>
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
<Stack horizontal verticalAlign="center">
|
||||||
|
<TextField
|
||||||
|
id="naturalLanguageInput"
|
||||||
|
value={userPrompt}
|
||||||
|
onChange={handleUserPromptChange}
|
||||||
|
onClick={() => {
|
||||||
|
inputEdited.current = true;
|
||||||
|
setShowSamplePrompts(true);
|
||||||
|
}}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === "Enter" && userPrompt) {
|
||||||
|
inputEdited.current = true;
|
||||||
|
startGenerateQueryProcess();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
style={{ lineHeight: 30 }}
|
||||||
|
styles={{ root: { width: "95%" }, fieldGroup: { borderRadius: 6 } }}
|
||||||
|
disabled={isGeneratingQuery}
|
||||||
|
autoComplete="off"
|
||||||
|
placeholder="Ask a question in natural language and we’ll generate the query for you."
|
||||||
|
aria-labelledby="copilot-textfield-label"
|
||||||
|
/>
|
||||||
|
{showPromptTeachingBubble && copilotTeachingBubbleVisible && (
|
||||||
|
<TeachingBubble
|
||||||
|
calloutProps={{ directionalHint: DirectionalHint.bottomCenter }}
|
||||||
|
target="#naturalLanguageInput"
|
||||||
|
hasCloseButton={true}
|
||||||
|
closeButtonAriaLabel="Close"
|
||||||
|
onDismiss={() => toggleCopilotTeachingBubbleVisible(false)}
|
||||||
|
hasSmallHeadline={true}
|
||||||
|
headline="Write a prompt"
|
||||||
|
>
|
||||||
|
Write a prompt here and Copilot will generate the query for you. You can also choose from our{" "}
|
||||||
|
<Link
|
||||||
|
onClick={() => {
|
||||||
|
setShowSamplePrompts(true);
|
||||||
|
toggleCopilotTeachingBubbleVisible(false);
|
||||||
|
}}
|
||||||
|
style={{ color: "white", fontWeight: 600 }}
|
||||||
|
>
|
||||||
|
sample prompts
|
||||||
|
</Link>{" "}
|
||||||
|
or write your own query
|
||||||
|
</TeachingBubble>
|
||||||
|
)}
|
||||||
|
<IconButton
|
||||||
|
iconProps={{ iconName: "Send" }}
|
||||||
|
disabled={isGeneratingQuery || !userPrompt.trim()}
|
||||||
|
style={{ marginLeft: 8 }}
|
||||||
|
onClick={() => startGenerateQueryProcess()}
|
||||||
|
aria-label="Send"
|
||||||
|
/>
|
||||||
|
<div role="alert" aria-label={getAriaLabel()}>
|
||||||
|
{isGeneratingQuery && <Spinner style={{ marginLeft: 8 }} />}
|
||||||
|
</div>
|
||||||
|
{showSamplePrompts && (
|
||||||
|
<Callout
|
||||||
|
styles={{ root: { minWidth: 400, maxWidth: "70vw" } }}
|
||||||
|
target="#naturalLanguageInput"
|
||||||
|
isBeakVisible={false}
|
||||||
|
onDismiss={() => setShowSamplePrompts(false)}
|
||||||
|
directionalHintFixed={true}
|
||||||
|
directionalHint={DirectionalHint.bottomLeftEdge}
|
||||||
|
alignTargetEdge={true}
|
||||||
|
gapSpace={4}
|
||||||
|
>
|
||||||
|
<Stack>
|
||||||
|
{filteredHistories?.length > 0 && (
|
||||||
|
<Stack>
|
||||||
|
<Text
|
||||||
|
style={{
|
||||||
|
width: "100%",
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: 600,
|
||||||
|
color: "#0078D4",
|
||||||
|
marginLeft: 16,
|
||||||
|
padding: "4px 0",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Recent
|
||||||
|
</Text>
|
||||||
|
{filteredHistories.map((history, i) => (
|
||||||
|
<DefaultButton
|
||||||
|
key={i}
|
||||||
|
onClick={() => {
|
||||||
|
setUserPrompt(history);
|
||||||
|
setShowSamplePrompts(false);
|
||||||
|
inputEdited.current = true;
|
||||||
|
}}
|
||||||
|
onRenderIcon={() => <Image src={RecentIcon} styles={{ root: { overflow: "unset" } }} />}
|
||||||
|
styles={promptStyles}
|
||||||
|
>
|
||||||
|
{history}
|
||||||
|
</DefaultButton>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
{filteredSuggestedPrompts?.length > 0 && (
|
||||||
|
<Stack>
|
||||||
|
<Text
|
||||||
|
style={{
|
||||||
|
width: "100%",
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: 600,
|
||||||
|
color: "#0078D4",
|
||||||
|
marginLeft: 16,
|
||||||
|
padding: "4px 0",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Suggested Prompts
|
||||||
|
</Text>
|
||||||
|
{filteredSuggestedPrompts.map((prompt) => (
|
||||||
|
<DefaultButton
|
||||||
|
key={prompt.id}
|
||||||
|
onClick={() => {
|
||||||
|
setUserPrompt(prompt.text);
|
||||||
|
setShowSamplePrompts(false);
|
||||||
|
inputEdited.current = true;
|
||||||
|
}}
|
||||||
|
onRenderIcon={() => <Image src={HintIcon} />}
|
||||||
|
styles={promptStyles}
|
||||||
|
>
|
||||||
|
{prompt.text}
|
||||||
|
</DefaultButton>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
{(filteredHistories?.length > 0 || filteredSuggestedPrompts?.length > 0) && (
|
||||||
|
<Stack>
|
||||||
|
<Separator
|
||||||
|
styles={{
|
||||||
|
root: {
|
||||||
|
selectors: { "::before": { background: "#E1DFDD" } },
|
||||||
|
padding: 0,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Text
|
||||||
|
style={{
|
||||||
|
width: "100%",
|
||||||
|
fontSize: 14,
|
||||||
|
marginLeft: 16,
|
||||||
|
padding: "4px 0",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Learn about{" "}
|
||||||
|
<Link target="_blank" href="https://aka.ms/cdb-copilot-writing">
|
||||||
|
writing effective prompts
|
||||||
|
</Link>
|
||||||
|
</Text>
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
</Callout>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
<Stack style={{ margin: "8px 0" }}>
|
||||||
|
<Text style={{ fontSize: 12 }}>
|
||||||
|
AI-generated content can have mistakes. Make sure it's accurate and appropriate before using it.{" "}
|
||||||
|
<Link href="https://aka.ms/cdb-copilot-preview-terms" target="_blank" style={{ color: "#0072D4" }}>
|
||||||
|
Read preview terms
|
||||||
|
</Link>
|
||||||
|
{showErrorMessageBar && (
|
||||||
|
<MessageBar messageBarType={MessageBarType.error}>
|
||||||
|
{errorMessage ? errorMessage : "We ran into an error and were not able to execute query."}
|
||||||
|
</MessageBar>
|
||||||
|
)}
|
||||||
|
{showInvalidQueryMessageBar && (
|
||||||
|
<MessageBar
|
||||||
|
messageBarType={MessageBarType.info}
|
||||||
|
styles={{ root: { backgroundColor: "#F0F6FF" }, icon: { color: "#015CDA" } }}
|
||||||
|
>
|
||||||
|
We were unable to generate a query based upon the prompt provided. Please modify the prompt and try again.
|
||||||
|
For examples of how to write a good prompt, please read
|
||||||
|
<Link href="https://aka.ms/cdb-copilot-writing" target="_blank">
|
||||||
|
this article.
|
||||||
|
</Link>{" "}
|
||||||
|
Our content guidelines can be found
|
||||||
|
<Link href="https://aka.ms/cdb-query-copilot" target="_blank">
|
||||||
|
here.
|
||||||
|
</Link>
|
||||||
|
</MessageBar>
|
||||||
|
)}
|
||||||
|
</Text>
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
{showFeedbackBar && (
|
||||||
|
<Stack
|
||||||
|
style={{ backgroundColor: "#FFF8F0", padding: "2px 8px", minHeight: 32 }}
|
||||||
|
horizontal
|
||||||
|
verticalAlign="center"
|
||||||
|
>
|
||||||
|
{userContext.feedbackPolicies?.policyAllowFeedback && (
|
||||||
|
<Stack horizontal verticalAlign="center">
|
||||||
|
<Text style={{ fontWeight: 600, fontSize: 12 }}>Provide feedback on the query generated</Text>
|
||||||
|
{showCallout && !hideFeedbackModalForLikedQueries && (
|
||||||
|
<Callout
|
||||||
|
role="status"
|
||||||
|
style={{ padding: 8 }}
|
||||||
|
target="#likeBtn"
|
||||||
|
onDismiss={() => {
|
||||||
|
setShowCallout(false);
|
||||||
|
SubmitFeedback({
|
||||||
|
params: {
|
||||||
|
generatedQuery: generatedQuery,
|
||||||
|
likeQuery: likeQuery,
|
||||||
|
description: "",
|
||||||
|
userPrompt: userPrompt,
|
||||||
|
},
|
||||||
|
explorer,
|
||||||
|
databaseId,
|
||||||
|
containerId,
|
||||||
|
mode: isSampleCopilotActive ? "Sample" : "User",
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
directionalHint={DirectionalHint.topCenter}
|
||||||
|
>
|
||||||
|
<Text>
|
||||||
|
Thank you. Need to give{" "}
|
||||||
|
<Link
|
||||||
|
onClick={() => {
|
||||||
|
setShowCallout(false);
|
||||||
|
openFeedbackModal(generatedQuery, true, userPrompt);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
more feedback?
|
||||||
|
</Link>
|
||||||
|
</Text>
|
||||||
|
</Callout>
|
||||||
|
)}
|
||||||
|
<IconButton
|
||||||
|
id="likeBtn"
|
||||||
|
style={{ marginLeft: 20 }}
|
||||||
|
aria-label="Like"
|
||||||
|
role="toggle"
|
||||||
|
iconProps={{ iconName: likeQuery === true ? "LikeSolid" : "Like" }}
|
||||||
|
onClick={() => {
|
||||||
|
setShowCallout(!likeQuery);
|
||||||
|
setLikeQuery(!likeQuery);
|
||||||
|
if (likeQuery === true) {
|
||||||
|
document.getElementById("likeStatus").innerHTML = "Unpressed";
|
||||||
|
}
|
||||||
|
if (likeQuery === false) {
|
||||||
|
document.getElementById("likeStatus").innerHTML = "Liked";
|
||||||
|
}
|
||||||
|
if (dislikeQuery) {
|
||||||
|
setDislikeQuery(!dislikeQuery);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<IconButton
|
||||||
|
style={{ margin: "0 10px" }}
|
||||||
|
role="toggle"
|
||||||
|
aria-label="Dislike"
|
||||||
|
iconProps={{ iconName: dislikeQuery === true ? "DislikeSolid" : "Dislike" }}
|
||||||
|
onClick={() => {
|
||||||
|
let toggleStatusValue = "Unpressed";
|
||||||
|
if (!dislikeQuery) {
|
||||||
|
openFeedbackModal(generatedQuery, false, userPrompt);
|
||||||
|
setLikeQuery(false);
|
||||||
|
toggleStatusValue = "Disliked";
|
||||||
|
}
|
||||||
|
setDislikeQuery(!dislikeQuery);
|
||||||
|
setShowCallout(false);
|
||||||
|
document.getElementById("likeStatus").innerHTML = toggleStatusValue;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<span role="status" style={{ position: "absolute", left: "-9999px" }} id="likeStatus"></span>
|
||||||
|
<Separator vertical style={{ color: "#EDEBE9" }} />
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
<CommandBarButton
|
||||||
|
className="copyQuery"
|
||||||
|
onClick={copyGeneratedCode}
|
||||||
|
iconProps={{ iconName: "Copy" }}
|
||||||
|
style={{ margin: "0 10px", backgroundColor: "#FFF8F0", transition: "background-color 0.3s ease" }}
|
||||||
|
>
|
||||||
|
Copy query
|
||||||
|
</CommandBarButton>
|
||||||
|
<CommandBarButton
|
||||||
|
className="deleteQuery"
|
||||||
|
onClick={() => {
|
||||||
|
setShowDeletePopup(true);
|
||||||
|
}}
|
||||||
|
iconProps={{ iconName: "Delete" }}
|
||||||
|
style={{ margin: "0 10px", backgroundColor: "#FFF8F0", transition: "background-color 0.3s ease" }}
|
||||||
|
>
|
||||||
|
Delete query
|
||||||
|
</CommandBarButton>
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
{isSamplePromptsOpen && <SamplePrompts sampleProps={sampleProps} />}
|
{isSamplePromptsOpen && <SamplePrompts sampleProps={sampleProps} />}
|
||||||
{query !== "" && query.trim().length !== 0 && (
|
{query !== "" && query.trim().length !== 0 && (
|
||||||
<DeletePopup
|
<DeletePopup
|
||||||
|
|||||||
@@ -104,16 +104,15 @@ export const QueryCopilotTab: React.FC<QueryCopilotProps> = ({ explorer }: Query
|
|||||||
)}
|
)}
|
||||||
<Stack className="tabPaneContentContainer">
|
<Stack className="tabPaneContentContainer">
|
||||||
<SplitterLayout percentage={true} vertical={true} primaryIndex={0} primaryMinSize={30} secondaryMinSize={70}>
|
<SplitterLayout percentage={true} vertical={true} primaryIndex={0} primaryMinSize={30} secondaryMinSize={70}>
|
||||||
COPILOT
|
|
||||||
<EditorReact
|
<EditorReact
|
||||||
language={"sql"}
|
language={"sql"}
|
||||||
content={query}
|
content={query}
|
||||||
isReadOnly={false}
|
isReadOnly={false}
|
||||||
wordWrap={"on"}
|
wordWrap={"on"}
|
||||||
ariaLabel={"Editing Query"}
|
ariaLabel={"Editing Query"}
|
||||||
lineNumbers={"on"}
|
lineNumbers={"on"}
|
||||||
onContentChanged={(newQuery: string) => setQuery(newQuery)}
|
onContentChanged={(newQuery: string) => setQuery(newQuery)}
|
||||||
onContentSelected={(selectedQuery: string) => setSelectedQuery(selectedQuery)}
|
onContentSelected={(selectedQuery: string) => setSelectedQuery(selectedQuery)}
|
||||||
/>
|
/>
|
||||||
<QueryCopilotResults />
|
<QueryCopilotResults />
|
||||||
</SplitterLayout>
|
</SplitterLayout>
|
||||||
|
|||||||
@@ -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 editable from "../../Common/EditableUtility";
|
|
||||||
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
|
|
||||||
import * as HeadersUtility from "../../Common/HeadersUtility";
|
|
||||||
import { MinimalQueryIterator } from "../../Common/IteratorUtilities";
|
|
||||||
import { Splitter, SplitterBounds, SplitterDirection } from "../../Common/Splitter";
|
|
||||||
import { createDocument } from "../../Common/dataAccess/createDocument";
|
import { createDocument } from "../../Common/dataAccess/createDocument";
|
||||||
import { deleteConflict } from "../../Common/dataAccess/deleteConflict";
|
import { deleteConflict } from "../../Common/dataAccess/deleteConflict";
|
||||||
import { deleteDocument } from "../../Common/dataAccess/deleteDocument";
|
import { deleteDocument } from "../../Common/dataAccess/deleteDocument";
|
||||||
import { queryConflicts } from "../../Common/dataAccess/queryConflicts";
|
import { queryConflicts } from "../../Common/dataAccess/queryConflicts";
|
||||||
import { updateDocument } from "../../Common/dataAccess/updateDocument";
|
import { updateDocument } from "../../Common/dataAccess/updateDocument";
|
||||||
|
import editable from "../../Common/EditableUtility";
|
||||||
|
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
|
||||||
|
import * as HeadersUtility from "../../Common/HeadersUtility";
|
||||||
|
import { MinimalQueryIterator } from "../../Common/IteratorUtilities";
|
||||||
|
import { Splitter, SplitterBounds, SplitterDirection } from "../../Common/Splitter";
|
||||||
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";
|
||||||
@@ -621,7 +621,6 @@ export default class ConflictsTab extends TabsBase {
|
|||||||
buttons.push({
|
buttons.push({
|
||||||
iconSrc: DiscardIcon,
|
iconSrc: DiscardIcon,
|
||||||
iconAlt: label,
|
iconAlt: label,
|
||||||
keyboardShortcut: "DISCARD",
|
|
||||||
onCommandClick: this.onDiscardClick,
|
onCommandClick: this.onDiscardClick,
|
||||||
commandButtonLabel: label,
|
commandButtonLabel: label,
|
||||||
ariaLabel: label,
|
ariaLabel: label,
|
||||||
|
|||||||
@@ -921,7 +921,6 @@ export default class DocumentsTab extends TabsBase {
|
|||||||
buttons.push({
|
buttons.push({
|
||||||
iconSrc: DiscardIcon,
|
iconSrc: DiscardIcon,
|
||||||
iconAlt: label,
|
iconAlt: label,
|
||||||
keyboardShortcut: "DISCARD",
|
|
||||||
onCommandClick: this.onRevertNewDocumentClick,
|
onCommandClick: this.onRevertNewDocumentClick,
|
||||||
commandButtonLabel: label,
|
commandButtonLabel: label,
|
||||||
ariaLabel: label,
|
ariaLabel: label,
|
||||||
@@ -951,7 +950,6 @@ export default class DocumentsTab extends TabsBase {
|
|||||||
buttons.push({
|
buttons.push({
|
||||||
iconSrc: DiscardIcon,
|
iconSrc: DiscardIcon,
|
||||||
iconAlt: label,
|
iconAlt: label,
|
||||||
keyboardShortcut: "DISCARD",
|
|
||||||
onCommandClick: this.onRevertExisitingDocumentClick,
|
onCommandClick: this.onRevertExisitingDocumentClick,
|
||||||
commandButtonLabel: label,
|
commandButtonLabel: label,
|
||||||
ariaLabel: label,
|
ariaLabel: label,
|
||||||
|
|||||||
@@ -3,8 +3,6 @@
|
|||||||
import { FeedOptions, QueryOperationOptions } from "@azure/cosmos";
|
import { FeedOptions, QueryOperationOptions } from "@azure/cosmos";
|
||||||
import { Platform, configContext } from "ConfigContext";
|
import { Platform, configContext } from "ConfigContext";
|
||||||
import { useDialog } from "Explorer/Controls/Dialog";
|
import { useDialog } from "Explorer/Controls/Dialog";
|
||||||
import { EditorReact } from "Explorer/Controls/Editor/EditorReact";
|
|
||||||
import { MonacoNamespace, monaco } from "Explorer/LazyMonaco";
|
|
||||||
import { QueryCopilotFeedbackModal } from "Explorer/QueryCopilot/Modal/QueryCopilotFeedbackModal";
|
import { QueryCopilotFeedbackModal } from "Explorer/QueryCopilot/Modal/QueryCopilotFeedbackModal";
|
||||||
import { useCopilotStore } from "Explorer/QueryCopilot/QueryCopilotContext";
|
import { useCopilotStore } from "Explorer/QueryCopilot/QueryCopilotContext";
|
||||||
import { QueryCopilotPromptbar } from "Explorer/QueryCopilot/QueryCopilotPromptbar";
|
import { QueryCopilotPromptbar } from "Explorer/QueryCopilot/QueryCopilotPromptbar";
|
||||||
@@ -41,6 +39,7 @@ import { userContext } from "../../../UserContext";
|
|||||||
import * as QueryUtils from "../../../Utils/QueryUtils";
|
import * as QueryUtils from "../../../Utils/QueryUtils";
|
||||||
import { useSidePanel } from "../../../hooks/useSidePanel";
|
import { useSidePanel } from "../../../hooks/useSidePanel";
|
||||||
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
|
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
|
||||||
|
import { EditorReact } from "../../Controls/Editor/EditorReact";
|
||||||
import Explorer from "../../Explorer";
|
import Explorer from "../../Explorer";
|
||||||
import { useCommandBar } from "../../Menus/CommandBar/CommandBarComponentAdapter";
|
import { useCommandBar } from "../../Menus/CommandBar/CommandBarComponentAdapter";
|
||||||
import { BrowseQueriesPane } from "../../Panes/BrowseQueriesPane/BrowseQueriesPane";
|
import { BrowseQueriesPane } from "../../Panes/BrowseQueriesPane/BrowseQueriesPane";
|
||||||
@@ -469,7 +468,6 @@ export default class QueryTabComponent extends React.Component<IQueryTabComponen
|
|||||||
buttons.push({
|
buttons.push({
|
||||||
iconSrc: CancelQueryIcon,
|
iconSrc: CancelQueryIcon,
|
||||||
iconAlt: label,
|
iconAlt: label,
|
||||||
keyboardShortcut: "CANCEL_QUERY",
|
|
||||||
onCommandClick: () => this.queryAbortController.abort(),
|
onCommandClick: () => this.queryAbortController.abort(),
|
||||||
commandButtonLabel: label,
|
commandButtonLabel: label,
|
||||||
ariaLabel: label,
|
ariaLabel: label,
|
||||||
@@ -586,15 +584,6 @@ export default class QueryTabComponent extends React.Component<IQueryTabComponen
|
|||||||
}
|
}
|
||||||
|
|
||||||
private getEditorAndQueryResult(): JSX.Element {
|
private getEditorAndQueryResult(): JSX.Element {
|
||||||
const configureEditor = (monaco: MonacoNamespace, editor: monaco.editor.IStandaloneCodeEditor) => {
|
|
||||||
editor.addAction({
|
|
||||||
id: "execute-query",
|
|
||||||
label: "Execute Query",
|
|
||||||
keybindings: [monaco.KeyMod.Shift | monaco.KeyCode.Enter],
|
|
||||||
run: () => this.onExecuteQueryClick(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<div className="tab-pane" id={this.props.tabId} role="tabpanel">
|
<div className="tab-pane" id={this.props.tabId} role="tabpanel">
|
||||||
@@ -619,8 +608,7 @@ export default class QueryTabComponent extends React.Component<IQueryTabComponen
|
|||||||
lineNumbers={"on"}
|
lineNumbers={"on"}
|
||||||
onContentChanged={(newContent: string) => this.onChangeContent(newContent)}
|
onContentChanged={(newContent: string) => this.onChangeContent(newContent)}
|
||||||
onContentSelected={(selectedContent: string) => this.onSelectedContent(selectedContent)}
|
onContentSelected={(selectedContent: string) => this.onSelectedContent(selectedContent)}
|
||||||
configureEditor={configureEditor}
|
/>
|
||||||
/>;
|
|
||||||
</div>
|
</div>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
{this.props.isSampleCopilotActive ? (
|
{this.props.isSampleCopilotActive ? (
|
||||||
|
|||||||
@@ -238,7 +238,6 @@ export default abstract class ScriptTabBase extends TabsBase implements ViewMode
|
|||||||
buttons.push({
|
buttons.push({
|
||||||
iconSrc: DiscardIcon,
|
iconSrc: DiscardIcon,
|
||||||
iconAlt: label,
|
iconAlt: label,
|
||||||
keyboardShortcut: "DISCARD",
|
|
||||||
onCommandClick: this.onDiscard,
|
onCommandClick: this.onDiscard,
|
||||||
commandButtonLabel: label,
|
commandButtonLabel: label,
|
||||||
ariaLabel: label,
|
ariaLabel: label,
|
||||||
|
|||||||
@@ -347,7 +347,6 @@ export default class StoredProcedureTabComponent extends React.Component<
|
|||||||
buttons.push({
|
buttons.push({
|
||||||
iconSrc: DiscardIcon,
|
iconSrc: DiscardIcon,
|
||||||
iconAlt: label,
|
iconAlt: label,
|
||||||
keyboardShortcut: "DISCARD",
|
|
||||||
onCommandClick: this.onDiscard,
|
onCommandClick: this.onDiscard,
|
||||||
commandButtonLabel: label,
|
commandButtonLabel: label,
|
||||||
ariaLabel: label,
|
ariaLabel: label,
|
||||||
|
|||||||
@@ -40,10 +40,11 @@ export default class TabsBase extends WaitsForTemplateViewModel {
|
|||||||
this.database = options.database;
|
this.database = options.database;
|
||||||
this.rid = options.rid || (this.collection && this.collection.rid) || "";
|
this.rid = options.rid || (this.collection && this.collection.rid) || "";
|
||||||
this.tabKind = options.tabKind;
|
this.tabKind = options.tabKind;
|
||||||
this.tabTitle = ko.observable<string>(this.getTitle(options));
|
this.tabTitle = ko.observable<string>(options.title);
|
||||||
this.tabPath =
|
this.tabPath =
|
||||||
this.collection &&
|
ko.observable(options.tabPath ?? "") ||
|
||||||
ko.observable<string>(`${this.collection.databaseId}>${this.collection.id()}>${options.title}`);
|
(this.collection &&
|
||||||
|
ko.observable<string>(`${this.collection.databaseId}>${this.collection.id()}>${this.tabTitle()}`));
|
||||||
this.pendingNotification = ko.observable<DataModels.Notification>(undefined);
|
this.pendingNotification = ko.observable<DataModels.Notification>(undefined);
|
||||||
this.onLoadStartKey = options.onLoadStartKey;
|
this.onLoadStartKey = options.onLoadStartKey;
|
||||||
this.closeTabButton = {
|
this.closeTabButton = {
|
||||||
@@ -142,26 +143,6 @@ export default class TabsBase extends WaitsForTemplateViewModel {
|
|||||||
return (this.collection && this.collection.container) || (this.database && this.database.container);
|
return (this.collection && this.collection.container) || (this.database && this.database.container);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getTitle(options: ViewModels.TabOptions): string {
|
|
||||||
const coll = this.collection?.id();
|
|
||||||
const db = this.database?.id();
|
|
||||||
if (coll) {
|
|
||||||
if (coll.length > 8) {
|
|
||||||
return coll.slice(0, 5) + "…" + options.title;
|
|
||||||
} else {
|
|
||||||
return coll + "." + options.title;
|
|
||||||
}
|
|
||||||
} else if (db) {
|
|
||||||
if (db.length > 8) {
|
|
||||||
return db.slice(0, 5) + "…" + options.title;
|
|
||||||
} else {
|
|
||||||
return db + "." + options.title;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return options.title;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Renders a Javascript object to be displayed inside Monaco Editor */
|
/** Renders a Javascript object to be displayed inside Monaco Editor */
|
||||||
public renderObjectForEditor(value: any, replacer: any, space: string | number): string {
|
public renderObjectForEditor(value: any, replacer: any, space: string | number): string {
|
||||||
return JSON.stringify(value, replacer, space);
|
return JSON.stringify(value, replacer, space);
|
||||||
|
|||||||
@@ -256,7 +256,6 @@ export class TriggerTabContent extends Component<TriggerTab, ITriggerTabContentS
|
|||||||
...this,
|
...this,
|
||||||
iconSrc: DiscardIcon,
|
iconSrc: DiscardIcon,
|
||||||
iconAlt: label,
|
iconAlt: label,
|
||||||
keyboardShortcut: "DISCARD",
|
|
||||||
onCommandClick: this.onDiscard,
|
onCommandClick: this.onDiscard,
|
||||||
commandButtonLabel: label,
|
commandButtonLabel: label,
|
||||||
ariaLabel: label,
|
ariaLabel: label,
|
||||||
|
|||||||
@@ -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";
|
||||||
@@ -109,7 +109,6 @@ export default class UserDefinedFunctionTabContent extends Component<
|
|||||||
...this,
|
...this,
|
||||||
iconSrc: DiscardIcon,
|
iconSrc: DiscardIcon,
|
||||||
iconAlt: label,
|
iconAlt: label,
|
||||||
keyboardShortcut: "DISCARD",
|
|
||||||
onCommandClick: this.onDiscard,
|
onCommandClick: this.onDiscard,
|
||||||
commandButtonLabel: label,
|
commandButtonLabel: label,
|
||||||
ariaLabel: label,
|
ariaLabel: label,
|
||||||
|
|||||||
@@ -308,7 +308,7 @@ export default class Collection implements ViewModels.Collection {
|
|||||||
collectionName: this.id(),
|
collectionName: this.id(),
|
||||||
|
|
||||||
dataExplorerArea: Constants.Areas.Tab,
|
dataExplorerArea: Constants.Areas.Tab,
|
||||||
tabTitle: "Items",
|
tabTitle: this.rawDataModel.id + " - Items",
|
||||||
});
|
});
|
||||||
this.documentIds([]);
|
this.documentIds([]);
|
||||||
|
|
||||||
@@ -316,7 +316,7 @@ export default class Collection implements ViewModels.Collection {
|
|||||||
partitionKey: this.partitionKey,
|
partitionKey: this.partitionKey,
|
||||||
documentIds: ko.observableArray<DocumentId>([]),
|
documentIds: ko.observableArray<DocumentId>([]),
|
||||||
tabKind: ViewModels.CollectionTabKind.Documents,
|
tabKind: ViewModels.CollectionTabKind.Documents,
|
||||||
title: "Items",
|
title: this.rawDataModel.id + " - Items",
|
||||||
collection: this,
|
collection: this,
|
||||||
node: this,
|
node: this,
|
||||||
tabPath: `${this.databaseId}>${this.id()}>Documents`,
|
tabPath: `${this.databaseId}>${this.id()}>Documents`,
|
||||||
|
|||||||
23
src/Main.tsx
23
src/Main.tsx
@@ -18,7 +18,6 @@ import "../externals/jquery.typeahead.min.js";
|
|||||||
// Image Dependencies
|
// Image Dependencies
|
||||||
import { Platform } from "ConfigContext";
|
import { Platform } from "ConfigContext";
|
||||||
import { QueryCopilotCarousel } from "Explorer/QueryCopilot/CopilotCarousel";
|
import { QueryCopilotCarousel } from "Explorer/QueryCopilot/CopilotCarousel";
|
||||||
import * as ReactHotkeys from "react-hotkeys";
|
|
||||||
import "../images/CosmosDB_rgb_ui_lighttheme.ico";
|
import "../images/CosmosDB_rgb_ui_lighttheme.ico";
|
||||||
import hdeConnectImage from "../images/HdeConnectCosmosDB.svg";
|
import hdeConnectImage from "../images/HdeConnectCosmosDB.svg";
|
||||||
import "../images/favicon.ico";
|
import "../images/favicon.ico";
|
||||||
@@ -62,28 +61,6 @@ import { useKnockoutExplorer } from "./hooks/useKnockoutExplorer";
|
|||||||
|
|
||||||
initializeIcons();
|
initializeIcons();
|
||||||
|
|
||||||
const tagsIgnoredByReactHotkeys = ["INPUT", "SELECT"];
|
|
||||||
ReactHotkeys.configure({
|
|
||||||
ignoreEventsCondition: (evt) => {
|
|
||||||
// The default react-hotkeys behavior is to ignore events targetting a textarea, but we want the monaco editor's key events to bubble up
|
|
||||||
// So, we configure it to ignore all events targetting a textarea except when the target is a monaco editor's text area
|
|
||||||
|
|
||||||
if (!(evt.target instanceof HTMLElement)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tagsIgnoredByReactHotkeys.includes(evt.target.tagName)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (evt.target.tagName === "TEXTAREA" && !evt.target.matches(".monaco-editor textarea")) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const App: React.FunctionComponent = () => {
|
const App: React.FunctionComponent = () => {
|
||||||
const [isLeftPaneExpanded, setIsLeftPaneExpanded] = useState<boolean>(true);
|
const [isLeftPaneExpanded, setIsLeftPaneExpanded] = useState<boolean>(true);
|
||||||
const isCarouselOpen = useCarousel((state) => state.shouldOpen);
|
const isCarouselOpen = useCarousel((state) => state.shouldOpen);
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
import { useBoolean } from "@fluentui/react-hooks";
|
import { useBoolean } from "@fluentui/react-hooks";
|
||||||
import { userContext } from "UserContext";
|
import { userContext } from "UserContext";
|
||||||
import { usePortalBackendEndpoint } from "Utils/EndpointUtils";
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import ConnectImage from "../../../../images/HdeConnectCosmosDB.svg";
|
import ConnectImage from "../../../../images/HdeConnectCosmosDB.svg";
|
||||||
import ErrorImage from "../../../../images/error.svg";
|
import ErrorImage from "../../../../images/error.svg";
|
||||||
import { AuthType } from "../../../AuthType";
|
import { AuthType } from "../../../AuthType";
|
||||||
import { BackendApi, HttpHeaders } from "../../../Common/Constants";
|
import { HttpHeaders } from "../../../Common/Constants";
|
||||||
import { configContext } from "../../../ConfigContext";
|
import { configContext } from "../../../ConfigContext";
|
||||||
import { GenerateTokenResponse } from "../../../Contracts/DataModels";
|
import { GenerateTokenResponse } from "../../../Contracts/DataModels";
|
||||||
import { isResourceTokenConnectionString } from "../Helpers/ResourceTokenUtils";
|
import { isResourceTokenConnectionString } from "../Helpers/ResourceTokenUtils";
|
||||||
@@ -19,23 +18,6 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const fetchEncryptedToken = async (connectionString: string): Promise<string> => {
|
export const fetchEncryptedToken = async (connectionString: string): Promise<string> => {
|
||||||
if (!usePortalBackendEndpoint(BackendApi.GenerateToken)) {
|
|
||||||
return await fetchEncryptedToken_ToBeDeprecated(connectionString);
|
|
||||||
}
|
|
||||||
|
|
||||||
const headers = new Headers();
|
|
||||||
headers.append(HttpHeaders.connectionString, connectionString);
|
|
||||||
const url = configContext.PORTAL_BACKEND_ENDPOINT + "/api/connectionstring/token/generatetoken";
|
|
||||||
const response = await fetch(url, { headers, method: "POST" });
|
|
||||||
if (!response.ok) {
|
|
||||||
throw response;
|
|
||||||
}
|
|
||||||
|
|
||||||
const encryptedTokenResponse: string = await response.json();
|
|
||||||
return decodeURIComponent(encryptedTokenResponse);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const fetchEncryptedToken_ToBeDeprecated = async (connectionString: string): Promise<string> => {
|
|
||||||
const headers = new Headers();
|
const headers = new Headers();
|
||||||
headers.append(HttpHeaders.connectionString, connectionString);
|
headers.append(HttpHeaders.connectionString, connectionString);
|
||||||
const url = configContext.BACKEND_ENDPOINT + "/api/guest/tokens/generateToken";
|
const url = configContext.BACKEND_ENDPOINT + "/api/guest/tokens/generateToken";
|
||||||
|
|||||||
@@ -51,7 +51,6 @@ interface FabricContext {
|
|||||||
connectionId: string;
|
connectionId: string;
|
||||||
databaseConnectionInfo: FabricDatabaseConnectionInfo | undefined;
|
databaseConnectionInfo: FabricDatabaseConnectionInfo | undefined;
|
||||||
isReadOnly: boolean;
|
isReadOnly: boolean;
|
||||||
isVisible: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AdminFeedbackControlPolicy =
|
export type AdminFeedbackControlPolicy =
|
||||||
|
|||||||
@@ -1,11 +1,4 @@
|
|||||||
import {
|
import { CassandraProxyEndpoints, JunoEndpoints, MongoProxyEndpoints } from "Common/Constants";
|
||||||
BackendApi,
|
|
||||||
CassandraProxyEndpoints,
|
|
||||||
JunoEndpoints,
|
|
||||||
MongoProxyEndpoints,
|
|
||||||
PortalBackendEndpoints,
|
|
||||||
} from "Common/Constants";
|
|
||||||
import { configContext } from "ConfigContext";
|
|
||||||
import * as Logger from "../Common/Logger";
|
import * as Logger from "../Common/Logger";
|
||||||
|
|
||||||
export function validateEndpoint(
|
export function validateEndpoint(
|
||||||
@@ -144,9 +137,3 @@ export const allowedJunoOrigins: ReadonlyArray<string> = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export const allowedNotebookServerUrls: ReadonlyArray<string> = [];
|
export const allowedNotebookServerUrls: ReadonlyArray<string> = [];
|
||||||
|
|
||||||
export function usePortalBackendEndpoint(backendApi: BackendApi): boolean {
|
|
||||||
const activePortalBackendEndpoints: string[] = [PortalBackendEndpoints.Development];
|
|
||||||
const activeBackendApi: boolean = configContext.NEW_BACKEND_APIS?.includes(backendApi) || false;
|
|
||||||
return activeBackendApi && activePortalBackendEndpoints.includes(configContext.PORTAL_BACKEND_ENDPOINT as string);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { createUri } from "Common/UrlUtility";
|
|||||||
import { DATA_EXPLORER_RPC_VERSION } from "Contracts/DataExplorerMessagesContract";
|
import { DATA_EXPLORER_RPC_VERSION } from "Contracts/DataExplorerMessagesContract";
|
||||||
import { FABRIC_RPC_VERSION, FabricMessageV2 } from "Contracts/FabricMessagesContract";
|
import { FABRIC_RPC_VERSION, FabricMessageV2 } from "Contracts/FabricMessagesContract";
|
||||||
import Explorer from "Explorer/Explorer";
|
import Explorer from "Explorer/Explorer";
|
||||||
|
import { useCommandBar } from "Explorer/Menus/CommandBar/CommandBarComponentAdapter";
|
||||||
import { useSelectedNode } from "Explorer/useSelectedNode";
|
import { useSelectedNode } from "Explorer/useSelectedNode";
|
||||||
import { scheduleRefreshDatabaseResourceToken } from "Platform/Fabric/FabricUtil";
|
import { scheduleRefreshDatabaseResourceToken } from "Platform/Fabric/FabricUtil";
|
||||||
import { getNetworkSettingsWarningMessage } from "Utils/NetworkUtility";
|
import { getNetworkSettingsWarningMessage } from "Utils/NetworkUtility";
|
||||||
@@ -89,7 +90,6 @@ async function configureFabric(): Promise<Explorer> {
|
|||||||
// These are the versions of Fabric that Data Explorer supports.
|
// These are the versions of Fabric that Data Explorer supports.
|
||||||
const SUPPORTED_FABRIC_VERSIONS = [FABRIC_RPC_VERSION];
|
const SUPPORTED_FABRIC_VERSIONS = [FABRIC_RPC_VERSION];
|
||||||
|
|
||||||
let firstContainerOpened = false;
|
|
||||||
let explorer: Explorer;
|
let explorer: Explorer;
|
||||||
return new Promise<Explorer>((resolve) => {
|
return new Promise<Explorer>((resolve) => {
|
||||||
window.addEventListener(
|
window.addEventListener(
|
||||||
@@ -121,10 +121,7 @@ async function configureFabric(): Promise<Explorer> {
|
|||||||
await scheduleRefreshDatabaseResourceToken(true);
|
await scheduleRefreshDatabaseResourceToken(true);
|
||||||
resolve(explorer);
|
resolve(explorer);
|
||||||
await explorer.refreshAllDatabases();
|
await explorer.refreshAllDatabases();
|
||||||
if (userContext.fabricContext.isVisible && !firstContainerOpened) {
|
openFirstContainer(explorer, userContext.fabricContext.databaseConnectionInfo.databaseId);
|
||||||
firstContainerOpened = true;
|
|
||||||
openFirstContainer(explorer, userContext.fabricContext.databaseConnectionInfo.databaseId);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "newContainer":
|
case "newContainer":
|
||||||
@@ -135,16 +132,8 @@ async function configureFabric(): Promise<Explorer> {
|
|||||||
handleCachedDataMessage(data);
|
handleCachedDataMessage(data);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "explorerVisible": {
|
case "setToolbarStatus": {
|
||||||
userContext.fabricContext.isVisible = data.message.visible;
|
useCommandBar.getState().setIsHidden(data.message.visible === false);
|
||||||
if (
|
|
||||||
userContext.fabricContext.isVisible &&
|
|
||||||
!firstContainerOpened &&
|
|
||||||
userContext?.fabricContext?.databaseConnectionInfo?.databaseId !== undefined
|
|
||||||
) {
|
|
||||||
firstContainerOpened = true;
|
|
||||||
openFirstContainer(explorer, userContext.fabricContext.databaseConnectionInfo.databaseId);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@@ -338,13 +327,12 @@ function configureHostedWithResourceToken(config: ResourceToken): Explorer {
|
|||||||
return explorer;
|
return explorer;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createExplorerFabric(params: { connectionId: string; isVisible: boolean }): Explorer {
|
function createExplorerFabric(params: { connectionId: string }): Explorer {
|
||||||
updateUserContext({
|
updateUserContext({
|
||||||
fabricContext: {
|
fabricContext: {
|
||||||
connectionId: params.connectionId,
|
connectionId: params.connectionId,
|
||||||
databaseConnectionInfo: undefined,
|
databaseConnectionInfo: undefined,
|
||||||
isReadOnly: true,
|
isReadOnly: true,
|
||||||
isVisible: params.isVisible ?? true,
|
|
||||||
},
|
},
|
||||||
authType: AuthType.ConnectionString,
|
authType: AuthType.ConnectionString,
|
||||||
databaseAccount: {
|
databaseAccount: {
|
||||||
|
|||||||
Reference in New Issue
Block a user