Compare commits

..

14 Commits

Author SHA1 Message Date
Victor Meng
91c9c8fee7 Remove extra / in url 2023-08-08 12:30:15 -07:00
Victor Meng
5c21e9d5ee Switch to tools federation endpoints for query copilot 2023-08-08 12:21:37 -07:00
sindhuba
879cb08949 Fix issue with feature flag (#1567) 2023-08-08 08:03:42 -07:00
sindhuba
92f43c28a7 Add logic in DE to show NPS Survey (#1565)
* Send messages from DE to Portal to display NPS Survey

* Address comments
2023-08-04 13:24:30 -07:00
bogercraig
5f0c7bcea2 Users/bogercraig/endpointvalidation (#1554)
* Adding example endpoint with trailing forward slash.

* Move backend and ARM endpoint validation to configContext for initialization from config.json.

* Added debugging script and attempts to relocate endpoint validation list.

* Move default endpoint list to endpoint validation code and allow falling back to the default list during unit tests if configContext is not initialized.

* Remove leftover debugger statements.

* Remove test debug script in package.json for debugging unit tests in browser.

* Run prettier on modified files.

* Overwriting with package.json from master.

* Overwriting with version from master.

* Remove test ARM endpoint.

* Replace ternary operator with || for more concise arguments per Victor's feedback.

---------

Co-authored-by: Craig Boger <craig.boger@microsoft.com>
2023-08-03 14:47:50 -04:00
Predrag Klepic
fa55d528ad [Query Copilot] Maintain Query Copilot state when switching tabs (#1559)
* Sample implementation for saving states

* Maintaining Query Copilot state

* Fixing failed PR checks

* Additional changes based on checks

* snapshots updated

* Changes based on merging previous PR

* test mock changed

* Fixing minor bug for close button

* Destruct of queryCopilotState

* passing only function in Tabs component

* Maintaining copilot state with zustand store

* additional test changes

* test snapshot updated

---------

Co-authored-by: Predrag Klepic <v-prklepic@microsoft.com>
2023-08-03 09:23:31 +02:00
Predrag Klepic
c873fed7aa Disable Query Copilot Tab flickering (#1564)
Co-authored-by: Predrag Klepic <v-prklepic@microsoft.com>
2023-08-02 17:37:37 +02:00
v-darkora
7ec5290293 [Query Copilot] Update feature flag after sample data connection info fetch (#1560)
* Update feature flag if sample data exists

* Add additional conditional

* Revert useknockout to starting condition

* Use tracked property for rendering conditiona
2023-07-28 09:43:12 +02:00
v-darkora
4005128211 [Query Copilot] Add telemetry for query copilot (#1555) 2023-07-27 11:41:41 -07:00
v-darkora
38d13cc74e [Query Copilot] Generate query on enter (#1558) 2023-07-27 10:45:42 -07:00
Predrag Klepic
4ab93a5a32 [Query Copilot] Updated Copilot links (#1556)
Co-authored-by: Predrag Klepic <v-prklepic@microsoft.com>
2023-07-27 10:43:24 -07:00
Predrag Klepic
44e25c0769 [Query Copilot] New Query button added on Items section (#1552)
Co-authored-by: Predrag Klepic <v-prklepic@microsoft.com>
2023-07-27 10:42:17 -07:00
MokireddySampath
8ea8f0230f role has been changed to heading (#1453)
* arialabel has been added to close button of invitational youtube video

* heading role has been addedd and tag has been changed to h1

* Update QuickstartCarousel.tsx

* Update QuickstartCarousel.tsx

* Update SplashScreen.tsx
2023-07-27 07:30:27 +05:30
MokireddySampath
655b998b84 outline has been added to choose columns links (#1454)
* arialabel has been added to close button of invitational youtube video

* heading role has been addedd and tag has been changed to h1

* outline has been restored to choose columns link in entities page

* Update QuickstartCarousel.tsx

* Update SplashScreen.tsx

* Update TableEntity.tsx

* outline for edit entity has been added on focus
2023-07-27 07:29:54 +05:30
29 changed files with 3175 additions and 5454 deletions

View File

@@ -230,7 +230,7 @@ input::-webkit-inner-spin-button {
.advanced-options-panel .advanced-options .select .select-options-link {
margin-left: 4px;
cursor: pointer;
outline: none;
padding: 2px;
}
.query-panel .row .column-headers .Field {

8198
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -167,7 +167,7 @@
"less-vars-loader": "1.1.0",
"mini-css-extract-plugin": "2.1.0",
"monaco-editor-webpack-plugin": "1.7.0",
"node-fetch": "3.3.2",
"node-fetch": "2.6.1",
"playwright": "1.13.0",
"prettier": "2.2.1",
"process": "0.11.10",

View File

@@ -358,6 +358,7 @@ export enum ContainerStatusType {
export enum PoolIdType {
DefaultPoolId = "default",
QueryCopilot = "query-copilot",
}
export const EmulatorMasterKey =

View File

@@ -137,7 +137,7 @@ export const TableEntity: FunctionComponent<TableEntityProps> = ({
/>
{!isEntityValueDisable && (
<TooltipHost content="Edit property" id="editTooltip">
<div tabIndex={0}>
<div>
<Image
{...imageProps}
src={EditIcon}

View File

@@ -1,14 +1,14 @@
import {
allowedAadEndpoints,
allowedArcadiaEndpoints,
allowedArmEndpoints,
allowedBackendEndpoints,
allowedEmulatorEndpoints,
allowedGraphEndpoints,
allowedHostedExplorerEndpoints,
allowedJunoOrigins,
allowedMongoBackendEndpoints,
allowedMsalRedirectEndpoints,
defaultAllowedArmEndpoints,
defaultAllowedBackendEndpoints,
validateEndpoint,
} from "Utils/EndpointValidation";
@@ -20,6 +20,8 @@ export enum Platform {
export interface ConfigContext {
platform: Platform;
allowedArmEndpoints: ReadonlyArray<string>;
allowedBackendEndpoints: ReadonlyArray<string>;
allowedParentFrameOrigins: ReadonlyArray<string>;
gitSha?: string;
proxyPath?: string;
@@ -49,6 +51,8 @@ export interface ConfigContext {
// Default configuration
let configContext: Readonly<ConfigContext> = {
platform: Platform.Portal,
allowedArmEndpoints: defaultAllowedArmEndpoints,
allowedBackendEndpoints: defaultAllowedBackendEndpoints,
allowedParentFrameOrigins: [
`^https:\\/\\/cosmos\\.azure\\.(com|cn|us)$`,
`^https:\\/\\/[\\.\\w]*portal\\.azure\\.(com|cn|us)$`,
@@ -77,7 +81,7 @@ let configContext: Readonly<ConfigContext> = {
export function resetConfigContext(): void {
if (process.env.NODE_ENV !== "test") {
throw new Error("resetConfigContext can only becalled in a test environment");
throw new Error("resetConfigContext can only be called in a test environment");
}
configContext = {} as ConfigContext;
}
@@ -87,7 +91,7 @@ export function updateConfigContext(newContext: Partial<ConfigContext>): void {
return;
}
if (!validateEndpoint(newContext.ARM_ENDPOINT, allowedArmEndpoints)) {
if (!validateEndpoint(newContext.ARM_ENDPOINT, configContext.allowedArmEndpoints || defaultAllowedArmEndpoints)) {
delete newContext.ARM_ENDPOINT;
}
@@ -107,7 +111,12 @@ export function updateConfigContext(newContext: Partial<ConfigContext>): void {
delete newContext.ARCADIA_ENDPOINT;
}
if (!validateEndpoint(newContext.BACKEND_ENDPOINT, allowedBackendEndpoints)) {
if (
!validateEndpoint(
newContext.BACKEND_ENDPOINT,
configContext.allowedBackendEndpoints || defaultAllowedBackendEndpoints
)
) {
delete newContext.BACKEND_ENDPOINT;
}
@@ -130,7 +139,7 @@ export function updateConfigContext(newContext: Partial<ConfigContext>): void {
Object.assign(configContext, newContext);
}
// Injected for local develpment. These will be removed in the production bundle by webpack
// Injected for local development. These will be removed in the production bundle by webpack
if (process.env.NODE_ENV === "development") {
const port: string = process.env.PORT || "1234";
updateConfigContext({

View File

@@ -1,3 +1,5 @@
import { Action } from "Shared/Telemetry/TelemetryConstants";
import { traceOpen } from "Shared/Telemetry/TelemetryProcessor";
import { ReactTabKind, useTabs } from "hooks/useTabs";
import React from "react";
import AddCollectionIcon from "../../images/AddCollection.svg";
@@ -146,7 +148,10 @@ export const createSampleCollectionContextMenuButton = (): TreeNodeMenuItem[] =>
if (userContext.apiType === "SQL") {
items.push({
iconSrc: AddSqlQueryIcon,
onClick: () => useTabs.getState().openAndActivateReactTab(ReactTabKind.QueryCopilot),
onClick: () => {
useTabs.getState().openAndActivateReactTab(ReactTabKind.QueryCopilot);
traceOpen(Action.OpenQueryCopilotFromNewQuery, { apiType: userContext.apiType });
},
label: "New SQL Query",
});
}

View File

@@ -1,5 +1,8 @@
import { Link } from "@fluentui/react/lib/Link";
import { isPublicInternetAccessAllowed } from "Common/DatabaseAccountUtility";
import { sendMessage } from "Common/MessageHandler";
import { Platform } from "ConfigContext";
import { MessageTypes } from "Contracts/ExplorerContracts";
import { IGalleryItem } from "Juno/JunoClient";
import { allowedNotebookServerUrls, validateEndpoint } from "Utils/EndpointValidation";
import * as ko from "knockout";
@@ -23,7 +26,7 @@ import { PhoenixClient } from "../Phoenix/PhoenixClient";
import * as ExplorerSettings from "../Shared/ExplorerSettings";
import { Action, ActionModifiers } from "../Shared/Telemetry/TelemetryConstants";
import * as TelemetryProcessor from "../Shared/Telemetry/TelemetryProcessor";
import { userContext } from "../UserContext";
import { isAccountNewerThanThresholdInMs, userContext } from "../UserContext";
import { getCollectionName, getUploadName } from "../Utils/APITypeUtils";
import { stringToBlob } from "../Utils/BlobUtils";
import { isCapabilityEnabled } from "../Utils/CapabilityUtils";
@@ -258,6 +261,45 @@ export default class Explorer {
// TODO: return result
}
private getRandomInt(max: number) {
return Math.floor(Math.random() * max);
}
public openNPSSurveyDialog(): void {
if (!Platform.Portal) {
return;
}
const NINETY_DAYS_IN_MS = 7776000000;
const ONE_DAY_IN_MS = 86400000;
const isAccountNewerThanNinetyDays = isAccountNewerThanThresholdInMs(
userContext.databaseAccount?.systemData?.createdAt || "",
NINETY_DAYS_IN_MS
);
// Try Cosmos DB subscription - survey shown to random 25% of users at day 1 in Data Explorer.
if (userContext.isTryCosmosDBSubscription) {
if (
isAccountNewerThanThresholdInMs(userContext.databaseAccount?.systemData?.createdAt || "", ONE_DAY_IN_MS) &&
this.getRandomInt(100) < 25
) {
sendMessage(MessageTypes.DisplayNPSSurvey);
}
} else {
// An existing account is lesser than 90 days old. For existing account show to random 10 % of users in Data Explorer.
if (isAccountNewerThanNinetyDays) {
if (this.getRandomInt(100) < 10) {
sendMessage(MessageTypes.DisplayNPSSurvey);
}
} else {
// An existing account is greater than 90 days. For existing account show to random 25 % of users in Data Explorer.
if (this.getRandomInt(100) < 25) {
sendMessage(MessageTypes.DisplayNPSSurvey);
}
}
}
}
public async refreshDatabaseForResourceToken(): Promise<void> {
const databaseId = userContext.parsedResourceToken?.databaseId;
const collectionId = userContext.parsedResourceToken?.collectionId;
@@ -353,7 +395,7 @@ export default class Explorer {
) {
const provisionData: IProvisionData = {
cosmosEndpoint: userContext?.databaseAccount?.properties?.documentEndpoint,
poolId: PoolIdType.DefaultPoolId,
poolId: PoolIdType.QueryCopilot,
};
const connectionStatus: ContainerConnectionInfo = {
status: ConnectionStatusType.Connecting,
@@ -1293,5 +1335,7 @@ export default class Explorer {
const sampleDataResourceTokenCollection = new ResourceTokenCollection(this, databaseId, collection, true);
useDatabases.setState({ sampleDataResourceTokenCollection });
await this.allocateContainer();
}
}

View File

@@ -1,3 +1,5 @@
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";
@@ -326,6 +328,7 @@ function createNewSQLQueryButton(selectedNodeState: SelectedNodeState): CommandB
onCommandClick: () => {
if (useSelectedNode.getState().isQueryCopilotCollectionSelected()) {
useTabs.getState().openAndActivateReactTab(ReactTabKind.QueryCopilot);
traceOpen(Action.OpenQueryCopilotFromNewQuery, { apiType: userContext.apiType });
} else {
const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection();
selectedCollection && selectedCollection.onNewQueryClick(selectedCollection);

View File

@@ -1425,6 +1425,10 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
this.setState({ isExecuting: false });
TelemetryProcessor.traceSuccess(Action.CreateCollection, telemetryData, startKey);
useSidePanel.getState().closeSidePanel();
// open NPS Survey Dialog once the collection is created
if (userContext.features.enableNPSSurvey) {
this.props.explorer.openNPSSurveyDialog();
}
} catch (error) {
const errorMessage: string = getErrorMessage(error);
this.setState({ isExecuting: false, errorMessage, showErrorDetails: true });

View File

@@ -112,6 +112,9 @@
margin-top: 28px;
margin-left: 4px !important;
}
.addRemoveIcon [alt="editEntity"]:focus,.addRemoveIconLabel [alt="editEntity"]:focus{
border:1px dashed #605E5C
}
.addNewParamStyle {
margin-top: 5px;
margin-left: 5px !important;

View File

@@ -80,7 +80,7 @@ export const QueryCopilotFeedbackModal: React.FC = (): JSX.Element => {
<Text style={{ fontSize: 12, marginBottom: 14 }}>
By pressing submit, your feedback will be used to improve Microsoft products and services. IT admins for your
organization will be able to view and manage your feedback data.{" "}
<Link href="" target="_blank">
<Link href="https://privacy.microsoft.com/privacystatement" target="_blank">
Privacy statement
</Link>
</Text>

View File

@@ -60,7 +60,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 href="">Learn more</Link>
<Link href="http://aka.ms/cdb-copilot-learn-more">Learn more</Link>
</Text>
</Stack.Item>
<Stack.Item align="center" className="text">
@@ -78,7 +78,7 @@ export const WelcomeModal = ({ visible }: { visible: boolean }): JSX.Element =>
<Text>
AI-generated content can have mistakes. Make sure its accurate and appropriate before using it.
<br />
<Link href="">Read preview terms</Link>
<Link href="http://aka.ms/cdb-copilot-preview-terms">Read preview terms</Link>
</Text>
</Stack.Item>
<Stack.Item align="center" className="text">
@@ -96,7 +96,7 @@ export const WelcomeModal = ({ visible }: { visible: boolean }): JSX.Element =>
<Text>
Copilot is setup on a sample database we have configured for you at no cost
<br />
<Link href="">Learn more</Link>
<Link href="http://aka.ms/cdb-copilot-learn-more">Learn more</Link>
</Text>
</Stack.Item>
</Stack>

View File

@@ -121,7 +121,7 @@ exports[`Query Copilot Feedback Modal snapshot test shoud render and match snaps
By pressing submit, your feedback will be used to improve Microsoft products and services. IT admins for your organization will be able to view and manage your feedback data.
<StyledLinkBase
href=""
href="https://privacy.microsoft.com/privacystatement"
target="_blank"
>
Privacy statement
@@ -274,7 +274,7 @@ exports[`Query Copilot Feedback Modal snapshot test should cancel submission 1`]
By pressing submit, your feedback will be used to improve Microsoft products and services. IT admins for your organization will be able to view and manage your feedback data.
<StyledLinkBase
href=""
href="https://privacy.microsoft.com/privacystatement"
target="_blank"
>
Privacy statement
@@ -427,7 +427,7 @@ exports[`Query Copilot Feedback Modal snapshot test should close on cancel click
By pressing submit, your feedback will be used to improve Microsoft products and services. IT admins for your organization will be able to view and manage your feedback data.
<StyledLinkBase
href=""
href="https://privacy.microsoft.com/privacystatement"
target="_blank"
>
Privacy statement
@@ -580,7 +580,7 @@ exports[`Query Copilot Feedback Modal snapshot test should get user unput 1`] =
By pressing submit, your feedback will be used to improve Microsoft products and services. IT admins for your organization will be able to view and manage your feedback data.
<StyledLinkBase
href=""
href="https://privacy.microsoft.com/privacystatement"
target="_blank"
>
Privacy statement
@@ -733,7 +733,7 @@ exports[`Query Copilot Feedback Modal snapshot test should not render dont show
By pressing submit, your feedback will be used to improve Microsoft products and services. IT admins for your organization will be able to view and manage your feedback data.
<StyledLinkBase
href=""
href="https://privacy.microsoft.com/privacystatement"
target="_blank"
>
Privacy statement
@@ -886,7 +886,7 @@ exports[`Query Copilot Feedback Modal snapshot test should record user contact c
By pressing submit, your feedback will be used to improve Microsoft products and services. IT admins for your organization will be able to view and manage your feedback data.
<StyledLinkBase
href=""
href="https://privacy.microsoft.com/privacystatement"
target="_blank"
>
Privacy statement
@@ -1039,7 +1039,7 @@ exports[`Query Copilot Feedback Modal snapshot test should record user contact c
By pressing submit, your feedback will be used to improve Microsoft products and services. IT admins for your organization will be able to view and manage your feedback data.
<StyledLinkBase
href=""
href="https://privacy.microsoft.com/privacystatement"
target="_blank"
>
Privacy statement
@@ -1192,7 +1192,7 @@ exports[`Query Copilot Feedback Modal snapshot test should render dont show agai
By pressing submit, your feedback will be used to improve Microsoft products and services. IT admins for your organization will be able to view and manage your feedback data.
<StyledLinkBase
href=""
href="https://privacy.microsoft.com/privacystatement"
target="_blank"
>
Privacy statement
@@ -1360,7 +1360,7 @@ exports[`Query Copilot Feedback Modal snapshot test should submit submission 1`]
By pressing submit, your feedback will be used to improve Microsoft products and services. IT admins for your organization will be able to view and manage your feedback data.
<StyledLinkBase
href=""
href="https://privacy.microsoft.com/privacystatement"
target="_blank"
>
Privacy statement

View File

@@ -97,7 +97,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=""
href="http://aka.ms/cdb-copilot-learn-more"
>
Learn more
</StyledLinkBase>
@@ -133,7 +133,7 @@ exports[`Query Copilot Welcome Modal snapshot test should render when isOpen is
AI-generated content can have mistakes. Make sure its accurate and appropriate before using it.
<br />
<StyledLinkBase
href=""
href="http://aka.ms/cdb-copilot-preview-terms"
>
Read preview terms
</StyledLinkBase>
@@ -169,7 +169,7 @@ exports[`Query Copilot Welcome Modal snapshot test should render when isOpen is
Copilot is setup on a sample database we have configured for you at no cost
<br />
<StyledLinkBase
href=""
href="http://aka.ms/cdb-copilot-learn-more"
>
Learn more
</StyledLinkBase>

View File

@@ -5,9 +5,7 @@ import { QueryCopilotTab } from "./QueryCopilotTab";
describe("Query copilot tab snapshot test", () => {
it("should render with initial input", () => {
const wrapper = shallow(
<QueryCopilotTab initialInput="Write a query to return all records in this table" explorer={new Explorer()} />
);
const wrapper = shallow(<QueryCopilotTab explorer={new Explorer()} />);
expect(wrapper).toMatchSnapshot();
});
});

View File

@@ -27,6 +27,7 @@ import { CommandButtonComponentProps } from "Explorer/Controls/CommandButton/Com
import { EditorReact } from "Explorer/Controls/Editor/EditorReact";
import Explorer from "Explorer/Explorer";
import { useCommandBar } from "Explorer/Menus/CommandBar/CommandBarComponentAdapter";
import { useNotebook } from "Explorer/Notebook/useNotebook";
import { SaveQueryPane } from "Explorer/Panes/SaveQueryPane/SaveQueryPane";
import { WelcomeModal } from "Explorer/QueryCopilot/Modal/WelcomeModal";
import { CopyPopup } from "Explorer/QueryCopilot/Popup/CopyPopup";
@@ -34,6 +35,8 @@ import { DeletePopup } from "Explorer/QueryCopilot/Popup/DeletePopup";
import { querySampleDocuments, submitFeedback } from "Explorer/QueryCopilot/QueryCopilotUtilities";
import { SamplePrompts, SamplePromptsProps } from "Explorer/QueryCopilot/SamplePrompts/SamplePrompts";
import { QueryResultSection } from "Explorer/Tabs/QueryTab/QueryResultSection";
import { Action } from "Shared/Telemetry/TelemetryConstants";
import { traceFailure, traceStart, traceSuccess } from "Shared/Telemetry/TelemetryProcessor";
import { userContext } from "UserContext";
import { queryPagesUntilContentPresent } from "Utils/QueryUtils";
import { useQueryCopilot } from "hooks/useQueryCopilot";
@@ -54,7 +57,6 @@ interface SuggestedPrompt {
}
interface QueryCopilotTabProps {
initialInput: string;
explorer: Explorer;
}
@@ -71,31 +73,50 @@ const promptStyles: IButtonStyles = {
label: { fontWeight: 400, textAlign: "left", paddingLeft: 8 },
};
export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({
initialInput,
explorer,
}: QueryCopilotTabProps): JSX.Element => {
const hideFeedbackModalForLikedQueries = useQueryCopilot((state) => state.hideFeedbackModalForLikedQueries);
const [userPrompt, setUserPrompt] = useState<string>(initialInput || "");
const [generatedQuery, setGeneratedQuery] = useState<string>("");
const [query, setQuery] = useState<string>("");
const [selectedQuery, setSelectedQuery] = useState<string>("");
const [isGeneratingQuery, setIsGeneratingQuery] = useState<boolean>(false);
const [isExecuting, setIsExecuting] = useState<boolean>(false);
const [likeQuery, setLikeQuery] = useState<boolean>();
const [dislikeQuery, setDislikeQuery] = useState<boolean>();
const [showCallout, setShowCallout] = useState<boolean>(false);
const [showSamplePrompts, setShowSamplePrompts] = useState<boolean>(false);
const [queryIterator, setQueryIterator] = useState<MinimalQueryIterator>();
const [queryResults, setQueryResults] = useState<QueryResults>();
const [errorMessage, setErrorMessage] = useState<string>("");
export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({ explorer }: QueryCopilotTabProps): JSX.Element => {
const [copilotTeachingBubbleVisible, { toggle: toggleCopilotTeachingBubbleVisible }] = useBoolean(false);
const inputEdited = useRef(false);
const [isSamplePromptsOpen, setIsSamplePromptsOpen] = useState<boolean>(false);
const [showDeletePopup, setShowDeletePopup] = useState<boolean>(false);
const [showFeedbackBar, setShowFeedbackBar] = useState<boolean>(false);
const [showCopyPopup, setshowCopyPopup] = useState<boolean>(false);
const [showErrorMessageBar, setShowErrorMessageBar] = useState<boolean>(false);
const {
hideFeedbackModalForLikedQueries,
userPrompt,
setUserPrompt,
generatedQuery,
setGeneratedQuery,
query,
setQuery,
selectedQuery,
setSelectedQuery,
isGeneratingQuery,
setIsGeneratingQuery,
isExecuting,
setIsExecuting,
likeQuery,
setLikeQuery,
dislikeQuery,
setDislikeQuery,
showCallout,
setShowCallout,
showSamplePrompts,
setShowSamplePrompts,
queryIterator,
setQueryIterator,
queryResults,
setQueryResults,
errorMessage,
setErrorMessage,
isSamplePromptsOpen,
setIsSamplePromptsOpen,
showDeletePopup,
setShowDeletePopup,
showFeedbackBar,
setShowFeedbackBar,
showCopyPopup,
setshowCopyPopup,
showErrorMessageBar,
setShowErrorMessageBar,
generatedQueryComments,
setGeneratedQueryComments,
} = useQueryCopilot();
const sampleProps: SamplePromptsProps = {
isSamplePromptsOpen: isSamplePromptsOpen,
@@ -170,10 +191,13 @@ export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({
userPrompt: userPrompt,
};
setShowDeletePopup(false);
const response = await fetch("https://copilotorchestrater.azurewebsites.net/generateSQLQuery", {
useQueryCopilot.getState().refreshCorrelationId();
const serverInfo = useNotebook.getState().notebookServerInfo;
const response = await fetch(`${serverInfo.notebookServerEndpoint}generateSQLQuery`, {
method: "POST",
headers: {
"content-type": "application/json",
"x-ms-correlationid": useQueryCopilot.getState().correlationId,
},
body: JSON.stringify(payload),
});
@@ -188,6 +212,7 @@ export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({
query += generateSQLQueryResponse.sql;
setQuery(query);
setGeneratedQuery(generateSQLQueryResponse.sql);
setGeneratedQueryComments(generateSQLQueryResponse.explanation);
setShowErrorMessageBar(false);
}
} else {
@@ -208,6 +233,13 @@ export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({
};
const onExecuteQueryClick = async (): Promise<void> => {
traceStart(Action.ExecuteQueryGeneratedFromQueryCopilot, {
correlationId: useQueryCopilot.getState().correlationId,
userPrompt: userPrompt,
generatedQuery: generatedQuery,
generatedQueryComments: generatedQueryComments,
executedQuery: selectedQuery || query,
});
const queryToExecute = selectedQuery || query;
const queryIterator = querySampleDocuments(queryToExecute, {
enableCrossPartitionQuery: shouldEnableCrossPartitionKey(),
@@ -233,8 +265,15 @@ export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({
setQueryResults(queryResults);
setErrorMessage("");
setShowErrorMessageBar(false);
traceSuccess(Action.ExecuteQueryGeneratedFromQueryCopilot, {
correlationId: useQueryCopilot.getState().correlationId,
});
} catch (error) {
const errorMessage = getErrorMessage(error);
traceFailure(Action.ExecuteQueryGeneratedFromQueryCopilot, {
correlationId: useQueryCopilot.getState().correlationId,
errorMessage: errorMessage,
});
setErrorMessage(errorMessage);
handleError(errorMessage, "executeQueryCopilotTab");
useTabs.getState().setIsQueryErrorThrown(true);
@@ -281,9 +320,10 @@ export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({
return [executeQueryBtn, saveQueryBtn];
};
const showTeachingBubble = (): void => {
if (!inputEdited.current) {
const shouldShowTeachingBubble = !inputEdited.current && userPrompt.trim() === "";
if (shouldShowTeachingBubble) {
setTimeout(() => {
if (!inputEdited.current) {
if (shouldShowTeachingBubble) {
toggleCopilotTeachingBubbleVisible();
inputEdited.current = true;
}
@@ -297,21 +337,24 @@ export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({
setShowCallout(false);
};
const startGenerateQueryProcess = () => {
updateHistories();
generateSQLQuery();
resetButtonState();
};
React.useEffect(() => {
useCommandBar.getState().setContextButtons(getCommandbarButtons());
}, [query, selectedQuery]);
React.useEffect(() => {
if (initialInput) {
generateSQLQuery();
}
showTeachingBubble();
useTabs.getState().setIsQueryErrorThrown(false);
}, []);
return (
<Stack className="tab-pane" style={{ padding: 24, width: "100%" }}>
<div style={{ overflowY: "auto", height: "100%" }}>
<div style={isGeneratingQuery ? { height: "100%" } : { overflowY: "auto", height: "100%" }}>
<Stack horizontal verticalAlign="center">
<Image src={CopilotIcon} />
<Text style={{ marginLeft: 8, fontWeight: 600, fontSize: 16 }}>Copilot</Text>
@@ -325,6 +368,11 @@ export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({
inputEdited.current = true;
setShowSamplePrompts(true);
}}
onKeyDown={(e) => {
if (e.key === "Enter") {
startGenerateQueryProcess();
}
}}
style={{ lineHeight: 30 }}
styles={{ root: { width: "95%" } }}
disabled={isGeneratingQuery}
@@ -357,11 +405,7 @@ export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({
iconProps={{ iconName: "Send" }}
disabled={isGeneratingQuery || !userPrompt.trim()}
style={{ marginLeft: 8 }}
onClick={() => {
updateHistories();
generateSQLQuery();
resetButtonState();
}}
onClick={() => startGenerateQueryProcess()}
/>
{isGeneratingQuery && <Spinner style={{ marginLeft: 8 }} />}
{showSamplePrompts && (
@@ -453,7 +497,7 @@ export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({
}}
>
Learn about{" "}
<Link target="_blank" href="">
<Link target="_blank" href="http://aka.ms/cdb-copilot-writing">
writing effective prompts
</Link>
</Text>
@@ -467,7 +511,7 @@ export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({
<Stack style={{ marginTop: 8, marginBottom: 24 }}>
<Text style={{ fontSize: 12 }}>
AI-generated content can have mistakes. Make sure it&apos;s accurate and appropriate before using it.{" "}
<Link href="" target="_blank">
<Link href="http://aka.ms/cdb-copilot-preview-terms" target="_blank">
Read preview terms
</Link>
{showErrorMessageBar && (
@@ -490,7 +534,12 @@ export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({
target="#likeBtn"
onDismiss={() => {
setShowCallout(false);
submitFeedback({ generatedQuery, likeQuery, description: "", userPrompt: userPrompt });
submitFeedback({
generatedQuery: generatedQuery,
likeQuery: likeQuery,
description: "",
userPrompt: userPrompt,
});
}}
directionalHint={DirectionalHint.topCenter}
>

View File

@@ -4,6 +4,7 @@ import { handleError } from "Common/ErrorHandlingUtils";
import { sampleDataClient } from "Common/SampleDataClient";
import * as commonUtils from "Common/dataAccess/queryDocuments";
import DocumentId from "Explorer/Tree/DocumentId";
import { useQueryCopilot } from "hooks/useQueryCopilot";
import { querySampleDocuments, readSampleDocument, submitFeedback } from "./QueryCopilotUtilities";
jest.mock("Explorer/Tree/DocumentId", () => {
return jest.fn().mockImplementation(() => {
@@ -56,6 +57,7 @@ describe("QueryCopilotUtilities", () => {
const mockFetch = jest.fn().mockResolvedValueOnce({});
globalThis.fetch = mockFetch;
useQueryCopilot.getState().refreshCorrelationId();
await submitFeedback({
likeQuery: true,
@@ -71,6 +73,7 @@ describe("QueryCopilotUtilities", () => {
method: "POST",
headers: {
"content-type": "application/json",
"x-ms-correlationid": useQueryCopilot.getState().correlationId,
},
})
);
@@ -86,6 +89,7 @@ describe("QueryCopilotUtilities", () => {
const mockFetch = jest.fn().mockResolvedValueOnce({});
globalThis.fetch = mockFetch;
useQueryCopilot.getState().refreshCorrelationId();
await submitFeedback({
likeQuery: false,
@@ -101,6 +105,7 @@ describe("QueryCopilotUtilities", () => {
method: "POST",
headers: {
"content-type": "application/json",
"x-ms-correlationid": useQueryCopilot.getState().correlationId,
},
})
);

View File

@@ -10,6 +10,7 @@ import { getPartitionKeyValue } from "Common/dataAccess/getPartitionKeyValue";
import { getCommonQueryOptions } from "Common/dataAccess/queryDocuments";
import DocumentId from "Explorer/Tree/DocumentId";
import { logConsoleProgress } from "Utils/NotificationConsoleUtils";
import { useQueryCopilot } from "hooks/useQueryCopilot";
interface FeedbackParams {
likeQuery: boolean;
@@ -35,6 +36,7 @@ export const submitFeedback = async (params: FeedbackParams): Promise<void> => {
method: "POST",
headers: {
"content-type": "application/json",
"x-ms-correlationid": useQueryCopilot.getState().correlationId,
},
body: JSON.stringify(payload),
});

View File

@@ -54,6 +54,7 @@ exports[`Query copilot tab snapshot test should render with initial input 1`] =
id="naturalLanguageInput"
onChange={[Function]}
onClick={[Function]}
onKeyDown={[Function]}
style={
Object {
"lineHeight": 30,
@@ -66,10 +67,10 @@ exports[`Query copilot tab snapshot test should render with initial input 1`] =
},
}
}
value="Write a query to return all records in this table"
value=""
/>
<CustomizedIconButton
disabled={false}
disabled={true}
iconProps={
Object {
"iconName": "Send",
@@ -101,7 +102,7 @@ exports[`Query copilot tab snapshot test should render with initial input 1`] =
AI-generated content can have mistakes. Make sure it's accurate and appropriate before using it.
<StyledLinkBase
href=""
href="http://aka.ms/cdb-copilot-preview-terms"
target="_blank"
>
Read preview terms

View File

@@ -97,6 +97,12 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
() => this.setState({}),
(state) => state.showResetPasswordBubble
),
},
{
dispose: useDatabases.subscribe(
() => this.setState({}),
(state) => state.sampleDataResourceTokenCollection
),
}
);
}
@@ -107,7 +113,11 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
};
private getSplashScreenButtons = (): JSX.Element => {
if (userContext.features.enableCopilot && userContext.apiType === "SQL") {
if (
useDatabases.getState().sampleDataResourceTokenCollection &&
userContext.features.enableCopilot &&
userContext.apiType === "SQL"
) {
return (
<Stack style={{ width: "66%", cursor: "pointer", margin: "40px auto" }} tokens={{ childrenGap: 16 }}>
<Stack horizontal tokens={{ childrenGap: 16 }}>
@@ -137,7 +147,10 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
description={
"Copilot is your AI buddy that helps you write Azure Cosmos DB queries like a pro. Try it using our sample data set now!"
}
onClick={() => useTabs.getState().openAndActivateReactTab(ReactTabKind.QueryCopilot)}
onClick={() => {
useTabs.getState().openAndActivateReactTab(ReactTabKind.QueryCopilot);
traceOpen(Action.OpenQueryCopilotFromSplashScreen, { apiType: userContext.apiType });
}}
/>
<SplashScreenButton
imgSrc={ConnectIcon}
@@ -246,8 +259,9 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
<form className="connectExplorerFormContainer">
<div className="splashScreenContainer">
<div className="splashScreen">
<div
<h1
className="title"
role="heading"
aria-label={
userContext.apiType === "Postgres"
? "Welcome to Azure Cosmos DB for PostgreSQL"
@@ -258,7 +272,7 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
? "Welcome to Azure Cosmos DB for PostgreSQL"
: "Welcome to Azure Cosmos DB"}
<FeaturePanelLauncher />
</div>
</h1>
<div className="subtitle">
{userContext.apiType === "Postgres"
? "Get started with our sample datasets, documentation, and additional tools."

View File

@@ -9,6 +9,7 @@ import { ConnectTab } from "Explorer/Tabs/ConnectTab";
import { PostgresConnectTab } from "Explorer/Tabs/PostgresConnectTab";
import { QuickstartTab } from "Explorer/Tabs/QuickstartTab";
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";
@@ -158,6 +159,7 @@ const CloseButton = ({
onClick={(event: React.MouseEvent<HTMLSpanElement, MouseEvent>) => {
event.stopPropagation();
tab ? tab.onCloseTabButtonClick() : useTabs.getState().closeReactTab(tabKind);
tabKind === ReactTabKind.QueryCopilot && useQueryCopilot.getState().resetQueryCopilotStates();
}}
tabIndex={active ? 0 : undefined}
onKeyPress={({ nativeEvent: e }) => tab.onKeyPressClose(undefined, e)}
@@ -251,7 +253,7 @@ const getReactTabContent = (activeReactTab: ReactTabKind, explorer: Explorer): J
case ReactTabKind.Quickstart:
return <QuickstartTab explorer={explorer} />;
case ReactTabKind.QueryCopilot:
return <QueryCopilotTab initialInput={useTabs.getState().queryCopilotTabInitialInput} explorer={explorer} />;
return <QueryCopilotTab explorer={explorer} />;
default:
throw Error(`Unsupported tab kind ${ReactTabKind[activeReactTab]}`);
}

View File

@@ -17,7 +17,7 @@ export const SampleDataTree = ({
const buildSampleDataTree = (): TreeNode => {
const updatedSampleTree: TreeNode = {
label: sampleDataResourceTokenCollection.databaseId,
isExpanded: true,
isExpanded: false,
iconSrc: CosmosDBIcon,
className: "databaseHeader",
children: [
@@ -47,6 +47,7 @@ export const SampleDataTree = ({
{
label: "Items",
onClick: () => sampleDataResourceTokenCollection.onDocumentDBDocumentsClick(),
contextMenu: ResourceTreeContextMenuButtonFactory.createSampleCollectionContextMenuButton(),
isSelected: () =>
useSelectedNode
.getState()

View File

@@ -1,18 +1,18 @@
import { useDialog } from "Explorer/Controls/Dialog";
import promiseRetry, { AbortError } from "p-retry";
import { Action } from "Shared/Telemetry/TelemetryConstants";
import { allowedJunoOrigins, validateEndpoint } from "Utils/EndpointValidation";
import promiseRetry, { AbortError } from "p-retry";
import {
Areas,
ConnectionStatusType,
ContainerStatusType,
HttpHeaders,
HttpStatusCodes,
JunoEndpoints,
Notebook,
} from "../Common/Constants";
import { getErrorMessage, getErrorStack } from "../Common/ErrorHandlingUtils";
import * as Logger from "../Common/Logger";
import { configContext } from "../ConfigContext";
import {
ContainerConnectionInfo,
ContainerInfo,
@@ -28,7 +28,6 @@ import {
} from "../Contracts/DataModels";
import { useNotebook } from "../Explorer/Notebook/useNotebook";
import * as TelemetryProcessor from "../Shared/Telemetry/TelemetryProcessor";
import { userContext } from "../UserContext";
import { getAuthorizationHeader } from "../Utils/AuthorizationUtils";
export class PhoenixClient {
@@ -231,8 +230,8 @@ export class PhoenixClient {
throw new Error("The Phoenix client was not initialized properly: missing ARM resourcce id");
}
const toolsEndpoint =
userContext.features.phoenixEndpoint ?? userContext.features.junoEndpoint ?? configContext.JUNO_ENDPOINT;
const toolsEndpoint = JunoEndpoints.Test2;
// userContext.features.phoenixEndpoint ?? userContext.features.junoEndpoint ?? configContext.JUNO_ENDPOINT;
if (!validateEndpoint(toolsEndpoint, allowedJunoOrigins)) {
const error = `${toolsEndpoint} not allowed as tools endpoint`;

View File

@@ -36,6 +36,7 @@ export type Features = {
readonly enableLegacyMongoShellV2Debug: boolean;
readonly loadLegacyMongoShellFromBE: boolean;
readonly enableCopilot: boolean;
readonly enableNPSSurvey: boolean;
// can be set via both flight and feature flag
autoscaleDefault: boolean;
@@ -104,6 +105,7 @@ export function extractFeatures(given = new URLSearchParams(window.location.sear
enableLegacyMongoShellV2Debug: "true" === get("enablelegacymongoshellv2debug"),
loadLegacyMongoShellFromBE: "true" === get("loadlegacymongoshellfrombe"),
enableCopilot: "true" === get("enablecopilot"),
enableNPSSurvey: "true" === get("enablenpssurvey"),
};
}

View File

@@ -131,6 +131,9 @@ export enum Action {
LaunchUITour,
CancelUITour,
CompleteUITour,
OpenQueryCopilotFromSplashScreen,
OpenQueryCopilotFromNewQuery,
ExecuteQueryGeneratedFromQueryCopilot,
}
export const ActionModifiers = {

View File

@@ -91,7 +91,7 @@ const userContext: UserContext = {
collectionCreationDefaults: CollectionCreationDefaults,
};
function isAccountNewerThanThresholdInMs(createdAt: string, threshold: number) {
export function isAccountNewerThanThresholdInMs(createdAt: string, threshold: number) {
let createdAtMs: number = Date.parse(createdAt);
if (isNaN(createdAtMs)) {
createdAtMs = 0;

View File

@@ -38,7 +38,7 @@ function validateEndpointInternal(
return valid;
}
export const allowedArmEndpoints: ReadonlyArray<string> = [
export const defaultAllowedArmEndpoints: ReadonlyArray<string> = [
"https://management.azure.com",
"https://management.usgovcloudapi.net",
"https://management.chinacloudapi.cn",
@@ -46,7 +46,7 @@ export const allowedArmEndpoints: ReadonlyArray<string> = [
export const allowedAadEndpoints: ReadonlyArray<string> = ["https://login.microsoftonline.com/"];
export const allowedBackendEndpoints: ReadonlyArray<string> = [
export const defaultAllowedBackendEndpoints: ReadonlyArray<string> = [
"https://main.documentdb.ext.azure.com",
"https://main.documentdb.ext.azure.cn",
"https://main.documentdb.ext.azure.us",

View File

@@ -1,3 +1,6 @@
import { MinimalQueryIterator } from "Common/IteratorUtilities";
import { QueryResults } from "Contracts/ViewModels";
import { guid } from "Explorer/Tables/Utilities";
import create, { UseStore } from "zustand";
interface QueryCopilotState {
@@ -6,20 +9,127 @@ interface QueryCopilotState {
userPrompt: string;
showFeedbackModal: boolean;
hideFeedbackModalForLikedQueries: boolean;
correlationId: string;
query: string;
selectedQuery: string;
isGeneratingQuery: boolean;
isExecuting: boolean;
dislikeQuery: boolean | undefined;
showCallout: boolean;
showSamplePrompts: boolean;
queryIterator: MinimalQueryIterator | undefined;
queryResults: QueryResults | undefined;
errorMessage: string;
isSamplePromptsOpen: boolean;
showDeletePopup: boolean;
showFeedbackBar: boolean;
showCopyPopup: boolean;
showErrorMessageBar: boolean;
generatedQueryComments: string;
openFeedbackModal: (generatedQuery: string, likeQuery: boolean, userPrompt: string) => void;
closeFeedbackModal: () => void;
setHideFeedbackModalForLikedQueries: (hideFeedbackModalForLikedQueries: boolean) => void;
refreshCorrelationId: () => void;
setUserPrompt: (userPrompt: string) => void;
setQuery: (query: string) => void;
setGeneratedQuery: (generatedQuery: string) => void;
setSelectedQuery: (selectedQuery: string) => void;
setIsGeneratingQuery: (isGeneratingQuery: boolean) => void;
setIsExecuting: (isExecuting: boolean) => void;
setLikeQuery: (likeQuery: boolean) => void;
setDislikeQuery: (dislikeQuery: boolean | undefined) => void;
setShowCallout: (showCallout: boolean) => void;
setShowSamplePrompts: (showSamplePrompts: boolean) => void;
setQueryIterator: (queryIterator: MinimalQueryIterator | undefined) => void;
setQueryResults: (queryResults: QueryResults | undefined) => void;
setErrorMessage: (errorMessage: string) => void;
setIsSamplePromptsOpen: (isSamplePromptsOpen: boolean) => void;
setShowDeletePopup: (showDeletePopup: boolean) => void;
setShowFeedbackBar: (showFeedbackBar: boolean) => void;
setshowCopyPopup: (showCopyPopup: boolean) => void;
setShowErrorMessageBar: (showErrorMessageBar: boolean) => void;
setGeneratedQueryComments: (generatedQueryComments: string) => void;
resetQueryCopilotStates: () => void;
}
export const useQueryCopilot: UseStore<QueryCopilotState> = create((set) => ({
type QueryCopilotStore = UseStore<QueryCopilotState>;
export const useQueryCopilot: QueryCopilotStore = create((set) => ({
generatedQuery: "",
likeQuery: false,
userPrompt: "",
showFeedbackModal: false,
hideFeedbackModalForLikedQueries: false,
correlationId: "",
query: "",
selectedQuery: "",
isGeneratingQuery: false,
isExecuting: false,
dislikeQuery: undefined,
showCallout: false,
showSamplePrompts: false,
queryIterator: undefined,
queryResults: undefined,
errorMessage: "",
isSamplePromptsOpen: false,
showDeletePopup: false,
showFeedbackBar: false,
showCopyPopup: false,
showErrorMessageBar: false,
generatedQueryComments: "",
openFeedbackModal: (generatedQuery: string, likeQuery: boolean, userPrompt: string) =>
set({ generatedQuery, likeQuery, userPrompt, showFeedbackModal: true }),
closeFeedbackModal: () => set({ generatedQuery: "", likeQuery: false, userPrompt: "", 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 }),
setIsExecuting: (isExecuting: boolean) => set({ isExecuting }),
setLikeQuery: (likeQuery: boolean) => set({ likeQuery }),
setDislikeQuery: (dislikeQuery: boolean | undefined) => set({ dislikeQuery }),
setShowCallout: (showCallout: boolean) => set({ showCallout }),
setShowSamplePrompts: (showSamplePrompts: boolean) => set({ showSamplePrompts }),
setQueryIterator: (queryIterator: MinimalQueryIterator | undefined) => set({ queryIterator }),
setQueryResults: (queryResults: QueryResults | undefined) => set({ queryResults }),
setErrorMessage: (errorMessage: string) => set({ errorMessage }),
setIsSamplePromptsOpen: (isSamplePromptsOpen: boolean) => set({ isSamplePromptsOpen }),
setShowDeletePopup: (showDeletePopup: boolean) => set({ showDeletePopup }),
setShowFeedbackBar: (showFeedbackBar: boolean) => set({ showFeedbackBar }),
setshowCopyPopup: (showCopyPopup: boolean) => set({ showCopyPopup }),
setShowErrorMessageBar: (showErrorMessageBar: boolean) => set({ showErrorMessageBar }),
setGeneratedQueryComments: (generatedQueryComments: string) => set({ generatedQueryComments }),
resetQueryCopilotStates: () => {
set((state) => ({
...state,
generatedQuery: "",
likeQuery: false,
userPrompt: "",
showFeedbackModal: false,
hideFeedbackModalForLikedQueries: false,
correlationId: "",
query: "",
selectedQuery: "",
isGeneratingQuery: false,
isExecuting: false,
dislikeQuery: undefined,
showCallout: false,
showSamplePrompts: false,
queryIterator: undefined,
queryResults: undefined,
errorMessage: "",
isSamplePromptsOpen: false,
showDeletePopup: false,
showFeedbackBar: false,
showCopyPopup: false,
showErrorMessageBar: false,
generatedQueryComments: "",
}));
},
}));