mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-10-13 15:28:05 +01:00
Merge branch 'master' of https://github.com/Azure/cosmos-explorer into migrate_querytablestab__to_react
This commit is contained in:
commit
e431692e0d
@ -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;
|
||||
|
@ -19,6 +19,10 @@
|
||||
.notebookHeader {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.clickDisabled {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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("");
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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 },
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -105,7 +105,6 @@ export const PublishNotebookPane: FunctionComponent<PublishNotebookPaneAProps> =
|
||||
notebookName,
|
||||
notebookDescription,
|
||||
notebookTags?.split(","),
|
||||
author,
|
||||
imageSrc,
|
||||
content
|
||||
);
|
||||
|
@ -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",
|
||||
|
@ -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),
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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"),
|
||||
};
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user