Move notebook flags to zustand (#912)
This commit is contained in:
parent
98d7bb37d5
commit
db34024259
|
@ -16,6 +16,7 @@ import { userContext } from "../UserContext";
|
||||||
import { getCollectionName, getDatabaseName } from "../Utils/APITypeUtils";
|
import { getCollectionName, getDatabaseName } from "../Utils/APITypeUtils";
|
||||||
import { TreeNodeMenuItem } from "./Controls/TreeComponent/TreeComponent";
|
import { TreeNodeMenuItem } from "./Controls/TreeComponent/TreeComponent";
|
||||||
import Explorer from "./Explorer";
|
import Explorer from "./Explorer";
|
||||||
|
import { useNotebook } from "./Notebook/useNotebook";
|
||||||
import { DeleteCollectionConfirmationPane } from "./Panes/DeleteCollectionConfirmationPane/DeleteCollectionConfirmationPane";
|
import { DeleteCollectionConfirmationPane } from "./Panes/DeleteCollectionConfirmationPane/DeleteCollectionConfirmationPane";
|
||||||
import { DeleteDatabaseConfirmationPanel } from "./Panes/DeleteDatabaseConfirmationPanel";
|
import { DeleteDatabaseConfirmationPanel } from "./Panes/DeleteDatabaseConfirmationPanel";
|
||||||
import StoredProcedure from "./Tree/StoredProcedure";
|
import StoredProcedure from "./Tree/StoredProcedure";
|
||||||
|
@ -81,13 +82,13 @@ export const createCollectionContextMenuButton = (
|
||||||
iconSrc: HostedTerminalIcon,
|
iconSrc: HostedTerminalIcon,
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
const selectedCollection: ViewModels.Collection = useSelectedNode.getState().findSelectedCollection();
|
const selectedCollection: ViewModels.Collection = useSelectedNode.getState().findSelectedCollection();
|
||||||
if (container.isShellEnabled()) {
|
if (useNotebook.getState().isShellEnabled) {
|
||||||
container.openNotebookTerminal(ViewModels.TerminalKind.Mongo);
|
container.openNotebookTerminal(ViewModels.TerminalKind.Mongo);
|
||||||
} else {
|
} else {
|
||||||
selectedCollection && selectedCollection.onNewMongoShellClick();
|
selectedCollection && selectedCollection.onNewMongoShellClick();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
label: container.isShellEnabled() ? "Open Mongo Shell" : "New Shell",
|
label: useNotebook.getState().isShellEnabled ? "Open Mongo Shell" : "New Shell",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,17 +30,9 @@ exports[`SettingsComponent renders 1`] = `
|
||||||
"container": Explorer {
|
"container": Explorer {
|
||||||
"_isInitializingNotebooks": false,
|
"_isInitializingNotebooks": false,
|
||||||
"_resetNotebookWorkspace": [Function],
|
"_resetNotebookWorkspace": [Function],
|
||||||
"isAccountReady": [Function],
|
|
||||||
"isFixedCollectionWithSharedThroughputSupported": [Function],
|
"isFixedCollectionWithSharedThroughputSupported": [Function],
|
||||||
"isNotebookEnabled": [Function],
|
|
||||||
"isNotebooksEnabledForAccount": [Function],
|
|
||||||
"isSchemaEnabled": [Function],
|
"isSchemaEnabled": [Function],
|
||||||
"isShellEnabled": [Function],
|
|
||||||
"isSynapseLinkUpdating": [Function],
|
|
||||||
"isTabsContentExpanded": [Function],
|
"isTabsContentExpanded": [Function],
|
||||||
"memoryUsageInfo": [Function],
|
|
||||||
"notebookBasePath": [Function],
|
|
||||||
"notebookServerInfo": [Function],
|
|
||||||
"onRefreshDatabasesKeyPress": [Function],
|
"onRefreshDatabasesKeyPress": [Function],
|
||||||
"onRefreshResourcesClick": [Function],
|
"onRefreshResourcesClick": [Function],
|
||||||
"provideFeedbackEmail": [Function],
|
"provideFeedbackEmail": [Function],
|
||||||
|
@ -57,7 +49,6 @@ exports[`SettingsComponent renders 1`] = `
|
||||||
"container": [Circular],
|
"container": [Circular],
|
||||||
"parameters": [Function],
|
"parameters": [Function],
|
||||||
},
|
},
|
||||||
"sparkClusterConnectionInfo": [Function],
|
|
||||||
"tabsManager": TabsManager {
|
"tabsManager": TabsManager {
|
||||||
"activeTab": [Function],
|
"activeTab": [Function],
|
||||||
"openedTabs": [Function],
|
"openedTabs": [Function],
|
||||||
|
@ -115,17 +106,9 @@ exports[`SettingsComponent renders 1`] = `
|
||||||
"container": Explorer {
|
"container": Explorer {
|
||||||
"_isInitializingNotebooks": false,
|
"_isInitializingNotebooks": false,
|
||||||
"_resetNotebookWorkspace": [Function],
|
"_resetNotebookWorkspace": [Function],
|
||||||
"isAccountReady": [Function],
|
|
||||||
"isFixedCollectionWithSharedThroughputSupported": [Function],
|
"isFixedCollectionWithSharedThroughputSupported": [Function],
|
||||||
"isNotebookEnabled": [Function],
|
|
||||||
"isNotebooksEnabledForAccount": [Function],
|
|
||||||
"isSchemaEnabled": [Function],
|
"isSchemaEnabled": [Function],
|
||||||
"isShellEnabled": [Function],
|
|
||||||
"isSynapseLinkUpdating": [Function],
|
|
||||||
"isTabsContentExpanded": [Function],
|
"isTabsContentExpanded": [Function],
|
||||||
"memoryUsageInfo": [Function],
|
|
||||||
"notebookBasePath": [Function],
|
|
||||||
"notebookServerInfo": [Function],
|
|
||||||
"onRefreshDatabasesKeyPress": [Function],
|
"onRefreshDatabasesKeyPress": [Function],
|
||||||
"onRefreshResourcesClick": [Function],
|
"onRefreshResourcesClick": [Function],
|
||||||
"provideFeedbackEmail": [Function],
|
"provideFeedbackEmail": [Function],
|
||||||
|
@ -142,7 +125,6 @@ exports[`SettingsComponent renders 1`] = `
|
||||||
"container": [Circular],
|
"container": [Circular],
|
||||||
"parameters": [Function],
|
"parameters": [Function],
|
||||||
},
|
},
|
||||||
"sparkClusterConnectionInfo": [Function],
|
|
||||||
"tabsManager": TabsManager {
|
"tabsManager": TabsManager {
|
||||||
"activeTab": [Function],
|
"activeTab": [Function],
|
||||||
"openedTabs": [Function],
|
"openedTabs": [Function],
|
||||||
|
|
|
@ -30,7 +30,6 @@ import {
|
||||||
listConnectionInfo,
|
listConnectionInfo,
|
||||||
start,
|
start,
|
||||||
} from "../Utils/arm/generatedClients/cosmosNotebooks/notebookWorkspaces";
|
} from "../Utils/arm/generatedClients/cosmosNotebooks/notebookWorkspaces";
|
||||||
import { getAuthorizationHeader } from "../Utils/AuthorizationUtils";
|
|
||||||
import { stringToBlob } from "../Utils/BlobUtils";
|
import { stringToBlob } from "../Utils/BlobUtils";
|
||||||
import { isCapabilityEnabled } from "../Utils/CapabilityUtils";
|
import { isCapabilityEnabled } from "../Utils/CapabilityUtils";
|
||||||
import { fromContentUri, toRawContentUri } from "../Utils/GitHubUtils";
|
import { fromContentUri, toRawContentUri } from "../Utils/GitHubUtils";
|
||||||
|
@ -46,6 +45,7 @@ import { NotebookContentItem, NotebookContentItemType } from "./Notebook/Noteboo
|
||||||
import type NotebookManager from "./Notebook/NotebookManager";
|
import type NotebookManager from "./Notebook/NotebookManager";
|
||||||
import type { NotebookPaneContent } from "./Notebook/NotebookManager";
|
import type { NotebookPaneContent } from "./Notebook/NotebookManager";
|
||||||
import { NotebookUtil } from "./Notebook/NotebookUtil";
|
import { NotebookUtil } from "./Notebook/NotebookUtil";
|
||||||
|
import { useNotebook } from "./Notebook/useNotebook";
|
||||||
import { AddCollectionPanel } from "./Panes/AddCollectionPanel";
|
import { AddCollectionPanel } from "./Panes/AddCollectionPanel";
|
||||||
import { AddDatabasePanel } from "./Panes/AddDatabasePanel/AddDatabasePanel";
|
import { AddDatabasePanel } from "./Panes/AddDatabasePanel/AddDatabasePanel";
|
||||||
import { BrowseQueriesPane } from "./Panes/BrowseQueriesPane/BrowseQueriesPane";
|
import { BrowseQueriesPane } from "./Panes/BrowseQueriesPane/BrowseQueriesPane";
|
||||||
|
@ -79,7 +79,6 @@ export interface ExplorerParams {
|
||||||
|
|
||||||
export default class Explorer {
|
export default class Explorer {
|
||||||
public isFixedCollectionWithSharedThroughputSupported: ko.Computed<boolean>;
|
public isFixedCollectionWithSharedThroughputSupported: ko.Computed<boolean>;
|
||||||
public isAccountReady: ko.Observable<boolean>;
|
|
||||||
public queriesClient: QueriesClient;
|
public queriesClient: QueriesClient;
|
||||||
public tableDataClient: TableDataClient;
|
public tableDataClient: TableDataClient;
|
||||||
|
|
||||||
|
@ -97,18 +96,9 @@ export default class Explorer {
|
||||||
public isSchemaEnabled: ko.Computed<boolean>;
|
public isSchemaEnabled: ko.Computed<boolean>;
|
||||||
|
|
||||||
// Notebooks
|
// Notebooks
|
||||||
public isNotebookEnabled: ko.Observable<boolean>;
|
|
||||||
public isNotebooksEnabledForAccount: ko.Observable<boolean>;
|
|
||||||
public notebookServerInfo: ko.Observable<DataModels.NotebookWorkspaceConnectionInfo>;
|
|
||||||
public sparkClusterConnectionInfo: ko.Observable<DataModels.SparkClusterConnectionInfo>;
|
|
||||||
public isSynapseLinkUpdating: ko.Observable<boolean>;
|
|
||||||
public memoryUsageInfo: ko.Observable<DataModels.MemoryUsageInfo>;
|
|
||||||
public notebookManager?: NotebookManager;
|
public notebookManager?: NotebookManager;
|
||||||
|
|
||||||
public isShellEnabled: ko.Observable<boolean>;
|
|
||||||
|
|
||||||
private _isInitializingNotebooks: boolean;
|
private _isInitializingNotebooks: boolean;
|
||||||
private notebookBasePath: ko.Observable<string>;
|
|
||||||
private notebookToImport: {
|
private notebookToImport: {
|
||||||
name: string;
|
name: string;
|
||||||
content: string;
|
content: string;
|
||||||
|
@ -120,40 +110,11 @@ export default class Explorer {
|
||||||
const startKey: number = TelemetryProcessor.traceStart(Action.InitializeDataExplorer, {
|
const startKey: number = TelemetryProcessor.traceStart(Action.InitializeDataExplorer, {
|
||||||
dataExplorerArea: Constants.Areas.ResourceTree,
|
dataExplorerArea: Constants.Areas.ResourceTree,
|
||||||
});
|
});
|
||||||
this.isAccountReady = ko.observable<boolean>(false);
|
|
||||||
this._isInitializingNotebooks = false;
|
this._isInitializingNotebooks = false;
|
||||||
this.isShellEnabled = ko.observable(false);
|
useNotebook.subscribe(
|
||||||
this.isNotebooksEnabledForAccount = ko.observable(false);
|
() => this.refreshCommandBarButtons(),
|
||||||
this.isNotebooksEnabledForAccount.subscribe((isEnabledForAccount: boolean) => this.refreshCommandBarButtons());
|
(state) => state.isNotebooksEnabledForAccount
|
||||||
this.isSynapseLinkUpdating = ko.observable<boolean>(false);
|
);
|
||||||
this.isAccountReady.subscribe(async (isAccountReady: boolean) => {
|
|
||||||
if (isAccountReady) {
|
|
||||||
userContext.authType === AuthType.ResourceToken
|
|
||||||
? this.refreshDatabaseForResourceToken()
|
|
||||||
: this.refreshAllDatabases(true);
|
|
||||||
await this._refreshNotebooksEnabledStateForAccount();
|
|
||||||
this.isNotebookEnabled(
|
|
||||||
userContext.authType !== AuthType.ResourceToken &&
|
|
||||||
((await this._containsDefaultNotebookWorkspace(userContext.databaseAccount)) ||
|
|
||||||
userContext.features.enableNotebooks)
|
|
||||||
);
|
|
||||||
|
|
||||||
this.isShellEnabled(this.isNotebookEnabled() && isPublicInternetAccessAllowed());
|
|
||||||
|
|
||||||
TelemetryProcessor.trace(Action.NotebookEnabled, ActionModifiers.Mark, {
|
|
||||||
isNotebookEnabled: this.isNotebookEnabled(),
|
|
||||||
dataExplorerArea: Constants.Areas.Notebook,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (this.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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.memoryUsageInfo = ko.observable<DataModels.MemoryUsageInfo>();
|
|
||||||
|
|
||||||
this.queriesClient = new QueriesClient(this);
|
this.queriesClient = new QueriesClient(this);
|
||||||
this.isSchemaEnabled = ko.computed<boolean>(() => userContext.features.enableSchema);
|
this.isSchemaEnabled = ko.computed<boolean>(() => userContext.features.enableSchema);
|
||||||
|
@ -214,53 +175,44 @@ export default class Explorer {
|
||||||
startKey
|
startKey
|
||||||
);
|
);
|
||||||
|
|
||||||
this.isNotebookEnabled = ko.observable(false);
|
useNotebook.subscribe(
|
||||||
this.isNotebookEnabled.subscribe(async () => {
|
async () => {
|
||||||
if (!this.notebookManager) {
|
if (!this.notebookManager) {
|
||||||
const NotebookManager = await (
|
const NotebookManager = await (
|
||||||
await import(/* webpackChunkName: "NotebookManager" */ "./Notebook/NotebookManager")
|
await import(/* webpackChunkName: "NotebookManager" */ "./Notebook/NotebookManager")
|
||||||
).default;
|
).default;
|
||||||
this.notebookManager = new NotebookManager();
|
this.notebookManager = new NotebookManager();
|
||||||
this.notebookManager.initialize({
|
this.notebookManager.initialize({
|
||||||
container: this,
|
container: this,
|
||||||
notebookBasePath: this.notebookBasePath,
|
resourceTree: this.resourceTree,
|
||||||
resourceTree: this.resourceTree,
|
refreshCommandBarButtons: () => this.refreshCommandBarButtons(),
|
||||||
refreshCommandBarButtons: () => this.refreshCommandBarButtons(),
|
refreshNotebookList: () => this.refreshNotebookList(),
|
||||||
refreshNotebookList: () => this.refreshNotebookList(),
|
});
|
||||||
});
|
}
|
||||||
}
|
|
||||||
|
|
||||||
this.refreshCommandBarButtons();
|
this.refreshCommandBarButtons();
|
||||||
this.refreshNotebookList();
|
this.refreshNotebookList();
|
||||||
});
|
},
|
||||||
|
(state) => state.isNotebookEnabled
|
||||||
|
);
|
||||||
|
|
||||||
this.resourceTree = new ResourceTreeAdapter(this);
|
this.resourceTree = new ResourceTreeAdapter(this);
|
||||||
this.resourceTreeForResourceToken = new ResourceTreeAdapterForResourceToken(this);
|
this.resourceTreeForResourceToken = new ResourceTreeAdapterForResourceToken(this);
|
||||||
this.notebookServerInfo = ko.observable<DataModels.NotebookWorkspaceConnectionInfo>({
|
|
||||||
notebookServerEndpoint: undefined,
|
|
||||||
authToken: undefined,
|
|
||||||
});
|
|
||||||
this.notebookBasePath = ko.observable(Constants.Notebook.defaultBasePath);
|
|
||||||
this.sparkClusterConnectionInfo = ko.observable<DataModels.SparkClusterConnectionInfo>({
|
|
||||||
userName: undefined,
|
|
||||||
password: undefined,
|
|
||||||
endpoints: [],
|
|
||||||
});
|
|
||||||
|
|
||||||
// Override notebook server parameters from URL parameters
|
// Override notebook server parameters from URL parameters
|
||||||
if (userContext.features.notebookServerUrl && userContext.features.notebookServerToken) {
|
if (userContext.features.notebookServerUrl && userContext.features.notebookServerToken) {
|
||||||
this.notebookServerInfo({
|
useNotebook.getState().setNotebookServerInfo({
|
||||||
notebookServerEndpoint: userContext.features.notebookServerUrl,
|
notebookServerEndpoint: userContext.features.notebookServerUrl,
|
||||||
authToken: userContext.features.notebookServerToken,
|
authToken: userContext.features.notebookServerToken,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (userContext.features.notebookBasePath) {
|
if (userContext.features.notebookBasePath) {
|
||||||
this.notebookBasePath(userContext.features.notebookBasePath);
|
useNotebook.getState().setNotebookBasePath(userContext.features.notebookBasePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (userContext.features.livyEndpoint) {
|
if (userContext.features.livyEndpoint) {
|
||||||
this.sparkClusterConnectionInfo({
|
useNotebook.getState().setSparkClusterConnectionInfo({
|
||||||
userName: undefined,
|
userName: undefined,
|
||||||
password: undefined,
|
password: undefined,
|
||||||
endpoints: [
|
endpoints: [
|
||||||
|
@ -275,7 +227,8 @@ export default class Explorer {
|
||||||
if (configContext.enableSchemaAnalyzer) {
|
if (configContext.enableSchemaAnalyzer) {
|
||||||
userContext.features.enableSchemaAnalyzer = true;
|
userContext.features.enableSchemaAnalyzer = true;
|
||||||
}
|
}
|
||||||
this.isAccountReady(true);
|
|
||||||
|
this.refreshExplorer();
|
||||||
}
|
}
|
||||||
|
|
||||||
public openEnableSynapseLinkDialog(): void {
|
public openEnableSynapseLinkDialog(): void {
|
||||||
|
@ -296,7 +249,7 @@ export default class Explorer {
|
||||||
const clearInProgressMessage = logConsoleProgress(
|
const clearInProgressMessage = logConsoleProgress(
|
||||||
"Enabling Azure Synapse Link for this account. This may take a few minutes before you can enable analytical store for this account."
|
"Enabling Azure Synapse Link for this account. This may take a few minutes before you can enable analytical store for this account."
|
||||||
);
|
);
|
||||||
this.isSynapseLinkUpdating(true);
|
useNotebook.getState().setIsSynapseLinkUpdating(true);
|
||||||
useDialog.getState().closeDialog();
|
useDialog.getState().closeDialog();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -315,7 +268,7 @@ export default class Explorer {
|
||||||
logConsoleError(`Enabling Azure Synapse Link for this account failed. ${getErrorMessage(error)}`);
|
logConsoleError(`Enabling Azure Synapse Link for this account failed. ${getErrorMessage(error)}`);
|
||||||
TelemetryProcessor.traceFailure(Action.EnableAzureSynapseLink, {}, startTime);
|
TelemetryProcessor.traceFailure(Action.EnableAzureSynapseLink, {}, startTime);
|
||||||
} finally {
|
} finally {
|
||||||
this.isSynapseLinkUpdating(false);
|
useNotebook.getState().setIsSynapseLinkUpdating(false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -464,18 +417,17 @@ export default class Explorer {
|
||||||
"default"
|
"default"
|
||||||
);
|
);
|
||||||
|
|
||||||
this.notebookServerInfo({
|
useNotebook.getState().setNotebookServerInfo({
|
||||||
notebookServerEndpoint: userContext.features.notebookServerUrl || connectionInfo.notebookServerEndpoint,
|
notebookServerEndpoint: userContext.features.notebookServerUrl || connectionInfo.notebookServerEndpoint,
|
||||||
authToken: userContext.features.notebookServerToken || connectionInfo.authToken,
|
authToken: userContext.features.notebookServerToken || connectionInfo.authToken,
|
||||||
});
|
});
|
||||||
this.notebookServerInfo.valueHasMutated();
|
|
||||||
this.refreshNotebookList();
|
this.refreshNotebookList();
|
||||||
|
|
||||||
this._isInitializingNotebooks = false;
|
this._isInitializingNotebooks = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public resetNotebookWorkspace() {
|
public resetNotebookWorkspace() {
|
||||||
if (!this.isNotebookEnabled() || !this.notebookManager?.notebookClient) {
|
if (!useNotebook.getState().isNotebookEnabled || !this.notebookManager?.notebookClient) {
|
||||||
handleError(
|
handleError(
|
||||||
"Attempt to reset notebook workspace, but notebook is not enabled",
|
"Attempt to reset notebook workspace, but notebook is not enabled",
|
||||||
"Explorer/resetNotebookWorkspace"
|
"Explorer/resetNotebookWorkspace"
|
||||||
|
@ -659,7 +611,7 @@ export default class Explorer {
|
||||||
}
|
}
|
||||||
|
|
||||||
public uploadFile(name: string, content: string, parent: NotebookContentItem): Promise<NotebookContentItem> {
|
public uploadFile(name: string, content: string, parent: NotebookContentItem): Promise<NotebookContentItem> {
|
||||||
if (!this.isNotebookEnabled() || !this.notebookManager?.notebookContentClient) {
|
if (!useNotebook.getState().isNotebookEnabled || !this.notebookManager?.notebookContentClient) {
|
||||||
const error = "Attempt to upload notebook, but notebook is not enabled";
|
const error = "Attempt to upload notebook, but notebook is not enabled";
|
||||||
handleError(error, "Explorer/uploadFile");
|
handleError(error, "Explorer/uploadFile");
|
||||||
throw new Error(error);
|
throw new Error(error);
|
||||||
|
@ -677,7 +629,7 @@ export default class Explorer {
|
||||||
const item = NotebookUtil.createNotebookContentItem(name, path, "file");
|
const item = NotebookUtil.createNotebookContentItem(name, path, "file");
|
||||||
const parent = this.resourceTree.myNotebooksContentRoot;
|
const parent = this.resourceTree.myNotebooksContentRoot;
|
||||||
|
|
||||||
if (parent && parent.children && this.isNotebookEnabled() && this.notebookManager?.notebookClient) {
|
if (parent && parent.children && useNotebook.getState().isNotebookEnabled && this.notebookManager?.notebookClient) {
|
||||||
const existingItem = _.find(parent.children, (node) => node.name === name);
|
const existingItem = _.find(parent.children, (node) => node.name === name);
|
||||||
if (existingItem) {
|
if (existingItem) {
|
||||||
return this.openNotebook(existingItem);
|
return this.openNotebook(existingItem);
|
||||||
|
@ -694,7 +646,7 @@ export default class Explorer {
|
||||||
public async importAndOpenContent(name: string, content: string): Promise<boolean> {
|
public async importAndOpenContent(name: string, content: string): Promise<boolean> {
|
||||||
const parent = this.resourceTree.myNotebooksContentRoot;
|
const parent = this.resourceTree.myNotebooksContentRoot;
|
||||||
|
|
||||||
if (parent && parent.children && this.isNotebookEnabled() && this.notebookManager?.notebookClient) {
|
if (parent && parent.children && useNotebook.getState().isNotebookEnabled && this.notebookManager?.notebookClient) {
|
||||||
if (this.notebookToImport && this.notebookToImport.name === name && this.notebookToImport.content === content) {
|
if (this.notebookToImport && this.notebookToImport.name === name && this.notebookToImport.content === content) {
|
||||||
this.notebookToImport = undefined; // we don't want to try opening this notebook again
|
this.notebookToImport = undefined; // we don't want to try opening this notebook again
|
||||||
}
|
}
|
||||||
|
@ -837,7 +789,7 @@ export default class Explorer {
|
||||||
}
|
}
|
||||||
|
|
||||||
public renameNotebook(notebookFile: NotebookContentItem): void {
|
public renameNotebook(notebookFile: NotebookContentItem): void {
|
||||||
if (!this.isNotebookEnabled() || !this.notebookManager?.notebookContentClient) {
|
if (!useNotebook.getState().isNotebookEnabled || !this.notebookManager?.notebookContentClient) {
|
||||||
const error = "Attempt to rename notebook, but notebook is not enabled";
|
const error = "Attempt to rename notebook, but notebook is not enabled";
|
||||||
handleError(error, "Explorer/renameNotebook");
|
handleError(error, "Explorer/renameNotebook");
|
||||||
throw new Error(error);
|
throw new Error(error);
|
||||||
|
@ -878,7 +830,7 @@ export default class Explorer {
|
||||||
}
|
}
|
||||||
|
|
||||||
public onCreateDirectory(parent: NotebookContentItem): void {
|
public onCreateDirectory(parent: NotebookContentItem): void {
|
||||||
if (!this.isNotebookEnabled() || !this.notebookManager?.notebookContentClient) {
|
if (!useNotebook.getState().isNotebookEnabled || !this.notebookManager?.notebookContentClient) {
|
||||||
const error = "Attempt to create notebook directory, but notebook is not enabled";
|
const error = "Attempt to create notebook directory, but notebook is not enabled";
|
||||||
handleError(error, "Explorer/onCreateDirectory");
|
handleError(error, "Explorer/onCreateDirectory");
|
||||||
throw new Error(error);
|
throw new Error(error);
|
||||||
|
@ -908,7 +860,7 @@ export default class Explorer {
|
||||||
}
|
}
|
||||||
|
|
||||||
public readFile(notebookFile: NotebookContentItem): Promise<string> {
|
public readFile(notebookFile: NotebookContentItem): Promise<string> {
|
||||||
if (!this.isNotebookEnabled() || !this.notebookManager?.notebookContentClient) {
|
if (!useNotebook.getState().isNotebookEnabled || !this.notebookManager?.notebookContentClient) {
|
||||||
const error = "Attempt to read file, but notebook is not enabled";
|
const error = "Attempt to read file, but notebook is not enabled";
|
||||||
handleError(error, "Explorer/downloadFile");
|
handleError(error, "Explorer/downloadFile");
|
||||||
throw new Error(error);
|
throw new Error(error);
|
||||||
|
@ -918,7 +870,7 @@ export default class Explorer {
|
||||||
}
|
}
|
||||||
|
|
||||||
public downloadFile(notebookFile: NotebookContentItem): Promise<void> {
|
public downloadFile(notebookFile: NotebookContentItem): Promise<void> {
|
||||||
if (!this.isNotebookEnabled() || !this.notebookManager?.notebookContentClient) {
|
if (!useNotebook.getState().isNotebookEnabled || !this.notebookManager?.notebookContentClient) {
|
||||||
const error = "Attempt to download file, but notebook is not enabled";
|
const error = "Attempt to download file, but notebook is not enabled";
|
||||||
handleError(error, "Explorer/downloadFile");
|
handleError(error, "Explorer/downloadFile");
|
||||||
throw new Error(error);
|
throw new Error(error);
|
||||||
|
@ -955,56 +907,8 @@ export default class Explorer {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _refreshNotebooksEnabledStateForAccount(): Promise<void> {
|
|
||||||
const { databaseAccount, authType } = userContext;
|
|
||||||
if (
|
|
||||||
authType === AuthType.EncryptedToken ||
|
|
||||||
authType === AuthType.ResourceToken ||
|
|
||||||
authType === AuthType.MasterKey
|
|
||||||
) {
|
|
||||||
this.isNotebooksEnabledForAccount(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const firstWriteLocation =
|
|
||||||
databaseAccount?.properties?.writeLocations &&
|
|
||||||
databaseAccount?.properties?.writeLocations[0]?.locationName.toLowerCase();
|
|
||||||
const disallowedLocationsUri = `${configContext.BACKEND_ENDPOINT}/api/disallowedLocations`;
|
|
||||||
const authorizationHeader = getAuthorizationHeader();
|
|
||||||
try {
|
|
||||||
const response = await fetch(disallowedLocationsUri, {
|
|
||||||
method: "POST",
|
|
||||||
body: JSON.stringify({
|
|
||||||
resourceTypes: [Constants.ArmResourceTypes.notebookWorkspaces],
|
|
||||||
}),
|
|
||||||
headers: {
|
|
||||||
[authorizationHeader.header]: authorizationHeader.token,
|
|
||||||
[Constants.HttpHeaders.contentType]: "application/json",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error("Failed to fetch disallowed locations");
|
|
||||||
}
|
|
||||||
|
|
||||||
const disallowedLocations: string[] = await response.json();
|
|
||||||
if (!disallowedLocations) {
|
|
||||||
Logger.logInfo("No disallowed locations found", "Explorer/isNotebooksEnabledForAccount");
|
|
||||||
this.isNotebooksEnabledForAccount(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// firstWriteLocation should not be disallowed
|
|
||||||
const isAccountInAllowedLocation = firstWriteLocation && disallowedLocations.indexOf(firstWriteLocation) === -1;
|
|
||||||
this.isNotebooksEnabledForAccount(isAccountInAllowedLocation);
|
|
||||||
} catch (error) {
|
|
||||||
Logger.logError(getErrorMessage(error), "Explorer/isNotebooksEnabledForAccount");
|
|
||||||
this.isNotebooksEnabledForAccount(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private refreshNotebookList = async (): Promise<void> => {
|
private refreshNotebookList = async (): Promise<void> => {
|
||||||
if (!this.isNotebookEnabled() || !this.notebookManager?.notebookContentClient) {
|
if (!useNotebook.getState().isNotebookEnabled || !this.notebookManager?.notebookContentClient) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1016,7 +920,7 @@ export default class Explorer {
|
||||||
};
|
};
|
||||||
|
|
||||||
public deleteNotebookFile(item: NotebookContentItem): Promise<void> {
|
public deleteNotebookFile(item: NotebookContentItem): Promise<void> {
|
||||||
if (!this.isNotebookEnabled() || !this.notebookManager?.notebookContentClient) {
|
if (!useNotebook.getState().isNotebookEnabled || !this.notebookManager?.notebookContentClient) {
|
||||||
const error = "Attempt to delete notebook file, but notebook is not enabled";
|
const error = "Attempt to delete notebook file, but notebook is not enabled";
|
||||||
handleError(error, "Explorer/deleteNotebookFile");
|
handleError(error, "Explorer/deleteNotebookFile");
|
||||||
throw new Error(error);
|
throw new Error(error);
|
||||||
|
@ -1057,7 +961,7 @@ export default class Explorer {
|
||||||
* This creates a new notebook file, then opens the notebook
|
* This creates a new notebook file, then opens the notebook
|
||||||
*/
|
*/
|
||||||
public onNewNotebookClicked(parent?: NotebookContentItem): void {
|
public onNewNotebookClicked(parent?: NotebookContentItem): void {
|
||||||
if (!this.isNotebookEnabled() || !this.notebookManager?.notebookContentClient) {
|
if (!useNotebook.getState().isNotebookEnabled || !this.notebookManager?.notebookContentClient) {
|
||||||
const error = "Attempt to create new notebook, but notebook is not enabled";
|
const error = "Attempt to create new notebook, but notebook is not enabled";
|
||||||
handleError(error, "Explorer/onNewNotebookClicked");
|
handleError(error, "Explorer/onNewNotebookClicked");
|
||||||
throw new Error(error);
|
throw new Error(error);
|
||||||
|
@ -1101,7 +1005,7 @@ export default class Explorer {
|
||||||
}
|
}
|
||||||
|
|
||||||
public refreshContentItem(item: NotebookContentItem): Promise<void> {
|
public refreshContentItem(item: NotebookContentItem): Promise<void> {
|
||||||
if (!this.isNotebookEnabled() || !this.notebookManager?.notebookContentClient) {
|
if (!useNotebook.getState().isNotebookEnabled || !this.notebookManager?.notebookContentClient) {
|
||||||
const error = "Attempt to refresh notebook list, but notebook is not enabled";
|
const error = "Attempt to refresh notebook list, but notebook is not enabled";
|
||||||
handleError(error, "Explorer/refreshContentItem");
|
handleError(error, "Explorer/refreshContentItem");
|
||||||
return Promise.reject(new Error(error));
|
return Promise.reject(new Error(error));
|
||||||
|
@ -1110,10 +1014,6 @@ export default class Explorer {
|
||||||
return this.notebookManager?.notebookContentClient.updateItemChildren(item);
|
return this.notebookManager?.notebookContentClient.updateItemChildren(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getNotebookBasePath(): string {
|
|
||||||
return this.notebookBasePath();
|
|
||||||
}
|
|
||||||
|
|
||||||
public openNotebookTerminal(kind: ViewModels.TerminalKind) {
|
public openNotebookTerminal(kind: ViewModels.TerminalKind) {
|
||||||
let title: string;
|
let title: string;
|
||||||
|
|
||||||
|
@ -1233,7 +1133,7 @@ export default class Explorer {
|
||||||
}
|
}
|
||||||
|
|
||||||
public async handleOpenFileAction(path: string): Promise<void> {
|
public async handleOpenFileAction(path: string): Promise<void> {
|
||||||
if (this.isAccountReady() && !(await this._containsDefaultNotebookWorkspace(userContext.databaseAccount))) {
|
if (!(await this._containsDefaultNotebookWorkspace(userContext.databaseAccount))) {
|
||||||
this._openSetupNotebooksPaneForQuickstart();
|
this._openSetupNotebooksPaneForQuickstart();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1304,4 +1204,29 @@ export default class Explorer {
|
||||||
.getState()
|
.getState()
|
||||||
.openSidePanel(title, <SetupNoteBooksPanel explorer={this} panelTitle={title} panelDescription={description} />);
|
.openSidePanel(title, <SetupNoteBooksPanel explorer={this} panelTitle={title} panelDescription={description} />);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async refreshExplorer(): Promise<void> {
|
||||||
|
userContext.authType === AuthType.ResourceToken
|
||||||
|
? this.refreshDatabaseForResourceToken()
|
||||||
|
: this.refreshAllDatabases(true);
|
||||||
|
await useNotebook.getState().refreshNotebooksEnabledStateForAccount();
|
||||||
|
const isNotebookEnabled: boolean =
|
||||||
|
userContext.authType !== AuthType.ResourceToken &&
|
||||||
|
((await this._containsDefaultNotebookWorkspace(userContext.databaseAccount)) ||
|
||||||
|
userContext.features.enableNotebooks);
|
||||||
|
useNotebook.getState().setIsNotebookEnabled(isNotebookEnabled);
|
||||||
|
useNotebook.getState().setIsShellEnabled(isNotebookEnabled && isPublicInternetAccessAllowed());
|
||||||
|
|
||||||
|
TelemetryProcessor.trace(Action.NotebookEnabled, ActionModifiers.Mark, {
|
||||||
|
isNotebookEnabled,
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,7 +54,7 @@ export const CommandBar: React.FC<Props> = ({ container }: Props) => {
|
||||||
uiFabricControlButtons.forEach((btn: ICommandBarItemProps) => (btn.iconOnly = true));
|
uiFabricControlButtons.forEach((btn: ICommandBarItemProps) => (btn.iconOnly = true));
|
||||||
|
|
||||||
if (container.tabsManager.activeTab()?.tabKind === ViewModels.CollectionTabKind.NotebookV2) {
|
if (container.tabsManager.activeTab()?.tabKind === ViewModels.CollectionTabKind.NotebookV2) {
|
||||||
uiFabricControlButtons.unshift(CommandBarUtil.createMemoryTracker("memoryTracker", container.memoryUsageInfo));
|
uiFabricControlButtons.unshift(CommandBarUtil.createMemoryTracker("memoryTracker"));
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { GitHubOAuthService } from "../../../GitHub/GitHubOAuthService";
|
||||||
import { updateUserContext } from "../../../UserContext";
|
import { updateUserContext } from "../../../UserContext";
|
||||||
import Explorer from "../../Explorer";
|
import Explorer from "../../Explorer";
|
||||||
import NotebookManager from "../../Notebook/NotebookManager";
|
import NotebookManager from "../../Notebook/NotebookManager";
|
||||||
|
import { useNotebook } from "../../Notebook/useNotebook";
|
||||||
import { useDatabases } from "../../useDatabases";
|
import { useDatabases } from "../../useDatabases";
|
||||||
import { useSelectedNode } from "../../useSelectedNode";
|
import { useSelectedNode } from "../../useSelectedNode";
|
||||||
import * as CommandBarComponentButtonFactory from "./CommandBarComponentButtonFactory";
|
import * as CommandBarComponentButtonFactory from "./CommandBarComponentButtonFactory";
|
||||||
|
@ -28,9 +29,6 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||||
},
|
},
|
||||||
} as DatabaseAccount,
|
} as DatabaseAccount,
|
||||||
});
|
});
|
||||||
mockExplorer.isSynapseLinkUpdating = ko.observable(false);
|
|
||||||
mockExplorer.isNotebookEnabled = ko.observable(false);
|
|
||||||
mockExplorer.isNotebooksEnabledForAccount = ko.observable(false);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Account is not serverless - button should be visible", () => {
|
it("Account is not serverless - button should be visible", () => {
|
||||||
|
@ -71,18 +69,19 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||||
},
|
},
|
||||||
} as DatabaseAccount,
|
} as DatabaseAccount,
|
||||||
});
|
});
|
||||||
mockExplorer.isSynapseLinkUpdating = ko.observable(false);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
updateUserContext({
|
updateUserContext({
|
||||||
portalEnv: "prod",
|
portalEnv: "prod",
|
||||||
});
|
});
|
||||||
|
useNotebook.getState().setIsNotebookEnabled(false);
|
||||||
|
useNotebook.getState().setIsNotebooksEnabledForAccount(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Notebooks is already enabled - button should be hidden", () => {
|
it("Notebooks is already enabled - button should be hidden", () => {
|
||||||
mockExplorer.isNotebookEnabled = ko.observable(true);
|
useNotebook.getState().setIsNotebookEnabled(true);
|
||||||
mockExplorer.isNotebooksEnabledForAccount = ko.observable(true);
|
useNotebook.getState().setIsNotebooksEnabledForAccount(true);
|
||||||
|
|
||||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||||
const enableNotebookBtn = buttons.find((button) => button.commandButtonLabel === enableNotebookBtnLabel);
|
const enableNotebookBtn = buttons.find((button) => button.commandButtonLabel === enableNotebookBtnLabel);
|
||||||
|
@ -90,8 +89,6 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Account is running on one of the national clouds - button should be hidden", () => {
|
it("Account is running on one of the national clouds - button should be hidden", () => {
|
||||||
mockExplorer.isNotebookEnabled = ko.observable(false);
|
|
||||||
mockExplorer.isNotebooksEnabledForAccount = ko.observable(false);
|
|
||||||
updateUserContext({
|
updateUserContext({
|
||||||
portalEnv: "mooncake",
|
portalEnv: "mooncake",
|
||||||
});
|
});
|
||||||
|
@ -102,8 +99,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Notebooks is not enabled but is available - button should be shown and enabled", () => {
|
it("Notebooks is not enabled but is available - button should be shown and enabled", () => {
|
||||||
mockExplorer.isNotebookEnabled = ko.observable(false);
|
useNotebook.getState().setIsNotebooksEnabledForAccount(true);
|
||||||
mockExplorer.isNotebooksEnabledForAccount = ko.observable(true);
|
|
||||||
|
|
||||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||||
const enableNotebookBtn = buttons.find((button) => button.commandButtonLabel === enableNotebookBtnLabel);
|
const enableNotebookBtn = buttons.find((button) => button.commandButtonLabel === enableNotebookBtnLabel);
|
||||||
|
@ -113,9 +109,6 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Notebooks is not enabled and is unavailable - button should be shown and disabled", () => {
|
it("Notebooks is not enabled and is unavailable - button should be shown and disabled", () => {
|
||||||
mockExplorer.isNotebookEnabled = ko.observable(false);
|
|
||||||
mockExplorer.isNotebooksEnabledForAccount = ko.observable(false);
|
|
||||||
|
|
||||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||||
const enableNotebookBtn = buttons.find((button) => button.commandButtonLabel === enableNotebookBtnLabel);
|
const enableNotebookBtn = buttons.find((button) => button.commandButtonLabel === enableNotebookBtnLabel);
|
||||||
expect(enableNotebookBtn).toBeDefined();
|
expect(enableNotebookBtn).toBeDefined();
|
||||||
|
@ -139,24 +132,25 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||||
},
|
},
|
||||||
} as DatabaseAccount,
|
} as DatabaseAccount,
|
||||||
});
|
});
|
||||||
mockExplorer.isSynapseLinkUpdating = ko.observable(false);
|
|
||||||
mockExplorer.isShellEnabled = ko.observable(true);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(() => {
|
afterAll(() => {
|
||||||
updateUserContext({
|
updateUserContext({
|
||||||
apiType: "SQL",
|
apiType: "SQL",
|
||||||
});
|
});
|
||||||
|
useNotebook.getState().setIsShellEnabled(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
updateUserContext({
|
updateUserContext({
|
||||||
apiType: "Mongo",
|
apiType: "Mongo",
|
||||||
});
|
});
|
||||||
mockExplorer.isNotebookEnabled = ko.observable(false);
|
useNotebook.getState().setIsShellEnabled(true);
|
||||||
mockExplorer.isNotebooksEnabledForAccount = ko.observable(false);
|
});
|
||||||
|
|
||||||
mockExplorer.isShellEnabled = ko.observable(true);
|
afterEach(() => {
|
||||||
|
useNotebook.getState().setIsNotebookEnabled(false);
|
||||||
|
useNotebook.getState().setIsNotebooksEnabledForAccount(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Mongo Api not available - button should be hidden", () => {
|
it("Mongo Api not available - button should be hidden", () => {
|
||||||
|
@ -185,7 +179,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Notebooks is not enabled and is available - button should be hidden", () => {
|
it("Notebooks is not enabled and is available - button should be hidden", () => {
|
||||||
mockExplorer.isNotebooksEnabledForAccount = ko.observable(true);
|
useNotebook.getState().setIsNotebooksEnabledForAccount(true);
|
||||||
|
|
||||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||||
const openMongoShellBtn = buttons.find((button) => button.commandButtonLabel === openMongoShellBtnLabel);
|
const openMongoShellBtn = buttons.find((button) => button.commandButtonLabel === openMongoShellBtnLabel);
|
||||||
|
@ -193,7 +187,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Notebooks is enabled and is unavailable - button should be shown and enabled", () => {
|
it("Notebooks is enabled and is unavailable - button should be shown and enabled", () => {
|
||||||
mockExplorer.isNotebookEnabled = ko.observable(true);
|
useNotebook.getState().setIsNotebookEnabled(true);
|
||||||
|
|
||||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||||
const openMongoShellBtn = buttons.find((button) => button.commandButtonLabel === openMongoShellBtnLabel);
|
const openMongoShellBtn = buttons.find((button) => button.commandButtonLabel === openMongoShellBtnLabel);
|
||||||
|
@ -203,8 +197,8 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Notebooks is enabled and is available - button should be shown and enabled", () => {
|
it("Notebooks is enabled and is available - button should be shown and enabled", () => {
|
||||||
mockExplorer.isNotebookEnabled = ko.observable(true);
|
useNotebook.getState().setIsNotebookEnabled(true);
|
||||||
mockExplorer.isNotebooksEnabledForAccount = ko.observable(true);
|
useNotebook.getState().setIsNotebooksEnabledForAccount(true);
|
||||||
|
|
||||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||||
const openMongoShellBtn = buttons.find((button) => button.commandButtonLabel === openMongoShellBtnLabel);
|
const openMongoShellBtn = buttons.find((button) => button.commandButtonLabel === openMongoShellBtnLabel);
|
||||||
|
@ -214,9 +208,9 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Notebooks is enabled and is available, terminal is unavailable due to ipRules - button should be hidden", () => {
|
it("Notebooks is enabled and is available, terminal is unavailable due to ipRules - button should be hidden", () => {
|
||||||
mockExplorer.isNotebookEnabled = ko.observable(true);
|
useNotebook.getState().setIsNotebookEnabled(true);
|
||||||
mockExplorer.isNotebooksEnabledForAccount = ko.observable(true);
|
useNotebook.getState().setIsNotebooksEnabledForAccount(true);
|
||||||
mockExplorer.isShellEnabled = ko.observable(false);
|
useNotebook.getState().setIsShellEnabled(false);
|
||||||
|
|
||||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||||
const openMongoShellBtn = buttons.find((button) => button.commandButtonLabel === openMongoShellBtnLabel);
|
const openMongoShellBtn = buttons.find((button) => button.commandButtonLabel === openMongoShellBtnLabel);
|
||||||
|
@ -237,7 +231,6 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||||
},
|
},
|
||||||
} as DatabaseAccount,
|
} as DatabaseAccount,
|
||||||
});
|
});
|
||||||
mockExplorer.isSynapseLinkUpdating = ko.observable(false);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
@ -248,8 +241,11 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||||
},
|
},
|
||||||
} as DatabaseAccount,
|
} as DatabaseAccount,
|
||||||
});
|
});
|
||||||
mockExplorer.isNotebookEnabled = ko.observable(false);
|
});
|
||||||
mockExplorer.isNotebooksEnabledForAccount = ko.observable(false);
|
|
||||||
|
afterEach(() => {
|
||||||
|
useNotebook.getState().setIsNotebookEnabled(false);
|
||||||
|
useNotebook.getState().setIsNotebooksEnabledForAccount(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Cassandra Api not available - button should be hidden", () => {
|
it("Cassandra Api not available - button should be hidden", () => {
|
||||||
|
@ -283,7 +279,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Notebooks is not enabled and is available - button should be shown and enabled", () => {
|
it("Notebooks is not enabled and is available - button should be shown and enabled", () => {
|
||||||
mockExplorer.isNotebooksEnabledForAccount = ko.observable(true);
|
useNotebook.getState().setIsNotebooksEnabledForAccount(true);
|
||||||
|
|
||||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||||
const openCassandraShellBtn = buttons.find((button) => button.commandButtonLabel === openCassandraShellBtnLabel);
|
const openCassandraShellBtn = buttons.find((button) => button.commandButtonLabel === openCassandraShellBtnLabel);
|
||||||
|
@ -291,7 +287,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Notebooks is enabled and is unavailable - button should be shown and enabled", () => {
|
it("Notebooks is enabled and is unavailable - button should be shown and enabled", () => {
|
||||||
mockExplorer.isNotebookEnabled = ko.observable(true);
|
useNotebook.getState().setIsNotebookEnabled(true);
|
||||||
|
|
||||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||||
const openCassandraShellBtn = buttons.find((button) => button.commandButtonLabel === openCassandraShellBtnLabel);
|
const openCassandraShellBtn = buttons.find((button) => button.commandButtonLabel === openCassandraShellBtnLabel);
|
||||||
|
@ -301,8 +297,8 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Notebooks is enabled and is available - button should be shown and enabled", () => {
|
it("Notebooks is enabled and is available - button should be shown and enabled", () => {
|
||||||
mockExplorer.isNotebookEnabled = ko.observable(true);
|
useNotebook.getState().setIsNotebookEnabled(true);
|
||||||
mockExplorer.isNotebooksEnabledForAccount = ko.observable(true);
|
useNotebook.getState().setIsNotebooksEnabledForAccount(true);
|
||||||
|
|
||||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||||
const openCassandraShellBtn = buttons.find((button) => button.commandButtonLabel === openCassandraShellBtnLabel);
|
const openCassandraShellBtn = buttons.find((button) => button.commandButtonLabel === openCassandraShellBtnLabel);
|
||||||
|
@ -327,23 +323,17 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||||
} as DatabaseAccount,
|
} as DatabaseAccount,
|
||||||
});
|
});
|
||||||
|
|
||||||
mockExplorer.isSynapseLinkUpdating = ko.observable(false);
|
|
||||||
mockExplorer.isNotebooksEnabledForAccount = ko.observable(false);
|
|
||||||
|
|
||||||
mockExplorer.notebookManager = new NotebookManager();
|
mockExplorer.notebookManager = new NotebookManager();
|
||||||
mockExplorer.notebookManager.gitHubOAuthService = new GitHubOAuthService(undefined);
|
mockExplorer.notebookManager.gitHubOAuthService = new GitHubOAuthService(undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
mockExplorer.isNotebookEnabled = ko.observable(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
jest.resetAllMocks();
|
jest.resetAllMocks();
|
||||||
|
useNotebook.getState().setIsNotebookEnabled(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Notebooks is enabled and GitHubOAuthService is not logged in - connect to github button should be visible", () => {
|
it("Notebooks is enabled and GitHubOAuthService is not logged in - connect to github button should be visible", () => {
|
||||||
mockExplorer.isNotebookEnabled = ko.observable(true);
|
useNotebook.getState().setIsNotebookEnabled(true);
|
||||||
|
|
||||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||||
const connectToGitHubBtn = buttons.find((button) => button.commandButtonLabel === connectToGitHubBtnLabel);
|
const connectToGitHubBtn = buttons.find((button) => button.commandButtonLabel === connectToGitHubBtnLabel);
|
||||||
|
@ -351,7 +341,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Notebooks is enabled and GitHubOAuthService is logged in - manage github settings button should be visible", () => {
|
it("Notebooks is enabled and GitHubOAuthService is logged in - manage github settings button should be visible", () => {
|
||||||
mockExplorer.isNotebookEnabled = ko.observable(true);
|
useNotebook.getState().setIsNotebookEnabled(true);
|
||||||
mockExplorer.notebookManager.gitHubOAuthService.isLoggedIn = jest.fn().mockReturnValue(true);
|
mockExplorer.notebookManager.gitHubOAuthService.isLoggedIn = jest.fn().mockReturnValue(true);
|
||||||
|
|
||||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||||
|
|
|
@ -28,6 +28,7 @@ import { isServerlessAccount } from "../../../Utils/CapabilityUtils";
|
||||||
import { isRunningOnNationalCloud } from "../../../Utils/CloudUtils";
|
import { isRunningOnNationalCloud } from "../../../Utils/CloudUtils";
|
||||||
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
|
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
|
||||||
import Explorer from "../../Explorer";
|
import Explorer from "../../Explorer";
|
||||||
|
import { useNotebook } from "../../Notebook/useNotebook";
|
||||||
import { OpenFullScreen } from "../../OpenFullScreen";
|
import { OpenFullScreen } from "../../OpenFullScreen";
|
||||||
import { LoadQueryPane } from "../../Panes/LoadQueryPane/LoadQueryPane";
|
import { LoadQueryPane } from "../../Panes/LoadQueryPane/LoadQueryPane";
|
||||||
import { SettingsPane } from "../../Panes/SettingsPane/SettingsPane";
|
import { SettingsPane } from "../../Panes/SettingsPane/SettingsPane";
|
||||||
|
@ -63,7 +64,7 @@ export function createStaticCommandBarButtons(
|
||||||
|
|
||||||
buttons.push(createDivider());
|
buttons.push(createDivider());
|
||||||
|
|
||||||
if (container.isNotebookEnabled()) {
|
if (useNotebook.getState().isNotebookEnabled) {
|
||||||
const newNotebookButton = createNewNotebookButton(container);
|
const newNotebookButton = createNewNotebookButton(container);
|
||||||
newNotebookButton.children = [createNewNotebookButton(container), createuploadNotebookButton(container)];
|
newNotebookButton.children = [createNewNotebookButton(container), createuploadNotebookButton(container)];
|
||||||
buttons.push(newNotebookButton);
|
buttons.push(newNotebookButton);
|
||||||
|
@ -77,7 +78,7 @@ export function createStaticCommandBarButtons(
|
||||||
buttons.push(createNotebookWorkspaceResetButton(container));
|
buttons.push(createNotebookWorkspaceResetButton(container));
|
||||||
if (
|
if (
|
||||||
(userContext.apiType === "Mongo" &&
|
(userContext.apiType === "Mongo" &&
|
||||||
container.isShellEnabled() &&
|
useNotebook.getState().isShellEnabled &&
|
||||||
selectedNodeState.isDatabaseNodeOrNoneSelected()) ||
|
selectedNodeState.isDatabaseNodeOrNoneSelected()) ||
|
||||||
userContext.apiType === "Cassandra"
|
userContext.apiType === "Cassandra"
|
||||||
) {
|
) {
|
||||||
|
@ -139,13 +140,13 @@ export function createContextCommandBarButtons(
|
||||||
const buttons: CommandButtonComponentProps[] = [];
|
const buttons: CommandButtonComponentProps[] = [];
|
||||||
|
|
||||||
if (!selectedNodeState.isDatabaseNodeOrNoneSelected() && userContext.apiType === "Mongo") {
|
if (!selectedNodeState.isDatabaseNodeOrNoneSelected() && userContext.apiType === "Mongo") {
|
||||||
const label = container.isShellEnabled() ? "Open Mongo Shell" : "New Shell";
|
const label = useNotebook.getState().isShellEnabled ? "Open Mongo Shell" : "New Shell";
|
||||||
const newMongoShellBtn: CommandButtonComponentProps = {
|
const newMongoShellBtn: CommandButtonComponentProps = {
|
||||||
iconSrc: HostedTerminalIcon,
|
iconSrc: HostedTerminalIcon,
|
||||||
iconAlt: label,
|
iconAlt: label,
|
||||||
onCommandClick: () => {
|
onCommandClick: () => {
|
||||||
const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection();
|
const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection();
|
||||||
if (container.isShellEnabled()) {
|
if (useNotebook.getState().isShellEnabled) {
|
||||||
container.openNotebookTerminal(ViewModels.TerminalKind.Mongo);
|
container.openNotebookTerminal(ViewModels.TerminalKind.Mongo);
|
||||||
} else {
|
} else {
|
||||||
selectedCollection && selectedCollection.onNewMongoShellClick();
|
selectedCollection && selectedCollection.onNewMongoShellClick();
|
||||||
|
@ -270,7 +271,7 @@ function createOpenSynapseLinkDialogButton(container: Explorer): CommandButtonCo
|
||||||
onCommandClick: () => container.openEnableSynapseLinkDialog(),
|
onCommandClick: () => container.openEnableSynapseLinkDialog(),
|
||||||
commandButtonLabel: label,
|
commandButtonLabel: label,
|
||||||
hasPopup: false,
|
hasPopup: false,
|
||||||
disabled: container.isSynapseLinkUpdating(),
|
disabled: useNotebook.getState().isSynapseLinkUpdating,
|
||||||
ariaLabel: label,
|
ariaLabel: label,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -450,9 +451,9 @@ function createEnableNotebooksButton(container: Explorer): CommandButtonComponen
|
||||||
onCommandClick: () => container.openSetupNotebooksPanel(label, description),
|
onCommandClick: () => container.openSetupNotebooksPanel(label, description),
|
||||||
commandButtonLabel: label,
|
commandButtonLabel: label,
|
||||||
hasPopup: false,
|
hasPopup: false,
|
||||||
disabled: !container.isNotebooksEnabledForAccount(),
|
disabled: !useNotebook.getState().isNotebooksEnabledForAccount,
|
||||||
ariaLabel: label,
|
ariaLabel: label,
|
||||||
tooltipText: container.isNotebooksEnabledForAccount() ? "" : tooltip,
|
tooltipText: useNotebook.getState().isNotebooksEnabledForAccount ? "" : tooltip,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -476,12 +477,13 @@ function createOpenMongoTerminalButton(container: Explorer): CommandButtonCompon
|
||||||
const title = "Set up workspace";
|
const title = "Set up workspace";
|
||||||
const description =
|
const description =
|
||||||
"Looks like you have not created a workspace for this account. To proceed and start using features including mongo shell and notebook, we will need to create a default workspace in this account.";
|
"Looks like you have not created a workspace for this account. To proceed and start using features including mongo shell and notebook, we will need to create a default workspace in this account.";
|
||||||
const disableButton = !container.isNotebooksEnabledForAccount() && !container.isNotebookEnabled();
|
const disableButton =
|
||||||
|
!useNotebook.getState().isNotebooksEnabledForAccount && !useNotebook.getState().isNotebookEnabled;
|
||||||
return {
|
return {
|
||||||
iconSrc: HostedTerminalIcon,
|
iconSrc: HostedTerminalIcon,
|
||||||
iconAlt: label,
|
iconAlt: label,
|
||||||
onCommandClick: () => {
|
onCommandClick: () => {
|
||||||
if (container.isNotebookEnabled()) {
|
if (useNotebook.getState().isNotebookEnabled) {
|
||||||
container.openNotebookTerminal(ViewModels.TerminalKind.Mongo);
|
container.openNotebookTerminal(ViewModels.TerminalKind.Mongo);
|
||||||
} else {
|
} else {
|
||||||
container.openSetupNotebooksPanel(title, description);
|
container.openSetupNotebooksPanel(title, description);
|
||||||
|
@ -502,12 +504,13 @@ function createOpenCassandraTerminalButton(container: Explorer): CommandButtonCo
|
||||||
const title = "Set up workspace";
|
const title = "Set up workspace";
|
||||||
const description =
|
const description =
|
||||||
"Looks like you have not created a workspace for this account. To proceed and start using features including cassandra shell and notebook, we will need to create a default workspace in this account.";
|
"Looks like you have not created a workspace for this account. To proceed and start using features including cassandra shell and notebook, we will need to create a default workspace in this account.";
|
||||||
const disableButton = !container.isNotebooksEnabledForAccount() && !container.isNotebookEnabled();
|
const disableButton =
|
||||||
|
!useNotebook.getState().isNotebooksEnabledForAccount && !useNotebook.getState().isNotebookEnabled;
|
||||||
return {
|
return {
|
||||||
iconSrc: HostedTerminalIcon,
|
iconSrc: HostedTerminalIcon,
|
||||||
iconAlt: label,
|
iconAlt: label,
|
||||||
onCommandClick: () => {
|
onCommandClick: () => {
|
||||||
if (container.isNotebookEnabled()) {
|
if (useNotebook.getState().isNotebookEnabled) {
|
||||||
container.openNotebookTerminal(ViewModels.TerminalKind.Cassandra);
|
container.openNotebookTerminal(ViewModels.TerminalKind.Cassandra);
|
||||||
} else {
|
} else {
|
||||||
container.openSetupNotebooksPanel(title, description);
|
container.openSetupNotebooksPanel(title, description);
|
||||||
|
|
|
@ -6,16 +6,14 @@ import {
|
||||||
IDropdownOption,
|
IDropdownOption,
|
||||||
IDropdownStyles,
|
IDropdownStyles,
|
||||||
} from "@fluentui/react";
|
} from "@fluentui/react";
|
||||||
import { Observable } from "knockout";
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import _ from "underscore";
|
import _ from "underscore";
|
||||||
import ChevronDownIcon from "../../../../images/Chevron_down.svg";
|
import ChevronDownIcon from "../../../../images/Chevron_down.svg";
|
||||||
import { StyleConstants } from "../../../Common/Constants";
|
import { StyleConstants } from "../../../Common/Constants";
|
||||||
import { MemoryUsageInfo } from "../../../Contracts/DataModels";
|
|
||||||
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
|
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
|
||||||
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
|
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
|
||||||
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
|
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
|
||||||
import { MemoryTrackerComponent } from "./MemoryTrackerComponent";
|
import { MemoryTracker } from "./MemoryTrackerComponent";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert our NavbarButtonConfig to UI Fabric buttons
|
* Convert our NavbarButtonConfig to UI Fabric buttons
|
||||||
|
@ -185,12 +183,9 @@ export const createDivider = (key: string): ICommandBarItemProps => {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createMemoryTracker = (
|
export const createMemoryTracker = (key: string): ICommandBarItemProps => {
|
||||||
key: string,
|
|
||||||
memoryUsageInfo: Observable<MemoryUsageInfo>
|
|
||||||
): ICommandBarItemProps => {
|
|
||||||
return {
|
return {
|
||||||
key,
|
key,
|
||||||
onRender: () => <MemoryTrackerComponent memoryUsageInfo={memoryUsageInfo} />,
|
onRender: () => <MemoryTracker />,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,48 +1,29 @@
|
||||||
import { ProgressIndicator, Spinner, SpinnerSize, Stack } from "@fluentui/react";
|
import { ProgressIndicator, Spinner, SpinnerSize, Stack } from "@fluentui/react";
|
||||||
import { Observable, Subscription } from "knockout";
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { MemoryUsageInfo } from "../../../Contracts/DataModels";
|
import { useNotebook } from "../../Notebook/useNotebook";
|
||||||
|
|
||||||
interface MemoryTrackerProps {
|
|
||||||
memoryUsageInfo: Observable<MemoryUsageInfo>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class MemoryTrackerComponent extends React.Component<MemoryTrackerProps> {
|
|
||||||
private memoryUsageInfoSubscription: Subscription;
|
|
||||||
|
|
||||||
public componentDidMount(): void {
|
|
||||||
this.memoryUsageInfoSubscription = this.props.memoryUsageInfo.subscribe(() => {
|
|
||||||
this.forceUpdate();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public componentWillUnmount(): void {
|
|
||||||
this.memoryUsageInfoSubscription && this.memoryUsageInfoSubscription.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
public render(): JSX.Element {
|
|
||||||
const memoryUsageInfo: MemoryUsageInfo = this.props.memoryUsageInfo();
|
|
||||||
if (!memoryUsageInfo) {
|
|
||||||
return (
|
|
||||||
<Stack className="memoryTrackerContainer" horizontal>
|
|
||||||
<span>Memory</span>
|
|
||||||
<Spinner size={SpinnerSize.medium} />
|
|
||||||
</Stack>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const totalGB = memoryUsageInfo.totalKB / 1048576;
|
|
||||||
const usedGB = totalGB - memoryUsageInfo.freeKB / 1048576;
|
|
||||||
|
|
||||||
|
export const MemoryTracker: React.FC = (): JSX.Element => {
|
||||||
|
const memoryUsageInfo = useNotebook((state) => state.memoryUsageInfo);
|
||||||
|
if (!memoryUsageInfo) {
|
||||||
return (
|
return (
|
||||||
<Stack className="memoryTrackerContainer" horizontal>
|
<Stack className="memoryTrackerContainer" horizontal>
|
||||||
<span>Memory</span>
|
<span>Memory</span>
|
||||||
<ProgressIndicator
|
<Spinner size={SpinnerSize.medium} />
|
||||||
className={usedGB / totalGB > 0.8 ? "lowMemory" : ""}
|
|
||||||
description={usedGB.toFixed(1) + " of " + totalGB.toFixed(1) + " GB"}
|
|
||||||
percentComplete={usedGB / totalGB}
|
|
||||||
/>
|
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
const totalGB = memoryUsageInfo.totalKB / 1048576;
|
||||||
|
const usedGB = totalGB - memoryUsageInfo.freeKB / 1048576;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack className="memoryTrackerContainer" horizontal>
|
||||||
|
<span>Memory</span>
|
||||||
|
<ProgressIndicator
|
||||||
|
className={usedGB / totalGB > 0.8 ? "lowMemory" : ""}
|
||||||
|
description={usedGB.toFixed(1) + " of " + totalGB.toFixed(1) + " GB"}
|
||||||
|
percentComplete={usedGB / totalGB}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
|
@ -8,25 +8,26 @@ import * as DataModels from "../../Contracts/DataModels";
|
||||||
import { userContext } from "../../UserContext";
|
import { userContext } from "../../UserContext";
|
||||||
import { createOrUpdate, destroy } from "../../Utils/arm/generatedClients/cosmosNotebooks/notebookWorkspaces";
|
import { createOrUpdate, destroy } from "../../Utils/arm/generatedClients/cosmosNotebooks/notebookWorkspaces";
|
||||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||||
|
import { useNotebook } from "./useNotebook";
|
||||||
|
|
||||||
export class NotebookContainerClient {
|
export class NotebookContainerClient {
|
||||||
private clearReconnectionAttemptMessage? = () => {};
|
private clearReconnectionAttemptMessage? = () => {};
|
||||||
private isResettingWorkspace: boolean;
|
private isResettingWorkspace: boolean;
|
||||||
|
|
||||||
constructor(
|
constructor(private onConnectionLost: () => void) {
|
||||||
private notebookServerInfo: ko.Observable<DataModels.NotebookWorkspaceConnectionInfo>,
|
const notebookServerInfo = useNotebook.getState().notebookServerInfo;
|
||||||
private onConnectionLost: () => void,
|
if (notebookServerInfo?.notebookServerEndpoint) {
|
||||||
private onMemoryUsageInfoUpdate: (update: DataModels.MemoryUsageInfo) => void
|
|
||||||
) {
|
|
||||||
if (notebookServerInfo() && notebookServerInfo().notebookServerEndpoint) {
|
|
||||||
this.scheduleHeartbeat(Constants.Notebook.heartbeatDelayMs);
|
this.scheduleHeartbeat(Constants.Notebook.heartbeatDelayMs);
|
||||||
} else {
|
} else {
|
||||||
const subscription = notebookServerInfo.subscribe((newServerInfo: DataModels.NotebookWorkspaceConnectionInfo) => {
|
const unsub = useNotebook.subscribe(
|
||||||
if (newServerInfo && newServerInfo.notebookServerEndpoint) {
|
(newServerInfo: DataModels.NotebookWorkspaceConnectionInfo) => {
|
||||||
this.scheduleHeartbeat(Constants.Notebook.heartbeatDelayMs);
|
if (newServerInfo?.notebookServerEndpoint) {
|
||||||
}
|
this.scheduleHeartbeat(Constants.Notebook.heartbeatDelayMs);
|
||||||
subscription.dispose();
|
}
|
||||||
});
|
unsub();
|
||||||
|
},
|
||||||
|
(state) => state.notebookServerInfo
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,13 +37,14 @@ export class NotebookContainerClient {
|
||||||
private scheduleHeartbeat(delayMs: number): void {
|
private scheduleHeartbeat(delayMs: number): void {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.getMemoryUsage()
|
this.getMemoryUsage()
|
||||||
.then((memoryUsageInfo) => this.onMemoryUsageInfoUpdate(memoryUsageInfo))
|
.then((memoryUsageInfo) => useNotebook.getState().setMemoryUsageInfo(memoryUsageInfo))
|
||||||
.finally(() => this.scheduleHeartbeat(Constants.Notebook.heartbeatDelayMs));
|
.finally(() => this.scheduleHeartbeat(Constants.Notebook.heartbeatDelayMs));
|
||||||
}, delayMs);
|
}, delayMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getMemoryUsage(): Promise<DataModels.MemoryUsageInfo> {
|
private async getMemoryUsage(): Promise<DataModels.MemoryUsageInfo> {
|
||||||
if (!this.notebookServerInfo() || !this.notebookServerInfo().notebookServerEndpoint) {
|
const notebookServerInfo = useNotebook.getState().notebookServerInfo;
|
||||||
|
if (!notebookServerInfo || !notebookServerInfo.notebookServerEndpoint) {
|
||||||
const error = "No server endpoint detected";
|
const error = "No server endpoint detected";
|
||||||
Logger.logError(error, "NotebookContainerClient/getMemoryUsage");
|
Logger.logError(error, "NotebookContainerClient/getMemoryUsage");
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
|
@ -98,7 +100,8 @@ export class NotebookContainerClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _resetWorkspace(): Promise<void> {
|
private async _resetWorkspace(): Promise<void> {
|
||||||
if (!this.notebookServerInfo() || !this.notebookServerInfo().notebookServerEndpoint) {
|
const notebookServerInfo = useNotebook.getState().notebookServerInfo;
|
||||||
|
if (!notebookServerInfo || !notebookServerInfo.notebookServerEndpoint) {
|
||||||
const error = "No server endpoint detected";
|
const error = "No server endpoint detected";
|
||||||
Logger.logError(error, "NotebookContainerClient/resetWorkspace");
|
Logger.logError(error, "NotebookContainerClient/resetWorkspace");
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
|
@ -117,15 +120,11 @@ export class NotebookContainerClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
private getNotebookServerConfig(): { notebookServerEndpoint: string; authToken: string } {
|
private getNotebookServerConfig(): { notebookServerEndpoint: string; authToken: string } {
|
||||||
let authToken: string,
|
const notebookServerInfo = useNotebook.getState().notebookServerInfo;
|
||||||
notebookServerEndpoint = this.notebookServerInfo().notebookServerEndpoint,
|
const authToken: string = notebookServerInfo.authToken ? `Token ${notebookServerInfo.authToken}` : undefined;
|
||||||
token = this.notebookServerInfo().authToken;
|
|
||||||
if (token) {
|
|
||||||
authToken = `Token ${token}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
notebookServerEndpoint,
|
notebookServerEndpoint: notebookServerInfo.notebookServerEndpoint,
|
||||||
authToken,
|
authToken,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,14 @@
|
||||||
import { stringifyNotebook } from "@nteract/commutable";
|
import { stringifyNotebook } from "@nteract/commutable";
|
||||||
import { FileType, IContent, IContentProvider, IEmptyContent, ServerConfig } from "@nteract/core";
|
import { FileType, IContent, IContentProvider, IEmptyContent, ServerConfig } from "@nteract/core";
|
||||||
import { AjaxResponse } from "rxjs/ajax";
|
import { AjaxResponse } from "rxjs/ajax";
|
||||||
import * as DataModels from "../../Contracts/DataModels";
|
|
||||||
import * as StringUtils from "../../Utils/StringUtils";
|
import * as StringUtils from "../../Utils/StringUtils";
|
||||||
import * as FileSystemUtil from "./FileSystemUtil";
|
import * as FileSystemUtil from "./FileSystemUtil";
|
||||||
import { NotebookContentItem, NotebookContentItemType } from "./NotebookContentItem";
|
import { NotebookContentItem, NotebookContentItemType } from "./NotebookContentItem";
|
||||||
import { NotebookUtil } from "./NotebookUtil";
|
import { NotebookUtil } from "./NotebookUtil";
|
||||||
|
import { useNotebook } from "./useNotebook";
|
||||||
|
|
||||||
export class NotebookContentClient {
|
export class NotebookContentClient {
|
||||||
constructor(
|
constructor(private contentProvider: IContentProvider) {}
|
||||||
private notebookServerInfo: ko.Observable<DataModels.NotebookWorkspaceConnectionInfo>,
|
|
||||||
public notebookBasePath: ko.Observable<string>,
|
|
||||||
private contentProvider: IContentProvider
|
|
||||||
) {}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This updates the item and points all the children's parent to this item
|
* This updates the item and points all the children's parent to this item
|
||||||
|
@ -271,9 +267,10 @@ export class NotebookContentClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
private getServerConfig(): ServerConfig {
|
private getServerConfig(): ServerConfig {
|
||||||
|
const notebookServerInfo = useNotebook.getState().notebookServerInfo;
|
||||||
return {
|
return {
|
||||||
endpoint: this.notebookServerInfo().notebookServerEndpoint,
|
endpoint: notebookServerInfo.notebookServerEndpoint,
|
||||||
token: this.notebookServerInfo().authToken,
|
token: notebookServerInfo.authToken,
|
||||||
crossDomain: true,
|
crossDomain: true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,13 +4,11 @@
|
||||||
|
|
||||||
import { ImmutableNotebook } from "@nteract/commutable";
|
import { ImmutableNotebook } from "@nteract/commutable";
|
||||||
import type { IContentProvider } from "@nteract/core";
|
import type { IContentProvider } from "@nteract/core";
|
||||||
import ko from "knockout";
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { contents } from "rx-jupyter";
|
import { contents } from "rx-jupyter";
|
||||||
import { Areas, HttpStatusCodes } from "../../Common/Constants";
|
import { Areas, HttpStatusCodes } from "../../Common/Constants";
|
||||||
import { getErrorMessage } from "../../Common/ErrorHandlingUtils";
|
import { getErrorMessage } from "../../Common/ErrorHandlingUtils";
|
||||||
import * as Logger from "../../Common/Logger";
|
import * as Logger from "../../Common/Logger";
|
||||||
import { MemoryUsageInfo } from "../../Contracts/DataModels";
|
|
||||||
import { GitHubClient } from "../../GitHub/GitHubClient";
|
import { GitHubClient } from "../../GitHub/GitHubClient";
|
||||||
import { GitHubContentProvider } from "../../GitHub/GitHubContentProvider";
|
import { GitHubContentProvider } from "../../GitHub/GitHubContentProvider";
|
||||||
import { GitHubOAuthService } from "../../GitHub/GitHubOAuthService";
|
import { GitHubOAuthService } from "../../GitHub/GitHubOAuthService";
|
||||||
|
@ -37,7 +35,6 @@ export type { NotebookPaneContent };
|
||||||
|
|
||||||
export interface NotebookManagerOptions {
|
export interface NotebookManagerOptions {
|
||||||
container: Explorer;
|
container: Explorer;
|
||||||
notebookBasePath: ko.Observable<string>;
|
|
||||||
resourceTree: ResourceTreeAdapter;
|
resourceTree: ResourceTreeAdapter;
|
||||||
refreshCommandBarButtons: () => void;
|
refreshCommandBarButtons: () => void;
|
||||||
refreshNotebookList: () => void;
|
refreshNotebookList: () => void;
|
||||||
|
@ -81,17 +78,11 @@ export default class NotebookManager {
|
||||||
contents.JupyterContentProvider
|
contents.JupyterContentProvider
|
||||||
);
|
);
|
||||||
|
|
||||||
this.notebookClient = new NotebookContainerClient(
|
this.notebookClient = new NotebookContainerClient(() =>
|
||||||
this.params.container.notebookServerInfo,
|
this.params.container.initNotebooks(userContext?.databaseAccount)
|
||||||
() => this.params.container.initNotebooks(userContext?.databaseAccount),
|
|
||||||
(update: MemoryUsageInfo) => this.params.container.memoryUsageInfo(update)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
this.notebookContentClient = new NotebookContentClient(
|
this.notebookContentClient = new NotebookContentClient(this.notebookContentProvider);
|
||||||
this.params.container.notebookServerInfo,
|
|
||||||
this.params.notebookBasePath,
|
|
||||||
this.notebookContentProvider
|
|
||||||
);
|
|
||||||
|
|
||||||
this.gitHubOAuthService.getTokenObservable().subscribe((token) => {
|
this.gitHubOAuthService.getTokenObservable().subscribe((token) => {
|
||||||
this.gitHubClient.setToken(token?.access_token);
|
this.gitHubClient.setToken(token?.access_token);
|
||||||
|
|
|
@ -0,0 +1,106 @@
|
||||||
|
import create, { UseStore } from "zustand";
|
||||||
|
import { AuthType } from "../../AuthType";
|
||||||
|
import * as Constants from "../../Common/Constants";
|
||||||
|
import { getErrorMessage } from "../../Common/ErrorHandlingUtils";
|
||||||
|
import * as Logger from "../../Common/Logger";
|
||||||
|
import { configContext } from "../../ConfigContext";
|
||||||
|
import * as DataModels from "../../Contracts/DataModels";
|
||||||
|
import { userContext } from "../../UserContext";
|
||||||
|
import { getAuthorizationHeader } from "../../Utils/AuthorizationUtils";
|
||||||
|
|
||||||
|
interface NotebookState {
|
||||||
|
isNotebookEnabled: boolean;
|
||||||
|
isNotebooksEnabledForAccount: boolean;
|
||||||
|
notebookServerInfo: DataModels.NotebookWorkspaceConnectionInfo;
|
||||||
|
sparkClusterConnectionInfo: DataModels.SparkClusterConnectionInfo;
|
||||||
|
isSynapseLinkUpdating: boolean;
|
||||||
|
memoryUsageInfo: DataModels.MemoryUsageInfo;
|
||||||
|
isShellEnabled: boolean;
|
||||||
|
notebookBasePath: string;
|
||||||
|
isInitializingNotebooks: boolean;
|
||||||
|
setIsNotebookEnabled: (isNotebookEnabled: boolean) => void;
|
||||||
|
setIsNotebooksEnabledForAccount: (isNotebooksEnabledForAccount: boolean) => void;
|
||||||
|
setNotebookServerInfo: (notebookServerInfo: DataModels.NotebookWorkspaceConnectionInfo) => void;
|
||||||
|
setSparkClusterConnectionInfo: (sparkClusterConnectionInfo: DataModels.SparkClusterConnectionInfo) => void;
|
||||||
|
setIsSynapseLinkUpdating: (isSynapseLinkUpdating: boolean) => void;
|
||||||
|
setMemoryUsageInfo: (memoryUsageInfo: DataModels.MemoryUsageInfo) => void;
|
||||||
|
setIsShellEnabled: (isShellEnabled: boolean) => void;
|
||||||
|
setNotebookBasePath: (notebookBasePath: string) => void;
|
||||||
|
refreshNotebooksEnabledStateForAccount: () => Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useNotebook: UseStore<NotebookState> = create((set) => ({
|
||||||
|
isNotebookEnabled: false,
|
||||||
|
isNotebooksEnabledForAccount: false,
|
||||||
|
notebookServerInfo: {
|
||||||
|
notebookServerEndpoint: undefined,
|
||||||
|
authToken: undefined,
|
||||||
|
},
|
||||||
|
sparkClusterConnectionInfo: {
|
||||||
|
userName: undefined,
|
||||||
|
password: undefined,
|
||||||
|
endpoints: [],
|
||||||
|
},
|
||||||
|
isSynapseLinkUpdating: false,
|
||||||
|
memoryUsageInfo: undefined,
|
||||||
|
isShellEnabled: false,
|
||||||
|
notebookBasePath: Constants.Notebook.defaultBasePath,
|
||||||
|
isInitializingNotebooks: false,
|
||||||
|
setIsNotebookEnabled: (isNotebookEnabled: boolean) => set({ isNotebookEnabled }),
|
||||||
|
setIsNotebooksEnabledForAccount: (isNotebooksEnabledForAccount: boolean) => set({ isNotebooksEnabledForAccount }),
|
||||||
|
setNotebookServerInfo: (notebookServerInfo: DataModels.NotebookWorkspaceConnectionInfo) =>
|
||||||
|
set({ notebookServerInfo }),
|
||||||
|
setSparkClusterConnectionInfo: (sparkClusterConnectionInfo: DataModels.SparkClusterConnectionInfo) =>
|
||||||
|
set({ sparkClusterConnectionInfo }),
|
||||||
|
setIsSynapseLinkUpdating: (isSynapseLinkUpdating: boolean) => set({ isSynapseLinkUpdating }),
|
||||||
|
setMemoryUsageInfo: (memoryUsageInfo: DataModels.MemoryUsageInfo) => set({ memoryUsageInfo }),
|
||||||
|
setIsShellEnabled: (isShellEnabled: boolean) => set({ isShellEnabled }),
|
||||||
|
setNotebookBasePath: (notebookBasePath: string) => set({ notebookBasePath }),
|
||||||
|
refreshNotebooksEnabledStateForAccount: async (): Promise<void> => {
|
||||||
|
const { databaseAccount, authType } = userContext;
|
||||||
|
if (
|
||||||
|
authType === AuthType.EncryptedToken ||
|
||||||
|
authType === AuthType.ResourceToken ||
|
||||||
|
authType === AuthType.MasterKey
|
||||||
|
) {
|
||||||
|
set({ isNotebooksEnabledForAccount: false });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const firstWriteLocation =
|
||||||
|
databaseAccount?.properties?.writeLocations &&
|
||||||
|
databaseAccount?.properties?.writeLocations[0]?.locationName.toLowerCase();
|
||||||
|
const disallowedLocationsUri = `${configContext.BACKEND_ENDPOINT}/api/disallowedLocations`;
|
||||||
|
const authorizationHeader = getAuthorizationHeader();
|
||||||
|
try {
|
||||||
|
const response = await fetch(disallowedLocationsUri, {
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify({
|
||||||
|
resourceTypes: [Constants.ArmResourceTypes.notebookWorkspaces],
|
||||||
|
}),
|
||||||
|
headers: {
|
||||||
|
[authorizationHeader.header]: authorizationHeader.token,
|
||||||
|
[Constants.HttpHeaders.contentType]: "application/json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error("Failed to fetch disallowed locations");
|
||||||
|
}
|
||||||
|
|
||||||
|
const disallowedLocations: string[] = await response.json();
|
||||||
|
if (!disallowedLocations) {
|
||||||
|
Logger.logInfo("No disallowed locations found", "Explorer/isNotebooksEnabledForAccount");
|
||||||
|
set({ isNotebooksEnabledForAccount: true });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// firstWriteLocation should not be disallowed
|
||||||
|
const isAccountInAllowedLocation = firstWriteLocation && disallowedLocations.indexOf(firstWriteLocation) === -1;
|
||||||
|
set({ isNotebooksEnabledForAccount: isAccountInAllowedLocation });
|
||||||
|
} catch (error) {
|
||||||
|
Logger.logError(getErrorMessage(error), "Explorer/isNotebooksEnabledForAccount");
|
||||||
|
set({ isNotebooksEnabledForAccount: false });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}));
|
|
@ -9,6 +9,7 @@ import * as GitHubUtils from "../../../Utils/GitHubUtils";
|
||||||
import * as NotificationConsoleUtils from "../../../Utils/NotificationConsoleUtils";
|
import * as NotificationConsoleUtils from "../../../Utils/NotificationConsoleUtils";
|
||||||
import Explorer from "../../Explorer";
|
import Explorer from "../../Explorer";
|
||||||
import { NotebookContentItem, NotebookContentItemType } from "../../Notebook/NotebookContentItem";
|
import { NotebookContentItem, NotebookContentItemType } from "../../Notebook/NotebookContentItem";
|
||||||
|
import { useNotebook } from "../../Notebook/useNotebook";
|
||||||
import { ResourceTreeAdapter } from "../../Tree/ResourceTreeAdapter";
|
import { ResourceTreeAdapter } from "../../Tree/ResourceTreeAdapter";
|
||||||
import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneForm";
|
import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneForm";
|
||||||
import { CopyNotebookPaneComponent, CopyNotebookPaneProps } from "./CopyNotebookPaneComponent";
|
import { CopyNotebookPaneComponent, CopyNotebookPaneProps } from "./CopyNotebookPaneComponent";
|
||||||
|
@ -101,7 +102,7 @@ export const CopyNotebookPane: FunctionComponent<CopyNotebookPanelProps> = ({
|
||||||
case "MyNotebooks":
|
case "MyNotebooks":
|
||||||
parent = {
|
parent = {
|
||||||
name: ResourceTreeAdapter.MyNotebooksTitle,
|
name: ResourceTreeAdapter.MyNotebooksTitle,
|
||||||
path: container.getNotebookBasePath(),
|
path: useNotebook.getState().notebookBasePath,
|
||||||
type: NotebookContentItemType.Directory,
|
type: NotebookContentItemType.Directory,
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -19,17 +19,9 @@ exports[`GitHub Repos Panel should render Default properly 1`] = `
|
||||||
"container": Explorer {
|
"container": Explorer {
|
||||||
"_isInitializingNotebooks": false,
|
"_isInitializingNotebooks": false,
|
||||||
"_resetNotebookWorkspace": [Function],
|
"_resetNotebookWorkspace": [Function],
|
||||||
"isAccountReady": [Function],
|
|
||||||
"isFixedCollectionWithSharedThroughputSupported": [Function],
|
"isFixedCollectionWithSharedThroughputSupported": [Function],
|
||||||
"isNotebookEnabled": [Function],
|
|
||||||
"isNotebooksEnabledForAccount": [Function],
|
|
||||||
"isSchemaEnabled": [Function],
|
"isSchemaEnabled": [Function],
|
||||||
"isShellEnabled": [Function],
|
|
||||||
"isSynapseLinkUpdating": [Function],
|
|
||||||
"isTabsContentExpanded": [Function],
|
"isTabsContentExpanded": [Function],
|
||||||
"memoryUsageInfo": [Function],
|
|
||||||
"notebookBasePath": [Function],
|
|
||||||
"notebookServerInfo": [Function],
|
|
||||||
"onRefreshDatabasesKeyPress": [Function],
|
"onRefreshDatabasesKeyPress": [Function],
|
||||||
"onRefreshResourcesClick": [Function],
|
"onRefreshResourcesClick": [Function],
|
||||||
"provideFeedbackEmail": [Function],
|
"provideFeedbackEmail": [Function],
|
||||||
|
@ -46,7 +38,6 @@ exports[`GitHub Repos Panel should render Default properly 1`] = `
|
||||||
"container": [Circular],
|
"container": [Circular],
|
||||||
"parameters": [Function],
|
"parameters": [Function],
|
||||||
},
|
},
|
||||||
"sparkClusterConnectionInfo": [Function],
|
|
||||||
"tabsManager": TabsManager {
|
"tabsManager": TabsManager {
|
||||||
"activeTab": [Function],
|
"activeTab": [Function],
|
||||||
"openedTabs": [Function],
|
"openedTabs": [Function],
|
||||||
|
|
|
@ -63,7 +63,7 @@ export const SetupNoteBooksPanel: FunctionComponent<SetupNoteBooksPanelProps> =
|
||||||
userContext.databaseAccount.name,
|
userContext.databaseAccount.name,
|
||||||
"default"
|
"default"
|
||||||
);
|
);
|
||||||
explorer.isAccountReady.valueHasMutated(); // re-trigger init notebooks
|
explorer.refreshExplorer();
|
||||||
|
|
||||||
closeSidePanel();
|
closeSidePanel();
|
||||||
|
|
||||||
|
|
|
@ -9,17 +9,9 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
|
||||||
Explorer {
|
Explorer {
|
||||||
"_isInitializingNotebooks": false,
|
"_isInitializingNotebooks": false,
|
||||||
"_resetNotebookWorkspace": [Function],
|
"_resetNotebookWorkspace": [Function],
|
||||||
"isAccountReady": [Function],
|
|
||||||
"isFixedCollectionWithSharedThroughputSupported": [Function],
|
"isFixedCollectionWithSharedThroughputSupported": [Function],
|
||||||
"isNotebookEnabled": [Function],
|
|
||||||
"isNotebooksEnabledForAccount": [Function],
|
|
||||||
"isSchemaEnabled": [Function],
|
"isSchemaEnabled": [Function],
|
||||||
"isShellEnabled": [Function],
|
|
||||||
"isSynapseLinkUpdating": [Function],
|
|
||||||
"isTabsContentExpanded": [Function],
|
"isTabsContentExpanded": [Function],
|
||||||
"memoryUsageInfo": [Function],
|
|
||||||
"notebookBasePath": [Function],
|
|
||||||
"notebookServerInfo": [Function],
|
|
||||||
"onRefreshDatabasesKeyPress": [Function],
|
"onRefreshDatabasesKeyPress": [Function],
|
||||||
"onRefreshResourcesClick": [Function],
|
"onRefreshResourcesClick": [Function],
|
||||||
"provideFeedbackEmail": [Function],
|
"provideFeedbackEmail": [Function],
|
||||||
|
@ -36,7 +28,6 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
|
||||||
"container": [Circular],
|
"container": [Circular],
|
||||||
"parameters": [Function],
|
"parameters": [Function],
|
||||||
},
|
},
|
||||||
"sparkClusterConnectionInfo": [Function],
|
|
||||||
"tabsManager": TabsManager {
|
"tabsManager": TabsManager {
|
||||||
"activeTab": [Function],
|
"activeTab": [Function],
|
||||||
"openedTabs": [Function],
|
"openedTabs": [Function],
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import * as ko from "knockout";
|
|
||||||
import { DataSamplesUtil } from "../DataSamples/DataSamplesUtil";
|
import { DataSamplesUtil } from "../DataSamples/DataSamplesUtil";
|
||||||
import Explorer from "../Explorer";
|
import Explorer from "../Explorer";
|
||||||
import { TabsManager } from "../Tabs/TabsManager";
|
import { TabsManager } from "../Tabs/TabsManager";
|
||||||
|
@ -7,7 +6,6 @@ jest.mock("../Explorer");
|
||||||
|
|
||||||
const createExplorer = () => {
|
const createExplorer = () => {
|
||||||
const mock = new Explorer();
|
const mock = new Explorer();
|
||||||
mock.isNotebookEnabled = ko.observable(false);
|
|
||||||
mock.tabsManager = new TabsManager();
|
mock.tabsManager = new TabsManager();
|
||||||
return mock as jest.Mocked<Explorer>;
|
return mock as jest.Mocked<Explorer>;
|
||||||
};
|
};
|
||||||
|
|
|
@ -22,6 +22,7 @@ import { FeaturePanelLauncher } from "../Controls/FeaturePanel/FeaturePanelLaunc
|
||||||
import { DataSamplesUtil } from "../DataSamples/DataSamplesUtil";
|
import { DataSamplesUtil } from "../DataSamples/DataSamplesUtil";
|
||||||
import Explorer from "../Explorer";
|
import Explorer from "../Explorer";
|
||||||
import * as MostRecentActivity from "../MostRecentActivity/MostRecentActivity";
|
import * as MostRecentActivity from "../MostRecentActivity/MostRecentActivity";
|
||||||
|
import { useNotebook } from "../Notebook/useNotebook";
|
||||||
import { useDatabases } from "../useDatabases";
|
import { useDatabases } from "../useDatabases";
|
||||||
import { useSelectedNode } from "../useSelectedNode";
|
import { useSelectedNode } from "../useSelectedNode";
|
||||||
|
|
||||||
|
@ -61,8 +62,13 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
|
||||||
|
|
||||||
public componentDidMount() {
|
public componentDidMount() {
|
||||||
this.subscriptions.push(
|
this.subscriptions.push(
|
||||||
{ dispose: useSelectedNode.subscribe(() => this.setState({})) },
|
{
|
||||||
this.container.isNotebookEnabled.subscribe(() => this.setState({}))
|
dispose: useNotebook.subscribe(
|
||||||
|
() => this.setState({}),
|
||||||
|
(state) => state.isNotebookEnabled
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{ dispose: useSelectedNode.subscribe(() => this.setState({})) }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,7 +216,7 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.container.isNotebookEnabled()) {
|
if (useNotebook.getState().isNotebookEnabled) {
|
||||||
heroes.push({
|
heroes.push({
|
||||||
iconSrc: NewNotebookIcon,
|
iconSrc: NewNotebookIcon,
|
||||||
title: "New Notebook",
|
title: "New Notebook",
|
||||||
|
|
|
@ -6,6 +6,7 @@ import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||||
import { userContext } from "../../UserContext";
|
import { userContext } from "../../UserContext";
|
||||||
import Explorer from "../Explorer";
|
import Explorer from "../Explorer";
|
||||||
import { NotebookClientV2 } from "../Notebook/NotebookClientV2";
|
import { NotebookClientV2 } from "../Notebook/NotebookClientV2";
|
||||||
|
import { useNotebook } from "../Notebook/useNotebook";
|
||||||
import TabsBase from "./TabsBase";
|
import TabsBase from "./TabsBase";
|
||||||
|
|
||||||
export interface NotebookTabBaseOptions extends ViewModels.TabOptions {
|
export interface NotebookTabBaseOptions extends ViewModels.TabOptions {
|
||||||
|
@ -28,7 +29,7 @@ export default class NotebookTabBase extends TabsBase {
|
||||||
|
|
||||||
if (!NotebookTabBase.clientManager) {
|
if (!NotebookTabBase.clientManager) {
|
||||||
NotebookTabBase.clientManager = new NotebookClientV2({
|
NotebookTabBase.clientManager = new NotebookClientV2({
|
||||||
connectionInfo: this.container.notebookServerInfo(),
|
connectionInfo: useNotebook.getState().notebookServerInfo,
|
||||||
databaseAccountName: userContext?.databaseAccount?.name,
|
databaseAccountName: userContext?.databaseAccount?.name,
|
||||||
defaultExperience: userContext.apiType,
|
defaultExperience: userContext.apiType,
|
||||||
contentProvider: this.container.notebookManager?.notebookContentProvider,
|
contentProvider: this.container.notebookManager?.notebookContentProvider,
|
||||||
|
|
|
@ -23,6 +23,7 @@ import * as CdbActions from "../Notebook/NotebookComponent/actions";
|
||||||
import { NotebookComponentAdapter } from "../Notebook/NotebookComponent/NotebookComponentAdapter";
|
import { NotebookComponentAdapter } from "../Notebook/NotebookComponent/NotebookComponentAdapter";
|
||||||
import { CdbAppState, SnapshotRequest } from "../Notebook/NotebookComponent/types";
|
import { CdbAppState, SnapshotRequest } from "../Notebook/NotebookComponent/types";
|
||||||
import { NotebookContentItem } from "../Notebook/NotebookContentItem";
|
import { NotebookContentItem } from "../Notebook/NotebookContentItem";
|
||||||
|
import { useNotebook } from "../Notebook/useNotebook";
|
||||||
import NotebookTabBase, { NotebookTabBaseOptions } from "./NotebookTabBase";
|
import NotebookTabBase, { NotebookTabBaseOptions } from "./NotebookTabBase";
|
||||||
|
|
||||||
export interface NotebookTabOptions extends NotebookTabBaseOptions {
|
export interface NotebookTabOptions extends NotebookTabBaseOptions {
|
||||||
|
@ -39,10 +40,13 @@ export default class NotebookTabV2 extends NotebookTabBase {
|
||||||
|
|
||||||
this.container = options.container;
|
this.container = options.container;
|
||||||
this.notebookPath = ko.observable(options.notebookContentItem.path);
|
this.notebookPath = ko.observable(options.notebookContentItem.path);
|
||||||
this.container.notebookServerInfo.subscribe(() => logConsoleInfo("New notebook server info received."));
|
useNotebook.subscribe(
|
||||||
|
() => logConsoleInfo("New notebook server info received."),
|
||||||
|
(state) => state.notebookServerInfo
|
||||||
|
);
|
||||||
this.notebookComponentAdapter = new NotebookComponentAdapter({
|
this.notebookComponentAdapter = new NotebookComponentAdapter({
|
||||||
contentItem: options.notebookContentItem,
|
contentItem: options.notebookContentItem,
|
||||||
notebooksBasePath: this.container.getNotebookBasePath(),
|
notebooksBasePath: useNotebook.getState().notebookBasePath,
|
||||||
notebookClient: NotebookTabBase.clientManager,
|
notebookClient: NotebookTabBase.clientManager,
|
||||||
onUpdateKernelInfo: this.onKernelUpdate,
|
onUpdateKernelInfo: this.onKernelUpdate,
|
||||||
});
|
});
|
||||||
|
@ -359,8 +363,8 @@ export default class NotebookTabV2 extends NotebookTabBase {
|
||||||
};
|
};
|
||||||
|
|
||||||
private async configureServiceEndpoints(kernelName: string) {
|
private async configureServiceEndpoints(kernelName: string) {
|
||||||
const notebookConnectionInfo = this.container && this.container.notebookServerInfo();
|
const notebookConnectionInfo = useNotebook.getState().notebookServerInfo;
|
||||||
const sparkClusterConnectionInfo = this.container && this.container.sparkClusterConnectionInfo();
|
const sparkClusterConnectionInfo = useNotebook.getState().sparkClusterConnectionInfo;
|
||||||
await NotebookConfigurationUtils.configureServiceEndpoints(
|
await NotebookConfigurationUtils.configureServiceEndpoints(
|
||||||
this.notebookPath(),
|
this.notebookPath(),
|
||||||
notebookConnectionInfo,
|
notebookConnectionInfo,
|
||||||
|
|
|
@ -8,6 +8,7 @@ import { userContext } from "../../UserContext";
|
||||||
import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent";
|
import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent";
|
||||||
import { NotebookTerminalComponent } from "../Controls/Notebook/NotebookTerminalComponent";
|
import { NotebookTerminalComponent } from "../Controls/Notebook/NotebookTerminalComponent";
|
||||||
import Explorer from "../Explorer";
|
import Explorer from "../Explorer";
|
||||||
|
import { useNotebook } from "../Notebook/useNotebook";
|
||||||
import TabsBase from "./TabsBase";
|
import TabsBase from "./TabsBase";
|
||||||
|
|
||||||
export interface TerminalTabOptions extends ViewModels.TabOptions {
|
export interface TerminalTabOptions extends ViewModels.TabOptions {
|
||||||
|
@ -54,8 +55,8 @@ export default class TerminalTab extends TabsBase {
|
||||||
this.notebookTerminalComponentAdapter.parameters = ko.computed<boolean>(() => {
|
this.notebookTerminalComponentAdapter.parameters = ko.computed<boolean>(() => {
|
||||||
if (
|
if (
|
||||||
this.isTemplateReady() &&
|
this.isTemplateReady() &&
|
||||||
this.container.isNotebookEnabled() &&
|
useNotebook.getState().isNotebookEnabled &&
|
||||||
this.container.notebookServerInfo().notebookServerEndpoint
|
useNotebook.getState().notebookServerInfo?.notebookServerEndpoint
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -95,7 +96,7 @@ export default class TerminalTab extends TabsBase {
|
||||||
throw new Error(`Terminal kind: ${options.kind} not supported`);
|
throw new Error(`Terminal kind: ${options.kind} not supported`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const info: DataModels.NotebookWorkspaceConnectionInfo = options.container.notebookServerInfo();
|
const info: DataModels.NotebookWorkspaceConnectionInfo = useNotebook.getState().notebookServerInfo;
|
||||||
return {
|
return {
|
||||||
authToken: info.authToken,
|
authToken: info.authToken,
|
||||||
notebookServerEndpoint: `${info.notebookServerEndpoint.replace(/\/+$/, "")}/${endpointSuffix}`,
|
notebookServerEndpoint: `${info.notebookServerEndpoint.replace(/\/+$/, "")}/${endpointSuffix}`,
|
||||||
|
|
|
@ -31,6 +31,7 @@ import { useCommandBar } from "../Menus/CommandBar/CommandBarComponentAdapter";
|
||||||
import { mostRecentActivity } from "../MostRecentActivity/MostRecentActivity";
|
import { mostRecentActivity } from "../MostRecentActivity/MostRecentActivity";
|
||||||
import { NotebookContentItem, NotebookContentItemType } from "../Notebook/NotebookContentItem";
|
import { NotebookContentItem, NotebookContentItemType } from "../Notebook/NotebookContentItem";
|
||||||
import { NotebookUtil } from "../Notebook/NotebookUtil";
|
import { NotebookUtil } from "../Notebook/NotebookUtil";
|
||||||
|
import { useNotebook } from "../Notebook/useNotebook";
|
||||||
import TabsBase from "../Tabs/TabsBase";
|
import TabsBase from "../Tabs/TabsBase";
|
||||||
import { useDatabases } from "../useDatabases";
|
import { useDatabases } from "../useDatabases";
|
||||||
import { useSelectedNode } from "../useSelectedNode";
|
import { useSelectedNode } from "../useSelectedNode";
|
||||||
|
@ -57,7 +58,10 @@ export class ResourceTreeAdapter implements ReactAdapter {
|
||||||
|
|
||||||
useSelectedNode.subscribe(() => this.triggerRender());
|
useSelectedNode.subscribe(() => this.triggerRender());
|
||||||
this.container.tabsManager.activeTab.subscribe((newValue: TabsBase) => this.triggerRender());
|
this.container.tabsManager.activeTab.subscribe((newValue: TabsBase) => this.triggerRender());
|
||||||
this.container.isNotebookEnabled.subscribe((newValue) => this.triggerRender());
|
useNotebook.subscribe(
|
||||||
|
() => this.triggerRender(),
|
||||||
|
(state) => state.isNotebookEnabled
|
||||||
|
);
|
||||||
|
|
||||||
useDatabases.subscribe(() => this.triggerRender());
|
useDatabases.subscribe(() => this.triggerRender());
|
||||||
this.triggerRender();
|
this.triggerRender();
|
||||||
|
@ -91,7 +95,7 @@ export class ResourceTreeAdapter implements ReactAdapter {
|
||||||
const dataRootNode = this.buildDataTree();
|
const dataRootNode = this.buildDataTree();
|
||||||
const notebooksRootNode = this.buildNotebooksTrees();
|
const notebooksRootNode = this.buildNotebooksTrees();
|
||||||
|
|
||||||
if (this.container.isNotebookEnabled()) {
|
if (useNotebook.getState().isNotebookEnabled) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<AccordionComponent>
|
<AccordionComponent>
|
||||||
|
@ -122,12 +126,12 @@ export class ResourceTreeAdapter implements ReactAdapter {
|
||||||
|
|
||||||
this.myNotebooksContentRoot = {
|
this.myNotebooksContentRoot = {
|
||||||
name: ResourceTreeAdapter.MyNotebooksTitle,
|
name: ResourceTreeAdapter.MyNotebooksTitle,
|
||||||
path: this.container.getNotebookBasePath(),
|
path: useNotebook.getState().notebookBasePath,
|
||||||
type: NotebookContentItemType.Directory,
|
type: NotebookContentItemType.Directory,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Only if notebook server is available we can refresh
|
// Only if notebook server is available we can refresh
|
||||||
if (this.container.notebookServerInfo().notebookServerEndpoint) {
|
if (useNotebook.getState().notebookServerInfo?.notebookServerEndpoint) {
|
||||||
refreshTasks.push(
|
refreshTasks.push(
|
||||||
this.container.refreshContentItem(this.myNotebooksContentRoot).then(() => {
|
this.container.refreshContentItem(this.myNotebooksContentRoot).then(() => {
|
||||||
this.triggerRender();
|
this.triggerRender();
|
||||||
|
@ -268,7 +272,11 @@ export class ResourceTreeAdapter implements ReactAdapter {
|
||||||
contextMenu: ResourceTreeContextMenuButtonFactory.createCollectionContextMenuButton(this.container, collection),
|
contextMenu: ResourceTreeContextMenuButtonFactory.createCollectionContextMenuButton(this.container, collection),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.container.isNotebookEnabled() && userContext.apiType === "Mongo" && isPublicInternetAccessAllowed()) {
|
if (
|
||||||
|
useNotebook.getState().isNotebookEnabled &&
|
||||||
|
userContext.apiType === "Mongo" &&
|
||||||
|
isPublicInternetAccessAllowed()
|
||||||
|
) {
|
||||||
children.push({
|
children.push({
|
||||||
label: "Schema (Preview)",
|
label: "Schema (Preview)",
|
||||||
onClick: collection.onSchemaAnalyzerClick.bind(collection),
|
onClick: collection.onSchemaAnalyzerClick.bind(collection),
|
||||||
|
|
|
@ -195,7 +195,6 @@ function configureEmulator(explorerParams: ExplorerParams): Explorer {
|
||||||
authType: AuthType.MasterKey,
|
authType: AuthType.MasterKey,
|
||||||
});
|
});
|
||||||
const explorer = new Explorer(explorerParams);
|
const explorer = new Explorer(explorerParams);
|
||||||
explorer.isAccountReady(true);
|
|
||||||
return explorer;
|
return explorer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -54,7 +54,6 @@
|
||||||
"./src/Explorer/Notebook/NotebookComponent/loadTransform.ts",
|
"./src/Explorer/Notebook/NotebookComponent/loadTransform.ts",
|
||||||
"./src/Explorer/Notebook/NotebookComponent/reducers.ts",
|
"./src/Explorer/Notebook/NotebookComponent/reducers.ts",
|
||||||
"./src/Explorer/Notebook/NotebookComponent/types.ts",
|
"./src/Explorer/Notebook/NotebookComponent/types.ts",
|
||||||
"./src/Explorer/Notebook/NotebookContentClient.ts",
|
|
||||||
"./src/Explorer/Notebook/NotebookContentItem.ts",
|
"./src/Explorer/Notebook/NotebookContentItem.ts",
|
||||||
"./src/Explorer/Notebook/NotebookRenderer/AzureTheme.tsx",
|
"./src/Explorer/Notebook/NotebookRenderer/AzureTheme.tsx",
|
||||||
"./src/Explorer/Notebook/NotebookRenderer/Prompt.tsx",
|
"./src/Explorer/Notebook/NotebookRenderer/Prompt.tsx",
|
||||||
|
|
Loading…
Reference in New Issue