Compare commits

...

6 Commits

Author SHA1 Message Date
sunghyunkang1111
d7c9edf1d4 Fix P2 bgs 2023-11-20 11:44:08 -06:00
sunghyunkang1111
3283d4073b Fix P2 bgs 2023-11-20 11:20:54 -06:00
bogercraig
6af1925d15 Enabling enableEndpointDiscovery to test DE Region Outage Resiliency (#1694)
* Setting enableEndpointDiscovery to true to test client resilience during region outage.
When this is set to false, it can impact the SDK's failover behavior.
2023-11-16 11:56:39 -08:00
sunghyunkang1111
b9cbfc924f do not throw error when checking for initial checking of copilot enablement (#1693) 2023-11-14 17:22:12 -06:00
sindhuba
1aa4c18119 Increase NPS sample size and remove constraints (#1692)
* Increase NPS sample size and remove constraints

* Run npm format

* Run npm format:check
2023-11-14 22:26:57 +05:30
sunghyunkang1111
0ab07419ce fix the phoenix endpoint (#1691) 2023-11-10 18:53:23 -06:00
12 changed files with 51 additions and 43 deletions

View File

@@ -148,9 +148,6 @@ export function client(): Cosmos.CosmosClient {
endpoint: endpoint() || "https://cosmos.azure.com", // CosmosClient gets upset if we pass a bad URL. This should never actually get called endpoint: endpoint() || "https://cosmos.azure.com", // CosmosClient gets upset if we pass a bad URL. This should never actually get called
key: userContext.masterKey, key: userContext.masterKey,
tokenProvider, tokenProvider,
connectionPolicy: {
enableEndpointDiscovery: false,
},
userAgentSuffix: "Azure Portal", userAgentSuffix: "Azure Portal",
defaultHeaders: _defaultHeaders, defaultHeaders: _defaultHeaders,
}; };

View File

@@ -275,6 +275,7 @@ export default class Explorer {
const NINETY_DAYS_IN_MS = 7776000000; const NINETY_DAYS_IN_MS = 7776000000;
const ONE_DAY_IN_MS = 86400000; const ONE_DAY_IN_MS = 86400000;
const THREE_DAYS_IN_MS = 259200000;
const isAccountNewerThanNinetyDays = isAccountNewerThanThresholdInMs( const isAccountNewerThanNinetyDays = isAccountNewerThanThresholdInMs(
userContext.databaseAccount?.systemData?.createdAt || "", userContext.databaseAccount?.systemData?.createdAt || "",
NINETY_DAYS_IN_MS, NINETY_DAYS_IN_MS,
@@ -294,32 +295,32 @@ export default class Explorer {
} }
} }
// Try Cosmos DB subscription - survey shown to random 25% of users at day 1 in Data Explorer. // Try Cosmos DB subscription - survey shown to 100% of users at day 1 in Data Explorer.
if (userContext.isTryCosmosDBSubscription) { if (userContext.isTryCosmosDBSubscription) {
if ( if (isAccountNewerThanThresholdInMs(userContext.databaseAccount?.systemData?.createdAt || "", ONE_DAY_IN_MS)) {
isAccountNewerThanThresholdInMs(userContext.databaseAccount?.systemData?.createdAt || "", ONE_DAY_IN_MS) && this.sendNPSMessage();
this.getRandomInt(100) < 25
) {
sendMessage({ type: MessageTypes.DisplayNPSSurvey });
localStorage.setItem("lastSubmitted", Date.now().toString());
} }
} else { } else {
// An existing account is lesser than 90 days old. For existing account show to random 10 % of users in Data Explorer. // An existing account is older than 3 days but less than 90 days old. For existing account show to 100% of users in Data Explorer.
if (isAccountNewerThanNinetyDays) { if (
if (this.getRandomInt(100) < 10) { !isAccountNewerThanThresholdInMs(userContext.databaseAccount?.systemData?.createdAt || "", THREE_DAYS_IN_MS) &&
sendMessage({ type: MessageTypes.DisplayNPSSurvey }); isAccountNewerThanNinetyDays
localStorage.setItem("lastSubmitted", Date.now().toString()); ) {
} this.sendNPSMessage();
} else { } else {
// An existing account is greater than 90 days. For existing account show to random 25 % of users in Data Explorer. // An existing account is greater than 90 days. For existing account show to random 33% of users in Data Explorer.
if (this.getRandomInt(100) < 25) { if (this.getRandomInt(100) < 33) {
sendMessage({ type: MessageTypes.DisplayNPSSurvey }); this.sendNPSMessage();
localStorage.setItem("lastSubmitted", Date.now().toString());
} }
} }
} }
} }
private sendNPSMessage() {
sendMessage({ type: MessageTypes.DisplayNPSSurvey });
localStorage.setItem("lastSubmitted", Date.now().toString());
}
public async refreshDatabaseForResourceToken(): Promise<void> { public async refreshDatabaseForResourceToken(): Promise<void> {
const databaseId = userContext.parsedResourceToken?.databaseId; const databaseId = userContext.parsedResourceToken?.databaseId;
const collectionId = userContext.parsedResourceToken?.collectionId; const collectionId = userContext.parsedResourceToken?.collectionId;
@@ -1396,6 +1397,7 @@ export default class Explorer {
]); ]);
useQueryCopilot.getState().setCopilotEnabled(copilotEnabled); useQueryCopilot.getState().setCopilotEnabled(copilotEnabled);
useQueryCopilot.getState().setCopilotUserDBEnabled(copilotUserDBEnabled); useQueryCopilot.getState().setCopilotUserDBEnabled(copilotUserDBEnabled);
useQueryCopilot.getState().setShowWelcomeModal(window.localStorage.getItem("hideWelcomeModal") !== "true");
} }
public async refreshSampleData(): Promise<void> { public async refreshSampleData(): Promise<void> {

View File

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

View File

@@ -336,7 +336,8 @@ exports[`AddCollectionPanel should render Default properly 1`] = `
directionalHint={4} directionalHint={4}
> >
<Icon <Icon
ariaLabel="Enable analytical store capability to perform near real-time analytics on your operational data, without impacting the performance of transactional workloads." ariaLabel="Enable analytical store capability to perform near real-time analytics on your operational data, without
impacting the performance of transactional workloads."
className="panelInfoIcon" className="panelInfoIcon"
iconName="Info" iconName="Info"
tabIndex={0} tabIndex={0}

View File

@@ -1,5 +1,6 @@
import { IconButton, Image, Link, Modal, PrimaryButton, Stack, StackItem, Text } from "@fluentui/react"; import { IconButton, Image, Link, Modal, PrimaryButton, Stack, StackItem, Text } from "@fluentui/react";
import { useBoolean } from "@fluentui/react-hooks"; import { useBoolean } from "@fluentui/react-hooks";
import { useQueryCopilot } from "hooks/useQueryCopilot";
import React from "react"; import React from "react";
import Flash from "../../../../images/CopilotFlash.svg"; import Flash from "../../../../images/CopilotFlash.svg";
import Thumb from "../../../../images/CopilotThumb.svg"; import Thumb from "../../../../images/CopilotThumb.svg";
@@ -13,7 +14,11 @@ export const WelcomeModal = ({ visible }: { visible: boolean }): JSX.Element =>
if (visible) { if (visible) {
window.localStorage.setItem("hideWelcomeModal", "true"); window.localStorage.setItem("hideWelcomeModal", "true");
} }
}); }, []);
React.useEffect(() => {
useQueryCopilot.getState().setShowWelcomeModal(isModalVisible);
}, [isModalVisible]);
return ( return (
<> <>
@@ -70,7 +75,7 @@ export const WelcomeModal = ({ visible }: { visible: boolean }): JSX.Element =>
<Text> <Text>
Ask Copilot to generate a query by describing the query in your words. Ask Copilot to generate a query by describing the query in your words.
<br /> <br />
<Link target="_blank" href="https://aka.ms/CopilotInAzureCDBDocs"> <Link target="_blank" href="https://aka.ms/MicrosoftCopilotForAzureInCDBHowTo">
Learn more Learn more
</Link> </Link>
</Text> </Text>

View File

@@ -111,7 +111,7 @@ exports[`Query Copilot Welcome Modal snapshot test should render when isOpen is
Ask Copilot to generate a query by describing the query in your words. Ask Copilot to generate a query by describing the query in your words.
<br /> <br />
<StyledLinkBase <StyledLinkBase
href="https://aka.ms/CopilotInAzureCDBDocs" href="https://aka.ms/MicrosoftCopilotForAzureInCDBHowTo"
target="_blank" target="_blank"
> >
Learn more Learn more

View File

@@ -35,7 +35,7 @@ import { GenerateSQLQueryResponse, QueryCopilotProps } from "Explorer/QueryCopil
import { SamplePrompts, SamplePromptsProps } from "Explorer/QueryCopilot/Shared/SamplePrompts/SamplePrompts"; import { SamplePrompts, SamplePromptsProps } from "Explorer/QueryCopilot/Shared/SamplePrompts/SamplePrompts";
import { Action } from "Shared/Telemetry/TelemetryConstants"; import { Action } from "Shared/Telemetry/TelemetryConstants";
import { userContext } from "UserContext"; import { userContext } from "UserContext";
import { useQueryCopilot } from "hooks/useQueryCopilot"; import { QueryCopilotState, useQueryCopilot } from "hooks/useQueryCopilot";
import React, { useRef, useState } from "react"; import React, { useRef, useState } from "react";
import HintIcon from "../../../images/Hint.svg"; import HintIcon from "../../../images/Hint.svg";
import CopilotIcon from "../../../images/QueryCopilotNewLogo.svg"; import CopilotIcon from "../../../images/QueryCopilotNewLogo.svg";
@@ -72,7 +72,6 @@ export const QueryCopilotPromptbar: React.FC<QueryCopilotPromptProps> = ({
containerId, containerId,
}: QueryCopilotPromptProps): JSX.Element => { }: QueryCopilotPromptProps): JSX.Element => {
const [copilotTeachingBubbleVisible, { toggle: toggleCopilotTeachingBubbleVisible }] = useBoolean(false); const [copilotTeachingBubbleVisible, { toggle: toggleCopilotTeachingBubbleVisible }] = useBoolean(false);
const inputEdited = useRef(false);
const { const {
openFeedbackModal, openFeedbackModal,
hideFeedbackModalForLikedQueries, hideFeedbackModalForLikedQueries,
@@ -110,6 +109,8 @@ export const QueryCopilotPromptbar: React.FC<QueryCopilotPromptProps> = ({
errorMessage, errorMessage,
} = useCopilotStore(); } = useCopilotStore();
const inputEdited = useRef(!!userPrompt);
const sampleProps: SamplePromptsProps = { const sampleProps: SamplePromptsProps = {
isSamplePromptsOpen: isSamplePromptsOpen, isSamplePromptsOpen: isSamplePromptsOpen,
setIsSamplePromptsOpen: setIsSamplePromptsOpen, setIsSamplePromptsOpen: setIsSamplePromptsOpen,
@@ -201,7 +202,7 @@ export const QueryCopilotPromptbar: React.FC<QueryCopilotPromptProps> = ({
const serverInfo = useQueryCopilot.getState().notebookServerInfo; const serverInfo = useQueryCopilot.getState().notebookServerInfo;
const queryUri = userContext.features.disableCopilotPhoenixGateaway const queryUri = userContext.features.disableCopilotPhoenixGateaway
? createUri("https://copilotorchestrater.azurewebsites.net/", "generateSQLQuery") ? createUri("https://copilotorchestrater.azurewebsites.net/", "generateSQLQuery")
: createUri(serverInfo.notebookServerEndpoint, "generateSQLQuery"); : createUri(serverInfo.notebookServerEndpoint, "public/generateSQLQuery");
const response = await fetch(queryUri, { const response = await fetch(queryUri, {
method: "POST", method: "POST",
headers: { headers: {
@@ -274,7 +275,7 @@ export const QueryCopilotPromptbar: React.FC<QueryCopilotPromptProps> = ({
const showTeachingBubble = (): void => { const showTeachingBubble = (): void => {
if (!inputEdited.current) { if (!inputEdited.current) {
setTimeout(() => { setTimeout(() => {
if (!inputEdited.current && !isWelcomModalVisible()) { if (!useQueryCopilot.getState().showWelcomeModal && !userPrompt && !inputEdited.current) {
toggleCopilotTeachingBubbleVisible(); toggleCopilotTeachingBubbleVisible();
inputEdited.current = true; inputEdited.current = true;
} }
@@ -282,10 +283,6 @@ export const QueryCopilotPromptbar: React.FC<QueryCopilotPromptProps> = ({
} }
}; };
const isWelcomModalVisible = (): boolean => {
return localStorage.getItem("hideWelcomeModal") !== "true";
};
const clearFeedback = () => { const clearFeedback = () => {
resetButtonState(); resetButtonState();
resetQueryResults(); resetQueryResults();
@@ -305,6 +302,7 @@ export const QueryCopilotPromptbar: React.FC<QueryCopilotPromptProps> = ({
React.useEffect(() => { React.useEffect(() => {
showTeachingBubble(); showTeachingBubble();
useQueryCopilot.subscribe(showTeachingBubble, (state: QueryCopilotState) => state.showWelcomeModal);
useTabs.getState().setIsQueryErrorThrown(false); useTabs.getState().setIsQueryErrorThrown(false);
}, []); }, []);
@@ -591,7 +589,7 @@ export const QueryCopilotPromptbar: React.FC<QueryCopilotPromptProps> = ({
</CommandBarButton> </CommandBarButton>
</Stack> </Stack>
)} )}
<WelcomeModal visible={isWelcomModalVisible()} /> <WelcomeModal visible={useQueryCopilot.getState().showWelcomeModal} />
{isSamplePromptsOpen && <SamplePrompts sampleProps={sampleProps} />} {isSamplePromptsOpen && <SamplePrompts sampleProps={sampleProps} />}
{query !== "" && query.trim().length !== 0 && ( {query !== "" && query.trim().length !== 0 && (
<DeletePopup <DeletePopup

View File

@@ -48,7 +48,7 @@ describe("Query Copilot Client", () => {
const feedbackUri = userContext.features.disableCopilotPhoenixGateaway const feedbackUri = userContext.features.disableCopilotPhoenixGateaway
? createUri("https://copilotorchestrater.azurewebsites.net/", "feedback") ? createUri("https://copilotorchestrater.azurewebsites.net/", "feedback")
: createUri(useQueryCopilot.getState().notebookServerInfo.notebookServerEndpoint, "feedback"); : createUri(useQueryCopilot.getState().notebookServerInfo.notebookServerEndpoint, "public/feedback");
it("should call fetch with the payload with like", async () => { it("should call fetch with the payload with like", async () => {
const mockFetch = jest.fn().mockResolvedValueOnce({}); const mockFetch = jest.fn().mockResolvedValueOnce({});

View File

@@ -93,7 +93,7 @@ export const getCopilotEnabled = async (): Promise<boolean> => {
} }
if (!response?.ok) { if (!response?.ok) {
throw new Error(await response?.text()); return false;
} }
const copilotPortalConfiguration = (await response?.json()) as CopilotEnabledConfiguration; const copilotPortalConfiguration = (await response?.json()) as CopilotEnabledConfiguration;
@@ -215,7 +215,7 @@ export const SendQueryRequest = async ({
const queryUri = userContext.features.disableCopilotPhoenixGateaway const queryUri = userContext.features.disableCopilotPhoenixGateaway
? createUri("https://copilotorchestrater.azurewebsites.net/", "generateSQLQuery") ? createUri("https://copilotorchestrater.azurewebsites.net/", "generateSQLQuery")
: createUri(serverInfo.notebookServerEndpoint, "generateSQLQuery"); : createUri(serverInfo.notebookServerEndpoint, "public/generateSQLQuery");
const payload = { const payload = {
containerSchema: userContext.features.enableCopilotFullSchema containerSchema: userContext.features.enableCopilotFullSchema
@@ -298,7 +298,7 @@ export const SubmitFeedback = async ({
const serverInfo = useQueryCopilot.getState().notebookServerInfo; const serverInfo = useQueryCopilot.getState().notebookServerInfo;
const feedbackUri = userContext.features.disableCopilotPhoenixGateaway const feedbackUri = userContext.features.disableCopilotPhoenixGateaway
? createUri("https://copilotorchestrater.azurewebsites.net/", "feedback") ? createUri("https://copilotorchestrater.azurewebsites.net/", "feedback")
: createUri(serverInfo.notebookServerEndpoint, "feedback"); : createUri(serverInfo.notebookServerEndpoint, "public/feedback");
await fetch(feedbackUri, { await fetch(feedbackUri, {
method: "POST", method: "POST",
headers: { headers: {

View File

@@ -104,12 +104,13 @@ interface IQueryTabStates {
export const QueryTabFunctionComponent = (props: IQueryTabComponentProps): any => { export const QueryTabFunctionComponent = (props: IQueryTabComponentProps): any => {
const copilotStore = useCopilotStore(); const copilotStore = useCopilotStore();
const copilotGlobalStore = useQueryCopilot();
const isSampleCopilotActive = useSelectedNode.getState().isQueryCopilotCollectionSelected(); const isSampleCopilotActive = useSelectedNode.getState().isQueryCopilotCollectionSelected();
const queryTabProps = { const queryTabProps = {
...props, ...props,
copilotEnabled: copilotEnabled:
useQueryCopilot().copilotEnabled && copilotGlobalStore.copilotEnabled &&
(useQueryCopilot().copilotUserDBEnabled || (isSampleCopilotActive && !!userContext.sampleDataConnectionInfo)), (copilotGlobalStore.copilotUserDBEnabled || (isSampleCopilotActive && !!userContext.sampleDataConnectionInfo)),
isSampleCopilotActive: isSampleCopilotActive, isSampleCopilotActive: isSampleCopilotActive,
copilotStore: copilotStore, copilotStore: copilotStore,
}; };

View File

@@ -420,9 +420,11 @@ async function configurePortal(): Promise<Explorer> {
updateContextsFromPortalMessage(inputs); updateContextsFromPortalMessage(inputs);
explorer = new Explorer(); explorer = new Explorer();
resolve(explorer); resolve(explorer);
if (userContext.apiType === "Postgres") {
explorer.openNPSSurveyDialog(); if (userContext.apiType === "Postgres" || userContext.apiType === "SQL" || userContext.apiType === "Mongo") {
setTimeout(() => explorer.openNPSSurveyDialog(), 3000);
} }
if (openAction) { if (openAction) {
handleOpenAction(openAction, useDatabases.getState().databases, explorer); handleOpenAction(openAction, useDatabases.getState().databases, explorer);
} }

View File

@@ -36,6 +36,7 @@ export interface QueryCopilotState {
generatedQueryComments: string; generatedQueryComments: string;
wasCopilotUsed: boolean; wasCopilotUsed: boolean;
showWelcomeSidebar: boolean; showWelcomeSidebar: boolean;
showWelcomeModal: boolean;
showCopilotSidebar: boolean; showCopilotSidebar: boolean;
chatMessages: CopilotMessage[]; chatMessages: CopilotMessage[];
shouldIncludeInMessages: boolean; shouldIncludeInMessages: boolean;
@@ -77,6 +78,7 @@ export interface QueryCopilotState {
setGeneratedQueryComments: (generatedQueryComments: string) => void; setGeneratedQueryComments: (generatedQueryComments: string) => void;
setWasCopilotUsed: (wasCopilotUsed: boolean) => void; setWasCopilotUsed: (wasCopilotUsed: boolean) => void;
setShowWelcomeSidebar: (showWelcomeSidebar: boolean) => void; setShowWelcomeSidebar: (showWelcomeSidebar: boolean) => void;
setShowWelcomeModal: (showWelcomeModal: boolean) => void;
setShowCopilotSidebar: (showCopilotSidebar: boolean) => void; setShowCopilotSidebar: (showCopilotSidebar: boolean) => void;
setChatMessages: (chatMessages: CopilotMessage[]) => void; setChatMessages: (chatMessages: CopilotMessage[]) => void;
setShouldIncludeInMessages: (shouldIncludeInMessages: boolean) => void; setShouldIncludeInMessages: (shouldIncludeInMessages: boolean) => void;
@@ -122,6 +124,7 @@ export const useQueryCopilot: QueryCopilotStore = create((set) => ({
generatedQueryComments: "", generatedQueryComments: "",
wasCopilotUsed: false, wasCopilotUsed: false,
showWelcomeSidebar: true, showWelcomeSidebar: true,
showWelcomeModal: true,
showCopilotSidebar: false, showCopilotSidebar: false,
chatMessages: [], chatMessages: [],
shouldIncludeInMessages: true, shouldIncludeInMessages: true,
@@ -174,6 +177,7 @@ export const useQueryCopilot: QueryCopilotStore = create((set) => ({
setGeneratedQueryComments: (generatedQueryComments: string) => set({ generatedQueryComments }), setGeneratedQueryComments: (generatedQueryComments: string) => set({ generatedQueryComments }),
setWasCopilotUsed: (wasCopilotUsed: boolean) => set({ wasCopilotUsed }), setWasCopilotUsed: (wasCopilotUsed: boolean) => set({ wasCopilotUsed }),
setShowWelcomeSidebar: (showWelcomeSidebar: boolean) => set({ showWelcomeSidebar }), setShowWelcomeSidebar: (showWelcomeSidebar: boolean) => set({ showWelcomeSidebar }),
setShowWelcomeModal: (showWelcomeModal: boolean) => set({ showWelcomeModal }),
setShowCopilotSidebar: (showCopilotSidebar: boolean) => set({ showCopilotSidebar }), setShowCopilotSidebar: (showCopilotSidebar: boolean) => set({ showCopilotSidebar }),
setChatMessages: (chatMessages: CopilotMessage[]) => set({ chatMessages }), setChatMessages: (chatMessages: CopilotMessage[]) => set({ chatMessages }),
setShouldIncludeInMessages: (shouldIncludeInMessages: boolean) => set({ shouldIncludeInMessages }), setShouldIncludeInMessages: (shouldIncludeInMessages: boolean) => set({ shouldIncludeInMessages }),