Compare commits

..

5 Commits

Author SHA1 Message Date
kcheekuri
0fe1e2940e Resolve compile errors 2021-11-10 14:22:30 -05:00
kcheekuri
f9d91061a7 Change endpoint 2021-11-10 12:56:08 -05:00
kcheekuri
4a50d138cd Merge branch 'master' into users/kcheekuri/removePhoenixFlag 2021-11-10 11:39:41 -05:00
kcheekuri
a7409553d4 Remove phoenix and notebook temporary down features and apply phoenix for whitelisted acounts 2021-11-10 10:52:41 -05:00
Srinath Narayanan
a9d38cdb3b fixed quickstarts 2021-11-09 13:01:24 -05:00
37 changed files with 263 additions and 434 deletions

View File

@@ -2077,7 +2077,7 @@ a:link {
.resourceTreeAndTabs {
display: flex;
flex: 1 1 auto;
overflow-x: clip;
overflow-x: auto;
overflow-y: auto;
height: 100%;
}
@@ -2245,7 +2245,7 @@ a:link {
}
.refreshColHeader {
padding: 3px 6px 10px 0px !important;
padding: 3px 6px 6px 6px;
}
.refreshColHeader:hover {
@@ -2869,31 +2869,31 @@ a:link {
}
}
.settingsSection {
border-bottom: 1px solid @BaseMedium;
margin-right: 24px;
padding: @MediumSpace 0px;
settings-pane {
.settingsSection {
border-bottom: 1px solid @BaseMedium;
margin-right: 24px;
padding: @MediumSpace 0px;
&:first-child {
padding-top: 0px;
padding-bottom: 10px;
}
&:first-child {
padding-top: 0px;
}
&:last-child {
border-bottom: none;
}
&:last-child {
border-bottom: none;
}
.settingsSectionPart {
padding-left: 8px;
}
.settingsSectionPart {
padding-left: 8px;
}
.settingsSectionLabel {
margin-bottom: @DefaultSpace;
margin-right: 5px;
}
.settingsSectionLabel {
margin-bottom: @DefaultSpace;
}
.pageOptionsPart {
padding-bottom: @MediumSpace;
.pageOptionsPart {
padding-bottom: @MediumSpace;
}
}
}

4
package-lock.json generated
View File

@@ -6326,8 +6326,8 @@
},
"@webpack-cli/serve": {
"version": "1.5.2",
"resolved": "https://msazure.pkgs.visualstudio.com/_packaging/AzurePortal/npm/registry/@webpack-cli/serve/-/serve-1.5.2.tgz",
"integrity": "sha1-6lhLY3/2PFpHf28hYEtaIFtyyew=",
"resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.5.2.tgz",
"integrity": "sha512-vgJ5OLWadI8aKjDlOH3rb+dYyPd2GTZuQC/Tihjct6F9GpXGZINo3Y/IVuZVTM1eDQB+/AOsjPUWH/WySDaXvw==",
"dev": true
},
"@xtuc/ieee754": {

View File

@@ -96,7 +96,6 @@ export class Flights {
public static readonly AutoscaleTest = "autoscaletest";
public static readonly PartitionKeyTest = "partitionkeytest";
public static readonly PKPartitionKeyTest = "pkpartitionkeytest";
public static readonly Phoenix = "phoenix";
public static readonly NotebooksDownBanner = "notebooksdownbanner";
}
@@ -412,11 +411,3 @@ export class TerminalQueryParams {
public static readonly SubscriptionId = "subscriptionId";
public static readonly TerminalEndpoint = "terminalEndpoint";
}
export class JunoEndpoints {
public static readonly Test = "https://juno-test.documents-dev.windows-int.net";
public static readonly Test2 = "https://juno-test2.documents-dev.windows-int.net";
public static readonly Test3 = "https://juno-test3.documents-dev.windows-int.net";
public static readonly Prod = "https://tools.cosmos.azure.com";
public static readonly Stage = "https://tools-staging.cosmos.azure.com";
}

View File

@@ -1,5 +1,3 @@
import { JunoEndpoints } from "Common/Constants";
export enum Platform {
Portal = "Portal",
Hosted = "Hosted",
@@ -25,7 +23,6 @@ export interface ConfigContext {
PROXY_PATH?: string;
JUNO_ENDPOINT: string;
GITHUB_CLIENT_ID: string;
GITHUB_TEST_ENV_CLIENT_ID: string;
GITHUB_CLIENT_SECRET?: string; // No need to inject secret for prod. Juno already knows it.
hostedExplorerURL: string;
armAPIVersion?: string;
@@ -55,16 +52,15 @@ let configContext: Readonly<ConfigContext> = {
GRAPH_API_VERSION: "1.6",
ARCADIA_ENDPOINT: "https://workspaceartifacts.projectarcadia.net",
ARCADIA_LIVY_ENDPOINT_DNS_ZONE: "dev.azuresynapse.net",
GITHUB_CLIENT_ID: "6cb2f63cf6f7b5cbdeca", // Registered OAuth app: https://github.com/organizations/AzureCosmosDBNotebooks/settings/applications/1189306
GITHUB_TEST_ENV_CLIENT_ID: "b63fc8cbf87fd3c6e2eb", // Registered OAuth app: https://github.com/organizations/AzureCosmosDBNotebooks/settings/applications/1777772
GITHUB_CLIENT_ID: "6cb2f63cf6f7b5cbdeca", // Registered OAuth app: https://github.com/settings/applications/1189306
JUNO_ENDPOINT: "https://tools.cosmos.azure.com",
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com",
allowedJunoOrigins: [
JunoEndpoints.Test,
JunoEndpoints.Test2,
JunoEndpoints.Test3,
JunoEndpoints.Prod,
JunoEndpoints.Stage,
"https://juno-test.documents-dev.windows-int.net",
"https://juno-test2.documents-dev.windows-int.net",
"https://juno-test3.documents-dev.windows-int.net",
"https://tools.cosmos.azure.com",
"https://tools-staging.cosmos.azure.com",
"https://localhost",
],
};

View File

@@ -438,14 +438,16 @@ export interface ContainerInfo {
}
export interface IProvisionData {
subscriptionId: string;
resourceGroup: string;
dbAccountName: string;
cosmosEndpoint: string;
}
export interface IContainerData {
export interface IAccountData {
subscriptionId: string;
resourceGroup: string;
dbAccountName: string;
}
export interface IContainerData extends IAccountData {
forwardingId: string;
}

View File

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

View File

@@ -17,7 +17,6 @@ import Explorer from "../../Explorer";
import { NotebookClientV2 } from "../../Notebook/NotebookClientV2";
import { NotebookComponentBootstrapper } from "../../Notebook/NotebookComponent/NotebookComponentBootstrapper";
import NotebookReadOnlyRenderer from "../../Notebook/NotebookRenderer/NotebookReadOnlyRenderer";
import { NotebookUtil } from "../../Notebook/NotebookUtil";
import { useNotebook } from "../../Notebook/useNotebook";
import { Dialog, TextFieldProps, useDialog } from "../Dialog";
import { NotebookMetadataComponent } from "./NotebookMetadataComponent";
@@ -148,9 +147,7 @@ export class NotebookViewerComponent
<NotebookMetadataComponent
data={this.state.galleryItem}
isFavorite={this.state.isFavorite}
downloadButtonText={
this.props.container && NotebookUtil.getNotebookBtnTitle(useNotebook.getState().notebookFolderName)
}
downloadButtonText={this.props.container && `Download to ${useNotebook.getState().notebookFolderName}`}
onTagClick={this.props.onTagClick}
onFavoriteClick={this.favoriteItem}
onUnfavoriteClick={this.unfavoriteItem}

View File

@@ -213,8 +213,7 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
},
};
const throughputCap = userContext.databaseAccount?.properties.capacity?.totalThroughputLimit;
if (throughputCap && throughputCap !== -1) {
if (userContext.databaseAccount?.properties.capacity?.totalThroughputLimit) {
this.calculateTotalThroughputUsed();
}
}
@@ -681,7 +680,7 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
const throughputCap = userContext.databaseAccount?.properties.capacity?.totalThroughputLimit;
const numberOfRegions = userContext.databaseAccount?.properties.locations?.length || 1;
const throughputDelta = (newThroughput - this.offer.autoscaleMaxThroughput) * numberOfRegions;
if (throughputCap && throughputCap !== -1 && throughputCap - this.totalThroughputUsed < throughputDelta) {
if (throughputCap && throughputCap - this.totalThroughputUsed < throughputDelta) {
throughputError = `Your account is currently configured with a total throughput limit of ${throughputCap} RU/s. This update isn't possible because it would increase the total throughput to ${
this.totalThroughputUsed + throughputDelta
} RU/s. Change total throughput limit in cost management.`;
@@ -694,7 +693,7 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
const throughputCap = userContext.databaseAccount?.properties.capacity?.totalThroughputLimit;
const numberOfRegions = userContext.databaseAccount?.properties.locations?.length || 1;
const throughputDelta = (newThroughput - this.offer.manualThroughput) * numberOfRegions;
if (throughputCap && throughputCap !== -1 && throughputCap - this.totalThroughputUsed < throughputDelta) {
if (throughputCap && throughputCap - this.totalThroughputUsed < newThroughput - this.offer.manualThroughput) {
throughputError = `Your account is currently configured with a total throughput limit of ${throughputCap} RU/s. This update isn't possible because it would increase the total throughput to ${
this.totalThroughputUsed + throughputDelta
} RU/s. Change total throughput limit in cost management.`;

View File

@@ -61,7 +61,7 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
totalThroughput *= numberOfRegions;
setTotalThroughputUsed(totalThroughput);
if (throughputCap && throughputCap !== -1 && throughputCap - totalThroughput < throughput) {
if (throughputCap && throughputCap - totalThroughput < throughput) {
setThroughputError(
`Your account is currently configured with a total throughput limit of ${throughputCap} RU/s. This update isn't possible because it would increase the total throughput to ${
totalThroughput + throughput * numberOfRegions
@@ -73,7 +73,7 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
}, []);
const checkThroughputCap = (newThroughput: number): boolean => {
if (throughputCap && throughputCap !== -1 && throughputCap - totalThroughputUsed < newThroughput) {
if (throughputCap && throughputCap - totalThroughputUsed < newThroughput) {
setThroughputError(
`Your account is currently configured with a total throughput limit of ${throughputCap} RU/s. This update isn't possible because it would increase the total throughput to ${
totalThroughputUsed + newThroughput * numberOfRegions

View File

@@ -7,14 +7,21 @@ import shallow from "zustand/shallow";
import { AuthType } from "../AuthType";
import { BindingHandlersRegisterer } from "../Bindings/BindingHandlersRegisterer";
import * as Constants from "../Common/Constants";
import { Areas, ConnectionStatusType, HttpStatusCodes, Notebook } from "../Common/Constants";
import { ConnectionStatusType, HttpStatusCodes, Notebook } from "../Common/Constants";
import { readCollection } from "../Common/dataAccess/readCollection";
import { readDatabases } from "../Common/dataAccess/readDatabases";
import { getErrorMessage, getErrorStack, handleError } from "../Common/ErrorHandlingUtils";
import * as Logger from "../Common/Logger";
import { QueriesClient } from "../Common/QueriesClient";
import * as DataModels from "../Contracts/DataModels";
import { ContainerConnectionInfo, IPhoenixConnectionInfoResult, IResponse } from "../Contracts/DataModels";
import {
ContainerConnectionInfo,
IAccountData,
IContainerData,
IPhoenixConnectionInfoResult,
IProvisionData,
IResponse,
} from "../Contracts/DataModels";
import * as ViewModels from "../Contracts/ViewModels";
import { GitHubOAuthService } from "../GitHub/GitHubOAuthService";
import { useSidePanel } from "../hooks/useSidePanel";
@@ -30,7 +37,6 @@ import { update } from "../Utils/arm/generatedClients/cosmos/databaseAccounts";
import {
get as getWorkspace,
listByDatabaseAccount,
listConnectionInfo,
start,
} from "../Utils/arm/generatedClients/cosmosNotebooks/notebookWorkspaces";
import { stringToBlob } from "../Utils/BlobUtils";
@@ -347,29 +353,11 @@ export default class Explorer {
if (!databaseAccount) {
throw new Error("No database account specified");
}
if (this._isInitializingNotebooks) {
return;
}
this._isInitializingNotebooks = true;
if (userContext.features.phoenix === false) {
await this.ensureNotebookWorkspaceRunning();
const connectionInfo = await listConnectionInfo(
userContext.subscriptionId,
userContext.resourceGroup,
databaseAccount.name,
"default"
);
useNotebook.getState().setNotebookServerInfo({
notebookServerEndpoint: userContext.features.notebookServerUrl || connectionInfo.notebookServerEndpoint,
authToken: userContext.features.notebookServerToken || connectionInfo.authToken,
forwardingId: undefined,
});
}
this.refreshNotebookList();
this._isInitializingNotebooks = false;
}
@@ -381,47 +369,31 @@ export default class Explorer {
(notebookServerInfo === undefined ||
(notebookServerInfo && notebookServerInfo.notebookServerEndpoint === undefined))
) {
const provisionData = {
const provisionData: IProvisionData = {
cosmosEndpoint: userContext.databaseAccount.properties.documentEndpoint,
};
const accountData: IAccountData = {
subscriptionId: userContext.subscriptionId,
resourceGroup: userContext.resourceGroup,
dbAccountName: userContext.databaseAccount.name,
cosmosEndpoint: userContext.databaseAccount.properties.documentEndpoint,
};
const connectionStatus: ContainerConnectionInfo = {
status: ConnectionStatusType.Connecting,
};
useNotebook.getState().setConnectionInfo(connectionStatus);
try {
TelemetryProcessor.traceStart(Action.PhoenixConnection, {
dataExplorerArea: Areas.Notebook,
});
useNotebook.getState().setIsAllocating(true);
const connectionInfo = await this.phoenixClient.allocateContainer(provisionData);
if (connectionInfo.status !== HttpStatusCodes.OK) {
throw new Error(`Received status code: ${connectionInfo?.status}`);
}
if (!connectionInfo?.data?.notebookServerUrl) {
throw new Error(`NotebookServerUrl is invalid!`);
}
const connectionInfo = await this.phoenixClient.allocateContainer(provisionData, accountData);
await this.setNotebookInfo(connectionInfo, connectionStatus);
TelemetryProcessor.traceSuccess(Action.PhoenixConnection, {
dataExplorerArea: Areas.Notebook,
});
} catch (error) {
TelemetryProcessor.traceFailure(Action.PhoenixConnection, {
dataExplorerArea: Areas.Notebook,
error: getErrorMessage(error),
errorStack: getErrorStack(error),
});
connectionStatus.status = ConnectionStatusType.Failed;
useNotebook.getState().resetContainerConnection(connectionStatus);
throw error;
} finally {
useNotebook.getState().setIsAllocating(false);
this.refreshCommandBarButtons();
this.refreshNotebookList();
this._isInitializingNotebooks = false;
}
this.refreshCommandBarButtons();
this.refreshNotebookList();
this._isInitializingNotebooks = false;
}
}
@@ -429,22 +401,30 @@ export default class Explorer {
connectionInfo: IResponse<IPhoenixConnectionInfoResult>,
connectionStatus: DataModels.ContainerConnectionInfo
) {
const containerData = {
forwardingId: connectionInfo.data.forwardingId,
dbAccountName: userContext.databaseAccount.name,
};
await this.phoenixClient.initiateContainerHeartBeat(containerData);
if (connectionInfo.status === HttpStatusCodes.OK && connectionInfo.data && connectionInfo.data.notebookServerUrl) {
const containerData: IContainerData = {
subscriptionId: userContext.subscriptionId,
resourceGroup: userContext.resourceGroup,
dbAccountName: userContext.databaseAccount.name,
forwardingId: connectionInfo.data.forwardingId,
};
await this.phoenixClient.initiateContainerHeartBeat(containerData);
connectionStatus.status = ConnectionStatusType.Connected;
useNotebook.getState().setConnectionInfo(connectionStatus);
useNotebook.getState().setNotebookServerInfo({
notebookServerEndpoint: userContext.features.notebookServerUrl || connectionInfo.data.notebookServerUrl,
authToken: userContext.features.notebookServerToken || connectionInfo.data.notebookAuthToken,
forwardingId: connectionInfo.data.forwardingId,
});
this.notebookManager?.notebookClient
.getMemoryUsage()
.then((memoryUsageInfo) => useNotebook.getState().setMemoryUsageInfo(memoryUsageInfo));
connectionStatus.status = ConnectionStatusType.Connected;
useNotebook.getState().setConnectionInfo(connectionStatus);
useNotebook.getState().setNotebookServerInfo({
notebookServerEndpoint: userContext.features.notebookServerUrl || connectionInfo.data.notebookServerUrl,
authToken: userContext.features.notebookServerToken || connectionInfo.data.notebookAuthToken,
forwardingId: connectionInfo.data.forwardingId,
});
this.notebookManager?.notebookClient
.getMemoryUsage()
.then((memoryUsageInfo) => useNotebook.getState().setMemoryUsageInfo(memoryUsageInfo));
} else {
connectionStatus.status = ConnectionStatusType.Failed;
useNotebook.getState().resetContainerConnection(connectionStatus);
}
useNotebook.getState().setIsAllocating(false);
}
public resetNotebookWorkspace(): void {
@@ -455,14 +435,10 @@ export default class Explorer {
);
return;
}
const dialogContent = NotebookUtil.isPhoenixEnabled()
? "Notebooks saved in the temporary workspace will be deleted. Do you want to proceed?"
: "This lets you keep your notebook files and the workspace will be restored to default. Proceed anyway?";
const resetConfirmationDialogProps: DialogProps = {
isModal: true,
title: "Reset Workspace",
subText: dialogContent,
subText: "Notebooks saved in the temporary workspace will be deleted. Do you want to proceed?",
primaryButtonText: "OK",
secondaryButtonText: "Cancel",
onPrimaryButtonClick: this._resetNotebookWorkspace,
@@ -529,45 +505,39 @@ export default class Explorer {
logConsoleError(error);
return;
}
TelemetryProcessor.traceStart(Action.PhoenixResetWorkspace, {
dataExplorerArea: Areas.Notebook,
});
if (NotebookUtil.isPhoenixEnabled()) {
useTabs.getState().closeAllNotebookTabs(true);
connectionStatus = {
status: ConnectionStatusType.Connecting,
};
useNotebook.getState().setConnectionInfo(connectionStatus);
}
useTabs.getState().closeAllNotebookTabs(true);
connectionStatus = {
status: ConnectionStatusType.Connecting,
};
useNotebook.getState().setConnectionInfo(connectionStatus);
const connectionInfo = await this.notebookManager?.notebookClient.resetWorkspace();
if (connectionInfo?.status !== HttpStatusCodes.OK) {
throw new Error(`Reset Workspace: Received status code- ${connectionInfo?.status}`);
}
if (!connectionInfo?.data?.notebookServerUrl) {
throw new Error(`Reset Workspace: NotebookServerUrl is invalid!`);
}
if (NotebookUtil.isPhoenixEnabled()) {
await this.setNotebookInfo(connectionInfo, connectionStatus);
useNotebook.getState().setIsRefreshed(!useNotebook.getState().isRefreshed);
}
logConsoleInfo("Successfully reset notebook workspace");
TelemetryProcessor.traceSuccess(Action.PhoenixResetWorkspace, {
dataExplorerArea: Areas.Notebook,
});
} catch (error) {
logConsoleError(`Failed to reset notebook workspace: ${error}`);
TelemetryProcessor.traceFailure(Action.PhoenixResetWorkspace, {
dataExplorerArea: Areas.Notebook,
error: getErrorMessage(error),
errorStack: getErrorStack(error),
});
if (NotebookUtil.isPhoenixEnabled()) {
if (connectionInfo && connectionInfo.status && connectionInfo.status === HttpStatusCodes.OK) {
if (connectionInfo.data && connectionInfo.data.notebookServerUrl) {
await this.setNotebookInfo(connectionInfo, connectionStatus);
useNotebook.getState().setIsRefreshed(!useNotebook.getState().isRefreshed);
}
logConsoleInfo("Successfully reset notebook workspace");
TelemetryProcessor.traceSuccess(Action.ResetNotebookWorkspace);
} else {
logConsoleError(`Failed to reset notebook workspace`);
TelemetryProcessor.traceFailure(Action.ResetNotebookWorkspace);
connectionStatus = {
status: ConnectionStatusType.Failed,
status: ConnectionStatusType.Reconnect,
};
useNotebook.getState().resetContainerConnection(connectionStatus);
useNotebook.getState().setIsRefreshed(!useNotebook.getState().isRefreshed);
}
} catch (error) {
logConsoleError(`Failed to reset notebook workspace: ${error}`);
TelemetryProcessor.traceFailure(Action.ResetNotebookWorkspace, {
error: getErrorMessage(error),
errorStack: getErrorStack(error),
});
connectionStatus = {
status: ConnectionStatusType.Failed,
};
useNotebook.getState().setConnectionInfo(connectionStatus);
useNotebook.getState().setIsRefreshed(!useNotebook.getState().isRefreshed);
throw error;
} finally {
clearInProgressMessage();
@@ -759,7 +729,7 @@ export default class Explorer {
if (!notebookContentItem || !notebookContentItem.path) {
throw new Error(`Invalid notebookContentItem: ${notebookContentItem}`);
}
if (notebookContentItem.type === NotebookContentItemType.Notebook && NotebookUtil.isPhoenixEnabled()) {
if (notebookContentItem.type === NotebookContentItemType.Notebook && useNotebook.getState().isPhoenix) {
await this.allocateContainer();
}
@@ -983,7 +953,7 @@ export default class Explorer {
handleError(error, "Explorer/onNewNotebookClicked");
throw new Error(error);
}
const isPhoenixEnabled = NotebookUtil.isPhoenixEnabled();
const isPhoenixEnabled = useNotebook.getState().isPhoenix;
if (isPhoenixEnabled) {
if (isGithubTree) {
async () => {
@@ -1075,7 +1045,7 @@ export default class Explorer {
}
public async openNotebookTerminal(kind: ViewModels.TerminalKind): Promise<void> {
if (NotebookUtil.isPhoenixEnabled()) {
if (useNotebook.getState().isPhoenix) {
await this.allocateContainer();
const notebookServerInfo = useNotebook.getState().notebookServerInfo;
if (notebookServerInfo && notebookServerInfo.notebookServerEndpoint !== undefined) {
@@ -1187,8 +1157,7 @@ export default class Explorer {
<CassandraAddCollectionPane explorer={this} cassandraApiClient={new CassandraAPIDataClient()} />
);
} else {
const throughputCap = userContext.databaseAccount?.properties.capacity?.totalThroughputLimit;
throughputCap && throughputCap !== -1
userContext.databaseAccount?.properties.capacity?.totalThroughputLimit
? await useDatabases.getState().loadAllOffers()
: await useDatabases.getState().loadDatabaseOffers();
useSidePanel
@@ -1216,7 +1185,7 @@ export default class Explorer {
}
public async handleOpenFileAction(path: string): Promise<void> {
if (userContext.features.phoenix) {
if (useNotebook.getState().isPhoenix) {
await this.allocateContainer();
} else if (!(await this._containsDefaultNotebookWorkspace(userContext.databaseAccount))) {
this._openSetupNotebooksPaneForQuickstart();
@@ -1250,7 +1219,7 @@ export default class Explorer {
}
public openUploadFilePanel(parent?: NotebookContentItem): void {
if (NotebookUtil.isPhoenixEnabled()) {
if (useNotebook.getState().isPhoenix) {
useDialog.getState().showOkCancelModalDialog(
Notebook.newNotebookUploadModalTitle,
undefined,
@@ -1264,9 +1233,6 @@ export default class Explorer {
undefined,
this.getNewNoteWarningText()
);
} else {
parent = parent || this.resourceTree.myNotebooksContentRoot;
this.uploadFilePanel(parent);
}
}
@@ -1280,7 +1246,7 @@ export default class Explorer {
}
public getDownloadModalConent(fileName: string): JSX.Element {
if (NotebookUtil.isPhoenixEnabled()) {
if (useNotebook.getState().isPhoenix) {
return (
<>
<p>{Notebook.galleryNotebookDownloadContent1}</p>
@@ -1304,16 +1270,15 @@ export default class Explorer {
await useNotebook.getState().refreshNotebooksEnabledStateForAccount();
// TODO: remove reference to isNotebookEnabled and isNotebooksEnabledForAccount
const isNotebookEnabled = userContext.features.notebooksDownBanner || userContext.features.phoenix;
const isNotebookEnabled = userContext.features.notebooksDownBanner || useNotebook.getState().isPhoenix;
useNotebook.getState().setIsNotebookEnabled(isNotebookEnabled);
useNotebook.getState().setIsShellEnabled(userContext.features.phoenix && isPublicInternetAccessAllowed());
useNotebook.getState().setIsShellEnabled(useNotebook.getState().isPhoenix && isPublicInternetAccessAllowed());
TelemetryProcessor.trace(Action.NotebookEnabled, ActionModifiers.Mark, {
isNotebookEnabled,
dataExplorerArea: Constants.Areas.Notebook,
});
if (!userContext.features.notebooksTemporarilyDown) {
if (useNotebook.getState().isPhoenix) {
if (isNotebookEnabled) {
await this.initNotebooks(userContext.databaseAccount);
} else if (this.notebookToImport) {

View File

@@ -4,15 +4,12 @@
* and update any knockout observables passed from the parent.
*/
import { CommandBar as FluentCommandBar, ICommandBarItemProps } from "@fluentui/react";
import { useNotebook } from "Explorer/Notebook/useNotebook";
import * as React from "react";
import create, { UseStore } from "zustand";
import { StyleConstants } from "../../../Common/Constants";
import * as ViewModels from "../../../Contracts/ViewModels";
import { useTabs } from "../../../hooks/useTabs";
import { userContext } from "../../../UserContext";
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
import Explorer from "../../Explorer";
import { NotebookUtil } from "../../Notebook/NotebookUtil";
import { useSelectedNode } from "../../useSelectedNode";
import * as CommandBarComponentButtonFactory from "./CommandBarComponentButtonFactory";
import * as CommandBarUtil from "./CommandBarUtil";
@@ -56,18 +53,10 @@ export const CommandBar: React.FC<Props> = ({ container }: Props) => {
const uiFabricControlButtons = CommandBarUtil.convertButton(controlButtons, backgroundColor);
uiFabricControlButtons.forEach((btn: ICommandBarItemProps) => (btn.iconOnly = true));
if (NotebookUtil.isPhoenixEnabled()) {
if (useNotebook.getState().isPhoenix) {
uiFabricControlButtons.unshift(CommandBarUtil.createConnectionStatus(container, "connectionStatus"));
}
if (
userContext.features.phoenix === false &&
userContext.features.notebooksTemporarilyDown === false &&
useTabs.getState().activeTab?.tabKind === ViewModels.CollectionTabKind.NotebookV2
) {
uiFabricControlButtons.unshift(CommandBarUtil.createMemoryTracker("memoryTracker"));
}
return (
<div className="commandBarContainer">
<FluentCommandBar

View File

@@ -194,29 +194,26 @@ describe("CommandBarComponentButtonFactory tests", () => {
it("Notebooks is enabled and is unavailable - button should be shown and enabled", () => {
useNotebook.getState().setIsNotebookEnabled(true);
useNotebook.getState().setIsPhoenix(true);
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
const openMongoShellBtn = buttons.find((button) => button.commandButtonLabel === openMongoShellBtnLabel);
expect(openMongoShellBtn).toBeDefined();
//TODO: modify once notebooks are available
expect(openMongoShellBtn.disabled).toBe(true);
//expect(openMongoShellBtn.disabled).toBe(false);
//expect(openMongoShellBtn.tooltipText).toBe("");
expect(openMongoShellBtn.disabled).toBe(false);
expect(openMongoShellBtn.tooltipText).toBe("");
});
it("Notebooks is enabled and is available - button should be shown and enabled", () => {
useNotebook.getState().setIsNotebookEnabled(true);
useNotebook.getState().setIsPhoenix(true);
useNotebook.getState().setIsNotebooksEnabledForAccount(true);
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
const openMongoShellBtn = buttons.find((button) => button.commandButtonLabel === openMongoShellBtnLabel);
expect(openMongoShellBtn).toBeDefined();
//TODO: modify once notebooks are available
expect(openMongoShellBtn.disabled).toBe(true);
//expect(openMongoShellBtn.disabled).toBe(false);
//expect(openMongoShellBtn.tooltipText).toBe("");
expect(openMongoShellBtn.disabled).toBe(false);
expect(openMongoShellBtn.tooltipText).toBe("");
});
it("Notebooks is enabled and is available, terminal is unavailable due to ipRules - button should be hidden", () => {
@@ -299,30 +296,27 @@ describe("CommandBarComponentButtonFactory tests", () => {
it("Notebooks is enabled and is unavailable - button should be shown and enabled", () => {
useNotebook.getState().setIsNotebookEnabled(true);
useNotebook.getState().setIsPhoenix(true);
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
const openCassandraShellBtn = buttons.find((button) => button.commandButtonLabel === openCassandraShellBtnLabel);
expect(openCassandraShellBtn).toBeDefined();
//TODO: modify once notebooks are available
expect(openCassandraShellBtn.disabled).toBe(true);
//expect(openCassandraShellBtn.disabled).toBe(false);
//expect(openCassandraShellBtn.tooltipText).toBe("");
expect(openCassandraShellBtn.disabled).toBe(false);
expect(openCassandraShellBtn.tooltipText).toBe("");
});
it("Notebooks is enabled and is available - button should be shown and enabled", () => {
useNotebook.getState().setIsNotebookEnabled(true);
useNotebook.getState().setIsPhoenix(true);
useNotebook.getState().setIsNotebooksEnabledForAccount(true);
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
const openCassandraShellBtn = buttons.find((button) => button.commandButtonLabel === openCassandraShellBtnLabel);
expect(openCassandraShellBtn).toBeDefined();
//TODO: modify once notebooks are available
expect(openCassandraShellBtn.disabled).toBe(true);
//expect(openCassandraShellBtn.disabled).toBe(false);
//expect(openCassandraShellBtn.tooltipText).toBe("");
expect(openCassandraShellBtn.disabled).toBe(false);
expect(openCassandraShellBtn.tooltipText).toBe("");
});
});

View File

@@ -10,7 +10,6 @@ import CosmosTerminalIcon from "../../../../images/Cosmos-Terminal.svg";
import FeedbackIcon from "../../../../images/Feedback-Command.svg";
import GitHubIcon from "../../../../images/github.svg";
import HostedTerminalIcon from "../../../../images/Hosted-Terminal.svg";
import EnableNotebooksIcon from "../../../../images/notebook/Notebook-enable.svg";
import NewNotebookIcon from "../../../../images/notebook/Notebook-new.svg";
import ResetWorkspaceIcon from "../../../../images/notebook/Notebook-reset-workspace.svg";
import OpenInTabIcon from "../../../../images/open-in-tab.svg";
@@ -98,7 +97,7 @@ export function createStaticCommandBarButtons(
}
notebookButtons.forEach((btn) => {
if (userContext.features.notebooksTemporarilyDown) {
if (useNotebook.getState().isPhoenix === false && userContext.features.notebooksDownBanner) {
if (btn.commandButtonLabel.indexOf("Cassandra") !== -1) {
applyNotebooksTemporarilyDownStyle(btn, Constants.Notebook.cassandraShellTemporarilyDownMsg);
} else if (btn.commandButtonLabel.indexOf("Mongo") !== -1) {
@@ -109,11 +108,6 @@ export function createStaticCommandBarButtons(
}
buttons.push(btn);
});
} else {
if (!isRunningOnNationalCloud() && !userContext.features.notebooksTemporarilyDown) {
buttons.push(createDivider());
buttons.push(createEnableNotebooksButton(container));
}
}
if (!selectedNodeState.isDatabaseNodeOrNoneSelected()) {
@@ -168,7 +162,7 @@ export function createContextCommandBarButtons(
onCommandClick: () => {
const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection();
if (useNotebook.getState().isShellEnabled) {
if (!userContext.features.notebooksTemporarilyDown) {
if (useNotebook.getState().isPhoenix) {
container.openNotebookTerminal(ViewModels.TerminalKind.Mongo);
}
} else {
@@ -179,12 +173,12 @@ export function createContextCommandBarButtons(
ariaLabel: label,
hasPopup: true,
tooltipText:
useNotebook.getState().isShellEnabled && userContext.features.notebooksTemporarilyDown
useNotebook.getState().isShellEnabled && useNotebook.getState().isPhoenix === false
? Constants.Notebook.mongoShellTemporarilyDownMsg
: undefined,
disabled:
(selectedNodeState.isDatabaseNodeOrNoneSelected() && userContext.apiType === "Mongo") ||
(useNotebook.getState().isShellEnabled && userContext.features.notebooksTemporarilyDown),
(useNotebook.getState().isShellEnabled && useNotebook.getState().isPhoenix === false),
};
buttons.push(newMongoShellBtn);
}
@@ -311,8 +305,7 @@ function createNewDatabase(container: Explorer): CommandButtonComponentProps {
iconSrc: AddDatabaseIcon,
iconAlt: label,
onCommandClick: async () => {
const throughputCap = userContext.databaseAccount?.properties.capacity?.totalThroughputLimit;
if (throughputCap && throughputCap !== -1) {
if (userContext.databaseAccount?.properties.capacity?.totalThroughputLimit) {
await useDatabases.getState().loadAllOffers();
}
useSidePanel.getState().openSidePanel("New " + getDatabaseName(), <AddDatabasePanel explorer={container} />);
@@ -477,33 +470,6 @@ function createOpenQueryFromDiskButton(): CommandButtonComponentProps {
};
}
function createEnableNotebooksButton(container: Explorer): CommandButtonComponentProps {
if (configContext.platform === Platform.Emulator) {
return undefined;
}
const label = "Enable Notebooks (Preview)";
const tooltip =
"Notebooks are not yet available in your account's region. View supported regions here: https://aka.ms/cosmos-enable-notebooks.";
const description =
"Looks like you have not yet created a notebooks workspace for this account. To proceed and start using notebooks, we'll need to create a default notebooks workspace in this account.";
return {
iconSrc: EnableNotebooksIcon,
iconAlt: label,
onCommandClick: () =>
useSidePanel
.getState()
.openSidePanel(
label,
<SetupNoteBooksPanel explorer={container} panelTitle={label} panelDescription={description} />
),
commandButtonLabel: label,
hasPopup: false,
disabled: !useNotebook.getState().isNotebooksEnabledForAccount,
ariaLabel: label,
tooltipText: useNotebook.getState().isNotebooksEnabledForAccount ? "" : tooltip,
};
}
function createOpenTerminalButton(container: Explorer): CommandButtonComponentProps {
const label = "Open Terminal";
return {

View File

@@ -10,6 +10,7 @@ import * as Logger from "../../Common/Logger";
import * as DataModels from "../../Contracts/DataModels";
import {
ContainerConnectionInfo,
IAccountData,
IPhoenixConnectionInfoResult,
IProvisionData,
IResponse,
@@ -18,7 +19,6 @@ import { userContext } from "../../UserContext";
import { createOrUpdate, destroy } from "../../Utils/arm/generatedClients/cosmosNotebooks/notebookWorkspaces";
import { getAuthorizationHeader } from "../../Utils/AuthorizationUtils";
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { NotebookUtil } from "./NotebookUtil";
import { useNotebook } from "./useNotebook";
export class NotebookContainerClient {
@@ -63,7 +63,7 @@ export class NotebookContainerClient {
this.scheduleHeartbeat(Constants.Notebook.heartbeatDelayMs);
}
} catch (exception) {
if (NotebookUtil.isPhoenixEnabled()) {
if (useNotebook.getState().isPhoenix) {
const connectionStatus: ContainerConnectionInfo = {
status: ConnectionStatusType.Failed,
};
@@ -131,14 +131,14 @@ export class NotebookContainerClient {
} else if (response.status === HttpStatusCodes.NotFound) {
throw new AbortError(response.statusText);
}
throw new Error();
throw new Error(response.statusText);
} else {
return undefined;
}
}
private checkStatus(): boolean {
if (NotebookUtil.isPhoenixEnabled()) {
if (useNotebook.getState().isPhoenix) {
if (useNotebook.getState().containerStatus?.status === Constants.ContainerStatusType.Disconnected) {
const connectionStatus: ContainerConnectionInfo = {
status: ConnectionStatusType.Reconnect,
@@ -173,21 +173,20 @@ export class NotebookContainerClient {
}
try {
if (NotebookUtil.isPhoenixEnabled()) {
if (useNotebook.getState().isPhoenix) {
const provisionData: IProvisionData = {
cosmosEndpoint: userContext.databaseAccount.properties.documentEndpoint,
};
const accountData: IAccountData = {
subscriptionId: userContext.subscriptionId,
resourceGroup: userContext.resourceGroup,
dbAccountName: userContext.databaseAccount.name,
cosmosEndpoint: userContext.databaseAccount.properties.documentEndpoint,
};
return await this.phoenixClient.resetContainer(provisionData);
return await this.phoenixClient.resetContainer(provisionData, accountData);
}
return null;
} catch (error) {
Logger.logError(getErrorMessage(error), "NotebookContainerClient/resetWorkspace");
if (!NotebookUtil.isPhoenixEnabled()) {
await this.recreateNotebookWorkspaceAsync();
}
throw error;
}
}

View File

@@ -3,7 +3,6 @@ import { AppState, selectors } from "@nteract/core";
import domtoimage from "dom-to-image";
import Html2Canvas from "html2canvas";
import path from "path";
import { userContext } from "../../UserContext";
import * as GitHubUtils from "../../Utils/GitHubUtils";
import * as StringUtils from "../../Utils/StringUtils";
import { SnapshotFragment } from "./NotebookComponent/types";
@@ -329,16 +328,4 @@ export class NotebookUtil {
link.click();
document.body.removeChild(link);
}
public static getNotebookBtnTitle(fileName: string): string {
if (this.isPhoenixEnabled()) {
return `Download to ${fileName}`;
} else {
return `Download to my notebooks`;
}
}
public static isPhoenixEnabled(): boolean {
return userContext.features.notebooksTemporarilyDown === false && userContext.features.phoenix === true;
}
}

View File

@@ -1,4 +1,5 @@
import { cloneDeep } from "lodash";
import { PhoenixClient } from "Phoenix/PhoenixClient";
import create, { UseStore } from "zustand";
import { AuthType } from "../../AuthType";
import * as Constants from "../../Common/Constants";
@@ -7,7 +8,7 @@ import { getErrorMessage } from "../../Common/ErrorHandlingUtils";
import * as Logger from "../../Common/Logger";
import { configContext } from "../../ConfigContext";
import * as DataModels from "../../Contracts/DataModels";
import { ContainerConnectionInfo, ContainerInfo } from "../../Contracts/DataModels";
import { ContainerConnectionInfo, ContainerInfo, IAccountData } from "../../Contracts/DataModels";
import { useTabs } from "../../hooks/useTabs";
import { IPinnedRepo } from "../../Juno/JunoClient";
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
@@ -17,7 +18,6 @@ import { getAuthorizationHeader } from "../../Utils/AuthorizationUtils";
import * as GitHubUtils from "../../Utils/GitHubUtils";
import { NotebookContentItem, NotebookContentItemType } from "./NotebookContentItem";
import NotebookManager from "./NotebookManager";
import { NotebookUtil } from "./NotebookUtil";
interface NotebookState {
isNotebookEnabled: boolean;
@@ -37,6 +37,7 @@ interface NotebookState {
isAllocating: boolean;
isRefreshed: boolean;
containerStatus: ContainerInfo;
isPhoenix: boolean;
setIsNotebookEnabled: (isNotebookEnabled: boolean) => void;
setIsNotebooksEnabledForAccount: (isNotebooksEnabledForAccount: boolean) => void;
setNotebookServerInfo: (notebookServerInfo: DataModels.NotebookWorkspaceConnectionInfo) => void;
@@ -58,6 +59,8 @@ interface NotebookState {
resetContainerConnection: (connectionStatus: ContainerConnectionInfo) => void;
setIsRefreshed: (isAllocating: boolean) => void;
setContainerStatus: (containerStatus: ContainerInfo) => void;
getPhoenixStatus: () => Promise<void>;
setIsPhoenix: (isPhoenix: boolean) => void;
}
export const useNotebook: UseStore<NotebookState> = create((set, get) => ({
@@ -92,6 +95,7 @@ export const useNotebook: UseStore<NotebookState> = create((set, get) => ({
durationLeftInMinutes: undefined,
notebookServerInfo: undefined,
},
isPhoenix: undefined,
setIsNotebookEnabled: (isNotebookEnabled: boolean) => set({ isNotebookEnabled }),
setIsNotebooksEnabledForAccount: (isNotebooksEnabledForAccount: boolean) => set({ isNotebooksEnabledForAccount }),
setNotebookServerInfo: (notebookServerInfo: DataModels.NotebookWorkspaceConnectionInfo) =>
@@ -104,6 +108,7 @@ export const useNotebook: UseStore<NotebookState> = create((set, get) => ({
setNotebookBasePath: (notebookBasePath: string) => set({ notebookBasePath }),
setNotebookFolderName: (notebookFolderName: string) => set({ notebookFolderName }),
refreshNotebooksEnabledStateForAccount: async (): Promise<void> => {
await get().getPhoenixStatus();
const { databaseAccount, authType } = userContext;
if (
authType === AuthType.EncryptedToken ||
@@ -196,7 +201,7 @@ export const useNotebook: UseStore<NotebookState> = create((set, get) => ({
isGithubTree ? set({ gitHubNotebooksContentRoot: root }) : set({ myNotebooksContentRoot: root });
},
initializeNotebooksTree: async (notebookManager: NotebookManager): Promise<void> => {
const notebookFolderName = NotebookUtil.isPhoenixEnabled() === true ? "Temporary Notebooks" : "My Notebooks";
const notebookFolderName = "Temporary Notebooks";
set({ notebookFolderName });
const myNotebooksContentRoot = {
name: get().notebookFolderName,
@@ -292,4 +297,17 @@ export const useNotebook: UseStore<NotebookState> = create((set, get) => ({
},
setIsRefreshed: (isRefreshed: boolean) => set({ isRefreshed }),
setContainerStatus: (containerStatus: ContainerInfo) => set({ containerStatus }),
getPhoenixStatus: async () => {
if (get().isPhoenix === undefined) {
const phoenixClient = new PhoenixClient();
const accountData: IAccountData = {
subscriptionId: userContext.subscriptionId,
resourceGroup: userContext.resourceGroup,
dbAccountName: userContext.databaseAccount.name,
};
const isPhoenix = await phoenixClient.IsDbAcountWhitelisted(accountData);
set({ isPhoenix });
}
},
setIsPhoenix: (isPhoenix: boolean) => set({ isPhoenix }),
}));

View File

@@ -11,7 +11,7 @@ import {
Separator,
Stack,
Text,
TooltipHost
TooltipHost,
} from "@fluentui/react";
import * as Constants from "Common/Constants";
import { createCollection } from "Common/dataAccess/createCollection";
@@ -468,8 +468,8 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
directionalHint={DirectionalHint.bottomLeftEdge}
content={`You can optionally provision dedicated throughput for a ${getCollectionName().toLocaleLowerCase()} within a database that has throughput
provisioned. This dedicated throughput amount will not be shared with other ${getCollectionName(
true
).toLocaleLowerCase()} in the database and
true
).toLocaleLowerCase()} in the database and
does not count towards the throughput you provisioned for the database. This throughput amount will be
billed in addition to the throughput amount you provisioned at the database level.`}
>
@@ -667,7 +667,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
{userContext.apiType === "SQL" && (
<Checkbox
label="My partition key is larger than 101 bytes"
label="My partition key is larger than 100 bytes"
checked={this.state.useHashV2}
styles={{
text: { fontSize: 12 },
@@ -1019,10 +1019,10 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
const partitionKeyVersion = this.state.useHashV2 ? 2 : undefined;
const partitionKey: DataModels.PartitionKey = partitionKeyString
? {
paths: [partitionKeyString],
kind: "Hash",
version: partitionKeyVersion,
}
paths: [partitionKeyString],
kind: "Hash",
version: partitionKeyVersion,
}
: undefined;
const indexingPolicy: DataModels.IndexingPolicy = this.state.enableIndexing

View File

@@ -23,12 +23,10 @@ import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneFor
export interface AddDatabasePaneProps {
explorer: Explorer;
buttonElement?: HTMLElement;
}
export const AddDatabasePanel: FunctionComponent<AddDatabasePaneProps> = ({
explorer: container,
buttonElement,
}: AddDatabasePaneProps) => {
const closeSidePanel = useSidePanel((state) => state.closeSidePanel);
let throughput: number;
@@ -80,9 +78,6 @@ export const AddDatabasePanel: FunctionComponent<AddDatabasePaneProps> = ({
dataExplorerArea: Constants.Areas.ContextualPane,
};
TelemetryProcessor.trace(Action.CreateDatabase, ActionModifiers.Open, addDatabasePaneOpenMessage);
if (buttonElement) {
buttonElement.focus();
}
}, []);
const onSubmit = () => {

View File

@@ -5,7 +5,6 @@ import { getErrorMessage, handleError } from "../../../Common/ErrorHandlingUtils
import { GitHubOAuthService } from "../../../GitHub/GitHubOAuthService";
import { useSidePanel } from "../../../hooks/useSidePanel";
import { IPinnedRepo, JunoClient } from "../../../Juno/JunoClient";
import { userContext } from "../../../UserContext";
import * as GitHubUtils from "../../../Utils/GitHubUtils";
import * as NotificationConsoleUtils from "../../../Utils/NotificationConsoleUtils";
import Explorer from "../../Explorer";
@@ -76,7 +75,7 @@ export const CopyNotebookPane: FunctionComponent<CopyNotebookPanelProps> = ({
selectedLocation.owner,
selectedLocation.repo
)} - ${selectedLocation.branch}`;
} else if (selectedLocation.type === "MyNotebooks" && userContext.features.phoenix) {
} else if (selectedLocation.type === "MyNotebooks") {
destination = useNotebook.getState().notebookFolderName;
}

View File

@@ -1,4 +1,4 @@
import { Checkbox, ChoiceGroup, IChoiceGroupOption, SpinButton, Stack, Text } from "@fluentui/react";
import { Checkbox, ChoiceGroup, IChoiceGroupOption, SpinButton } from "@fluentui/react";
import * as Constants from "Common/Constants";
import { InfoTooltip } from "Common/Tooltip/InfoTooltip";
import { configContext } from "ConfigContext";
@@ -113,44 +113,20 @@ export const SettingsPane: FunctionComponent = () => {
const handleOnPageOptionChange = (ev: React.FormEvent<HTMLInputElement>, option: IChoiceGroupOption): void => {
setPageOption(option.key);
};
const choiceButtonStyles = {
flexContainer: [
{
selectors: {
".ms-ChoiceField-wrapper label": {
fontSize: 12,
paddingTop: 0,
},
".ms-ChoiceField": {
marginTop: 0,
},
},
},
],
};
return (
<RightPaneForm {...genericPaneProps}>
<div className="paneMainContent">
{shouldShowQueryPageOptions && (
<div className="settingsSection">
<div className="settingsSectionPart">
<Stack horizontal>
<Text id="pageOptions" className="settingsSectionLabel" variant="small">
Page options
</Text>
<div className="settingsSectionPart pageOptionsPart">
<div className="settingsSectionLabel">
Page options
<InfoTooltip>
Choose Custom to specify a fixed amount of query results to show, or choose Unlimited to show as many
query results per page.
</InfoTooltip>
</Stack>
<ChoiceGroup
ariaLabelledBy="pageOptions"
selectedKey={pageOption}
options={pageOptionList}
styles={choiceButtonStyles}
onChange={handleOnPageOptionChange}
/>
</div>
<ChoiceGroup selectedKey={pageOption} options={pageOptionList} onChange={handleOnPageOptionChange} />
</div>
<div className="tabs settingsSectionPart">
{isCustomPageOptionSelected() && (

View File

@@ -14,24 +14,17 @@ exports[`Settings Pane should render Default properly 1`] = `
className="settingsSection"
>
<div
className="settingsSectionPart"
className="settingsSectionPart pageOptionsPart"
>
<Stack
horizontal={true}
<div
className="settingsSectionLabel"
>
<Text
className="settingsSectionLabel"
id="pageOptions"
variant="small"
>
Page options
</Text>
Page options
<InfoTooltip>
Choose Custom to specify a fixed amount of query results to show, or choose Unlimited to show as many query results per page.
</InfoTooltip>
</Stack>
</div>
<StyledChoiceGroup
ariaLabelledBy="pageOptions"
onChange={[Function]}
options={
Array [
@@ -46,23 +39,6 @@ exports[`Settings Pane should render Default properly 1`] = `
]
}
selectedKey="custom"
styles={
Object {
"flexContainer": Array [
Object {
"selectors": Object {
".ms-ChoiceField": Object {
"marginTop": 0,
},
".ms-ChoiceField-wrapper label": Object {
"fontSize": 12,
"paddingTop": 0,
},
},
},
],
}
}
/>
</div>
<div

View File

@@ -33,7 +33,6 @@ const {
Inet,
Smallint,
Tinyint,
Timestamp,
} = TableConstants.CassandraType;
export const cassandraOptions = [
{ key: Text, text: Text },
@@ -51,7 +50,6 @@ export const cassandraOptions = [
{ key: Inet, text: Inet },
{ key: Smallint, text: Smallint },
{ key: Tinyint, text: Tinyint },
{ key: Timestamp, text: Timestamp },
];
export const imageProps: IImageProps = {

View File

@@ -84,10 +84,7 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
const mainItems = this.createMainItems();
const commonTaskItems = this.createCommonTaskItems();
let recentItems = this.createRecentItems();
if (userContext.features.notebooksTemporarilyDown) {
recentItems = recentItems.filter((item) => item.description !== "Notebook");
}
recentItems = recentItems.filter((item) => item.description !== "Notebook");
const tipsItems = this.createTipsItems();
const onClearRecent = this.clearMostRecent;
@@ -223,7 +220,7 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
});
}
if (useNotebook.getState().isNotebookEnabled && !userContext.features.notebooksTemporarilyDown) {
if (useNotebook.getState().isPhoenix) {
heroes.push({
iconSrc: NewNotebookIcon,
title: "New Notebook",
@@ -308,16 +305,12 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
title: "New " + getDatabaseName(),
description: undefined,
onClick: async () => {
const throughputCap = userContext.databaseAccount?.properties.capacity?.totalThroughputLimit;
if (throughputCap && throughputCap !== -1) {
if (userContext.databaseAccount?.properties.capacity?.totalThroughputLimit) {
await useDatabases.getState().loadAllOffers();
}
useSidePanel
.getState()
.openSidePanel(
"New " + getDatabaseName(),
<AddDatabasePanel explorer={this.container} buttonElement={document.activeElement as HTMLElement} />
);
.openSidePanel("New " + getDatabaseName(), <AddDatabasePanel explorer={this.container} />);
},
});
}

View File

@@ -19,7 +19,6 @@ export const CassandraType = {
Float: "Float",
Int: "Int",
Text: "Text",
Timestamp: "Timestamp",
Uuid: "Uuid",
Varchar: "Varchar",
Varint: "Varint",

View File

@@ -431,7 +431,7 @@ export default class TableEntityListViewModel extends DataTableViewModel {
if (newHeaders.length > 0) {
// Any new columns found will be added into headers array, which will trigger a re-render of the DataTable.
// So there is no need to call it here.
this.updateHeaders(selectedHeadersUnion, /* notifyColumnChanges */ true);
this.updateHeaders(newHeaders, /* notifyColumnChanges */ true);
} else {
if (columnSortOrder) {
this.sortColumns(columnSortOrder, oSettings);

View File

@@ -535,8 +535,7 @@ export class CassandraAPIDataClient extends TableDataClient {
dataType === TableConstants.CassandraType.Text ||
dataType === TableConstants.CassandraType.Inet ||
dataType === TableConstants.CassandraType.Ascii ||
dataType === TableConstants.CassandraType.Varchar ||
dataType === TableConstants.CassandraType.Timestamp
dataType === TableConstants.CassandraType.Varchar
);
}

View File

@@ -1,5 +1,5 @@
import { Resource, StoredProcedureDefinition, TriggerDefinition, UserDefinedFunctionDefinition } from "@azure/cosmos";
import { NotebookUtil } from "Explorer/Notebook/NotebookUtil";
import { useNotebook } from "Explorer/Notebook/useNotebook";
import * as ko from "knockout";
import * as _ from "underscore";
import * as Constants from "../../Common/Constants";
@@ -529,7 +529,7 @@ export default class Collection implements ViewModels.Collection {
};
public onSchemaAnalyzerClick = async () => {
if (NotebookUtil.isPhoenixEnabled()) {
if (useNotebook.getState().isPhoenix) {
await this.container.allocateContainer();
}
useSelectedNode.getState().setSelectedNode(this);
@@ -576,8 +576,9 @@ export default class Collection implements ViewModels.Collection {
public onSettingsClick = async (): Promise<void> => {
useSelectedNode.getState().setSelectedNode(this);
const throughputCap = userContext.databaseAccount?.properties.capacity?.totalThroughputLimit;
throughputCap && throughputCap !== -1 ? await useDatabases.getState().loadAllOffers() : await this.loadOffer();
userContext.databaseAccount?.properties.capacity?.totalThroughputLimit
? await useDatabases.getState().loadAllOffers()
: await this.loadOffer();
this.selectedSubnodeKind(ViewModels.CollectionTabKind.Settings);
TelemetryProcessor.trace(Action.SelectItem, ActionModifiers.Mark, {
description: "Settings node",

View File

@@ -66,8 +66,7 @@ export default class Database implements ViewModels.Database {
dataExplorerArea: Constants.Areas.ResourceTree,
});
const throughputCap = userContext.databaseAccount?.properties.capacity?.totalThroughputLimit;
if (throughputCap && throughputCap !== -1) {
if (userContext.databaseAccount?.properties.capacity?.totalThroughputLimit) {
await useDatabases.getState().loadAllOffers();
}

View File

@@ -121,18 +121,17 @@ export const ResourceTree: React.FC<ResourceTreeProps> = ({ container }: Resourc
children: [],
};
if (userContext.features.notebooksTemporarilyDown) {
if (useNotebook.getState().isPhoenix === false && userContext.features.notebooksDownBanner) {
notebooksTree.children.push(buildNotebooksTemporarilyDownTree());
} else {
} else if (useNotebook.getState().isPhoenix) {
if (galleryContentRoot) {
notebooksTree.children.push(buildGalleryNotebooksTree());
}
if (
myNotebooksContentRoot &&
((NotebookUtil.isPhoenixEnabled() &&
useNotebook.getState().connectionInfo.status === ConnectionStatusType.Connected) ||
userContext.features.phoenix === false)
useNotebook.getState().isPhoenix &&
useNotebook.getState().connectionInfo.status === ConnectionStatusType.Connected
) {
notebooksTree.children.push(buildMyNotebooksTree());
}
@@ -166,15 +165,7 @@ export const ResourceTree: React.FC<ResourceTreeProps> = ({ container }: Resourc
const myNotebooksTree: TreeNode = buildNotebookDirectoryNode(
myNotebooksContentRoot,
(item: NotebookContentItem) => {
container.openNotebook(item).then((hasOpened) => {
if (
hasOpened &&
userContext.features.notebooksTemporarilyDown === false &&
userContext.features.phoenix === false
) {
mostRecentActivity.notebookWasItemOpened(userContext.databaseAccount?.id, item);
}
});
container.openNotebook(item);
}
);
@@ -189,15 +180,7 @@ export const ResourceTree: React.FC<ResourceTreeProps> = ({ container }: Resourc
const gitHubNotebooksTree: TreeNode = buildNotebookDirectoryNode(
gitHubNotebooksContentRoot,
(item: NotebookContentItem) => {
container.openNotebook(item).then((hasOpened) => {
if (
hasOpened &&
userContext.features.notebooksTemporarilyDown === false &&
userContext.features.phoenix === false
) {
mostRecentActivity.notebookWasItemOpened(userContext.databaseAccount?.id, item);
}
});
container.openNotebook(item);
},
true
);
@@ -528,7 +511,7 @@ export const ResourceTree: React.FC<ResourceTreeProps> = ({ container }: Resourc
isNotebookEnabled &&
userContext.apiType === "Mongo" &&
isPublicInternetAccessAllowed() &&
!userContext.features.notebooksTemporarilyDown
useNotebook.getState().isPhoenix
) {
children.push({
label: "Schema (Preview)",

View File

@@ -1,8 +1,8 @@
import ko from "knockout";
import postRobot from "post-robot";
import { GetGithubClientId } from "Utils/GitHubUtils";
import { HttpStatusCodes } from "../Common/Constants";
import { handleError } from "../Common/ErrorHandlingUtils";
import { configContext } from "../ConfigContext";
import { AuthorizeAccessComponent } from "../Explorer/Controls/GitHub/AuthorizeAccessComponent";
import { JunoClient } from "../Juno/JunoClient";
import { logConsoleInfo } from "../Utils/NotificationConsoleUtils";
@@ -55,7 +55,7 @@ export class GitHubOAuthService {
const params = {
scope,
client_id: GetGithubClientId(),
client_id: configContext.GITHUB_CLIENT_ID,
redirect_uri: new URL("./connectToGitHub.html", window.location.href).href,
state: this.resetState(),
};

View File

@@ -1,5 +1,4 @@
import ko from "knockout";
import { GetGithubClientId } from "Utils/GitHubUtils";
import { HttpHeaders, HttpStatusCodes } from "../Common/Constants";
import { configContext } from "../ConfigContext";
import * as DataModels from "../Contracts/DataModels";
@@ -523,7 +522,7 @@ export class JunoClient {
private static getGitHubClientParams(): URLSearchParams {
const githubParams = new URLSearchParams({
client_id: GetGithubClientId(),
client_id: configContext.GITHUB_CLIENT_ID,
});
if (configContext.GITHUB_CLIENT_SECRET) {

View File

@@ -5,6 +5,7 @@ import * as Logger from "../Common/Logger";
import { configContext } from "../ConfigContext";
import {
ContainerInfo,
IAccountData,
IContainerData,
IPhoenixConnectionInfoResult,
IProvisionData,
@@ -22,24 +23,36 @@ export class PhoenixClient {
minTimeout: Notebook.retryAttemptDelayMs,
};
public async allocateContainer(provisionData: IProvisionData): Promise<IResponse<IPhoenixConnectionInfoResult>> {
return this.executeContainerAssignmentOperation(provisionData, "allocate");
public async allocateContainer(
provisionData: IProvisionData,
accountData: IAccountData
): Promise<IResponse<IPhoenixConnectionInfoResult>> {
return this.executeContainerAssignmentOperation(provisionData, accountData, "allocate");
}
public async resetContainer(provisionData: IProvisionData): Promise<IResponse<IPhoenixConnectionInfoResult>> {
return this.executeContainerAssignmentOperation(provisionData, "reset");
public async resetContainer(
provisionData: IProvisionData,
accountData: IAccountData
): Promise<IResponse<IPhoenixConnectionInfoResult>> {
return this.executeContainerAssignmentOperation(provisionData, accountData, "reset");
}
private async executeContainerAssignmentOperation(
provisionData: IProvisionData,
accountData: IAccountData,
operation: string
): Promise<IResponse<IPhoenixConnectionInfoResult>> {
try {
const response = await fetch(`${this.getPhoenixContainerPoolingEndPoint()}/${operation}`, {
method: "POST",
headers: PhoenixClient.getHeaders(),
body: JSON.stringify(provisionData),
});
const response = await fetch(
`${this.getPhoenixContainerPoolingEndPoint()}/subscriptions/${accountData.subscriptionId}/resourceGroups/${
accountData.resourceGroup
}/providers/Microsoft.DocumentDB/databaseAccounts/${accountData.dbAccountName}/containerconnections`,
{
method: operation === "allocate" ? "POST" : "PATCH",
headers: PhoenixClient.getHeaders(),
body: JSON.stringify(provisionData),
}
);
let data: IPhoenixConnectionInfoResult;
if (response.status === HttpStatusCodes.OK) {
@@ -55,7 +68,7 @@ export class PhoenixClient {
}
}
public async initiateContainerHeartBeat(containerData: { forwardingId: string; dbAccountName: string }) {
public async initiateContainerHeartBeat(containerData: IContainerData) {
if (this.containerHealthHandler) {
clearTimeout(this.containerHealthHandler);
}
@@ -72,7 +85,11 @@ export class PhoenixClient {
try {
const runContainerStatusAsync = async () => {
const response = await window.fetch(
`${this.getPhoenixContainerPoolingEndPoint()}/${containerData.dbAccountName}/${containerData.forwardingId}`,
`${this.getPhoenixContainerPoolingEndPoint()}/subscriptions/${containerData.subscriptionId}/resourceGroups/${
containerData.resourceGroup
}/providers/Microsoft.DocumentDB/databaseAccounts/${containerData.dbAccountName}/${
containerData.forwardingId
}`,
{
method: "GET",
headers: PhoenixClient.getHeaders(),
@@ -101,7 +118,7 @@ export class PhoenixClient {
}
}
private async getContainerHealth(delayMs: number, containerData: { forwardingId: string; dbAccountName: string }) {
private async getContainerHealth(delayMs: number, containerData: IContainerData) {
try {
const containerInfo = await this.getContainerStatusAsync(containerData);
useNotebook.getState().setContainerStatus(containerInfo);
@@ -117,6 +134,24 @@ export class PhoenixClient {
}
}
public async IsDbAcountWhitelisted(accountData: IAccountData) {
try {
const response = await window.fetch(
`${this.getPhoenixContainerPoolingEndPoint()}/subscriptions/${accountData.subscriptionId}/resourceGroups/${
accountData.resourceGroup
}/providers/Microsoft.DocumentDB/databaseAccounts/${accountData.dbAccountName}`,
{
method: "GET",
headers: PhoenixClient.getHeaders(),
}
);
return response.status === HttpStatusCodes.OK;
} catch (error) {
Logger.logError(getErrorMessage(error), "PhoenixClient/IsDbAcountWhitelisted");
return false;
}
}
public static getPhoenixEndpoint(): string {
const phoenixEndpoint =
userContext.features.phoenixEndpoint ?? userContext.features.junoEndpoint ?? configContext.JUNO_ENDPOINT;
@@ -130,7 +165,7 @@ export class PhoenixClient {
}
public getPhoenixContainerPoolingEndPoint(): string {
return `${PhoenixClient.getPhoenixEndpoint()}/api/controlplane/toolscontainer`;
return `${PhoenixClient.getPhoenixEndpoint()}/api/controlplane/toolscontainer/cosmosaccounts`;
}
private static getHeaders(): HeadersInit {

View File

@@ -11,7 +11,6 @@ export type Features = {
autoscaleDefault: boolean;
partitionKeyDefault: boolean;
partitionKeyDefault2: boolean;
phoenix: boolean;
notebooksDownBanner: boolean;
readonly enableSDKoperations: boolean;
readonly enableSpark: boolean;
@@ -33,7 +32,6 @@ export type Features = {
readonly ttl90Days: boolean;
readonly mongoProxyEndpoint?: string;
readonly mongoProxyAPIs?: string;
readonly notebooksTemporarilyDown: boolean;
readonly enableThroughputCap: boolean;
};
@@ -84,8 +82,6 @@ export function extractFeatures(given = new URLSearchParams(window.location.sear
autoscaleDefault: "true" === get("autoscaledefault"),
partitionKeyDefault: "true" === get("partitionkeytest"),
partitionKeyDefault2: "true" === get("pkpartitionkeytest"),
notebooksTemporarilyDown: "true" === get("notebookstemporarilydown", "true"),
phoenix: "true" === get("phoenix"),
notebooksDownBanner: "true" === get("notebooksDownBanner"),
enableThroughputCap: "true" === get("enablethroughputcap"),
};

View File

@@ -50,6 +50,7 @@ export enum Action {
SubscriptionSwitch,
TenantSwitch,
DefaultTenantSwitch,
ResetNotebookWorkspace,
CreateNotebookWorkspace,
NotebookErrorNotification,
CreateSparkCluster,
@@ -81,8 +82,6 @@ export enum Action {
NotebooksInsertTextCellBelowFromMenu,
NotebooksMoveCellUpFromMenu,
NotebooksMoveCellDownFromMenu,
PhoenixConnection,
PhoenixResetWorkspace,
DeleteCellFromMenu,
OpenTerminal,
CreateMongoCollectionWithWildcardIndex,

View File

@@ -10,7 +10,6 @@ import {
SortBy,
} from "../Explorer/Controls/NotebookGallery/GalleryViewerComponent";
import Explorer from "../Explorer/Explorer";
import { NotebookUtil } from "../Explorer/Notebook/NotebookUtil";
import { useNotebook } from "../Explorer/Notebook/useNotebook";
import { IGalleryItem, JunoClient } from "../Juno/JunoClient";
import { Action, ActionModifiers } from "../Shared/Telemetry/TelemetryConstants";
@@ -229,7 +228,7 @@ export function downloadItem(
undefined,
"Download",
async () => {
if (NotebookUtil.isPhoenixEnabled()) {
if (useNotebook.getState().isPhoenix) {
await container.allocateContainer();
}
const notebookServerInfo = useNotebook.getState().notebookServerInfo;

View File

@@ -1,9 +1,4 @@
// https://github.com/<owner>/<repo>/tree/<branch>
import { JunoEndpoints } from "Common/Constants";
import { configContext } from "ConfigContext";
import { userContext } from "UserContext";
// The url when users visit a repo/branch on github.com
export const RepoUriPattern = /https:\/\/github.com\/([^/]*)\/([^/]*)\/tree\/([^?]*)/;
@@ -65,15 +60,3 @@ export function toContentUri(owner: string, repo: string, branch: string, path:
export function toRawContentUri(owner: string, repo: string, branch: string, path: string): string {
return `https://raw.githubusercontent.com/${owner}/${repo}/${branch}/${path}`;
}
export function GetGithubClientId(): string {
const junoEndpoint = userContext.features.junoEndpoint ?? configContext.JUNO_ENDPOINT;
if (
junoEndpoint === JunoEndpoints.Test ||
junoEndpoint === JunoEndpoints.Test2 ||
junoEndpoint === JunoEndpoints.Test3
) {
return configContext.GITHUB_TEST_ENV_CLIENT_ID;
}
return configContext.GITHUB_CLIENT_ID;
}

View File

@@ -339,9 +339,6 @@ function updateContextsFromPortalMessage(inputs: DataExplorerInputsFrame) {
if (inputs.flights.indexOf(Flights.PKPartitionKeyTest) !== -1) {
userContext.features.partitionKeyDefault2 = true;
}
if (inputs.flights.indexOf(Flights.Phoenix) !== -1) {
userContext.features.phoenix = true;
}
if (inputs.flights.indexOf(Flights.NotebooksDownBanner) !== -1) {
userContext.features.notebooksDownBanner = true;
}