From dcd8d1637bb58daf7ac6f2b5c90e7ee907770aae Mon Sep 17 00:00:00 2001 From: Laurent Nguyen Date: Tue, 10 Oct 2023 19:25:58 +0000 Subject: [PATCH 1/4] Implement retrieval of authorization token for Fabric via iframe rpc (#1647) * For Fabric, send message to get Authorization token from iframe parent * tokenProvider: set date header and return token * Expect account endpoint on initialize message from Fabric * Fix format --------- Co-authored-by: artrejo --- src/Common/CosmosClient.ts | 26 ++++++++++++---- src/Common/MessageHandler.ts | 2 +- src/Contracts/ExplorerContracts.ts | 44 ++------------------------- src/Contracts/FabricContract.ts | 37 +++++++++++++++++++++-- src/Contracts/MessageTypes.ts | 48 ++++++++++++++++++++++++++++++ src/hooks/useKnockoutExplorer.ts | 45 +++++++++++++++------------- 6 files changed, 131 insertions(+), 71 deletions(-) create mode 100644 src/Contracts/MessageTypes.ts diff --git a/src/Common/CosmosClient.ts b/src/Common/CosmosClient.ts index 84d80b0e2..bd4b76d0d 100644 --- a/src/Common/CosmosClient.ts +++ b/src/Common/CosmosClient.ts @@ -1,13 +1,14 @@ import * as Cosmos from "@azure/cosmos"; -import { configContext, Platform } from "../ConfigContext"; +import { sendCachedDataMessage } from "Common/MessageHandler"; +import { AuthorizationToken, MessageTypes } from "Contracts/MessageTypes"; +import { AuthType } from "../AuthType"; +import { PriorityLevel } from "../Common/Constants"; +import { Platform, configContext } from "../ConfigContext"; import { userContext } from "../UserContext"; import { logConsoleError } from "../Utils/NotificationConsoleUtils"; +import * as PriorityBasedExecutionUtils from "../Utils/PriorityBasedExecutionUtils"; import { EmulatorMasterKey, HttpHeaders } from "./Constants"; import { getErrorMessage } from "./ErrorHandlingUtils"; -import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility"; -import { PriorityLevel } from "../Common/Constants"; -import * as PriorityBasedExecutionUtils from "../Utils/PriorityBasedExecutionUtils"; -import { AuthType } from "../AuthType"; const _global = typeof self === "undefined" ? window : self; @@ -26,6 +27,15 @@ export const tokenProvider = async (requestInfo: Cosmos.RequestInfo) => { return decodeURIComponent(headers.authorization); } + if (configContext.platform === Platform.Fabric) { + const authorizationToken = await sendCachedDataMessage(MessageTypes.GetAuthorizationToken, [ + requestInfo, + ]); + console.log("Response from Fabric: ", authorizationToken); + headers[HttpHeaders.msDate] = authorizationToken.XDate; + return authorizationToken.PrimaryReadWriteToken; + } + if (userContext.masterKey) { // TODO This SDK method mutates the headers object. Find a better one or fix the SDK. await Cosmos.setAuthorizationTokenHeaderUsingMasterKey(verb, resourceId, resourceType, headers, EmulatorMasterKey); @@ -56,7 +66,11 @@ export const endpoint = () => { return userContext.endpoint || userContext?.databaseAccount?.properties?.documentEndpoint; }; -export async function getTokenFromAuthService(verb: string, resourceType: string, resourceId?: string): Promise { +export async function getTokenFromAuthService( + verb: string, + resourceType: string, + resourceId?: string, +): Promise { try { const host = configContext.BACKEND_ENDPOINT; const response = await _global.fetch(host + "/api/guest/runtimeproxy/authorizationTokens", { diff --git a/src/Common/MessageHandler.ts b/src/Common/MessageHandler.ts index 834e2d7b5..e32b31d6c 100644 --- a/src/Common/MessageHandler.ts +++ b/src/Common/MessageHandler.ts @@ -22,7 +22,7 @@ export function handleCachedDataMessage(message: any): void { if (messageContent.error != null) { cachedDataPromise.deferred.reject(messageContent.error); } else { - cachedDataPromise.deferred.resolve(JSON.parse(messageContent.data)); + cachedDataPromise.deferred.resolve(messageContent.data); } runGarbageCollector(); } diff --git a/src/Contracts/ExplorerContracts.ts b/src/Contracts/ExplorerContracts.ts index fa286f1fc..42e5d3dc0 100644 --- a/src/Contracts/ExplorerContracts.ts +++ b/src/Contracts/ExplorerContracts.ts @@ -1,46 +1,6 @@ +import { MessageTypes } from "Contracts/MessageTypes"; import * as ActionContracts from "./ActionContracts"; import * as Diagnostics from "./Diagnostics"; import * as Versions from "./Versions"; -/** - * Messaging types used with Data Explorer <-> Portal communication - * and Hosted <-> Explorer communication - */ -export enum MessageTypes { - TelemetryInfo, - LogInfo, - RefreshResources, - AllDatabases, - CollectionsForDatabase, - RefreshOffers, - AllOffers, - UpdateLocationHash, - SingleOffer, - RefreshOffer, - UpdateAccountName, - ForbiddenError, - AadSignIn, - GetAccessAadRequest, - GetAccessAadResponse, - UpdateAccountSwitch, - UpdateDirectoryControl, - SwitchAccount, - SendNotification, - ClearNotification, - ExplorerClickEvent, - LoadingStatus, - GetArcadiaToken, - CreateWorkspace, - CreateSparkPool, - RefreshDatabaseAccount, - CloseTab, - OpenQuickstartBlade, - OpenPostgreSQLPasswordReset, - OpenPostgresNetworkingBlade, - OpenCosmosDBNetworkingBlade, - DisplayNPSSurvey, - OpenVCoreMongoNetworkingBlade, - OpenVCoreMongoConnectionStringsBlade, -} - -export { ActionContracts, Diagnostics, Versions }; +export { ActionContracts, Diagnostics, MessageTypes, Versions }; diff --git a/src/Contracts/FabricContract.ts b/src/Contracts/FabricContract.ts index 61f5c0bdb..14dca076a 100644 --- a/src/Contracts/FabricContract.ts +++ b/src/Contracts/FabricContract.ts @@ -1,3 +1,5 @@ +import { AuthorizationToken, MessageTypes } from "./MessageTypes"; + export type FabricMessage = | { type: "newContainer"; @@ -5,21 +7,52 @@ export type FabricMessage = } | { type: "initialize"; - connectionString: string | undefined; + message: { + endpoint: string | undefined; + error: string | undefined; + }; } | { type: "openTab"; databaseName: string; collectionName: string | undefined; + } + | { + type: "authorizationToken"; + message: { + id: string; + error: string | undefined; + data: AuthorizationToken | undefined; + }; }; export type DataExploreMessage = | "ready" | { - type: number; + type: MessageTypes.TelemetryInfo; data: { action: "LoadDatabases"; actionModifier: "success" | "start"; defaultExperience: "SQL"; }; + } + | { + type: MessageTypes.GetAuthorizationToken; + id: string; + params: GetCosmosTokenMessageOptions[]; }; + +export type GetCosmosTokenMessageOptions = { + verb: "connect" | "delete" | "get" | "head" | "options" | "patch" | "post" | "put" | "trace"; + resourceType: "" | "dbs" | "colls" | "docs" | "sprocs" | "pkranges"; + resourceId: string; +}; + +export type CosmosDBTokenResponse = { + token: string; + date: string; +}; + +export type CosmosDBConnectionInfoResponse = { + endpoint: string; +}; diff --git a/src/Contracts/MessageTypes.ts b/src/Contracts/MessageTypes.ts new file mode 100644 index 000000000..7bd129819 --- /dev/null +++ b/src/Contracts/MessageTypes.ts @@ -0,0 +1,48 @@ +/** + * Messaging types used with Data Explorer <-> Portal communication, + * Hosted <-> Explorer communication and Data Explorer -> Fabric communication. + */ +export enum MessageTypes { + TelemetryInfo, + LogInfo, + RefreshResources, + AllDatabases, + CollectionsForDatabase, + RefreshOffers, + AllOffers, + UpdateLocationHash, + SingleOffer, + RefreshOffer, + UpdateAccountName, + ForbiddenError, + AadSignIn, + GetAccessAadRequest, + GetAccessAadResponse, + UpdateAccountSwitch, + UpdateDirectoryControl, + SwitchAccount, + SendNotification, + ClearNotification, + ExplorerClickEvent, + LoadingStatus, + GetArcadiaToken, + CreateWorkspace, + CreateSparkPool, + RefreshDatabaseAccount, + CloseTab, + OpenQuickstartBlade, + OpenPostgreSQLPasswordReset, + OpenPostgresNetworkingBlade, + OpenCosmosDBNetworkingBlade, + DisplayNPSSurvey, + OpenVCoreMongoNetworkingBlade, + OpenVCoreMongoConnectionStringsBlade, + + // Data Explorer -> Fabric communication + GetAuthorizationToken, +} + +export interface AuthorizationToken { + XDate: string; + PrimaryReadWriteToken: string; +} diff --git a/src/hooks/useKnockoutExplorer.ts b/src/hooks/useKnockoutExplorer.ts index 16d044105..f2c8c2abe 100644 --- a/src/hooks/useKnockoutExplorer.ts +++ b/src/hooks/useKnockoutExplorer.ts @@ -2,15 +2,13 @@ import { createUri } from "Common/UrlUtility"; import { FabricMessage } from "Contracts/FabricContract"; import Explorer from "Explorer/Explorer"; import { useSelectedNode } from "Explorer/useSelectedNode"; -import { fetchEncryptedToken } from "Platform/Hosted/Components/ConnectExplorer"; import { getNetworkSettingsWarningMessage } from "Utils/NetworkUtility"; -import { fetchAccessData } from "hooks/usePortalAccessToken"; import { ReactTabKind, useTabs } from "hooks/useTabs"; import { useEffect, useState } from "react"; import { AuthType } from "../AuthType"; import { AccountKind, Flights } from "../Common/Constants"; import { normalizeArmEndpoint } from "../Common/EnvironmentUtility"; -import { sendMessage, sendReadyMessage } from "../Common/MessageHandler"; +import { handleCachedDataMessage, sendMessage, sendReadyMessage } from "../Common/MessageHandler"; import { Platform, configContext, updateConfigContext } from "../ConfigContext"; import { ActionType, DataExplorerAction, TabKind } from "../Contracts/ActionContracts"; import { MessageTypes } from "../Contracts/ExplorerContracts"; @@ -107,23 +105,7 @@ async function configureFabric(): Promise { switch (data.type) { case "initialize": { - // TODO For now, retrieve info from session storage. Replace with info injected into Data Explorer - const connectionString = data.connectionString ?? sessionStorage.getItem("connectionString"); - if (!connectionString) { - console.error("No connection string found in session storage"); - return undefined; - } - const encryptedToken = await fetchEncryptedToken(connectionString); - // TODO Duplicated from useTokenMetadata - const encryptedTokenMetadata = await fetchAccessData(encryptedToken); - - const hostedConfig: EncryptedToken = { - authType: AuthType.EncryptedToken, - encryptedToken, - encryptedTokenMetadata, - }; - - explorer = await configureWithEncryptedToken(hostedConfig); + explorer = await configureWithFabric(data.message.endpoint); resolve(explorer); break; } @@ -166,6 +148,10 @@ async function configureFabric(): Promise { break; } + case "authorizationToken": { + handleCachedDataMessage(data); + break; + } default: console.error(`Unknown Fabric message type: ${JSON.stringify(data)}`); break; @@ -315,6 +301,25 @@ function configureHostedWithResourceToken(config: ResourceToken): Explorer { return explorer; } +function configureWithFabric(documentEndpoint: string): Explorer { + updateUserContext({ + authType: AuthType.ConnectionString, + databaseAccount: { + id: "", + location: "", + type: "", + name: "Mounted", + kind: AccountKind.Default, + properties: { + documentEndpoint, + }, + }, + }); + const explorer = new Explorer(); + setTimeout(() => explorer.refreshAllDatabases(), 0); + return explorer; +} + function configureWithEncryptedToken(config: EncryptedToken): Explorer { const apiExperience = DefaultExperienceUtility.getDefaultExperienceFromApiKind(config.encryptedTokenMetadata.apiKind); updateUserContext({ From 9669301d146be814a8672643870b1004bece830b Mon Sep 17 00:00:00 2001 From: Laurent Nguyen Date: Thu, 12 Oct 2023 06:00:08 +0000 Subject: [PATCH 2/4] Remove obsolete unit test (#1655) --- .../DataSamples/ContainerSampleGenerator.test.ts | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/Explorer/DataSamples/ContainerSampleGenerator.test.ts b/src/Explorer/DataSamples/ContainerSampleGenerator.test.ts index c06596531..0b9bf4ec2 100644 --- a/src/Explorer/DataSamples/ContainerSampleGenerator.test.ts +++ b/src/Explorer/DataSamples/ContainerSampleGenerator.test.ts @@ -123,19 +123,6 @@ describe("ContainerSampleGenerator", () => { await generator.createSampleContainerAsync(); }); - it("should not create any sample for Mongo API account", async () => { - const experience = "Sample generation not supported for this API Mongo"; - updateUserContext({ - databaseAccount: { - properties: { - capabilities: [{ name: "EnableMongo" }], - }, - } as DatabaseAccount, - }); - - expect(ContainerSampleGenerator.createSampleGeneratorAsync(explorerStub)).rejects.toMatch(experience); - }); - it("should not create any sample for Table API account", async () => { const experience = "Sample generation not supported for this API Tables"; updateUserContext({ From d376a7463c960e214a49b61719e5cf721b2a6ba8 Mon Sep 17 00:00:00 2001 From: sunghyunkang1111 <114709653+sunghyunkang1111@users.noreply.github.com> Date: Thu, 12 Oct 2023 21:54:01 -0500 Subject: [PATCH 3/4] P1, P2 bug fixes for private preview (#1657) * P1 bug fix for private preview * Add updated snapshot files * Fix failing unit test * Fix unit tests and update snapshot --- .../Modal/QueryCopilotFeedbackModal.test.tsx | 18 +- .../Modal/QueryCopilotFeedbackModal.tsx | 157 +- .../QueryCopilotFeedbackModal.test.tsx.snap | 2403 +++++++++-------- .../QueryCopilot/QueryCopilotPromptbar.tsx | 14 +- src/Explorer/QueryCopilot/QueryCopilotTab.tsx | 11 +- .../QueryCopilotTab.test.tsx.snap | 37 +- src/hooks/useQueryCopilot.ts | 2 +- 7 files changed, 1373 insertions(+), 1269 deletions(-) diff --git a/src/Explorer/QueryCopilot/Modal/QueryCopilotFeedbackModal.test.tsx b/src/Explorer/QueryCopilot/Modal/QueryCopilotFeedbackModal.test.tsx index 765760085..14cca91a7 100644 --- a/src/Explorer/QueryCopilot/Modal/QueryCopilotFeedbackModal.test.tsx +++ b/src/Explorer/QueryCopilot/Modal/QueryCopilotFeedbackModal.test.tsx @@ -102,7 +102,7 @@ describe("Query Copilot Feedback Modal snapshot test", () => { expect(wrapper).toMatchSnapshot(); }); - it("should submit submission", () => { + it("should not submit submission if required description field is null", () => { const explorer = new Explorer(); const wrapper = shallow(); @@ -110,12 +110,24 @@ describe("Query Copilot Feedback Modal snapshot test", () => { submitButton.simulate("click"); wrapper.setProps({}); + expect(SubmitFeedback).toHaveBeenCalledTimes(0); + }); + + it("should submit submission", () => { + useQueryCopilot.getState().openFeedbackModal("test query", false, "test prompt"); + const explorer = new Explorer(); + const wrapper = shallow(); + + const submitButton = wrapper.find("form"); + submitButton.simulate("submit"); + wrapper.setProps({}); + expect(SubmitFeedback).toHaveBeenCalledTimes(1); expect(SubmitFeedback).toHaveBeenCalledWith({ params: { likeQuery: false, - generatedQuery: "", - userPrompt: "", + generatedQuery: "test query", + userPrompt: "test prompt", description: "", contact: getUserEmail(), }, diff --git a/src/Explorer/QueryCopilot/Modal/QueryCopilotFeedbackModal.tsx b/src/Explorer/QueryCopilot/Modal/QueryCopilotFeedbackModal.tsx index 86a1b7b94..aa865ed0e 100644 --- a/src/Explorer/QueryCopilot/Modal/QueryCopilotFeedbackModal.tsx +++ b/src/Explorer/QueryCopilot/Modal/QueryCopilotFeedbackModal.tsx @@ -25,93 +25,94 @@ export const QueryCopilotFeedbackModal = ({ explorer }: { explorer: Explorer }): closeFeedbackModal, setHideFeedbackModalForLikedQueries, } = useQueryCopilot(); - const [isContactAllowed, setIsContactAllowed] = React.useState(true); + const [isContactAllowed, setIsContactAllowed] = React.useState(false); const [description, setDescription] = React.useState(""); const [doNotShowAgainChecked, setDoNotShowAgainChecked] = React.useState(false); const [contact, setContact] = React.useState(getUserEmail()); + const handleSubmit = () => { + closeFeedbackModal(); + setHideFeedbackModalForLikedQueries(doNotShowAgainChecked); + SubmitFeedback({ + params: { generatedQuery, likeQuery, description, userPrompt, contact }, + explorer: explorer, + }); + }; + return ( - - - Send feedback to Microsoft - closeFeedbackModal()} /> - - Your feedback will help improve the experience. - setDescription(newValue)} - multiline - rows={3} - /> - - { - setIsContactAllowed(option.key === "yes"); - setContact(option.key === "yes" ? getUserEmail() : ""); - }} - > - - By pressing submit, your feedback will be used to improve Microsoft products and services. Please see the{" "} - { - - Privacy statement - - }{" "} - for more information. - - {likeQuery && ( - setDoNotShowAgainChecked(checked)} +
+ + + Send feedback to Microsoft + closeFeedbackModal()} /> + + Your feedback will help improve the experience. + setDescription(newValue)} + multiline + rows={3} /> - )} - - { - closeFeedbackModal(); - setHideFeedbackModalForLikedQueries(doNotShowAgainChecked); - SubmitFeedback({ - params: { generatedQuery, likeQuery, description, userPrompt, contact }, - explorer: explorer, - }); + + - Submit - - closeFeedbackModal()}>Cancel + 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() : ""); + }} + > + + By pressing submit, your feedback will be used to improve Microsoft products and services. Please see the{" "} + { + + Privacy statement + + }{" "} + for more information. + + {likeQuery && ( + setDoNotShowAgainChecked(checked)} + /> + )} + + + Submit + + closeFeedbackModal()}>Cancel + - +
); }; diff --git a/src/Explorer/QueryCopilot/Modal/__snapshots__/QueryCopilotFeedbackModal.test.tsx.snap b/src/Explorer/QueryCopilot/Modal/__snapshots__/QueryCopilotFeedbackModal.test.tsx.snap index aa6cce88c..2d333adb5 100644 --- a/src/Explorer/QueryCopilot/Modal/__snapshots__/QueryCopilotFeedbackModal.test.tsx.snap +++ b/src/Explorer/QueryCopilot/Modal/__snapshots__/QueryCopilotFeedbackModal.test.tsx.snap @@ -4,154 +4,158 @@ exports[`Query Copilot Feedback Modal snapshot test shoud render and match snaps - + + + Send feedback to Microsoft + + + - Send feedback to Microsoft + Your feedback will help improve the experience. - - - - Your feedback will help improve the experience. - - - - - - By pressing submit, your feedback will be used to improve Microsoft products and services. Please see the - - - Privacy statement - - - for more information. - - - + + + - Submit - - + Privacy statement + + + for more information. + + - Cancel - + + Submit + + + Cancel + + - + `; @@ -159,154 +163,173 @@ exports[`Query Copilot Feedback Modal snapshot test should cancel submission 1`] - + + + Send feedback to Microsoft + + + - Send feedback to Microsoft + Your feedback will help improve the experience. - - - - Your feedback will help improve the experience. - - - - - - By pressing submit, your feedback will be used to improve Microsoft products and services. Please see the - - - Privacy statement - - - for more information. - - - + + + - Submit - - + Privacy statement + + + for more information. + + + - Cancel - + + Submit + + + Cancel + + - + `; @@ -314,154 +337,158 @@ exports[`Query Copilot Feedback Modal snapshot test should close on cancel click - + + + Send feedback to Microsoft + + + - Send feedback to Microsoft + Your feedback will help improve the experience. - - - - Your feedback will help improve the experience. - - - - - - By pressing submit, your feedback will be used to improve Microsoft products and services. Please see the - - - Privacy statement - - - for more information. - - - + + + - Submit - - + Privacy statement + + + for more information. + + - Cancel - + + Submit + + + Cancel + + - + `; @@ -469,154 +496,158 @@ exports[`Query Copilot Feedback Modal snapshot test should get user unput 1`] = - + + + Send feedback to Microsoft + + + - Send feedback to Microsoft + Your feedback will help improve the experience. - - - - Your feedback will help improve the experience. - - - - - - By pressing submit, your feedback will be used to improve Microsoft products and services. Please see the - - - Privacy statement - - - for more information. - - - + + + - Submit - - + Privacy statement + + + for more information. + + - Cancel - + + Submit + + + Cancel + + - + `; @@ -624,154 +655,158 @@ exports[`Query Copilot Feedback Modal snapshot test should not render dont show - + + + Send feedback to Microsoft + + + - Send feedback to Microsoft + Your feedback will help improve the experience. - - - - Your feedback will help improve the experience. - - - - - - By pressing submit, your feedback will be used to improve Microsoft products and services. Please see the - - - Privacy statement - - - for more information. - - - + + + - Submit - - + Privacy statement + + + for more information. + + - Cancel - + + Submit + + + Cancel + + - + `; @@ -779,154 +814,158 @@ exports[`Query Copilot Feedback Modal snapshot test should record user contact c - + + + Send feedback to Microsoft + + + - Send feedback to Microsoft + Your feedback will help improve the experience. - - - - Your feedback will help improve the experience. - - - - - - By pressing submit, your feedback will be used to improve Microsoft products and services. Please see the - - - Privacy statement - - - for more information. - - - + + + - Submit - - + Privacy statement + + + for more information. + + - Cancel - + + Submit + + + Cancel + + - + `; @@ -934,154 +973,158 @@ exports[`Query Copilot Feedback Modal snapshot test should record user contact c - + + + Send feedback to Microsoft + + + - Send feedback to Microsoft + Your feedback will help improve the experience. - - - - Your feedback will help improve the experience. - - - - - - By pressing submit, your feedback will be used to improve Microsoft products and services. Please see the - - - Privacy statement - - - for more information. - - - + + + - Submit - - + Privacy statement + + + for more information. + + - Cancel - + + Submit + + + Cancel + + - + `; @@ -1089,169 +1132,173 @@ exports[`Query Copilot Feedback Modal snapshot test should render dont show agai - + + + Send feedback to Microsoft + + + - Send feedback to Microsoft + Your feedback will help improve the experience. - - - - Your feedback will help improve the experience. - - - - - - By pressing submit, your feedback will be used to improve Microsoft products and services. Please see the - - - Privacy statement - - - for more information. - - - - + + + - Submit - - + Privacy statement + + + for more information. + + + - Cancel - + + Submit + + + Cancel + + - + `; @@ -1259,153 +1306,157 @@ exports[`Query Copilot Feedback Modal snapshot test should submit submission 1`] - + + + Send feedback to Microsoft + + + - Send feedback to Microsoft + Your feedback will help improve the experience. - - - - Your feedback will help improve the experience. - - - - - - By pressing submit, your feedback will be used to improve Microsoft products and services. Please see the - - - Privacy statement - - - for more information. - - - + + + - Submit - - + Privacy statement + + + for more information. + + - Cancel - + + Submit + + + Cancel + + - + `; diff --git a/src/Explorer/QueryCopilot/QueryCopilotPromptbar.tsx b/src/Explorer/QueryCopilot/QueryCopilotPromptbar.tsx index 59f5e8d7d..88f9352ab 100644 --- a/src/Explorer/QueryCopilot/QueryCopilotPromptbar.tsx +++ b/src/Explorer/QueryCopilot/QueryCopilotPromptbar.tsx @@ -237,7 +237,7 @@ export const QueryCopilotPromptbar: React.FC = ({ const showTeachingBubble = (): void => { if (!inputEdited.current) { setTimeout(() => { - if (!inputEdited.current) { + if (!inputEdited.current && !isWelcomModalVisible()) { toggleCopilotTeachingBubbleVisible(); inputEdited.current = true; } @@ -245,6 +245,10 @@ export const QueryCopilotPromptbar: React.FC = ({ } }; + const isWelcomModalVisible = (): boolean => { + return localStorage.getItem("hideWelcomeModal") !== "true"; + }; + const clearFeedback = () => { resetButtonState(); resetQueryResults(); @@ -297,7 +301,7 @@ export const QueryCopilotPromptbar: React.FC = ({ setShowSamplePrompts(true); }} onKeyDown={(e) => { - if (e.key === "Enter") { + if (e.key === "Enter" && userPrompt) { inputEdited.current = true; startGenerateQueryProcess(); } @@ -533,7 +537,7 @@ export const QueryCopilotPromptbar: React.FC = ({ iconProps={{ iconName: "Copy" }} style={{ margin: "0 10px", backgroundColor: "#FFF8F0", transition: "background-color 0.3s ease" }} > - Copy code + Copy query { @@ -542,11 +546,11 @@ export const QueryCopilotPromptbar: React.FC = ({ iconProps={{ iconName: "Delete" }} style={{ margin: "0 10px", backgroundColor: "#FFF8F0", transition: "background-color 0.3s ease" }} > - Delete code + Delete query )} - + {isSamplePromptsOpen && } {query !== "" && query.trim().length !== 0 && ( = ({ explorer }: QueryCopilotProps): JSX.Element => { const { query, setQuery, selectedQuery, setSelectedQuery, isGeneratingQuery } = useQueryCopilot(); - const cachedCopilotToggleStatus = localStorage.getItem(`${userContext.databaseAccount?.id}-queryCopilotToggleStatus`); - const [copilotActive, setCopilotActive] = useState(StringUtility.toBoolean(cachedCopilotToggleStatus)); + const cachedCopilotToggleStatus: string = localStorage.getItem( + `${userContext.databaseAccount?.id}-queryCopilotToggleStatus`, + ); + const copilotInitialActive: boolean = cachedCopilotToggleStatus + ? StringUtility.toBoolean(cachedCopilotToggleStatus) + : true; + const [copilotActive, setCopilotActive] = useState(copilotInitialActive); const getCommandbarButtons = (): CommandButtonComponentProps[] => { const executeQueryBtnLabel = selectedQuery ? "Execute Selection" : "Execute Query"; @@ -87,7 +92,7 @@ export const QueryCopilotTab: React.FC = ({ explorer }: Query )} - + + @@ -25,10 +56,10 @@ exports[`Query copilot tab snapshot test should render with initial input 1`] = onDragEnd={null} onDragStart={null} onSecondaryPaneSizeChange={null} - percentage={false} + percentage={true} primaryIndex={0} - primaryMinSize={100} - secondaryMinSize={200} + primaryMinSize={30} + secondaryMinSize={70} vertical={true} > ({ openFeedbackModal: (generatedQuery: string, likeQuery: boolean, userPrompt: string) => set({ generatedQuery, likeQuery, userPrompt, showFeedbackModal: true }), - closeFeedbackModal: () => set({ generatedQuery: "", likeQuery: false, userPrompt: "", showFeedbackModal: false }), + closeFeedbackModal: () => set({ showFeedbackModal: false }), setHideFeedbackModalForLikedQueries: (hideFeedbackModalForLikedQueries: boolean) => set({ hideFeedbackModalForLikedQueries }), refreshCorrelationId: () => set({ correlationId: guid() }), From 14d76770564a37fede5fabb977087afce5cb16fe Mon Sep 17 00:00:00 2001 From: jawelton74 <103591340+jawelton74@users.noreply.github.com> Date: Mon, 16 Oct 2023 13:18:40 -0700 Subject: [PATCH 4/4] Add feature for disabling connection string login and enable this for aad redirect. (#1660) * Add redirect for /aad to /?feature.enableAadDataPlane=true * Add feature to hide the connection string login link. Enable this new feature for the aad redirect. --- .../Components/ConnectExplorer.test.tsx | 23 +++++++++++++++++++ .../Hosted/Components/ConnectExplorer.tsx | 12 ++++++---- src/Platform/Hosted/extractFeatures.ts | 2 ++ web.config | 2 +- 4 files changed, 34 insertions(+), 5 deletions(-) diff --git a/src/Platform/Hosted/Components/ConnectExplorer.test.tsx b/src/Platform/Hosted/Components/ConnectExplorer.test.tsx index ed342a17b..dc54a5f2a 100644 --- a/src/Platform/Hosted/Components/ConnectExplorer.test.tsx +++ b/src/Platform/Hosted/Components/ConnectExplorer.test.tsx @@ -1,6 +1,8 @@ jest.mock("../../../hooks/useDirectories"); import "@testing-library/jest-dom"; import { fireEvent, render, screen } from "@testing-library/react"; +import { extractFeatures } from "Platform/Hosted/extractFeatures"; +import { updateUserContext, userContext } from "UserContext"; import React from "react"; import { ConnectExplorer } from "./ConnectExplorer"; @@ -16,3 +18,24 @@ it("shows the connect form", () => { fireEvent.click(screen.getByText("Connect to your account with connection string")); expect(screen.queryByPlaceholderText("Please enter a connection string")).toBeDefined(); }); + +it("hides the connection string link when feature.disableConnectionStringLogin is true", () => { + const connectionString = "fakeConnectionString"; + const login = jest.fn(); + const setConnectionString = jest.fn(); + const setEncryptedToken = jest.fn(); + const setAuthType = jest.fn(); + const oldFeatures = userContext.features; + + const params = new URLSearchParams({ + "feature.disableConnectionStringLogin": "true", + }); + + const testFeatures = extractFeatures(params); + updateUserContext({ features: testFeatures }); + + render(); + expect(screen.queryByPlaceholderText("Connect to your account with connection string")).toBeNull(); + + updateUserContext({ features: oldFeatures }); +}); diff --git a/src/Platform/Hosted/Components/ConnectExplorer.tsx b/src/Platform/Hosted/Components/ConnectExplorer.tsx index 575093bc2..3ee72246e 100644 --- a/src/Platform/Hosted/Components/ConnectExplorer.tsx +++ b/src/Platform/Hosted/Components/ConnectExplorer.tsx @@ -1,4 +1,5 @@ import { useBoolean } from "@fluentui/react-hooks"; +import { userContext } from "UserContext"; import * as React from "react"; import ConnectImage from "../../../../images/HdeConnectCosmosDB.svg"; import ErrorImage from "../../../../images/error.svg"; @@ -37,6 +38,7 @@ export const ConnectExplorer: React.FunctionComponent = ({ setConnectionString, }: Props) => { const [isFormVisible, { setTrue: showForm }] = useBoolean(false); + const enableConnectionStringLogin = !userContext.features.disableConnectionStringLogin; return (
@@ -46,7 +48,7 @@ export const ConnectExplorer: React.FunctionComponent = ({ Azure Cosmos DB

Welcome to Azure Cosmos DB

- {isFormVisible ? ( + {isFormVisible && enableConnectionStringLogin ? (
{ @@ -89,9 +91,11 @@ export const ConnectExplorer: React.FunctionComponent = ({ ) : (
-

- Connect to your account with connection string -

+ {enableConnectionStringLogin && ( +

+ Connect to your account with connection string +

+ )}
)}
diff --git a/src/Platform/Hosted/extractFeatures.ts b/src/Platform/Hosted/extractFeatures.ts index 3e34ced0b..d6f8c8538 100644 --- a/src/Platform/Hosted/extractFeatures.ts +++ b/src/Platform/Hosted/extractFeatures.ts @@ -41,6 +41,7 @@ export type Features = { readonly enableCopilotFullSchema: boolean; readonly copilotChatFixedMonacoEditorHeight: boolean; readonly enablePriorityBasedExecution: boolean; + readonly disableConnectionStringLogin: boolean; // can be set via both flight and feature flag autoscaleDefault: boolean; @@ -114,6 +115,7 @@ export function extractFeatures(given = new URLSearchParams(window.location.sear enableCopilotFullSchema: "true" === get("enablecopilotfullschema", "true"), copilotChatFixedMonacoEditorHeight: "true" === get("copilotchatfixedmonacoeditorheight"), enablePriorityBasedExecution: "true" === get("enableprioritybasedexecution"), + disableConnectionStringLogin: "true" === get("disableconnectionstringlogin"), }; } diff --git a/web.config b/web.config index 752b01196..9d9ff2619 100644 --- a/web.config +++ b/web.config @@ -12,7 +12,7 @@ - +