Compare commits

...

11 Commits

Author SHA1 Message Date
sunilyadav840
9593407677 fixed logger.test.ts GraphData.test.ts securityWarningBar.tsx typescript in strict mode 2021-08-23 21:21:17 +05:30
victor-meng
8eeda41021 Move synapse link out of advanced section in add collection panel (#989) 2021-08-19 12:18:21 -07:00
Hardikkumar Nai
960cd9fc55 Resolve ESlint Controls (#990) 2021-08-19 12:16:35 -05:00
Hardikkumar Nai
9ec0ac9f54 Resolve ESLint Contracts (#986) 2021-08-19 12:15:52 -05:00
Steve Faulkner
b66aeb814a Polyfill Buffer (#988) 2021-08-18 21:12:40 -05:00
Tanuj Mittal
410f582378 Fix notebooksTemporarilyDown feature flag (#983) 2021-08-17 07:27:41 +05:30
Hardikkumar Nai
678ca51c77 Update to Webpack 5 (#964)
Co-authored-by: Steve Faulkner <southpolesteve@gmail.com>
2021-08-16 15:44:40 -05:00
Steve Faulkner
2dfd90ca0f Disable Notebooks Test (#980) 2021-08-16 11:35:21 -05:00
Srinath Narayanan
4110be10bd Removing author from publish notebook payload (#966)
* removing author from publish payload

* fixed failing tests
2021-08-15 13:15:30 +05:30
Srinath Narayanan
6e55e397b3 Changes for Disabling notebook related features (#979)
* notebook removal changes

* fixed failing tests
2021-08-14 12:09:22 +05:30
Tanuj Mittal
24d0a16123 Disable notebooks temporarily (#978)
* Disable notebooks temporarily

* Updates
2021-08-14 07:03:58 +05:30
36 changed files with 2163 additions and 2994 deletions

View File

@@ -21,16 +21,8 @@ src/Common/MongoUtility.ts
src/Common/NotificationsClientBase.ts
src/Common/QueriesClient.ts
src/Common/Splitter.ts
src/Config.ts
src/Contracts/ActionContracts.ts
src/Contracts/DataModels.ts
src/Contracts/Diagnostics.ts
src/Contracts/ExplorerContracts.ts
src/Contracts/Versions.ts
src/Contracts/ViewModels.ts
src/Controls/Heatmap/Heatmap.test.ts
src/Controls/Heatmap/Heatmap.ts
src/Controls/Heatmap/HeatmapDatatypes.ts
src/Definitions/datatables.d.ts
src/Definitions/gif.d.ts
src/Definitions/globals.d.ts
@@ -44,29 +36,10 @@ src/Definitions/png.d.ts
src/Definitions/svg.d.ts
src/Explorer/ComponentRegisterer.test.ts
src/Explorer/ComponentRegisterer.ts
src/Explorer/Controls/CollapsiblePanel/CollapsiblePanelComponent.ts
src/Explorer/Controls/DiffEditor/DiffEditorComponent.ts
src/Explorer/Controls/DynamicList/DynamicList.test.ts
src/Explorer/Controls/DynamicList/DynamicListComponent.ts
src/Explorer/Controls/Editor/EditorComponent.ts
src/Explorer/Controls/ErrorDisplayComponent/ErrorDisplayComponent.ts
src/Explorer/Controls/InputTypeahead/InputTypeahead.ts
src/Explorer/Controls/JsonEditor/JsonEditorComponent.ts
src/Explorer/Controls/Notebook/NotebookAppMessageHandler.ts
src/Explorer/Controls/ThroughputInput/ThroughputInputComponent.ts
src/Explorer/Controls/ThroughputInput/ThroughputInputComponentAutoPilotV3.ts
src/Explorer/Controls/Toolbar/IToolbarAction.ts
src/Explorer/Controls/Toolbar/IToolbarDisplayable.ts
src/Explorer/Controls/Toolbar/IToolbarDropDown.ts
src/Explorer/Controls/Toolbar/IToolbarItem.ts
src/Explorer/Controls/Toolbar/IToolbarSeperator.ts
src/Explorer/Controls/Toolbar/IToolbarToggle.ts
src/Explorer/Controls/Toolbar/KeyCodes.ts
src/Explorer/Controls/Toolbar/Toolbar.ts
src/Explorer/Controls/Toolbar/ToolbarAction.ts
src/Explorer/Controls/Toolbar/ToolbarDropDown.ts
src/Explorer/Controls/Toolbar/ToolbarToggle.ts
src/Explorer/Controls/Toolbar/Utilities.ts
src/Explorer/DataSamples/ContainerSampleGenerator.test.ts
src/Explorer/DataSamples/ContainerSampleGenerator.ts
src/Explorer/DataSamples/DataSamplesUtil.test.ts

View File

@@ -143,7 +143,7 @@ jobs:
- ./test/mongo/container.spec.ts
- ./test/mongo/container32.spec.ts
- ./test/selfServe/selfServeExample.spec.ts
- ./test/notebooks/upload.spec.ts
# - ./test/notebooks/upload.spec.ts // TEMP disabled since notebooks service is off
- ./test/sql/resourceToken.spec.ts
- ./test/tables/container.spec.ts
steps:

View File

Before

Width:  |  Height:  |  Size: 842 B

After

Width:  |  Height:  |  Size: 842 B

View File

Before

Width:  |  Height:  |  Size: 371 B

After

Width:  |  Height:  |  Size: 371 B

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -9,6 +9,7 @@
@DataExplorerFont: wf_segoe-ui_normal, "Segoe UI", "Segoe WP", Tahoma, Arial, sans-serif;
@SemiboldFont: "Segoe UI Semibold", "Segoe UI", "Segoe WP", Tahoma, Arial, sans-serif;
@GrayScale: "grayscale()";
@xSmallFontSize: 4px;
@smallFontSize: 8px;

View File

@@ -19,6 +19,10 @@
.notebookHeader {
font-size: 12px;
}
.clickDisabled {
pointer-events: none;
}
}

4641
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -48,9 +48,9 @@
"applicationinsights": "1.8.0",
"bootstrap": "3.4.1",
"canvas": "file:./canvas",
"clean-webpack-plugin": "0.1.19",
"clean-webpack-plugin": "3.0.0",
"clipboard-copy": "4.0.1",
"copy-webpack-plugin": "6.0.2",
"copy-webpack-plugin": "9.0.1",
"crossroads": "0.12.2",
"css-element-queries": "1.1.1",
"d3": "6.1.1",
@@ -81,10 +81,10 @@
"plotly.js-cartesian-dist-min": "1.52.3",
"post-robot": "10.0.42",
"q": "1.5.1",
"react": "16.13.1",
"react": "16.14.0",
"react-animate-height": "2.0.8",
"react-dnd": "9.4.0",
"react-dnd-html5-backend": "9.4.0",
"react-dnd": "14.0.2",
"react-dnd-html5-backend": "14.0.0",
"react-dom": "16.13.1",
"react-hotkeys": "2.0.0",
"react-i18next": "11.8.5",
@@ -98,7 +98,7 @@
"sanitize-html": "2.3.3",
"styled-components": "4.3.2",
"swr": "0.4.0",
"terser-webpack-plugin": "3.1.0",
"terser-webpack-plugin": "5.1.4",
"underscore": "1.9.1",
"utility-types": "3.10.0",
"zustand": "3.5.0"
@@ -132,6 +132,7 @@
"@types/underscore": "1.7.36",
"@typescript-eslint/eslint-plugin": "4.22.0",
"@typescript-eslint/parser": "4.22.0",
"@webpack-cli/serve": "1.5.2",
"babel-jest": "24.9.0",
"babel-loader": "8.1.0",
"buffer": "5.1.0",
@@ -153,44 +154,45 @@
"html-inline-css-webpack-plugin": "1.11.0",
"html-loader": "0.5.5",
"html-loader-jest": "0.2.1",
"html-webpack-plugin": "4.5.2",
"jest": "25.5.4",
"jest-canvas-mock": "2.1.0",
"html-webpack-plugin": "5.3.2",
"jest": "26.6.3",
"jest-canvas-mock": "2.3.1",
"jest-playwright-preset": "1.5.1",
"jest-trx-results-processor": "0.0.7",
"less": "3.8.1",
"less-loader": "4.1.0",
"less-vars-loader": "1.1.0",
"mini-css-extract-plugin": "0.4.3",
"mini-css-extract-plugin": "2.1.0",
"monaco-editor-webpack-plugin": "1.7.0",
"node-fetch": "2.6.1",
"playwright": "1.13.0",
"prettier": "2.2.1",
"process": "0.11.10",
"raw-loader": "0.5.1",
"react-dev-utils": "11.0.4",
"rimraf": "3.0.0",
"sinon": "3.2.1",
"style-loader": "0.23.0",
"ts-loader": "6.2.2",
"ts-loader": "9.2.4",
"tslint": "5.11.0",
"tslint-microsoft-contrib": "6.0.0",
"typedoc": "0.20.36",
"typescript": "4.3.4",
"url-loader": "1.1.1",
"wait-on": "4.0.2",
"webpack": "4.46.0",
"webpack-bundle-analyzer": "3.6.1",
"webpack-cli": "3.3.10",
"webpack-dev-server": "3.11.0"
"webpack": "5.47.0",
"webpack-bundle-analyzer": "4.4.2",
"webpack-cli": "4.7.2",
"webpack-dev-server": "3.11.2"
},
"scripts": {
"start": "node --max-old-space-size=10196 node_modules/webpack-dev-server/bin/webpack-dev-server.js",
"start": "webpack serve --mode development",
"dev": "echo \"WARNING: npm run dev has been deprecated\" && npm run build",
"build:dataExplorer:ci": "npm run build:ci",
"build": "npm run format:check && npm run lint && npm run compile && npm run compile:strict && npm run pack:prod && npm run copyToConsumers",
"build:ci": "npm run format:check && npm run lint && npm run compile && npm run compile:strict && npm run pack:fast",
"pack:prod": "node --max_old_space_size=10196 ./node_modules/webpack/bin/webpack.js --mode production",
"pack:fast": "node --max_old_space_size=10196 ./node_modules/webpack/bin/webpack.js --mode development --progress",
"pack:prod": "webpack --mode production",
"pack:fast": "webpack --mode development --progress",
"copyToConsumers": "node copyToConsumers",
"test": "rimraf coverage && jest",
"test:e2e": "jest -c ./jest.config.playwright.js --detectOpenHandles",

View File

@@ -350,6 +350,11 @@ export class Notebook {
public static readonly kernelRestartInitialDelayMs = 1000;
public static readonly kernelRestartMaxDelayMs = 20000;
public static readonly autoSaveIntervalMs = 120000;
public static readonly temporarilyDownMsg = "Notebooks is currently not available. We are working on it.";
public static readonly mongoShellTemporarilyDownMsg =
"We have identified an issue with the Mongo Shell and it is unavailable right now. We are actively working on the mitigation.";
public static readonly cassandraShellTemporarilyDownMsg =
"We have identified an issue with the Cassandra Shell and it is unavailable right now. We are actively working on the mitigation.";
}
export class SparkLibrary {

View File

@@ -1,7 +1,5 @@
jest.mock("./MessageHandler");
import { LogEntryLevel } from "../Contracts/Diagnostics";
import * as Logger from "./Logger";
import { MessageTypes } from "../Contracts/ExplorerContracts";
import { sendMessage } from "./MessageHandler";
describe("Logger", () => {

View File

@@ -22,8 +22,8 @@ describe("The Heatmap Control", () => {
};
let heatmap: Heatmap;
let theme: PortalTheme = 1;
const divElement: string = `<div id="${Heatmap.elementId}"></div>`;
const theme: PortalTheme = 1;
const divElement = `<div id="${Heatmap.elementId}"></div>`;
describe("drawHeatmap rendering", () => {
beforeEach(() => {
@@ -100,7 +100,7 @@ describe("iframe rendering when there is no data", () => {
});
it("should show a no data message with a dark theme", () => {
let data = {
const data = {
data: {
signature: "pcIframe",
data: {
@@ -111,7 +111,7 @@ describe("iframe rendering when there is no data", () => {
},
};
const divElement: string = `<div id="${Heatmap.elementId}"></div>`;
const divElement = `<div id="${Heatmap.elementId}"></div>`;
document.body.innerHTML = divElement;
handleMessage(data as MessageEvent);
@@ -120,7 +120,7 @@ describe("iframe rendering when there is no data", () => {
});
it("should show a no data message with a white theme", () => {
let data = {
const data = {
data: {
signature: "pcIframe",
data: {
@@ -131,7 +131,7 @@ describe("iframe rendering when there is no data", () => {
},
};
const divElement: string = `<div id="${Heatmap.elementId}"></div>`;
const divElement = `<div id="${Heatmap.elementId}"></div>`;
document.body.innerHTML = divElement;
handleMessage(data as MessageEvent);

View File

@@ -39,7 +39,7 @@ export class Heatmap {
}
}
private _getFontStyles(size: number = StyleConstants.MediumFontSize, color: string = "#838383"): FontSettings {
private _getFontStyles(size: number = StyleConstants.MediumFontSize, color = "#838383"): FontSettings {
return {
family: StyleConstants.DataExplorerFont,
size,
@@ -78,9 +78,9 @@ export class Heatmap {
// go thru all rows and create 2d matrix for heatmap...
for (let i = 0; i < rows.length; i++) {
output.yAxisPoints.push(rows[i]);
let dataPoints: number[] = [];
const dataPoints: number[] = [];
for (let a = 0; a < output.xAxisPoints.length; a++) {
let row: PartitionTimeStampToData = data[rows[i]];
const row: PartitionTimeStampToData = data[rows[i]];
dataPoints.push(row[output.xAxisPoints[a]]["Normalized Throughput"]);
}
output.dataPoints.push(dataPoints);
@@ -193,7 +193,7 @@ export class Heatmap {
this._getLayoutSettings(),
this._getChartDisplaySettings()
);
let plotDiv: any = document.getElementById(Heatmap.elementId);
const plotDiv: any = document.getElementById(Heatmap.elementId);
plotDiv.on("plotly_click", (data: any) => {
let timeSelected: string = data.points[0].x;
timeSelected = timeSelected.replace(" ", "T");
@@ -205,7 +205,7 @@ export class Heatmap {
break;
}
}
let output = [];
const output = [];
for (let i = 0; i < this._chartData.dataPoints.length; i++) {
output.push(this._chartData.dataPoints[i][xAxisIndex]);
}

View File

@@ -83,6 +83,7 @@ export const createCollectionContextMenuButton = (
items.push({
iconSrc: HostedTerminalIcon,
isDisabled: useNotebook.getState().isShellEnabled && userContext.features.notebooksTemporarilyDown,
onClick: () => {
const selectedCollection: ViewModels.Collection = useSelectedNode.getState().findSelectedCollection();
if (useNotebook.getState().isShellEnabled) {

View File

@@ -1084,11 +1084,13 @@ export default class Explorer {
dataExplorerArea: Constants.Areas.Notebook,
});
if (isNotebookEnabled) {
await this.initNotebooks(userContext.databaseAccount);
} else if (this.notebookToImport) {
// if notebooks is not enabled but the user is trying to do a quickstart setup with notebooks, open the SetupNotebooksPane
this._openSetupNotebooksPaneForQuickstart();
if (!userContext.features.notebooksTemporarilyDown) {
if (isNotebookEnabled) {
await this.initNotebooks(userContext.databaseAccount);
} else if (this.notebookToImport) {
// if notebooks is not enabled but the user is trying to do a quickstart setup with notebooks, open the SetupNotebooksPane
this._openSetupNotebooksPaneForQuickstart();
}
}
}
}

View File

@@ -1,11 +1,11 @@
import { GraphData, GremlinVertex, GremlinEdge } from "./GraphData";
import { GraphData, GremlinEdge, GremlinVertex } from "./GraphData";
describe("Graph Data", () => {
it("should set only one node as root", () => {
const graphData = new GraphData<GremlinVertex, GremlinEdge>();
const v1: GremlinVertex = { id: "1", label: null };
const v2: GremlinVertex = { id: "2", label: null };
const v3: GremlinVertex = { id: "3", label: null };
const v1: GremlinVertex = { id: "1", label: undefined };
const v2: GremlinVertex = { id: "2", label: undefined };
const v3: GremlinVertex = { id: "3", label: undefined };
v3._isRoot = true;
graphData.addVertex(v1);
@@ -28,9 +28,9 @@ describe("Graph Data", () => {
it("should properly find root id", () => {
const graphData = new GraphData();
const v1: GremlinVertex = { id: "1", label: null };
const v2: GremlinVertex = { id: "2", label: null };
const v3: GremlinVertex = { id: "3", label: null };
const v1: GremlinVertex = { id: "1", label: undefined };
const v2: GremlinVertex = { id: "2", label: undefined };
const v3: GremlinVertex = { id: "3", label: undefined };
graphData.addVertex(v1);
graphData.addVertex(v2);
@@ -44,12 +44,12 @@ describe("Graph Data", () => {
it("should remove edge from graph", () => {
const graphData = new GraphData();
graphData.addVertex({ id: "v1", label: null });
graphData.addVertex({ id: "v2", label: null });
graphData.addVertex({ id: "v3", label: null });
graphData.addVertex({ id: "v1", label: undefined });
graphData.addVertex({ id: "v2", label: undefined });
graphData.addVertex({ id: "v3", label: undefined });
graphData.addEdge({ id: "e1", inV: "v1", outV: "v2", label: null });
graphData.addEdge({ id: "e2", inV: "v1", outV: "v3", label: null });
graphData.addEdge({ id: "e1", inV: "v1", outV: "v2", label: "" });
graphData.addEdge({ id: "e2", inV: "v1", outV: "v3", label: "" });
// in edge
graphData.removeEdge("e1", false);

View File

@@ -1,5 +1,5 @@
import { SimulationLinkDatum, SimulationNodeDatum } from "d3";
import _ from "underscore";
import { SimulationNodeDatum, SimulationLinkDatum } from "d3";
export interface PaginationInfo {
total: number;
@@ -10,7 +10,7 @@ export interface PaginationInfo {
export interface GremlinVertex {
id: string;
label: string;
label?: string;
inE?: { [label: string]: GremlinShortInEdge[] };
outE?: { [label: string]: GremlinShortOutEdge[] };
properties?: { [propName: string]: GremlinProperty[] };

View File

@@ -6,9 +6,9 @@
import * as React from "react";
import CancelIcon from "../../../../images/cancel.svg";
import CheckIcon from "../../../../images/check.svg";
import CheckIcon from "../../../../images/check-1.svg";
import DeleteIcon from "../../../../images/delete.svg";
import EditIcon from "../../../../images/edit.svg";
import EditIcon from "../../../../images/edit-1.svg";
import * as ViewModels from "../../../Contracts/ViewModels";
import { AccessibleElement } from "../../Controls/AccessibleElement/AccessibleElement";
import { CollapsiblePanel } from "../../Controls/CollapsiblePanel/CollapsiblePanel";

View File

@@ -103,19 +103,25 @@ describe("CommandBarComponentButtonFactory tests", () => {
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
const enableNotebookBtn = buttons.find((button) => button.commandButtonLabel === enableNotebookBtnLabel);
expect(enableNotebookBtn).toBeDefined();
expect(enableNotebookBtn.disabled).toBe(false);
expect(enableNotebookBtn.tooltipText).toBe("");
//TODO: modify once notebooks are available
expect(enableNotebookBtn).toBeUndefined();
//expect(enableNotebookBtn).toBeDefined();
//expect(enableNotebookBtn.disabled).toBe(false);
//expect(enableNotebookBtn.tooltipText).toBe("");
});
it("Notebooks is not enabled and is unavailable - button should be shown and disabled", () => {
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
const enableNotebookBtn = buttons.find((button) => button.commandButtonLabel === enableNotebookBtnLabel);
expect(enableNotebookBtn).toBeDefined();
expect(enableNotebookBtn.disabled).toBe(true);
expect(enableNotebookBtn.tooltipText).toBe(
"Notebooks are not yet available in your account's region. View supported regions here: https://aka.ms/cosmos-enable-notebooks."
);
//TODO: modify once notebooks are available
expect(enableNotebookBtn).toBeUndefined();
//expect(enableNotebookBtn).toBeDefined();
//expect(enableNotebookBtn.disabled).toBe(true);
//expect(enableNotebookBtn.tooltipText).toBe(
// "Notebooks are not yet available in your account's region. View supported regions here: https://aka.ms/cosmos-enable-notebooks."
//);
});
});
@@ -192,8 +198,11 @@ describe("CommandBarComponentButtonFactory tests", () => {
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
const openMongoShellBtn = buttons.find((button) => button.commandButtonLabel === openMongoShellBtnLabel);
expect(openMongoShellBtn).toBeDefined();
expect(openMongoShellBtn.disabled).toBe(false);
expect(openMongoShellBtn.tooltipText).toBe("");
//TODO: modify once notebooks are available
expect(openMongoShellBtn.disabled).toBe(true);
//expect(openMongoShellBtn.disabled).toBe(false);
//expect(openMongoShellBtn.tooltipText).toBe("");
});
it("Notebooks is enabled and is available - button should be shown and enabled", () => {
@@ -203,8 +212,11 @@ describe("CommandBarComponentButtonFactory tests", () => {
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
const openMongoShellBtn = buttons.find((button) => button.commandButtonLabel === openMongoShellBtnLabel);
expect(openMongoShellBtn).toBeDefined();
expect(openMongoShellBtn.disabled).toBe(false);
expect(openMongoShellBtn.tooltipText).toBe("");
//TODO: modify once notebooks are available
expect(openMongoShellBtn.disabled).toBe(true);
//expect(openMongoShellBtn.disabled).toBe(false);
//expect(openMongoShellBtn.tooltipText).toBe("");
});
it("Notebooks is enabled and is available, terminal is unavailable due to ipRules - button should be hidden", () => {
@@ -290,9 +302,13 @@ describe("CommandBarComponentButtonFactory tests", () => {
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
const openCassandraShellBtn = buttons.find((button) => button.commandButtonLabel === openCassandraShellBtnLabel);
expect(openCassandraShellBtn).toBeDefined();
expect(openCassandraShellBtn.disabled).toBe(false);
expect(openCassandraShellBtn.tooltipText).toBe("");
//TODO: modify once notebooks are available
expect(openCassandraShellBtn.disabled).toBe(true);
//expect(openCassandraShellBtn.disabled).toBe(false);
//expect(openCassandraShellBtn.tooltipText).toBe("");
});
it("Notebooks is enabled and is available - button should be shown and enabled", () => {
@@ -302,8 +318,11 @@ describe("CommandBarComponentButtonFactory tests", () => {
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
const openCassandraShellBtn = buttons.find((button) => button.commandButtonLabel === openCassandraShellBtnLabel);
expect(openCassandraShellBtn).toBeDefined();
expect(openCassandraShellBtn.disabled).toBe(false);
expect(openCassandraShellBtn.tooltipText).toBe("");
//TODO: modify once notebooks are available
expect(openCassandraShellBtn.disabled).toBe(true);
//expect(openCassandraShellBtn.disabled).toBe(false);
//expect(openCassandraShellBtn.tooltipText).toBe("");
});
});

View File

@@ -67,35 +67,50 @@ export function createStaticCommandBarButtons(
newCollectionBtn.children.push(newDatabaseBtn);
}
buttons.push(createDivider());
if (useNotebook.getState().isNotebookEnabled) {
buttons.push(createDivider());
const notebookButtons: CommandButtonComponentProps[] = [];
const newNotebookButton = createNewNotebookButton(container);
newNotebookButton.children = [createNewNotebookButton(container), createuploadNotebookButton(container)];
buttons.push(newNotebookButton);
notebookButtons.push(newNotebookButton);
if (container.notebookManager?.gitHubOAuthService) {
buttons.push(createManageGitHubAccountButton(container));
notebookButtons.push(createManageGitHubAccountButton(container));
}
buttons.push(createOpenTerminalButton(container));
notebookButtons.push(createOpenTerminalButton(container));
buttons.push(createNotebookWorkspaceResetButton(container));
notebookButtons.push(createNotebookWorkspaceResetButton(container));
if (
(userContext.apiType === "Mongo" &&
useNotebook.getState().isShellEnabled &&
selectedNodeState.isDatabaseNodeOrNoneSelected()) ||
userContext.apiType === "Cassandra"
) {
buttons.push(createDivider());
notebookButtons.push(createDivider());
if (userContext.apiType === "Cassandra") {
buttons.push(createOpenCassandraTerminalButton(container));
notebookButtons.push(createOpenCassandraTerminalButton(container));
} else {
buttons.push(createOpenMongoTerminalButton(container));
notebookButtons.push(createOpenMongoTerminalButton(container));
}
}
notebookButtons.forEach((btn) => {
if (userContext.features.notebooksTemporarilyDown) {
if (btn.commandButtonLabel.indexOf("Cassandra") !== -1) {
applyNotebooksTemporarilyDownStyle(btn, Constants.Notebook.cassandraShellTemporarilyDownMsg);
} else if (btn.commandButtonLabel.indexOf("Mongo") !== -1) {
applyNotebooksTemporarilyDownStyle(btn, Constants.Notebook.mongoShellTemporarilyDownMsg);
} else {
applyNotebooksTemporarilyDownStyle(btn, Constants.Notebook.temporarilyDownMsg);
}
}
buttons.push(btn);
});
} else {
if (!isRunningOnNationalCloud()) {
if (!isRunningOnNationalCloud() && !userContext.features.notebooksTemporarilyDown) {
buttons.push(createDivider());
buttons.push(createEnableNotebooksButton(container));
}
}
@@ -152,7 +167,9 @@ export function createContextCommandBarButtons(
onCommandClick: () => {
const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection();
if (useNotebook.getState().isShellEnabled) {
container.openNotebookTerminal(ViewModels.TerminalKind.Mongo);
if (!userContext.features.notebooksTemporarilyDown) {
container.openNotebookTerminal(ViewModels.TerminalKind.Mongo);
}
} else {
selectedCollection && selectedCollection.onNewMongoShellClick();
}
@@ -160,7 +177,13 @@ export function createContextCommandBarButtons(
commandButtonLabel: label,
ariaLabel: label,
hasPopup: true,
disabled: selectedNodeState.isDatabaseNodeOrNoneSelected() && userContext.apiType === "Mongo",
tooltipText:
useNotebook.getState().isShellEnabled && userContext.features.notebooksTemporarilyDown
? Constants.Notebook.mongoShellTemporarilyDownMsg
: undefined,
disabled:
(selectedNodeState.isDatabaseNodeOrNoneSelected() && userContext.apiType === "Mongo") ||
(useNotebook.getState().isShellEnabled && userContext.features.notebooksTemporarilyDown),
};
buttons.push(newMongoShellBtn);
}
@@ -388,6 +411,13 @@ export function createScriptCommandButtons(selectedNodeState: SelectedNodeState)
return buttons;
}
function applyNotebooksTemporarilyDownStyle(buttonProps: CommandButtonComponentProps, tooltip: string): void {
if (!buttonProps.isDivider) {
buttonProps.disabled = true;
buttonProps.tooltipText = tooltip;
}
}
function createNewNotebookButton(container: Explorer): CommandButtonComponentProps {
const label = "New Notebook";
return {

View File

@@ -22,6 +22,13 @@ import { MemoryTracker } from "./MemoryTrackerComponent";
export const convertButton = (btns: CommandButtonComponentProps[], backgroundColor: string): ICommandBarItemProps[] => {
const buttonHeightPx = StyleConstants.CommandBarButtonHeight;
const getFilter = (isDisabled: boolean): string => {
if (isDisabled) {
return StyleConstants.GrayScale;
}
return undefined;
};
return btns
.filter((btn) => btn)
.map(
@@ -37,6 +44,7 @@ export const convertButton = (btns: CommandButtonComponentProps[], backgroundCol
style: {
width: StyleConstants.CommandBarIconWidth, // 16
alignSelf: btn.iconName ? "baseline" : undefined,
filter: getFilter(btn.disabled),
},
imageProps: btn.iconSrc ? { src: btn.iconSrc, alt: btn.iconAlt } : undefined,
iconName: btn.iconName,
@@ -123,8 +131,12 @@ export const convertButton = (btns: CommandButtonComponentProps[], backgroundCol
width: 12,
paddingLeft: 1,
paddingTop: 6,
filter: getFilter(btn.disabled),
},
imageProps: {
src: ChevronDownIcon,
alt: btn.iconAlt,
},
imageProps: { src: ChevronDownIcon, alt: btn.iconAlt },
};
}

View File

@@ -6,7 +6,7 @@ import { Dropdown, IDropdownOption } from "@fluentui/react";
import * as React from "react";
import AnimateHeight from "react-animate-height";
import LoaderIcon from "../../../../images/circular_loader_black_16x16.gif";
import ClearIcon from "../../../../images/Clear.svg";
import ClearIcon from "../../../../images/Clear-1.svg";
import ErrorBlackIcon from "../../../../images/error_black.svg";
import ErrorRedIcon from "../../../../images/error_red.svg";
import infoBubbleIcon from "../../../../images/info-bubble-9x9.svg";

View File

@@ -6,7 +6,7 @@ import CodeMirrorEditor from "@nteract/stateful-components/lib/inputs/connected-
import { PassedEditorProps } from "@nteract/stateful-components/lib/inputs/editor";
import * as React from "react";
import { DndProvider } from "react-dnd";
import HTML5Backend from "react-dnd-html5-backend";
import { HTML5Backend } from "react-dnd-html5-backend";
import { connect } from "react-redux";
import { Dispatch } from "redux";
import { userContext } from "../../../UserContext";

View File

@@ -11,7 +11,6 @@ import {
DropTargetConnector,
DropTargetMonitor,
} from "react-dnd";
import { connect } from "react-redux";
import { Dispatch } from "redux";
import styled, { StyledComponent } from "styled-components";
@@ -123,9 +122,10 @@ export const cellTarget = {
drop(props: Props, monitor: DropTargetMonitor, component: any): void {
if (monitor) {
const hoverUpperHalf = isDragUpper(props, monitor, component.el);
const item: Props = monitor.getItem();
// DropTargetSpec monitor definition could be undefined. we'll need a check for monitor in order to pass validation.
props.moveCell({
id: monitor.getItem().id,
id: item.id,
destinationId: props.id,
above: hoverUpperHalf,
contentRef: props.contentRef,

View File

@@ -66,7 +66,7 @@ interface InitialProps {
}
// Redux
const makeMapStateToProps = (state: AppState, initialProps: InitialProps) => {
const makeMapStateToProps = (_state: AppState, initialProps: InitialProps) => {
const mapStateToProps = (state: AppState): StateProps => ({
isNotebookUntrusted: NotebookUtil.isNotebookUntrusted(state, initialProps.contentRef),
});

View File

@@ -550,6 +550,72 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
</Stack>
)}
{this.shouldShowAnalyticalStoreOptions() && (
<Stack className="panelGroupSpacing">
<Stack horizontal>
<Text className="panelTextBold" variant="small">
Analytical store
</Text>
<TooltipHost
directionalHint={DirectionalHint.bottomLeftEdge}
content={this.getAnalyticalStorageTooltipContent()}
>
<Icon iconName="Info" className="panelInfoIcon" />
</TooltipHost>
</Stack>
<Stack horizontal verticalAlign="center">
<input
className="panelRadioBtn"
checked={this.state.enableAnalyticalStore}
disabled={!this.isSynapseLinkEnabled()}
aria-label="Enable analytical store"
aria-checked={this.state.enableAnalyticalStore}
name="analyticalStore"
type="radio"
role="radio"
id="enableAnalyticalStoreBtn"
tabIndex={0}
onChange={this.onEnableAnalyticalStoreRadioBtnChange.bind(this)}
/>
<span className="panelRadioBtnLabel">On</span>
<input
className="panelRadioBtn"
checked={!this.state.enableAnalyticalStore}
disabled={!this.isSynapseLinkEnabled()}
aria-label="Disable analytical store"
aria-checked={!this.state.enableAnalyticalStore}
name="analyticalStore"
type="radio"
role="radio"
id="disableAnalyticalStoreBtn"
tabIndex={0}
onChange={this.onDisableAnalyticalStoreRadioBtnChange.bind(this)}
/>
<span className="panelRadioBtnLabel">Off</span>
</Stack>
{!this.isSynapseLinkEnabled() && (
<Stack className="panelGroupSpacing">
<Text variant="small">
Azure Synapse Link is required for creating an analytical store{" "}
{getCollectionName().toLocaleLowerCase()}. Enable Synapse Link for this Cosmos DB account.{" "}
<Link href="https://aka.ms/cosmosdb-synapselink" target="_blank">
Learn more
</Link>
</Text>
<DefaultButton
text="Enable"
onClick={() => this.props.explorer.openEnableSynapseLinkDialog()}
style={{ height: 27, width: 80 }}
styles={{ label: { fontSize: 12 } }}
/>
</Stack>
)}
</Stack>
)}
{userContext.apiType !== "Tables" && (
<CollapsibleSectionComponent
title="Advanced"
@@ -604,72 +670,6 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
}
/>
)}
{this.shouldShowAnalyticalStoreOptions() && (
<Stack className="panelGroupSpacing">
<Stack horizontal>
<Text className="panelTextBold" variant="small">
Analytical store
</Text>
<TooltipHost
directionalHint={DirectionalHint.bottomLeftEdge}
content={this.getAnalyticalStorageTooltipContent()}
>
<Icon iconName="Info" className="panelInfoIcon" />
</TooltipHost>
</Stack>
<Stack horizontal verticalAlign="center">
<input
className="panelRadioBtn"
checked={this.state.enableAnalyticalStore}
disabled={!this.isSynapseLinkEnabled()}
aria-label="Enable analytical store"
aria-checked={this.state.enableAnalyticalStore}
name="analyticalStore"
type="radio"
role="radio"
id="enableAnalyticalStoreBtn"
tabIndex={0}
onChange={this.onEnableAnalyticalStoreRadioBtnChange.bind(this)}
/>
<span className="panelRadioBtnLabel">On</span>
<input
className="panelRadioBtn"
checked={!this.state.enableAnalyticalStore}
disabled={!this.isSynapseLinkEnabled()}
aria-label="Disable analytical store"
aria-checked={!this.state.enableAnalyticalStore}
name="analyticalStore"
type="radio"
role="radio"
id="disableAnalyticalStoreBtn"
tabIndex={0}
onChange={this.onDisableAnalyticalStoreRadioBtnChange.bind(this)}
/>
<span className="panelRadioBtnLabel">Off</span>
</Stack>
{!this.isSynapseLinkEnabled() && (
<Stack className="panelGroupSpacing">
<Text variant="small">
Azure Synapse Link is required for creating an analytical store{" "}
{getCollectionName().toLocaleLowerCase()}. Enable Synapse Link for this Cosmos DB account.{" "}
<Link href="https://aka.ms/cosmosdb-synapselink" target="_blank">
Learn more
</Link>
</Text>
<DefaultButton
text="Enable"
onClick={() => this.props.explorer.openEnableSynapseLinkDialog()}
style={{ height: 27, width: 80 }}
styles={{ label: { fontSize: 12 } }}
/>
</Stack>
)}
</Stack>
)}
</Stack>
</CollapsibleSectionComponent>
)}

View File

@@ -105,7 +105,6 @@ export const PublishNotebookPane: FunctionComponent<PublishNotebookPaneAProps> =
notebookName,
notebookDescription,
notebookTags?.split(","),
author,
imageSrc,
content
);

View File

@@ -91,9 +91,7 @@ export const UploadItemsPane: FunctionComponent = () => {
accept="application/json"
multiple
tabIndex={0}
tooltip="Select one or more JSON files to upload. Each file can contain a single JSON document or an array of JSON
documents. The combined size of all files in an individual upload operation must be less than 2 MB. You
can perform multiple upload operations for larger data sets."
tooltip="Select one or more JSON files to upload. Each file can contain a single JSON document or an array of JSON documents. The combined size of all files in an individual upload operation must be less than 2 MB. You can perform multiple upload operations for larger data sets."
/>
{uploadFileData?.length > 0 && (
<div className="fileUploadSummaryContainer">

View File

@@ -83,7 +83,11 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
public render(): JSX.Element {
const mainItems = this.createMainItems();
const commonTaskItems = this.createCommonTaskItems();
const recentItems = this.createRecentItems();
let recentItems = this.createRecentItems();
if (userContext.features.notebooksTemporarilyDown) {
recentItems = recentItems.filter((item) => item.description !== "Notebook");
}
const tipsItems = this.createTipsItems();
const onClearRecent = this.clearMostRecent;
@@ -219,7 +223,7 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
});
}
if (useNotebook.getState().isNotebookEnabled) {
if (useNotebook.getState().isNotebookEnabled && !userContext.features.notebooksTemporarilyDown) {
heroes.push({
iconSrc: NewNotebookIcon,
title: "New Notebook",

View File

@@ -11,7 +11,7 @@ import NotebookIcon from "../../../images/notebook/Notebook-resource.svg";
import PublishIcon from "../../../images/notebook/publish_content.svg";
import RefreshIcon from "../../../images/refresh-cosmos.svg";
import CollectionIcon from "../../../images/tree-collection.svg";
import { Areas } from "../../Common/Constants";
import { Areas, Notebook } from "../../Common/Constants";
import { isPublicInternetAccessAllowed } from "../../Common/DatabaseAccountUtility";
import * as DataModels from "../../Contracts/DataModels";
import * as ViewModels from "../../Contracts/ViewModels";
@@ -121,23 +121,34 @@ export const ResourceTree: React.FC<ResourceTreeProps> = ({ container }: Resourc
children: [],
};
if (galleryContentRoot) {
notebooksTree.children.push(buildGalleryNotebooksTree());
}
if (userContext.features.notebooksTemporarilyDown) {
notebooksTree.children.push(buildNotebooksTemporarilyDownTree());
} else {
if (galleryContentRoot) {
notebooksTree.children.push(buildGalleryNotebooksTree());
}
if (myNotebooksContentRoot) {
notebooksTree.children.push(buildMyNotebooksTree());
}
if (myNotebooksContentRoot) {
notebooksTree.children.push(buildMyNotebooksTree());
}
if (container.notebookManager?.gitHubOAuthService.isLoggedIn()) {
// collapse all other notebook nodes
notebooksTree.children.forEach((node) => (node.isExpanded = false));
notebooksTree.children.push(buildGitHubNotebooksTree());
if (container.notebookManager?.gitHubOAuthService.isLoggedIn()) {
// collapse all other notebook nodes
notebooksTree.children.forEach((node) => (node.isExpanded = false));
notebooksTree.children.push(buildGitHubNotebooksTree());
}
}
return notebooksTree;
};
const buildNotebooksTemporarilyDownTree = (): TreeNode => {
return {
label: Notebook.temporarilyDownMsg,
className: "clickDisabled",
};
};
const buildGalleryNotebooksTree = (): TreeNode => {
return {
label: "Gallery",
@@ -503,7 +514,12 @@ export const ResourceTree: React.FC<ResourceTreeProps> = ({ container }: Resourc
contextMenu: ResourceTreeContextMenuButtonFactory.createCollectionContextMenuButton(container, collection),
});
if (isNotebookEnabled && userContext.apiType === "Mongo" && isPublicInternetAccessAllowed()) {
if (
isNotebookEnabled &&
userContext.apiType === "Mongo" &&
isPublicInternetAccessAllowed() &&
!userContext.features.notebooksTemporarilyDown
) {
children.push({
label: "Schema (Preview)",
onClick: collection.onSchemaAnalyzerClick.bind(collection),

View File

@@ -364,7 +364,6 @@ describe("Gallery", () => {
const name = "name";
const description = "description";
const tags = ["tag"];
const author = "author";
const thumbnailUrl = "thumbnailUrl";
const content = `{ "key": "value" }`;
const addLinkToNotebookViewer = true;
@@ -373,7 +372,7 @@ describe("Gallery", () => {
json: () => undefined as any,
});
const response = await junoClient.publishNotebook(name, description, tags, author, thumbnailUrl, content);
const response = await junoClient.publishNotebook(name, description, tags, thumbnailUrl, content);
const authorizationHeader = getAuthorizationHeader();
expect(response.status).toBe(HttpStatusCodes.OK);
@@ -391,7 +390,6 @@ describe("Gallery", () => {
name,
description,
tags,
author,
thumbnailUrl,
content: JSON.parse(content),
addLinkToNotebookViewer,

View File

@@ -61,7 +61,6 @@ export interface IPublishNotebookRequest {
name: string;
description: string;
tags: string[];
author: string;
thumbnailUrl: string;
content: any;
addLinkToNotebookViewer: boolean;
@@ -359,7 +358,6 @@ export class JunoClient {
name: string,
description: string,
tags: string[],
author: string,
thumbnailUrl: string,
content: string
): Promise<IJunoResponse<IGalleryItem>> {
@@ -370,7 +368,6 @@ export class JunoClient {
name,
description,
tags,
author,
thumbnailUrl,
content: JSON.parse(content),
addLinkToNotebookViewer: true,

View File

@@ -28,6 +28,7 @@ export type Features = {
readonly pr?: string;
readonly showMinRUSurvey: boolean;
readonly ttl90Days: boolean;
readonly notebooksTemporarilyDown: boolean;
};
export function extractFeatures(given = new URLSearchParams(window.location.search)): Features {
@@ -74,5 +75,6 @@ export function extractFeatures(given = new URLSearchParams(window.location.sear
autoscaleDefault: "true" === get("autoscaledefault"),
partitionKeyDefault: "true" === get("partitionkeytest"),
partitionKeyDefault2: "true" === get("pkpartitionkeytest"),
notebooksTemporarilyDown: "true" === get("notebookstemporarilydown", "true"),
};
}

View File

@@ -16,7 +16,7 @@ test("Mongo CRUD", async () => {
await explorer.click('[data-test="New Collection"]');
await explorer.fill('[aria-label="New database id"]', databaseId);
await explorer.fill('[aria-label="Collection id"]', containerId);
await explorer.fill('[aria-label="Shard key"]', "/pk");
await explorer.fill('[aria-label="Shard key"]', "pk");
await explorer.click("#sidePanelOkButton");
await explorer.click(`.nodeItem >> text=${databaseId}`);
await explorer.click(`.nodeItem >> text=${containerId}`);

View File

@@ -8,6 +8,8 @@
"noUnusedParameters": true
},
"files": [
"./src/Explorer/Graph/GraphExplorerComponent/GraphData.test.ts",
"./src/Common/Logger.test.ts",
"./src/AuthType.ts",
"./src/Bindings/ReactBindingHandler.ts",
"./src/Common/ArrayHashMap.ts",
@@ -55,6 +57,7 @@
"./src/Explorer/Notebook/NotebookContentItem.ts",
"./src/Explorer/Notebook/NotebookRenderer/AzureTheme.tsx",
"./src/Explorer/Notebook/NotebookRenderer/Prompt.tsx",
"./src/Explorer/Notebook/NotebookRenderer/PromptContent.test.tsx",
"./src/Explorer/Notebook/NotebookRenderer/PromptContent.tsx",
"./src/Explorer/Notebook/NotebookRenderer/StatusBar.tsx",
"./src/Explorer/Notebook/NotebookRenderer/decorators/CellCreator.tsx",
@@ -62,6 +65,7 @@
"./src/Explorer/Notebook/NotebookUtil.ts",
"./src/Explorer/Notebook/SchemaAnalyzer/SchemaAnalyzerSplashScreen.tsx",
"./src/Explorer/Notebook/SchemaAnalyzer/SchemaAnalyzerUtils.ts",
"./src/Explorer/Notebook/SecurityWarningBar/SecurityWarningBar.tsx",
"./src/Explorer/OpenFullScreen.test.tsx",
"./src/Explorer/OpenFullScreen.tsx",
"./src/Explorer/Panes/PanelContainerComponent.test.tsx",
@@ -82,10 +86,13 @@
"./src/Explorer/Tree/AccessibleVerticalList.ts",
"./src/GitHub/GitHubConnector.ts",
"./src/HostedExplorerChildFrame.ts",
"./src/Index.tsx",
"./src/Platform/Hosted/Authorization.ts",
"./src/Platform/Hosted/Components/MeControl.test.tsx",
"./src/Platform/Hosted/Components/MeControl.tsx",
"./src/Platform/Hosted/Components/SignInButton.tsx",
"./src/Platform/Hosted/Components/SwitchAccount.tsx",
"./src/Platform/Hosted/Components/SwitchSubscription.tsx",
"./src/Platform/Hosted/HostedUtils.test.ts",
"./src/Platform/Hosted/HostedUtils.ts",
"./src/Platform/Hosted/extractFeatures.test.ts",
@@ -129,17 +136,15 @@
"./src/hooks/useFullScreenURLs.tsx",
"./src/hooks/useGraphPhoto.tsx",
"./src/hooks/useNotebookSnapshotStore.ts",
"./src/hooks/usePortalAccessToken.tsx",
"./src/hooks/useNotificationConsole.ts",
"./src/hooks/useObservable.ts",
"./src/hooks/usePortalAccessToken.tsx",
"./src/hooks/useSidePanel.ts",
"./src/i18n.ts",
"./src/quickstart.ts",
"./src/setupTests.ts",
"./src/userContext.test.ts",
"src/Common/EntityValue.tsx",
"./src/Platform/Hosted/Components/SwitchAccount.tsx",
"./src/Platform/Hosted/Components/SwitchSubscription.tsx"
"src/Common/EntityValue.tsx"
],
"include": [
"src/CellOutputViewer/transforms/**/*",
@@ -165,4 +170,4 @@
"src/Terminal/**/*",
"src/Utils/arm/**/*"
]
}
}

View File

@@ -9,12 +9,13 @@ const HTMLInlineCSSWebpackPlugin = require("html-inline-css-webpack-plugin").def
const { EnvironmentPlugin } = require("webpack");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CopyWebpackPlugin = require("copy-webpack-plugin");
const CleanWebpackPlugin = require("clean-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const CaseSensitivePathsPlugin = require("case-sensitive-paths-webpack-plugin");
const CreateFileWebpack = require("create-file-webpack");
const childProcess = require("child_process");
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer").BundleAnalyzerPlugin;
const TerserPlugin = require("terser-webpack-plugin");
const webpack = require("webpack");
const isCI = require("is-ci");
const gitSha = childProcess.execSync("git rev-parse HEAD").toString("utf8");
@@ -109,7 +110,11 @@ module.exports = function (_env = {}, argv = {}) {
}
const plugins = [
new CleanWebpackPlugin(["dist"]),
new CleanWebpackPlugin(),
new webpack.ProvidePlugin({
process: "process/browser",
Buffer: ["buffer", "Buffer"],
}),
new CreateFileWebpack({
path: "./dist",
fileName: "version.txt",
@@ -210,23 +215,26 @@ module.exports = function (_env = {}, argv = {}) {
selfServe: "./src/SelfServe/SelfServe.tsx",
connectToGitHub: "./src/GitHub/GitHubConnector.ts",
},
node: {
util: true,
tls: "empty",
net: "empty",
fs: "empty",
},
output: {
chunkFilename: "[name].[chunkhash:6].js",
filename: "[name].[chunkhash:6].js",
path: path.resolve(__dirname, "dist"),
publicPath: "",
},
devtool: mode === "development" ? "cheap-eval-source-map" : "source-map",
devtool: mode === "development" ? "eval-source-map" : "source-map",
plugins,
module: {
rules,
},
resolve: {
alias: {
process: "process/browser",
},
fallback: {
crypto: false,
fs: false,
},
extensions: [".tsx", ".ts", ".js"],
},
optimization: {
@@ -244,7 +252,7 @@ module.exports = function (_env = {}, argv = {}) {
}),
],
},
watch: isCI || mode === "production" ? false : true,
watch: false,
// Hack since it is hard to disable watch entirely with webpack dev server https://github.com/webpack/webpack-dev-server/issues/1251#issuecomment-654240734
watchOptions: isCI ? { poll: 24 * 60 * 60 * 1000 } : {},
devServer: {