Compare commits

..

3 Commits

Author SHA1 Message Date
Asier Isayas
db719cc898 fix old backend 2024-07-01 12:21:12 -04:00
Asier Isayas
3e4454e9e5 fixed mongo endpoint 2024-06-26 14:03:37 -04:00
Asier Isayas
13df1e8da0 testing for legacy mongo shell 2024-06-21 12:46:47 -04:00
23 changed files with 64 additions and 111 deletions

View File

@@ -133,7 +133,6 @@ export enum MongoBackendEndpointType {
export class BackendApi { export class BackendApi {
public static readonly GenerateToken: string = "GenerateToken"; public static readonly GenerateToken: string = "GenerateToken";
public static readonly PortalSettings: string = "PortalSettings"; public static readonly PortalSettings: string = "PortalSettings";
public static readonly AccountRestrictions: string = "AccountRestrictions";
} }
export class PortalBackendEndpoints { export class PortalBackendEndpoints {

View File

@@ -20,6 +20,7 @@ export const tokenProvider = async (requestInfo: Cosmos.RequestInfo) => {
if (userContext.features.enableAadDataPlane && userContext.aadToken) { if (userContext.features.enableAadDataPlane && userContext.aadToken) {
const AUTH_PREFIX = `type=aad&ver=1.0&sig=`; const AUTH_PREFIX = `type=aad&ver=1.0&sig=`;
const authorizationToken = `${AUTH_PREFIX}${userContext.aadToken}`; const authorizationToken = `${AUTH_PREFIX}${userContext.aadToken}`;
console.log(authorizationToken)
return authorizationToken; return authorizationToken;
} }

View File

@@ -1,7 +1,6 @@
import { FabricMessageTypes } from "Contracts/FabricMessageTypes"; import { FabricMessageTypes } from "Contracts/FabricMessageTypes";
import Q from "q"; import Q from "q";
import * as _ from "underscore"; import * as _ from "underscore";
import * as Logger from "../Common/Logger";
import { MessageTypes } from "../Contracts/ExplorerContracts"; import { MessageTypes } from "../Contracts/ExplorerContracts";
import { getDataExplorerWindow } from "../Utils/WindowUtils"; import { getDataExplorerWindow } from "../Utils/WindowUtils";
import * as Constants from "./Constants"; import * as Constants from "./Constants";
@@ -97,18 +96,10 @@ const _sendMessage = (message: any): void => {
const portalChildWindow = getDataExplorerWindow(window) || window; const portalChildWindow = getDataExplorerWindow(window) || window;
if (portalChildWindow === window) { if (portalChildWindow === window) {
// Current window is a child of portal, send message to portal window // Current window is a child of portal, send message to portal window
if (portalChildWindow.document.referrer) { portalChildWindow.parent.postMessage(message, portalChildWindow.document.referrer || "*");
portalChildWindow.parent.postMessage(message, portalChildWindow.document.referrer);
} else {
Logger.logError("Iframe failed to send message to portal", "MessageHandler");
}
} else { } else {
// Current window is not a child of portal, send message to the child window instead (which is data explorer) // Current window is not a child of portal, send message to the child window instead (which is data explorer)
if (portalChildWindow.location.origin) { portalChildWindow.postMessage(message, portalChildWindow.location.origin || "*");
portalChildWindow.postMessage(message, portalChildWindow.location.origin);
} else {
Logger.logError("Iframe failed to send message to data explorer", "MessageHandler");
}
} }
} }
}; };

View File

@@ -70,7 +70,7 @@ export function queryDocuments(
if (!useMongoProxyEndpoint("resourcelist") || !useMongoProxyEndpoint("queryDocuments")) { if (!useMongoProxyEndpoint("resourcelist") || !useMongoProxyEndpoint("queryDocuments")) {
return queryDocuments_ToBeDeprecated(databaseId, collection, isResourceList, query, continuationToken); return queryDocuments_ToBeDeprecated(databaseId, collection, isResourceList, query, continuationToken);
} }
console.log(configContext.MONGO_PROXY_ENDPOINT)
const { databaseAccount } = userContext; const { databaseAccount } = userContext;
const resourceEndpoint = databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint; const resourceEndpoint = databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint;
const params = { const params = {

View File

@@ -2,6 +2,7 @@ import { CosmosClient } from "@azure/cosmos";
import { sampleDataClient } from "Common/SampleDataClient"; import { sampleDataClient } from "Common/SampleDataClient";
import { userContext } from "UserContext"; import { userContext } from "UserContext";
import * as DataModels from "../../Contracts/DataModels"; import * as DataModels from "../../Contracts/DataModels";
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { client } from "../CosmosClient"; import { client } from "../CosmosClient";
import { handleError } from "../ErrorHandlingUtils"; import { handleError } from "../ErrorHandlingUtils";
@@ -30,6 +31,7 @@ export async function readCollectionInternal(
collectionId: string, collectionId: string,
): Promise<DataModels.Collection> { ): Promise<DataModels.Collection> {
let collection: DataModels.Collection; let collection: DataModels.Collection;
const clearMessage = logConsoleProgress(`Querying container ${collectionId}`);
try { try {
const response = await cosmosClient.database(databaseId).container(collectionId).read(); const response = await cosmosClient.database(databaseId).container(collectionId).read();
collection = response.resource as DataModels.Collection; collection = response.resource as DataModels.Collection;
@@ -37,5 +39,6 @@ export async function readCollectionInternal(
handleError(error, "ReadCollection", `Error while querying container ${collectionId}`); handleError(error, "ReadCollection", `Error while querying container ${collectionId}`);
throw error; throw error;
} }
clearMessage();
return collection; return collection;
} }

View File

@@ -107,7 +107,7 @@ let configContext: Readonly<ConfigContext> = {
JUNO_ENDPOINT: JunoEndpoints.Prod, JUNO_ENDPOINT: JunoEndpoints.Prod,
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com", BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com",
PORTAL_BACKEND_ENDPOINT: PortalBackendEndpoints.Prod, PORTAL_BACKEND_ENDPOINT: PortalBackendEndpoints.Prod,
MONGO_PROXY_ENDPOINT: MongoProxyEndpoints.Prod, MONGO_PROXY_ENDPOINT: MongoProxyEndpoints.Local,
NEW_MONGO_APIS: [ NEW_MONGO_APIS: [
"resourcelist", "resourcelist",
"queryDocuments", "queryDocuments",
@@ -200,7 +200,7 @@ if (process.env.NODE_ENV === "development") {
PROXY_PATH: "/proxy", PROXY_PATH: "/proxy",
EMULATOR_ENDPOINT: "https://localhost:8081", EMULATOR_ENDPOINT: "https://localhost:8081",
PORTAL_BACKEND_ENDPOINT: PortalBackendEndpoints.Mpac, PORTAL_BACKEND_ENDPOINT: PortalBackendEndpoints.Mpac,
MONGO_PROXY_ENDPOINT: MongoProxyEndpoints.Mpac, // MONGO_PROXY_ENDPOINT: MongoProxyEndpoints.Mpac,
CASSANDRA_PROXY_ENDPOINT: CassandraProxyEndpoints.Mpac, CASSANDRA_PROXY_ENDPOINT: CassandraProxyEndpoints.Mpac,
}); });
} }
@@ -255,3 +255,4 @@ export async function initializeConfiguration(): Promise<ConfigContext> {
} }
export { configContext }; export { configContext };

View File

@@ -52,15 +52,12 @@ export const createDatabaseContextMenu = (container: Explorer, databaseId: strin
if (userContext.apiType !== "Tables" || userContext.features.enableSDKoperations) { if (userContext.apiType !== "Tables" || userContext.features.enableSDKoperations) {
items.push({ items.push({
iconSrc: DeleteDatabaseIcon, iconSrc: DeleteDatabaseIcon,
onClick: (lastFocusedElement?: React.RefObject<HTMLElement>) => onClick: () =>
useSidePanel useSidePanel
.getState() .getState()
.openSidePanel( .openSidePanel(
"Delete " + getDatabaseName(), "Delete " + getDatabaseName(),
<DeleteDatabaseConfirmationPanel <DeleteDatabaseConfirmationPanel refreshDatabases={() => container.refreshAllDatabases()} />,
lastFocusedElement={lastFocusedElement}
refreshDatabases={() => container.refreshAllDatabases()}
/>,
), ),
label: `Delete ${getDatabaseName()}`, label: `Delete ${getDatabaseName()}`,
styleClass: "deleteDatabaseMenuItem", styleClass: "deleteDatabaseMenuItem",
@@ -135,16 +132,13 @@ export const createCollectionContextMenuButton = (
if (configContext.platform !== Platform.Fabric) { if (configContext.platform !== Platform.Fabric) {
items.push({ items.push({
iconSrc: DeleteCollectionIcon, iconSrc: DeleteCollectionIcon,
onClick: (lastFocusedElement?: React.RefObject<HTMLElement>) => { onClick: () => {
useSelectedNode.getState().setSelectedNode(selectedCollection); useSelectedNode.getState().setSelectedNode(selectedCollection);
useSidePanel useSidePanel
.getState() .getState()
.openSidePanel( .openSidePanel(
"Delete " + getCollectionName(), "Delete " + getCollectionName(),
<DeleteCollectionConfirmationPane <DeleteCollectionConfirmationPane refreshDatabases={() => container.refreshAllDatabases()} />,
lastFocusedElement={lastFocusedElement}
refreshDatabases={() => container.refreshAllDatabases()}
/>,
); );
}, },
label: `Delete ${getCollectionName()}`, label: `Delete ${getCollectionName()}`,

View File

@@ -21,7 +21,7 @@ import { useCallback } from "react";
export interface TreeNodeMenuItem { export interface TreeNodeMenuItem {
label: string; label: string;
onClick: (value?: React.RefObject<HTMLElement>) => void; onClick: () => void;
iconSrc?: string; iconSrc?: string;
isDisabled?: boolean; isDisabled?: boolean;
styleClass?: string; styleClass?: string;
@@ -73,7 +73,6 @@ export const TreeNodeComponent: React.FC<TreeNodeComponentProps> = ({
treeNodeId, treeNodeId,
}: TreeNodeComponentProps): JSX.Element => { }: TreeNodeComponentProps): JSX.Element => {
const [isLoading, setIsLoading] = React.useState<boolean>(false); const [isLoading, setIsLoading] = React.useState<boolean>(false);
const contextMenuRef = React.useRef<HTMLButtonElement>(null);
const getSortedChildren = (treeNode: TreeNode): TreeNode[] => { const getSortedChildren = (treeNode: TreeNode): TreeNode[] => {
if (!treeNode || !treeNode.children) { if (!treeNode || !treeNode.children) {
@@ -140,7 +139,7 @@ export const TreeNodeComponent: React.FC<TreeNodeComponentProps> = ({
data-test={`TreeNode/ContextMenuItem:${menuItem.label}`} data-test={`TreeNode/ContextMenuItem:${menuItem.label}`}
disabled={menuItem.isDisabled} disabled={menuItem.isDisabled}
key={menuItem.label} key={menuItem.label}
onClick={() => menuItem.onClick(contextMenuRef)} onClick={menuItem.onClick}
> >
{menuItem.label} {menuItem.label}
</MenuItem> </MenuItem>
@@ -164,7 +163,6 @@ export const TreeNodeComponent: React.FC<TreeNodeComponentProps> = ({
aria-label="More options" aria-label="More options"
data-test="TreeNode/ContextMenuTrigger" data-test="TreeNode/ContextMenuTrigger"
appearance="subtle" appearance="subtle"
ref={contextMenuRef}
icon={<MoreHorizontal20Regular />} icon={<MoreHorizontal20Regular />}
/> />
</MenuTrigger> </MenuTrigger>

View File

@@ -1560,14 +1560,14 @@ exports[`TreeNodeComponent renders a node with a menu 1`] = `
<MenuList> <MenuList>
<MenuItem <MenuItem
data-test="TreeNode/ContextMenuItem:enabledItem" data-test="TreeNode/ContextMenuItem:enabledItem"
onClick={[Function]} onClick={[MockFunction enabledItemClick]}
> >
enabledItem enabledItem
</MenuItem> </MenuItem>
<MenuItem <MenuItem
data-test="TreeNode/ContextMenuItem:disabledItem" data-test="TreeNode/ContextMenuItem:disabledItem"
disabled={true} disabled={true}
onClick={[Function]} onClick={[MockFunction disabledItemClick]}
> >
disabledItem disabledItem
</MenuItem> </MenuItem>
@@ -1604,7 +1604,7 @@ exports[`TreeNodeComponent renders a node with a menu 1`] = `
<MenuItem <MenuItem
data-test="TreeNode/ContextMenuItem:enabledItem" data-test="TreeNode/ContextMenuItem:enabledItem"
key="enabledItem" key="enabledItem"
onClick={[Function]} onClick={[MockFunction enabledItemClick]}
> >
enabledItem enabledItem
</MenuItem> </MenuItem>
@@ -1612,7 +1612,7 @@ exports[`TreeNodeComponent renders a node with a menu 1`] = `
data-test="TreeNode/ContextMenuItem:disabledItem" data-test="TreeNode/ContextMenuItem:disabledItem"
disabled={true} disabled={true}
key="disabledItem" key="disabledItem"
onClick={[Function]} onClick={[MockFunction disabledItemClick]}
> >
disabledItem disabledItem
</MenuItem> </MenuItem>

View File

@@ -1,5 +1,4 @@
import { Link } from "@fluentui/react/lib/Link"; import { Link } from "@fluentui/react/lib/Link";
import { isPublicInternetAccessAllowed } from "Common/DatabaseAccountUtility";
import { sendMessage } from "Common/MessageHandler"; import { sendMessage } from "Common/MessageHandler";
import { Platform, configContext } from "ConfigContext"; import { Platform, configContext } from "ConfigContext";
import { MessageTypes } from "Contracts/ExplorerContracts"; import { MessageTypes } from "Contracts/ExplorerContracts";
@@ -1124,7 +1123,7 @@ export default class Explorer {
useNotebook.getState().setIsNotebookEnabled(isNotebookEnabled); useNotebook.getState().setIsNotebookEnabled(isNotebookEnabled);
useNotebook useNotebook
.getState() .getState()
.setIsShellEnabled(useNotebook.getState().isPhoenixFeatures && isPublicInternetAccessAllowed()); .setIsShellEnabled(false);
TelemetryProcessor.trace(Action.NotebookEnabled, ActionModifiers.Mark, { TelemetryProcessor.trace(Action.NotebookEnabled, ActionModifiers.Mark, {
isNotebookEnabled, isNotebookEnabled,

View File

@@ -162,7 +162,7 @@ export const addRootChildToGraph = (
* @param value * @param value
*/ */
export const escapeDoubleQuotes = (value: string): string => { export const escapeDoubleQuotes = (value: string): string => {
return value === undefined ? value : value.replace(/\\/g, "\\\\").replace(/"/g, '\\"'); return value === undefined ? value : value.replace(/"/g, '\\"');
}; };
/** /**
@@ -186,5 +186,5 @@ export const getQuotedPropValue = (ip: ViewModels.InputPropertyValue): string =>
* @param value * @param value
*/ */
export const escapeSingleQuotes = (value: string): string => { export const escapeSingleQuotes = (value: string): string => {
return value === undefined ? value : value.replace(/\\/g, "\\\\").replace(/'/g, "\\'"); return value === undefined ? value : value.replace(/'/g, "\\'");
}; };

View File

@@ -576,7 +576,9 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
</TooltipHost> </TooltipHost>
</Stack> </Stack>
<Text variant="small">{this.getPartitionKeySubtext()}</Text> <Text variant="small" aria-label="pkDescription">
{this.getPartitionKeySubtext()}
</Text>
<input <input
type="text" type="text"

View File

@@ -264,7 +264,7 @@ export const ChangePartitionKeyPane: React.FC<ChangePartitionKeyPaneProps> = ({
</TooltipHost> </TooltipHost>
</Stack> </Stack>
<Text variant="small"> <Text variant="small" aria-label="pkDescription">
{getPartitionKeySubtext(userContext.features.partitionKeyDefault, userContext.apiType)} {getPartitionKeySubtext(userContext.features.partitionKeyDefault, userContext.apiType)}
</Text> </Text>

View File

@@ -52,9 +52,7 @@ describe("Delete Collection Confirmation Pane", () => {
describe("shouldRecordFeedback()", () => { describe("shouldRecordFeedback()", () => {
it("should return true if last collection and database does not have shared throughput else false", () => { it("should return true if last collection and database does not have shared throughput else false", () => {
const wrapper = shallow( const wrapper = shallow(<DeleteCollectionConfirmationPane refreshDatabases={() => undefined} />);
<DeleteCollectionConfirmationPane refreshDatabases={() => undefined} lastFocusedElement={undefined} />,
);
expect(wrapper.exists(".deleteCollectionFeedback")).toBe(false); expect(wrapper.exists(".deleteCollectionFeedback")).toBe(false);
const database = { id: ko.observable("testDB") } as Database; const database = { id: ko.observable("testDB") } as Database;
@@ -111,9 +109,7 @@ describe("Delete Collection Confirmation Pane", () => {
}); });
it("should call delete collection", () => { it("should call delete collection", () => {
const wrapper = mount( const wrapper = mount(<DeleteCollectionConfirmationPane refreshDatabases={() => undefined} />);
<DeleteCollectionConfirmationPane refreshDatabases={() => undefined} lastFocusedElement={undefined} />,
);
expect(wrapper).toMatchSnapshot(); expect(wrapper).toMatchSnapshot();
expect(wrapper.exists("#confirmCollectionId")).toBe(true); expect(wrapper.exists("#confirmCollectionId")).toBe(true);
@@ -130,9 +126,7 @@ describe("Delete Collection Confirmation Pane", () => {
}); });
it("should record feedback", async () => { it("should record feedback", async () => {
const wrapper = mount( const wrapper = mount(<DeleteCollectionConfirmationPane refreshDatabases={() => undefined} />);
<DeleteCollectionConfirmationPane refreshDatabases={() => undefined} lastFocusedElement={undefined} />,
);
expect(wrapper.exists("#confirmCollectionId")).toBe(true); expect(wrapper.exists("#confirmCollectionId")).toBe(true);
wrapper wrapper
.find("#confirmCollectionId") .find("#confirmCollectionId")

View File

@@ -12,19 +12,17 @@ import { getCollectionName } from "Utils/APITypeUtils";
import * as NotificationConsoleUtils from "Utils/NotificationConsoleUtils"; import * as NotificationConsoleUtils from "Utils/NotificationConsoleUtils";
import { useSidePanel } from "hooks/useSidePanel"; import { useSidePanel } from "hooks/useSidePanel";
import { useTabs } from "hooks/useTabs"; import { useTabs } from "hooks/useTabs";
import React, { FunctionComponent, useEffect, useState } from "react"; import React, { FunctionComponent, useState } from "react";
import { useDatabases } from "../../useDatabases"; import { useDatabases } from "../../useDatabases";
import { useSelectedNode } from "../../useSelectedNode"; import { useSelectedNode } from "../../useSelectedNode";
import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneForm"; import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneForm";
export interface DeleteCollectionConfirmationPaneProps { export interface DeleteCollectionConfirmationPaneProps {
refreshDatabases: () => Promise<void>; refreshDatabases: () => Promise<void>;
lastFocusedElement: React.MutableRefObject<HTMLElement>;
} }
export const DeleteCollectionConfirmationPane: FunctionComponent<DeleteCollectionConfirmationPaneProps> = ({ export const DeleteCollectionConfirmationPane: FunctionComponent<DeleteCollectionConfirmationPaneProps> = ({
refreshDatabases, refreshDatabases,
lastFocusedElement,
}: DeleteCollectionConfirmationPaneProps) => { }: DeleteCollectionConfirmationPaneProps) => {
const closeSidePanel = useSidePanel((state) => state.closeSidePanel); const closeSidePanel = useSidePanel((state) => state.closeSidePanel);
const [deleteCollectionFeedback, setDeleteCollectionFeedback] = useState<string>(""); const [deleteCollectionFeedback, setDeleteCollectionFeedback] = useState<string>("");
@@ -37,7 +35,7 @@ export const DeleteCollectionConfirmationPane: FunctionComponent<DeleteCollectio
const collectionName = getCollectionName().toLocaleLowerCase(); const collectionName = getCollectionName().toLocaleLowerCase();
const paneTitle = "Delete " + collectionName; const paneTitle = "Delete " + collectionName;
const lastItemElement = lastFocusedElement?.current;
const onSubmit = async (): Promise<void> => { const onSubmit = async (): Promise<void> => {
const collection = useSelectedNode.getState().findSelectedCollection(); const collection = useSelectedNode.getState().findSelectedCollection();
if (!collection || inputCollectionName !== collection.id()) { if (!collection || inputCollectionName !== collection.id()) {
@@ -113,14 +111,6 @@ export const DeleteCollectionConfirmationPane: FunctionComponent<DeleteCollectio
}; };
const confirmContainer = `Confirm by typing the ${collectionName.toLowerCase()} id`; const confirmContainer = `Confirm by typing the ${collectionName.toLowerCase()} id`;
const reasonInfo = `Help us improve Azure Cosmos DB! What is the reason why you are deleting this ${collectionName}?`; const reasonInfo = `Help us improve Azure Cosmos DB! What is the reason why you are deleting this ${collectionName}?`;
useEffect(() => {
return () => {
if (lastItemElement) {
lastItemElement.focus();
}
};
}, [lastItemElement]);
return ( return (
<RightPaneForm {...props}> <RightPaneForm {...props}>
<div className="panelFormWrapper"> <div className="panelFormWrapper">

View File

@@ -49,9 +49,7 @@ describe("Delete Database Confirmation Pane", () => {
}); });
it("shouldRecordFeedback() should return true if last non empty database or is last database that has shared throughput", () => { it("shouldRecordFeedback() should return true if last non empty database or is last database that has shared throughput", () => {
const wrapper = shallow( const wrapper = shallow(<DeleteDatabaseConfirmationPanel refreshDatabases={() => undefined} />);
<DeleteDatabaseConfirmationPanel refreshDatabases={() => undefined} lastFocusedElement={undefined} />,
);
expect(wrapper.exists(".deleteDatabaseFeedback")).toBe(true); expect(wrapper.exists(".deleteDatabaseFeedback")).toBe(true);
useDatabases.getState().addDatabases([database]); useDatabases.getState().addDatabases([database]);
@@ -61,9 +59,7 @@ describe("Delete Database Confirmation Pane", () => {
}); });
it("Should call delete database", () => { it("Should call delete database", () => {
const wrapper = mount( const wrapper = mount(<DeleteDatabaseConfirmationPanel refreshDatabases={() => undefined} />);
<DeleteDatabaseConfirmationPanel refreshDatabases={() => undefined} lastFocusedElement={undefined} />,
);
expect(wrapper).toMatchSnapshot(); expect(wrapper).toMatchSnapshot();
expect(wrapper.exists("#confirmDatabaseId")).toBe(true); expect(wrapper.exists("#confirmDatabaseId")).toBe(true);
@@ -78,9 +74,7 @@ describe("Delete Database Confirmation Pane", () => {
}); });
it("should record feedback", async () => { it("should record feedback", async () => {
const wrapper = mount( const wrapper = mount(<DeleteDatabaseConfirmationPanel refreshDatabases={() => undefined} />);
<DeleteDatabaseConfirmationPanel refreshDatabases={() => undefined} lastFocusedElement={undefined} />,
);
expect(wrapper.exists("#confirmDatabaseId")).toBe(true); expect(wrapper.exists("#confirmDatabaseId")).toBe(true);
wrapper wrapper
.find("#confirmDatabaseId") .find("#confirmDatabaseId")

View File

@@ -13,7 +13,7 @@ import { getDatabaseName } from "Utils/APITypeUtils";
import { logConsoleError } from "Utils/NotificationConsoleUtils"; import { logConsoleError } from "Utils/NotificationConsoleUtils";
import { useSidePanel } from "hooks/useSidePanel"; import { useSidePanel } from "hooks/useSidePanel";
import { useTabs } from "hooks/useTabs"; import { useTabs } from "hooks/useTabs";
import React, { FunctionComponent, useEffect, useState } from "react"; import React, { FunctionComponent, useState } from "react";
import { useDatabases } from "../useDatabases"; import { useDatabases } from "../useDatabases";
import { useSelectedNode } from "../useSelectedNode"; import { useSelectedNode } from "../useSelectedNode";
import { PanelInfoErrorComponent, PanelInfoErrorProps } from "./PanelInfoErrorComponent"; import { PanelInfoErrorComponent, PanelInfoErrorProps } from "./PanelInfoErrorComponent";
@@ -21,12 +21,10 @@ import { RightPaneForm, RightPaneFormProps } from "./RightPaneForm/RightPaneForm
interface DeleteDatabaseConfirmationPanelProps { interface DeleteDatabaseConfirmationPanelProps {
refreshDatabases: () => Promise<void>; refreshDatabases: () => Promise<void>;
lastFocusedElement: React.MutableRefObject<HTMLElement>;
} }
export const DeleteDatabaseConfirmationPanel: FunctionComponent<DeleteDatabaseConfirmationPanelProps> = ({ export const DeleteDatabaseConfirmationPanel: FunctionComponent<DeleteDatabaseConfirmationPanelProps> = ({
refreshDatabases, refreshDatabases,
lastFocusedElement,
}: DeleteDatabaseConfirmationPanelProps): JSX.Element => { }: DeleteDatabaseConfirmationPanelProps): JSX.Element => {
const closeSidePanel = useSidePanel((state) => state.closeSidePanel); const closeSidePanel = useSidePanel((state) => state.closeSidePanel);
const isLastNonEmptyDatabase = useDatabases((state) => state.isLastNonEmptyDatabase); const isLastNonEmptyDatabase = useDatabases((state) => state.isLastNonEmptyDatabase);
@@ -36,7 +34,7 @@ export const DeleteDatabaseConfirmationPanel: FunctionComponent<DeleteDatabaseCo
const [databaseInput, setDatabaseInput] = useState<string>(""); const [databaseInput, setDatabaseInput] = useState<string>("");
const [databaseFeedbackInput, setDatabaseFeedbackInput] = useState<string>(""); const [databaseFeedbackInput, setDatabaseFeedbackInput] = useState<string>("");
const selectedDatabase: Database = useDatabases.getState().findSelectedDatabase(); const selectedDatabase: Database = useDatabases.getState().findSelectedDatabase();
const lastItemElement = lastFocusedElement?.current;
const submit = async (): Promise<void> => { const submit = async (): Promise<void> => {
if (selectedDatabase?.id() && databaseInput !== selectedDatabase.id()) { if (selectedDatabase?.id() && databaseInput !== selectedDatabase.id()) {
setFormError( setFormError(
@@ -128,13 +126,6 @@ export const DeleteDatabaseConfirmationPanel: FunctionComponent<DeleteDatabaseCo
}; };
const confirmDatabase = `Confirm by typing the ${getDatabaseName()} id`; const confirmDatabase = `Confirm by typing the ${getDatabaseName()} id`;
const reasonInfo = `Help us improve Azure Cosmos DB! What is the reason why you are deleting this ${getDatabaseName()}?`; const reasonInfo = `Help us improve Azure Cosmos DB! What is the reason why you are deleting this ${getDatabaseName()}?`;
useEffect(() => {
return () => {
if (lastItemElement) {
lastItemElement.focus();
}
};
}, [lastItemElement]);
return ( return (
<RightPaneForm {...props}> <RightPaneForm {...props}>
{!formError && <PanelInfoErrorComponent {...errorProps} />} {!formError && <PanelInfoErrorComponent {...errorProps} />}

View File

@@ -223,6 +223,7 @@ exports[`AddCollectionPanel should render Default properly 1`] = `
</StyledTooltipHostBase> </StyledTooltipHostBase>
</Stack> </Stack>
<Text <Text
aria-label="pkDescription"
variant="small" variant="small"
/> />
<input <input

View File

@@ -12,6 +12,7 @@ import Explorer from "../../Explorer";
import TabsBase from "../TabsBase"; import TabsBase from "../TabsBase";
import { getMongoShellUrl } from "./getMongoShellUrl"; import { getMongoShellUrl } from "./getMongoShellUrl";
//eslint-disable-next-line //eslint-disable-next-line
class MessageType { class MessageType {
static IframeReady = "iframeready"; static IframeReady = "iframeready";
@@ -56,7 +57,6 @@ export default class MongoShellTabComponent extends Component<
super(props); super(props);
this._logTraces = new Map(); this._logTraces = new Map();
this._useMongoProxyEndpoint = useMongoProxyEndpoint("legacyMongoShell"); this._useMongoProxyEndpoint = useMongoProxyEndpoint("legacyMongoShell");
this.state = { this.state = {
url: getMongoShellUrl(this._useMongoProxyEndpoint), url: getMongoShellUrl(this._useMongoProxyEndpoint),
}; };
@@ -114,11 +114,11 @@ export default class MongoShellTabComponent extends Component<
const accountName = databaseAccount?.name; const accountName = databaseAccount?.name;
const documentEndpoint = databaseAccount?.properties.mongoEndpoint || databaseAccount?.properties.documentEndpoint; const documentEndpoint = databaseAccount?.properties.mongoEndpoint || databaseAccount?.properties.documentEndpoint;
const mongoEndpoint = const mongoEndpoint =
documentEndpoint.substr( documentEndpoint.substr(
Constants.MongoDBAccounts.protocol.length + 3, Constants.MongoDBAccounts.protocol.length + 3,
documentEndpoint.length - documentEndpoint.length -
(Constants.MongoDBAccounts.protocol.length + 2 + Constants.MongoDBAccounts.defaultPort.length), (Constants.MongoDBAccounts.protocol.length + 2 + Constants.MongoDBAccounts.defaultPort.length),
) + Constants.MongoDBAccounts.defaultPort.toString(); ) + Constants.MongoDBAccounts.defaultPort.toString();
const databaseId = this.props.collection.databaseId; const databaseId = this.props.collection.databaseId;
const collectionId = this.props.collection.id(); const collectionId = this.props.collection.id();
const apiEndpoint = this._useMongoProxyEndpoint const apiEndpoint = this._useMongoProxyEndpoint
@@ -132,7 +132,7 @@ export default class MongoShellTabComponent extends Component<
data: { data: {
resourceId: resourceId, resourceId: resourceId,
accountName: accountName, accountName: accountName,
mongoEndpoint: mongoEndpoint, mongoEndpoint: this._useMongoProxyEndpoint ? documentEndpoint : mongoEndpoint,
authorization: authorization, authorization: authorization,
databaseId: databaseId, databaseId: databaseId,
collectionId: collectionId, collectionId: collectionId,
@@ -140,7 +140,7 @@ export default class MongoShellTabComponent extends Component<
apiEndpoint: apiEndpoint, apiEndpoint: apiEndpoint,
}, },
}, },
window.origin, "https://localhost:8080",
); );
} }

View File

@@ -7,5 +7,5 @@ export function getMongoShellUrl(useMongoProxyEndpoint?: boolean): string {
const mongoEndpoint = account?.properties?.mongoEndpoint || account?.properties?.documentEndpoint; const mongoEndpoint = account?.properties?.mongoEndpoint || account?.properties?.documentEndpoint;
const queryString = `resourceId=${resourceId}&accountName=${accountName}&mongoEndpoint=${mongoEndpoint}`; const queryString = `resourceId=${resourceId}&accountName=${accountName}&mongoEndpoint=${mongoEndpoint}`;
return useMongoProxyEndpoint ? `/mongoshell/index.html?${queryString}` : `/mongoshell/indexv2.html?${queryString}`; return useMongoProxyEndpoint ? `https://localhost:8080/index.html?${queryString}` : `https://localhost:8080/indexv2.html?${queryString}`;
} }

View File

@@ -51,18 +51,13 @@ export const fetchEncryptedToken_ToBeDeprecated = async (connectionString: strin
export const isAccountRestrictedForConnectionStringLogin = async (connectionString: string): Promise<boolean> => { export const isAccountRestrictedForConnectionStringLogin = async (connectionString: string): Promise<boolean> => {
const headers = new Headers(); const headers = new Headers();
headers.append(HttpHeaders.connectionString, connectionString); headers.append(HttpHeaders.connectionString, connectionString);
const url = configContext.BACKEND_ENDPOINT + "/api/guest/accountrestrictions/checkconnectionstringlogin";
const backendEndpoint: string = useNewPortalBackendEndpoint(BackendApi.PortalSettings)
? configContext.PORTAL_BACKEND_ENDPOINT
: configContext.BACKEND_ENDPOINT;
const url = backendEndpoint + "/api/guest/accountrestrictions/checkconnectionstringlogin";
const response = await fetch(url, { headers, method: "POST" }); const response = await fetch(url, { headers, method: "POST" });
if (!response.ok) { if (!response.ok) {
throw response; throw response;
} }
return (await response.text()).toLowerCase() === "true"; return (await response.text()) === "True";
}; };
export const ConnectExplorer: React.FunctionComponent<Props> = ({ export const ConnectExplorer: React.FunctionComponent<Props> = ({

View File

@@ -164,7 +164,6 @@ export function useNewPortalBackendEndpoint(backendApi: string): boolean {
PortalBackendEndpoints.Mpac, PortalBackendEndpoints.Mpac,
PortalBackendEndpoints.Prod, PortalBackendEndpoints.Prod,
], ],
[BackendApi.AccountRestrictions]: [PortalBackendEndpoints.Development, PortalBackendEndpoints.Mpac],
}; };
if (!newBackendApiEnvironmentMap[backendApi] || !configContext.PORTAL_BACKEND_ENDPOINT) { if (!newBackendApiEnvironmentMap[backendApi] || !configContext.PORTAL_BACKEND_ENDPOINT) {

View File

@@ -5,20 +5,21 @@ export function isInvalidParentFrameOrigin(event: MessageEvent): boolean {
} }
function isValidOrigin(allowedOrigins: ReadonlyArray<string>, event: MessageEvent): boolean { function isValidOrigin(allowedOrigins: ReadonlyArray<string>, event: MessageEvent): boolean {
const eventOrigin = (event && event.origin) || ""; return true;
const windowOrigin = (window && window.origin) || ""; // const eventOrigin = (event && event.origin) || "";
if (eventOrigin === windowOrigin) { // const windowOrigin = (window && window.origin) || "";
return true; // if (eventOrigin === windowOrigin) {
} // return true;
// }
for (const origin of allowedOrigins) { // for (const origin of allowedOrigins) {
const result = new RegExp(origin).test(eventOrigin); // const result = new RegExp(origin).test(eventOrigin);
if (result) { // if (result) {
return true; // return true;
} // }
} // }
console.error(`Invalid parent frame origin detected: ${eventOrigin}`); // console.error(`Invalid parent frame origin detected: ${eventOrigin}`);
return false; // return false;
} }
export function shouldProcessMessage(event: MessageEvent): boolean { export function shouldProcessMessage(event: MessageEvent): boolean {