mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2026-01-08 03:57:31 +00:00
Compare commits
1 Commits
users/chsk
...
dependabot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
40a530d572 |
1456
package-lock.json
generated
1456
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -15,7 +15,7 @@
|
||||
"@fluentui/react": "8.14.3",
|
||||
"@fluentui/react-components": "9.32.1",
|
||||
"@jupyterlab/services": "6.0.2",
|
||||
"@jupyterlab/terminal": "3.0.3",
|
||||
"@jupyterlab/terminal": "4.0.8",
|
||||
"@microsoft/applicationinsights-web": "2.6.1",
|
||||
"@nteract/commutable": "7.4.2",
|
||||
"@nteract/connected-components": "6.8.2",
|
||||
|
||||
@@ -171,7 +171,6 @@ export class Areas {
|
||||
public static Tab: string = "Tab";
|
||||
public static ShareDialog: string = "Share Access Dialog";
|
||||
public static Notebook: string = "Notebook";
|
||||
public static Copilot: string = "Copilot";
|
||||
}
|
||||
|
||||
export class HttpHeaders {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { JunoEndpoints } from "Common/Constants";
|
||||
import {
|
||||
allowedAadEndpoints,
|
||||
allowedArcadiaEndpoints,
|
||||
@@ -79,7 +78,7 @@ let configContext: Readonly<ConfigContext> = {
|
||||
ARCADIA_LIVY_ENDPOINT_DNS_ZONE: "dev.azuresynapse.net",
|
||||
GITHUB_CLIENT_ID: "6cb2f63cf6f7b5cbdeca", // Registered OAuth app: https://github.com/organizations/AzureCosmosDBNotebooks/settings/applications/1189306
|
||||
GITHUB_TEST_ENV_CLIENT_ID: "b63fc8cbf87fd3c6e2eb", // Registered OAuth app: https://github.com/organizations/AzureCosmosDBNotebooks/settings/applications/1777772
|
||||
JUNO_ENDPOINT: JunoEndpoints.Prod,
|
||||
JUNO_ENDPOINT: "https://tools.cosmos.azure.com",
|
||||
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com",
|
||||
isTerminalEnabled: false,
|
||||
isPhoenixEnabled: false,
|
||||
|
||||
@@ -159,7 +159,6 @@ export interface Collection extends Resource {
|
||||
geospatialConfig?: GeospatialConfig;
|
||||
schema?: ISchema;
|
||||
requestSchema?: () => void;
|
||||
computedProperties?: ComputedProperties;
|
||||
}
|
||||
|
||||
export interface CollectionsWithPagination {
|
||||
@@ -198,13 +197,6 @@ export interface IndexingPolicy {
|
||||
spatialIndexes?: any;
|
||||
}
|
||||
|
||||
export interface ComputedProperty {
|
||||
name: string;
|
||||
query: string;
|
||||
}
|
||||
|
||||
export type ComputedProperties = ComputedProperty[];
|
||||
|
||||
export interface PartitionKey {
|
||||
paths: string[];
|
||||
kind: "Hash" | "Range" | "MultiHash";
|
||||
@@ -465,11 +457,8 @@ export interface ContainerInfo {
|
||||
}
|
||||
|
||||
export interface IProvisionData {
|
||||
cosmosEndpoint?: string;
|
||||
cosmosEndpoint: string;
|
||||
poolId: string;
|
||||
databaseId?: string;
|
||||
containerId?: string;
|
||||
mode?: string;
|
||||
}
|
||||
|
||||
export interface IContainerData {
|
||||
@@ -612,14 +601,3 @@ export enum PhoenixErrorType {
|
||||
PhoenixFlightFallback = "PhoenixFlightFallback",
|
||||
UserMissingPermissionsError = "UserMissingPermissionsError",
|
||||
}
|
||||
|
||||
export interface CopilotEnabledConfiguration {
|
||||
isEnabled: boolean;
|
||||
}
|
||||
|
||||
export interface FeatureRegistration {
|
||||
name: string;
|
||||
properties: {
|
||||
state: string;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -135,7 +135,6 @@ export interface Collection extends CollectionBase {
|
||||
changeFeedPolicy: ko.Observable<DataModels.ChangeFeedPolicy>;
|
||||
geospatialConfig: ko.Observable<DataModels.GeospatialConfig>;
|
||||
documentIds: ko.ObservableArray<DocumentId>;
|
||||
computedProperties: ko.Observable<DataModels.ComputedProperties>;
|
||||
|
||||
cassandraKeys: CassandraTableKeys;
|
||||
cassandraSchema: CassandraTableKey[];
|
||||
|
||||
@@ -48,7 +48,7 @@ export class EditorReact extends React.Component<EditorReactProps, EditorReactSt
|
||||
|
||||
public componentDidUpdate(previous: EditorReactProps) {
|
||||
if (this.props.content !== previous.content) {
|
||||
this.editor?.setValue(this.props.content);
|
||||
this.editor.setValue(this.props.content);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ export class EditorReact extends React.Component<EditorReactProps, EditorReactSt
|
||||
|
||||
this.rootNode.innerHTML = "";
|
||||
const monaco = await loadMonaco();
|
||||
createCallback(monaco?.editor?.create(this.rootNode, options));
|
||||
createCallback(monaco.editor.create(this.rootNode, options));
|
||||
|
||||
if (this.rootNode.innerHTML) {
|
||||
this.setState({
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
import { IPivotItemProps, IPivotProps, Pivot, PivotItem } from "@fluentui/react";
|
||||
import {
|
||||
ComputedPropertiesComponent,
|
||||
ComputedPropertiesComponentProps,
|
||||
} from "Explorer/Controls/Settings/SettingsSubComponents/ComputedPropertiesComponent";
|
||||
import { useDatabases } from "Explorer/useDatabases";
|
||||
import * as React from "react";
|
||||
import DiscardIcon from "../../../../images/discard.svg";
|
||||
@@ -107,11 +103,6 @@ export interface SettingsComponentState {
|
||||
indexesToAdd: AddMongoIndexProps[];
|
||||
indexTransformationProgress: number;
|
||||
|
||||
computedPropertiesContent: DataModels.ComputedProperties;
|
||||
computedPropertiesContentBaseline: DataModels.ComputedProperties;
|
||||
shouldDiscardComputedProperties: boolean;
|
||||
isComputedPropertiesDirty: boolean;
|
||||
|
||||
conflictResolutionPolicyMode: DataModels.ConflictResolutionMode;
|
||||
conflictResolutionPolicyModeBaseline: DataModels.ConflictResolutionMode;
|
||||
conflictResolutionPolicyPath: string;
|
||||
@@ -136,7 +127,6 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
private offer: DataModels.Offer;
|
||||
private changeFeedPolicyVisible: boolean;
|
||||
private isFixedContainer: boolean;
|
||||
private shouldShowComputedPropertiesEditor: boolean;
|
||||
private shouldShowIndexingPolicyEditor: boolean;
|
||||
private totalThroughputUsed: number;
|
||||
public mongoDBCollectionResource: MongoDBCollectionResource;
|
||||
@@ -149,7 +139,6 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
this.collection = this.props.settingsTab.collection as ViewModels.Collection;
|
||||
this.offer = this.collection?.offer();
|
||||
this.isAnalyticalStorageEnabled = !!this.collection?.analyticalStorageTtl();
|
||||
this.shouldShowComputedPropertiesEditor = userContext.apiType === "SQL";
|
||||
this.shouldShowIndexingPolicyEditor = userContext.apiType !== "Cassandra" && userContext.apiType !== "Mongo";
|
||||
|
||||
this.changeFeedPolicyVisible = userContext.features.enableChangeFeedPolicy;
|
||||
@@ -202,11 +191,6 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
isMongoIndexingPolicyDiscardable: false,
|
||||
indexTransformationProgress: undefined,
|
||||
|
||||
computedPropertiesContent: undefined,
|
||||
computedPropertiesContentBaseline: undefined,
|
||||
shouldDiscardComputedProperties: false,
|
||||
isComputedPropertiesDirty: false,
|
||||
|
||||
conflictResolutionPolicyMode: undefined,
|
||||
conflictResolutionPolicyModeBaseline: undefined,
|
||||
conflictResolutionPolicyPath: undefined,
|
||||
@@ -297,7 +281,6 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
this.state.isSubSettingsSaveable ||
|
||||
this.state.isIndexingPolicyDirty ||
|
||||
this.state.isConflictResolutionDirty ||
|
||||
this.state.isComputedPropertiesDirty ||
|
||||
(!!this.state.currentMongoIndexes && this.state.isMongoIndexingPolicySaveable)
|
||||
);
|
||||
};
|
||||
@@ -308,7 +291,6 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
this.state.isSubSettingsDiscardable ||
|
||||
this.state.isIndexingPolicyDirty ||
|
||||
this.state.isConflictResolutionDirty ||
|
||||
this.state.isComputedPropertiesDirty ||
|
||||
(!!this.state.currentMongoIndexes && this.state.isMongoIndexingPolicyDiscardable)
|
||||
);
|
||||
};
|
||||
@@ -413,9 +395,6 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
isMongoIndexingPolicySaveable: false,
|
||||
isMongoIndexingPolicyDiscardable: false,
|
||||
isConflictResolutionDirty: false,
|
||||
computedPropertiesContent: this.state.computedPropertiesContentBaseline,
|
||||
shouldDiscardComputedProperties: true,
|
||||
isComputedPropertiesDirty: false,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -535,31 +514,6 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
private onMongoIndexingPolicyDiscardableChange = (isMongoIndexingPolicyDiscardable: boolean): void =>
|
||||
this.setState({ isMongoIndexingPolicyDiscardable });
|
||||
|
||||
private onComputedPropertiesContentChange = (newComputedProperties: DataModels.ComputedProperties): void =>
|
||||
this.setState({ computedPropertiesContent: newComputedProperties });
|
||||
|
||||
private resetShouldDiscardComputedProperties = (): void => this.setState({ shouldDiscardComputedProperties: false });
|
||||
|
||||
private logComputedPropertiesSuccessMessage = (): void => {
|
||||
if (this.props.settingsTab.onLoadStartKey) {
|
||||
traceSuccess(
|
||||
Action.Tab,
|
||||
{
|
||||
databaseName: this.collection.databaseId,
|
||||
collectionName: this.collection.id(),
|
||||
|
||||
dataExplorerArea: Constants.Areas.Tab,
|
||||
tabTitle: this.props.settingsTab.tabTitle(),
|
||||
},
|
||||
this.props.settingsTab.onLoadStartKey
|
||||
);
|
||||
this.props.settingsTab.onLoadStartKey = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
private onComputedPropertiesDirtyChange = (isComputedPropertiesDirty: boolean): void =>
|
||||
this.setState({ isComputedPropertiesDirty: isComputedPropertiesDirty });
|
||||
|
||||
private calculateTotalThroughputUsed = (): void => {
|
||||
this.totalThroughputUsed = 0;
|
||||
(useDatabases.getState().databases || []).forEach(async (database) => {
|
||||
@@ -682,6 +636,7 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
const indexingPolicyContent = this.collection.indexingPolicy();
|
||||
const conflictResolutionPolicy: DataModels.ConflictResolutionPolicy =
|
||||
this.collection.conflictResolutionPolicy && this.collection.conflictResolutionPolicy();
|
||||
|
||||
const conflictResolutionPolicyMode = parseConflictResolutionMode(conflictResolutionPolicy?.mode);
|
||||
const conflictResolutionPolicyPath = conflictResolutionPolicy?.conflictResolutionPath;
|
||||
const conflictResolutionPolicyProcedure = parseConflictResolutionProcedure(
|
||||
@@ -690,12 +645,6 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
const geospatialConfigTypeString: string =
|
||||
(this.collection.geospatialConfig && this.collection.geospatialConfig()?.type) || GeospatialConfigType.Geometry;
|
||||
const geoSpatialConfigType = GeospatialConfigType[geospatialConfigTypeString as keyof typeof GeospatialConfigType];
|
||||
let computedPropertiesContent = this.collection.computedProperties();
|
||||
if (!computedPropertiesContent || computedPropertiesContent.length === 0) {
|
||||
computedPropertiesContent = [
|
||||
{ name: "name_of_property", query: "query_to_compute_property" },
|
||||
] as DataModels.ComputedProperties;
|
||||
}
|
||||
|
||||
return {
|
||||
throughput: offerThroughput,
|
||||
@@ -722,8 +671,6 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
conflictResolutionPolicyProcedureBaseline: conflictResolutionPolicyProcedure,
|
||||
geospatialConfigType: geoSpatialConfigType,
|
||||
geospatialConfigTypeBaseline: geoSpatialConfigType,
|
||||
computedPropertiesContent: computedPropertiesContent,
|
||||
computedPropertiesContentBaseline: computedPropertiesContent,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -840,12 +787,7 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
private saveCollectionSettings = async (startKey: number): Promise<void> => {
|
||||
const newCollection: DataModels.Collection = { ...this.collection.rawDataModel };
|
||||
|
||||
if (
|
||||
this.state.isSubSettingsSaveable ||
|
||||
this.state.isIndexingPolicyDirty ||
|
||||
this.state.isConflictResolutionDirty ||
|
||||
this.state.isComputedPropertiesDirty
|
||||
) {
|
||||
if (this.state.isSubSettingsSaveable || this.state.isIndexingPolicyDirty || this.state.isConflictResolutionDirty) {
|
||||
let defaultTtl: number;
|
||||
switch (this.state.timeToLive) {
|
||||
case TtlType.On:
|
||||
@@ -883,10 +825,6 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
newCollection.conflictResolutionPolicy = conflictResolutionChanges;
|
||||
}
|
||||
|
||||
if (this.state.isComputedPropertiesDirty) {
|
||||
newCollection.computedProperties = this.state.computedPropertiesContent;
|
||||
}
|
||||
|
||||
const updatedCollection: DataModels.Collection = await updateCollection(
|
||||
this.collection.databaseId,
|
||||
this.collection.id(),
|
||||
@@ -900,7 +838,6 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
this.collection.conflictResolutionPolicy(updatedCollection.conflictResolutionPolicy);
|
||||
this.collection.changeFeedPolicy(updatedCollection.changeFeedPolicy);
|
||||
this.collection.geospatialConfig(updatedCollection.geospatialConfig);
|
||||
this.collection.computedProperties(updatedCollection.computedProperties);
|
||||
|
||||
if (wasIndexingPolicyModified) {
|
||||
await this.refreshIndexTransformationProgress();
|
||||
@@ -911,7 +848,6 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
isSubSettingsDiscardable: false,
|
||||
isIndexingPolicyDirty: false,
|
||||
isConflictResolutionDirty: false,
|
||||
isComputedPropertiesDirty: false,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1106,16 +1042,6 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
onMongoIndexingPolicyDiscardableChange: this.onMongoIndexingPolicyDiscardableChange,
|
||||
};
|
||||
|
||||
const computedPropertiesComponentProps: ComputedPropertiesComponentProps = {
|
||||
computedPropertiesContent: this.state.computedPropertiesContent,
|
||||
computedPropertiesContentBaseline: this.state.computedPropertiesContentBaseline,
|
||||
logComputedPropertiesSuccessMessage: this.logComputedPropertiesSuccessMessage,
|
||||
onComputedPropertiesContentChange: this.onComputedPropertiesContentChange,
|
||||
onComputedPropertiesDirtyChange: this.onComputedPropertiesDirtyChange,
|
||||
resetShouldDiscardComputedProperties: this.resetShouldDiscardComputedProperties,
|
||||
shouldDiscardComputedProperties: this.state.shouldDiscardComputedProperties,
|
||||
};
|
||||
|
||||
const conflictResolutionPolicyComponentProps: ConflictResolutionComponentProps = {
|
||||
collection: this.collection,
|
||||
conflictResolutionPolicyMode: this.state.conflictResolutionPolicyMode,
|
||||
@@ -1158,13 +1084,6 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
}
|
||||
}
|
||||
|
||||
if (this.shouldShowComputedPropertiesEditor) {
|
||||
tabs.push({
|
||||
tab: SettingsV2TabTypes.ComputedPropertiesTab,
|
||||
content: <ComputedPropertiesComponent {...computedPropertiesComponentProps} />,
|
||||
});
|
||||
}
|
||||
|
||||
if (this.hasConflictResolution()) {
|
||||
tabs.push({
|
||||
tab: SettingsV2TabTypes.ConflictResolutionTab,
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
getThroughputApplyLongDelayMessage,
|
||||
getThroughputApplyShortDelayMessage,
|
||||
getToolTipContainer,
|
||||
indexingPolicynUnsavedWarningMessage,
|
||||
manualToAutoscaleDisclaimerElement,
|
||||
mongoIndexTransformationRefreshingMessage,
|
||||
mongoIndexingPolicyAADError,
|
||||
@@ -38,6 +39,7 @@ class SettingsRenderUtilsTestComponent extends React.Component {
|
||||
|
||||
{manualToAutoscaleDisclaimerElement}
|
||||
{ttlWarning}
|
||||
{indexingPolicynUnsavedWarningMessage}
|
||||
{updateThroughputDelayedApplyWarningMessage}
|
||||
|
||||
{getThroughputApplyDelayedMessage(false, 1000, "RU/s", "sampleDb", "sampleCollection", 2000)}
|
||||
|
||||
@@ -61,8 +61,6 @@ export interface PriceBreakdown {
|
||||
currencySign: string;
|
||||
}
|
||||
|
||||
export type editorType = "indexPolicy" | "computedProperties";
|
||||
|
||||
export const infoAndToolTipTextStyle: ITextStyles = { root: { fontSize: 14, color: "windowtext" } };
|
||||
|
||||
export const noLeftPaddingCheckBoxStyle: ICheckboxStyles = {
|
||||
@@ -256,10 +254,9 @@ export const ttlWarning: JSX.Element = (
|
||||
</Text>
|
||||
);
|
||||
|
||||
export const unsavedEditorWarningMessage = (editor: editorType): JSX.Element => (
|
||||
export const indexingPolicynUnsavedWarningMessage: JSX.Element = (
|
||||
<Text styles={infoAndToolTipTextStyle}>
|
||||
You have not saved the latest changes made to your{" "}
|
||||
{editor === "indexPolicy" ? "indexing policy" : "computed properties"}. Please click save to confirm the changes.
|
||||
You have not saved the latest changes made to your indexing policy. Please click save to confirm the changes.
|
||||
</Text>
|
||||
);
|
||||
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
import * as DataModels from "Contracts/DataModels";
|
||||
import { shallow } from "enzyme";
|
||||
import React from "react";
|
||||
import { ComputedPropertiesComponent, ComputedPropertiesComponentProps } from "./ComputedPropertiesComponent";
|
||||
|
||||
describe("ComputedPropertiesComponent", () => {
|
||||
const initialComputedPropertiesContent: DataModels.ComputedProperties = [
|
||||
{
|
||||
name: "prop1",
|
||||
query: "query1",
|
||||
},
|
||||
];
|
||||
const baseProps: ComputedPropertiesComponentProps = {
|
||||
computedPropertiesContent: initialComputedPropertiesContent,
|
||||
computedPropertiesContentBaseline: initialComputedPropertiesContent,
|
||||
logComputedPropertiesSuccessMessage: () => {
|
||||
return;
|
||||
},
|
||||
onComputedPropertiesContentChange: () => {
|
||||
return;
|
||||
},
|
||||
onComputedPropertiesDirtyChange: () => {
|
||||
return;
|
||||
},
|
||||
resetShouldDiscardComputedProperties: () => {
|
||||
return;
|
||||
},
|
||||
shouldDiscardComputedProperties: false,
|
||||
};
|
||||
|
||||
it("renders", () => {
|
||||
const wrapper = shallow(<ComputedPropertiesComponent {...baseProps} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("computed properties are reset", () => {
|
||||
const wrapper = shallow(<ComputedPropertiesComponent {...baseProps} />);
|
||||
|
||||
const computedPropertiesComponentInstance = wrapper.instance() as ComputedPropertiesComponent;
|
||||
const resetComputedPropertiesEditorMockFn = jest.fn();
|
||||
computedPropertiesComponentInstance.resetComputedPropertiesEditor = resetComputedPropertiesEditorMockFn;
|
||||
|
||||
wrapper.setProps({ shouldDiscardComputedProperties: true });
|
||||
wrapper.update();
|
||||
expect(resetComputedPropertiesEditorMockFn.mock.calls.length).toEqual(1);
|
||||
});
|
||||
|
||||
it("dirty is set", () => {
|
||||
let computedPropertiesComponent = new ComputedPropertiesComponent(baseProps);
|
||||
expect(computedPropertiesComponent.IsComponentDirty()).toEqual(false);
|
||||
|
||||
const newProps = { ...baseProps, computedPropertiesContent: undefined as DataModels.ComputedProperties };
|
||||
computedPropertiesComponent = new ComputedPropertiesComponent(newProps);
|
||||
expect(computedPropertiesComponent.IsComponentDirty()).toEqual(true);
|
||||
});
|
||||
});
|
||||
@@ -1,128 +0,0 @@
|
||||
import { FontIcon, Link, MessageBar, MessageBarType, Stack, Text } from "@fluentui/react";
|
||||
import * as DataModels from "Contracts/DataModels";
|
||||
import { titleAndInputStackProps, unsavedEditorWarningMessage } from "Explorer/Controls/Settings/SettingsRenderUtils";
|
||||
import { isDirty } from "Explorer/Controls/Settings/SettingsUtils";
|
||||
import { loadMonaco } from "Explorer/LazyMonaco";
|
||||
import * as monaco from "monaco-editor";
|
||||
import * as React from "react";
|
||||
|
||||
export interface ComputedPropertiesComponentProps {
|
||||
computedPropertiesContent: DataModels.ComputedProperties;
|
||||
computedPropertiesContentBaseline: DataModels.ComputedProperties;
|
||||
logComputedPropertiesSuccessMessage: () => void;
|
||||
onComputedPropertiesContentChange: (newComputedProperties: DataModels.ComputedProperties) => void;
|
||||
onComputedPropertiesDirtyChange: (isComputedPropertiesDirty: boolean) => void;
|
||||
resetShouldDiscardComputedProperties: () => void;
|
||||
shouldDiscardComputedProperties: boolean;
|
||||
}
|
||||
|
||||
interface ComputedPropertiesComponentState {
|
||||
computedPropertiesContentIsValid: boolean;
|
||||
}
|
||||
|
||||
export class ComputedPropertiesComponent extends React.Component<
|
||||
ComputedPropertiesComponentProps,
|
||||
ComputedPropertiesComponentState
|
||||
> {
|
||||
private shouldCheckComponentIsDirty = true;
|
||||
private computedPropertiesDiv = React.createRef<HTMLDivElement>();
|
||||
private computedPropertiesEditor: monaco.editor.IStandaloneCodeEditor;
|
||||
|
||||
constructor(props: ComputedPropertiesComponentProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
computedPropertiesContentIsValid: true,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidUpdate(): void {
|
||||
if (this.props.shouldDiscardComputedProperties) {
|
||||
this.resetComputedPropertiesEditor();
|
||||
this.props.resetShouldDiscardComputedProperties();
|
||||
}
|
||||
this.onComponentUpdate();
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
this.resetComputedPropertiesEditor();
|
||||
this.onComponentUpdate();
|
||||
}
|
||||
|
||||
public resetComputedPropertiesEditor = (): void => {
|
||||
if (!this.computedPropertiesEditor) {
|
||||
this.createComputedPropertiesEditor();
|
||||
} else {
|
||||
const indexingPolicyEditorModel = this.computedPropertiesEditor.getModel();
|
||||
const value: string = JSON.stringify(this.props.computedPropertiesContent, undefined, 4);
|
||||
indexingPolicyEditorModel.setValue(value);
|
||||
}
|
||||
this.onComponentUpdate();
|
||||
};
|
||||
|
||||
private onComponentUpdate = (): void => {
|
||||
if (!this.shouldCheckComponentIsDirty) {
|
||||
this.shouldCheckComponentIsDirty = true;
|
||||
return;
|
||||
}
|
||||
this.props.onComputedPropertiesDirtyChange(this.IsComponentDirty());
|
||||
this.shouldCheckComponentIsDirty = false;
|
||||
};
|
||||
|
||||
public IsComponentDirty = (): boolean => {
|
||||
if (
|
||||
isDirty(this.props.computedPropertiesContent, this.props.computedPropertiesContentBaseline) &&
|
||||
this.state.computedPropertiesContentIsValid
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
private async createComputedPropertiesEditor(): Promise<void> {
|
||||
const value: string = JSON.stringify(this.props.computedPropertiesContent, undefined, 4);
|
||||
const monaco = await loadMonaco();
|
||||
this.computedPropertiesEditor = monaco.editor.create(this.computedPropertiesDiv.current, {
|
||||
value: value,
|
||||
language: "json",
|
||||
ariaLabel: "Computed properties",
|
||||
});
|
||||
if (this.computedPropertiesEditor) {
|
||||
const computedPropertiesEditorModel = this.computedPropertiesEditor.getModel();
|
||||
computedPropertiesEditorModel.onDidChangeContent(this.onEditorContentChange.bind(this));
|
||||
this.props.logComputedPropertiesSuccessMessage();
|
||||
}
|
||||
}
|
||||
|
||||
private onEditorContentChange = (): void => {
|
||||
const computedPropertiesEditorModel = this.computedPropertiesEditor.getModel();
|
||||
try {
|
||||
const newComputedPropertiesContent = JSON.parse(
|
||||
computedPropertiesEditorModel.getValue()
|
||||
) as DataModels.ComputedProperties;
|
||||
this.props.onComputedPropertiesContentChange(newComputedPropertiesContent);
|
||||
this.setState({ computedPropertiesContentIsValid: true });
|
||||
} catch (e) {
|
||||
this.setState({ computedPropertiesContentIsValid: false });
|
||||
}
|
||||
};
|
||||
|
||||
public render(): JSX.Element {
|
||||
return (
|
||||
<Stack {...titleAndInputStackProps}>
|
||||
{isDirty(this.props.computedPropertiesContent, this.props.computedPropertiesContentBaseline) && (
|
||||
<MessageBar messageBarType={MessageBarType.warning}>
|
||||
{unsavedEditorWarningMessage("computedProperties")}
|
||||
</MessageBar>
|
||||
)}
|
||||
<Text style={{ marginLeft: "30px", marginBottom: "10px" }}>
|
||||
<Link target="_blank" href="https://aka.ms/computed-properties-preview/">
|
||||
{"Learn more"} <FontIcon iconName="NavigateExternalInline" />
|
||||
</Link>
|
||||
  about how to define computed properties and how to use them.
|
||||
</Text>
|
||||
<div className="settingsV2IndexingPolicyEditor" tabIndex={0} ref={this.computedPropertiesDiv}></div>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ import * as monaco from "monaco-editor";
|
||||
import * as React from "react";
|
||||
import * as DataModels from "../../../../Contracts/DataModels";
|
||||
import { loadMonaco } from "../../../LazyMonaco";
|
||||
import { titleAndInputStackProps, unsavedEditorWarningMessage } from "../SettingsRenderUtils";
|
||||
import { indexingPolicynUnsavedWarningMessage, titleAndInputStackProps } from "../SettingsRenderUtils";
|
||||
import { isDirty, isIndexTransforming } from "../SettingsUtils";
|
||||
import { IndexingPolicyRefreshComponent } from "./IndexingPolicyRefresh/IndexingPolicyRefreshComponent";
|
||||
|
||||
@@ -120,7 +120,7 @@ export class IndexingPolicyComponent extends React.Component<
|
||||
refreshIndexTransformationProgress={this.props.refreshIndexTransformationProgress}
|
||||
/>
|
||||
{isDirty(this.props.indexingPolicyContent, this.props.indexingPolicyContentBaseline) && (
|
||||
<MessageBar messageBarType={MessageBarType.warning}>{unsavedEditorWarningMessage("indexPolicy")}</MessageBar>
|
||||
<MessageBar messageBarType={MessageBarType.warning}>{indexingPolicynUnsavedWarningMessage}</MessageBar>
|
||||
)}
|
||||
<div className="settingsV2IndexingPolicyEditor" tabIndex={0} ref={this.indexingPolicyDiv}></div>
|
||||
</Stack>
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
addMongoIndexStackProps,
|
||||
createAndAddMongoIndexStackProps,
|
||||
customDetailsListStyles,
|
||||
indexingPolicynUnsavedWarningMessage,
|
||||
infoAndToolTipTextStyle,
|
||||
mediumWidthStackStyles,
|
||||
mongoCompoundIndexNotSupportedMessage,
|
||||
@@ -26,16 +27,15 @@ import {
|
||||
onRenderRow,
|
||||
separatorStyles,
|
||||
subComponentStackProps,
|
||||
unsavedEditorWarningMessage,
|
||||
} from "../../SettingsRenderUtils";
|
||||
import {
|
||||
AddMongoIndexProps,
|
||||
MongoIndexIdField,
|
||||
MongoIndexTypes,
|
||||
MongoNotificationType,
|
||||
getMongoIndexType,
|
||||
getMongoIndexTypeText,
|
||||
isIndexTransforming,
|
||||
MongoIndexIdField,
|
||||
MongoIndexTypes,
|
||||
MongoNotificationType,
|
||||
} from "../../SettingsUtils";
|
||||
import { IndexingPolicyRefreshComponent } from "../IndexingPolicyRefresh/IndexingPolicyRefreshComponent";
|
||||
import { AddMongoIndexComponent } from "./AddMongoIndexComponent";
|
||||
@@ -297,7 +297,7 @@ export class MongoIndexingPolicyComponent extends React.Component<MongoIndexingP
|
||||
if (this.getMongoWarningNotificationMessage()) {
|
||||
warningMessage = this.getMongoWarningNotificationMessage();
|
||||
} else if (this.isMongoIndexingPolicySaveable()) {
|
||||
warningMessage = unsavedEditorWarningMessage("indexPolicy");
|
||||
warningMessage = indexingPolicynUnsavedWarningMessage;
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`ComputedPropertiesComponent renders 1`] = `
|
||||
<Stack
|
||||
tokens={
|
||||
Object {
|
||||
"childrenGap": 5,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"marginBottom": "10px",
|
||||
"marginLeft": "30px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<StyledLinkBase
|
||||
href="https://aka.ms/computed-properties-preview/"
|
||||
target="_blank"
|
||||
>
|
||||
Learn more
|
||||
|
||||
<FontIcon
|
||||
iconName="NavigateExternalInline"
|
||||
/>
|
||||
</StyledLinkBase>
|
||||
about how to define computed properties and how to use them.
|
||||
</Text>
|
||||
<div
|
||||
className="settingsV2IndexingPolicyEditor"
|
||||
tabIndex={0}
|
||||
/>
|
||||
</Stack>
|
||||
`;
|
||||
@@ -4,7 +4,7 @@ import * as ViewModels from "../../../Contracts/ViewModels";
|
||||
import { MongoIndex } from "../../../Utils/arm/generatedClients/cosmos/types";
|
||||
|
||||
const zeroValue = 0;
|
||||
export type isDirtyTypes = boolean | string | number | DataModels.IndexingPolicy | DataModels.ComputedProperties;
|
||||
export type isDirtyTypes = boolean | string | number | DataModels.IndexingPolicy;
|
||||
export const TtlOff = "off";
|
||||
export const TtlOn = "on";
|
||||
export const TtlOnNoDefault = "on-nodefault";
|
||||
@@ -45,7 +45,6 @@ export enum SettingsV2TabTypes {
|
||||
ConflictResolutionTab,
|
||||
SubSettingsTab,
|
||||
IndexingPolicyTab,
|
||||
ComputedPropertiesTab,
|
||||
}
|
||||
|
||||
export interface IsComponentDirtyResult {
|
||||
@@ -147,8 +146,6 @@ export const getTabTitle = (tab: SettingsV2TabTypes): string => {
|
||||
return "Settings";
|
||||
case SettingsV2TabTypes.IndexingPolicyTab:
|
||||
return "Indexing Policy";
|
||||
case SettingsV2TabTypes.ComputedPropertiesTab:
|
||||
return "Computed Properties (preview)";
|
||||
default:
|
||||
throw new Error(`Unknown tab ${tab}`);
|
||||
}
|
||||
|
||||
@@ -40,12 +40,6 @@ export const collection = {
|
||||
version: 2,
|
||||
},
|
||||
partitionKeyProperties: ["partitionKey"],
|
||||
computedProperties: ko.observable<DataModels.ComputedProperties>([
|
||||
{
|
||||
name: "queryName",
|
||||
query: "query",
|
||||
},
|
||||
]),
|
||||
readSettings: () => {
|
||||
return;
|
||||
},
|
||||
|
||||
@@ -26,7 +26,6 @@ exports[`SettingsComponent renders 1`] = `
|
||||
Object {
|
||||
"analyticalStorageTtl": [Function],
|
||||
"changeFeedPolicy": [Function],
|
||||
"computedProperties": [Function],
|
||||
"conflictResolutionPolicy": [Function],
|
||||
"container": Explorer {
|
||||
"_isInitializingNotebooks": false,
|
||||
@@ -104,7 +103,6 @@ exports[`SettingsComponent renders 1`] = `
|
||||
Object {
|
||||
"analyticalStorageTtl": [Function],
|
||||
"changeFeedPolicy": [Function],
|
||||
"computedProperties": [Function],
|
||||
"conflictResolutionPolicy": [Function],
|
||||
"container": Explorer {
|
||||
"_isInitializingNotebooks": false,
|
||||
@@ -206,40 +204,6 @@ exports[`SettingsComponent renders 1`] = `
|
||||
shouldDiscardIndexingPolicy={false}
|
||||
/>
|
||||
</PivotItem>
|
||||
<PivotItem
|
||||
headerText="Computed Properties (preview)"
|
||||
itemKey="ComputedPropertiesTab"
|
||||
key="ComputedPropertiesTab"
|
||||
style={
|
||||
Object {
|
||||
"marginTop": 20,
|
||||
}
|
||||
}
|
||||
>
|
||||
<ComputedPropertiesComponent
|
||||
computedPropertiesContent={
|
||||
Array [
|
||||
Object {
|
||||
"name": "queryName",
|
||||
"query": "query",
|
||||
},
|
||||
]
|
||||
}
|
||||
computedPropertiesContentBaseline={
|
||||
Array [
|
||||
Object {
|
||||
"name": "queryName",
|
||||
"query": "query",
|
||||
},
|
||||
]
|
||||
}
|
||||
logComputedPropertiesSuccessMessage={[Function]}
|
||||
onComputedPropertiesContentChange={[Function]}
|
||||
onComputedPropertiesDirtyChange={[Function]}
|
||||
resetShouldDiscardComputedProperties={[Function]}
|
||||
shouldDiscardComputedProperties={false}
|
||||
/>
|
||||
</PivotItem>
|
||||
</StyledPivot>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -99,6 +99,18 @@ exports[`SettingsUtils functions render 1`] = `
|
||||
</StyledLinkBase>
|
||||
.
|
||||
</Text>
|
||||
<Text
|
||||
styles={
|
||||
Object {
|
||||
"root": Object {
|
||||
"color": "windowtext",
|
||||
"fontSize": 14,
|
||||
},
|
||||
}
|
||||
}
|
||||
>
|
||||
You have not saved the latest changes made to your indexing policy. Please click save to confirm the changes.
|
||||
</Text>
|
||||
<Text
|
||||
id="updateThroughputDelayedApplyWarningMessage"
|
||||
styles={
|
||||
|
||||
@@ -3,7 +3,6 @@ import { isPublicInternetAccessAllowed } from "Common/DatabaseAccountUtility";
|
||||
import { sendMessage } from "Common/MessageHandler";
|
||||
import { Platform, configContext } from "ConfigContext";
|
||||
import { MessageTypes } from "Contracts/ExplorerContracts";
|
||||
import { getCopilotEnabled, isCopilotFeatureRegistered } from "Explorer/QueryCopilot/Shared/QueryCopilotClient";
|
||||
import { IGalleryItem } from "Juno/JunoClient";
|
||||
import { requestDatabaseResourceTokens } from "Platform/Fabric/FabricUtil";
|
||||
import { allowedNotebookServerUrls, validateEndpoint } from "Utils/EndpointValidation";
|
||||
@@ -93,7 +92,7 @@ export default class Explorer {
|
||||
};
|
||||
|
||||
private static readonly MaxNbDatabasesToAutoExpand = 5;
|
||||
public phoenixClient: PhoenixClient;
|
||||
private phoenixClient: PhoenixClient;
|
||||
constructor() {
|
||||
const startKey: number = TelemetryProcessor.traceStart(Action.InitializeDataExplorer, {
|
||||
dataExplorerArea: Constants.Areas.ResourceTree,
|
||||
@@ -412,7 +411,7 @@ export default class Explorer {
|
||||
this._isInitializingNotebooks = false;
|
||||
}
|
||||
|
||||
public async allocateContainer(poolId: PoolIdType, mode?: string): Promise<void> {
|
||||
public async allocateContainer(poolId: PoolIdType): Promise<void> {
|
||||
const shouldUseNotebookStates = poolId === PoolIdType.DefaultPoolId ? true : false;
|
||||
const notebookServerInfo = shouldUseNotebookStates
|
||||
? useNotebook.getState().notebookServerInfo
|
||||
@@ -426,6 +425,10 @@ export default class Explorer {
|
||||
(notebookServerInfo === undefined ||
|
||||
(notebookServerInfo && notebookServerInfo.notebookServerEndpoint === undefined))
|
||||
) {
|
||||
const provisionData: IProvisionData = {
|
||||
cosmosEndpoint: userContext?.databaseAccount?.properties?.documentEndpoint,
|
||||
poolId: shouldUseNotebookStates ? undefined : poolId,
|
||||
};
|
||||
const connectionStatus: ContainerConnectionInfo = {
|
||||
status: ConnectionStatusType.Connecting,
|
||||
};
|
||||
@@ -433,26 +436,14 @@ export default class Explorer {
|
||||
shouldUseNotebookStates && useNotebook.getState().setConnectionInfo(connectionStatus);
|
||||
|
||||
let connectionInfo;
|
||||
let provisionData: IProvisionData;
|
||||
try {
|
||||
TelemetryProcessor.traceStart(Action.PhoenixConnection, {
|
||||
dataExplorerArea: Areas.Notebook,
|
||||
});
|
||||
if (shouldUseNotebookStates) {
|
||||
useNotebook.getState().setIsAllocating(true);
|
||||
provisionData = {
|
||||
cosmosEndpoint: userContext?.databaseAccount?.properties?.documentEndpoint,
|
||||
poolId: undefined,
|
||||
};
|
||||
} else {
|
||||
useQueryCopilot.getState().setIsAllocatingContainer(true);
|
||||
provisionData = {
|
||||
poolId: poolId,
|
||||
databaseId: useTabs.getState().activeTab.collection.databaseId,
|
||||
containerId: useTabs.getState().activeTab.collection.id(),
|
||||
mode: mode,
|
||||
};
|
||||
}
|
||||
shouldUseNotebookStates
|
||||
? useNotebook.getState().setIsAllocating(true)
|
||||
: useQueryCopilot.getState().setIsAllocatingContainer(true);
|
||||
|
||||
connectionInfo = await this.phoenixClient.allocateContainer(provisionData);
|
||||
if (!connectionInfo?.data?.phoenixServiceUrl) {
|
||||
throw new Error(`PhoenixServiceUrl is invalid!`);
|
||||
@@ -468,21 +459,19 @@ export default class Explorer {
|
||||
error: getErrorMessage(error),
|
||||
errorStack: getErrorStack(error),
|
||||
});
|
||||
if (shouldUseNotebookStates) {
|
||||
connectionStatus.status = ConnectionStatusType.Failed;
|
||||
shouldUseNotebookStates
|
||||
? useNotebook.getState().resetContainerConnection(connectionStatus)
|
||||
: useQueryCopilot.getState().resetContainerConnection();
|
||||
if (error?.status === HttpStatusCodes.Forbidden && error.message) {
|
||||
useDialog.getState().showOkModalDialog("Connection Failed", `${error.message}`);
|
||||
} else {
|
||||
useDialog
|
||||
.getState()
|
||||
.showOkModalDialog(
|
||||
"Connection Failed",
|
||||
"We are unable to connect to the temporary workspace. Please try again in a few minutes. If the error persists, file a support ticket.",
|
||||
);
|
||||
}
|
||||
connectionStatus.status = ConnectionStatusType.Failed;
|
||||
shouldUseNotebookStates
|
||||
? useNotebook.getState().resetContainerConnection(connectionStatus)
|
||||
: useQueryCopilot.getState().resetContainerConnection();
|
||||
if (error?.status === HttpStatusCodes.Forbidden && error.message) {
|
||||
useDialog.getState().showOkModalDialog("Connection Failed", `${error.message}`);
|
||||
} else {
|
||||
useDialog
|
||||
.getState()
|
||||
.showOkModalDialog(
|
||||
"Connection Failed",
|
||||
"We are unable to connect to the temporary workspace. Please try again in a few minutes. If the error persists, file a support ticket.",
|
||||
);
|
||||
}
|
||||
throw error;
|
||||
} finally {
|
||||
@@ -496,11 +485,11 @@ export default class Explorer {
|
||||
}
|
||||
}
|
||||
|
||||
public async setNotebookInfo(
|
||||
private async setNotebookInfo(
|
||||
shouldUseNotebookStates: boolean,
|
||||
connectionInfo: IResponse<IPhoenixServiceInfo>,
|
||||
connectionStatus: DataModels.ContainerConnectionInfo,
|
||||
): Promise<void> {
|
||||
) {
|
||||
const containerData = {
|
||||
forwardingId: connectionInfo.data.forwardingId,
|
||||
dbAccountName: userContext.databaseAccount.name,
|
||||
@@ -521,7 +510,6 @@ export default class Explorer {
|
||||
shouldUseNotebookStates
|
||||
? useNotebook.getState().setNotebookServerInfo(noteBookServerInfo)
|
||||
: useQueryCopilot.getState().setNotebookServerInfo(noteBookServerInfo);
|
||||
|
||||
shouldUseNotebookStates &&
|
||||
this.notebookManager?.notebookClient
|
||||
.getMemoryUsage()
|
||||
@@ -1384,20 +1372,6 @@ export default class Explorer {
|
||||
await this.refreshSampleData();
|
||||
}
|
||||
|
||||
public async configureCopilot(): Promise<void> {
|
||||
if (userContext.apiType !== "SQL" || !userContext.subscriptionId) {
|
||||
return;
|
||||
}
|
||||
const copilotEnabledPromise = getCopilotEnabled();
|
||||
const copilotUserDBEnabledPromise = isCopilotFeatureRegistered(userContext.subscriptionId);
|
||||
const [copilotEnabled, copilotUserDBEnabled] = await Promise.all([
|
||||
copilotEnabledPromise,
|
||||
copilotUserDBEnabledPromise,
|
||||
]);
|
||||
useQueryCopilot.getState().setCopilotEnabled(copilotEnabled);
|
||||
useQueryCopilot.getState().setCopilotUserDBEnabled(copilotUserDBEnabled);
|
||||
}
|
||||
|
||||
public async refreshSampleData(): Promise<void> {
|
||||
if (!userContext.sampleDataConnectionInfo) {
|
||||
return;
|
||||
|
||||
@@ -345,7 +345,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||
|
||||
describe("Open Postgres and vCore Mongo buttons", () => {
|
||||
const openPostgresShellButtonLabel = "Open PSQL shell";
|
||||
const openVCoreMongoShellButtonLabel = "Open MongoDB (vCore) shell";
|
||||
const openVCoreMongoShellButtonLabel = "Open MongoDB (vcore) shell";
|
||||
|
||||
beforeAll(() => {
|
||||
mockExplorer = {} as Explorer;
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import { Action } from "Shared/Telemetry/TelemetryConstants";
|
||||
import { traceOpen } from "Shared/Telemetry/TelemetryProcessor";
|
||||
import { ReactTabKind, useTabs } from "hooks/useTabs";
|
||||
import * as React from "react";
|
||||
import AddCollectionIcon from "../../../../images/AddCollection.svg";
|
||||
import AddDatabaseIcon from "../../../../images/AddDatabase.svg";
|
||||
@@ -334,8 +337,13 @@ function createNewSQLQueryButton(selectedNodeState: SelectedNodeState): CommandB
|
||||
iconSrc: AddSqlQueryIcon,
|
||||
iconAlt: label,
|
||||
onCommandClick: () => {
|
||||
const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection();
|
||||
selectedCollection && selectedCollection.onNewQueryClick(selectedCollection);
|
||||
if (useSelectedNode.getState().isQueryCopilotCollectionSelected()) {
|
||||
useTabs.getState().openAndActivateReactTab(ReactTabKind.QueryCopilot);
|
||||
traceOpen(Action.OpenQueryCopilotFromNewQuery, { apiType: userContext.apiType });
|
||||
} else {
|
||||
const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection();
|
||||
selectedCollection && selectedCollection.onNewQueryClick(selectedCollection);
|
||||
}
|
||||
},
|
||||
commandButtonLabel: label,
|
||||
ariaLabel: label,
|
||||
@@ -515,7 +523,7 @@ function createOpenTerminalButtonByKind(
|
||||
case ViewModels.TerminalKind.Postgres:
|
||||
return "PSQL";
|
||||
case ViewModels.TerminalKind.VCoreMongo:
|
||||
return "MongoDB (vCore)";
|
||||
return "MongoDB (vcore)";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
IDropdownOption,
|
||||
IDropdownStyles,
|
||||
} from "@fluentui/react";
|
||||
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||
import * as React from "react";
|
||||
import _ from "underscore";
|
||||
import ChevronDownIcon from "../../../../images/Chevron_down.svg";
|
||||
@@ -58,11 +57,7 @@ export const convertButton = (btns: CommandButtonComponentProps[], backgroundCol
|
||||
},
|
||||
onClick: (ev?: React.MouseEvent<HTMLElement, MouseEvent> | React.KeyboardEvent<HTMLElement>) => {
|
||||
btn.onCommandClick(ev);
|
||||
let copilotEnabled = false;
|
||||
if (useQueryCopilot.getState().copilotEnabled && useQueryCopilot.getState().copilotUserDBEnabled) {
|
||||
copilotEnabled = useQueryCopilot.getState().copilotEnabledforExecution;
|
||||
}
|
||||
TelemetryProcessor.trace(Action.ClickCommandBarButton, ActionModifiers.Mark, { label, copilotEnabled });
|
||||
TelemetryProcessor.trace(Action.ClickCommandBarButton, ActionModifiers.Mark, { label });
|
||||
},
|
||||
key: `${btn.commandButtonLabel}${index}`,
|
||||
text: label,
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Checkbox, DefaultButton, IconButton, PrimaryButton, TextField } from "@fluentui/react";
|
||||
import { Checkbox, ChoiceGroup, DefaultButton, IconButton, PrimaryButton, TextField } from "@fluentui/react";
|
||||
import Explorer from "Explorer/Explorer";
|
||||
import { QueryCopilotFeedbackModal } from "Explorer/QueryCopilot/Modal/QueryCopilotFeedbackModal";
|
||||
import { useCopilotStore } from "Explorer/QueryCopilot/QueryCopilotContext";
|
||||
import { SubmitFeedback } from "Explorer/QueryCopilot/Shared/QueryCopilotClient";
|
||||
import { getUserEmail } from "Utils/UserUtils";
|
||||
import { shallow } from "enzyme";
|
||||
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||
import React from "react";
|
||||
|
||||
jest.mock("Utils/UserUtils");
|
||||
@@ -13,49 +13,21 @@ jest.mock("Utils/UserUtils");
|
||||
jest.mock("Explorer/QueryCopilot/Shared/QueryCopilotClient");
|
||||
SubmitFeedback as jest.Mock;
|
||||
|
||||
jest.mock("Explorer/QueryCopilot/QueryCopilotContext");
|
||||
const mockUseCopilotStore = useCopilotStore as jest.Mock;
|
||||
const mockReturnValue = {
|
||||
generatedQuery: "test query",
|
||||
userPrompt: "test prompt",
|
||||
likeQuery: false,
|
||||
showFeedbackModal: false,
|
||||
closeFeedbackModal: jest.fn,
|
||||
setHideFeedbackModalForLikedQueries: jest.fn,
|
||||
};
|
||||
|
||||
describe("Query Copilot Feedback Modal snapshot test", () => {
|
||||
beforeEach(() => {
|
||||
mockUseCopilotStore.mockReturnValue(mockReturnValue);
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
it("shoud render and match snapshot", () => {
|
||||
mockUseCopilotStore.mockReturnValue({
|
||||
...mockReturnValue,
|
||||
showFeedbackModal: true,
|
||||
});
|
||||
const wrapper = shallow(
|
||||
<QueryCopilotFeedbackModal
|
||||
explorer={new Explorer()}
|
||||
databaseId="CopilotUserDb"
|
||||
containerId="CopilotUserContainer"
|
||||
mode="User"
|
||||
/>,
|
||||
);
|
||||
useQueryCopilot.getState().openFeedbackModal("test query", false, "test prompt");
|
||||
|
||||
const wrapper = shallow(<QueryCopilotFeedbackModal explorer={new Explorer()} />);
|
||||
|
||||
expect(wrapper.props().isOpen).toBeTruthy();
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should close on cancel click", () => {
|
||||
const wrapper = shallow(
|
||||
<QueryCopilotFeedbackModal
|
||||
explorer={new Explorer()}
|
||||
databaseId="CopilotUserDb"
|
||||
containerId="CopilotUserContainer"
|
||||
mode="User"
|
||||
/>,
|
||||
);
|
||||
const wrapper = shallow(<QueryCopilotFeedbackModal explorer={new Explorer()} />);
|
||||
|
||||
const cancelButton = wrapper.find(IconButton);
|
||||
cancelButton.simulate("click");
|
||||
@@ -66,14 +38,7 @@ describe("Query Copilot Feedback Modal snapshot test", () => {
|
||||
});
|
||||
|
||||
it("should get user unput", () => {
|
||||
const wrapper = shallow(
|
||||
<QueryCopilotFeedbackModal
|
||||
explorer={new Explorer()}
|
||||
databaseId="CopilotUserDb"
|
||||
containerId="CopilotUserContainer"
|
||||
mode="User"
|
||||
/>,
|
||||
);
|
||||
const wrapper = shallow(<QueryCopilotFeedbackModal explorer={new Explorer()} />);
|
||||
const testUserInput = "test user input";
|
||||
|
||||
const userInput = wrapper.find(TextField).first();
|
||||
@@ -83,15 +48,30 @@ describe("Query Copilot Feedback Modal snapshot test", () => {
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should record user contact choice no", () => {
|
||||
const wrapper = shallow(<QueryCopilotFeedbackModal explorer={new Explorer()} />);
|
||||
const contactAllowed = wrapper.find(ChoiceGroup);
|
||||
|
||||
contactAllowed.simulate("change", {}, { key: "no" });
|
||||
|
||||
expect(getUserEmail).toHaveBeenCalledTimes(3);
|
||||
expect(wrapper.find(ChoiceGroup).props().selectedKey).toEqual("no");
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should record user contact choice yes", () => {
|
||||
const wrapper = shallow(<QueryCopilotFeedbackModal explorer={new Explorer()} />);
|
||||
const contactAllowed = wrapper.find(ChoiceGroup);
|
||||
|
||||
contactAllowed.simulate("change", {}, { key: "yes" });
|
||||
|
||||
expect(getUserEmail).toHaveBeenCalledTimes(4);
|
||||
expect(wrapper.find(ChoiceGroup).props().selectedKey).toEqual("yes");
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should not render dont show again button", () => {
|
||||
const wrapper = shallow(
|
||||
<QueryCopilotFeedbackModal
|
||||
explorer={new Explorer()}
|
||||
databaseId="CopilotUserDb"
|
||||
containerId="CopilotUserContainer"
|
||||
mode="User"
|
||||
/>,
|
||||
);
|
||||
const wrapper = shallow(<QueryCopilotFeedbackModal explorer={new Explorer()} />);
|
||||
|
||||
const dontShowAgain = wrapper.find(Checkbox);
|
||||
|
||||
@@ -100,19 +80,8 @@ describe("Query Copilot Feedback Modal snapshot test", () => {
|
||||
});
|
||||
|
||||
it("should render dont show again button and check it ", () => {
|
||||
mockUseCopilotStore.mockReturnValue({
|
||||
...mockReturnValue,
|
||||
showFeedbackModal: true,
|
||||
likeQuery: true,
|
||||
});
|
||||
const wrapper = shallow(
|
||||
<QueryCopilotFeedbackModal
|
||||
explorer={new Explorer()}
|
||||
databaseId="CopilotUserDb"
|
||||
containerId="CopilotUserContainer"
|
||||
mode="User"
|
||||
/>,
|
||||
);
|
||||
useQueryCopilot.getState().openFeedbackModal("test query", true, "test prompt");
|
||||
const wrapper = shallow(<QueryCopilotFeedbackModal explorer={new Explorer()} />);
|
||||
|
||||
const dontShowAgain = wrapper.find(Checkbox);
|
||||
dontShowAgain.simulate("change", {}, true);
|
||||
@@ -123,14 +92,7 @@ describe("Query Copilot Feedback Modal snapshot test", () => {
|
||||
});
|
||||
|
||||
it("should cancel submission", () => {
|
||||
const wrapper = shallow(
|
||||
<QueryCopilotFeedbackModal
|
||||
explorer={new Explorer()}
|
||||
databaseId="CopilotUserDb"
|
||||
containerId="CopilotUserContainer"
|
||||
mode="User"
|
||||
/>,
|
||||
);
|
||||
const wrapper = shallow(<QueryCopilotFeedbackModal explorer={new Explorer()} />);
|
||||
|
||||
const cancelButton = wrapper.find(DefaultButton);
|
||||
cancelButton.simulate("click");
|
||||
@@ -142,14 +104,7 @@ describe("Query Copilot Feedback Modal snapshot test", () => {
|
||||
|
||||
it("should not submit submission if required description field is null", () => {
|
||||
const explorer = new Explorer();
|
||||
const wrapper = shallow(
|
||||
<QueryCopilotFeedbackModal
|
||||
explorer={explorer}
|
||||
databaseId="CopilotUserDb"
|
||||
containerId="CopilotUserContainer"
|
||||
mode="User"
|
||||
/>,
|
||||
);
|
||||
const wrapper = shallow(<QueryCopilotFeedbackModal explorer={explorer} />);
|
||||
|
||||
const submitButton = wrapper.find(PrimaryButton);
|
||||
submitButton.simulate("click");
|
||||
@@ -159,15 +114,9 @@ describe("Query Copilot Feedback Modal snapshot test", () => {
|
||||
});
|
||||
|
||||
it("should submit submission", () => {
|
||||
useQueryCopilot.getState().openFeedbackModal("test query", false, "test prompt");
|
||||
const explorer = new Explorer();
|
||||
const wrapper = shallow(
|
||||
<QueryCopilotFeedbackModal
|
||||
explorer={explorer}
|
||||
databaseId="CopilotUserDb"
|
||||
containerId="CopilotUserContainer"
|
||||
mode="User"
|
||||
/>,
|
||||
);
|
||||
const wrapper = shallow(<QueryCopilotFeedbackModal explorer={explorer} />);
|
||||
|
||||
const submitButton = wrapper.find("form");
|
||||
submitButton.simulate("submit");
|
||||
@@ -175,14 +124,12 @@ describe("Query Copilot Feedback Modal snapshot test", () => {
|
||||
|
||||
expect(SubmitFeedback).toHaveBeenCalledTimes(1);
|
||||
expect(SubmitFeedback).toHaveBeenCalledWith({
|
||||
containerId: "CopilotUserContainer",
|
||||
databaseId: "CopilotUserDb",
|
||||
mode: "User",
|
||||
params: {
|
||||
likeQuery: false,
|
||||
generatedQuery: "test query",
|
||||
userPrompt: "test prompt",
|
||||
description: "",
|
||||
contact: getUserEmail(),
|
||||
},
|
||||
explorer: explorer,
|
||||
});
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {
|
||||
Checkbox,
|
||||
ChoiceGroup,
|
||||
DefaultButton,
|
||||
IconButton,
|
||||
Link,
|
||||
@@ -10,21 +11,12 @@ import {
|
||||
TextField,
|
||||
} from "@fluentui/react";
|
||||
import Explorer from "Explorer/Explorer";
|
||||
import { useCopilotStore } from "Explorer/QueryCopilot/QueryCopilotContext";
|
||||
import { SubmitFeedback } from "Explorer/QueryCopilot/Shared/QueryCopilotClient";
|
||||
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||
import React from "react";
|
||||
import { getUserEmail } from "../../../Utils/UserUtils";
|
||||
|
||||
export const QueryCopilotFeedbackModal = ({
|
||||
explorer,
|
||||
databaseId,
|
||||
containerId,
|
||||
mode,
|
||||
}: {
|
||||
explorer: Explorer;
|
||||
databaseId: string;
|
||||
containerId: string;
|
||||
mode: string;
|
||||
}): JSX.Element => {
|
||||
export const QueryCopilotFeedbackModal = ({ explorer }: { explorer: Explorer }): JSX.Element => {
|
||||
const {
|
||||
generatedQuery,
|
||||
userPrompt,
|
||||
@@ -32,19 +24,18 @@ export const QueryCopilotFeedbackModal = ({
|
||||
showFeedbackModal,
|
||||
closeFeedbackModal,
|
||||
setHideFeedbackModalForLikedQueries,
|
||||
} = useCopilotStore();
|
||||
} = useQueryCopilot();
|
||||
const [isContactAllowed, setIsContactAllowed] = React.useState<boolean>(false);
|
||||
const [description, setDescription] = React.useState<string>("");
|
||||
const [doNotShowAgainChecked, setDoNotShowAgainChecked] = React.useState<boolean>(false);
|
||||
const [contact, setContact] = React.useState<string>(getUserEmail());
|
||||
|
||||
const handleSubmit = () => {
|
||||
closeFeedbackModal();
|
||||
setHideFeedbackModalForLikedQueries(doNotShowAgainChecked);
|
||||
SubmitFeedback({
|
||||
params: { generatedQuery, likeQuery, description, userPrompt },
|
||||
explorer,
|
||||
databaseId,
|
||||
containerId,
|
||||
mode: mode,
|
||||
params: { generatedQuery, likeQuery, description, userPrompt, contact },
|
||||
explorer: explorer,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -73,6 +64,30 @@ export const QueryCopilotFeedbackModal = ({
|
||||
defaultValue={generatedQuery}
|
||||
readOnly
|
||||
/>
|
||||
<ChoiceGroup
|
||||
styles={{
|
||||
root: {
|
||||
marginBottom: 14,
|
||||
},
|
||||
flexContainer: {
|
||||
selectors: {
|
||||
".ms-ChoiceField-field::before": { marginTop: 4 },
|
||||
".ms-ChoiceField-field::after": { marginTop: 4 },
|
||||
".ms-ChoiceFieldLabel": { paddingLeft: 6 },
|
||||
},
|
||||
},
|
||||
}}
|
||||
label="May we contact you about your feedback?"
|
||||
options={[
|
||||
{ key: "yes", text: "Yes, you may contact me." },
|
||||
{ key: "no", text: "No, do not contact me." },
|
||||
]}
|
||||
selectedKey={isContactAllowed ? "yes" : "no"}
|
||||
onChange={(_, option) => {
|
||||
setIsContactAllowed(option.key === "yes");
|
||||
setContact(option.key === "yes" ? getUserEmail() : "");
|
||||
}}
|
||||
></ChoiceGroup>
|
||||
<Text style={{ fontSize: 12, marginBottom: 14 }}>
|
||||
By pressing submit, your feedback will be used to improve Microsoft products and services. Please see the{" "}
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
.modalContentPadding {
|
||||
padding-top: 15px;
|
||||
width: 513px;
|
||||
height: 638px;
|
||||
}
|
||||
|
||||
.exitPadding {
|
||||
@@ -38,7 +39,7 @@
|
||||
}
|
||||
|
||||
.buttonPadding {
|
||||
padding: 15px 0px 15px 0px;
|
||||
padding: 15px 0px 0px 0px;
|
||||
}
|
||||
|
||||
.tryButton {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { IconButton, Image, Link, Modal, PrimaryButton, Stack, StackItem, Text } from "@fluentui/react";
|
||||
import { useBoolean } from "@fluentui/react-hooks";
|
||||
import React from "react";
|
||||
import Database from "../../../../images/CopilotDatabase.svg";
|
||||
import Flash from "../../../../images/CopilotFlash.svg";
|
||||
import Thumb from "../../../../images/CopilotThumb.svg";
|
||||
import CoplilotWelcomeIllustration from "../../../../images/CopliotWelcomeIllustration.svg";
|
||||
@@ -22,10 +23,8 @@ export const WelcomeModal = ({ visible }: { visible: boolean }): JSX.Element =>
|
||||
onDismiss={hideModal}
|
||||
isBlocking={false}
|
||||
styles={{
|
||||
main: {
|
||||
maxHeight: 600,
|
||||
borderRadius: 10,
|
||||
overflow: "hidden",
|
||||
scrollableContent: {
|
||||
minHeight: 680,
|
||||
},
|
||||
}}
|
||||
>
|
||||
@@ -53,7 +52,7 @@ export const WelcomeModal = ({ visible }: { visible: boolean }): JSX.Element =>
|
||||
</Stack>
|
||||
<Stack horizontalAlign="center">
|
||||
<Stack.Item align="center" style={{ textAlign: "center" }}>
|
||||
<Text className="title bold">Welcome to Microsoft Copilot for Azure in Cosmos DB (preview)</Text>
|
||||
<Text className="title bold">Welcome to Copilot in Azure Cosmos DB (Private Preview)</Text>
|
||||
</Stack.Item>
|
||||
<Stack.Item align="center" className="text">
|
||||
<Stack horizontal>
|
||||
@@ -70,7 +69,7 @@ export const WelcomeModal = ({ visible }: { visible: boolean }): JSX.Element =>
|
||||
<Text>
|
||||
Ask Copilot to generate a query by describing the query in your words.
|
||||
<br />
|
||||
<Link target="_blank" href="https://aka.ms/MicrosoftCopilotForAzureInCDBHowTo">
|
||||
<Link target="_blank" href="https://aka.ms/cdb-copilot-learn-more">
|
||||
Learn more
|
||||
</Link>
|
||||
</Text>
|
||||
@@ -88,11 +87,31 @@ export const WelcomeModal = ({ visible }: { visible: boolean }): JSX.Element =>
|
||||
</StackItem>
|
||||
</Stack>
|
||||
<Text>
|
||||
AI-generated content can have mistakes. Make sure it is accurate and appropriate before executing the
|
||||
query.
|
||||
AI-generated content can have mistakes. Make sure it’s accurate and appropriate before using it.
|
||||
<br />
|
||||
<Link target="_blank" href="https://aka.ms/cdb-copilot-preview-terms">
|
||||
Read our preview terms here
|
||||
Read preview terms
|
||||
</Link>
|
||||
</Text>
|
||||
</Stack.Item>
|
||||
<Stack.Item align="center" className="text">
|
||||
<Stack horizontal>
|
||||
<StackItem align="start" className="imageTextPadding">
|
||||
<Image src={Database} />
|
||||
</StackItem>
|
||||
<StackItem align="start">
|
||||
<Text className="bold">
|
||||
Query Copilot works on a sample database.
|
||||
<br />
|
||||
</Text>
|
||||
</StackItem>
|
||||
</Stack>
|
||||
<Text>
|
||||
While in Private Preview, Query Copilot is setup to work on sample database we have configured for you
|
||||
at no cost.
|
||||
<br />
|
||||
<Link target="_blank" href="https://aka.ms/cdb-copilot-learn-more">
|
||||
Learn more
|
||||
</Link>
|
||||
</Text>
|
||||
</Stack.Item>
|
||||
|
||||
@@ -76,6 +76,43 @@ exports[`Query Copilot Feedback Modal snapshot test shoud render and match snaps
|
||||
}
|
||||
}
|
||||
/>
|
||||
<StyledChoiceGroup
|
||||
label="May we contact you about your feedback?"
|
||||
onChange={[Function]}
|
||||
options={
|
||||
Array [
|
||||
Object {
|
||||
"key": "yes",
|
||||
"text": "Yes, you may contact me.",
|
||||
},
|
||||
Object {
|
||||
"key": "no",
|
||||
"text": "No, do not contact me.",
|
||||
},
|
||||
]
|
||||
}
|
||||
selectedKey="no"
|
||||
styles={
|
||||
Object {
|
||||
"flexContainer": Object {
|
||||
"selectors": Object {
|
||||
".ms-ChoiceField-field::after": Object {
|
||||
"marginTop": 4,
|
||||
},
|
||||
".ms-ChoiceField-field::before": Object {
|
||||
"marginTop": 4,
|
||||
},
|
||||
".ms-ChoiceFieldLabel": Object {
|
||||
"paddingLeft": 6,
|
||||
},
|
||||
},
|
||||
},
|
||||
"root": Object {
|
||||
"marginBottom": 14,
|
||||
},
|
||||
}
|
||||
}
|
||||
/>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
@@ -198,6 +235,43 @@ exports[`Query Copilot Feedback Modal snapshot test should cancel submission 1`]
|
||||
}
|
||||
}
|
||||
/>
|
||||
<StyledChoiceGroup
|
||||
label="May we contact you about your feedback?"
|
||||
onChange={[Function]}
|
||||
options={
|
||||
Array [
|
||||
Object {
|
||||
"key": "yes",
|
||||
"text": "Yes, you may contact me.",
|
||||
},
|
||||
Object {
|
||||
"key": "no",
|
||||
"text": "No, do not contact me.",
|
||||
},
|
||||
]
|
||||
}
|
||||
selectedKey="no"
|
||||
styles={
|
||||
Object {
|
||||
"flexContainer": Object {
|
||||
"selectors": Object {
|
||||
".ms-ChoiceField-field::after": Object {
|
||||
"marginTop": 4,
|
||||
},
|
||||
".ms-ChoiceField-field::before": Object {
|
||||
"marginTop": 4,
|
||||
},
|
||||
".ms-ChoiceFieldLabel": Object {
|
||||
"paddingLeft": 6,
|
||||
},
|
||||
},
|
||||
},
|
||||
"root": Object {
|
||||
"marginBottom": 14,
|
||||
},
|
||||
}
|
||||
}
|
||||
/>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
@@ -217,6 +291,21 @@ exports[`Query Copilot Feedback Modal snapshot test should cancel submission 1`]
|
||||
|
||||
for more information.
|
||||
</Text>
|
||||
<StyledCheckboxBase
|
||||
checked={false}
|
||||
label="Don't show me this next time"
|
||||
onChange={[Function]}
|
||||
styles={
|
||||
Object {
|
||||
"label": Object {
|
||||
"paddingLeft": 0,
|
||||
},
|
||||
"root": Object {
|
||||
"marginBottom": 14,
|
||||
},
|
||||
}
|
||||
}
|
||||
/>
|
||||
<Stack
|
||||
horizontal={true}
|
||||
horizontalAlign="end"
|
||||
@@ -320,6 +409,43 @@ exports[`Query Copilot Feedback Modal snapshot test should close on cancel click
|
||||
}
|
||||
}
|
||||
/>
|
||||
<StyledChoiceGroup
|
||||
label="May we contact you about your feedback?"
|
||||
onChange={[Function]}
|
||||
options={
|
||||
Array [
|
||||
Object {
|
||||
"key": "yes",
|
||||
"text": "Yes, you may contact me.",
|
||||
},
|
||||
Object {
|
||||
"key": "no",
|
||||
"text": "No, do not contact me.",
|
||||
},
|
||||
]
|
||||
}
|
||||
selectedKey="no"
|
||||
styles={
|
||||
Object {
|
||||
"flexContainer": Object {
|
||||
"selectors": Object {
|
||||
".ms-ChoiceField-field::after": Object {
|
||||
"marginTop": 4,
|
||||
},
|
||||
".ms-ChoiceField-field::before": Object {
|
||||
"marginTop": 4,
|
||||
},
|
||||
".ms-ChoiceFieldLabel": Object {
|
||||
"paddingLeft": 6,
|
||||
},
|
||||
},
|
||||
},
|
||||
"root": Object {
|
||||
"marginBottom": 14,
|
||||
},
|
||||
}
|
||||
}
|
||||
/>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
@@ -442,6 +568,43 @@ exports[`Query Copilot Feedback Modal snapshot test should get user unput 1`] =
|
||||
}
|
||||
}
|
||||
/>
|
||||
<StyledChoiceGroup
|
||||
label="May we contact you about your feedback?"
|
||||
onChange={[Function]}
|
||||
options={
|
||||
Array [
|
||||
Object {
|
||||
"key": "yes",
|
||||
"text": "Yes, you may contact me.",
|
||||
},
|
||||
Object {
|
||||
"key": "no",
|
||||
"text": "No, do not contact me.",
|
||||
},
|
||||
]
|
||||
}
|
||||
selectedKey="no"
|
||||
styles={
|
||||
Object {
|
||||
"flexContainer": Object {
|
||||
"selectors": Object {
|
||||
".ms-ChoiceField-field::after": Object {
|
||||
"marginTop": 4,
|
||||
},
|
||||
".ms-ChoiceField-field::before": Object {
|
||||
"marginTop": 4,
|
||||
},
|
||||
".ms-ChoiceFieldLabel": Object {
|
||||
"paddingLeft": 6,
|
||||
},
|
||||
},
|
||||
},
|
||||
"root": Object {
|
||||
"marginBottom": 14,
|
||||
},
|
||||
}
|
||||
}
|
||||
/>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
@@ -564,6 +727,361 @@ exports[`Query Copilot Feedback Modal snapshot test should not render dont show
|
||||
}
|
||||
}
|
||||
/>
|
||||
<StyledChoiceGroup
|
||||
label="May we contact you about your feedback?"
|
||||
onChange={[Function]}
|
||||
options={
|
||||
Array [
|
||||
Object {
|
||||
"key": "yes",
|
||||
"text": "Yes, you may contact me.",
|
||||
},
|
||||
Object {
|
||||
"key": "no",
|
||||
"text": "No, do not contact me.",
|
||||
},
|
||||
]
|
||||
}
|
||||
selectedKey="no"
|
||||
styles={
|
||||
Object {
|
||||
"flexContainer": Object {
|
||||
"selectors": Object {
|
||||
".ms-ChoiceField-field::after": Object {
|
||||
"marginTop": 4,
|
||||
},
|
||||
".ms-ChoiceField-field::before": Object {
|
||||
"marginTop": 4,
|
||||
},
|
||||
".ms-ChoiceFieldLabel": Object {
|
||||
"paddingLeft": 6,
|
||||
},
|
||||
},
|
||||
},
|
||||
"root": Object {
|
||||
"marginBottom": 14,
|
||||
},
|
||||
}
|
||||
}
|
||||
/>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"fontSize": 12,
|
||||
"marginBottom": 14,
|
||||
}
|
||||
}
|
||||
>
|
||||
By pressing submit, your feedback will be used to improve Microsoft products and services. Please see the
|
||||
|
||||
<StyledLinkBase
|
||||
href="https://privacy.microsoft.com/privacystatement"
|
||||
target="_blank"
|
||||
>
|
||||
Privacy statement
|
||||
</StyledLinkBase>
|
||||
|
||||
for more information.
|
||||
</Text>
|
||||
<Stack
|
||||
horizontal={true}
|
||||
horizontalAlign="end"
|
||||
>
|
||||
<CustomizedPrimaryButton
|
||||
styles={
|
||||
Object {
|
||||
"root": Object {
|
||||
"marginRight": 8,
|
||||
},
|
||||
}
|
||||
}
|
||||
type="submit"
|
||||
>
|
||||
Submit
|
||||
</CustomizedPrimaryButton>
|
||||
<CustomizedDefaultButton
|
||||
onClick={[Function]}
|
||||
>
|
||||
Cancel
|
||||
</CustomizedDefaultButton>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</form>
|
||||
</Modal>
|
||||
`;
|
||||
|
||||
exports[`Query Copilot Feedback Modal snapshot test should record user contact choice no 1`] = `
|
||||
<Modal
|
||||
isOpen={false}
|
||||
>
|
||||
<form
|
||||
onSubmit={[Function]}
|
||||
>
|
||||
<Stack
|
||||
style={
|
||||
Object {
|
||||
"padding": 24,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Stack
|
||||
horizontal={true}
|
||||
horizontalAlign="space-between"
|
||||
>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"fontSize": 20,
|
||||
"fontWeight": 600,
|
||||
"marginBottom": 20,
|
||||
}
|
||||
}
|
||||
>
|
||||
Send feedback to Microsoft
|
||||
</Text>
|
||||
<CustomizedIconButton
|
||||
iconProps={
|
||||
Object {
|
||||
"iconName": "Cancel",
|
||||
}
|
||||
}
|
||||
onClick={[Function]}
|
||||
/>
|
||||
</Stack>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"fontSize": 14,
|
||||
"marginBottom": 14,
|
||||
}
|
||||
}
|
||||
>
|
||||
Your feedback will help improve the experience.
|
||||
</Text>
|
||||
<StyledTextFieldBase
|
||||
label="Description"
|
||||
multiline={true}
|
||||
onChange={[Function]}
|
||||
placeholder="Provide more details"
|
||||
required={true}
|
||||
rows={3}
|
||||
styles={
|
||||
Object {
|
||||
"root": Object {
|
||||
"marginBottom": 14,
|
||||
},
|
||||
}
|
||||
}
|
||||
value=""
|
||||
/>
|
||||
<StyledTextFieldBase
|
||||
defaultValue="test query"
|
||||
label="Query generated"
|
||||
readOnly={true}
|
||||
styles={
|
||||
Object {
|
||||
"root": Object {
|
||||
"marginBottom": 14,
|
||||
},
|
||||
}
|
||||
}
|
||||
/>
|
||||
<StyledChoiceGroup
|
||||
label="May we contact you about your feedback?"
|
||||
onChange={[Function]}
|
||||
options={
|
||||
Array [
|
||||
Object {
|
||||
"key": "yes",
|
||||
"text": "Yes, you may contact me.",
|
||||
},
|
||||
Object {
|
||||
"key": "no",
|
||||
"text": "No, do not contact me.",
|
||||
},
|
||||
]
|
||||
}
|
||||
selectedKey="no"
|
||||
styles={
|
||||
Object {
|
||||
"flexContainer": Object {
|
||||
"selectors": Object {
|
||||
".ms-ChoiceField-field::after": Object {
|
||||
"marginTop": 4,
|
||||
},
|
||||
".ms-ChoiceField-field::before": Object {
|
||||
"marginTop": 4,
|
||||
},
|
||||
".ms-ChoiceFieldLabel": Object {
|
||||
"paddingLeft": 6,
|
||||
},
|
||||
},
|
||||
},
|
||||
"root": Object {
|
||||
"marginBottom": 14,
|
||||
},
|
||||
}
|
||||
}
|
||||
/>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"fontSize": 12,
|
||||
"marginBottom": 14,
|
||||
}
|
||||
}
|
||||
>
|
||||
By pressing submit, your feedback will be used to improve Microsoft products and services. Please see the
|
||||
|
||||
<StyledLinkBase
|
||||
href="https://privacy.microsoft.com/privacystatement"
|
||||
target="_blank"
|
||||
>
|
||||
Privacy statement
|
||||
</StyledLinkBase>
|
||||
|
||||
for more information.
|
||||
</Text>
|
||||
<Stack
|
||||
horizontal={true}
|
||||
horizontalAlign="end"
|
||||
>
|
||||
<CustomizedPrimaryButton
|
||||
styles={
|
||||
Object {
|
||||
"root": Object {
|
||||
"marginRight": 8,
|
||||
},
|
||||
}
|
||||
}
|
||||
type="submit"
|
||||
>
|
||||
Submit
|
||||
</CustomizedPrimaryButton>
|
||||
<CustomizedDefaultButton
|
||||
onClick={[Function]}
|
||||
>
|
||||
Cancel
|
||||
</CustomizedDefaultButton>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</form>
|
||||
</Modal>
|
||||
`;
|
||||
|
||||
exports[`Query Copilot Feedback Modal snapshot test should record user contact choice yes 1`] = `
|
||||
<Modal
|
||||
isOpen={false}
|
||||
>
|
||||
<form
|
||||
onSubmit={[Function]}
|
||||
>
|
||||
<Stack
|
||||
style={
|
||||
Object {
|
||||
"padding": 24,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Stack
|
||||
horizontal={true}
|
||||
horizontalAlign="space-between"
|
||||
>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"fontSize": 20,
|
||||
"fontWeight": 600,
|
||||
"marginBottom": 20,
|
||||
}
|
||||
}
|
||||
>
|
||||
Send feedback to Microsoft
|
||||
</Text>
|
||||
<CustomizedIconButton
|
||||
iconProps={
|
||||
Object {
|
||||
"iconName": "Cancel",
|
||||
}
|
||||
}
|
||||
onClick={[Function]}
|
||||
/>
|
||||
</Stack>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"fontSize": 14,
|
||||
"marginBottom": 14,
|
||||
}
|
||||
}
|
||||
>
|
||||
Your feedback will help improve the experience.
|
||||
</Text>
|
||||
<StyledTextFieldBase
|
||||
label="Description"
|
||||
multiline={true}
|
||||
onChange={[Function]}
|
||||
placeholder="Provide more details"
|
||||
required={true}
|
||||
rows={3}
|
||||
styles={
|
||||
Object {
|
||||
"root": Object {
|
||||
"marginBottom": 14,
|
||||
},
|
||||
}
|
||||
}
|
||||
value=""
|
||||
/>
|
||||
<StyledTextFieldBase
|
||||
defaultValue="test query"
|
||||
label="Query generated"
|
||||
readOnly={true}
|
||||
styles={
|
||||
Object {
|
||||
"root": Object {
|
||||
"marginBottom": 14,
|
||||
},
|
||||
}
|
||||
}
|
||||
/>
|
||||
<StyledChoiceGroup
|
||||
label="May we contact you about your feedback?"
|
||||
onChange={[Function]}
|
||||
options={
|
||||
Array [
|
||||
Object {
|
||||
"key": "yes",
|
||||
"text": "Yes, you may contact me.",
|
||||
},
|
||||
Object {
|
||||
"key": "no",
|
||||
"text": "No, do not contact me.",
|
||||
},
|
||||
]
|
||||
}
|
||||
selectedKey="yes"
|
||||
styles={
|
||||
Object {
|
||||
"flexContainer": Object {
|
||||
"selectors": Object {
|
||||
".ms-ChoiceField-field::after": Object {
|
||||
"marginTop": 4,
|
||||
},
|
||||
".ms-ChoiceField-field::before": Object {
|
||||
"marginTop": 4,
|
||||
},
|
||||
".ms-ChoiceFieldLabel": Object {
|
||||
"paddingLeft": 6,
|
||||
},
|
||||
},
|
||||
},
|
||||
"root": Object {
|
||||
"marginBottom": 14,
|
||||
},
|
||||
}
|
||||
}
|
||||
/>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
@@ -686,6 +1204,43 @@ exports[`Query Copilot Feedback Modal snapshot test should render dont show agai
|
||||
}
|
||||
}
|
||||
/>
|
||||
<StyledChoiceGroup
|
||||
label="May we contact you about your feedback?"
|
||||
onChange={[Function]}
|
||||
options={
|
||||
Array [
|
||||
Object {
|
||||
"key": "yes",
|
||||
"text": "Yes, you may contact me.",
|
||||
},
|
||||
Object {
|
||||
"key": "no",
|
||||
"text": "No, do not contact me.",
|
||||
},
|
||||
]
|
||||
}
|
||||
selectedKey="no"
|
||||
styles={
|
||||
Object {
|
||||
"flexContainer": Object {
|
||||
"selectors": Object {
|
||||
".ms-ChoiceField-field::after": Object {
|
||||
"marginTop": 4,
|
||||
},
|
||||
".ms-ChoiceField-field::before": Object {
|
||||
"marginTop": 4,
|
||||
},
|
||||
".ms-ChoiceFieldLabel": Object {
|
||||
"paddingLeft": 6,
|
||||
},
|
||||
},
|
||||
},
|
||||
"root": Object {
|
||||
"marginBottom": 14,
|
||||
},
|
||||
}
|
||||
}
|
||||
/>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
@@ -823,6 +1378,43 @@ exports[`Query Copilot Feedback Modal snapshot test should submit submission 1`]
|
||||
}
|
||||
}
|
||||
/>
|
||||
<StyledChoiceGroup
|
||||
label="May we contact you about your feedback?"
|
||||
onChange={[Function]}
|
||||
options={
|
||||
Array [
|
||||
Object {
|
||||
"key": "yes",
|
||||
"text": "Yes, you may contact me.",
|
||||
},
|
||||
Object {
|
||||
"key": "no",
|
||||
"text": "No, do not contact me.",
|
||||
},
|
||||
]
|
||||
}
|
||||
selectedKey="no"
|
||||
styles={
|
||||
Object {
|
||||
"flexContainer": Object {
|
||||
"selectors": Object {
|
||||
".ms-ChoiceField-field::after": Object {
|
||||
"marginTop": 4,
|
||||
},
|
||||
".ms-ChoiceField-field::before": Object {
|
||||
"marginTop": 4,
|
||||
},
|
||||
".ms-ChoiceFieldLabel": Object {
|
||||
"paddingLeft": 6,
|
||||
},
|
||||
},
|
||||
},
|
||||
"root": Object {
|
||||
"marginBottom": 14,
|
||||
},
|
||||
}
|
||||
}
|
||||
/>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
|
||||
@@ -8,10 +8,8 @@ exports[`Query Copilot Welcome Modal snapshot test should render when isOpen is
|
||||
onDismiss={[Function]}
|
||||
styles={
|
||||
Object {
|
||||
"main": Object {
|
||||
"borderRadius": 10,
|
||||
"maxHeight": 600,
|
||||
"overflow": "hidden",
|
||||
"scrollableContent": Object {
|
||||
"minHeight": 680,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -78,7 +76,7 @@ exports[`Query Copilot Welcome Modal snapshot test should render when isOpen is
|
||||
<Text
|
||||
className="title bold"
|
||||
>
|
||||
Welcome to Microsoft Copilot for Azure in Cosmos DB (preview)
|
||||
Welcome to Copilot in Azure Cosmos DB (Private Preview)
|
||||
</Text>
|
||||
</StackItem>
|
||||
<StackItem
|
||||
@@ -111,7 +109,7 @@ exports[`Query Copilot Welcome Modal snapshot test should render when isOpen is
|
||||
Ask Copilot to generate a query by describing the query in your words.
|
||||
<br />
|
||||
<StyledLinkBase
|
||||
href="https://aka.ms/MicrosoftCopilotForAzureInCDBHowTo"
|
||||
href="https://aka.ms/cdb-copilot-learn-more"
|
||||
target="_blank"
|
||||
>
|
||||
Learn more
|
||||
@@ -145,13 +143,50 @@ exports[`Query Copilot Welcome Modal snapshot test should render when isOpen is
|
||||
</StackItem>
|
||||
</Stack>
|
||||
<Text>
|
||||
AI-generated content can have mistakes. Make sure it is accurate and appropriate before executing the query.
|
||||
AI-generated content can have mistakes. Make sure it’s accurate and appropriate before using it.
|
||||
<br />
|
||||
<StyledLinkBase
|
||||
href="https://aka.ms/cdb-copilot-preview-terms"
|
||||
target="_blank"
|
||||
>
|
||||
Read our preview terms here
|
||||
Read preview terms
|
||||
</StyledLinkBase>
|
||||
</Text>
|
||||
</StackItem>
|
||||
<StackItem
|
||||
align="center"
|
||||
className="text"
|
||||
>
|
||||
<Stack
|
||||
horizontal={true}
|
||||
>
|
||||
<StackItem
|
||||
align="start"
|
||||
className="imageTextPadding"
|
||||
>
|
||||
<Image
|
||||
src={Object {}}
|
||||
/>
|
||||
</StackItem>
|
||||
<StackItem
|
||||
align="start"
|
||||
>
|
||||
<Text
|
||||
className="bold"
|
||||
>
|
||||
Query Copilot works on a sample database.
|
||||
<br />
|
||||
</Text>
|
||||
</StackItem>
|
||||
</Stack>
|
||||
<Text>
|
||||
While in Private Preview, Query Copilot is setup to work on sample database we have configured for you at no cost.
|
||||
<br />
|
||||
<StyledLinkBase
|
||||
href="https://aka.ms/cdb-copilot-learn-more"
|
||||
target="_blank"
|
||||
>
|
||||
Learn more
|
||||
</StyledLinkBase>
|
||||
</Text>
|
||||
</StackItem>
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
import { MinimalQueryIterator } from "Common/IteratorUtilities";
|
||||
import { QueryResults } from "Contracts/ViewModels";
|
||||
import { CopilotMessage } from "Explorer/QueryCopilot/Shared/QueryCopilotInterfaces";
|
||||
import { guid } from "Explorer/Tables/Utilities";
|
||||
import { QueryCopilotState } from "hooks/useQueryCopilot";
|
||||
|
||||
import React, { createContext, useContext, useState } from "react";
|
||||
import create from "zustand";
|
||||
const context = createContext(null);
|
||||
const useCopilotStore = (): Partial<QueryCopilotState> => useContext(context);
|
||||
|
||||
const CopilotProvider = ({ children }: { children: React.ReactNode }): JSX.Element => {
|
||||
const [useStore] = useState(() =>
|
||||
create((set, get) => ({
|
||||
generatedQuery: "",
|
||||
likeQuery: false,
|
||||
userPrompt: "",
|
||||
showFeedbackModal: false,
|
||||
hideFeedbackModalForLikedQueries: false,
|
||||
correlationId: "",
|
||||
query: "SELECT * FROM c",
|
||||
selectedQuery: "",
|
||||
isGeneratingQuery: false,
|
||||
isGeneratingExplanation: false,
|
||||
isExecuting: false,
|
||||
dislikeQuery: undefined,
|
||||
showCallout: false,
|
||||
showSamplePrompts: false,
|
||||
queryIterator: undefined,
|
||||
queryResults: undefined,
|
||||
errorMessage: "",
|
||||
isSamplePromptsOpen: false,
|
||||
showDeletePopup: false,
|
||||
showFeedbackBar: false,
|
||||
showCopyPopup: false,
|
||||
showErrorMessageBar: false,
|
||||
showInvalidQueryMessageBar: false,
|
||||
generatedQueryComments: "",
|
||||
wasCopilotUsed: false,
|
||||
showWelcomeSidebar: true,
|
||||
showCopilotSidebar: false,
|
||||
chatMessages: [],
|
||||
shouldIncludeInMessages: true,
|
||||
showExplanationBubble: false,
|
||||
isAllocatingContainer: false,
|
||||
|
||||
openFeedbackModal: (generatedQuery: string, likeQuery: boolean, userPrompt: string) =>
|
||||
set({ generatedQuery, likeQuery, userPrompt, showFeedbackModal: true }),
|
||||
closeFeedbackModal: () => set({ showFeedbackModal: false }),
|
||||
setHideFeedbackModalForLikedQueries: (hideFeedbackModalForLikedQueries: boolean) =>
|
||||
set({ hideFeedbackModalForLikedQueries }),
|
||||
refreshCorrelationId: () => set({ correlationId: guid() }),
|
||||
setUserPrompt: (userPrompt: string) => set({ userPrompt }),
|
||||
setQuery: (query: string) => set({ query }),
|
||||
setGeneratedQuery: (generatedQuery: string) => set({ generatedQuery }),
|
||||
setSelectedQuery: (selectedQuery: string) => set({ selectedQuery }),
|
||||
setIsGeneratingQuery: (isGeneratingQuery: boolean) => set({ isGeneratingQuery }),
|
||||
setIsGeneratingExplanation: (isGeneratingExplanation: boolean) => set({ isGeneratingExplanation }),
|
||||
setIsExecuting: (isExecuting: boolean) => set({ isExecuting }),
|
||||
setLikeQuery: (likeQuery: boolean) => set({ likeQuery }),
|
||||
setDislikeQuery: (dislikeQuery: boolean | undefined) => set({ dislikeQuery }),
|
||||
setShowCallout: (showCallout: boolean) => set({ showCallout }),
|
||||
setShowSamplePrompts: (showSamplePrompts: boolean) => set({ showSamplePrompts }),
|
||||
setQueryIterator: (queryIterator: MinimalQueryIterator | undefined) => set({ queryIterator }),
|
||||
setQueryResults: (queryResults: QueryResults | undefined) => set({ queryResults }),
|
||||
setErrorMessage: (errorMessage: string) => set({ errorMessage }),
|
||||
setIsSamplePromptsOpen: (isSamplePromptsOpen: boolean) => set({ isSamplePromptsOpen }),
|
||||
setShowDeletePopup: (showDeletePopup: boolean) => set({ showDeletePopup }),
|
||||
setShowFeedbackBar: (showFeedbackBar: boolean) => set({ showFeedbackBar }),
|
||||
setshowCopyPopup: (showCopyPopup: boolean) => set({ showCopyPopup }),
|
||||
setShowErrorMessageBar: (showErrorMessageBar: boolean) => set({ showErrorMessageBar }),
|
||||
setShowInvalidQueryMessageBar: (showInvalidQueryMessageBar: boolean) => set({ showInvalidQueryMessageBar }),
|
||||
setGeneratedQueryComments: (generatedQueryComments: string) => set({ generatedQueryComments }),
|
||||
setWasCopilotUsed: (wasCopilotUsed: boolean) => set({ wasCopilotUsed }),
|
||||
setShowWelcomeSidebar: (showWelcomeSidebar: boolean) => set({ showWelcomeSidebar }),
|
||||
setShowCopilotSidebar: (showCopilotSidebar: boolean) => set({ showCopilotSidebar }),
|
||||
setChatMessages: (chatMessages: CopilotMessage[]) => set({ chatMessages }),
|
||||
setShouldIncludeInMessages: (shouldIncludeInMessages: boolean) => set({ shouldIncludeInMessages }),
|
||||
setShowExplanationBubble: (showExplanationBubble: boolean) => set({ showExplanationBubble }),
|
||||
|
||||
getState: () => {
|
||||
return get();
|
||||
},
|
||||
|
||||
resetQueryCopilotStates: () => {
|
||||
set((state) => ({
|
||||
...state,
|
||||
generatedQuery: "",
|
||||
likeQuery: false,
|
||||
userPrompt: "",
|
||||
showFeedbackModal: false,
|
||||
hideFeedbackModalForLikedQueries: false,
|
||||
correlationId: "",
|
||||
query: "SELECT * FROM c",
|
||||
selectedQuery: "",
|
||||
isGeneratingQuery: false,
|
||||
isGeneratingExplanation: false,
|
||||
isExecuting: false,
|
||||
dislikeQuery: undefined,
|
||||
showCallout: false,
|
||||
showSamplePrompts: false,
|
||||
queryIterator: undefined,
|
||||
queryResults: undefined,
|
||||
errorMessage: "",
|
||||
isSamplePromptsOpen: false,
|
||||
showDeletePopup: false,
|
||||
showFeedbackBar: false,
|
||||
showCopyPopup: false,
|
||||
showErrorMessageBar: false,
|
||||
showInvalidQueryMessageBar: false,
|
||||
generatedQueryComments: "",
|
||||
wasCopilotUsed: false,
|
||||
showCopilotSidebar: false,
|
||||
chatMessages: [],
|
||||
shouldIncludeInMessages: true,
|
||||
showExplanationBubble: false,
|
||||
notebookServerInfo: {
|
||||
notebookServerEndpoint: undefined,
|
||||
authToken: undefined,
|
||||
forwardingId: undefined,
|
||||
},
|
||||
containerStatus: {
|
||||
status: undefined,
|
||||
durationLeftInMinutes: undefined,
|
||||
phoenixServerInfo: undefined,
|
||||
},
|
||||
isAllocatingContainer: false,
|
||||
}));
|
||||
},
|
||||
})),
|
||||
);
|
||||
return <context.Provider value={useStore()}>{children}</context.Provider>;
|
||||
};
|
||||
|
||||
export { CopilotProvider, useCopilotStore };
|
||||
@@ -1,5 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable no-console */
|
||||
import {
|
||||
Callout,
|
||||
CommandBarButton,
|
||||
@@ -19,21 +17,20 @@ import {
|
||||
TextField,
|
||||
} from "@fluentui/react";
|
||||
import { useBoolean } from "@fluentui/react-hooks";
|
||||
import { HttpStatusCodes } from "Common/Constants";
|
||||
import {
|
||||
ContainerStatusType,
|
||||
PoolIdType,
|
||||
QueryCopilotSampleContainerSchema,
|
||||
ShortenedQueryCopilotSampleContainerSchema,
|
||||
} from "Common/Constants";
|
||||
import { handleError } from "Common/ErrorHandlingUtils";
|
||||
import { createUri } from "Common/UrlUtility";
|
||||
import { WelcomeModal } from "Explorer/QueryCopilot/Modal/WelcomeModal";
|
||||
import { CopyPopup } from "Explorer/QueryCopilot/Popup/CopyPopup";
|
||||
import { DeletePopup } from "Explorer/QueryCopilot/Popup/DeletePopup";
|
||||
import {
|
||||
SuggestedPrompt,
|
||||
getSampleDatabaseSuggestedPrompts,
|
||||
getSuggestedPrompts,
|
||||
} from "Explorer/QueryCopilot/QueryCopilotUtilities";
|
||||
import { SubmitFeedback, allocatePhoenixContainer } from "Explorer/QueryCopilot/Shared/QueryCopilotClient";
|
||||
import { SubmitFeedback } from "Explorer/QueryCopilot/Shared/QueryCopilotClient";
|
||||
import { GenerateSQLQueryResponse, QueryCopilotProps } from "Explorer/QueryCopilot/Shared/QueryCopilotInterfaces";
|
||||
import { SamplePrompts, SamplePromptsProps } from "Explorer/QueryCopilot/Shared/SamplePrompts/SamplePrompts";
|
||||
import { Action } from "Shared/Telemetry/TelemetryConstants";
|
||||
import { userContext } from "UserContext";
|
||||
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||
import React, { useRef, useState } from "react";
|
||||
@@ -41,40 +38,29 @@ import HintIcon from "../../../images/Hint.svg";
|
||||
import CopilotIcon from "../../../images/QueryCopilotNewLogo.svg";
|
||||
import RecentIcon from "../../../images/Recent.svg";
|
||||
import errorIcon from "../../../images/close-black.svg";
|
||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||
import { useTabs } from "../../hooks/useTabs";
|
||||
import { useCopilotStore } from "../QueryCopilot/QueryCopilotContext";
|
||||
import { useSelectedNode } from "../useSelectedNode";
|
||||
|
||||
type QueryCopilotPromptProps = QueryCopilotProps & {
|
||||
databaseId: string;
|
||||
containerId: string;
|
||||
toggleCopilot: (toggle: boolean) => void;
|
||||
};
|
||||
|
||||
interface SuggestedPrompt {
|
||||
id: number;
|
||||
text: string;
|
||||
}
|
||||
|
||||
const promptStyles: IButtonStyles = {
|
||||
root: { border: 0, selectors: { ":hover": { outline: "1px dashed #605e5c" } } },
|
||||
label: {
|
||||
fontWeight: 400,
|
||||
textAlign: "left",
|
||||
paddingLeft: 8,
|
||||
overflow: "hidden",
|
||||
whiteSpace: "nowrap",
|
||||
textOverflow: "ellipsis",
|
||||
},
|
||||
textContainer: { overflow: "hidden" },
|
||||
label: { fontWeight: 400, textAlign: "left", paddingLeft: 8 },
|
||||
};
|
||||
|
||||
export const QueryCopilotPromptbar: React.FC<QueryCopilotPromptProps> = ({
|
||||
explorer,
|
||||
toggleCopilot,
|
||||
databaseId,
|
||||
containerId,
|
||||
}: QueryCopilotPromptProps): JSX.Element => {
|
||||
const [copilotTeachingBubbleVisible, { toggle: toggleCopilotTeachingBubbleVisible }] = useBoolean(false);
|
||||
const inputEdited = useRef(false);
|
||||
const {
|
||||
openFeedbackModal,
|
||||
hideFeedbackModalForLikedQueries,
|
||||
userPrompt,
|
||||
setUserPrompt,
|
||||
@@ -107,8 +93,7 @@ export const QueryCopilotPromptbar: React.FC<QueryCopilotPromptProps> = ({
|
||||
setGeneratedQueryComments,
|
||||
setQueryResults,
|
||||
setErrorMessage,
|
||||
errorMessage,
|
||||
} = useCopilotStore();
|
||||
} = useQueryCopilot();
|
||||
|
||||
const sampleProps: SamplePromptsProps = {
|
||||
isSamplePromptsOpen: isSamplePromptsOpen,
|
||||
@@ -133,13 +118,14 @@ export const QueryCopilotPromptbar: React.FC<QueryCopilotPromptProps> = ({
|
||||
}, 6000);
|
||||
};
|
||||
|
||||
const isSampleCopilotActive = useSelectedNode.getState().isQueryCopilotCollectionSelected();
|
||||
const cachedHistoriesString = localStorage.getItem(`${userContext.databaseAccount?.id}-queryCopilotHistories`);
|
||||
const cachedHistories = cachedHistoriesString?.split("|");
|
||||
const [histories, setHistories] = useState<string[]>(cachedHistories || []);
|
||||
const suggestedPrompts: SuggestedPrompt[] = isSampleCopilotActive
|
||||
? getSampleDatabaseSuggestedPrompts()
|
||||
: getSuggestedPrompts();
|
||||
const suggestedPrompts: SuggestedPrompt[] = [
|
||||
{ id: 1, text: 'Show all products that have the word "ultra" in the name or description' },
|
||||
{ id: 2, text: "What are all of the possible categories for the products, and their counts?" },
|
||||
{ id: 3, text: 'Show me all products that have been reviewed by someone with a username that contains "bob"' },
|
||||
];
|
||||
const [filteredHistories, setFilteredHistories] = useState<string[]>(histories);
|
||||
const [filteredSuggestedPrompts, setFilteredSuggestedPrompts] = useState<SuggestedPrompt[]>(suggestedPrompts);
|
||||
|
||||
@@ -190,24 +176,28 @@ export const QueryCopilotPromptbar: React.FC<QueryCopilotPromptProps> = ({
|
||||
setShowDeletePopup(false);
|
||||
useTabs.getState().setIsTabExecuting(true);
|
||||
useTabs.getState().setIsQueryErrorThrown(false);
|
||||
const mode: string = isSampleCopilotActive ? "Sample" : "User";
|
||||
|
||||
await allocatePhoenixContainer({ explorer, databaseId, containerId, mode });
|
||||
|
||||
if (
|
||||
useQueryCopilot.getState().containerStatus.status !== ContainerStatusType.Active &&
|
||||
!userContext.features.disableCopilotPhoenixGateaway
|
||||
) {
|
||||
await explorer.allocateContainer(PoolIdType.QueryCopilot);
|
||||
}
|
||||
const payload = {
|
||||
containerSchema: userContext.features.enableCopilotFullSchema
|
||||
? QueryCopilotSampleContainerSchema
|
||||
: ShortenedQueryCopilotSampleContainerSchema,
|
||||
userPrompt: userPrompt,
|
||||
};
|
||||
useQueryCopilot.getState().refreshCorrelationId();
|
||||
const serverInfo = useQueryCopilot.getState().notebookServerInfo;
|
||||
const queryUri = userContext.features.disableCopilotPhoenixGateaway
|
||||
? createUri("https://copilotorchestrater.azurewebsites.net/", "generateSQLQuery")
|
||||
: createUri(serverInfo.notebookServerEndpoint, "public/generateSQLQuery");
|
||||
: createUri(serverInfo.notebookServerEndpoint, "generateSQLQuery");
|
||||
const response = await fetch(queryUri, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
"x-ms-correlationid": useQueryCopilot.getState().correlationId,
|
||||
Authorization: `token ${useQueryCopilot.getState().notebookServerInfo.authToken}`,
|
||||
},
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
@@ -225,40 +215,13 @@ export const QueryCopilotPromptbar: React.FC<QueryCopilotPromptProps> = ({
|
||||
setGeneratedQueryComments(generateSQLQueryResponse.explanation);
|
||||
setShowFeedbackBar(true);
|
||||
resetQueryResults();
|
||||
TelemetryProcessor.traceSuccess(Action.QueryGenerationFromCopilotPrompt, {
|
||||
databaseName: databaseId,
|
||||
collectionId: containerId,
|
||||
copilotLatency:
|
||||
Date.parse(generateSQLQueryResponse?.generateEnd) - Date.parse(generateSQLQueryResponse?.generateStart),
|
||||
responseCode: response.status,
|
||||
});
|
||||
} else {
|
||||
setShowInvalidQueryMessageBar(true);
|
||||
TelemetryProcessor.traceFailure(Action.QueryGenerationFromCopilotPrompt, {
|
||||
databaseName: databaseId,
|
||||
collectionId: containerId,
|
||||
responseCode: response.status,
|
||||
});
|
||||
}
|
||||
} else if (response?.status === HttpStatusCodes.TooManyRequests) {
|
||||
handleError(JSON.stringify(generateSQLQueryResponse), "copilotTooManyRequestError");
|
||||
useTabs.getState().setIsQueryErrorThrown(true);
|
||||
setShowErrorMessageBar(true);
|
||||
setErrorMessage("Ratelimit exceeded 5 per 1 minute. Please try again after sometime");
|
||||
TelemetryProcessor.traceFailure(Action.QueryGenerationFromCopilotPrompt, {
|
||||
databaseName: databaseId,
|
||||
collectionId: containerId,
|
||||
responseCode: response.status,
|
||||
});
|
||||
} else {
|
||||
handleError(JSON.stringify(generateSQLQueryResponse), "copilotInternalServerError");
|
||||
useTabs.getState().setIsQueryErrorThrown(true);
|
||||
setShowErrorMessageBar(true);
|
||||
TelemetryProcessor.traceFailure(Action.QueryGenerationFromCopilotPrompt, {
|
||||
databaseName: databaseId,
|
||||
collectionId: containerId,
|
||||
responseCode: response.status,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
handleError(error, "executeNaturalLanguageQuery");
|
||||
@@ -347,7 +310,6 @@ export const QueryCopilotPromptbar: React.FC<QueryCopilotPromptProps> = ({
|
||||
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."
|
||||
/>
|
||||
{copilotTeachingBubbleVisible && (
|
||||
<TeachingBubble
|
||||
@@ -381,7 +343,7 @@ export const QueryCopilotPromptbar: React.FC<QueryCopilotPromptProps> = ({
|
||||
{isGeneratingQuery && <Spinner style={{ marginLeft: 8 }} />}
|
||||
{showSamplePrompts && (
|
||||
<Callout
|
||||
styles={{ root: { minWidth: 400, maxWidth: "70vw" } }}
|
||||
styles={{ root: { minWidth: 400 } }}
|
||||
target="#naturalLanguageInput"
|
||||
isBeakVisible={false}
|
||||
onDismiss={() => setShowSamplePrompts(false)}
|
||||
@@ -413,7 +375,7 @@ export const QueryCopilotPromptbar: React.FC<QueryCopilotPromptProps> = ({
|
||||
setShowSamplePrompts(false);
|
||||
inputEdited.current = true;
|
||||
}}
|
||||
onRenderIcon={() => <Image src={RecentIcon} styles={{ root: { overflow: "unset" } }} />}
|
||||
onRenderIcon={() => <Image src={RecentIcon} />}
|
||||
styles={promptStyles}
|
||||
>
|
||||
{history}
|
||||
@@ -489,7 +451,7 @@ export const QueryCopilotPromptbar: React.FC<QueryCopilotPromptProps> = ({
|
||||
</Link>
|
||||
{showErrorMessageBar && (
|
||||
<MessageBar messageBarType={MessageBarType.error}>
|
||||
{errorMessage ? errorMessage : "We ran into an error and were not able to execute query."}
|
||||
We ran into an error and were not able to execute query.
|
||||
</MessageBar>
|
||||
)}
|
||||
{showInvalidQueryMessageBar && (
|
||||
@@ -527,10 +489,7 @@ export const QueryCopilotPromptbar: React.FC<QueryCopilotPromptProps> = ({
|
||||
description: "",
|
||||
userPrompt: userPrompt,
|
||||
},
|
||||
explorer,
|
||||
databaseId,
|
||||
containerId,
|
||||
mode: isSampleCopilotActive ? "Sample" : "User",
|
||||
explorer: explorer,
|
||||
});
|
||||
}}
|
||||
directionalHint={DirectionalHint.topCenter}
|
||||
@@ -540,7 +499,7 @@ export const QueryCopilotPromptbar: React.FC<QueryCopilotPromptProps> = ({
|
||||
<Link
|
||||
onClick={() => {
|
||||
setShowCallout(false);
|
||||
openFeedbackModal(generatedQuery, true, userPrompt);
|
||||
useQueryCopilot.getState().openFeedbackModal(generatedQuery, true, userPrompt);
|
||||
}}
|
||||
>
|
||||
more feedback?
|
||||
@@ -565,7 +524,7 @@ export const QueryCopilotPromptbar: React.FC<QueryCopilotPromptProps> = ({
|
||||
iconProps={{ iconName: dislikeQuery === true ? "DislikeSolid" : "Dislike" }}
|
||||
onClick={() => {
|
||||
if (!dislikeQuery) {
|
||||
openFeedbackModal(generatedQuery, false, userPrompt);
|
||||
useQueryCopilot.getState().openFeedbackModal(generatedQuery, false, userPrompt);
|
||||
setLikeQuery(false);
|
||||
}
|
||||
setDislikeQuery(!dislikeQuery);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/* eslint-disable no-console */
|
||||
import { Stack } from "@fluentui/react";
|
||||
import { QueryCopilotSampleContainerId, QueryCopilotSampleDatabaseId } from "Common/Constants";
|
||||
import { CommandButtonComponentProps } from "Explorer/Controls/CommandButton/CommandButtonComponent";
|
||||
import { EditorReact } from "Explorer/Controls/Editor/EditorReact";
|
||||
import { useCommandBar } from "Explorer/Menus/CommandBar/CommandBarComponentAdapter";
|
||||
@@ -12,7 +11,6 @@ import { QueryCopilotResults } from "Explorer/QueryCopilot/Shared/QueryCopilotRe
|
||||
import { userContext } from "UserContext";
|
||||
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||
import { useSidePanel } from "hooks/useSidePanel";
|
||||
import { ReactTabKind, TabsState, useTabs } from "hooks/useTabs";
|
||||
import React, { useState } from "react";
|
||||
import SplitterLayout from "react-splitter-layout";
|
||||
import QueryCommandIcon from "../../../images/CopilotCommand.svg";
|
||||
@@ -30,14 +28,13 @@ export const QueryCopilotTab: React.FC<QueryCopilotProps> = ({ explorer }: Query
|
||||
? StringUtility.toBoolean(cachedCopilotToggleStatus)
|
||||
: true;
|
||||
const [copilotActive, setCopilotActive] = useState<boolean>(copilotInitialActive);
|
||||
const [tabActive, setTabActive] = useState<boolean>(true);
|
||||
|
||||
const getCommandbarButtons = (): CommandButtonComponentProps[] => {
|
||||
const executeQueryBtnLabel = selectedQuery ? "Execute Selection" : "Execute Query";
|
||||
const executeQueryBtn = {
|
||||
iconSrc: ExecuteQueryIcon,
|
||||
iconAlt: executeQueryBtnLabel,
|
||||
onCommandClick: () => OnExecuteQueryClick(useQueryCopilot),
|
||||
onCommandClick: () => OnExecuteQueryClick(),
|
||||
commandButtonLabel: executeQueryBtnLabel,
|
||||
ariaLabel: executeQueryBtnLabel,
|
||||
hasPopup: false,
|
||||
@@ -76,13 +73,10 @@ export const QueryCopilotTab: React.FC<QueryCopilotProps> = ({ explorer }: Query
|
||||
|
||||
React.useEffect(() => {
|
||||
return () => {
|
||||
useTabs.subscribe((state: TabsState) => {
|
||||
if (state.activeReactTab === ReactTabKind.QueryCopilot) {
|
||||
setTabActive(true);
|
||||
} else {
|
||||
setTabActive(false);
|
||||
}
|
||||
});
|
||||
const commandbarButtons = getCommandbarButtons();
|
||||
commandbarButtons.pop();
|
||||
commandbarButtons.map((props: CommandButtonComponentProps) => (props.disabled = true));
|
||||
useCommandBar.getState().setContextButtons(commandbarButtons);
|
||||
};
|
||||
}, []);
|
||||
|
||||
@@ -94,13 +88,8 @@ export const QueryCopilotTab: React.FC<QueryCopilotProps> = ({ explorer }: Query
|
||||
return (
|
||||
<Stack className="tab-pane" style={{ width: "100%" }}>
|
||||
<div style={isGeneratingQuery ? { height: "100%" } : { overflowY: "auto", height: "100%" }}>
|
||||
{tabActive && copilotActive && (
|
||||
<QueryCopilotPromptbar
|
||||
explorer={explorer}
|
||||
toggleCopilot={toggleCopilot}
|
||||
databaseId={QueryCopilotSampleDatabaseId}
|
||||
containerId={QueryCopilotSampleContainerId}
|
||||
></QueryCopilotPromptbar>
|
||||
{copilotActive && (
|
||||
<QueryCopilotPromptbar explorer={explorer} toggleCopilot={toggleCopilot}></QueryCopilotPromptbar>
|
||||
)}
|
||||
<Stack className="tabPaneContentContainer">
|
||||
<SplitterLayout percentage={true} vertical={true} primaryIndex={0} primaryMinSize={30} secondaryMinSize={70}>
|
||||
|
||||
@@ -7,11 +7,6 @@ import { getCommonQueryOptions } from "Common/dataAccess/queryDocuments";
|
||||
import DocumentId from "Explorer/Tree/DocumentId";
|
||||
import { logConsoleProgress } from "Utils/NotificationConsoleUtils";
|
||||
|
||||
export interface SuggestedPrompt {
|
||||
id: number;
|
||||
text: string;
|
||||
}
|
||||
|
||||
export const querySampleDocuments = (query: string, options: FeedOptions): QueryIterator<ItemDefinition & Resource> => {
|
||||
options = getCommonQueryOptions(options);
|
||||
return sampleDataClient()
|
||||
@@ -38,19 +33,3 @@ export const readSampleDocument = async (documentId: DocumentId): Promise<Item>
|
||||
clearMessage();
|
||||
}
|
||||
};
|
||||
|
||||
export const getSampleDatabaseSuggestedPrompts = (): SuggestedPrompt[] => {
|
||||
return [
|
||||
{ id: 1, text: 'Show all products that have the word "ultra" in the name or description' },
|
||||
{ id: 2, text: "What are all of the possible categories for the products, and their counts?" },
|
||||
{ id: 3, text: 'Show me all products that have been reviewed by someone with a username that contains "bob"' },
|
||||
];
|
||||
};
|
||||
|
||||
export const getSuggestedPrompts = (): SuggestedPrompt[] => {
|
||||
return [
|
||||
{ id: 1, text: "Show the first 10 items" },
|
||||
{ id: 2, text: 'Count all the items in my data as "numItems"' },
|
||||
{ id: 3, text: "Find the oldest item added to my collection" },
|
||||
];
|
||||
};
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { QueryCopilotSampleContainerSchema, ShortenedQueryCopilotSampleContainerSchema } from "Common/Constants";
|
||||
import { handleError } from "Common/ErrorHandlingUtils";
|
||||
import { createUri } from "Common/UrlUtility";
|
||||
import Explorer from "Explorer/Explorer";
|
||||
@@ -36,6 +37,9 @@ describe("Query Copilot Client", () => {
|
||||
userPrompt: "UserPrompt",
|
||||
description: "Description",
|
||||
contact: "Contact",
|
||||
containerSchema: userContext.features.enableCopilotFullSchema
|
||||
? QueryCopilotSampleContainerSchema
|
||||
: ShortenedQueryCopilotSampleContainerSchema,
|
||||
};
|
||||
|
||||
const mockStore = useQueryCopilot.getState();
|
||||
@@ -48,16 +52,13 @@ describe("Query Copilot Client", () => {
|
||||
|
||||
const feedbackUri = userContext.features.disableCopilotPhoenixGateaway
|
||||
? createUri("https://copilotorchestrater.azurewebsites.net/", "feedback")
|
||||
: createUri(useQueryCopilot.getState().notebookServerInfo.notebookServerEndpoint, "public/feedback");
|
||||
: createUri(useQueryCopilot.getState().notebookServerInfo.notebookServerEndpoint, "feedback");
|
||||
|
||||
it("should call fetch with the payload with like", async () => {
|
||||
const mockFetch = jest.fn().mockResolvedValueOnce({});
|
||||
|
||||
globalThis.fetch = mockFetch;
|
||||
await SubmitFeedback({
|
||||
databaseId: "test",
|
||||
containerId: "test",
|
||||
mode: "User",
|
||||
params: {
|
||||
likeQuery: true,
|
||||
generatedQuery: "GeneratedQuery",
|
||||
@@ -90,9 +91,6 @@ describe("Query Copilot Client", () => {
|
||||
globalThis.fetch = mockFetch;
|
||||
|
||||
await SubmitFeedback({
|
||||
databaseId: "test",
|
||||
containerId: "test",
|
||||
mode: "User",
|
||||
params: {
|
||||
likeQuery: false,
|
||||
generatedQuery: "GeneratedQuery",
|
||||
@@ -110,7 +108,6 @@ describe("Query Copilot Client", () => {
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
"x-ms-correlationid": "mocked-correlation-id",
|
||||
Authorization: "token mocked-token",
|
||||
},
|
||||
}),
|
||||
);
|
||||
@@ -123,9 +120,6 @@ describe("Query Copilot Client", () => {
|
||||
globalThis.fetch = jest.fn().mockRejectedValueOnce(new Error("Mock error"));
|
||||
|
||||
await SubmitFeedback({
|
||||
databaseId: "test",
|
||||
containerId: "test",
|
||||
mode: "User",
|
||||
params: {
|
||||
likeQuery: true,
|
||||
generatedQuery: "GeneratedQuery",
|
||||
|
||||
@@ -1,192 +1,28 @@
|
||||
import { FeedOptions } from "@azure/cosmos";
|
||||
import {
|
||||
Areas,
|
||||
ConnectionStatusType,
|
||||
ContainerStatusType,
|
||||
HttpStatusCodes,
|
||||
PoolIdType,
|
||||
QueryCopilotSampleContainerId,
|
||||
QueryCopilotSampleContainerSchema,
|
||||
ShortenedQueryCopilotSampleContainerSchema,
|
||||
} from "Common/Constants";
|
||||
import { getErrorMessage, getErrorStack, handleError } from "Common/ErrorHandlingUtils";
|
||||
import { getErrorMessage, handleError } from "Common/ErrorHandlingUtils";
|
||||
import { shouldEnableCrossPartitionKey } from "Common/HeadersUtility";
|
||||
import { MinimalQueryIterator } from "Common/IteratorUtilities";
|
||||
import { createUri } from "Common/UrlUtility";
|
||||
import { queryDocumentsPage } from "Common/dataAccess/queryDocumentsPage";
|
||||
import { configContext } from "ConfigContext";
|
||||
import {
|
||||
ContainerConnectionInfo,
|
||||
CopilotEnabledConfiguration,
|
||||
FeatureRegistration,
|
||||
IProvisionData,
|
||||
} from "Contracts/DataModels";
|
||||
import { AuthorizationTokenHeaderMetadata, QueryResults } from "Contracts/ViewModels";
|
||||
import { useDialog } from "Explorer/Controls/Dialog";
|
||||
import { QueryResults } from "Contracts/ViewModels";
|
||||
import Explorer from "Explorer/Explorer";
|
||||
import { querySampleDocuments } from "Explorer/QueryCopilot/QueryCopilotUtilities";
|
||||
import { FeedbackParams, GenerateSQLQueryResponse } from "Explorer/QueryCopilot/Shared/QueryCopilotInterfaces";
|
||||
import { Action } from "Shared/Telemetry/TelemetryConstants";
|
||||
import { traceFailure, traceStart, traceSuccess } from "Shared/Telemetry/TelemetryProcessor";
|
||||
import { userContext } from "UserContext";
|
||||
import { getAuthorizationHeader } from "Utils/AuthorizationUtils";
|
||||
import { queryPagesUntilContentPresent } from "Utils/QueryUtils";
|
||||
import { QueryCopilotState, useQueryCopilot } from "hooks/useQueryCopilot";
|
||||
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||
import { useTabs } from "hooks/useTabs";
|
||||
import * as StringUtility from "../../../Shared/StringUtility";
|
||||
|
||||
async function fetchWithTimeout(
|
||||
url: string,
|
||||
headers: {
|
||||
[x: string]: string;
|
||||
},
|
||||
) {
|
||||
const timeout = 10000;
|
||||
const options = { timeout };
|
||||
|
||||
const controller = new AbortController();
|
||||
const id = setTimeout(() => controller.abort(), timeout);
|
||||
|
||||
const response = await window.fetch(url, {
|
||||
headers,
|
||||
...options,
|
||||
signal: controller.signal,
|
||||
});
|
||||
clearTimeout(id);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
export const isCopilotFeatureRegistered = async (subscriptionId: string): Promise<boolean> => {
|
||||
const api_version = "2021-07-01";
|
||||
const url = `${configContext.ARM_ENDPOINT}/subscriptions/${subscriptionId}/providers/Microsoft.Features/featureProviders/Microsoft.DocumentDB/subscriptionFeatureRegistrations/MicrosoftCopilotForAzureInCDB?api-version=${api_version}`;
|
||||
const authorizationHeader: AuthorizationTokenHeaderMetadata = getAuthorizationHeader();
|
||||
const headers = { [authorizationHeader.header]: authorizationHeader.token };
|
||||
|
||||
let response;
|
||||
|
||||
try {
|
||||
response = await fetchWithTimeout(url, headers);
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!response?.ok) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const featureRegistration = (await response?.json()) as FeatureRegistration;
|
||||
return featureRegistration?.properties?.state === "Registered";
|
||||
};
|
||||
|
||||
export const getCopilotEnabled = async (): Promise<boolean> => {
|
||||
const url = `${configContext.BACKEND_ENDPOINT}/api/portalsettings/querycopilot`;
|
||||
const authorizationHeader: AuthorizationTokenHeaderMetadata = getAuthorizationHeader();
|
||||
const headers = { [authorizationHeader.header]: authorizationHeader.token };
|
||||
|
||||
let response;
|
||||
|
||||
try {
|
||||
response = await fetchWithTimeout(url, headers);
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!response?.ok) {
|
||||
throw new Error(await response?.text());
|
||||
}
|
||||
|
||||
const copilotPortalConfiguration = (await response?.json()) as CopilotEnabledConfiguration;
|
||||
return copilotPortalConfiguration?.isEnabled;
|
||||
};
|
||||
|
||||
export const allocatePhoenixContainer = async ({
|
||||
explorer,
|
||||
databaseId,
|
||||
containerId,
|
||||
mode,
|
||||
}: {
|
||||
explorer: Explorer;
|
||||
databaseId: string;
|
||||
containerId: string;
|
||||
mode: string;
|
||||
}): Promise<void> => {
|
||||
try {
|
||||
if (
|
||||
useQueryCopilot.getState().containerStatus.status !== ContainerStatusType.Active &&
|
||||
!userContext.features.disableCopilotPhoenixGateaway
|
||||
) {
|
||||
await explorer.allocateContainer(PoolIdType.QueryCopilot, mode);
|
||||
} else {
|
||||
const currentAllocatedSchemaInfo = useQueryCopilot.getState().schemaAllocationInfo;
|
||||
if (
|
||||
currentAllocatedSchemaInfo.databaseId !== databaseId ||
|
||||
currentAllocatedSchemaInfo.containerId !== containerId
|
||||
) {
|
||||
await resetPhoenixContainerSchema({ explorer, databaseId, containerId, mode });
|
||||
}
|
||||
}
|
||||
useQueryCopilot.getState().setSchemaAllocationInfo({
|
||||
databaseId,
|
||||
containerId,
|
||||
});
|
||||
} catch (error) {
|
||||
traceFailure(Action.PhoenixConnection, {
|
||||
dataExplorerArea: Areas.Copilot,
|
||||
status: error.status,
|
||||
error: getErrorMessage(error),
|
||||
errorStack: getErrorStack(error),
|
||||
});
|
||||
useQueryCopilot.getState().resetContainerConnection();
|
||||
if (error?.status === HttpStatusCodes.Forbidden && error.message) {
|
||||
useDialog.getState().showOkModalDialog("Connection Failed", `${error.message}`);
|
||||
} else {
|
||||
useDialog
|
||||
.getState()
|
||||
.showOkModalDialog(
|
||||
"Connection Failed",
|
||||
"We are unable to connect to the temporary workspace. Please try again in a few minutes. If the error persists, file a support ticket.",
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
useTabs.getState().setIsTabExecuting(false);
|
||||
}
|
||||
};
|
||||
|
||||
export const resetPhoenixContainerSchema = async ({
|
||||
explorer,
|
||||
databaseId,
|
||||
containerId,
|
||||
mode,
|
||||
}: {
|
||||
explorer: Explorer;
|
||||
databaseId: string;
|
||||
containerId: string;
|
||||
mode: string;
|
||||
}): Promise<void> => {
|
||||
try {
|
||||
const provisionData: IProvisionData = {
|
||||
poolId: PoolIdType.QueryCopilot,
|
||||
databaseId: databaseId,
|
||||
containerId: containerId,
|
||||
mode: mode,
|
||||
};
|
||||
const connectionInfo = await explorer.phoenixClient.allocateContainer(provisionData);
|
||||
const connectionStatus: ContainerConnectionInfo = {
|
||||
status: ConnectionStatusType.Connecting,
|
||||
};
|
||||
await explorer.setNotebookInfo(false, connectionInfo, connectionStatus);
|
||||
} catch (error) {
|
||||
traceFailure(Action.PhoenixConnection, {
|
||||
dataExplorerArea: Areas.Copilot,
|
||||
status: error.status,
|
||||
error: getErrorMessage(error),
|
||||
errorStack: getErrorStack(error),
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export const SendQueryRequest = async ({
|
||||
userPrompt,
|
||||
explorer,
|
||||
@@ -215,7 +51,7 @@ export const SendQueryRequest = async ({
|
||||
|
||||
const queryUri = userContext.features.disableCopilotPhoenixGateaway
|
||||
? createUri("https://copilotorchestrater.azurewebsites.net/", "generateSQLQuery")
|
||||
: createUri(serverInfo.notebookServerEndpoint, "public/generateSQLQuery");
|
||||
: createUri(serverInfo.notebookServerEndpoint, "generateSQLQuery");
|
||||
|
||||
const payload = {
|
||||
containerSchema: userContext.features.enableCopilotFullSchema
|
||||
@@ -270,19 +106,16 @@ export const SendQueryRequest = async ({
|
||||
export const SubmitFeedback = async ({
|
||||
params,
|
||||
explorer,
|
||||
databaseId,
|
||||
containerId,
|
||||
mode,
|
||||
}: {
|
||||
params: FeedbackParams;
|
||||
explorer: Explorer;
|
||||
databaseId: string;
|
||||
containerId: string;
|
||||
mode: string;
|
||||
}): Promise<void> => {
|
||||
try {
|
||||
const { likeQuery, generatedQuery, userPrompt, description, contact } = params;
|
||||
const payload = {
|
||||
containerSchema: userContext.features.enableCopilotFullSchema
|
||||
? QueryCopilotSampleContainerSchema
|
||||
: ShortenedQueryCopilotSampleContainerSchema,
|
||||
like: likeQuery ? "like" : "dislike",
|
||||
generatedSql: generatedQuery,
|
||||
userPrompt,
|
||||
@@ -293,18 +126,17 @@ export const SubmitFeedback = async ({
|
||||
useQueryCopilot.getState().containerStatus.status !== ContainerStatusType.Active &&
|
||||
!userContext.features.disableCopilotPhoenixGateaway
|
||||
) {
|
||||
await allocatePhoenixContainer({ explorer, databaseId, containerId, mode });
|
||||
await explorer.allocateContainer(PoolIdType.QueryCopilot);
|
||||
}
|
||||
const serverInfo = useQueryCopilot.getState().notebookServerInfo;
|
||||
const feedbackUri = userContext.features.disableCopilotPhoenixGateaway
|
||||
? createUri("https://copilotorchestrater.azurewebsites.net/", "feedback")
|
||||
: createUri(serverInfo.notebookServerEndpoint, "public/feedback");
|
||||
: createUri(serverInfo.notebookServerEndpoint, "feedback");
|
||||
await fetch(feedbackUri, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
"x-ms-correlationid": useQueryCopilot.getState().correlationId,
|
||||
Authorization: `token ${useQueryCopilot.getState().notebookServerInfo.authToken}`,
|
||||
},
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
@@ -313,7 +145,7 @@ export const SubmitFeedback = async ({
|
||||
}
|
||||
};
|
||||
|
||||
export const OnExecuteQueryClick = async (useQueryCopilot: Partial<QueryCopilotState>): Promise<void> => {
|
||||
export const OnExecuteQueryClick = async (): Promise<void> => {
|
||||
traceStart(Action.ExecuteQueryGeneratedFromQueryCopilot, {
|
||||
correlationId: useQueryCopilot.getState().correlationId,
|
||||
userPrompt: useQueryCopilot.getState().userPrompt,
|
||||
@@ -328,14 +160,13 @@ export const OnExecuteQueryClick = async (useQueryCopilot: Partial<QueryCopilotS
|
||||
useQueryCopilot.getState().setQueryIterator(queryIterator);
|
||||
|
||||
setTimeout(async () => {
|
||||
await QueryDocumentsPerPage(0, queryIterator, useQueryCopilot);
|
||||
await QueryDocumentsPerPage(0, queryIterator);
|
||||
}, 100);
|
||||
};
|
||||
|
||||
export const QueryDocumentsPerPage = async (
|
||||
firstItemIndex: number,
|
||||
queryIterator: MinimalQueryIterator,
|
||||
useQueryCopilot: Partial<QueryCopilotState>,
|
||||
): Promise<void> => {
|
||||
try {
|
||||
useQueryCopilot.getState().setIsExecuting(true);
|
||||
|
||||
@@ -32,8 +32,3 @@ export interface FeedbackParams {
|
||||
export interface QueryCopilotProps {
|
||||
explorer: Explorer;
|
||||
}
|
||||
|
||||
export interface CopilotSchemaAllocationInfo {
|
||||
databaseId: string;
|
||||
containerId: string;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ export const QueryCopilotResults: React.FC = (): JSX.Element => {
|
||||
queryResults={useQueryCopilot.getState().queryResults}
|
||||
isExecuting={useQueryCopilot.getState().isExecuting}
|
||||
executeQueryDocumentsPage={(firstItemIndex: number) =>
|
||||
QueryDocumentsPerPage(firstItemIndex, useQueryCopilot.getState().queryIterator, useQueryCopilot)
|
||||
QueryDocumentsPerPage(firstItemIndex, useQueryCopilot.getState().queryIterator)
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -18,8 +18,6 @@ exports[`Query copilot tab snapshot test should render with initial input 1`] =
|
||||
}
|
||||
>
|
||||
<QueryCopilotPromptbar
|
||||
containerId="SampleContainer"
|
||||
databaseId="CopilotSampleDb"
|
||||
explorer={
|
||||
Explorer {
|
||||
"_isInitializingNotebooks": false,
|
||||
|
||||
@@ -19,7 +19,6 @@ import { Action } from "Shared/Telemetry/TelemetryConstants";
|
||||
import { traceOpen } from "Shared/Telemetry/TelemetryProcessor";
|
||||
import { useCarousel } from "hooks/useCarousel";
|
||||
import { usePostgres } from "hooks/usePostgres";
|
||||
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||
import { ReactTabKind, useTabs } from "hooks/useTabs";
|
||||
import * as React from "react";
|
||||
import ConnectIcon from "../../../images/Connect_color.svg";
|
||||
@@ -105,12 +104,6 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
|
||||
(state) => state.sampleDataResourceTokenCollection,
|
||||
),
|
||||
},
|
||||
{
|
||||
dispose: useQueryCopilot.subscribe(
|
||||
() => this.setState({}),
|
||||
(state) => state.copilotEnabled,
|
||||
),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -121,9 +114,9 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
|
||||
|
||||
private getSplashScreenButtons = (): JSX.Element => {
|
||||
if (
|
||||
userContext.apiType === "SQL" &&
|
||||
useQueryCopilot.getState().copilotEnabled &&
|
||||
useDatabases.getState().sampleDataResourceTokenCollection
|
||||
useDatabases.getState().sampleDataResourceTokenCollection &&
|
||||
userContext.features.enableCopilot &&
|
||||
userContext.apiType === "SQL"
|
||||
) {
|
||||
return (
|
||||
<Stack style={{ width: "66%", cursor: "pointer", margin: "40px auto" }} tokens={{ childrenGap: 16 }}>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { ItemDefinition, PartitionKey, PartitionKeyDefinition, QueryIterator, Resource } from "@azure/cosmos";
|
||||
import { querySampleDocuments, readSampleDocument } from "Explorer/QueryCopilot/QueryCopilotUtilities";
|
||||
import { QueryConstants } from "Shared/Constants";
|
||||
import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
|
||||
import * as ko from "knockout";
|
||||
import Q from "q";
|
||||
import { format } from "react-string-format";
|
||||
import { QueryConstants } from "Shared/Constants";
|
||||
import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
|
||||
import DeleteDocumentIcon from "../../../images/DeleteDocument.svg";
|
||||
import NewDocumentIcon from "../../../images/NewDocument.svg";
|
||||
import UploadIcon from "../../../images/Upload_16x16.svg";
|
||||
@@ -331,7 +331,6 @@ export default class DocumentsTab extends TabsBase {
|
||||
|
||||
this.showPartitionKey = this._shouldShowPartitionKey();
|
||||
this._isQueryCopilotSampleContainer =
|
||||
this.collection?.isSampleCollection &&
|
||||
this.collection?.databaseId === QueryCopilotSampleDatabaseId &&
|
||||
this.collection?.id() === QueryCopilotSampleContainerId;
|
||||
}
|
||||
|
||||
@@ -1,16 +1,11 @@
|
||||
import { CopilotProvider } from "Explorer/QueryCopilot/QueryCopilotContext";
|
||||
import { userContext } from "UserContext";
|
||||
import React from "react";
|
||||
import * as DataModels from "../../../Contracts/DataModels";
|
||||
import type { QueryTabOptions } from "../../../Contracts/ViewModels";
|
||||
import { useTabs } from "../../../hooks/useTabs";
|
||||
import Explorer from "../../Explorer";
|
||||
import QueryTabComponent, {
|
||||
IQueryTabComponentProps,
|
||||
ITabAccessor,
|
||||
QueryTabFunctionComponent,
|
||||
} from "../../Tabs/QueryTab/QueryTabComponent";
|
||||
import { IQueryTabComponentProps, ITabAccessor } from "../../Tabs/QueryTab/QueryTabComponent";
|
||||
import TabsBase from "../TabsBase";
|
||||
import QueryTabComponent from "./QueryTabComponent";
|
||||
|
||||
export interface IQueryTabProps {
|
||||
container: Explorer;
|
||||
@@ -45,13 +40,7 @@ export class NewQueryTab extends TabsBase {
|
||||
}
|
||||
|
||||
public render(): JSX.Element {
|
||||
return userContext.apiType === "SQL" ? (
|
||||
<CopilotProvider>
|
||||
<QueryTabFunctionComponent {...this.iQueryTabComponentProps} />
|
||||
</CopilotProvider>
|
||||
) : (
|
||||
<QueryTabComponent {...this.iQueryTabComponentProps} />
|
||||
);
|
||||
return <QueryTabComponent {...this.iQueryTabComponentProps} />;
|
||||
}
|
||||
|
||||
public onTabClick(): void {
|
||||
|
||||
@@ -1,16 +1,6 @@
|
||||
import { fireEvent, render } from "@testing-library/react";
|
||||
import { CollectionTabKind } from "Contracts/ViewModels";
|
||||
import { CopilotProvider } from "Explorer/QueryCopilot/QueryCopilotContext";
|
||||
import { QueryCopilotPromptbar } from "Explorer/QueryCopilot/QueryCopilotPromptbar";
|
||||
import QueryTabComponent, {
|
||||
IQueryTabComponentProps,
|
||||
QueryTabFunctionComponent,
|
||||
} from "Explorer/Tabs/QueryTab/QueryTabComponent";
|
||||
import TabsBase from "Explorer/Tabs/TabsBase";
|
||||
import { updateUserContext, userContext } from "UserContext";
|
||||
import { mount } from "enzyme";
|
||||
import QueryTabComponent, { IQueryTabComponentProps } from "Explorer/Tabs/QueryTab/QueryTabComponent";
|
||||
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||
import { useTabs } from "hooks/useTabs";
|
||||
import React from "react";
|
||||
|
||||
jest.mock("Explorer/Controls/Editor/EditorReact");
|
||||
@@ -21,15 +11,9 @@ describe("QueryTabComponent", () => {
|
||||
mockStore.showCopilotSidebar = false;
|
||||
mockStore.setShowCopilotSidebar = jest.fn();
|
||||
});
|
||||
afterEach(() => jest.clearAllMocks());
|
||||
beforeEach(() => jest.clearAllMocks());
|
||||
|
||||
it("should launch conversational Copilot when ALT+C is pressed and when copilot version is 3", () => {
|
||||
updateUserContext({
|
||||
features: {
|
||||
...userContext.features,
|
||||
copilotVersion: "v3.0",
|
||||
},
|
||||
});
|
||||
it("should launch Copilot when ALT+C is pressed", () => {
|
||||
const propsMock: Readonly<IQueryTabComponentProps> = {
|
||||
collection: { databaseId: "CopilotSampleDb" },
|
||||
onTabAccessor: () => jest.fn(),
|
||||
@@ -47,32 +31,4 @@ describe("QueryTabComponent", () => {
|
||||
|
||||
expect(mockStore.setShowCopilotSidebar).toHaveBeenCalledWith(true);
|
||||
});
|
||||
|
||||
it("copilot should be enabled by default when tab is active", () => {
|
||||
useQueryCopilot.getState().setCopilotEnabled(true);
|
||||
useQueryCopilot.getState().setCopilotUserDBEnabled(true);
|
||||
const activeTab = new TabsBase({
|
||||
tabKind: CollectionTabKind.Query,
|
||||
title: "Query",
|
||||
tabPath: "",
|
||||
});
|
||||
activeTab.tabId = "mockTabId";
|
||||
useTabs.getState().activeTab = activeTab;
|
||||
const propsMock: Readonly<IQueryTabComponentProps> = {
|
||||
collection: { databaseId: "CopilotUserDb", id: () => "CopilotUserContainer" },
|
||||
onTabAccessor: () => jest.fn(),
|
||||
isExecutionError: false,
|
||||
tabId: "mockTabId",
|
||||
tabsBaseInstance: {
|
||||
updateNavbarWithTabsButtons: () => jest.fn(),
|
||||
},
|
||||
} as unknown as IQueryTabComponentProps;
|
||||
|
||||
const container = mount(
|
||||
<CopilotProvider>
|
||||
<QueryTabFunctionComponent {...propsMock} />
|
||||
</CopilotProvider>,
|
||||
);
|
||||
expect(container.find(QueryCopilotPromptbar).exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,29 +1,21 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable no-console */
|
||||
import { FeedOptions } from "@azure/cosmos";
|
||||
import { useDialog } from "Explorer/Controls/Dialog";
|
||||
import { QueryCopilotFeedbackModal } from "Explorer/QueryCopilot/Modal/QueryCopilotFeedbackModal";
|
||||
import { useCopilotStore } from "Explorer/QueryCopilot/QueryCopilotContext";
|
||||
import { QueryCopilotPromptbar } from "Explorer/QueryCopilot/QueryCopilotPromptbar";
|
||||
import { OnExecuteQueryClick, QueryDocumentsPerPage } from "Explorer/QueryCopilot/Shared/QueryCopilotClient";
|
||||
import { OnExecuteQueryClick } from "Explorer/QueryCopilot/Shared/QueryCopilotClient";
|
||||
import { QueryCopilotResults } from "Explorer/QueryCopilot/Shared/QueryCopilotResults";
|
||||
import { QueryCopilotSidebar } from "Explorer/QueryCopilot/V2/Sidebar/QueryCopilotSidebar";
|
||||
import { QueryResultSection } from "Explorer/Tabs/QueryTab/QueryResultSection";
|
||||
import { useSelectedNode } from "Explorer/useSelectedNode";
|
||||
import { QueryConstants } from "Shared/Constants";
|
||||
import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
|
||||
import { Action } from "Shared/Telemetry/TelemetryConstants";
|
||||
import { QueryCopilotState, useQueryCopilot } from "hooks/useQueryCopilot";
|
||||
import { TabsState, useTabs } from "hooks/useTabs";
|
||||
import React, { Fragment } from "react";
|
||||
import SplitterLayout from "react-splitter-layout";
|
||||
import "react-splitter-layout/lib/index.css";
|
||||
import { format } from "react-string-format";
|
||||
import QueryCommandIcon from "../../../../images/CopilotCommand.svg";
|
||||
import LaunchCopilot from "../../../../images/CopilotTabIcon.svg";
|
||||
import CancelQueryIcon from "../../../../images/Entity_cancel.svg";
|
||||
import ExecuteQueryIcon from "../../../../images/ExecuteQuery.svg";
|
||||
import SaveQueryIcon from "../../../../images/save-cosmos.svg";
|
||||
import { NormalizedEventKey } from "../../../Common/Constants";
|
||||
import { NormalizedEventKey, QueryCopilotSampleDatabaseId } from "../../../Common/Constants";
|
||||
import { getErrorMessage } from "../../../Common/ErrorHandlingUtils";
|
||||
import * as HeadersUtility from "../../../Common/HeadersUtility";
|
||||
import { MinimalQueryIterator } from "../../../Common/IteratorUtilities";
|
||||
@@ -32,8 +24,6 @@ import { queryDocuments } from "../../../Common/dataAccess/queryDocuments";
|
||||
import { queryDocumentsPage } from "../../../Common/dataAccess/queryDocumentsPage";
|
||||
import * as DataModels from "../../../Contracts/DataModels";
|
||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||
import * as StringUtility from "../../../Shared/StringUtility";
|
||||
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
|
||||
import { userContext } from "../../../UserContext";
|
||||
import * as QueryUtils from "../../../Utils/QueryUtils";
|
||||
import { useSidePanel } from "../../../hooks/useSidePanel";
|
||||
@@ -82,9 +72,6 @@ export interface IQueryTabComponentProps {
|
||||
isPreferredApiMongoDB?: boolean;
|
||||
monacoEditorSetting?: string;
|
||||
viewModelcollection?: ViewModels.Collection;
|
||||
copilotEnabled?: boolean;
|
||||
isSampleCopilotActive?: boolean;
|
||||
copilotStore?: Partial<QueryCopilotState>;
|
||||
}
|
||||
|
||||
interface IQueryTabStates {
|
||||
@@ -98,24 +85,8 @@ interface IQueryTabStates {
|
||||
showCopilotSidebar: boolean;
|
||||
queryCopilotGeneratedQuery: string;
|
||||
cancelQueryTimeoutID: NodeJS.Timeout;
|
||||
copilotActive: boolean;
|
||||
currentTabActive: boolean;
|
||||
}
|
||||
|
||||
export const QueryTabFunctionComponent = (props: IQueryTabComponentProps): any => {
|
||||
const copilotStore = useCopilotStore();
|
||||
const isSampleCopilotActive = useSelectedNode.getState().isQueryCopilotCollectionSelected();
|
||||
const queryTabProps = {
|
||||
...props,
|
||||
copilotEnabled:
|
||||
useQueryCopilot().copilotEnabled &&
|
||||
(useQueryCopilot().copilotUserDBEnabled || (isSampleCopilotActive && !!userContext.sampleDataConnectionInfo)),
|
||||
isSampleCopilotActive: isSampleCopilotActive,
|
||||
copilotStore: copilotStore,
|
||||
};
|
||||
return <QueryTabComponent {...queryTabProps}></QueryTabComponent>;
|
||||
};
|
||||
|
||||
export default class QueryTabComponent extends React.Component<IQueryTabComponentProps, IQueryTabStates> {
|
||||
public queryEditorId: string;
|
||||
public executeQueryButton: Button;
|
||||
@@ -142,14 +113,12 @@ export default class QueryTabComponent extends React.Component<IQueryTabComponen
|
||||
showCopilotSidebar: useQueryCopilot.getState().showCopilotSidebar,
|
||||
queryCopilotGeneratedQuery: useQueryCopilot.getState().query,
|
||||
cancelQueryTimeoutID: undefined,
|
||||
copilotActive: this._queryCopilotActive(),
|
||||
currentTabActive: true,
|
||||
};
|
||||
this.isCloseClicked = false;
|
||||
this.splitterId = this.props.tabId + "_splitter";
|
||||
this.queryEditorId = `queryeditor${this.props.tabId}`;
|
||||
this.isPreferredApiMongoDB = this.props.isPreferredApiMongoDB;
|
||||
this.isCopilotTabActive = userContext.features.copilotVersion === "v3.0";
|
||||
this.isCopilotTabActive = QueryCopilotSampleDatabaseId === this.props.collection.databaseId;
|
||||
this.executeQueryButton = {
|
||||
enabled: !!this.state.sqlQueryEditorContent && this.state.sqlQueryEditorContent.length > 0,
|
||||
visible: true,
|
||||
@@ -174,19 +143,6 @@ export default class QueryTabComponent extends React.Component<IQueryTabComponen
|
||||
});
|
||||
}
|
||||
|
||||
private _queryCopilotActive(): boolean {
|
||||
if (this.props.copilotEnabled) {
|
||||
const cachedCopilotToggleStatus: string = localStorage.getItem(
|
||||
`${userContext.databaseAccount?.id}-queryCopilotToggleStatus`,
|
||||
);
|
||||
const copilotInitialActive: boolean = cachedCopilotToggleStatus
|
||||
? StringUtility.toBoolean(cachedCopilotToggleStatus)
|
||||
: true;
|
||||
return copilotInitialActive;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public onCloseClick(isClicked: boolean): void {
|
||||
this.isCloseClicked = isClicked;
|
||||
if (useQueryCopilot.getState().wasCopilotUsed && this.isCopilotTabActive) {
|
||||
@@ -211,16 +167,6 @@ export default class QueryTabComponent extends React.Component<IQueryTabComponen
|
||||
setTimeout(async () => {
|
||||
await this._executeQueryDocumentsPage(0);
|
||||
}, 100);
|
||||
if (this.state.copilotActive) {
|
||||
const query = this.state.sqlQueryEditorContent.split("\r\n")?.pop();
|
||||
const isqueryEdited = this.props.copilotStore.generatedQuery && this.props.copilotStore.generatedQuery !== query;
|
||||
if (isqueryEdited) {
|
||||
TelemetryProcessor.traceMark(Action.QueryEdited, {
|
||||
databaseName: this.props.collection.databaseId,
|
||||
collectionId: this.props.collection.id(),
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public onSaveQueryClick = (): void => {
|
||||
@@ -380,9 +326,7 @@ export default class QueryTabComponent extends React.Component<IQueryTabComponen
|
||||
buttons.push({
|
||||
iconSrc: ExecuteQueryIcon,
|
||||
iconAlt: label,
|
||||
onCommandClick: this.props.isSampleCopilotActive
|
||||
? () => OnExecuteQueryClick(this.props.copilotStore)
|
||||
: this.onExecuteQueryClick,
|
||||
onCommandClick: this.isCopilotTabActive ? () => OnExecuteQueryClick() : this.onExecuteQueryClick,
|
||||
commandButtonLabel: label,
|
||||
ariaLabel: label,
|
||||
hasPopup: false,
|
||||
@@ -436,20 +380,6 @@ export default class QueryTabComponent extends React.Component<IQueryTabComponen
|
||||
buttons.push(launchCopilotButton);
|
||||
}
|
||||
|
||||
if (this.props.copilotEnabled) {
|
||||
const toggleCopilotButton = {
|
||||
iconSrc: QueryCommandIcon,
|
||||
iconAlt: "Copilot",
|
||||
onCommandClick: () => {
|
||||
this._toggleCopilot(!this.state.copilotActive);
|
||||
},
|
||||
commandButtonLabel: this.state.copilotActive ? "Disable Copilot" : "Enable Copilot",
|
||||
ariaLabel: "Copilot",
|
||||
hasPopup: false,
|
||||
};
|
||||
buttons.push(toggleCopilotButton);
|
||||
}
|
||||
|
||||
if (!this.props.isPreferredApiMongoDB && this.state.isExecuting) {
|
||||
const label = "Cancel query";
|
||||
buttons.push({
|
||||
@@ -465,31 +395,11 @@ export default class QueryTabComponent extends React.Component<IQueryTabComponen
|
||||
return buttons;
|
||||
}
|
||||
|
||||
private _toggleCopilot = (active: boolean) => {
|
||||
this.setState({ copilotActive: active });
|
||||
useQueryCopilot.getState().setCopilotEnabledforExecution(active);
|
||||
localStorage.setItem(`${userContext.databaseAccount?.id}-queryCopilotToggleStatus`, active.toString());
|
||||
|
||||
TelemetryProcessor.traceSuccess(active ? Action.ActivateQueryCopilot : Action.DeactivateQueryCopilot, {
|
||||
databaseName: this.props.collection.databaseId,
|
||||
collectionId: this.props.collection.id(),
|
||||
});
|
||||
};
|
||||
|
||||
componentDidUpdate = (_prevProps: IQueryTabComponentProps, prevState: IQueryTabStates): void => {
|
||||
if (prevState.copilotActive !== this.state.copilotActive) {
|
||||
useCommandBar.getState().setContextButtons(this.getTabsButtons());
|
||||
}
|
||||
};
|
||||
|
||||
public onChangeContent(newContent: string): void {
|
||||
this.setState({
|
||||
sqlQueryEditorContent: newContent,
|
||||
queryCopilotGeneratedQuery: "",
|
||||
});
|
||||
if (this.state.copilotActive) {
|
||||
this.props.copilotStore?.setQuery(newContent);
|
||||
}
|
||||
if (this.isPreferredApiMongoDB) {
|
||||
if (newContent.length > 0) {
|
||||
this.executeQueryButton = {
|
||||
@@ -524,10 +434,6 @@ export default class QueryTabComponent extends React.Component<IQueryTabComponen
|
||||
: useQueryCopilot.getState().setSelectedQuery("");
|
||||
}
|
||||
|
||||
if (this.state.copilotActive) {
|
||||
this.props.copilotStore?.setSelectedQuery(selectedContent);
|
||||
}
|
||||
|
||||
useCommandBar.getState().setContextButtons(this.getTabsButtons());
|
||||
}
|
||||
|
||||
@@ -536,10 +442,6 @@ export default class QueryTabComponent extends React.Component<IQueryTabComponen
|
||||
return this.state.queryCopilotGeneratedQuery;
|
||||
}
|
||||
|
||||
if (this.state.copilotActive) {
|
||||
return this.props.copilotStore?.query;
|
||||
}
|
||||
|
||||
return this.state.sqlQueryEditorContent;
|
||||
}
|
||||
|
||||
@@ -550,15 +452,12 @@ export default class QueryTabComponent extends React.Component<IQueryTabComponen
|
||||
private unsubscribeCopilotSidebar: () => void;
|
||||
|
||||
componentDidMount(): void {
|
||||
useTabs.subscribe((state: TabsState) => {
|
||||
if (this.state.currentTabActive && state.activeTab?.tabId !== this.props.tabId) {
|
||||
this.setState({
|
||||
currentTabActive: false,
|
||||
});
|
||||
} else if (!this.state.currentTabActive && state.activeTab?.tabId === this.props.tabId) {
|
||||
this.setState({
|
||||
currentTabActive: true,
|
||||
});
|
||||
this.unsubscribeCopilotSidebar = useQueryCopilot.subscribe((state: QueryCopilotState) => {
|
||||
if (this.state.showCopilotSidebar !== state.showCopilotSidebar) {
|
||||
this.setState({ showCopilotSidebar: state.showCopilotSidebar });
|
||||
}
|
||||
if (this.state.queryCopilotGeneratedQuery !== state.query) {
|
||||
this.setState({ queryCopilotGeneratedQuery: state.query });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -567,6 +466,7 @@ export default class QueryTabComponent extends React.Component<IQueryTabComponen
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
this.unsubscribeCopilotSidebar();
|
||||
document.removeEventListener("keydown", this.handleCopilotKeyDown);
|
||||
}
|
||||
|
||||
@@ -574,14 +474,6 @@ export default class QueryTabComponent extends React.Component<IQueryTabComponen
|
||||
return (
|
||||
<Fragment>
|
||||
<div className="tab-pane" id={this.props.tabId} role="tabpanel">
|
||||
{this.props.copilotEnabled && this.state.currentTabActive && this.state.copilotActive && (
|
||||
<QueryCopilotPromptbar
|
||||
explorer={this.props.collection.container}
|
||||
toggleCopilot={this._toggleCopilot}
|
||||
databaseId={this.props.collection.databaseId}
|
||||
containerId={this.props.collection.id()}
|
||||
></QueryCopilotPromptbar>
|
||||
)}
|
||||
<div className="tabPaneContentContainer">
|
||||
<SplitterLayout vertical={true} primaryIndex={0} primaryMinSize={100} secondaryMinSize={200}>
|
||||
<Fragment>
|
||||
@@ -590,7 +482,6 @@ export default class QueryTabComponent extends React.Component<IQueryTabComponen
|
||||
language={"sql"}
|
||||
content={this.setEditorContent()}
|
||||
isReadOnly={false}
|
||||
wordWrap={"on"}
|
||||
ariaLabel={"Editing Query"}
|
||||
lineNumbers={"on"}
|
||||
onContentChanged={(newContent: string) => this.onChangeContent(newContent)}
|
||||
@@ -598,21 +489,8 @@ export default class QueryTabComponent extends React.Component<IQueryTabComponen
|
||||
/>
|
||||
</div>
|
||||
</Fragment>
|
||||
{this.props.isSampleCopilotActive ? (
|
||||
<QueryResultSection
|
||||
isMongoDB={this.props.isPreferredApiMongoDB}
|
||||
queryEditorContent={this.state.sqlQueryEditorContent}
|
||||
error={this.props.copilotStore?.errorMessage}
|
||||
queryResults={this.props.copilotStore?.queryResults}
|
||||
isExecuting={this.props.copilotStore?.isExecuting}
|
||||
executeQueryDocumentsPage={(firstItemIndex: number) =>
|
||||
QueryDocumentsPerPage(
|
||||
firstItemIndex,
|
||||
this.props.copilotStore.queryIterator,
|
||||
this.props.copilotStore,
|
||||
)
|
||||
}
|
||||
/>
|
||||
{this.isCopilotTabActive ? (
|
||||
<QueryCopilotResults />
|
||||
) : (
|
||||
<QueryResultSection
|
||||
isMongoDB={this.props.isPreferredApiMongoDB}
|
||||
@@ -628,14 +506,6 @@ export default class QueryTabComponent extends React.Component<IQueryTabComponen
|
||||
</SplitterLayout>
|
||||
</div>
|
||||
</div>
|
||||
{this.props.copilotEnabled && this.props.copilotStore?.showFeedbackModal && (
|
||||
<QueryCopilotFeedbackModal
|
||||
explorer={this.props.collection.container}
|
||||
databaseId={this.props.collection.databaseId}
|
||||
containerId={this.props.collection.id()}
|
||||
mode={this.props.isSampleCopilotActive ? "Sample" : "User"}
|
||||
/>
|
||||
)}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import { QuickstartTab } from "Explorer/Tabs/QuickstartTab";
|
||||
import { VcoreMongoConnectTab } from "Explorer/Tabs/VCoreMongoConnectTab";
|
||||
import { VcoreMongoQuickstartTab } from "Explorer/Tabs/VCoreMongoQuickstartTab";
|
||||
import { userContext } from "UserContext";
|
||||
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||
import { useTeachingBubble } from "hooks/useTeachingBubble";
|
||||
import ko from "knockout";
|
||||
import React, { MutableRefObject, useEffect, useRef, useState } from "react";
|
||||
@@ -169,7 +170,7 @@ const CloseButton = ({
|
||||
onClick={(event: React.MouseEvent<HTMLSpanElement, MouseEvent>) => {
|
||||
event.stopPropagation();
|
||||
tab ? tab.onCloseTabButtonClick() : useTabs.getState().closeReactTab(tabKind);
|
||||
// tabKind === ReactTabKind.QueryCopilot && useQueryCopilot.getState().resetQueryCopilotStates();
|
||||
tabKind === ReactTabKind.QueryCopilot && useQueryCopilot.getState().resetQueryCopilotStates();
|
||||
}}
|
||||
tabIndex={active ? 0 : undefined}
|
||||
onKeyPress={({ nativeEvent: e }) => tab.onKeyPressClose(undefined, e)}
|
||||
@@ -255,7 +256,6 @@ const isQueryErrorThrown = (tab?: Tab, tabKind?: ReactTabKind): boolean => {
|
||||
};
|
||||
|
||||
const getReactTabContent = (activeReactTab: ReactTabKind, explorer: Explorer): JSX.Element => {
|
||||
// eslint-disable-next-line no-console
|
||||
switch (activeReactTab) {
|
||||
case ReactTabKind.Connect:
|
||||
return userContext.apiType === "VCoreMongo" ? (
|
||||
|
||||
@@ -3,15 +3,15 @@ import * as Constants from "../../Common/Constants";
|
||||
import * as ThemeUtility from "../../Common/ThemeUtility";
|
||||
import * as DataModels from "../../Contracts/DataModels";
|
||||
import * as ViewModels from "../../Contracts/ViewModels";
|
||||
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
|
||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||
import { useNotificationConsole } from "../../hooks/useNotificationConsole";
|
||||
import { useTabs } from "../../hooks/useTabs";
|
||||
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
|
||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||
import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent";
|
||||
import Explorer from "../Explorer";
|
||||
import { useCommandBar } from "../Menus/CommandBar/CommandBarComponentAdapter";
|
||||
import { WaitsForTemplateViewModel } from "../WaitsForTemplateViewModel";
|
||||
import { useSelectedNode } from "../useSelectedNode";
|
||||
import { WaitsForTemplateViewModel } from "../WaitsForTemplateViewModel";
|
||||
// TODO: Use specific actions for logging telemetry data
|
||||
export default class TabsBase extends WaitsForTemplateViewModel {
|
||||
private static id = 0;
|
||||
|
||||
@@ -58,7 +58,6 @@ export default class Collection implements ViewModels.Collection {
|
||||
public indexingPolicy: ko.Observable<DataModels.IndexingPolicy>;
|
||||
public uniqueKeyPolicy: DataModels.UniqueKeyPolicy;
|
||||
public usageSizeInKB: ko.Observable<number>;
|
||||
public computedProperties: ko.Observable<DataModels.ComputedProperties>;
|
||||
|
||||
public offer: ko.Observable<DataModels.Offer>;
|
||||
public conflictResolutionPolicy: ko.Observable<DataModels.ConflictResolutionPolicy>;
|
||||
@@ -122,7 +121,6 @@ export default class Collection implements ViewModels.Collection {
|
||||
this.schema = data.schema;
|
||||
this.requestSchema = data.requestSchema;
|
||||
this.geospatialConfig = ko.observable(data.geospatialConfig);
|
||||
this.computedProperties = ko.observable(data.computedProperties);
|
||||
|
||||
this.partitionKeyPropertyHeaders = this.partitionKey?.paths;
|
||||
this.partitionKeyProperties = this.partitionKeyPropertyHeaders?.map((partitionKeyPropertyHeader, i) => {
|
||||
@@ -179,8 +177,8 @@ export default class Collection implements ViewModels.Collection {
|
||||
this.children.subscribe(() => {
|
||||
// update the database in zustand store
|
||||
const database = this.getDatabase();
|
||||
database?.collections(
|
||||
database?.collections()?.map((collection) => {
|
||||
database.collections(
|
||||
database.collections()?.map((collection) => {
|
||||
if (collection.id() === this.id()) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -156,6 +156,6 @@ export default class ResourceTokenCollection implements ViewModels.CollectionBas
|
||||
}
|
||||
|
||||
public getDatabase(): ViewModels.Database {
|
||||
return useDatabases.getState().findDatabaseWithId(this.databaseId, this.isSampleCollection);
|
||||
return useDatabases.getState().findDatabaseWithId(this.databaseId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Callout, DirectionalHint, ICalloutProps, ILinkProps, Link, Stack, Text } from "@fluentui/react";
|
||||
import { SampleDataTree } from "Explorer/Tree/SampleDataTree";
|
||||
import { getItemName } from "Utils/APITypeUtils";
|
||||
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||
import * as React from "react";
|
||||
import shallow from "zustand/shallow";
|
||||
import CosmosDBIcon from "../../../images/Azure-Cosmos-DB.svg";
|
||||
@@ -768,8 +767,7 @@ export const ResourceTree: React.FC<ResourceTreeProps> = ({ container }: Resourc
|
||||
};
|
||||
|
||||
const dataRootNode = buildDataTree();
|
||||
const isSampleDataEnabled =
|
||||
useQueryCopilot().copilotEnabled && userContext.sampleDataConnectionInfo && userContext.apiType === "SQL";
|
||||
const isSampleDataEnabled = userContext.sampleDataConnectionInfo && userContext.apiType === "SQL";
|
||||
const sampleDataResourceTokenCollection = useDatabases((state) => state.sampleDataResourceTokenCollection);
|
||||
|
||||
return (
|
||||
|
||||
@@ -14,7 +14,7 @@ interface DatabasesState {
|
||||
deleteDatabase: (database: ViewModels.Database) => void;
|
||||
clearDatabases: () => void;
|
||||
isSaveQueryEnabled: () => boolean;
|
||||
findDatabaseWithId: (databaseId: string, isSampleDatabase?: boolean) => ViewModels.Database;
|
||||
findDatabaseWithId: (databaseId: string) => ViewModels.Database;
|
||||
isLastNonEmptyDatabase: () => boolean;
|
||||
findCollection: (databaseId: string, collectionId: string) => ViewModels.Collection;
|
||||
isLastCollection: () => boolean;
|
||||
@@ -33,7 +33,7 @@ export const useDatabases: UseStore<DatabasesState> = create((set, get) => ({
|
||||
updateDatabase: (updatedDatabase: ViewModels.Database) =>
|
||||
set((state) => {
|
||||
const updatedDatabases = state.databases.map((database: ViewModels.Database) => {
|
||||
if (database?.id() === updatedDatabase?.id()) {
|
||||
if (database.id() === updatedDatabase.id()) {
|
||||
return updatedDatabase;
|
||||
}
|
||||
|
||||
@@ -67,9 +67,7 @@ export const useDatabases: UseStore<DatabasesState> = create((set, get) => ({
|
||||
}
|
||||
return true;
|
||||
},
|
||||
findDatabaseWithId: (databaseId: string, isSampleDatabase?: boolean) => {
|
||||
return get().databases.find((db) => databaseId === db.id() && db.isSampleDB === isSampleDatabase);
|
||||
},
|
||||
findDatabaseWithId: (databaseId: string) => get().databases.find((db) => databaseId === db.id()),
|
||||
isLastNonEmptyDatabase: () => {
|
||||
const databases = get().databases;
|
||||
return databases.length === 1 && (databases[0].collections()?.length > 0 || !!databases[0].offer());
|
||||
|
||||
22
src/Main.tsx
22
src/Main.tsx
@@ -1,13 +1,13 @@
|
||||
// CSS Dependencies
|
||||
import { initializeIcons, loadTheme } from "@fluentui/react";
|
||||
import "bootstrap/dist/css/bootstrap.css";
|
||||
import { QuickstartCarousel } from "Explorer/Quickstart/QuickstartCarousel";
|
||||
import { MongoQuickstartTutorial } from "Explorer/Quickstart/Tutorials/MongoQuickstartTutorial";
|
||||
import { SQLQuickstartTutorial } from "Explorer/Quickstart/Tutorials/SQLQuickstartTutorial";
|
||||
import { userContext } from "UserContext";
|
||||
import "bootstrap/dist/css/bootstrap.css";
|
||||
import { useCarousel } from "hooks/useCarousel";
|
||||
import React, { useState } from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import { userContext } from "UserContext";
|
||||
import "../externals/jquery-ui.min.css";
|
||||
import "../externals/jquery-ui.min.js";
|
||||
import "../externals/jquery-ui.structure.min.css";
|
||||
@@ -18,19 +18,21 @@ import "../externals/jquery.typeahead.min.js";
|
||||
// Image Dependencies
|
||||
import { Platform } from "ConfigContext";
|
||||
import { QueryCopilotCarousel } from "Explorer/QueryCopilot/CopilotCarousel";
|
||||
import { QueryCopilotFeedbackModal } from "Explorer/QueryCopilot/Modal/QueryCopilotFeedbackModal";
|
||||
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||
import "../images/CosmosDB_rgb_ui_lighttheme.ico";
|
||||
import hdeConnectImage from "../images/HdeConnectCosmosDB.svg";
|
||||
import "../images/favicon.ico";
|
||||
import "../less/TableStyles/CustomizeColumns.less";
|
||||
import "../less/TableStyles/EntityEditor.less";
|
||||
import "../less/TableStyles/fulldatatables.less";
|
||||
import "../less/TableStyles/queryBuilder.less";
|
||||
import hdeConnectImage from "../images/HdeConnectCosmosDB.svg";
|
||||
import "../less/documentDB.less";
|
||||
import "../less/forms.less";
|
||||
import "../less/infobox.less";
|
||||
import "../less/menus.less";
|
||||
import "../less/messagebox.less";
|
||||
import "../less/resourceTree.less";
|
||||
import "../less/TableStyles/CustomizeColumns.less";
|
||||
import "../less/TableStyles/EntityEditor.less";
|
||||
import "../less/TableStyles/fulldatatables.less";
|
||||
import "../less/TableStyles/queryBuilder.less";
|
||||
import "../less/tree.less";
|
||||
import { CollapsedResourceTree } from "./Common/CollapsedResourceTree";
|
||||
import { ResourceTreeContainer } from "./Common/ResourceTreeContainer";
|
||||
@@ -53,11 +55,11 @@ import "./Explorer/Panes/PanelComponent.less";
|
||||
import { SidePanel } from "./Explorer/Panes/PanelContainerComponent";
|
||||
import "./Explorer/SplashScreen/SplashScreen.less";
|
||||
import { Tabs } from "./Explorer/Tabs/Tabs";
|
||||
import { useConfig } from "./hooks/useConfig";
|
||||
import { useKnockoutExplorer } from "./hooks/useKnockoutExplorer";
|
||||
import "./Libs/jquery";
|
||||
import { appThemeFabric } from "./Platform/Fabric/FabricTheme";
|
||||
import "./Shared/appInsights";
|
||||
import { useConfig } from "./hooks/useConfig";
|
||||
import { useKnockoutExplorer } from "./hooks/useKnockoutExplorer";
|
||||
|
||||
initializeIcons();
|
||||
|
||||
@@ -65,6 +67,7 @@ const App: React.FunctionComponent = () => {
|
||||
const [isLeftPaneExpanded, setIsLeftPaneExpanded] = useState<boolean>(true);
|
||||
const isCarouselOpen = useCarousel((state) => state.shouldOpen);
|
||||
const isCopilotCarouselOpen = useCarousel((state) => state.showCopilotCarousel);
|
||||
const shouldShowModal = useQueryCopilot((state) => state.showFeedbackModal);
|
||||
|
||||
const config = useConfig();
|
||||
if (config?.platform === Platform.Fabric) {
|
||||
@@ -133,6 +136,7 @@ const App: React.FunctionComponent = () => {
|
||||
{<SQLQuickstartTutorial />}
|
||||
{<MongoQuickstartTutorial />}
|
||||
{<QueryCopilotCarousel isOpen={isCopilotCarouselOpen} explorer={explorer} />}
|
||||
{shouldShowModal && <QueryCopilotFeedbackModal explorer={explorer} />}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -112,7 +112,7 @@ export function extractFeatures(given = new URLSearchParams(window.location.sear
|
||||
enableLegacyMongoShellV2Debug: "true" === get("enablelegacymongoshellv2debug"),
|
||||
loadLegacyMongoShellFromBE: "true" === get("loadlegacymongoshellfrombe"),
|
||||
enableCopilot: "true" === get("enablecopilot", "true"),
|
||||
copilotVersion: get("copilotversion") ?? "v2.0",
|
||||
copilotVersion: get("copilotversion") ?? "v1.0",
|
||||
disableCopilotPhoenixGateaway: "true" === get("disablecopilotphoenixgateaway"),
|
||||
enableCopilotFullSchema: "true" === get("enablecopilotfullschema", "true"),
|
||||
copilotChatFixedMonacoEditorHeight: "true" === get("copilotchatfixedmonacoeditorheight"),
|
||||
|
||||
@@ -133,10 +133,6 @@ export enum Action {
|
||||
CompleteUITour,
|
||||
OpenQueryCopilotFromSplashScreen,
|
||||
OpenQueryCopilotFromNewQuery,
|
||||
ActivateQueryCopilot,
|
||||
DeactivateQueryCopilot,
|
||||
QueryGenerationFromCopilotPrompt,
|
||||
QueryEdited,
|
||||
ExecuteQueryGeneratedFromQueryCopilot,
|
||||
}
|
||||
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
Run "npm run generateARMClients" to regenerate
|
||||
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
||||
|
||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-02-15-preview/cosmos-db.json
|
||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2023-09-15-preview/cosmos-db.json
|
||||
*/
|
||||
|
||||
import { configContext } from "../../../../ConfigContext";
|
||||
import { armRequest } from "../../request";
|
||||
import * as Types from "./types";
|
||||
const apiVersion = "2024-02-15-preview";
|
||||
import { configContext } from "../../../../ConfigContext";
|
||||
const apiVersion = "2023-09-15-preview";
|
||||
|
||||
/* Lists the Cassandra keyspaces under an existing Azure Cosmos DB database account. */
|
||||
export async function listCassandraKeyspaces(
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
Run "npm run generateARMClients" to regenerate
|
||||
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
||||
|
||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-02-15-preview/cosmos-db.json
|
||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2023-09-15-preview/cosmos-db.json
|
||||
*/
|
||||
|
||||
import { configContext } from "../../../../ConfigContext";
|
||||
import { armRequest } from "../../request";
|
||||
import * as Types from "./types";
|
||||
const apiVersion = "2024-02-15-preview";
|
||||
import { configContext } from "../../../../ConfigContext";
|
||||
const apiVersion = "2023-09-15-preview";
|
||||
|
||||
/* Retrieves the metrics determined by the given filter for the given database account and collection. */
|
||||
export async function listMetrics(
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
Run "npm run generateARMClients" to regenerate
|
||||
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
||||
|
||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-02-15-preview/cosmos-db.json
|
||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2023-09-15-preview/cosmos-db.json
|
||||
*/
|
||||
|
||||
import { configContext } from "../../../../ConfigContext";
|
||||
import { armRequest } from "../../request";
|
||||
import * as Types from "./types";
|
||||
const apiVersion = "2024-02-15-preview";
|
||||
import { configContext } from "../../../../ConfigContext";
|
||||
const apiVersion = "2023-09-15-preview";
|
||||
|
||||
/* Retrieves the metrics determined by the given filter for the given collection, split by partition. */
|
||||
export async function listMetrics(
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
Run "npm run generateARMClients" to regenerate
|
||||
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
||||
|
||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-02-15-preview/cosmos-db.json
|
||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2023-09-15-preview/cosmos-db.json
|
||||
*/
|
||||
|
||||
import { configContext } from "../../../../ConfigContext";
|
||||
import { armRequest } from "../../request";
|
||||
import * as Types from "./types";
|
||||
const apiVersion = "2024-02-15-preview";
|
||||
import { configContext } from "../../../../ConfigContext";
|
||||
const apiVersion = "2023-09-15-preview";
|
||||
|
||||
/* Retrieves the metrics determined by the given filter for the given collection and region, split by partition. */
|
||||
export async function listMetrics(
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
Run "npm run generateARMClients" to regenerate
|
||||
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
||||
|
||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-02-15-preview/cosmos-db.json
|
||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2023-09-15-preview/cosmos-db.json
|
||||
*/
|
||||
|
||||
import { configContext } from "../../../../ConfigContext";
|
||||
import { armRequest } from "../../request";
|
||||
import * as Types from "./types";
|
||||
const apiVersion = "2024-02-15-preview";
|
||||
import { configContext } from "../../../../ConfigContext";
|
||||
const apiVersion = "2023-09-15-preview";
|
||||
|
||||
/* Retrieves the metrics determined by the given filter for the given database account, collection and region. */
|
||||
export async function listMetrics(
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
Run "npm run generateARMClients" to regenerate
|
||||
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
||||
|
||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-02-15-preview/cosmos-db.json
|
||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2023-09-15-preview/cosmos-db.json
|
||||
*/
|
||||
|
||||
import { configContext } from "../../../../ConfigContext";
|
||||
import { armRequest } from "../../request";
|
||||
import * as Types from "./types";
|
||||
const apiVersion = "2024-02-15-preview";
|
||||
import { configContext } from "../../../../ConfigContext";
|
||||
const apiVersion = "2023-09-15-preview";
|
||||
|
||||
/* Retrieves the metrics determined by the given filter for the given database account and database. */
|
||||
export async function listMetrics(
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
Run "npm run generateARMClients" to regenerate
|
||||
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
||||
|
||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-02-15-preview/cosmos-db.json
|
||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2023-09-15-preview/cosmos-db.json
|
||||
*/
|
||||
|
||||
import { configContext } from "../../../../ConfigContext";
|
||||
import { armRequest } from "../../request";
|
||||
import * as Types from "./types";
|
||||
const apiVersion = "2024-02-15-preview";
|
||||
import { configContext } from "../../../../ConfigContext";
|
||||
const apiVersion = "2023-09-15-preview";
|
||||
|
||||
/* Retrieves the metrics determined by the given filter for the given database account and region. */
|
||||
export async function listMetrics(
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
Run "npm run generateARMClients" to regenerate
|
||||
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
||||
|
||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-02-15-preview/cosmos-db.json
|
||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2023-09-15-preview/cosmos-db.json
|
||||
*/
|
||||
|
||||
import { configContext } from "../../../../ConfigContext";
|
||||
import { armRequest } from "../../request";
|
||||
import * as Types from "./types";
|
||||
const apiVersion = "2024-02-15-preview";
|
||||
import { configContext } from "../../../../ConfigContext";
|
||||
const apiVersion = "2023-09-15-preview";
|
||||
|
||||
/* Retrieves the properties of an existing Azure Cosmos DB database account. */
|
||||
export async function get(
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
Run "npm run generateARMClients" to regenerate
|
||||
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
||||
|
||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-02-15-preview/cosmos-db.json
|
||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2023-09-15-preview/cosmos-db.json
|
||||
*/
|
||||
|
||||
import { configContext } from "../../../../ConfigContext";
|
||||
import { armRequest } from "../../request";
|
||||
import * as Types from "./types";
|
||||
const apiVersion = "2024-02-15-preview";
|
||||
import { configContext } from "../../../../ConfigContext";
|
||||
const apiVersion = "2023-09-15-preview";
|
||||
|
||||
/* Lists the graphs under an existing Azure Cosmos DB database account. */
|
||||
export async function listGraphs(
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
Run "npm run generateARMClients" to regenerate
|
||||
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
||||
|
||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-02-15-preview/cosmos-db.json
|
||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2023-09-15-preview/cosmos-db.json
|
||||
*/
|
||||
|
||||
import { configContext } from "../../../../ConfigContext";
|
||||
import { armRequest } from "../../request";
|
||||
import * as Types from "./types";
|
||||
const apiVersion = "2024-02-15-preview";
|
||||
import { configContext } from "../../../../ConfigContext";
|
||||
const apiVersion = "2023-09-15-preview";
|
||||
|
||||
/* Lists the Gremlin databases under an existing Azure Cosmos DB database account. */
|
||||
export async function listGremlinDatabases(
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
Run "npm run generateARMClients" to regenerate
|
||||
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
||||
|
||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-02-15-preview/cosmos-db.json
|
||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2023-09-15-preview/cosmos-db.json
|
||||
*/
|
||||
|
||||
import { configContext } from "../../../../ConfigContext";
|
||||
import { armRequest } from "../../request";
|
||||
import * as Types from "./types";
|
||||
const apiVersion = "2024-02-15-preview";
|
||||
import { configContext } from "../../../../ConfigContext";
|
||||
const apiVersion = "2023-09-15-preview";
|
||||
|
||||
/* List Cosmos DB locations and their properties */
|
||||
export async function list(subscriptionId: string): Promise<Types.LocationListResult | Types.CloudError> {
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
Run "npm run generateARMClients" to regenerate
|
||||
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
||||
|
||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-02-15-preview/cosmos-db.json
|
||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2023-09-15-preview/cosmos-db.json
|
||||
*/
|
||||
|
||||
import { configContext } from "../../../../ConfigContext";
|
||||
import { armRequest } from "../../request";
|
||||
import * as Types from "./types";
|
||||
const apiVersion = "2024-02-15-preview";
|
||||
import { configContext } from "../../../../ConfigContext";
|
||||
const apiVersion = "2023-09-15-preview";
|
||||
|
||||
/* Lists the MongoDB databases under an existing Azure Cosmos DB database account. */
|
||||
export async function listMongoDBDatabases(
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
Run "npm run generateARMClients" to regenerate
|
||||
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
||||
|
||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-02-15-preview/cosmos-db.json
|
||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2023-09-15-preview/cosmos-db.json
|
||||
*/
|
||||
|
||||
import { configContext } from "../../../../ConfigContext";
|
||||
import { armRequest } from "../../request";
|
||||
import * as Types from "./types";
|
||||
const apiVersion = "2024-02-15-preview";
|
||||
import { configContext } from "../../../../ConfigContext";
|
||||
const apiVersion = "2023-09-15-preview";
|
||||
|
||||
/* Lists all of the available Cosmos DB Resource Provider operations. */
|
||||
export async function list(): Promise<Types.OperationListResult> {
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
Run "npm run generateARMClients" to regenerate
|
||||
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
||||
|
||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-02-15-preview/cosmos-db.json
|
||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2023-09-15-preview/cosmos-db.json
|
||||
*/
|
||||
|
||||
import { configContext } from "../../../../ConfigContext";
|
||||
import { armRequest } from "../../request";
|
||||
import * as Types from "./types";
|
||||
const apiVersion = "2024-02-15-preview";
|
||||
import { configContext } from "../../../../ConfigContext";
|
||||
const apiVersion = "2023-09-15-preview";
|
||||
|
||||
/* Retrieves the metrics determined by the given filter for the given partition key range id. */
|
||||
export async function listMetrics(
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
Run "npm run generateARMClients" to regenerate
|
||||
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
||||
|
||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-02-15-preview/cosmos-db.json
|
||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2023-09-15-preview/cosmos-db.json
|
||||
*/
|
||||
|
||||
import { configContext } from "../../../../ConfigContext";
|
||||
import { armRequest } from "../../request";
|
||||
import * as Types from "./types";
|
||||
const apiVersion = "2024-02-15-preview";
|
||||
import { configContext } from "../../../../ConfigContext";
|
||||
const apiVersion = "2023-09-15-preview";
|
||||
|
||||
/* Retrieves the metrics determined by the given filter for the given partition key range id and region. */
|
||||
export async function listMetrics(
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
Run "npm run generateARMClients" to regenerate
|
||||
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
||||
|
||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-02-15-preview/cosmos-db.json
|
||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2023-09-15-preview/cosmos-db.json
|
||||
*/
|
||||
|
||||
import { configContext } from "../../../../ConfigContext";
|
||||
import { armRequest } from "../../request";
|
||||
import * as Types from "./types";
|
||||
const apiVersion = "2024-02-15-preview";
|
||||
import { configContext } from "../../../../ConfigContext";
|
||||
const apiVersion = "2023-09-15-preview";
|
||||
|
||||
/* Retrieves the metrics determined by the given filter for the given database account. This url is only for PBS and Replication Latency data */
|
||||
export async function listMetrics(
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
Run "npm run generateARMClients" to regenerate
|
||||
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
||||
|
||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-02-15-preview/cosmos-db.json
|
||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2023-09-15-preview/cosmos-db.json
|
||||
*/
|
||||
|
||||
import { configContext } from "../../../../ConfigContext";
|
||||
import { armRequest } from "../../request";
|
||||
import * as Types from "./types";
|
||||
const apiVersion = "2024-02-15-preview";
|
||||
import { configContext } from "../../../../ConfigContext";
|
||||
const apiVersion = "2023-09-15-preview";
|
||||
|
||||
/* Retrieves the metrics determined by the given filter for the given account, source and target region. This url is only for PBS and Replication Latency data */
|
||||
export async function listMetrics(
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
Run "npm run generateARMClients" to regenerate
|
||||
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
||||
|
||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-02-15-preview/cosmos-db.json
|
||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2023-09-15-preview/cosmos-db.json
|
||||
*/
|
||||
|
||||
import { configContext } from "../../../../ConfigContext";
|
||||
import { armRequest } from "../../request";
|
||||
import * as Types from "./types";
|
||||
const apiVersion = "2024-02-15-preview";
|
||||
import { configContext } from "../../../../ConfigContext";
|
||||
const apiVersion = "2023-09-15-preview";
|
||||
|
||||
/* Retrieves the metrics determined by the given filter for the given account target region. This url is only for PBS and Replication Latency data */
|
||||
export async function listMetrics(
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
Run "npm run generateARMClients" to regenerate
|
||||
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
||||
|
||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-02-15-preview/cosmos-db.json
|
||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2023-09-15-preview/cosmos-db.json
|
||||
*/
|
||||
|
||||
import { configContext } from "../../../../ConfigContext";
|
||||
import { armRequest } from "../../request";
|
||||
import * as Types from "./types";
|
||||
const apiVersion = "2024-02-15-preview";
|
||||
import { configContext } from "../../../../ConfigContext";
|
||||
const apiVersion = "2023-09-15-preview";
|
||||
|
||||
/* Lists the SQL databases under an existing Azure Cosmos DB database account. */
|
||||
export async function listSqlDatabases(
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
Run "npm run generateARMClients" to regenerate
|
||||
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
||||
|
||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-02-15-preview/cosmos-db.json
|
||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2023-09-15-preview/cosmos-db.json
|
||||
*/
|
||||
|
||||
import { configContext } from "../../../../ConfigContext";
|
||||
import { armRequest } from "../../request";
|
||||
import * as Types from "./types";
|
||||
const apiVersion = "2024-02-15-preview";
|
||||
import { configContext } from "../../../../ConfigContext";
|
||||
const apiVersion = "2023-09-15-preview";
|
||||
|
||||
/* Lists the Tables under an existing Azure Cosmos DB database account. */
|
||||
export async function listTables(
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
Run "npm run generateARMClients" to regenerate
|
||||
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
||||
|
||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-02-15-preview/cosmos-db.json
|
||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2023-09-15-preview/cosmos-db.json
|
||||
*/
|
||||
|
||||
/* The List operation response, that contains the client encryption keys and their properties. */
|
||||
@@ -566,14 +566,12 @@ export interface DatabaseAccountGetProperties {
|
||||
minimalTlsVersion?: MinimalTlsVersion;
|
||||
|
||||
/* Indicates the status of the Customer Managed Key feature on the account. In case there are errors, the property provides troubleshooting guidance. */
|
||||
customerManagedKeyStatus?: string;
|
||||
customerManagedKeyStatus?: CustomerManagedKeyStatus;
|
||||
|
||||
/* Flag to indicate enabling/disabling of Priority Based Execution Preview feature on the account */
|
||||
enablePriorityBasedExecution?: boolean;
|
||||
/* Enum to indicate default Priority Level of request for Priority Based Execution. */
|
||||
defaultPriorityLevel?: DefaultPriorityLevel;
|
||||
|
||||
/* Flag to indicate enabling/disabling of Per-Region Per-partition autoscale Preview feature on the account */
|
||||
enablePerRegionPerPartitionAutoscale?: boolean;
|
||||
}
|
||||
|
||||
/* Properties to create and update Azure Cosmos DB database accounts. */
|
||||
@@ -665,14 +663,12 @@ export interface DatabaseAccountCreateUpdateProperties {
|
||||
minimalTlsVersion?: MinimalTlsVersion;
|
||||
|
||||
/* Indicates the status of the Customer Managed Key feature on the account. In case there are errors, the property provides troubleshooting guidance. */
|
||||
customerManagedKeyStatus?: string;
|
||||
customerManagedKeyStatus?: CustomerManagedKeyStatus;
|
||||
|
||||
/* Flag to indicate enabling/disabling of Priority Based Execution Preview feature on the account */
|
||||
enablePriorityBasedExecution?: boolean;
|
||||
/* Enum to indicate default Priority Level of request for Priority Based Execution. */
|
||||
defaultPriorityLevel?: DefaultPriorityLevel;
|
||||
|
||||
/* Flag to indicate enabling/disabling of Per-Region Per-partition autoscale Preview feature on the account */
|
||||
enablePerRegionPerPartitionAutoscale?: boolean;
|
||||
}
|
||||
|
||||
/* Parameters to create and update Cosmos DB database accounts. */
|
||||
@@ -767,14 +763,12 @@ export interface DatabaseAccountUpdateProperties {
|
||||
minimalTlsVersion?: MinimalTlsVersion;
|
||||
|
||||
/* Indicates the status of the Customer Managed Key feature on the account. In case there are errors, the property provides troubleshooting guidance. */
|
||||
customerManagedKeyStatus?: string;
|
||||
customerManagedKeyStatus?: CustomerManagedKeyStatus;
|
||||
|
||||
/* Flag to indicate enabling/disabling of Priority Based Execution Preview feature on the account */
|
||||
enablePriorityBasedExecution?: boolean;
|
||||
/* Enum to indicate default Priority Level of request for Priority Based Execution. */
|
||||
defaultPriorityLevel?: DefaultPriorityLevel;
|
||||
|
||||
/* Flag to indicate enabling/disabling of Per-Region Per-partition autoscale Preview feature on the account */
|
||||
enablePerRegionPerPartitionAutoscale?: boolean;
|
||||
}
|
||||
|
||||
/* Parameters for patching Azure Cosmos DB database account properties. */
|
||||
@@ -1262,9 +1256,6 @@ export interface SqlContainerResource {
|
||||
|
||||
/* The configuration for defining Materialized Views. This must be specified only for creating a Materialized View container. */
|
||||
materializedViewDefinition?: MaterializedViewDefinition;
|
||||
|
||||
/* List of computed properties */
|
||||
computedProperties?: ComputedProperty[];
|
||||
}
|
||||
|
||||
/* Cosmos DB indexing policy */
|
||||
@@ -1334,14 +1325,6 @@ export interface SpatialSpec {
|
||||
/* Indicates the spatial type of index. */
|
||||
export type SpatialType = "Point" | "LineString" | "Polygon" | "MultiPolygon";
|
||||
|
||||
/* The definition of a computed property */
|
||||
export interface ComputedProperty {
|
||||
/* The name of a computed property, for example - "cp_lowerName" */
|
||||
name?: string;
|
||||
/* The query that evaluates the value for computed property, for example - "SELECT VALUE LOWER(c.name) FROM c" */
|
||||
query?: string;
|
||||
}
|
||||
|
||||
/* The configuration of the partition key to be used for partitioning data into multiple partitions */
|
||||
export interface ContainerPartitionKey {
|
||||
/* List of paths using which data within the container can be partitioned */
|
||||
@@ -1946,8 +1929,6 @@ export interface RestoreParametersBase {
|
||||
restoreSource?: string;
|
||||
/* Time to which the account has to be restored (ISO-8601 format). */
|
||||
restoreTimestampInUtc?: string;
|
||||
/* Specifies whether the restored account will have Time-To-Live disabled upon the successful restore. */
|
||||
restoreWithTtlDisabled?: boolean;
|
||||
}
|
||||
|
||||
/* Parameters to indicate the information about the restore. */
|
||||
@@ -2091,5 +2072,19 @@ export type ContinuousTier = "Continuous7Days" | "Continuous30Days";
|
||||
/* Indicates the minimum allowed Tls version. The default is Tls 1.0, except for Cassandra and Mongo API's, which only work with Tls 1.2. */
|
||||
export type MinimalTlsVersion = "Tls" | "Tls11" | "Tls12";
|
||||
|
||||
/* Indicates the status of the Customer Managed Key feature on the account. In case there are errors, the property provides troubleshooting guidance. */
|
||||
export type CustomerManagedKeyStatus =
|
||||
| "Access to your account is currently revoked because the Azure Cosmos DB service is unable to obtain the AAD authentication token for the account's default identity; for more details about this error and how to restore access to your account please visit https://learn.microsoft.com/en-us/azure/cosmos-db/cmk-troubleshooting-guide#azure-active-directory-token-acquisition-error (4000)."
|
||||
| "Access to your account is currently revoked because the Azure Cosmos DB account's key vault key URI does not follow the expected format; for more details about this error and how to restore access to your account please visit https://learn.microsoft.com/en-us/azure/cosmos-db/cmk-troubleshooting-guide#improper-syntax-detected-on-the-key-vault-uri-property (4006)."
|
||||
| "Access to your account is currently revoked because the current default identity no longer has permission to the associated Key Vault key; for more details about this error and how to restore access to your account please visit https://learn.microsoft.com/en-us/azure/cosmos-db/cmk-troubleshooting-guide#default-identity-is-unauthorized-to-access-the-azure-key-vault-key (4002)."
|
||||
| "Access to your account is currently revoked because the Azure Key Vault DNS name specified by the account's keyvaultkeyuri property could not be resolved; for more details about this error and how to restore access to your account please visit https://learn.microsoft.com/en-us/azure/cosmos-db/cmk-troubleshooting-guide#unable-to-resolve-the-key-vaults-dns (4009)."
|
||||
| "Access to your account is currently revoked because the correspondent key is not found on the specified Key Vault; for more details about this error and how to restore access to your account please visit https://learn.microsoft.com/en-us/azure/cosmos-db/cmk-troubleshooting-guide#azure-key-vault-resource-not-found (4003)."
|
||||
| "Access to your account is currently revoked because the Azure Cosmos DB service is unable to wrap or unwrap the key; for more details about this error and how to restore access to your account please visit https://learn.microsoft.com/en-us/azure/cosmos-db/cmk-troubleshooting-guide#internal-unwrapping-procedure-error (4005)."
|
||||
| "Access to your account is currently revoked because the Azure Cosmos DB account has an undefined default identity; for more details about this error and how to restore access to your account please visit https://learn.microsoft.com/en-us/azure/cosmos-db/cmk-troubleshooting-guide#invalid-azure-cosmos-db-default-identity (4015)."
|
||||
| "Access to your account is currently revoked because the access rules are blocking outbound requests to the Azure Key Vault service; for more details about this error and how to restore access to your account please visit https://learn.microsoft.com/en-us/azure/cosmos-db/cmk-troubleshooting-guide (4016)."
|
||||
| "Access to your account is currently revoked because the correspondent Azure Key Vault was not found; for more details about this error and how to restore access to your account please visit https://learn.microsoft.com/en-us/azure/cosmos-db/cmk-troubleshooting-guide#azure-key-vault-resource-not-found (4017)."
|
||||
| "Access to your account is currently revoked; for more details about this error and how to restore access to your account please visit https://learn.microsoft.com/en-us/azure/cosmos-db/cmk-troubleshooting-guide"
|
||||
| "Access to the configured customer managed key confirmed.";
|
||||
|
||||
/* Enum to indicate default priorityLevel of requests */
|
||||
export type DefaultPriorityLevel = "High" | "Low";
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
scheduleRefreshDatabaseResourceToken,
|
||||
} from "Platform/Fabric/FabricUtil";
|
||||
import { getNetworkSettingsWarningMessage } from "Utils/NetworkUtility";
|
||||
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||
import { ReactTabKind, useTabs } from "hooks/useTabs";
|
||||
import { useEffect, useState } from "react";
|
||||
import { AuthType } from "../AuthType";
|
||||
@@ -58,21 +57,19 @@ export function useKnockoutExplorer(platform: Platform): Explorer {
|
||||
userContext.features.phoenixNotebooks = true;
|
||||
userContext.features.phoenixFeatures = true;
|
||||
}
|
||||
let explorer: Explorer;
|
||||
if (platform === Platform.Hosted) {
|
||||
explorer = await configureHosted();
|
||||
const explorer = await configureHosted();
|
||||
setExplorer(explorer);
|
||||
} else if (platform === Platform.Emulator) {
|
||||
explorer = configureEmulator();
|
||||
const explorer = configureEmulator();
|
||||
setExplorer(explorer);
|
||||
} else if (platform === Platform.Portal) {
|
||||
explorer = await configurePortal();
|
||||
const explorer = await configurePortal();
|
||||
setExplorer(explorer);
|
||||
} else if (platform === Platform.Fabric) {
|
||||
explorer = await configureFabric();
|
||||
const explorer = await configureFabric();
|
||||
setExplorer(explorer);
|
||||
}
|
||||
if (explorer && userContext.features.enableCopilot) {
|
||||
await updateContextForCopilot(explorer);
|
||||
await updateContextForSampleData(explorer);
|
||||
}
|
||||
setExplorer(explorer);
|
||||
}
|
||||
};
|
||||
effect();
|
||||
@@ -81,6 +78,9 @@ export function useKnockoutExplorer(platform: Platform): Explorer {
|
||||
useEffect(() => {
|
||||
if (explorer) {
|
||||
applyExplorerBindings(explorer);
|
||||
if (userContext.features.enableCopilot) {
|
||||
updateContextForSampleData(explorer);
|
||||
}
|
||||
}
|
||||
}, [explorer]);
|
||||
|
||||
@@ -554,23 +554,12 @@ interface PortalMessage {
|
||||
inputs?: DataExplorerInputsFrame;
|
||||
}
|
||||
|
||||
async function updateContextForCopilot(explorer: Explorer): Promise<void> {
|
||||
await explorer.configureCopilot();
|
||||
}
|
||||
|
||||
async function updateContextForSampleData(explorer: Explorer): Promise<void> {
|
||||
const copilotEnabled =
|
||||
userContext.apiType === "SQL" && userContext.features.enableCopilot && useQueryCopilot.getState().copilotEnabled;
|
||||
|
||||
if (!copilotEnabled) {
|
||||
if (!userContext.features.enableCopilot) {
|
||||
return;
|
||||
}
|
||||
|
||||
const sampleDatabaseEndpoint = useQueryCopilot.getState().copilotUserDBEnabled
|
||||
? `/api/tokens/sampledataconnection/v2`
|
||||
: `/api/tokens/sampledataconnection`;
|
||||
|
||||
const url = createUri(`${configContext.BACKEND_ENDPOINT}`, sampleDatabaseEndpoint);
|
||||
const url = createUri(`${configContext.BACKEND_ENDPOINT}`, `/api/tokens/sampledataconnection`);
|
||||
const authorizationHeader = getAuthorizationHeader();
|
||||
const headers = { [authorizationHeader.header]: authorizationHeader.token };
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { MinimalQueryIterator } from "Common/IteratorUtilities";
|
||||
import { QueryResults } from "Contracts/ViewModels";
|
||||
import { CopilotMessage, CopilotSchemaAllocationInfo } from "Explorer/QueryCopilot/Shared/QueryCopilotInterfaces";
|
||||
import { CopilotMessage } from "Explorer/QueryCopilot/Shared/QueryCopilotInterfaces";
|
||||
import { guid } from "Explorer/Tables/Utilities";
|
||||
import { useTabs } from "hooks/useTabs";
|
||||
import create, { UseStore } from "zustand";
|
||||
@@ -8,8 +8,6 @@ import * as DataModels from "../Contracts/DataModels";
|
||||
import { ContainerInfo } from "../Contracts/DataModels";
|
||||
|
||||
export interface QueryCopilotState {
|
||||
copilotEnabled: boolean;
|
||||
copilotUserDBEnabled: boolean;
|
||||
generatedQuery: string;
|
||||
likeQuery: boolean;
|
||||
userPrompt: string;
|
||||
@@ -42,14 +40,8 @@ export interface QueryCopilotState {
|
||||
showExplanationBubble: boolean;
|
||||
notebookServerInfo: DataModels.NotebookWorkspaceConnectionInfo;
|
||||
containerStatus: ContainerInfo;
|
||||
schemaAllocationInfo: CopilotSchemaAllocationInfo;
|
||||
isAllocatingContainer: boolean;
|
||||
copilotEnabledforExecution: boolean;
|
||||
|
||||
getState?: () => QueryCopilotState;
|
||||
|
||||
setCopilotEnabled: (copilotEnabled: boolean) => void;
|
||||
setCopilotUserDBEnabled: (copilotUserDBEnabled: boolean) => void;
|
||||
openFeedbackModal: (generatedQuery: string, likeQuery: boolean, userPrompt: string) => void;
|
||||
closeFeedbackModal: () => void;
|
||||
setHideFeedbackModalForLikedQueries: (hideFeedbackModalForLikedQueries: boolean) => void;
|
||||
@@ -84,8 +76,6 @@ export interface QueryCopilotState {
|
||||
setNotebookServerInfo: (notebookServerInfo: DataModels.NotebookWorkspaceConnectionInfo) => void;
|
||||
setContainerStatus: (containerStatus: ContainerInfo) => void;
|
||||
setIsAllocatingContainer: (isAllocatingContainer: boolean) => void;
|
||||
setSchemaAllocationInfo: (schemaAllocationInfo: CopilotSchemaAllocationInfo) => void;
|
||||
setCopilotEnabledforExecution: (copilotEnabledforExecution: boolean) => void;
|
||||
|
||||
resetContainerConnection: () => void;
|
||||
resetQueryCopilotStates: () => void;
|
||||
@@ -94,8 +84,6 @@ export interface QueryCopilotState {
|
||||
type QueryCopilotStore = UseStore<QueryCopilotState>;
|
||||
|
||||
export const useQueryCopilot: QueryCopilotStore = create((set) => ({
|
||||
copilotEnabled: false,
|
||||
copilotUserDBEnabled: false,
|
||||
generatedQuery: "",
|
||||
likeQuery: false,
|
||||
userPrompt: "",
|
||||
@@ -136,15 +124,8 @@ export const useQueryCopilot: QueryCopilotStore = create((set) => ({
|
||||
durationLeftInMinutes: undefined,
|
||||
phoenixServerInfo: undefined,
|
||||
},
|
||||
schemaAllocationInfo: {
|
||||
databaseId: undefined,
|
||||
containerId: undefined,
|
||||
},
|
||||
isAllocatingContainer: false,
|
||||
copilotEnabledforExecution: false,
|
||||
|
||||
setCopilotEnabled: (copilotEnabled: boolean) => set({ copilotEnabled }),
|
||||
setCopilotUserDBEnabled: (copilotUserDBEnabled: boolean) => set({ copilotUserDBEnabled }),
|
||||
openFeedbackModal: (generatedQuery: string, likeQuery: boolean, userPrompt: string) =>
|
||||
set({ generatedQuery, likeQuery, userPrompt, showFeedbackModal: true }),
|
||||
closeFeedbackModal: () => set({ showFeedbackModal: false }),
|
||||
@@ -182,8 +163,6 @@ export const useQueryCopilot: QueryCopilotStore = create((set) => ({
|
||||
set({ notebookServerInfo }),
|
||||
setContainerStatus: (containerStatus: ContainerInfo) => set({ containerStatus }),
|
||||
setIsAllocatingContainer: (isAllocatingContainer: boolean) => set({ isAllocatingContainer }),
|
||||
setSchemaAllocationInfo: (schemaAllocationInfo: CopilotSchemaAllocationInfo) => set({ schemaAllocationInfo }),
|
||||
setCopilotEnabledforExecution: (copilotEnabledforExecution: boolean) => set({ copilotEnabledforExecution }),
|
||||
|
||||
resetContainerConnection: (): void => {
|
||||
useTabs.getState().closeAllNotebookTabs(true);
|
||||
@@ -194,10 +173,6 @@ export const useQueryCopilot: QueryCopilotStore = create((set) => ({
|
||||
durationLeftInMinutes: undefined,
|
||||
phoenixServerInfo: undefined,
|
||||
});
|
||||
useQueryCopilot.getState().setSchemaAllocationInfo({
|
||||
databaseId: undefined,
|
||||
containerId: undefined,
|
||||
});
|
||||
},
|
||||
|
||||
resetQueryCopilotStates: () => {
|
||||
@@ -242,10 +217,6 @@ export const useQueryCopilot: QueryCopilotStore = create((set) => ({
|
||||
durationLeftInMinutes: undefined,
|
||||
phoenixServerInfo: undefined,
|
||||
},
|
||||
schemaAllocationInfo: {
|
||||
databaseId: undefined,
|
||||
containerId: undefined,
|
||||
},
|
||||
isAllocatingContainer: false,
|
||||
}));
|
||||
},
|
||||
|
||||
@@ -5,7 +5,7 @@ import NotebookTabV2 from "../Explorer/Tabs/NotebookV2Tab";
|
||||
import TabsBase from "../Explorer/Tabs/TabsBase";
|
||||
import { Platform, configContext } from "./../ConfigContext";
|
||||
|
||||
export interface TabsState {
|
||||
interface TabsState {
|
||||
openedTabs: TabsBase[];
|
||||
openedReactTabs: ReactTabKind[];
|
||||
activeTab: TabsBase | undefined;
|
||||
|
||||
@@ -16,7 +16,7 @@ Results of this file should be checked into the repo.
|
||||
*/
|
||||
|
||||
// CHANGE THESE VALUES TO GENERATE NEW CLIENTS
|
||||
const version = "2024-02-15-preview";
|
||||
const version = "2023-09-15-preview";
|
||||
/* The following are legal options for resourceName but you generally will only use cosmos-db:
|
||||
"cosmos-db" | "managedCassandra" | "mongorbac" | "notebook" | "privateEndpointConnection" | "privateLinkResources" |
|
||||
"rbac" | "restorable" | "services"
|
||||
@@ -117,9 +117,9 @@ const propertyToType = (property: Property, prop: string, required: boolean) =>
|
||||
if (property.allOf) {
|
||||
outputTypes.push(`
|
||||
/* ${property.description || "undocumented"} */
|
||||
${property.readOnly ? "readonly " : ""}${prop}${required ? "" : "?"}: ${property.allOf
|
||||
.map((allof: { $ref: string }) => refToType(allof.$ref))
|
||||
.join(" & ")}`);
|
||||
${property.readOnly ? "readonly " : ""}${prop}${
|
||||
required ? "" : "?"
|
||||
}: ${property.allOf.map((allof: { $ref: string }) => refToType(allof.$ref)).join(" & ")}`);
|
||||
} else if (property.$ref) {
|
||||
const type = refToType(property.$ref);
|
||||
outputTypes.push(`
|
||||
@@ -142,8 +142,8 @@ const propertyToType = (property: Property, prop: string, required: boolean) =>
|
||||
outputTypes.push(`
|
||||
/* ${property.description || "undocumented"} */
|
||||
${property.readOnly ? "readonly " : ""}${prop}${required ? "" : "?"}: ${property.enum
|
||||
.map((v: string) => `"${v}"`)
|
||||
.join(" | ")}
|
||||
.map((v: string) => `"${v}"`)
|
||||
.join(" | ")}
|
||||
`);
|
||||
} else {
|
||||
if (property.type === undefined) {
|
||||
@@ -153,8 +153,8 @@ const propertyToType = (property: Property, prop: string, required: boolean) =>
|
||||
outputTypes.push(`
|
||||
/* ${property.description || "undocumented"} */
|
||||
${property.readOnly ? "readonly " : ""}${prop}${required ? "" : "?"}: ${
|
||||
propertyMap[property.type] ? propertyMap[property.type] : property.type
|
||||
}`);
|
||||
propertyMap[property.type] ? propertyMap[property.type] : property.type
|
||||
}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -247,7 +247,7 @@ async function main() {
|
||||
const operation = schema.paths[path][method];
|
||||
const [, methodName] = operation.operationId.split("_");
|
||||
const bodyParameter = operation.parameters.find(
|
||||
(parameter: { in: string; required: boolean }) => parameter.in === "body" && parameter.required === true,
|
||||
(parameter: { in: string; required: boolean }) => parameter.in === "body" && parameter.required === true
|
||||
);
|
||||
outputClient.push(`
|
||||
/* ${operation.description || "undocumented"} */
|
||||
@@ -259,8 +259,8 @@ async function main() {
|
||||
) : Promise<${responseType(operation, "Types")}> {
|
||||
const path = \`${path.replace(/{/g, "${")}\`
|
||||
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "${method.toLocaleUpperCase()}", apiVersion, ${
|
||||
bodyParameter ? "body" : ""
|
||||
} })
|
||||
bodyParameter ? "body" : ""
|
||||
} })
|
||||
}
|
||||
`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user