mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2026-01-12 06:00:02 +00:00
Compare commits
3 Commits
defect2392
...
users/v-pr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
863fdb4f7e | ||
|
|
c9bd0b6c7f | ||
|
|
8ce7aaa728 |
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -23,6 +23,5 @@
|
||||
"source.fixAll.eslint": true,
|
||||
"source.organizeImports": true
|
||||
},
|
||||
"typescript.preferences.importModuleSpecifier": "non-relative",
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
"typescript.preferences.importModuleSpecifier": "non-relative"
|
||||
}
|
||||
|
||||
@@ -199,7 +199,6 @@
|
||||
"pack:fast": "webpack --mode development --progress",
|
||||
"copyToConsumers": "node copyToConsumers",
|
||||
"test": "rimraf coverage && jest",
|
||||
"test:debug": "jest --runInBand",
|
||||
"test:e2e": "jest -c ./jest.config.playwright.js --detectOpenHandles",
|
||||
"watch": "npm run start",
|
||||
"wait-for-server": "wait-on -t 240000 -i 5000 -v https-get://0.0.0.0:1234/",
|
||||
|
||||
@@ -68,7 +68,7 @@ let configContext: Readonly<ConfigContext> = {
|
||||
ARM_AUTH_AREA: "https://management.azure.com/",
|
||||
ARM_ENDPOINT: "https://management.azure.com/",
|
||||
ARM_API_VERSION: "2016-06-01",
|
||||
GRAPH_ENDPOINT: "https://graph.microsoft.com",
|
||||
GRAPH_ENDPOINT: "https://graph.windows.net",
|
||||
GRAPH_API_VERSION: "1.6",
|
||||
ARCADIA_ENDPOINT: "https://workspaceartifacts.projectarcadia.net",
|
||||
ARCADIA_LIVY_ENDPOINT_DNS_ZONE: "dev.azuresynapse.net",
|
||||
|
||||
@@ -32,20 +32,6 @@ const testCassandraAccount: DataModels.DatabaseAccount = {
|
||||
},
|
||||
};
|
||||
|
||||
const testPostgresAccount: DataModels.DatabaseAccount = {
|
||||
...testAccount,
|
||||
properties: {
|
||||
postgresqlEndpoint: "https://testPostgresEndpoint.azure.com/",
|
||||
},
|
||||
};
|
||||
|
||||
const testVCoreMongoAccount: DataModels.DatabaseAccount = {
|
||||
...testAccount,
|
||||
properties: {
|
||||
vcoreMongoEndpoint: "https://testVCoreMongoEndpoint.azure.com/",
|
||||
},
|
||||
};
|
||||
|
||||
const testNotebookServerInfo: DataModels.NotebookWorkspaceConnectionInfo = {
|
||||
authToken: "authToken",
|
||||
notebookServerEndpoint: "https://testNotebookServerEndpoint.azure.com",
|
||||
@@ -64,18 +50,6 @@ const testCassandraNotebookServerInfo: DataModels.NotebookWorkspaceConnectionInf
|
||||
forwardingId: "Id",
|
||||
};
|
||||
|
||||
const testPostgresNotebookServerInfo: DataModels.NotebookWorkspaceConnectionInfo = {
|
||||
authToken: "authToken",
|
||||
notebookServerEndpoint: "https://testNotebookServerEndpoint.azure.com/postgresql",
|
||||
forwardingId: "Id",
|
||||
};
|
||||
|
||||
const testVCoreMongoNotebookServerInfo: DataModels.NotebookWorkspaceConnectionInfo = {
|
||||
authToken: "authToken",
|
||||
notebookServerEndpoint: "https://testNotebookServerEndpoint.azure.com/mongovcore",
|
||||
forwardingId: "Id",
|
||||
};
|
||||
|
||||
describe("NotebookTerminalComponent", () => {
|
||||
it("renders terminal", () => {
|
||||
const props: NotebookTerminalComponentProps = {
|
||||
@@ -120,27 +94,4 @@ describe("NotebookTerminalComponent", () => {
|
||||
const wrapper = shallow(<NotebookTerminalComponent {...props} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("renders Postgres shell", () => {
|
||||
const props: NotebookTerminalComponentProps = {
|
||||
databaseAccount: testPostgresAccount,
|
||||
notebookServerInfo: testPostgresNotebookServerInfo,
|
||||
tabId: undefined,
|
||||
};
|
||||
|
||||
const wrapper = shallow(<NotebookTerminalComponent {...props} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("renders vCore Mongo shell", () => {
|
||||
const props: NotebookTerminalComponentProps = {
|
||||
databaseAccount: testVCoreMongoAccount,
|
||||
notebookServerInfo: testVCoreMongoNotebookServerInfo,
|
||||
tabId: undefined,
|
||||
username: "username",
|
||||
};
|
||||
|
||||
const wrapper = shallow(<NotebookTerminalComponent {...props} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,17 +1,5 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`NotebookTerminalComponent renders Postgres shell 1`] = `
|
||||
<div
|
||||
className="notebookTerminalContainer"
|
||||
>
|
||||
<iframe
|
||||
onLoad={[Function]}
|
||||
src="terminal.html"
|
||||
title="Terminal to Notebook Server"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`NotebookTerminalComponent renders cassandra shell 1`] = `
|
||||
<div
|
||||
className="notebookTerminalContainer"
|
||||
@@ -59,15 +47,3 @@ exports[`NotebookTerminalComponent renders terminal 1`] = `
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`NotebookTerminalComponent renders vCore Mongo shell 1`] = `
|
||||
<div
|
||||
className="notebookTerminalContainer"
|
||||
>
|
||||
<iframe
|
||||
onLoad={[Function]}
|
||||
src="terminal.html"
|
||||
title="Terminal to Notebook Server"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -343,31 +343,6 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("Open Postgres and vCore Mongo buttons", () => {
|
||||
const openPostgresShellButtonLabel = "Open PSQL shell";
|
||||
const openVCoreMongoShellButtonLabel = "Open MongoDB (vcore) shell";
|
||||
|
||||
beforeAll(() => {
|
||||
mockExplorer = {} as Explorer;
|
||||
});
|
||||
|
||||
it("creates Postgres shell button", () => {
|
||||
const buttons = CommandBarComponentButtonFactory.createPostgreButtons(mockExplorer);
|
||||
const openPostgresShellButton = buttons.find(
|
||||
(button) => button.commandButtonLabel === openPostgresShellButtonLabel
|
||||
);
|
||||
expect(openPostgresShellButton).toBeDefined();
|
||||
});
|
||||
|
||||
it("creates vCore Mongo shell button", () => {
|
||||
const buttons = CommandBarComponentButtonFactory.createVCoreMongoButtons(mockExplorer);
|
||||
const openVCoreMongoShellButton = buttons.find(
|
||||
(button) => button.commandButtonLabel === openVCoreMongoShellButtonLabel
|
||||
);
|
||||
expect(openVCoreMongoShellButton).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("GitHub buttons", () => {
|
||||
const connectToGitHubBtnLabel = "Connect to GitHub";
|
||||
const manageGitHubSettingsBtnLabel = "Manage GitHub settings";
|
||||
|
||||
@@ -1,15 +1,8 @@
|
||||
import {
|
||||
DetailsList,
|
||||
DetailsListLayoutMode,
|
||||
DirectionalHint,
|
||||
IColumn,
|
||||
SelectionMode,
|
||||
TooltipHost,
|
||||
} from "@fluentui/react";
|
||||
import { DetailsList, DetailsListLayoutMode, IColumn, SelectionMode } from "@fluentui/react";
|
||||
import { Upload } from "Common/Upload/Upload";
|
||||
import { UploadDetailsRecord } from "Contracts/ViewModels";
|
||||
import { logConsoleError } from "Utils/NotificationConsoleUtils";
|
||||
import React, { ChangeEvent, FunctionComponent, useState } from "react";
|
||||
import { logConsoleError } from "Utils/NotificationConsoleUtils";
|
||||
import { getErrorMessage } from "../../Tables/Utilities";
|
||||
import { useSelectedNode } from "../../useSelectedNode";
|
||||
import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneForm";
|
||||
@@ -81,21 +74,12 @@ export const UploadItemsPane: FunctionComponent = () => {
|
||||
];
|
||||
|
||||
const _renderItemColumn = (item: UploadDetailsRecord, index: number, column: IColumn) => {
|
||||
let fieldContent: string;
|
||||
const tooltipId = `tooltip-${index}-${column.key}`;
|
||||
|
||||
switch (column.key) {
|
||||
case "status":
|
||||
fieldContent = `${item.numSucceeded} created, ${item.numThrottled} throttled, ${item.numFailed} errors`;
|
||||
break;
|
||||
return `${item.numSucceeded} created, ${item.numThrottled} throttled, ${item.numFailed} errors`;
|
||||
default:
|
||||
fieldContent = item.fileName;
|
||||
return item.fileName;
|
||||
}
|
||||
return (
|
||||
<TooltipHost content={fieldContent} id={tooltipId} directionalHint={DirectionalHint.rightCenter}>
|
||||
{fieldContent}
|
||||
</TooltipHost>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -60,7 +60,7 @@ export const WelcomeModal = ({ visible }: { visible: boolean }): JSX.Element =>
|
||||
<Text>
|
||||
Ask Copilot to generate a query by describing the query in your words.
|
||||
<br />
|
||||
<Link href="https://aka.ms/cdb-copilot-learn-more">Learn more</Link>
|
||||
<Link href="http://aka.ms/cdb-copilot-learn-more">Learn more</Link>
|
||||
</Text>
|
||||
</Stack.Item>
|
||||
<Stack.Item align="center" className="text">
|
||||
@@ -78,7 +78,7 @@ export const WelcomeModal = ({ visible }: { visible: boolean }): JSX.Element =>
|
||||
<Text>
|
||||
AI-generated content can have mistakes. Make sure it’s accurate and appropriate before using it.
|
||||
<br />
|
||||
<Link href="https://aka.ms/cdb-copilot-preview-terms">Read preview terms</Link>
|
||||
<Link href="http://aka.ms/cdb-copilot-preview-terms">Read preview terms</Link>
|
||||
</Text>
|
||||
</Stack.Item>
|
||||
<Stack.Item align="center" className="text">
|
||||
@@ -97,7 +97,7 @@ export const WelcomeModal = ({ visible }: { visible: boolean }): JSX.Element =>
|
||||
While in Private Preview, Query Copilot is setup to work on sample database we have configured for you
|
||||
at no cost.
|
||||
<br />
|
||||
<Link href="https://aka.ms/cdb-copilot-learn-more">Learn more</Link>
|
||||
<Link href="http://aka.ms/cdb-copilot-learn-more">Learn more</Link>
|
||||
</Text>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
|
||||
@@ -102,7 +102,7 @@ exports[`Query Copilot Welcome Modal snapshot test should render when isOpen is
|
||||
Ask Copilot to generate a query by describing the query in your words.
|
||||
<br />
|
||||
<StyledLinkBase
|
||||
href="https://aka.ms/cdb-copilot-learn-more"
|
||||
href="http://aka.ms/cdb-copilot-learn-more"
|
||||
>
|
||||
Learn more
|
||||
</StyledLinkBase>
|
||||
@@ -138,7 +138,7 @@ exports[`Query Copilot Welcome Modal snapshot test should render when isOpen is
|
||||
AI-generated content can have mistakes. Make sure it’s accurate and appropriate before using it.
|
||||
<br />
|
||||
<StyledLinkBase
|
||||
href="https://aka.ms/cdb-copilot-preview-terms"
|
||||
href="http://aka.ms/cdb-copilot-preview-terms"
|
||||
>
|
||||
Read preview terms
|
||||
</StyledLinkBase>
|
||||
@@ -174,7 +174,7 @@ exports[`Query Copilot Welcome Modal snapshot test should render when isOpen is
|
||||
While in Private Preview, Query Copilot is setup to work on sample database we have configured for you at no cost.
|
||||
<br />
|
||||
<StyledLinkBase
|
||||
href="https://aka.ms/cdb-copilot-learn-more"
|
||||
href="http://aka.ms/cdb-copilot-learn-more"
|
||||
>
|
||||
Learn more
|
||||
</StyledLinkBase>
|
||||
|
||||
@@ -434,7 +434,7 @@ export const QueryCopilotTab: React.FC<QueryCopilotProps> = ({ explorer }: Query
|
||||
}}
|
||||
>
|
||||
Learn about{" "}
|
||||
<Link target="_blank" href="https://aka.ms/cdb-copilot-writing">
|
||||
<Link target="_blank" href="http://aka.ms/cdb-copilot-writing">
|
||||
writing effective prompts
|
||||
</Link>
|
||||
</Text>
|
||||
@@ -448,7 +448,7 @@ export const QueryCopilotTab: React.FC<QueryCopilotProps> = ({ explorer }: Query
|
||||
<Stack style={{ marginTop: 8, marginBottom: 24 }}>
|
||||
<Text style={{ fontSize: 12 }}>
|
||||
AI-generated content can have mistakes. Make sure it's accurate and appropriate before using it.{" "}
|
||||
<Link href="https://aka.ms/cdb-copilot-preview-terms" target="_blank">
|
||||
<Link href="http://aka.ms/cdb-copilot-preview-terms" target="_blank">
|
||||
Read preview terms
|
||||
</Link>
|
||||
{showErrorMessageBar && (
|
||||
|
||||
@@ -1,39 +1,81 @@
|
||||
import { IconButton, Image, Stack, Text } from "@fluentui/react";
|
||||
import { DefaultButton, IconButton, Image, Modal, PrimaryButton, Stack, Text } from "@fluentui/react";
|
||||
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||
import React from "react";
|
||||
import CopilotIcon from "../../../../../images/CopilotSidebarLogo.svg";
|
||||
|
||||
export const Header: React.FC = (): JSX.Element => {
|
||||
const { setShowCopilotSidebar } = useQueryCopilot();
|
||||
const { setShowCopilotSidebar, chatMessages, setChatMessages, setShowExplanationBubble } = useQueryCopilot();
|
||||
const [showDeleteHistoryModal, setShowDeleteHistoryModal] = React.useState(false);
|
||||
|
||||
const getDeleteHistoryModal = () => {
|
||||
return (
|
||||
<Modal isOpen={showDeleteHistoryModal} styles={{ main: { minHeight: "122px", minWidth: "480px" } }}>
|
||||
<Stack style={{ padding: "16px 24px", height: "auto" }}>
|
||||
<Text style={{ height: 24, fontSize: "18px" }}>
|
||||
<b>Delete chat history?</b>
|
||||
</Text>
|
||||
<Text style={{ marginTop: 10, marginBottom: 20 }}>
|
||||
This action will clear all chat history. Are you sure you want to continue?
|
||||
</Text>
|
||||
<Stack horizontal tokens={{ childrenGap: 10 }} horizontalAlign="start">
|
||||
<PrimaryButton
|
||||
style={{ padding: "0px 20px", height: 24 }}
|
||||
onClick={() => {
|
||||
setChatMessages([]);
|
||||
setShowExplanationBubble(false);
|
||||
setShowDeleteHistoryModal(false);
|
||||
}}
|
||||
>
|
||||
Continue
|
||||
</PrimaryButton>
|
||||
<DefaultButton style={{ padding: "0px 20px", height: 24 }} onClick={() => setShowDeleteHistoryModal(false)}>
|
||||
Close
|
||||
</DefaultButton>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Stack
|
||||
style={{ margin: "15px 0px 0px 0px", padding: "5px", display: "flex", justifyContent: "space-between" }}
|
||||
horizontal
|
||||
verticalAlign="center"
|
||||
>
|
||||
<Stack horizontal verticalAlign="center">
|
||||
<Image src={CopilotIcon} />
|
||||
<Text style={{ marginLeft: "5px", fontWeight: "bold" }}>Copilot</Text>
|
||||
<Text
|
||||
style={{
|
||||
background: "#f0f0f0",
|
||||
fontSize: "10px",
|
||||
padding: "2px 4px",
|
||||
marginLeft: "5px",
|
||||
borderRadius: "8px",
|
||||
}}
|
||||
>
|
||||
Preview
|
||||
</Text>
|
||||
<>
|
||||
<Stack
|
||||
style={{ margin: "15px 0px 0px 0px", padding: "5px", display: "flex", justifyContent: "space-between" }}
|
||||
horizontal
|
||||
verticalAlign="center"
|
||||
>
|
||||
<Stack horizontal verticalAlign="center">
|
||||
<Image src={CopilotIcon} />
|
||||
<Text style={{ marginLeft: "5px", fontWeight: "bold" }}>Copilot</Text>
|
||||
<Text
|
||||
style={{
|
||||
background: "#f0f0f0",
|
||||
fontSize: "10px",
|
||||
padding: "2px 4px",
|
||||
marginLeft: "5px",
|
||||
borderRadius: "8px",
|
||||
}}
|
||||
>
|
||||
Preview
|
||||
</Text>
|
||||
</Stack>
|
||||
<IconButton
|
||||
onClick={() => setShowDeleteHistoryModal(true)}
|
||||
iconProps={{ iconName: "History" }}
|
||||
title="Delete history"
|
||||
ariaLabel="Delete history"
|
||||
style={{ color: "#424242", verticalAlign: "middle" }}
|
||||
disabled={chatMessages.length === 0}
|
||||
/>
|
||||
<IconButton
|
||||
onClick={() => setShowCopilotSidebar(false)}
|
||||
iconProps={{ iconName: "Cancel" }}
|
||||
title="Exit"
|
||||
ariaLabel="Exit"
|
||||
style={{ color: "#424242", verticalAlign: "middle" }}
|
||||
/>
|
||||
</Stack>
|
||||
<IconButton
|
||||
onClick={() => setShowCopilotSidebar(false)}
|
||||
iconProps={{ iconName: "Cancel" }}
|
||||
title="Exit"
|
||||
ariaLabel="Exit"
|
||||
style={{ color: "#424242", verticalAlign: "middle" }}
|
||||
/>
|
||||
</Stack>
|
||||
{getDeleteHistoryModal()}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,64 +1,158 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Header snapshot test should close on button click 1`] = `
|
||||
<Stack
|
||||
horizontal={true}
|
||||
style={
|
||||
Object {
|
||||
"display": "flex",
|
||||
"justifyContent": "space-between",
|
||||
"margin": "15px 0px 0px 0px",
|
||||
"padding": "5px",
|
||||
}
|
||||
}
|
||||
verticalAlign="center"
|
||||
>
|
||||
<Fragment>
|
||||
<Stack
|
||||
horizontal={true}
|
||||
verticalAlign="center"
|
||||
>
|
||||
<Image
|
||||
src={Object {}}
|
||||
/>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"fontWeight": "bold",
|
||||
"marginLeft": "5px",
|
||||
}
|
||||
}
|
||||
>
|
||||
Copilot
|
||||
</Text>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"background": "#f0f0f0",
|
||||
"borderRadius": "8px",
|
||||
"fontSize": "10px",
|
||||
"marginLeft": "5px",
|
||||
"padding": "2px 4px",
|
||||
}
|
||||
}
|
||||
>
|
||||
Preview
|
||||
</Text>
|
||||
</Stack>
|
||||
<CustomizedIconButton
|
||||
ariaLabel="Exit"
|
||||
iconProps={
|
||||
Object {
|
||||
"iconName": "Cancel",
|
||||
}
|
||||
}
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#424242",
|
||||
"verticalAlign": "middle",
|
||||
"display": "flex",
|
||||
"justifyContent": "space-between",
|
||||
"margin": "15px 0px 0px 0px",
|
||||
"padding": "5px",
|
||||
}
|
||||
}
|
||||
title="Exit"
|
||||
/>
|
||||
</Stack>
|
||||
verticalAlign="center"
|
||||
>
|
||||
<Stack
|
||||
horizontal={true}
|
||||
verticalAlign="center"
|
||||
>
|
||||
<Image
|
||||
src={Object {}}
|
||||
/>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"fontWeight": "bold",
|
||||
"marginLeft": "5px",
|
||||
}
|
||||
}
|
||||
>
|
||||
Copilot
|
||||
</Text>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"background": "#f0f0f0",
|
||||
"borderRadius": "8px",
|
||||
"fontSize": "10px",
|
||||
"marginLeft": "5px",
|
||||
"padding": "2px 4px",
|
||||
}
|
||||
}
|
||||
>
|
||||
Preview
|
||||
</Text>
|
||||
</Stack>
|
||||
<CustomizedIconButton
|
||||
ariaLabel="Delete history"
|
||||
disabled={true}
|
||||
iconProps={
|
||||
Object {
|
||||
"iconName": "History",
|
||||
}
|
||||
}
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#424242",
|
||||
"verticalAlign": "middle",
|
||||
}
|
||||
}
|
||||
title="Delete history"
|
||||
/>
|
||||
<CustomizedIconButton
|
||||
ariaLabel="Exit"
|
||||
iconProps={
|
||||
Object {
|
||||
"iconName": "Cancel",
|
||||
}
|
||||
}
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#424242",
|
||||
"verticalAlign": "middle",
|
||||
}
|
||||
}
|
||||
title="Exit"
|
||||
/>
|
||||
</Stack>
|
||||
<Modal
|
||||
isOpen={true}
|
||||
styles={
|
||||
Object {
|
||||
"main": Object {
|
||||
"minHeight": "122px",
|
||||
"minWidth": "480px",
|
||||
},
|
||||
}
|
||||
}
|
||||
>
|
||||
<Stack
|
||||
style={
|
||||
Object {
|
||||
"height": "auto",
|
||||
"padding": "16px 24px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"fontSize": "18px",
|
||||
"height": 24,
|
||||
}
|
||||
}
|
||||
>
|
||||
<b>
|
||||
Delete chat history?
|
||||
</b>
|
||||
</Text>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"marginBottom": 20,
|
||||
"marginTop": 10,
|
||||
}
|
||||
}
|
||||
>
|
||||
This action will clear all chat history. Are you sure you want to continue?
|
||||
</Text>
|
||||
<Stack
|
||||
horizontal={true}
|
||||
horizontalAlign="start"
|
||||
tokens={
|
||||
Object {
|
||||
"childrenGap": 10,
|
||||
}
|
||||
}
|
||||
>
|
||||
<CustomizedPrimaryButton
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"height": 24,
|
||||
"padding": "0px 20px",
|
||||
}
|
||||
}
|
||||
>
|
||||
Continue
|
||||
</CustomizedPrimaryButton>
|
||||
<CustomizedDefaultButton
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"height": 24,
|
||||
"padding": "0px 20px",
|
||||
}
|
||||
}
|
||||
>
|
||||
Close
|
||||
</CustomizedDefaultButton>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Modal>
|
||||
</Fragment>
|
||||
`;
|
||||
|
||||
@@ -65,7 +65,7 @@ export const WelcomeSidebarModal: React.FC = (): JSX.Element => {
|
||||
<Text>
|
||||
Ask Copilot to generate a query by describing the query in your words.
|
||||
<br />
|
||||
<Link href="https://aka.ms/cdb-copilot-learn-more">Learn more</Link>
|
||||
<Link href="http://aka.ms/cdb-copilot-learn-more">Learn more</Link>
|
||||
</Text>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
@@ -87,7 +87,7 @@ export const WelcomeSidebarModal: React.FC = (): JSX.Element => {
|
||||
<Text>
|
||||
AI-generated content can have mistakes. Make sure it’s accurate and appropriate before using it.
|
||||
<br />
|
||||
<Link href="https://aka.ms/cdb-copilot-preview-terms">Read preview terms</Link>
|
||||
<Link href="http://aka.ms/cdb-copilot-preview-terms">Read preview terms</Link>
|
||||
</Text>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
@@ -109,7 +109,7 @@ export const WelcomeSidebarModal: React.FC = (): JSX.Element => {
|
||||
<Text>
|
||||
Copilot is setup on a sample database we have configured for you at no cost
|
||||
<br />
|
||||
<Link href="https://aka.ms/cdb-copilot-learn-more">Learn more</Link>
|
||||
<Link href="http://aka.ms/cdb-copilot-learn-more">Learn more</Link>
|
||||
</Text>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
|
||||
@@ -102,7 +102,7 @@ exports[`Query copilot tab snapshot test should render with initial input 1`] =
|
||||
AI-generated content can have mistakes. Make sure it's accurate and appropriate before using it.
|
||||
|
||||
<StyledLinkBase
|
||||
href="https://aka.ms/cdb-copilot-preview-terms"
|
||||
href="http://aka.ms/cdb-copilot-preview-terms"
|
||||
target="_blank"
|
||||
>
|
||||
Read preview terms
|
||||
|
||||
@@ -4,6 +4,7 @@ import { traceSuccess } from "Shared/Telemetry/TelemetryProcessor";
|
||||
import { userContext } from "UserContext";
|
||||
import { useCarousel } from "hooks/useCarousel";
|
||||
import React, { useState } from "react";
|
||||
import Youtube from "react-youtube";
|
||||
import Image1 from "../../../images/CarouselImage1.svg";
|
||||
import Image2 from "../../../images/CarouselImage2.svg";
|
||||
|
||||
@@ -78,12 +79,7 @@ const getHeaderText = (page: number): string => {
|
||||
const getContent = (page: number): JSX.Element => {
|
||||
switch (page) {
|
||||
case 1:
|
||||
return (
|
||||
<video controls width="640" height="360" controlsList="nofullscreen nodownload ">
|
||||
<source src="src/Explorer/Quickstart/Videos/Cosmos-db-turorial.mp4" type="video/mp4"></source>
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
);
|
||||
return <Youtube videoId="Jvgh64rvdXU" onPlay={() => traceSuccess(Action.PlayCarouselVideo)} />;
|
||||
case 2:
|
||||
return <Image style={{ width: 640 }} src={Image1} />;
|
||||
case 3:
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -1,91 +0,0 @@
|
||||
import { DatabaseAccount, FirewallRule } from "Contracts/DataModels";
|
||||
import { checkFirewallRules } from "Explorer/Tabs/Shared/CheckFirewallRules";
|
||||
import { updateUserContext } from "UserContext";
|
||||
import { mockFunction } from "Utils/JestUtils";
|
||||
import { armRequest } from "Utils/arm/request";
|
||||
import React from "react";
|
||||
|
||||
jest.mock("Utils/arm/request");
|
||||
const armRequestMock = mockFunction(armRequest);
|
||||
|
||||
describe("CheckFirewallRule tests", () => {
|
||||
const apiVersion = "2023-03-15-preview";
|
||||
const rulePredicate = (rule: FirewallRule) =>
|
||||
rule.properties.startIpAddress === "0.0.0.0" && rule.properties.endIpAddress === "255.255.255.255";
|
||||
let isAllPublicIPAddressEnabled: boolean;
|
||||
const setIsAllPublicIPAddressEnabled = jest.fn((value) => (isAllPublicIPAddressEnabled = value));
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const useStateMock: any = (initState: any) => [initState, setIsAllPublicIPAddressEnabled];
|
||||
jest.spyOn(React, "useState").mockImplementation(useStateMock);
|
||||
|
||||
beforeAll(() => {
|
||||
updateUserContext({
|
||||
databaseAccount: {
|
||||
id: "testResourceId",
|
||||
} as DatabaseAccount,
|
||||
});
|
||||
});
|
||||
|
||||
it("returns 'all public IP addresses' is enabled for account with the proper firewall rule", async () => {
|
||||
armRequestMock.mockResolvedValueOnce({
|
||||
value: [
|
||||
{
|
||||
id: "resourceId",
|
||||
name: "AllowAll",
|
||||
type: "Microsoft.DocumentDB/mongoClusters/firewallRules",
|
||||
properties: {
|
||||
provisioningState: "Succeeded",
|
||||
startIpAddress: "0.0.0.0",
|
||||
endIpAddress: "255.255.255.255",
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await checkFirewallRules(apiVersion, rulePredicate, setIsAllPublicIPAddressEnabled);
|
||||
|
||||
expect(isAllPublicIPAddressEnabled).toBe(true);
|
||||
});
|
||||
|
||||
it("returns 'all public IP addresses' is NOT enabled for account without the proper firewall rule", async () => {
|
||||
armRequestMock.mockResolvedValueOnce([
|
||||
{
|
||||
id: "resourceId",
|
||||
name: "AllowAll",
|
||||
type: "Microsoft.DocumentDB/mongoClusters/firewallRules",
|
||||
properties: {
|
||||
provisioningState: "Succeeded",
|
||||
startIpAddress: "10.10.10.10",
|
||||
endIpAddress: "10.10.10.10",
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
await checkFirewallRules(apiVersion, rulePredicate, setIsAllPublicIPAddressEnabled);
|
||||
|
||||
expect(isAllPublicIPAddressEnabled).toBe(false);
|
||||
});
|
||||
|
||||
it("sets message for account without the proper firewall rule", async () => {
|
||||
armRequestMock.mockResolvedValueOnce([
|
||||
{
|
||||
id: "resourceId",
|
||||
name: "AllowAll",
|
||||
type: "Microsoft.DocumentDB/mongoClusters/firewallRules",
|
||||
properties: {
|
||||
provisioningState: "Succeeded",
|
||||
startIpAddress: "0.0.0.0",
|
||||
endIpAddress: "255.255.255.255",
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
const warningMessage = "This is a warning message";
|
||||
let warningMessageResult: string;
|
||||
const warningMessageFunc = (msg: string) => (warningMessageResult = msg);
|
||||
|
||||
await checkFirewallRules(apiVersion, rulePredicate, undefined, warningMessageFunc, warningMessage);
|
||||
|
||||
expect(warningMessageResult).toEqual(warningMessage);
|
||||
});
|
||||
});
|
||||
@@ -55,17 +55,6 @@ export const defaultAllowedBackendEndpoints: ReadonlyArray<string> = [
|
||||
"https://localhost:1234",
|
||||
];
|
||||
|
||||
export const PortalBackendIPs: { [key: string]: string[] } = {
|
||||
"https://main.documentdb.ext.azure.com": ["104.42.195.92", "40.76.54.131"],
|
||||
// DE doesn't talk to prod2 (main2) but it might be added
|
||||
//"https://main2.documentdb.ext.azure.com": ["104.42.196.69"],
|
||||
"https://main.documentdb.ext.azure.cn": ["139.217.8.252"],
|
||||
"https://main.documentdb.ext.azure.us": ["52.244.48.71"],
|
||||
// Add ussec and usnat when endpoint address is known:
|
||||
//ussec: ["29.26.26.67", "29.26.26.66"],
|
||||
//usnat: ["7.28.202.68"],
|
||||
};
|
||||
|
||||
export const allowedMongoProxyEndpoints: ReadonlyArray<string> = [
|
||||
"https://main.documentdb.ext.azure.com",
|
||||
"https://main.documentdb.ext.azure.cn",
|
||||
@@ -78,7 +67,7 @@ export const allowedEmulatorEndpoints: ReadonlyArray<string> = ["https://localho
|
||||
|
||||
export const allowedMongoBackendEndpoints: ReadonlyArray<string> = ["https://localhost:1234"];
|
||||
|
||||
export const allowedGraphEndpoints: ReadonlyArray<string> = ["https://graph.microsoft.com"];
|
||||
export const allowedGraphEndpoints: ReadonlyArray<string> = ["https://graph.windows.net"];
|
||||
|
||||
export const allowedArcadiaEndpoints: ReadonlyArray<string> = ["https://workspaceartifacts.projectarcadia.net"];
|
||||
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export function mockFunction<T extends (...args: any[]) => any>(fn: T): jest.MockedFunction<T> {
|
||||
return fn as jest.MockedFunction<T>;
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
import { resetConfigContext, updateConfigContext } from "ConfigContext";
|
||||
import { DatabaseAccount, IpRule } from "Contracts/DataModels";
|
||||
import { updateUserContext } from "UserContext";
|
||||
import { PortalBackendIPs } from "Utils/EndpointValidation";
|
||||
import { getNetworkSettingsWarningMessage } from "./NetworkUtility";
|
||||
|
||||
describe("NetworkUtility tests", () => {
|
||||
describe("getNetworkSettingsWarningMessage", () => {
|
||||
const publicAccessMessagePart = "Please enable public access to proceed";
|
||||
const accessMessagePart = "Please allow access from Azure Portal to proceed";
|
||||
// validEnpoints are a subset of those from Utils/EndpointValidation/PortalBackendIPs
|
||||
const validEndpoints = [
|
||||
"https://main.documentdb.ext.azure.com",
|
||||
"https://main.documentdb.ext.azure.cn",
|
||||
"https://main.documentdb.ext.azure.us",
|
||||
];
|
||||
|
||||
let warningMessageResult: string;
|
||||
const warningMessageFunc = (msg: string) => (warningMessageResult = msg);
|
||||
|
||||
beforeEach(() => {
|
||||
warningMessageResult = undefined;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
resetConfigContext();
|
||||
});
|
||||
|
||||
it("should return no message when publicNetworkAccess is enabled", async () => {
|
||||
updateUserContext({
|
||||
databaseAccount: {
|
||||
properties: {
|
||||
publicNetworkAccess: "Enabled",
|
||||
},
|
||||
} as DatabaseAccount,
|
||||
});
|
||||
|
||||
await getNetworkSettingsWarningMessage(warningMessageFunc);
|
||||
expect(warningMessageResult).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should return publicAccessMessage when publicNetworkAccess is disabled", async () => {
|
||||
updateUserContext({
|
||||
databaseAccount: {
|
||||
properties: {
|
||||
publicNetworkAccess: "Disabled",
|
||||
},
|
||||
} as DatabaseAccount,
|
||||
});
|
||||
|
||||
await getNetworkSettingsWarningMessage(warningMessageFunc);
|
||||
expect(warningMessageResult).toContain(publicAccessMessagePart);
|
||||
});
|
||||
|
||||
it(`should return no message when the appropriate ip rules are added to mongo/cassandra account per endpoint`, () => {
|
||||
validEndpoints.forEach(async (endpoint) => {
|
||||
updateUserContext({
|
||||
databaseAccount: {
|
||||
kind: "MongoDB",
|
||||
properties: {
|
||||
ipRules: PortalBackendIPs[endpoint].map((ip: string) => ({ ipAddressOrRange: ip } as IpRule)),
|
||||
publicNetworkAccess: "Enabled",
|
||||
},
|
||||
} as DatabaseAccount,
|
||||
});
|
||||
|
||||
updateConfigContext({
|
||||
BACKEND_ENDPOINT: endpoint,
|
||||
});
|
||||
|
||||
let asyncWarningMessageResult: string;
|
||||
const asyncWarningMessageFunc = (msg: string) => (asyncWarningMessageResult = msg);
|
||||
|
||||
await getNetworkSettingsWarningMessage(asyncWarningMessageFunc);
|
||||
expect(asyncWarningMessageResult).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
it("should return accessMessage when incorrent ip rule is added to mongo/cassandra account per endpoint", () => {
|
||||
validEndpoints.forEach(async (endpoint) => {
|
||||
updateUserContext({
|
||||
databaseAccount: {
|
||||
kind: "MongoDB",
|
||||
properties: {
|
||||
ipRules: [{ ipAddressOrRange: "1.1.1.1" }],
|
||||
publicNetworkAccess: "Enabled",
|
||||
},
|
||||
} as DatabaseAccount,
|
||||
});
|
||||
|
||||
updateConfigContext({
|
||||
BACKEND_ENDPOINT: endpoint,
|
||||
});
|
||||
|
||||
let asyncWarningMessageResult: string;
|
||||
const asyncWarningMessageFunc = (msg: string) => (asyncWarningMessageResult = msg);
|
||||
|
||||
await getNetworkSettingsWarningMessage(asyncWarningMessageFunc);
|
||||
expect(asyncWarningMessageResult).toContain(accessMessagePart);
|
||||
});
|
||||
});
|
||||
|
||||
// Postgres and vcore mongo account checks basically pass through to CheckFirewallRules so those
|
||||
// tests are omitted here and included in CheckFirewallRules.test.ts
|
||||
});
|
||||
});
|
||||
@@ -1,7 +1,15 @@
|
||||
import { configContext } from "ConfigContext";
|
||||
import { checkFirewallRules } from "Explorer/Tabs/Shared/CheckFirewallRules";
|
||||
import { userContext } from "UserContext";
|
||||
import { PortalBackendIPs } from "Utils/EndpointValidation";
|
||||
|
||||
const PortalIPs: { [key: string]: string[] } = {
|
||||
prod1: ["104.42.195.92", "40.76.54.131"],
|
||||
prod2: ["104.42.196.69"],
|
||||
mooncake: ["139.217.8.252"],
|
||||
blackforest: ["51.4.229.218"],
|
||||
fairfax: ["52.244.48.71"],
|
||||
ussec: ["29.26.26.67", "29.26.26.66"],
|
||||
usnat: ["7.28.202.68"],
|
||||
};
|
||||
|
||||
export const getNetworkSettingsWarningMessage = async (
|
||||
setStateFunc: (warningMessage: string) => void
|
||||
@@ -20,7 +28,6 @@ export const getNetworkSettingsWarningMessage = async (
|
||||
setStateFunc,
|
||||
accessMessage
|
||||
);
|
||||
return;
|
||||
} else if (userContext.apiType === "VCoreMongo") {
|
||||
checkFirewallRules(
|
||||
"2023-03-01-preview",
|
||||
@@ -31,7 +38,6 @@ export const getNetworkSettingsWarningMessage = async (
|
||||
setStateFunc,
|
||||
accessMessage
|
||||
);
|
||||
return;
|
||||
} else if (accountProperties) {
|
||||
// public network access is disabled
|
||||
if (
|
||||
@@ -39,14 +45,13 @@ export const getNetworkSettingsWarningMessage = async (
|
||||
accountProperties.publicNetworkAccess !== "SecuredByPerimeter"
|
||||
) {
|
||||
setStateFunc(publicAccessMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
const ipRules = accountProperties.ipRules;
|
||||
// public network access is NOT set to "All networks"
|
||||
if (ipRules?.length > 0) {
|
||||
if (ipRules.length > 0) {
|
||||
if (userContext.apiType === "Cassandra" || userContext.apiType === "Mongo") {
|
||||
const portalIPs = PortalBackendIPs[configContext.BACKEND_ENDPOINT];
|
||||
const portalIPs = PortalIPs[userContext.portalEnv];
|
||||
let numberOfMatches = 0;
|
||||
ipRules.forEach((ipRule) => {
|
||||
if (portalIPs.indexOf(ipRule.ipAddressOrRange) !== -1) {
|
||||
|
||||
@@ -57,22 +57,4 @@ describe("shouldShowQueryPageOptions()", () => {
|
||||
});
|
||||
expect(userContext.apiType).toBe("Mongo");
|
||||
});
|
||||
|
||||
it("should be 'Postgres' for Postgres API", () => {
|
||||
updateUserContext({
|
||||
databaseAccount: {
|
||||
kind: "Postgres",
|
||||
} as DatabaseAccount,
|
||||
});
|
||||
expect(userContext.apiType).toBe("Postgres");
|
||||
});
|
||||
|
||||
it("should be 'VCoreMongo' for vCore Mongo", () => {
|
||||
updateUserContext({
|
||||
databaseAccount: {
|
||||
kind: "VCoreMongo",
|
||||
} as DatabaseAccount,
|
||||
});
|
||||
expect(userContext.apiType).toBe("VCoreMongo");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<clear />
|
||||
<add name="X-Xss-Protection" value="1; mode=block" />
|
||||
<add name="X-Content-Type-Options" value="nosniff" />
|
||||
<add name="Content-Security-Policy" value="frame-ancestors 'self' portal.azure.com *.portal.azure.com portal.azure.us portal.azure.cn portal.microsoftazure.de df.onecloud.azure-test.net *.fabric.microsoft.com *.powerbi.com *.analysis-df.windows.net" />
|
||||
<add name="Content-Security-Policy" value="frame-ancestors 'self' portal.azure.com *.portal.azure.com portal.azure.us portal.azure.cn portal.microsoftazure.de df.onecloud.azure-test.net *.fabric.microsoft.com *.powerbi.com pbi-rdb-edog.analysis-df.windows.net" />
|
||||
</customHeaders>
|
||||
<redirectHeaders>
|
||||
<clear />
|
||||
|
||||
Reference in New Issue
Block a user