mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-22 02:11:29 +00:00
242 lines
7.9 KiB
TypeScript
242 lines
7.9 KiB
TypeScript
import { toJS } from "@nteract/commutable";
|
|
import { ImmutableNotebook } from "@nteract/commutable/src";
|
|
import ko from "knockout";
|
|
import * as React from "react";
|
|
import { ReactAdapter } from "../../Bindings/ReactBindingHandler";
|
|
import { HttpStatusCodes } from "../../Common/Constants";
|
|
import { getErrorMessage, getErrorStack, handleError } from "../../Common/ErrorHandlingUtils";
|
|
import { JunoClient } from "../../Juno/JunoClient";
|
|
import { Action } from "../../Shared/Telemetry/TelemetryConstants";
|
|
import { traceFailure, traceStart, traceSuccess } from "../../Shared/Telemetry/TelemetryProcessor";
|
|
import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils";
|
|
import { CodeOfConductComponent } from "../Controls/NotebookGallery/CodeOfConductComponent";
|
|
import { GalleryTab } from "../Controls/NotebookGallery/GalleryViewerComponent";
|
|
import Explorer from "../Explorer";
|
|
import * as FileSystemUtil from "../Notebook/FileSystemUtil";
|
|
import { PublishNotebookPaneComponent, PublishNotebookPaneProps } from "./PublishNotebookPaneComponent";
|
|
import { RightPaneForm, RightPaneFormProps } from "./RightPaneForm/RightPaneForm";
|
|
|
|
export class PublishNotebookPaneAdapter implements ReactAdapter {
|
|
parameters: ko.Observable<number>;
|
|
private isOpened: boolean;
|
|
private isExecuting: boolean;
|
|
private formError: string;
|
|
private formErrorDetail: string;
|
|
|
|
private name: string;
|
|
private author: string;
|
|
private content: string;
|
|
private description: string;
|
|
private tags: string;
|
|
private imageSrc: string;
|
|
private notebookObject: ImmutableNotebook;
|
|
private parentDomElement: HTMLElement;
|
|
private isCodeOfConductAccepted: boolean;
|
|
|
|
constructor(private container: Explorer, private junoClient: JunoClient) {
|
|
this.parameters = ko.observable(Date.now());
|
|
this.reset();
|
|
this.triggerRender();
|
|
}
|
|
|
|
public renderComponent(): JSX.Element {
|
|
if (!this.isOpened) {
|
|
return undefined;
|
|
}
|
|
|
|
const props: RightPaneFormProps = {
|
|
container: this.container,
|
|
formError: this.formError,
|
|
formErrorDetail: this.formErrorDetail,
|
|
id: "publishnotebookpane",
|
|
isExecuting: this.isExecuting,
|
|
title: "Publish to gallery",
|
|
submitButtonText: "Publish",
|
|
onClose: () => this.close(),
|
|
onSubmit: () => this.submit(),
|
|
isSubmitButtonHidden: !this.isCodeOfConductAccepted,
|
|
};
|
|
|
|
const publishNotebookPaneProps: PublishNotebookPaneProps = {
|
|
notebookName: this.name,
|
|
notebookDescription: "",
|
|
notebookTags: "",
|
|
notebookAuthor: this.author,
|
|
notebookCreatedDate: new Date().toISOString(),
|
|
notebookObject: this.notebookObject,
|
|
notebookParentDomElement: this.parentDomElement,
|
|
onChangeName: (newValue: string) => (this.name = newValue),
|
|
onChangeDescription: (newValue: string) => (this.description = newValue),
|
|
onChangeTags: (newValue: string) => (this.tags = newValue),
|
|
onChangeImageSrc: (newValue: string) => (this.imageSrc = newValue),
|
|
onError: this.createFormError,
|
|
clearFormError: this.clearFormError,
|
|
};
|
|
|
|
return (
|
|
<RightPaneForm {...props}>
|
|
{!this.isCodeOfConductAccepted ? (
|
|
<div style={{ padding: "15px", marginTop: "10px" }}>
|
|
<CodeOfConductComponent
|
|
junoClient={this.junoClient}
|
|
onAcceptCodeOfConduct={() => {
|
|
this.isCodeOfConductAccepted = true;
|
|
this.triggerRender();
|
|
}}
|
|
/>
|
|
</div>
|
|
) : (
|
|
<PublishNotebookPaneComponent {...publishNotebookPaneProps} />
|
|
)}
|
|
</RightPaneForm>
|
|
);
|
|
}
|
|
|
|
public triggerRender(): void {
|
|
window.requestAnimationFrame(() => this.parameters(Date.now()));
|
|
}
|
|
|
|
public async open(
|
|
name: string,
|
|
author: string,
|
|
notebookContent: string | ImmutableNotebook,
|
|
parentDomElement: HTMLElement
|
|
): Promise<void> {
|
|
try {
|
|
const response = await this.junoClient.isCodeOfConductAccepted();
|
|
if (response.status !== HttpStatusCodes.OK && response.status !== HttpStatusCodes.NoContent) {
|
|
throw new Error(`Received HTTP ${response.status} when accepting code of conduct`);
|
|
}
|
|
|
|
this.isCodeOfConductAccepted = response.data;
|
|
} catch (error) {
|
|
handleError(
|
|
error,
|
|
"PublishNotebookPaneAdapter/isCodeOfConductAccepted",
|
|
"Failed to check if code of conduct was accepted"
|
|
);
|
|
}
|
|
|
|
this.name = name;
|
|
this.author = author;
|
|
if (typeof notebookContent === "string") {
|
|
this.content = notebookContent as string;
|
|
} else {
|
|
this.content = JSON.stringify(toJS(notebookContent));
|
|
this.notebookObject = notebookContent;
|
|
}
|
|
this.parentDomElement = parentDomElement;
|
|
|
|
this.isOpened = true;
|
|
this.triggerRender();
|
|
}
|
|
|
|
public close(): void {
|
|
this.reset();
|
|
this.triggerRender();
|
|
}
|
|
|
|
public async submit(): Promise<void> {
|
|
const clearPublishingMessage = NotificationConsoleUtils.logConsoleProgress(`Publishing ${this.name} to gallery`);
|
|
this.isExecuting = true;
|
|
this.triggerRender();
|
|
|
|
let startKey: number;
|
|
|
|
if (!this.name || !this.description || !this.author || !this.imageSrc) {
|
|
const formError = `Failed to publish ${this.name} to gallery`;
|
|
const formErrorDetail = "Name, description, author and cover image are required";
|
|
this.createFormError(formError, formErrorDetail, "PublishNotebookPaneAdapter/submit");
|
|
this.isExecuting = false;
|
|
return;
|
|
}
|
|
|
|
try {
|
|
startKey = traceStart(Action.NotebooksGalleryPublish, {});
|
|
|
|
const response = await this.junoClient.publishNotebook(
|
|
this.name,
|
|
this.description,
|
|
this.tags?.split(","),
|
|
this.author,
|
|
this.imageSrc,
|
|
this.content
|
|
);
|
|
|
|
const data = response.data;
|
|
if (data) {
|
|
let isPublishPending = false;
|
|
|
|
if (data.pendingScanJobIds?.length > 0) {
|
|
isPublishPending = true;
|
|
NotificationConsoleUtils.logConsoleInfo(
|
|
`Content of ${this.name} is currently being scanned for illegal content. It will not be available in the public gallery until the review is complete (may take a few days).`
|
|
);
|
|
} else {
|
|
NotificationConsoleUtils.logConsoleInfo(`Published ${this.name} to gallery`);
|
|
this.container.openGallery(GalleryTab.Published);
|
|
}
|
|
|
|
traceSuccess(
|
|
Action.NotebooksGalleryPublish,
|
|
{
|
|
notebookId: data.id,
|
|
isPublishPending,
|
|
},
|
|
startKey
|
|
);
|
|
}
|
|
} catch (error) {
|
|
traceFailure(
|
|
Action.NotebooksGalleryPublish,
|
|
{
|
|
error: getErrorMessage(error),
|
|
errorStack: getErrorStack(error),
|
|
},
|
|
startKey
|
|
);
|
|
|
|
const errorMessage = getErrorMessage(error);
|
|
this.formError = `Failed to publish ${FileSystemUtil.stripExtension(this.name, "ipynb")} to gallery`;
|
|
this.formErrorDetail = `${errorMessage}`;
|
|
handleError(errorMessage, "PublishNotebookPaneAdapter/submit", this.formError);
|
|
return;
|
|
} finally {
|
|
clearPublishingMessage();
|
|
this.isExecuting = false;
|
|
this.triggerRender();
|
|
}
|
|
|
|
this.close();
|
|
}
|
|
|
|
private createFormError = (formError: string, formErrorDetail: string, area: string): void => {
|
|
this.formError = formError;
|
|
this.formErrorDetail = formErrorDetail;
|
|
handleError(formErrorDetail, area, formError);
|
|
this.triggerRender();
|
|
};
|
|
|
|
private clearFormError = (): void => {
|
|
this.formError = undefined;
|
|
this.formErrorDetail = undefined;
|
|
this.triggerRender();
|
|
};
|
|
|
|
private reset = (): void => {
|
|
this.isOpened = false;
|
|
this.isExecuting = false;
|
|
this.formError = undefined;
|
|
this.formErrorDetail = undefined;
|
|
this.name = undefined;
|
|
this.author = undefined;
|
|
this.content = undefined;
|
|
this.description = undefined;
|
|
this.tags = undefined;
|
|
this.imageSrc = undefined;
|
|
this.notebookObject = undefined;
|
|
this.parentDomElement = undefined;
|
|
this.isCodeOfConductAccepted = undefined;
|
|
};
|
|
}
|