Adjusted Extecute Query logic (#1605)

Co-authored-by: Predrag Klepic <v-prklepic@microsoft.com>
This commit is contained in:
Predrag Klepic 2023-09-11 17:22:30 +02:00 committed by GitHub
parent 76408e2f98
commit 12ed591634
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 124 additions and 105 deletions

View File

@ -1,5 +1,4 @@
/* eslint-disable no-console */ /* eslint-disable no-console */
import { FeedOptions } from "@azure/cosmos";
import { import {
Callout, Callout,
CommandBarButton, CommandBarButton,
@ -20,16 +19,11 @@ import { useBoolean } from "@fluentui/react-hooks";
import { import {
ContainerStatusType, ContainerStatusType,
PoolIdType, PoolIdType,
QueryCopilotSampleContainerId,
QueryCopilotSampleContainerSchema, QueryCopilotSampleContainerSchema,
ShortenedQueryCopilotSampleContainerSchema, ShortenedQueryCopilotSampleContainerSchema,
} from "Common/Constants"; } from "Common/Constants";
import { getErrorMessage, handleError } from "Common/ErrorHandlingUtils"; import { handleError } from "Common/ErrorHandlingUtils";
import { shouldEnableCrossPartitionKey } from "Common/HeadersUtility";
import { MinimalQueryIterator } from "Common/IteratorUtilities";
import { createUri } from "Common/UrlUtility"; import { createUri } from "Common/UrlUtility";
import { queryDocumentsPage } from "Common/dataAccess/queryDocumentsPage";
import { QueryResults } from "Contracts/ViewModels";
import { CommandButtonComponentProps } from "Explorer/Controls/CommandButton/CommandButtonComponent"; import { CommandButtonComponentProps } from "Explorer/Controls/CommandButton/CommandButtonComponent";
import { EditorReact } from "Explorer/Controls/Editor/EditorReact"; import { EditorReact } from "Explorer/Controls/Editor/EditorReact";
import { useCommandBar } from "Explorer/Menus/CommandBar/CommandBarComponentAdapter"; import { useCommandBar } from "Explorer/Menus/CommandBar/CommandBarComponentAdapter";
@ -37,15 +31,11 @@ import { SaveQueryPane } from "Explorer/Panes/SaveQueryPane/SaveQueryPane";
import { WelcomeModal } from "Explorer/QueryCopilot/Modal/WelcomeModal"; import { WelcomeModal } from "Explorer/QueryCopilot/Modal/WelcomeModal";
import { CopyPopup } from "Explorer/QueryCopilot/Popup/CopyPopup"; import { CopyPopup } from "Explorer/QueryCopilot/Popup/CopyPopup";
import { DeletePopup } from "Explorer/QueryCopilot/Popup/DeletePopup"; import { DeletePopup } from "Explorer/QueryCopilot/Popup/DeletePopup";
import { querySampleDocuments } from "Explorer/QueryCopilot/QueryCopilotUtilities"; import { OnExecuteQueryClick, SubmitFeedback } from "Explorer/QueryCopilot/Shared/QueryCopilotClient";
import { SubmitFeedback } from "Explorer/QueryCopilot/Shared/QueryCopilotClient";
import { GenerateSQLQueryResponse, QueryCopilotProps } from "Explorer/QueryCopilot/Shared/QueryCopilotInterfaces"; import { GenerateSQLQueryResponse, QueryCopilotProps } from "Explorer/QueryCopilot/Shared/QueryCopilotInterfaces";
import { QueryCopilotResults } from "Explorer/QueryCopilot/Shared/QueryCopilotResults";
import { SamplePrompts, SamplePromptsProps } from "Explorer/QueryCopilot/Shared/SamplePrompts/SamplePrompts"; import { SamplePrompts, SamplePromptsProps } from "Explorer/QueryCopilot/Shared/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 { userContext } from "UserContext";
import { queryPagesUntilContentPresent } from "Utils/QueryUtils";
import { useQueryCopilot } from "hooks/useQueryCopilot"; import { useQueryCopilot } from "hooks/useQueryCopilot";
import { useSidePanel } from "hooks/useSidePanel"; import { useSidePanel } from "hooks/useSidePanel";
import React, { useRef, useState } from "react"; import React, { useRef, useState } from "react";
@ -83,8 +73,6 @@ export const QueryCopilotTab: React.FC<QueryCopilotProps> = ({ explorer }: Query
setSelectedQuery, setSelectedQuery,
isGeneratingQuery, isGeneratingQuery,
setIsGeneratingQuery, setIsGeneratingQuery,
isExecuting,
setIsExecuting,
likeQuery, likeQuery,
setLikeQuery, setLikeQuery,
dislikeQuery, dislikeQuery,
@ -93,12 +81,6 @@ export const QueryCopilotTab: React.FC<QueryCopilotProps> = ({ explorer }: Query
setShowCallout, setShowCallout,
showSamplePrompts, showSamplePrompts,
setShowSamplePrompts, setShowSamplePrompts,
queryIterator,
setQueryIterator,
queryResults,
setQueryResults,
errorMessage,
setErrorMessage,
isSamplePromptsOpen, isSamplePromptsOpen,
setIsSamplePromptsOpen, setIsSamplePromptsOpen,
showDeletePopup, showDeletePopup,
@ -109,7 +91,6 @@ export const QueryCopilotTab: React.FC<QueryCopilotProps> = ({ explorer }: Query
setshowCopyPopup, setshowCopyPopup,
showErrorMessageBar, showErrorMessageBar,
setShowErrorMessageBar, setShowErrorMessageBar,
generatedQueryComments,
setGeneratedQueryComments, setGeneratedQueryComments,
} = useQueryCopilot(); } = useQueryCopilot();
@ -238,64 +219,12 @@ export const QueryCopilotTab: React.FC<QueryCopilotProps> = ({ explorer }: Query
} }
}; };
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(),
} as FeedOptions);
setQueryIterator(queryIterator);
setTimeout(async () => {
await queryDocumentsPerPage(0, queryIterator);
}, 100);
};
const queryDocumentsPerPage = async (firstItemIndex: number, queryIterator: MinimalQueryIterator): Promise<void> => {
try {
setIsExecuting(true);
useTabs.getState().setIsTabExecuting(true);
useTabs.getState().setIsQueryErrorThrown(false);
const queryResults: QueryResults = await queryPagesUntilContentPresent(
firstItemIndex,
async (firstItemIndex: number) =>
queryDocumentsPage(QueryCopilotSampleContainerId, queryIterator, firstItemIndex)
);
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);
setShowErrorMessageBar(true);
} finally {
setIsExecuting(false);
useTabs.getState().setIsTabExecuting(false);
}
};
const getCommandbarButtons = (): CommandButtonComponentProps[] => { const getCommandbarButtons = (): CommandButtonComponentProps[] => {
const executeQueryBtnLabel = selectedQuery ? "Execute Selection" : "Execute Query"; const executeQueryBtnLabel = selectedQuery ? "Execute Selection" : "Execute Query";
const executeQueryBtn = { const executeQueryBtn = {
iconSrc: ExecuteQueryIcon, iconSrc: ExecuteQueryIcon,
iconAlt: executeQueryBtnLabel, iconAlt: executeQueryBtnLabel,
onCommandClick: () => onExecuteQueryClick(), onCommandClick: () => OnExecuteQueryClick(),
commandButtonLabel: executeQueryBtnLabel, commandButtonLabel: executeQueryBtnLabel,
ariaLabel: executeQueryBtnLabel, ariaLabel: executeQueryBtnLabel,
hasPopup: false, hasPopup: false,
@ -622,16 +551,7 @@ export const QueryCopilotTab: React.FC<QueryCopilotProps> = ({ explorer }: Query
onContentChanged={(newQuery: string) => setQuery(newQuery)} onContentChanged={(newQuery: string) => setQuery(newQuery)}
onContentSelected={(selectedQuery: string) => setSelectedQuery(selectedQuery)} onContentSelected={(selectedQuery: string) => setSelectedQuery(selectedQuery)}
/> />
<QueryResultSection <QueryCopilotResults />
isMongoDB={false}
queryEditorContent={selectedQuery || query}
error={errorMessage}
queryResults={queryResults}
isExecuting={isExecuting}
executeQueryDocumentsPage={(firstItemIndex: number) =>
queryDocumentsPerPage(firstItemIndex, queryIterator)
}
/>
</SplitterLayout> </SplitterLayout>
</Stack> </Stack>
<WelcomeModal visible={localStorage.getItem("hideWelcomeModal") !== "true"} /> <WelcomeModal visible={localStorage.getItem("hideWelcomeModal") !== "true"} />

View File

@ -1,14 +1,24 @@
import { FeedOptions } from "@azure/cosmos";
import { import {
ContainerStatusType, ContainerStatusType,
PoolIdType, PoolIdType,
QueryCopilotSampleContainerId,
QueryCopilotSampleContainerSchema, QueryCopilotSampleContainerSchema,
ShortenedQueryCopilotSampleContainerSchema, ShortenedQueryCopilotSampleContainerSchema,
} from "Common/Constants"; } from "Common/Constants";
import { 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 { createUri } from "Common/UrlUtility";
import { queryDocumentsPage } from "Common/dataAccess/queryDocumentsPage";
import { QueryResults } from "Contracts/ViewModels";
import Explorer from "Explorer/Explorer"; import Explorer from "Explorer/Explorer";
import { querySampleDocuments } from "Explorer/QueryCopilot/QueryCopilotUtilities";
import { FeedbackParams, GenerateSQLQueryResponse } from "Explorer/QueryCopilot/Shared/QueryCopilotInterfaces"; 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 { userContext } from "UserContext";
import { queryPagesUntilContentPresent } from "Utils/QueryUtils";
import { useQueryCopilot } from "hooks/useQueryCopilot"; import { useQueryCopilot } from "hooks/useQueryCopilot";
import { useTabs } from "hooks/useTabs"; import { useTabs } from "hooks/useTabs";
@ -133,3 +143,57 @@ export const SubmitFeedback = async ({
handleError(error, "copilotSubmitFeedback"); handleError(error, "copilotSubmitFeedback");
} }
}; };
export const OnExecuteQueryClick = async (): Promise<void> => {
traceStart(Action.ExecuteQueryGeneratedFromQueryCopilot, {
correlationId: useQueryCopilot.getState().correlationId,
userPrompt: useQueryCopilot.getState().userPrompt,
generatedQuery: useQueryCopilot.getState().generatedQuery,
generatedQueryComments: useQueryCopilot.getState().generatedQueryComments,
executedQuery: useQueryCopilot.getState().selectedQuery || useQueryCopilot.getState().query,
});
const queryToExecute = useQueryCopilot.getState().selectedQuery || useQueryCopilot.getState().query;
const queryIterator = querySampleDocuments(queryToExecute, {
enableCrossPartitionQuery: shouldEnableCrossPartitionKey(),
} as FeedOptions);
useQueryCopilot.getState().setQueryIterator(queryIterator);
setTimeout(async () => {
await QueryDocumentsPerPage(0, queryIterator);
}, 100);
};
export const QueryDocumentsPerPage = async (
firstItemIndex: number,
queryIterator: MinimalQueryIterator
): Promise<void> => {
try {
useQueryCopilot.getState().setIsExecuting(true);
useTabs.getState().setIsTabExecuting(true);
useTabs.getState().setIsQueryErrorThrown(false);
const queryResults: QueryResults = await queryPagesUntilContentPresent(
firstItemIndex,
async (firstItemIndex: number) => queryDocumentsPage(QueryCopilotSampleContainerId, queryIterator, firstItemIndex)
);
useQueryCopilot.getState().setQueryResults(queryResults);
useQueryCopilot.getState().setErrorMessage("");
useQueryCopilot.getState().setShowErrorMessageBar(false);
traceSuccess(Action.ExecuteQueryGeneratedFromQueryCopilot, {
correlationId: useQueryCopilot.getState().correlationId,
});
} catch (error) {
const errorMessage = getErrorMessage(error);
traceFailure(Action.ExecuteQueryGeneratedFromQueryCopilot, {
correlationId: useQueryCopilot.getState().correlationId,
errorMessage: errorMessage,
});
useQueryCopilot.getState().setErrorMessage(errorMessage);
handleError(errorMessage, "executeQueryCopilotTab");
useTabs.getState().setIsQueryErrorThrown(true);
useQueryCopilot.getState().setShowErrorMessageBar(true);
} finally {
useQueryCopilot.getState().setIsExecuting(false);
useTabs.getState().setIsTabExecuting(false);
}
};

View File

@ -0,0 +1,19 @@
import { QueryDocumentsPerPage } from "Explorer/QueryCopilot/Shared/QueryCopilotClient";
import { QueryResultSection } from "Explorer/Tabs/QueryTab/QueryResultSection";
import { useQueryCopilot } from "hooks/useQueryCopilot";
import React from "react";
export const QueryCopilotResults: React.FC = (): JSX.Element => {
return (
<QueryResultSection
isMongoDB={false}
queryEditorContent={useQueryCopilot.getState().selectedQuery || useQueryCopilot.getState().query}
error={useQueryCopilot.getState().errorMessage}
queryResults={useQueryCopilot.getState().queryResults}
isExecuting={useQueryCopilot.getState().isExecuting}
executeQueryDocumentsPage={(firstItemIndex: number) =>
QueryDocumentsPerPage(firstItemIndex, useQueryCopilot.getState().queryIterator)
}
/>
);
};

View File

@ -125,25 +125,26 @@ exports[`Query copilot tab snapshot test should render with initial input 1`] =
> >
<EditorReact <EditorReact
ariaLabel="Editing Query" ariaLabel="Editing Query"
content="" content="SELECT * FROM c"
isReadOnly={false} isReadOnly={false}
language="sql" language="sql"
lineNumbers="on" lineNumbers="on"
onContentChanged={[Function]} onContentChanged={[Function]}
onContentSelected={[Function]} onContentSelected={[Function]}
/> />
<QueryResultSection <QueryCopilotResults />
error=""
executeQueryDocumentsPage={[Function]}
isExecuting={false}
isMongoDB={false}
queryEditorContent=""
/>
</t> </t>
</Stack> </Stack>
<WelcomeModal <WelcomeModal
visible={true} visible={true}
/> />
<DeletePopup
clearFeedback={[Function]}
setQuery={[Function]}
setShowDeletePopup={[Function]}
showDeletePopup={false}
showFeedbackBar={[Function]}
/>
<CopyPopup <CopyPopup
setShowCopyPopup={[Function]} setShowCopyPopup={[Function]}
showCopyPopup={false} showCopyPopup={false}

View File

@ -1,4 +1,6 @@
import { FeedOptions } from "@azure/cosmos"; import { FeedOptions } from "@azure/cosmos";
import { OnExecuteQueryClick } from "Explorer/QueryCopilot/Shared/QueryCopilotClient";
import { QueryCopilotResults } from "Explorer/QueryCopilot/Shared/QueryCopilotResults";
import { QueryCopilotSidebar } from "Explorer/QueryCopilot/V2/Sidebar/QueryCopilotSidebar"; import { QueryCopilotSidebar } from "Explorer/QueryCopilot/V2/Sidebar/QueryCopilotSidebar";
import { QueryResultSection } from "Explorer/Tabs/QueryTab/QueryResultSection"; import { QueryResultSection } from "Explorer/Tabs/QueryTab/QueryResultSection";
import { QueryCopilotState, useQueryCopilot } from "hooks/useQueryCopilot"; import { QueryCopilotState, useQueryCopilot } from "hooks/useQueryCopilot";
@ -276,7 +278,7 @@ export default class QueryTabComponent extends React.Component<IQueryTabComponen
buttons.push({ buttons.push({
iconSrc: ExecuteQueryIcon, iconSrc: ExecuteQueryIcon,
iconAlt: label, iconAlt: label,
onCommandClick: this.onExecuteQueryClick, onCommandClick: this.isCopilotTabActive ? () => OnExecuteQueryClick() : this.onExecuteQueryClick,
commandButtonLabel: label, commandButtonLabel: label,
ariaLabel: label, ariaLabel: label,
hasPopup: false, hasPopup: false,
@ -365,11 +367,18 @@ export default class QueryTabComponent extends React.Component<IQueryTabComponen
selectedContent: "", selectedContent: "",
}); });
} }
if (this.isCopilotTabActive) {
selectedContent.trim().length > 0
? useQueryCopilot.getState().setSelectedQuery(selectedContent)
: useQueryCopilot.getState().setSelectedQuery("");
}
useCommandBar.getState().setContextButtons(this.getTabsButtons()); useCommandBar.getState().setContextButtons(this.getTabsButtons());
} }
public setEditorContent(): string { public setEditorContent(): string {
if (this.state.queryCopilotGeneratedQuery) { if (this.isCopilotTabActive && this.state.queryCopilotGeneratedQuery) {
return this.state.queryCopilotGeneratedQuery; return this.state.queryCopilotGeneratedQuery;
} }
@ -416,14 +425,20 @@ export default class QueryTabComponent extends React.Component<IQueryTabComponen
/> />
</div> </div>
</Fragment> </Fragment>
<QueryResultSection {this.isCopilotTabActive ? (
isMongoDB={this.props.isPreferredApiMongoDB} <QueryCopilotResults />
queryEditorContent={this.state.sqlQueryEditorContent} ) : (
error={this.state.error} <QueryResultSection
queryResults={this.state.queryResults} isMongoDB={this.props.isPreferredApiMongoDB}
isExecuting={this.state.isExecuting} queryEditorContent={this.state.sqlQueryEditorContent}
executeQueryDocumentsPage={(firstItemIndex: number) => this._executeQueryDocumentsPage(firstItemIndex)} error={this.state.error}
/> queryResults={this.state.queryResults}
isExecuting={this.state.isExecuting}
executeQueryDocumentsPage={(firstItemIndex: number) =>
this._executeQueryDocumentsPage(firstItemIndex)
}
/>
)}
</SplitterLayout> </SplitterLayout>
</div> </div>
</div> </div>

View File

@ -88,7 +88,7 @@ export const useQueryCopilot: QueryCopilotStore = create((set) => ({
showFeedbackModal: false, showFeedbackModal: false,
hideFeedbackModalForLikedQueries: false, hideFeedbackModalForLikedQueries: false,
correlationId: "", correlationId: "",
query: "", query: "SELECT * FROM c",
selectedQuery: "", selectedQuery: "",
isGeneratingQuery: false, isGeneratingQuery: false,
isGeneratingExplanation: false, isGeneratingExplanation: false,