mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-02-16 17:25:58 +00:00
More Spark UI Cleanup (#89)
Co-authored-by: Steve Faulkner <stfaul@microsoft.com>
This commit is contained in:
parent
99c6a7ebcc
commit
444e25c086
@ -138,7 +138,6 @@ src/Explorer/Panes/AddDatabasePane.test.ts
|
|||||||
src/Explorer/Panes/AddDatabasePane.ts
|
src/Explorer/Panes/AddDatabasePane.ts
|
||||||
src/Explorer/Panes/BrowseQueriesPane.ts
|
src/Explorer/Panes/BrowseQueriesPane.ts
|
||||||
src/Explorer/Panes/CassandraAddCollectionPane.ts
|
src/Explorer/Panes/CassandraAddCollectionPane.ts
|
||||||
src/Explorer/Panes/ClusterLibraryPane.ts
|
|
||||||
src/Explorer/Panes/ContextualPaneBase.ts
|
src/Explorer/Panes/ContextualPaneBase.ts
|
||||||
src/Explorer/Panes/DeleteCollectionConfirmationPane.test.ts
|
src/Explorer/Panes/DeleteCollectionConfirmationPane.test.ts
|
||||||
src/Explorer/Panes/DeleteCollectionConfirmationPane.ts
|
src/Explorer/Panes/DeleteCollectionConfirmationPane.ts
|
||||||
@ -146,7 +145,6 @@ src/Explorer/Panes/DeleteDatabaseConfirmationPane.test.ts
|
|||||||
src/Explorer/Panes/DeleteDatabaseConfirmationPane.ts
|
src/Explorer/Panes/DeleteDatabaseConfirmationPane.ts
|
||||||
src/Explorer/Panes/ExecuteSprocParamsPane.ts
|
src/Explorer/Panes/ExecuteSprocParamsPane.ts
|
||||||
src/Explorer/Panes/GraphStylingPane.ts
|
src/Explorer/Panes/GraphStylingPane.ts
|
||||||
src/Explorer/Panes/LibraryManagePane.ts
|
|
||||||
src/Explorer/Panes/LoadQueryPane.ts
|
src/Explorer/Panes/LoadQueryPane.ts
|
||||||
src/Explorer/Panes/NewVertexPane.ts
|
src/Explorer/Panes/NewVertexPane.ts
|
||||||
src/Explorer/Panes/PaneComponents.ts
|
src/Explorer/Panes/PaneComponents.ts
|
||||||
@ -332,10 +330,6 @@ src/Explorer/Controls/Directory/DirectoryListComponent.test.tsx
|
|||||||
src/Explorer/Controls/Directory/DirectoryListComponent.tsx
|
src/Explorer/Controls/Directory/DirectoryListComponent.tsx
|
||||||
src/Explorer/Controls/Editor/EditorReact.tsx
|
src/Explorer/Controls/Editor/EditorReact.tsx
|
||||||
src/Explorer/Controls/InputTypeahead/InputTypeaheadComponent.tsx
|
src/Explorer/Controls/InputTypeahead/InputTypeaheadComponent.tsx
|
||||||
src/Explorer/Controls/LibraryManagement/ClusterLibraryGrid.tsx
|
|
||||||
src/Explorer/Controls/LibraryManagement/ClusterLibraryGridAdapter.tsx
|
|
||||||
src/Explorer/Controls/LibraryManagement/LibraryManage.tsx
|
|
||||||
src/Explorer/Controls/LibraryManagement/LibraryManageComponentAdapter.tsx
|
|
||||||
src/Explorer/Controls/Notebook/NotebookTerminalComponent.test.tsx
|
src/Explorer/Controls/Notebook/NotebookTerminalComponent.test.tsx
|
||||||
src/Explorer/Controls/Notebook/NotebookTerminalComponent.tsx
|
src/Explorer/Controls/Notebook/NotebookTerminalComponent.tsx
|
||||||
src/Explorer/Controls/NotebookViewer/NotebookMetadataComponent.tsx
|
src/Explorer/Controls/NotebookViewer/NotebookMetadataComponent.tsx
|
||||||
|
@ -148,8 +148,6 @@ export interface Explorer {
|
|||||||
uploadFilePane: UploadFilePane;
|
uploadFilePane: UploadFilePane;
|
||||||
stringInputPane: StringInputPane;
|
stringInputPane: StringInputPane;
|
||||||
setupNotebooksPane: SetupNotebooksPane;
|
setupNotebooksPane: SetupNotebooksPane;
|
||||||
libraryManagePane: ContextualPane;
|
|
||||||
clusterLibraryPane: ContextualPane;
|
|
||||||
gitHubReposPane: ContextualPane;
|
gitHubReposPane: ContextualPane;
|
||||||
publishNotebookPaneAdapter: ReactAdapter;
|
publishNotebookPaneAdapter: ReactAdapter;
|
||||||
|
|
||||||
@ -230,11 +228,8 @@ export interface Explorer {
|
|||||||
openGallery: (notebookUrl?: string, galleryItem?: IGalleryItem, isFavorite?: boolean) => void;
|
openGallery: (notebookUrl?: string, galleryItem?: IGalleryItem, isFavorite?: boolean) => void;
|
||||||
openNotebookViewer: (notebookUrl: string) => void;
|
openNotebookViewer: (notebookUrl: string) => void;
|
||||||
notebookWorkspaceManager: NotebookWorkspaceManager;
|
notebookWorkspaceManager: NotebookWorkspaceManager;
|
||||||
sparkClusterManager: SparkClusterManager;
|
|
||||||
mostRecentActivity: MostRecentActivity;
|
mostRecentActivity: MostRecentActivity;
|
||||||
initNotebooks: (databaseAccount: DataModels.DatabaseAccount) => Promise<void>;
|
initNotebooks: (databaseAccount: DataModels.DatabaseAccount) => Promise<void>;
|
||||||
deleteCluster(): void;
|
|
||||||
openSparkMasterTab(): Promise<void>;
|
|
||||||
handleOpenFileAction(path: string): Promise<void>;
|
handleOpenFileAction(path: string): Promise<void>;
|
||||||
|
|
||||||
// Notebook operations
|
// Notebook operations
|
||||||
@ -275,26 +270,6 @@ export interface KernelConnectionMetadata {
|
|||||||
notebookConnectionInfo: DataModels.NotebookWorkspaceConnectionInfo;
|
notebookConnectionInfo: DataModels.NotebookWorkspaceConnectionInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SparkClusterManager {
|
|
||||||
getClustersAsync(cosmosAccountResourceId: string): Promise<DataModels.SparkCluster[]>;
|
|
||||||
getClusterAsync(cosmosAccountResourceId: string, clusterId: string): Promise<DataModels.SparkCluster>;
|
|
||||||
createClusterAsync(cosmosAccountResourceId: string, cluster: Partial<DataModels.SparkCluster>): Promise<void>;
|
|
||||||
updateClusterAsync(
|
|
||||||
cosmosAccountResourceId: string,
|
|
||||||
clusterId: string,
|
|
||||||
sparkCluster: DataModels.SparkCluster
|
|
||||||
): Promise<DataModels.SparkCluster>;
|
|
||||||
deleteClusterAsync(cosmosAccountResourceId: string, clusterId: string): Promise<void>;
|
|
||||||
getClusterConnectionInfoAsync(
|
|
||||||
cosmosAccountResourceId: string,
|
|
||||||
clusterId: string
|
|
||||||
): Promise<DataModels.SparkClusterConnectionInfo>;
|
|
||||||
getLibrariesAsync(cosmosdbResourceId: string): Promise<Library[]>;
|
|
||||||
getLibraryAsync(cosmosdbResourceId: string, libraryName: string): Promise<Library>;
|
|
||||||
addLibraryAsync(cosmosdbResourceId: string, libraryName: string, library: Library): Promise<void>;
|
|
||||||
deleteLibraryAsync(cosmosdbResourceId: string, libraryName: string): Promise<void>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ArcadiaResourceManager {
|
export interface ArcadiaResourceManager {
|
||||||
getWorkspacesAsync(arcadiaResourceId: string): Promise<DataModels.ArcadiaWorkspace[]>;
|
getWorkspacesAsync(arcadiaResourceId: string): Promise<DataModels.ArcadiaWorkspace[]>;
|
||||||
getWorkspaceAsync(arcadiaResourceId: string, workspaceId: string): Promise<DataModels.ArcadiaWorkspace>;
|
getWorkspaceAsync(arcadiaResourceId: string, workspaceId: string): Promise<DataModels.ArcadiaWorkspace>;
|
||||||
|
@ -123,12 +123,4 @@ describe("Component Registerer", () => {
|
|||||||
it("should register throughput-input component", () => {
|
it("should register throughput-input component", () => {
|
||||||
expect(ko.components.isRegistered("throughput-input")).toBe(true);
|
expect(ko.components.isRegistered("throughput-input")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should register library-manage-pane component", () => {
|
|
||||||
expect(ko.components.isRegistered("library-manage-pane")).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should register cluster-library-pane component", () => {
|
|
||||||
expect(ko.components.isRegistered("cluster-library-pane")).toBe(true);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
@ -78,6 +78,4 @@ ko.components.register("browse-queries-pane", new PaneComponents.BrowseQueriesPa
|
|||||||
ko.components.register("upload-file-pane", new PaneComponents.UploadFilePaneComponent());
|
ko.components.register("upload-file-pane", new PaneComponents.UploadFilePaneComponent());
|
||||||
ko.components.register("string-input-pane", new PaneComponents.StringInputPaneComponent());
|
ko.components.register("string-input-pane", new PaneComponents.StringInputPaneComponent());
|
||||||
ko.components.register("setup-notebooks-pane", new PaneComponents.SetupNotebooksPaneComponent());
|
ko.components.register("setup-notebooks-pane", new PaneComponents.SetupNotebooksPaneComponent());
|
||||||
ko.components.register("library-manage-pane", new PaneComponents.LibraryManagePaneComponent());
|
|
||||||
ko.components.register("cluster-library-pane", new PaneComponents.ClusterLibraryPaneComponent());
|
|
||||||
ko.components.register("github-repos-pane", new PaneComponents.GitHubReposPaneComponent());
|
ko.components.register("github-repos-pane", new PaneComponents.GitHubReposPaneComponent());
|
||||||
|
@ -1,40 +0,0 @@
|
|||||||
import * as React from "react";
|
|
||||||
import { DetailsList, IColumn, SelectionMode } from "office-ui-fabric-react/lib/DetailsList";
|
|
||||||
import { Library } from "../../../Contracts/DataModels";
|
|
||||||
|
|
||||||
export interface ClusterLibraryItem extends Library {
|
|
||||||
installed: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ClusterLibraryGridProps {
|
|
||||||
libraryItems: ClusterLibraryItem[];
|
|
||||||
onInstalledChanged: (libraryName: string, installed: boolean) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ClusterLibraryGrid(props: ClusterLibraryGridProps): JSX.Element {
|
|
||||||
const onInstalledChanged = (e: React.FormEvent<HTMLInputElement>) => {
|
|
||||||
const target = e.target;
|
|
||||||
const libraryName = (target as any).dataset.name;
|
|
||||||
const checked = (target as any).checked;
|
|
||||||
return props.onInstalledChanged(libraryName, checked);
|
|
||||||
};
|
|
||||||
|
|
||||||
const columns: IColumn[] = [
|
|
||||||
{
|
|
||||||
key: "name",
|
|
||||||
name: "Name",
|
|
||||||
fieldName: "name",
|
|
||||||
minWidth: 150
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "installed",
|
|
||||||
name: "Installed",
|
|
||||||
minWidth: 100,
|
|
||||||
onRender: (item: ClusterLibraryItem) => {
|
|
||||||
return <input type="checkbox" checked={item.installed} onChange={onInstalledChanged} data-name={item.name} />;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
return <DetailsList columns={columns} items={props.libraryItems} selectionMode={SelectionMode.none} />;
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
import * as React from "react";
|
|
||||||
import { ReactAdapter } from "../../../Bindings/ReactBindingHandler";
|
|
||||||
import { ClusterLibraryGrid, ClusterLibraryGridProps } from "./ClusterLibraryGrid";
|
|
||||||
|
|
||||||
export class ClusterLibraryGridAdapter implements ReactAdapter {
|
|
||||||
public parameters: ko.Observable<ClusterLibraryGridProps>;
|
|
||||||
|
|
||||||
public renderComponent(): JSX.Element {
|
|
||||||
return <ClusterLibraryGrid {...this.parameters()} />;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,156 +0,0 @@
|
|||||||
import * as React from "react";
|
|
||||||
import DeleteIcon from "../../../../images/delete.svg";
|
|
||||||
import { Button } from "office-ui-fabric-react/lib/Button";
|
|
||||||
import { DetailsList, IColumn, SelectionMode } from "office-ui-fabric-react/lib/DetailsList";
|
|
||||||
import { Library } from "../../../Contracts/DataModels";
|
|
||||||
import { Label } from "office-ui-fabric-react/lib/Label";
|
|
||||||
import { SparkLibrary } from "../../../Common/Constants";
|
|
||||||
import { TextField } from "office-ui-fabric-react/lib/TextField";
|
|
||||||
|
|
||||||
export interface LibraryManageComponentProps {
|
|
||||||
addProps: {
|
|
||||||
nameProps: LibraryAddNameTextFieldProps;
|
|
||||||
urlProps: LibraryAddUrlTextFieldProps;
|
|
||||||
buttonProps: LibraryAddButtonProps;
|
|
||||||
};
|
|
||||||
gridProps: LibraryManageGridProps;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function LibraryManageComponent(props: LibraryManageComponentProps): JSX.Element {
|
|
||||||
const {
|
|
||||||
addProps: { nameProps, urlProps, buttonProps },
|
|
||||||
gridProps
|
|
||||||
} = props;
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className="library-add-container">
|
|
||||||
<LibraryAddNameTextField {...nameProps} />
|
|
||||||
<LibraryAddUrlTextField {...urlProps} />
|
|
||||||
<LibraryAddButton {...buttonProps} />
|
|
||||||
</div>
|
|
||||||
<div className="library-grid-container">
|
|
||||||
<Label>All Libraries</Label>
|
|
||||||
<LibraryManageGrid {...gridProps} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LibraryManageGridProps {
|
|
||||||
items: Library[];
|
|
||||||
onLibraryDeleteClick: (libraryName: string) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
function LibraryManageGrid(props: LibraryManageGridProps): JSX.Element {
|
|
||||||
const columns: IColumn[] = [
|
|
||||||
{
|
|
||||||
key: "name",
|
|
||||||
name: "Name",
|
|
||||||
fieldName: "name",
|
|
||||||
minWidth: 200
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "delete",
|
|
||||||
name: "Delete",
|
|
||||||
minWidth: 60,
|
|
||||||
onRender: (item: Library) => {
|
|
||||||
const onDelete = () => {
|
|
||||||
props.onLibraryDeleteClick(item.name);
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<span className="library-delete">
|
|
||||||
<img src={DeleteIcon} alt="Delete" onClick={onDelete} />
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
];
|
|
||||||
return <DetailsList columns={columns} items={props.items} selectionMode={SelectionMode.none} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LibraryAddButtonProps {
|
|
||||||
disabled: boolean;
|
|
||||||
onLibraryAddClick: (event: React.FormEvent<any>) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
function LibraryAddButton(props: LibraryAddButtonProps): JSX.Element {
|
|
||||||
return (
|
|
||||||
<Button text="Add" className="library-add-button" onClick={props.onLibraryAddClick} disabled={props.disabled} />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LibraryAddUrlTextFieldProps {
|
|
||||||
libraryAddress: string;
|
|
||||||
onLibraryAddressChange: (libraryAddress: string) => void;
|
|
||||||
onLibraryAddressValidated: (errorMessage: string, value: string) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
function LibraryAddUrlTextField(props: LibraryAddUrlTextFieldProps): JSX.Element {
|
|
||||||
const handleTextChange = (e: React.FormEvent<any>, libraryAddress: string) => {
|
|
||||||
props.onLibraryAddressChange(libraryAddress);
|
|
||||||
};
|
|
||||||
const validateText = (text: string): string => {
|
|
||||||
if (!text) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
const libraryUrlRegex = /^(https:\/\/.+\/)(.+)\.(jar)$/gi;
|
|
||||||
const isValidUrl = libraryUrlRegex.test(text);
|
|
||||||
if (isValidUrl) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
return "Need to be a valid https uri";
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<TextField
|
|
||||||
value={props.libraryAddress}
|
|
||||||
label="Url"
|
|
||||||
type="url"
|
|
||||||
className="library-add-textfield"
|
|
||||||
onChange={handleTextChange}
|
|
||||||
onGetErrorMessage={validateText}
|
|
||||||
onNotifyValidationResult={props.onLibraryAddressValidated}
|
|
||||||
placeholder="https://myrepo/myjar.jar"
|
|
||||||
autoComplete="off"
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LibraryAddNameTextFieldProps {
|
|
||||||
libraryName: string;
|
|
||||||
onLibraryNameChange: (libraryName: string) => void;
|
|
||||||
onLibraryNameValidated: (errorMessage: string, value: string) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
function LibraryAddNameTextField(props: LibraryAddNameTextFieldProps): JSX.Element {
|
|
||||||
const handleTextChange = (e: React.FormEvent<any>, libraryName: string) => {
|
|
||||||
props.onLibraryNameChange(libraryName);
|
|
||||||
};
|
|
||||||
const validateText = (text: string): string => {
|
|
||||||
if (!text) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
const length = text.length;
|
|
||||||
if (length < SparkLibrary.nameMinLength || length > SparkLibrary.nameMaxLength) {
|
|
||||||
return "Library name length need to be between 3 and 63.";
|
|
||||||
}
|
|
||||||
const nameRegex = /^[a-z0-9][-a-z0-9]*[a-z0-9]$/gi;
|
|
||||||
const isValidUrl = nameRegex.test(text);
|
|
||||||
if (isValidUrl) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
return "Need to be a valid name. Letters, numbers and - are allowed";
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<TextField
|
|
||||||
value={props.libraryName}
|
|
||||||
label="Name"
|
|
||||||
type="text"
|
|
||||||
className="library-add-textfield"
|
|
||||||
onChange={handleTextChange}
|
|
||||||
onGetErrorMessage={validateText}
|
|
||||||
onNotifyValidationResult={props.onLibraryNameValidated}
|
|
||||||
placeholder="myjar"
|
|
||||||
autoComplete="off"
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
import * as React from "react";
|
|
||||||
import { ReactAdapter } from "../../../Bindings/ReactBindingHandler";
|
|
||||||
import { LibraryManageComponent, LibraryManageComponentProps } from "./LibraryManage";
|
|
||||||
|
|
||||||
export class LibraryManageComponentAdapter implements ReactAdapter {
|
|
||||||
public parameters: ko.Observable<LibraryManageComponentProps>;
|
|
||||||
|
|
||||||
public renderComponent(): JSX.Element {
|
|
||||||
return <LibraryManageComponent {...this.parameters()} />;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
@import "../../../../less/Common/Constants";
|
|
||||||
|
|
||||||
.labelWithRedAsterisk {
|
|
||||||
line-height: 18px;
|
|
||||||
font-size: @DefaultFontSize;
|
|
||||||
font-family: @DataExplorerFont;
|
|
||||||
color: @DefaultFontColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
.labelWithRedAsterisk::before {
|
|
||||||
content: "* ";
|
|
||||||
color: @SelectionHigh;
|
|
||||||
}
|
|
||||||
|
|
||||||
.clusterSettingsDropdown {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
@ -1,159 +0,0 @@
|
|||||||
import * as React from "react";
|
|
||||||
import { Dropdown, IDropdownOption, IDropdownProps } from "office-ui-fabric-react/lib/Dropdown";
|
|
||||||
import { Slider, ISliderProps } from "office-ui-fabric-react/lib/Slider";
|
|
||||||
import { Stack, IStackItemStyles, IStackStyles } from "office-ui-fabric-react/lib/Stack";
|
|
||||||
import { TextField, ITextFieldProps } from "office-ui-fabric-react/lib/TextField";
|
|
||||||
import { Spark } from "../../../Common/Constants";
|
|
||||||
import { SparkCluster } from "../../../Contracts/DataModels";
|
|
||||||
|
|
||||||
export interface ClusterSettingsComponentProps {
|
|
||||||
cluster: SparkCluster;
|
|
||||||
onClusterSettingsChanged: (cluster: SparkCluster) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ClusterSettingsComponent extends React.Component<ClusterSettingsComponentProps, {}> {
|
|
||||||
constructor(props: ClusterSettingsComponentProps) {
|
|
||||||
super(props);
|
|
||||||
}
|
|
||||||
|
|
||||||
public render(): JSX.Element {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{this.getMasterSizeDropdown()}
|
|
||||||
{this.getWorkerSizeDropdown()}
|
|
||||||
{this.getWorkerCountSliderInput()}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private getMasterSizeDropdown(): JSX.Element {
|
|
||||||
const driverSize: string =
|
|
||||||
this.props.cluster && this.props.cluster.properties && this.props.cluster.properties.driverSize;
|
|
||||||
const masterSizeOptions: IDropdownOption[] = Spark.SKUs.keys().map(sku => ({
|
|
||||||
key: sku,
|
|
||||||
text: Spark.SKUs.get(sku)
|
|
||||||
}));
|
|
||||||
const masterSizeDropdownProps: IDropdownProps = {
|
|
||||||
label: "Master Size",
|
|
||||||
options: masterSizeOptions,
|
|
||||||
defaultSelectedKey: driverSize,
|
|
||||||
onChange: this._onDriverSizeChange,
|
|
||||||
styles: {
|
|
||||||
root: "clusterSettingsDropdown"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return <Dropdown {...masterSizeDropdownProps} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
private getWorkerSizeDropdown(): JSX.Element {
|
|
||||||
const workerSize: string =
|
|
||||||
this.props.cluster && this.props.cluster.properties && this.props.cluster.properties.workerSize;
|
|
||||||
const workerSizeOptions: IDropdownOption[] = Spark.SKUs.keys().map(sku => ({
|
|
||||||
key: sku,
|
|
||||||
text: Spark.SKUs.get(sku)
|
|
||||||
}));
|
|
||||||
const workerSizeDropdownProps: IDropdownProps = {
|
|
||||||
label: "Worker Size",
|
|
||||||
options: workerSizeOptions,
|
|
||||||
defaultSelectedKey: workerSize,
|
|
||||||
onChange: this._onWorkerSizeChange,
|
|
||||||
styles: {
|
|
||||||
label: "labelWithRedAsterisk",
|
|
||||||
root: "clusterSettingsDropdown"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return <Dropdown {...workerSizeDropdownProps} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
private getWorkerCountSliderInput(): JSX.Element {
|
|
||||||
const workerCount: number =
|
|
||||||
(this.props.cluster &&
|
|
||||||
this.props.cluster.properties &&
|
|
||||||
this.props.cluster.properties.workerInstanceCount !== undefined &&
|
|
||||||
this.props.cluster.properties.workerInstanceCount) ||
|
|
||||||
0;
|
|
||||||
const stackStyle: IStackStyles = {
|
|
||||||
root: {
|
|
||||||
paddingTop: 5
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const sliderItemStyle: IStackItemStyles = {
|
|
||||||
root: {
|
|
||||||
width: "100%",
|
|
||||||
paddingRight: 20
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const workerCountSliderProps: ISliderProps = {
|
|
||||||
min: 0,
|
|
||||||
max: Spark.MaxWorkerCount,
|
|
||||||
step: 1,
|
|
||||||
value: workerCount,
|
|
||||||
showValue: false,
|
|
||||||
onChange: this._onWorkerCountChange,
|
|
||||||
styles: {
|
|
||||||
root: {
|
|
||||||
width: "100%",
|
|
||||||
paddingRight: 20
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const workerCountTextFieldProps: ITextFieldProps = {
|
|
||||||
value: workerCount.toString(),
|
|
||||||
styles: {
|
|
||||||
fieldGroup: {
|
|
||||||
width: 45,
|
|
||||||
height: 25
|
|
||||||
},
|
|
||||||
field: {
|
|
||||||
textAlign: "center"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onChange: this._onWorkerCountTextFieldChange
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Stack styles={stackStyle}>
|
|
||||||
<span className="labelWithRedAsterisk">Worker Nodes</span>
|
|
||||||
<Stack horizontal verticalAlign="center">
|
|
||||||
<Slider {...workerCountSliderProps} />
|
|
||||||
<TextField {...workerCountTextFieldProps} />
|
|
||||||
</Stack>
|
|
||||||
</Stack>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _onDriverSizeChange = (_event: React.FormEvent, selectedOption: IDropdownOption) => {
|
|
||||||
const newValue: string = selectedOption.key as string;
|
|
||||||
const cluster = this.props.cluster;
|
|
||||||
if (cluster) {
|
|
||||||
cluster.properties.driverSize = newValue;
|
|
||||||
this.props.onClusterSettingsChanged(cluster);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private _onWorkerSizeChange = (_event: React.FormEvent, selectedOption: IDropdownOption) => {
|
|
||||||
const newValue: string = selectedOption.key as string;
|
|
||||||
const cluster = this.props.cluster;
|
|
||||||
if (cluster) {
|
|
||||||
cluster.properties.workerSize = newValue;
|
|
||||||
this.props.onClusterSettingsChanged(cluster);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private _onWorkerCountChange = (count: number) => {
|
|
||||||
count = Math.min(count, Spark.MaxWorkerCount);
|
|
||||||
const cluster = this.props.cluster;
|
|
||||||
if (cluster) {
|
|
||||||
cluster.properties.workerInstanceCount = count;
|
|
||||||
this.props.onClusterSettingsChanged(cluster);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private _onWorkerCountTextFieldChange = (_event: React.FormEvent, newValue: string) => {
|
|
||||||
const count = parseInt(newValue);
|
|
||||||
if (!isNaN(count)) {
|
|
||||||
this._onWorkerCountChange(count);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
import * as React from "react";
|
|
||||||
import { ClusterSettingsComponent, ClusterSettingsComponentProps } from "./ClusterSettingsComponent";
|
|
||||||
import { ReactAdapter } from "../../../Bindings/ReactBindingHandler";
|
|
||||||
|
|
||||||
export class ClusterSettingsComponentAdapter implements ReactAdapter {
|
|
||||||
public parameters: ko.Observable<ClusterSettingsComponentProps>;
|
|
||||||
|
|
||||||
public renderComponent(): JSX.Element {
|
|
||||||
return <ClusterSettingsComponent {...this.parameters()} />;
|
|
||||||
}
|
|
||||||
}
|
|
@ -24,7 +24,6 @@ import NewVertexPane from "./Panes/NewVertexPane";
|
|||||||
import NotebookV2Tab from "./Tabs/NotebookV2Tab";
|
import NotebookV2Tab from "./Tabs/NotebookV2Tab";
|
||||||
import Q from "q";
|
import Q from "q";
|
||||||
import ResourceTokenCollection from "./Tree/ResourceTokenCollection";
|
import ResourceTokenCollection from "./Tree/ResourceTokenCollection";
|
||||||
import SparkMasterTab from "./Tabs/SparkMasterTab";
|
|
||||||
import TelemetryProcessor from "../Shared/Telemetry/TelemetryProcessor";
|
import TelemetryProcessor from "../Shared/Telemetry/TelemetryProcessor";
|
||||||
import TerminalTab from "./Tabs/TerminalTab";
|
import TerminalTab from "./Tabs/TerminalTab";
|
||||||
import { Action, ActionModifiers } from "../Shared/Telemetry/TelemetryConstants";
|
import { Action, ActionModifiers } from "../Shared/Telemetry/TelemetryConstants";
|
||||||
@ -36,7 +35,6 @@ import { BindingHandlersRegisterer } from "../Bindings/BindingHandlersRegisterer
|
|||||||
import { BrowseQueriesPane } from "./Panes/BrowseQueriesPane";
|
import { BrowseQueriesPane } from "./Panes/BrowseQueriesPane";
|
||||||
import { CassandraApi } from "../Api/Apis";
|
import { CassandraApi } from "../Api/Apis";
|
||||||
import { CassandraAPIDataClient, TableDataClient, TablesAPIDataClient } from "./Tables/TableDataClient";
|
import { CassandraAPIDataClient, TableDataClient, TablesAPIDataClient } from "./Tables/TableDataClient";
|
||||||
import { ClusterLibraryPane } from "./Panes/ClusterLibraryPane";
|
|
||||||
import { CommandBarComponentAdapter } from "./Menus/CommandBar/CommandBarComponentAdapter";
|
import { CommandBarComponentAdapter } from "./Menus/CommandBar/CommandBarComponentAdapter";
|
||||||
import { config } from "../Config";
|
import { config } from "../Config";
|
||||||
import { ConsoleData, ConsoleDataType } from "./Menus/NotificationConsole/NotificationConsoleComponent";
|
import { ConsoleData, ConsoleDataType } from "./Menus/NotificationConsole/NotificationConsoleComponent";
|
||||||
@ -52,7 +50,6 @@ import { FileSystemUtil } from "./Notebook/FileSystemUtil";
|
|||||||
import { handleOpenAction } from "./OpenActions";
|
import { handleOpenAction } from "./OpenActions";
|
||||||
import { isInvalidParentFrameOrigin } from "../Utils/MessageValidation";
|
import { isInvalidParentFrameOrigin } from "../Utils/MessageValidation";
|
||||||
import { IGalleryItem } from "../Juno/JunoClient";
|
import { IGalleryItem } from "../Juno/JunoClient";
|
||||||
import { LibraryManagePane } from "./Panes/LibraryManagePane";
|
|
||||||
import { LoadQueryPane } from "./Panes/LoadQueryPane";
|
import { LoadQueryPane } from "./Panes/LoadQueryPane";
|
||||||
import * as Logger from "../Common/Logger";
|
import * as Logger from "../Common/Logger";
|
||||||
import { MessageHandler } from "../Common/MessageHandler";
|
import { MessageHandler } from "../Common/MessageHandler";
|
||||||
@ -72,7 +69,6 @@ import { RouteHandler } from "../RouteHandlers/RouteHandler";
|
|||||||
import { SaveQueryPane } from "./Panes/SaveQueryPane";
|
import { SaveQueryPane } from "./Panes/SaveQueryPane";
|
||||||
import { SettingsPane } from "./Panes/SettingsPane";
|
import { SettingsPane } from "./Panes/SettingsPane";
|
||||||
import { SetupNotebooksPane } from "./Panes/SetupNotebooksPane";
|
import { SetupNotebooksPane } from "./Panes/SetupNotebooksPane";
|
||||||
import { SparkClusterManager } from "../SparkClusterManager/SparkClusterManager";
|
|
||||||
import { SplashScreenComponentAdapter } from "./SplashScreen/SplashScreenComponentApdapter";
|
import { SplashScreenComponentAdapter } from "./SplashScreen/SplashScreenComponentApdapter";
|
||||||
import { Splitter, SplitterBounds, SplitterDirection } from "../Common/Splitter";
|
import { Splitter, SplitterBounds, SplitterDirection } from "../Common/Splitter";
|
||||||
import { StringInputPane } from "./Panes/StringInputPane";
|
import { StringInputPane } from "./Panes/StringInputPane";
|
||||||
@ -190,8 +186,6 @@ export default class Explorer implements ViewModels.Explorer {
|
|||||||
public uploadFilePane: UploadFilePane;
|
public uploadFilePane: UploadFilePane;
|
||||||
public stringInputPane: StringInputPane;
|
public stringInputPane: StringInputPane;
|
||||||
public setupNotebooksPane: SetupNotebooksPane;
|
public setupNotebooksPane: SetupNotebooksPane;
|
||||||
public libraryManagePane: ViewModels.ContextualPane;
|
|
||||||
public clusterLibraryPane: ViewModels.ContextualPane;
|
|
||||||
public gitHubReposPane: ViewModels.ContextualPane;
|
public gitHubReposPane: ViewModels.ContextualPane;
|
||||||
public publishNotebookPaneAdapter: ReactAdapter;
|
public publishNotebookPaneAdapter: ReactAdapter;
|
||||||
|
|
||||||
@ -221,7 +215,6 @@ export default class Explorer implements ViewModels.Explorer {
|
|||||||
public isNotebooksEnabledForAccount: ko.Observable<boolean>;
|
public isNotebooksEnabledForAccount: ko.Observable<boolean>;
|
||||||
public notebookServerInfo: ko.Observable<DataModels.NotebookWorkspaceConnectionInfo>;
|
public notebookServerInfo: ko.Observable<DataModels.NotebookWorkspaceConnectionInfo>;
|
||||||
public notebookWorkspaceManager: ViewModels.NotebookWorkspaceManager;
|
public notebookWorkspaceManager: ViewModels.NotebookWorkspaceManager;
|
||||||
public sparkClusterManager: ViewModels.SparkClusterManager;
|
|
||||||
public sparkClusterConnectionInfo: ko.Observable<DataModels.SparkClusterConnectionInfo>;
|
public sparkClusterConnectionInfo: ko.Observable<DataModels.SparkClusterConnectionInfo>;
|
||||||
public isSparkEnabled: ko.Observable<boolean>;
|
public isSparkEnabled: ko.Observable<boolean>;
|
||||||
public isSparkEnabledForAccount: ko.Observable<boolean>;
|
public isSparkEnabledForAccount: ko.Observable<boolean>;
|
||||||
@ -313,7 +306,6 @@ export default class Explorer implements ViewModels.Explorer {
|
|||||||
this.isAuthWithResourceToken() ? this.refreshDatabaseForResourceToken() : this.refreshAllDatabases(true);
|
this.isAuthWithResourceToken() ? this.refreshDatabaseForResourceToken() : this.refreshAllDatabases(true);
|
||||||
RouteHandler.getInstance().initHandler();
|
RouteHandler.getInstance().initHandler();
|
||||||
this.notebookWorkspaceManager = new NotebookWorkspaceManager(this.armEndpoint());
|
this.notebookWorkspaceManager = new NotebookWorkspaceManager(this.armEndpoint());
|
||||||
this.sparkClusterManager = new SparkClusterManager(this.armEndpoint());
|
|
||||||
this.arcadiaWorkspaces = ko.observableArray();
|
this.arcadiaWorkspaces = ko.observableArray();
|
||||||
this._arcadiaManager = new ArcadiaResourceManager(this.armEndpoint());
|
this._arcadiaManager = new ArcadiaResourceManager(this.armEndpoint());
|
||||||
this._isAfecFeatureRegistered(Constants.AfecFeatures.StorageAnalytics).then(isRegistered =>
|
this._isAfecFeatureRegistered(Constants.AfecFeatures.StorageAnalytics).then(isRegistered =>
|
||||||
@ -750,22 +742,6 @@ export default class Explorer implements ViewModels.Explorer {
|
|||||||
container: this
|
container: this
|
||||||
});
|
});
|
||||||
|
|
||||||
this.libraryManagePane = new LibraryManagePane({
|
|
||||||
documentClientUtility: this.documentClientUtility,
|
|
||||||
id: "libraryManagePane",
|
|
||||||
visible: ko.observable<boolean>(false),
|
|
||||||
|
|
||||||
container: this
|
|
||||||
});
|
|
||||||
|
|
||||||
this.clusterLibraryPane = new ClusterLibraryPane({
|
|
||||||
documentClientUtility: this.documentClientUtility,
|
|
||||||
id: "clusterLibraryPane",
|
|
||||||
visible: ko.observable<boolean>(false),
|
|
||||||
|
|
||||||
container: this
|
|
||||||
});
|
|
||||||
|
|
||||||
this.tabsManager = new TabsManager();
|
this.tabsManager = new TabsManager();
|
||||||
|
|
||||||
this._panes = [
|
this._panes = [
|
||||||
@ -1607,70 +1583,6 @@ export default class Explorer implements ViewModels.Explorer {
|
|||||||
window.open(Constants.Urls.feedbackEmail, "_self");
|
window.open(Constants.Urls.feedbackEmail, "_self");
|
||||||
};
|
};
|
||||||
|
|
||||||
public async initSparkConnectionInfo(databaseAccount: DataModels.DatabaseAccount) {
|
|
||||||
if (!databaseAccount) {
|
|
||||||
throw new Error("No database account specified");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._isInitializingSparkConnectionInfo) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this._isInitializingSparkConnectionInfo = true;
|
|
||||||
|
|
||||||
let connectionInfo: DataModels.SparkClusterConnectionInfo;
|
|
||||||
try {
|
|
||||||
connectionInfo = await this.sparkClusterManager.getClusterConnectionInfoAsync(databaseAccount.id, "default");
|
|
||||||
} catch (error) {
|
|
||||||
this._isInitializingSparkConnectionInfo = false;
|
|
||||||
Logger.logError(error, "initSparkConnectionInfo/getClusterConnectionInfoAsync");
|
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
|
||||||
ConsoleDataType.Error,
|
|
||||||
`Failed to get cluster connection info: ${JSON.stringify(error)}`
|
|
||||||
);
|
|
||||||
throw error;
|
|
||||||
} finally {
|
|
||||||
// Overwrite with feature flags
|
|
||||||
if (this.isFeatureEnabled(Constants.Features.livyEndpoint)) {
|
|
||||||
connectionInfo = {
|
|
||||||
userName: undefined,
|
|
||||||
password: undefined,
|
|
||||||
endpoints: [
|
|
||||||
{
|
|
||||||
kind: DataModels.SparkClusterEndpointKind.Livy,
|
|
||||||
endpoint: this.features()[Constants.Features.livyEndpoint]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.sparkClusterConnectionInfo(connectionInfo);
|
|
||||||
this.sparkClusterConnectionInfo.valueHasMutated();
|
|
||||||
this._isInitializingSparkConnectionInfo = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public deleteCluster() {
|
|
||||||
if (!this.isSparkEnabled() || !this.sparkClusterManager) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const deleteClusterDialogProps: DialogProps = {
|
|
||||||
isModal: true,
|
|
||||||
visible: true,
|
|
||||||
title: "Delete Cluster",
|
|
||||||
subText:
|
|
||||||
"This will delete the default cluster associated with this account and interrupt any scheduled jobs. Proceed anyway?",
|
|
||||||
primaryButtonText: "OK",
|
|
||||||
secondaryButtonText: "Cancel",
|
|
||||||
onPrimaryButtonClick: async () => {
|
|
||||||
this._closeModalDialog();
|
|
||||||
await this._deleteCluster();
|
|
||||||
},
|
|
||||||
onSecondaryButtonClick: this._closeModalDialog
|
|
||||||
};
|
|
||||||
this._dialogProps(deleteClusterDialogProps);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async getArcadiaToken(): Promise<string> {
|
public async getArcadiaToken(): Promise<string> {
|
||||||
return new Promise<string>((resolve: (token: string) => void, reject: (error: any) => void) => {
|
return new Promise<string>((resolve: (token: string) => void, reject: (error: any) => void) => {
|
||||||
MessageHandler.sendCachedDataMessage<string>(MessageTypes.GetArcadiaToken, undefined /** params **/).then(
|
MessageHandler.sendCachedDataMessage<string>(MessageTypes.GetArcadiaToken, undefined /** params **/).then(
|
||||||
@ -1821,53 +1733,6 @@ export default class Explorer implements ViewModels.Explorer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _deleteCluster = async () => {
|
|
||||||
const startKey: number = TelemetryProcessor.traceStart(Action.DeleteSparkCluster, {
|
|
||||||
databaseAccountName: this.databaseAccount() && this.databaseAccount().name,
|
|
||||||
defaultExperience: this.defaultExperience(),
|
|
||||||
dataExplorerArea: Constants.Areas.ResourceTree
|
|
||||||
});
|
|
||||||
const id = NotificationConsoleUtils.logConsoleMessage(
|
|
||||||
ConsoleDataType.InProgress,
|
|
||||||
"Deleting the default spark cluster associated with this account"
|
|
||||||
);
|
|
||||||
try {
|
|
||||||
await this.sparkClusterManager.deleteClusterAsync(this.databaseAccount().id, "default");
|
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
|
||||||
ConsoleDataType.Info,
|
|
||||||
"Successfully deleted the default spark cluster associated with this account"
|
|
||||||
);
|
|
||||||
TelemetryProcessor.traceSuccess(
|
|
||||||
Action.DeleteSparkCluster,
|
|
||||||
{
|
|
||||||
databaseAccountName: this.databaseAccount() && this.databaseAccount().name,
|
|
||||||
defaultExperience: this.defaultExperience(),
|
|
||||||
dataExplorerArea: Constants.Areas.ResourceTree
|
|
||||||
},
|
|
||||||
startKey
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
const errorMessage = JSON.stringify(error);
|
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
|
||||||
ConsoleDataType.Error,
|
|
||||||
`Failed to delete default spark cluster: ${errorMessage}`
|
|
||||||
);
|
|
||||||
TelemetryProcessor.traceFailure(
|
|
||||||
Action.DeleteSparkCluster,
|
|
||||||
{
|
|
||||||
databaseAccountName: this.databaseAccount() && this.databaseAccount().name,
|
|
||||||
defaultExperience: this.defaultExperience(),
|
|
||||||
dataExplorerArea: Constants.Areas.ResourceTree,
|
|
||||||
error,
|
|
||||||
errorMessage
|
|
||||||
},
|
|
||||||
startKey
|
|
||||||
);
|
|
||||||
} finally {
|
|
||||||
NotificationConsoleUtils.clearInProgressMessageWithId(id);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private _resetNotebookWorkspace = async () => {
|
private _resetNotebookWorkspace = async () => {
|
||||||
this._closeModalDialog();
|
this._closeModalDialog();
|
||||||
const id = NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.InProgress, "Resetting notebook workspace");
|
const id = NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.InProgress, "Resetting notebook workspace");
|
||||||
@ -2835,42 +2700,6 @@ export default class Explorer implements ViewModels.Explorer {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public async openSparkMasterTab() {
|
|
||||||
if (!this.sparkClusterConnectionInfo()) {
|
|
||||||
await this.initSparkConnectionInfo(this.databaseAccount());
|
|
||||||
}
|
|
||||||
|
|
||||||
const sparkMasterTabs: SparkMasterTab[] = this.tabsManager.getTabs(
|
|
||||||
ViewModels.CollectionTabKind.SparkMasterTab
|
|
||||||
) as SparkMasterTab[];
|
|
||||||
let sparkMasterTab: SparkMasterTab = sparkMasterTabs && sparkMasterTabs[0];
|
|
||||||
|
|
||||||
if (sparkMasterTab) {
|
|
||||||
this.tabsManager.activateTab(sparkMasterTab);
|
|
||||||
} else {
|
|
||||||
sparkMasterTab = new SparkMasterTab({
|
|
||||||
clusterConnectionInfo: this.sparkClusterConnectionInfo(),
|
|
||||||
tabKind: ViewModels.CollectionTabKind.SparkMasterTab,
|
|
||||||
node: null,
|
|
||||||
title: "Apache Spark",
|
|
||||||
tabPath: "",
|
|
||||||
documentClientUtility: null,
|
|
||||||
|
|
||||||
collection: null,
|
|
||||||
selfLink: null,
|
|
||||||
hashLocation: "sparkmaster",
|
|
||||||
isActive: ko.observable(false),
|
|
||||||
isTabsContentExpanded: ko.observable(true),
|
|
||||||
onLoadStartKey: null,
|
|
||||||
onUpdateTabsButtons: this.onUpdateTabsButtons,
|
|
||||||
container: this
|
|
||||||
});
|
|
||||||
|
|
||||||
this.tabsManager.activateNewTab(sparkMasterTab);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private refreshNotebookList = async (): Promise<void> => {
|
private refreshNotebookList = async (): Promise<void> => {
|
||||||
if (!this.isNotebookEnabled() || !this.notebookManager?.notebookContentClient) {
|
if (!this.isNotebookEnabled() || !this.notebookManager?.notebookContentClient) {
|
||||||
return;
|
return;
|
||||||
|
@ -108,11 +108,8 @@ export class ExplorerStub implements ViewModels.Explorer {
|
|||||||
public isSparkEnabledForAccount: ko.Observable<boolean>;
|
public isSparkEnabledForAccount: ko.Observable<boolean>;
|
||||||
public arcadiaToken: ko.Observable<string>;
|
public arcadiaToken: ko.Observable<string>;
|
||||||
public notebookWorkspaceManager: ViewModels.NotebookWorkspaceManager;
|
public notebookWorkspaceManager: ViewModels.NotebookWorkspaceManager;
|
||||||
public sparkClusterManager: ViewModels.SparkClusterManager;
|
|
||||||
public notebookServerInfo: ko.Observable<DataModels.NotebookWorkspaceConnectionInfo>;
|
public notebookServerInfo: ko.Observable<DataModels.NotebookWorkspaceConnectionInfo>;
|
||||||
public sparkClusterConnectionInfo: ko.Observable<DataModels.SparkClusterConnectionInfo>;
|
public sparkClusterConnectionInfo: ko.Observable<DataModels.SparkClusterConnectionInfo>;
|
||||||
public libraryManagePane: ViewModels.ContextualPane;
|
|
||||||
public clusterLibraryPane: ViewModels.ContextualPane;
|
|
||||||
public gitHubReposPane: ViewModels.ContextualPane;
|
public gitHubReposPane: ViewModels.ContextualPane;
|
||||||
public publishNotebookPaneAdapter: ReactAdapter;
|
public publishNotebookPaneAdapter: ReactAdapter;
|
||||||
public arcadiaWorkspaces: ko.ObservableArray<ArcadiaWorkspaceItem>;
|
public arcadiaWorkspaces: ko.ObservableArray<ArcadiaWorkspaceItem>;
|
||||||
@ -405,14 +402,6 @@ export class ExplorerStub implements ViewModels.Explorer {
|
|||||||
throw new Error("Not implemented");
|
throw new Error("Not implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
public deleteCluster(): void {
|
|
||||||
throw new Error("Not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
public async openSparkMasterTab(): Promise<void> {
|
|
||||||
throw new Error("Not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
public createNotebookContentItemFile(name: string, filepath: string): NotebookContentItem {
|
public createNotebookContentItemFile(name: string, filepath: string): NotebookContentItem {
|
||||||
throw new Error("Not implemented");
|
throw new Error("Not implemented");
|
||||||
}
|
}
|
||||||
|
@ -1,59 +0,0 @@
|
|||||||
<div data-bind="visible: visible, event: { keydown: onPaneKeyDown }">
|
|
||||||
<div class="contextual-pane-out" data-bind="click: cancel, clickBubble: false"></div>
|
|
||||||
<div class="contextual-pane" id="clusterLibraryPane">
|
|
||||||
<!-- Cluster Library -- Start -->
|
|
||||||
<div class="contextual-pane-in">
|
|
||||||
<form class="paneContentContainer" data-bind="submit: submit">
|
|
||||||
<!-- Cluster Library header - Start -->
|
|
||||||
<div class="firstdivbg headerline">
|
|
||||||
<span data-bind="text: title"></span>
|
|
||||||
<div
|
|
||||||
class="closeImg"
|
|
||||||
role="button"
|
|
||||||
aria-label="Close pane"
|
|
||||||
tabindex="0"
|
|
||||||
data-bind="click: cancel, event: { keypress: onCloseKeyPress }"
|
|
||||||
>
|
|
||||||
<img src="../../../images/close-black.svg" title="Close" alt="Close" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- Cluster Library header - End -->
|
|
||||||
|
|
||||||
<!-- Cluster Library errors - Start -->
|
|
||||||
<div
|
|
||||||
class="warningErrorContainer"
|
|
||||||
aria-live="assertive"
|
|
||||||
data-bind="visible: formErrors() && formErrors() !== ''"
|
|
||||||
>
|
|
||||||
<div class="warningErrorContent">
|
|
||||||
<span><img class="paneErrorIcon" src="/error_red.svg" alt="Error"/></span>
|
|
||||||
<span class="warningErrorDetailsLinkContainer">
|
|
||||||
<span class="formErrors" data-bind="text: formErrors, attr: { title: formErrors }"></span>
|
|
||||||
<a
|
|
||||||
class="errorLink"
|
|
||||||
role="link"
|
|
||||||
data-bind="visible: formErrorsDetails() && formErrorsDetails() !== '', click: showErrorDetails"
|
|
||||||
>More details</a
|
|
||||||
>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- Cluster Library errors - End -->
|
|
||||||
|
|
||||||
<!-- Cluster Library inputs - Start -->
|
|
||||||
<div class="paneMainContent"><div data-bind="react: clusterLibraryGridAdapter"></div></div>
|
|
||||||
<!-- Cluster Library inputs - End -->
|
|
||||||
|
|
||||||
<div class="paneFooter">
|
|
||||||
<div class="leftpanel-okbut"><input type="submit" value="Save" class="btncreatecoll1" /></div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<!-- Cluster Library - End -->
|
|
||||||
<!-- Loader - Start -->
|
|
||||||
<div class="dataExplorerLoaderContainer dataExplorerPaneLoaderContainer" data-bind="visible: isExecuting">
|
|
||||||
<img class="dataExplorerLoader" src="/LoadingIndicator_3Squares.gif" />
|
|
||||||
</div>
|
|
||||||
<!-- Loader - End -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
@ -1,237 +0,0 @@
|
|||||||
import _ from "underscore";
|
|
||||||
import * as ko from "knockout";
|
|
||||||
import * as Constants from "../../Common/Constants";
|
|
||||||
import * as ViewModels from "../../Contracts/ViewModels";
|
|
||||||
import * as ErrorParserUtility from "../../Common/ErrorParserUtility";
|
|
||||||
import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
|
||||||
import { Action } from "../../Shared/Telemetry/TelemetryConstants";
|
|
||||||
import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent";
|
|
||||||
import { ContextualPaneBase } from "./ContextualPaneBase";
|
|
||||||
import { ClusterLibraryGridAdapter } from "../Controls/LibraryManagement/ClusterLibraryGridAdapter";
|
|
||||||
import { ClusterLibraryGridProps, ClusterLibraryItem } from "../Controls/LibraryManagement/ClusterLibraryGrid";
|
|
||||||
import { Library, SparkCluster, SparkClusterLibrary } from "../../Contracts/DataModels";
|
|
||||||
import * as Logger from "../../Common/Logger";
|
|
||||||
import { NotificationConsoleUtils } from "../../Utils/NotificationConsoleUtils";
|
|
||||||
|
|
||||||
export class ClusterLibraryPane extends ContextualPaneBase {
|
|
||||||
public clusterLibraryGridAdapter: ClusterLibraryGridAdapter;
|
|
||||||
|
|
||||||
private _clusterLibraryProps: ko.Observable<ClusterLibraryGridProps>;
|
|
||||||
private _originalCluster: SparkCluster;
|
|
||||||
|
|
||||||
constructor(options: ViewModels.PaneOptions) {
|
|
||||||
super(options);
|
|
||||||
this.title("Cluster Libraries");
|
|
||||||
|
|
||||||
this._clusterLibraryProps = ko.observable<ClusterLibraryGridProps>({
|
|
||||||
libraryItems: [],
|
|
||||||
onInstalledChanged: this._onInstalledChanged
|
|
||||||
});
|
|
||||||
this.clusterLibraryGridAdapter = new ClusterLibraryGridAdapter();
|
|
||||||
this.clusterLibraryGridAdapter.parameters = this._clusterLibraryProps;
|
|
||||||
|
|
||||||
this.resetData();
|
|
||||||
}
|
|
||||||
|
|
||||||
public open(): void {
|
|
||||||
const resourceId: string = this.container.databaseAccount() && this.container.databaseAccount().id;
|
|
||||||
Promise.all([this._getLibraries(resourceId), this._getDefaultCluster(resourceId)]).then(
|
|
||||||
result => {
|
|
||||||
const [libraries, cluster] = result;
|
|
||||||
this._originalCluster = cluster;
|
|
||||||
const libraryItems = this._mapClusterLibraries(cluster, libraries);
|
|
||||||
this._updateClusterLibraryGridStates({ libraryItems });
|
|
||||||
},
|
|
||||||
reason => {
|
|
||||||
const parsedError = ErrorParserUtility.parse(reason);
|
|
||||||
this.formErrors(parsedError[0].message);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
super.open();
|
|
||||||
}
|
|
||||||
|
|
||||||
public submit(): void {
|
|
||||||
const resourceId: string = this.container.databaseAccount() && this.container.databaseAccount().id;
|
|
||||||
this.isExecuting(true);
|
|
||||||
if (this._areLibrariesChanged()) {
|
|
||||||
const newLibraries = this._clusterLibraryProps()
|
|
||||||
.libraryItems.filter(lib => lib.installed)
|
|
||||||
.map(lib => ({ name: lib.name }));
|
|
||||||
this._updateClusterLibraries(resourceId, this._originalCluster, newLibraries).then(
|
|
||||||
() => {
|
|
||||||
this.isExecuting(false);
|
|
||||||
this.close();
|
|
||||||
},
|
|
||||||
reason => {
|
|
||||||
this.isExecuting(false);
|
|
||||||
const parsedError = ErrorParserUtility.parse(reason);
|
|
||||||
this.formErrors(parsedError[0].message);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
this.isExecuting(false);
|
|
||||||
this.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _updateClusterLibraryGridStates(states: Partial<ClusterLibraryGridProps>): void {
|
|
||||||
const merged = { ...this._clusterLibraryProps(), ...states };
|
|
||||||
this._clusterLibraryProps(merged);
|
|
||||||
this._clusterLibraryProps.valueHasMutated();
|
|
||||||
}
|
|
||||||
|
|
||||||
private _onInstalledChanged = (libraryName: string, installed: boolean): void => {
|
|
||||||
const items = this._clusterLibraryProps().libraryItems;
|
|
||||||
const library = _.find(items, item => item.name === libraryName);
|
|
||||||
library.installed = installed;
|
|
||||||
this._clusterLibraryProps.valueHasMutated();
|
|
||||||
};
|
|
||||||
|
|
||||||
private _areLibrariesChanged(): boolean {
|
|
||||||
const original = this._originalCluster.properties && this._originalCluster.properties.libraries;
|
|
||||||
const changed = this._clusterLibraryProps()
|
|
||||||
.libraryItems.filter(lib => lib.installed)
|
|
||||||
.map(lib => lib.name);
|
|
||||||
if (original.length !== changed.length) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
const newLibraries = new Set(changed);
|
|
||||||
for (let o of original) {
|
|
||||||
if (!newLibraries.has(o.name)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
newLibraries.delete(o.name);
|
|
||||||
}
|
|
||||||
return newLibraries.size === 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _mapClusterLibraries(cluster: SparkCluster, libraries: Library[]): ClusterLibraryItem[] {
|
|
||||||
const clusterLibraries = cluster && cluster.properties && cluster.properties.libraries;
|
|
||||||
const libraryItems = libraries.map(lib => ({
|
|
||||||
...lib,
|
|
||||||
installed: clusterLibraries.some(clusterLib => clusterLib.name === lib.name)
|
|
||||||
}));
|
|
||||||
return libraryItems;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _getLibraries(resourceId: string): Promise<Library[]> {
|
|
||||||
if (!resourceId) {
|
|
||||||
return Promise.reject("invalid inputs");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.container.sparkClusterManager) {
|
|
||||||
return Promise.reject("cluster client is not initialized yet");
|
|
||||||
}
|
|
||||||
|
|
||||||
const inProgressId = NotificationConsoleUtils.logConsoleMessage(
|
|
||||||
ConsoleDataType.InProgress,
|
|
||||||
`Fetching libraries...`
|
|
||||||
);
|
|
||||||
try {
|
|
||||||
return await this.container.sparkClusterManager.getLibrariesAsync(resourceId);
|
|
||||||
} catch (e) {
|
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
|
||||||
ConsoleDataType.Error,
|
|
||||||
`Failed to fetch libraries. Reason: ${JSON.stringify(e)}`
|
|
||||||
);
|
|
||||||
Logger.logError(e, "Explorer/_getLibraries");
|
|
||||||
throw e;
|
|
||||||
} finally {
|
|
||||||
NotificationConsoleUtils.clearInProgressMessageWithId(inProgressId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _getDefaultCluster(resourceId: string, clusterId: string = "default"): Promise<SparkCluster> {
|
|
||||||
if (!resourceId) {
|
|
||||||
return Promise.reject("invalid inputs");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.container.sparkClusterManager) {
|
|
||||||
return Promise.reject("cluster client is not initialized yet");
|
|
||||||
}
|
|
||||||
|
|
||||||
const inProgressId = NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.InProgress, `Fetching cluster...`);
|
|
||||||
try {
|
|
||||||
const cluster = await this.container.sparkClusterManager.getClusterAsync(resourceId, clusterId);
|
|
||||||
return cluster;
|
|
||||||
} catch (e) {
|
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
|
||||||
ConsoleDataType.Error,
|
|
||||||
`Failed to fetch cluster. Reason: ${JSON.stringify(e)}`
|
|
||||||
);
|
|
||||||
Logger.logError(e, "Explorer/_getCluster");
|
|
||||||
throw e;
|
|
||||||
} finally {
|
|
||||||
NotificationConsoleUtils.clearInProgressMessageWithId(inProgressId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _updateClusterLibraries(
|
|
||||||
resourceId: string,
|
|
||||||
originalCluster: SparkCluster,
|
|
||||||
newLibrarys: SparkClusterLibrary[]
|
|
||||||
): Promise<void> {
|
|
||||||
if (!originalCluster || !resourceId) {
|
|
||||||
return Promise.reject("Invalid inputs");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.container.sparkClusterManager) {
|
|
||||||
return Promise.reject("Cluster client is not initialized yet");
|
|
||||||
}
|
|
||||||
|
|
||||||
TelemetryProcessor.traceStart(Action.ClusterLibraryManage, {
|
|
||||||
resourceId,
|
|
||||||
defaultExperience: this.container.defaultExperience(),
|
|
||||||
dataExplorerArea: Constants.Areas.ContextualPane,
|
|
||||||
paneTitle: this.title(),
|
|
||||||
area: "ClusterLibraryPane/_updateClusterLibraries",
|
|
||||||
originalCluster,
|
|
||||||
newLibrarys
|
|
||||||
});
|
|
||||||
|
|
||||||
let newCluster = originalCluster;
|
|
||||||
newCluster.properties.libraries = newLibrarys;
|
|
||||||
|
|
||||||
const consoleId = NotificationConsoleUtils.logConsoleMessage(
|
|
||||||
ConsoleDataType.InProgress,
|
|
||||||
`Updating ${newCluster.name} libraries...`
|
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const cluster = await this.container.sparkClusterManager.updateClusterAsync(
|
|
||||||
resourceId,
|
|
||||||
originalCluster.name,
|
|
||||||
newCluster
|
|
||||||
);
|
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
|
||||||
ConsoleDataType.Info,
|
|
||||||
`Successfully updated ${newCluster.name} libraries.`
|
|
||||||
);
|
|
||||||
TelemetryProcessor.traceSuccess(Action.ClusterLibraryManage, {
|
|
||||||
resourceId,
|
|
||||||
defaultExperience: this.container.defaultExperience(),
|
|
||||||
dataExplorerArea: Constants.Areas.ContextualPane,
|
|
||||||
paneTitle: this.title(),
|
|
||||||
area: "ClusterLibraryPane/_updateClusterLibraries",
|
|
||||||
cluster
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
|
||||||
ConsoleDataType.Error,
|
|
||||||
`Failed to upload ${newCluster.name} libraries. Reason: ${JSON.stringify(e)}`
|
|
||||||
);
|
|
||||||
TelemetryProcessor.traceFailure(Action.ClusterLibraryManage, {
|
|
||||||
databaseAccountName: this.container.databaseAccount().name,
|
|
||||||
defaultExperience: this.container.defaultExperience(),
|
|
||||||
dataExplorerArea: Constants.Areas.ContextualPane,
|
|
||||||
paneTitle: this.title(),
|
|
||||||
area: "ClusterLibraryPane/_updateClusterLibraries",
|
|
||||||
error: e
|
|
||||||
});
|
|
||||||
Logger.logError(e, "Explorer/_updateClusterLibraries");
|
|
||||||
throw e;
|
|
||||||
} finally {
|
|
||||||
NotificationConsoleUtils.clearInProgressMessageWithId(consoleId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,55 +0,0 @@
|
|||||||
<div data-bind="visible: visible, event: { keydown: onPaneKeyDown }">
|
|
||||||
<div class="contextual-pane-out" data-bind="click: cancel, clickBubble: false"></div>
|
|
||||||
<div class="contextual-pane" id="libraryManagePane">
|
|
||||||
<!-- Library Manage -- Start -->
|
|
||||||
<div class="contextual-pane-in">
|
|
||||||
<form class="paneContentContainer" data-bind="submit: submit">
|
|
||||||
<!-- Library Manage header - Start -->
|
|
||||||
<div class="firstdivbg headerline">
|
|
||||||
<span data-bind="text: title"></span>
|
|
||||||
<div
|
|
||||||
class="closeImg"
|
|
||||||
role="button"
|
|
||||||
aria-label="Close pane"
|
|
||||||
tabindex="0"
|
|
||||||
data-bind="click: cancel, event: { keypress: onCloseKeyPress }"
|
|
||||||
>
|
|
||||||
<img src="../../../images/close-black.svg" title="Close" alt="Close" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- Library Manage header - End -->
|
|
||||||
|
|
||||||
<!-- Library Manage errors - Start -->
|
|
||||||
<div
|
|
||||||
class="warningErrorContainer"
|
|
||||||
aria-live="assertive"
|
|
||||||
data-bind="visible: formErrors() && formErrors() !== ''"
|
|
||||||
>
|
|
||||||
<div class="warningErrorContent">
|
|
||||||
<span><img class="paneErrorIcon" src="/error_red.svg" alt="Error"/></span>
|
|
||||||
<span class="warningErrorDetailsLinkContainer">
|
|
||||||
<span class="formErrors" data-bind="text: formErrors, attr: { title: formErrors }"></span>
|
|
||||||
<a
|
|
||||||
class="errorLink"
|
|
||||||
role="link"
|
|
||||||
data-bind="visible: formErrorsDetails() && formErrorsDetails() !== '', click: showErrorDetails"
|
|
||||||
>More details</a
|
|
||||||
>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- Library Manage errors - End -->
|
|
||||||
|
|
||||||
<!-- Library Manage inputs - Start -->
|
|
||||||
<div class="paneMainContent"><div data-bind="react: libraryManageComponentAdapter"></div></div>
|
|
||||||
<!-- Library Manage inputs - End -->
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<!-- Library Manage - End -->
|
|
||||||
<!-- Loader - Start -->
|
|
||||||
<div class="dataExplorerLoaderContainer dataExplorerPaneLoaderContainer" data-bind="visible: isExecuting">
|
|
||||||
<img class="dataExplorerLoader" src="/LoadingIndicator_3Squares.gif" />
|
|
||||||
</div>
|
|
||||||
<!-- Loader - End -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
@ -1,372 +0,0 @@
|
|||||||
import * as ko from "knockout";
|
|
||||||
import * as Constants from "../../Common/Constants";
|
|
||||||
import * as ViewModels from "../../Contracts/ViewModels";
|
|
||||||
import * as ErrorParserUtility from "../../Common/ErrorParserUtility";
|
|
||||||
import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
|
||||||
import { Action } from "../../Shared/Telemetry/TelemetryConstants";
|
|
||||||
import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent";
|
|
||||||
import { ContextualPaneBase } from "./ContextualPaneBase";
|
|
||||||
import { LibraryManageComponentAdapter } from "../Controls/LibraryManagement/LibraryManageComponentAdapter";
|
|
||||||
import {
|
|
||||||
LibraryManageComponentProps,
|
|
||||||
LibraryAddNameTextFieldProps,
|
|
||||||
LibraryAddUrlTextFieldProps,
|
|
||||||
LibraryAddButtonProps,
|
|
||||||
LibraryManageGridProps
|
|
||||||
} from "../Controls/LibraryManagement/LibraryManage";
|
|
||||||
import { Library } from "../../Contracts/DataModels";
|
|
||||||
import * as Logger from "../../Common/Logger";
|
|
||||||
import { NotificationConsoleUtils } from "../../Utils/NotificationConsoleUtils";
|
|
||||||
|
|
||||||
export class LibraryManagePane extends ContextualPaneBase {
|
|
||||||
public libraryManageComponentAdapter: LibraryManageComponentAdapter;
|
|
||||||
|
|
||||||
private _libraryManageProps: ko.Observable<LibraryManageComponentProps>;
|
|
||||||
private _libraryManageStates: { isNameValid: boolean; isUrlValid: boolean };
|
|
||||||
|
|
||||||
constructor(options: ViewModels.PaneOptions) {
|
|
||||||
super(options);
|
|
||||||
this.title("Libraries");
|
|
||||||
|
|
||||||
this._libraryManageStates = {
|
|
||||||
isNameValid: true,
|
|
||||||
isUrlValid: true
|
|
||||||
};
|
|
||||||
this._libraryManageProps = ko.observable<LibraryManageComponentProps>({
|
|
||||||
addProps: {
|
|
||||||
nameProps: {
|
|
||||||
libraryName: "",
|
|
||||||
onLibraryNameChange: this._onLibraryNameChange,
|
|
||||||
onLibraryNameValidated: this._onLibraryNameValidated
|
|
||||||
},
|
|
||||||
urlProps: {
|
|
||||||
libraryAddress: "",
|
|
||||||
onLibraryAddressChange: this._onLibraryAddressChange,
|
|
||||||
onLibraryAddressValidated: this._onLibraryAddressValidated
|
|
||||||
},
|
|
||||||
buttonProps: {
|
|
||||||
disabled: false,
|
|
||||||
onLibraryAddClick: this._onLibraryAddClick
|
|
||||||
}
|
|
||||||
},
|
|
||||||
gridProps: {
|
|
||||||
items: [],
|
|
||||||
onLibraryDeleteClick: this._onLibraryDeleteClick
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.libraryManageComponentAdapter = new LibraryManageComponentAdapter();
|
|
||||||
this.libraryManageComponentAdapter.parameters = this._libraryManageProps;
|
|
||||||
|
|
||||||
this.resetData();
|
|
||||||
}
|
|
||||||
|
|
||||||
public open(): void {
|
|
||||||
const resourceId: string = this.container.databaseAccount() && this.container.databaseAccount().id;
|
|
||||||
this._getLibraries(resourceId).then(
|
|
||||||
(libraries: Library[]) => {
|
|
||||||
this._updateLibraryManageComponentProps(null, null, null, {
|
|
||||||
items: libraries
|
|
||||||
});
|
|
||||||
},
|
|
||||||
reason => {
|
|
||||||
const parsedError = ErrorParserUtility.parse(reason);
|
|
||||||
this.formErrors(parsedError[0].message);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
super.open();
|
|
||||||
}
|
|
||||||
|
|
||||||
public submit(): void {
|
|
||||||
// override default behavior because this is not a form
|
|
||||||
}
|
|
||||||
|
|
||||||
private _updateLibraryManageComponentProps(
|
|
||||||
newNameProps?: Partial<LibraryAddNameTextFieldProps>,
|
|
||||||
newUrlProps?: Partial<LibraryAddUrlTextFieldProps>,
|
|
||||||
newButtonProps?: Partial<LibraryAddButtonProps>,
|
|
||||||
newGridProps?: Partial<LibraryManageGridProps>
|
|
||||||
): void {
|
|
||||||
let {
|
|
||||||
addProps: { buttonProps, nameProps, urlProps },
|
|
||||||
gridProps
|
|
||||||
} = this._libraryManageProps();
|
|
||||||
if (newNameProps) {
|
|
||||||
nameProps = { ...nameProps, ...newNameProps };
|
|
||||||
}
|
|
||||||
if (newUrlProps) {
|
|
||||||
urlProps = { ...urlProps, ...newUrlProps };
|
|
||||||
}
|
|
||||||
if (newButtonProps) {
|
|
||||||
buttonProps = { ...buttonProps, ...newButtonProps };
|
|
||||||
}
|
|
||||||
if (newGridProps) {
|
|
||||||
gridProps = { ...gridProps, ...newGridProps };
|
|
||||||
}
|
|
||||||
this._libraryManageProps({
|
|
||||||
addProps: {
|
|
||||||
nameProps,
|
|
||||||
urlProps,
|
|
||||||
buttonProps
|
|
||||||
},
|
|
||||||
gridProps
|
|
||||||
});
|
|
||||||
this._libraryManageProps.valueHasMutated();
|
|
||||||
}
|
|
||||||
|
|
||||||
private _onLibraryNameChange = (libraryName: string): void => {
|
|
||||||
this._updateLibraryManageComponentProps({ libraryName });
|
|
||||||
};
|
|
||||||
|
|
||||||
private _onLibraryNameValidated = (errorMessage: string): void => {
|
|
||||||
this._libraryManageStates.isNameValid = !errorMessage;
|
|
||||||
this._validateAddButton();
|
|
||||||
};
|
|
||||||
|
|
||||||
private _onLibraryAddressChange = (libraryAddress: string): void => {
|
|
||||||
this._updateLibraryManageComponentProps(null, {
|
|
||||||
libraryAddress
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!this._libraryManageProps().addProps.nameProps.libraryName) {
|
|
||||||
const parsedLibraryAddress = this._parseLibraryUrl(libraryAddress);
|
|
||||||
if (!parsedLibraryAddress) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let libraryName = this._sanitizeLibraryName(parsedLibraryAddress[2]);
|
|
||||||
this._updateLibraryManageComponentProps({ libraryName });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private _sanitizeLibraryName = (libraryName: string): string => {
|
|
||||||
const invalidCharRegex = /[^a-zA-Z0-9-]/gm;
|
|
||||||
return libraryName
|
|
||||||
.replace(invalidCharRegex, "-")
|
|
||||||
.substring(0, Math.min(Constants.SparkLibrary.nameMaxLength, libraryName.length));
|
|
||||||
};
|
|
||||||
|
|
||||||
private _onLibraryAddressValidated = (errorMessage: string): void => {
|
|
||||||
this._libraryManageStates.isUrlValid = !errorMessage;
|
|
||||||
this._validateAddButton();
|
|
||||||
};
|
|
||||||
|
|
||||||
private _validateAddButton = (): void => {
|
|
||||||
const isValid = this._libraryManageStates.isNameValid && this._libraryManageStates.isUrlValid;
|
|
||||||
const isUploadDisabled = this._libraryManageProps().addProps.buttonProps.disabled;
|
|
||||||
if (isValid === isUploadDisabled) {
|
|
||||||
this._updateLibraryManageComponentProps(null, null, {
|
|
||||||
disabled: !isUploadDisabled
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private _onLibraryDeleteClick = (libraryName: string): void => {
|
|
||||||
const resourceId: string = this.container.databaseAccount() && this.container.databaseAccount().id;
|
|
||||||
this.isExecuting(true);
|
|
||||||
this._deleteLibrary(resourceId, libraryName).then(
|
|
||||||
() => {
|
|
||||||
this.isExecuting(false);
|
|
||||||
const items = this._libraryManageProps().gridProps.items.filter(lib => lib.name !== libraryName);
|
|
||||||
this._updateLibraryManageComponentProps(null, null, null, {
|
|
||||||
items
|
|
||||||
});
|
|
||||||
},
|
|
||||||
reason => {
|
|
||||||
this.isExecuting(false);
|
|
||||||
const parsedError = ErrorParserUtility.parse(reason);
|
|
||||||
this.formErrors(parsedError[0].message);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
private _onLibraryAddClick = (): void => {
|
|
||||||
const libraryAddress = this._libraryManageProps().addProps.urlProps.libraryAddress;
|
|
||||||
if (!libraryAddress) {
|
|
||||||
this.formErrors("Library Url cannot be null");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const libraryName = this._libraryManageProps().addProps.nameProps.libraryName || this._generateLibraryName();
|
|
||||||
if (!libraryName) {
|
|
||||||
this.formErrors("Library Name cannot be null");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const parsedLibraryAddress = this._parseLibraryUrl(libraryAddress);
|
|
||||||
if (!parsedLibraryAddress) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const library: Library = {
|
|
||||||
name: libraryName,
|
|
||||||
properties: {
|
|
||||||
kind: "Jar",
|
|
||||||
source: {
|
|
||||||
kind: "HttpsUri",
|
|
||||||
libraryFileName: `${libraryName}.${parsedLibraryAddress[3]}`,
|
|
||||||
uri: libraryAddress
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const resourceId: string = this.container.databaseAccount() && this.container.databaseAccount().id;
|
|
||||||
|
|
||||||
this.isExecuting(true);
|
|
||||||
this._updateLibraryManageComponentProps(null, null, { disabled: true });
|
|
||||||
this._addLibrary(resourceId, library).then(
|
|
||||||
() => {
|
|
||||||
this.isExecuting(false);
|
|
||||||
this._updateLibraryManageComponentProps(
|
|
||||||
{
|
|
||||||
libraryName: ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
libraryAddress: ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
disabled: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
items: [...this._libraryManageProps().gridProps.items, library]
|
|
||||||
}
|
|
||||||
);
|
|
||||||
},
|
|
||||||
reason => {
|
|
||||||
this.isExecuting(false);
|
|
||||||
const parsedError = ErrorParserUtility.parse(reason);
|
|
||||||
this.formErrors(parsedError[0].message);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
private _parseLibraryUrl = (url: string): RegExpExecArray => {
|
|
||||||
const libraryUrlRegex = /^(https:\/\/.+\/)(.+)\.(jar)$/gi;
|
|
||||||
return libraryUrlRegex.exec(url);
|
|
||||||
};
|
|
||||||
|
|
||||||
private _generateLibraryName = (): string => {
|
|
||||||
return `library-${Math.random()
|
|
||||||
.toString(32)
|
|
||||||
.substring(2)}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
private async _getLibraries(resourceId: string): Promise<Library[]> {
|
|
||||||
if (!resourceId) {
|
|
||||||
return Promise.reject("Invalid inputs");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.container.sparkClusterManager) {
|
|
||||||
return Promise.reject("Cluster client is not initialized yet");
|
|
||||||
}
|
|
||||||
|
|
||||||
const inProgressId = NotificationConsoleUtils.logConsoleMessage(
|
|
||||||
ConsoleDataType.InProgress,
|
|
||||||
`Fetching libraries...`
|
|
||||||
);
|
|
||||||
try {
|
|
||||||
const libraries = await this.container.sparkClusterManager.getLibrariesAsync(resourceId);
|
|
||||||
return libraries;
|
|
||||||
} catch (e) {
|
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
|
||||||
ConsoleDataType.Error,
|
|
||||||
`Failed to fetch libraries. Reason: ${JSON.stringify(e)}`
|
|
||||||
);
|
|
||||||
Logger.logError(e, "Explorer/_getLibraries");
|
|
||||||
throw e;
|
|
||||||
} finally {
|
|
||||||
NotificationConsoleUtils.clearInProgressMessageWithId(inProgressId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _addLibrary(resourceId: string, library: Library): Promise<void> {
|
|
||||||
if (!library || !resourceId) {
|
|
||||||
return Promise.reject("invalid inputs");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.container.sparkClusterManager) {
|
|
||||||
return Promise.reject("cluster client is not initialized yet");
|
|
||||||
}
|
|
||||||
|
|
||||||
TelemetryProcessor.traceStart(Action.LibraryManage, {
|
|
||||||
resourceId,
|
|
||||||
defaultExperience: this.container.defaultExperience(),
|
|
||||||
dataExplorerArea: Constants.Areas.ContextualPane,
|
|
||||||
paneTitle: this.title(),
|
|
||||||
area: "LibraryManagePane/_deleteLibrary",
|
|
||||||
libraryName: library.name
|
|
||||||
});
|
|
||||||
|
|
||||||
const libraryName = library.name;
|
|
||||||
const inProgressId = NotificationConsoleUtils.logConsoleMessage(
|
|
||||||
ConsoleDataType.InProgress,
|
|
||||||
`Uploading ${libraryName}...`
|
|
||||||
);
|
|
||||||
try {
|
|
||||||
await this.container.sparkClusterManager.addLibraryAsync(resourceId, libraryName, library);
|
|
||||||
TelemetryProcessor.traceSuccess(Action.LibraryManage, {
|
|
||||||
resourceId,
|
|
||||||
area: "LibraryManagePane/_deleteLibrary",
|
|
||||||
libraryName: library.name
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
|
||||||
ConsoleDataType.Error,
|
|
||||||
`Failed to upload ${libraryName}. Reason: ${JSON.stringify(e)}`
|
|
||||||
);
|
|
||||||
TelemetryProcessor.traceFailure(Action.LibraryManage, {
|
|
||||||
resourceId,
|
|
||||||
area: "LibraryManagePane/_deleteLibrary",
|
|
||||||
libraryName: library.name,
|
|
||||||
error: e
|
|
||||||
});
|
|
||||||
Logger.logError(e, "Explorer/_uploadLibrary");
|
|
||||||
throw e;
|
|
||||||
} finally {
|
|
||||||
NotificationConsoleUtils.clearInProgressMessageWithId(inProgressId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _deleteLibrary(resourceId: string, libraryName: string): Promise<void> {
|
|
||||||
if (!libraryName || !resourceId) {
|
|
||||||
return Promise.reject("invalid inputs");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.container.sparkClusterManager) {
|
|
||||||
return Promise.reject("cluster client is not initialized yet");
|
|
||||||
}
|
|
||||||
|
|
||||||
TelemetryProcessor.traceStart(Action.LibraryManage, {
|
|
||||||
resourceId,
|
|
||||||
defaultExperience: this.container.defaultExperience(),
|
|
||||||
dataExplorerArea: Constants.Areas.ContextualPane,
|
|
||||||
paneTitle: this.title(),
|
|
||||||
area: "LibraryManagePane/_deleteLibrary",
|
|
||||||
libraryName
|
|
||||||
});
|
|
||||||
const inProgressId = NotificationConsoleUtils.logConsoleMessage(
|
|
||||||
ConsoleDataType.InProgress,
|
|
||||||
`Deleting ${libraryName}...`
|
|
||||||
);
|
|
||||||
try {
|
|
||||||
await this.container.sparkClusterManager.deleteLibraryAsync(resourceId, libraryName);
|
|
||||||
TelemetryProcessor.traceSuccess(Action.LibraryManage, {
|
|
||||||
resourceId,
|
|
||||||
area: "LibraryManagePane/_deleteLibrary",
|
|
||||||
libraryName
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
|
||||||
ConsoleDataType.Error,
|
|
||||||
`Failed to delete ${libraryName}. Reason: ${JSON.stringify(e)}`
|
|
||||||
);
|
|
||||||
TelemetryProcessor.traceFailure(Action.LibraryManage, {
|
|
||||||
resourceId,
|
|
||||||
area: "LibraryManagePane/_deleteLibrary",
|
|
||||||
libraryName,
|
|
||||||
error: e
|
|
||||||
});
|
|
||||||
Logger.logError(e, "Explorer/_deleteLibrary");
|
|
||||||
throw e;
|
|
||||||
} finally {
|
|
||||||
NotificationConsoleUtils.clearInProgressMessageWithId(inProgressId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -19,8 +19,6 @@ import BrowseQueriesPaneTemplate from "./BrowseQueriesPane.html";
|
|||||||
import UploadFilePaneTemplate from "./UploadFilePane.html";
|
import UploadFilePaneTemplate from "./UploadFilePane.html";
|
||||||
import StringInputPaneTemplate from "./StringInputPane.html";
|
import StringInputPaneTemplate from "./StringInputPane.html";
|
||||||
import SetupNotebooksPaneTemplate from "./SetupNotebooksPane.html";
|
import SetupNotebooksPaneTemplate from "./SetupNotebooksPane.html";
|
||||||
import LibraryManagePaneTemplate from "./LibraryManagePane.html";
|
|
||||||
import ClusterLibraryPaneTemplate from "./ClusterLibraryPane.html";
|
|
||||||
import GitHubReposPaneTemplate from "./GitHubReposPane.html";
|
import GitHubReposPaneTemplate from "./GitHubReposPane.html";
|
||||||
|
|
||||||
export class PaneComponent {
|
export class PaneComponent {
|
||||||
@ -218,24 +216,6 @@ export class SetupNotebooksPaneComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class LibraryManagePaneComponent {
|
|
||||||
constructor() {
|
|
||||||
return {
|
|
||||||
viewModel: PaneComponent,
|
|
||||||
template: LibraryManagePaneTemplate
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ClusterLibraryPaneComponent {
|
|
||||||
constructor() {
|
|
||||||
return {
|
|
||||||
viewModel: PaneComponent,
|
|
||||||
template: ClusterLibraryPaneTemplate
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class GitHubReposPaneComponent {
|
export class GitHubReposPaneComponent {
|
||||||
constructor() {
|
constructor() {
|
||||||
return {
|
return {
|
||||||
|
@ -32,7 +32,6 @@ import "./Explorer/Controls/TreeComponent/treeComponent.less";
|
|||||||
import "./Explorer/Controls/Accordion/AccordionComponent.less";
|
import "./Explorer/Controls/Accordion/AccordionComponent.less";
|
||||||
import "./Explorer/SplashScreen/SplashScreenComponent.less";
|
import "./Explorer/SplashScreen/SplashScreenComponent.less";
|
||||||
import "./Explorer/Controls/Notebook/NotebookTerminalComponent.less";
|
import "./Explorer/Controls/Notebook/NotebookTerminalComponent.less";
|
||||||
import "./Explorer/Controls/Spark/ClusterSettingsComponent.less";
|
|
||||||
|
|
||||||
// Image Dependencies
|
// Image Dependencies
|
||||||
import "../images/CosmosDB_rgb_ui_lighttheme.ico";
|
import "../images/CosmosDB_rgb_ui_lighttheme.ico";
|
||||||
|
@ -1,137 +0,0 @@
|
|||||||
import * as ViewModels from "../Contracts/ViewModels";
|
|
||||||
import { ArmApiVersions } from "../Common/Constants";
|
|
||||||
import { IResourceProviderClient, IResourceProviderClientFactory } from "../ResourceProvider/IResourceProviderClient";
|
|
||||||
import * as Logger from "../Common/Logger";
|
|
||||||
import { ResourceProviderClientFactory } from "../ResourceProvider/ResourceProviderClientFactory";
|
|
||||||
import {
|
|
||||||
SparkCluster,
|
|
||||||
SparkClusterConnectionInfo,
|
|
||||||
SparkClusterFeedResponse,
|
|
||||||
Library,
|
|
||||||
LibraryFeedResponse
|
|
||||||
} from "../Contracts/DataModels";
|
|
||||||
|
|
||||||
export class SparkClusterManager implements ViewModels.SparkClusterManager {
|
|
||||||
private resourceProviderClientFactory: IResourceProviderClientFactory<any>;
|
|
||||||
|
|
||||||
constructor(private armEndpoint: string) {
|
|
||||||
this.resourceProviderClientFactory = new ResourceProviderClientFactory(this.armEndpoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async getClustersAsync(cosmosdbResourceId: string): Promise<SparkCluster[]> {
|
|
||||||
const uri = `${cosmosdbResourceId}/clusters`;
|
|
||||||
try {
|
|
||||||
const response = (await this.rpClient(uri).getAsync(uri, ArmApiVersions.documentDB)) as SparkClusterFeedResponse;
|
|
||||||
return response && response.value;
|
|
||||||
} catch (error) {
|
|
||||||
Logger.logError(error, "SparkClusterManager/getClustersAsync");
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async getClusterAsync(cosmosdbResourceId: string, clusterId: string): Promise<SparkCluster> {
|
|
||||||
const uri = `${cosmosdbResourceId}/clusters/${clusterId}`;
|
|
||||||
try {
|
|
||||||
return (await this.rpClient(uri).getAsync(uri, ArmApiVersions.documentDB)) as SparkCluster;
|
|
||||||
} catch (error) {
|
|
||||||
Logger.logError(error, "SparkClusterManager/getClusterAsync");
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async createClusterAsync(cosmosdbResourceId: string, cluster: Partial<SparkCluster>): Promise<void> {
|
|
||||||
if (!cluster || !cluster.name) {
|
|
||||||
throw new Error("Invalid or incomplete spark cluster properties specified");
|
|
||||||
}
|
|
||||||
|
|
||||||
const uri = `${cosmosdbResourceId}/clusters/${cluster.name}`;
|
|
||||||
try {
|
|
||||||
await this.rpClient(uri).putAsync(uri, ArmApiVersions.documentDB, cluster);
|
|
||||||
} catch (error) {
|
|
||||||
Logger.logError(error, "SparkClusterManager/createClusterAsync");
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async updateClusterAsync(
|
|
||||||
cosmosdbResourceId: string,
|
|
||||||
clusterId: string,
|
|
||||||
cluster: SparkCluster
|
|
||||||
): Promise<SparkCluster> {
|
|
||||||
const uri = `${cosmosdbResourceId}/clusters/${clusterId}`;
|
|
||||||
try {
|
|
||||||
return await this.rpClient<SparkCluster>(uri).putAsync(uri, ArmApiVersions.documentDB, cluster);
|
|
||||||
} catch (error) {
|
|
||||||
Logger.logError(error, "SparkClusterManager/updateClusterAsync");
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async deleteClusterAsync(cosmosdbResourceId: string, clusterId: string): Promise<void> {
|
|
||||||
const uri = `${cosmosdbResourceId}/clusters/${clusterId}`;
|
|
||||||
try {
|
|
||||||
await this.rpClient(uri).deleteAsync(uri, ArmApiVersions.documentDB);
|
|
||||||
} catch (error) {
|
|
||||||
Logger.logError(error, "SparkClusterManager/deleteClusterAsync");
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async getClusterConnectionInfoAsync(
|
|
||||||
cosmosdbResourceId: string,
|
|
||||||
clusterId: string
|
|
||||||
): Promise<SparkClusterConnectionInfo> {
|
|
||||||
const uri = `${cosmosdbResourceId}/clusters/${clusterId}/getConnectionInfo`;
|
|
||||||
try {
|
|
||||||
return await this.rpClient<SparkClusterConnectionInfo>(uri).postAsync(uri, ArmApiVersions.documentDB, undefined);
|
|
||||||
} catch (error) {
|
|
||||||
Logger.logError(error, "SparkClusterManager/getClusterConnectionInfoAsync");
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async getLibrariesAsync(cosmosdbResourceId: string): Promise<Library[]> {
|
|
||||||
const uri = `${cosmosdbResourceId}/libraries`;
|
|
||||||
try {
|
|
||||||
const response = (await this.rpClient(uri).getAsync(uri, ArmApiVersions.documentDB)) as LibraryFeedResponse;
|
|
||||||
return response && response.value;
|
|
||||||
} catch (error) {
|
|
||||||
Logger.logError(error, "SparkClusterManager/getLibrariesAsync");
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async getLibraryAsync(cosmosdbResourceId: string, libraryName: string): Promise<Library> {
|
|
||||||
const uri = `${cosmosdbResourceId}/libraries/${libraryName}`;
|
|
||||||
try {
|
|
||||||
return (await this.rpClient(uri).getAsync(uri, ArmApiVersions.documentDB)) as Library;
|
|
||||||
} catch (error) {
|
|
||||||
Logger.logError(error, "SparkClusterManager/getLibraryAsync");
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async addLibraryAsync(cosmosdbResourceId: string, libraryName: string, library: Library): Promise<void> {
|
|
||||||
const uri = `${cosmosdbResourceId}/libraries/${encodeURIComponent(libraryName)}`;
|
|
||||||
try {
|
|
||||||
await this.rpClient(uri).putAsync(uri, ArmApiVersions.documentDB, library);
|
|
||||||
} catch (error) {
|
|
||||||
Logger.logError(error, "SparkClusterManager/putLibraryAsync");
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async deleteLibraryAsync(cosmosdbResourceId: string, libraryName: string): Promise<void> {
|
|
||||||
const uri = `${cosmosdbResourceId}/libraries/${libraryName}`;
|
|
||||||
try {
|
|
||||||
await this.rpClient(uri).deleteAsync(uri, ArmApiVersions.documentDB);
|
|
||||||
} catch (error) {
|
|
||||||
Logger.logError(error, "SparkClusterManager/deleteLibraryAsync");
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private rpClient<TResource>(uri: string): IResourceProviderClient<TResource> {
|
|
||||||
return this.resourceProviderClientFactory.getOrCreate(uri);
|
|
||||||
}
|
|
||||||
}
|
|
@ -287,8 +287,6 @@
|
|||||||
<upload-file-pane params="{data: uploadFilePane}"></upload-file-pane>
|
<upload-file-pane params="{data: uploadFilePane}"></upload-file-pane>
|
||||||
<string-input-pane params="{data: stringInputPane}"></string-input-pane>
|
<string-input-pane params="{data: stringInputPane}"></string-input-pane>
|
||||||
<setup-notebooks-pane params="{data: setupNotebooksPane}"></setup-notebooks-pane>
|
<setup-notebooks-pane params="{data: setupNotebooksPane}"></setup-notebooks-pane>
|
||||||
<library-manage-pane params="{data: libraryManagePane}"></library-manage-pane>
|
|
||||||
<cluster-library-pane params="{data: clusterLibraryPane}"></cluster-library-pane>
|
|
||||||
|
|
||||||
<!-- ko if: isGitHubPaneEnabled -->
|
<!-- ko if: isGitHubPaneEnabled -->
|
||||||
<github-repos-pane params="{data: gitHubReposPane}"></github-repos-pane>
|
<github-repos-pane params="{data: gitHubReposPane}"></github-repos-pane>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user