diff --git a/src/Common/Constants.ts b/src/Common/Constants.ts index cbdd2da8e..293e1b3e8 100644 --- a/src/Common/Constants.ts +++ b/src/Common/Constants.ts @@ -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 { diff --git a/src/Explorer/ContextMenuButtonFactory.tsx b/src/Explorer/ContextMenuButtonFactory.tsx index 70d02e5aa..a52254cc3 100644 --- a/src/Explorer/ContextMenuButtonFactory.tsx +++ b/src/Explorer/ContextMenuButtonFactory.tsx @@ -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) { diff --git a/src/Explorer/Menus/CommandBar/CommandBarComponentButtonFactory.tsx b/src/Explorer/Menus/CommandBar/CommandBarComponentButtonFactory.tsx index 305c5e899..f1c0afc92 100644 --- a/src/Explorer/Menus/CommandBar/CommandBarComponentButtonFactory.tsx +++ b/src/Explorer/Menus/CommandBar/CommandBarComponentButtonFactory.tsx @@ -70,30 +70,45 @@ export function createStaticCommandBarButtons( buttons.push(createDivider()); if (useNotebook.getState().isNotebookEnabled) { + 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()) { 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 { diff --git a/src/Explorer/SplashScreen/SplashScreen.tsx b/src/Explorer/SplashScreen/SplashScreen.tsx index a84e53e8f..b52593707 100644 --- a/src/Explorer/SplashScreen/SplashScreen.tsx +++ b/src/Explorer/SplashScreen/SplashScreen.tsx @@ -83,7 +83,11 @@ export class SplashScreen extends React.Component { 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 { }); } - if (useNotebook.getState().isNotebookEnabled) { + if (useNotebook.getState().isNotebookEnabled && !userContext.features.notebooksTemporarilyDown) { heroes.push({ iconSrc: NewNotebookIcon, title: "New Notebook", diff --git a/src/Explorer/Tree/ResourceTree.tsx b/src/Explorer/Tree/ResourceTree.tsx index b8dae8259..f6f05bde2 100644 --- a/src/Explorer/Tree/ResourceTree.tsx +++ b/src/Explorer/Tree/ResourceTree.tsx @@ -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,33 @@ export const ResourceTree: React.FC = ({ 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, + }; + }; + const buildGalleryNotebooksTree = (): TreeNode => { return { label: "Gallery", @@ -503,7 +513,12 @@ export const ResourceTree: React.FC = ({ 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), diff --git a/src/Platform/Hosted/extractFeatures.ts b/src/Platform/Hosted/extractFeatures.ts index c7ea359ab..5c1e0f789 100644 --- a/src/Platform/Hosted/extractFeatures.ts +++ b/src/Platform/Hosted/extractFeatures.ts @@ -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"), }; }