Files
cosmos-explorer/src/Explorer/Notebook/SchemaAnalyzer/SchemaAnalyzer.tsx
Laurent Nguyen 90c1439d34 Update prettier to latest. Remove tslint (#1641)
* Rev up prettier

* Reformat

* Remove deprecated tslint

* Remove call to tslint and update package-lock.json
2023-10-03 17:13:24 +02:00

234 lines
7.4 KiB
TypeScript

import { Spinner, SpinnerSize, Stack } from "@fluentui/react";
import { ImmutableExecuteResult, ImmutableOutput } from "@nteract/commutable";
import { actions, AppState, ContentRef, KernelRef, selectors } from "@nteract/core";
import Immutable from "immutable";
import * as React from "react";
import { connect } from "react-redux";
import { Dispatch } from "redux";
import * as Logger from "../../../Common/Logger";
import { Action } from "../../../Shared/Telemetry/TelemetryConstants";
import { traceFailure, traceStart, traceSuccess } from "../../../Shared/Telemetry/TelemetryProcessor";
import loadTransform from "../NotebookComponent/loadTransform";
import SandboxOutputs from "../NotebookRenderer/outputs/SandboxOutputs";
import "./SchemaAnalyzer.less";
import { DefaultFilter, DefaultSampleSize, SchemaAnalyzerHeader } from "./SchemaAnalyzerHeader";
import { SchemaAnalyzerSplashScreen } from "./SchemaAnalyzerSplashScreen";
interface SchemaAnalyzerPureProps {
contentRef: ContentRef;
kernelRef: KernelRef;
databaseId: string;
collectionId: string;
}
interface SchemaAnalyzerDispatchProps {
runCell: (contentRef: ContentRef, cellId: string) => void;
addTransform: (transform: React.ComponentType & { MIMETYPE: string }) => void;
updateCell: (text: string, id: string, contentRef: ContentRef) => void;
}
type OutputType = "rich" | "json";
interface SchemaAnalyzerState {
outputType: OutputType;
isFiltering: boolean;
sampleSize: string;
}
type SchemaAnalyzerProps = SchemaAnalyzerPureProps & StateProps & SchemaAnalyzerDispatchProps;
export class SchemaAnalyzer extends React.Component<SchemaAnalyzerProps, SchemaAnalyzerState> {
private clickAnalyzeTelemetryStartKey: number;
constructor(props: SchemaAnalyzerProps) {
super(props);
this.state = {
outputType: "rich",
isFiltering: false,
sampleSize: DefaultSampleSize,
};
}
componentDidMount(): void {
loadTransform(this.props);
}
private onAnalyzeButtonClick = (filter: string = DefaultFilter, sampleSize: string = this.state.sampleSize) => {
const query = {
command: "listSchema",
database: this.props.databaseId,
collection: this.props.collectionId,
outputType: this.state.outputType,
filter,
sampleSize,
};
this.setState({
isFiltering: true,
});
this.props.updateCell(JSON.stringify(query), this.props.firstCellId, this.props.contentRef);
this.clickAnalyzeTelemetryStartKey = traceStart(Action.SchemaAnalyzerClickAnalyze, {
database: this.props.databaseId,
collection: this.props.collectionId,
sampleSize,
});
this.props.runCell(this.props.contentRef, this.props.firstCellId);
};
private traceClickAnalyzeComplete = (kernelStatus: string, outputs: Immutable.List<ImmutableOutput>) => {
/**
* CosmosMongoKernel always returns 1st output as "text/html"
* This output can be an error stack or information about how many documents were sampled
*/
let firstTextHtmlOutput: string;
if (outputs.size > 0 && outputs.get(0).output_type === "execute_result") {
const executeResult = outputs.get(0) as ImmutableExecuteResult;
firstTextHtmlOutput = executeResult.data["text/html"];
}
const data = {
database: this.props.databaseId,
collection: this.props.collectionId,
firstTextHtmlOutput,
sampleSize: this.state.sampleSize,
numOfOutputs: outputs.size,
kernelStatus,
};
// Only in cases where CosmosMongoKernel runs into an error we get a single output
if (outputs.size === 1) {
traceFailure(Action.SchemaAnalyzerClickAnalyze, data, this.clickAnalyzeTelemetryStartKey);
Logger.logError(`Failed to analyze schema: ${JSON.stringify(data)}`, "SchemaAnalyzer/traceClickAnalyzeComplete");
} else {
traceSuccess(Action.SchemaAnalyzerClickAnalyze, data, this.clickAnalyzeTelemetryStartKey);
}
};
render(): JSX.Element {
const { firstCellId: id, contentRef, kernelStatus, outputs } = this.props;
if (!id) {
return <></>;
}
const isKernelBusy = kernelStatus === "busy";
const isKernelIdle = kernelStatus === "idle";
const showSchemaOutput = isKernelIdle && outputs?.size > 0;
if (showSchemaOutput && this.clickAnalyzeTelemetryStartKey) {
this.traceClickAnalyzeComplete(kernelStatus, outputs);
this.clickAnalyzeTelemetryStartKey = undefined;
}
return (
<div className="schemaAnalyzer">
<Stack tokens={{ childrenGap: 20, padding: 20 }}>
<SchemaAnalyzerHeader
isKernelIdle={isKernelIdle}
isKernelBusy={isKernelBusy}
onSampleSizeUpdated={(sampleSize) => this.setState({ sampleSize })}
onAnalyzeButtonClick={this.onAnalyzeButtonClick}
/>
{showSchemaOutput ? (
<SandboxOutputs
id={id}
contentRef={contentRef}
outputsContainerClassName="schema-analyzer-cell-outputs"
outputClassName="schema-analyzer-cell-output"
/>
) : this.state.isFiltering ? (
<Spinner styles={{ root: { marginTop: 40 } }} size={SpinnerSize.large} />
) : (
<SchemaAnalyzerSplashScreen
isKernelIdle={isKernelIdle}
isKernelBusy={isKernelBusy}
onAnalyzeButtonClick={this.onAnalyzeButtonClick}
/>
)}
</Stack>
</div>
);
}
}
interface StateProps {
firstCellId: string;
kernelStatus: string;
outputs: Immutable.List<ImmutableOutput>;
}
interface InitialProps {
kernelRef: string;
contentRef: string;
}
// Redux
const makeMapStateToProps = (state: AppState, initialProps: InitialProps) => {
const { kernelRef, contentRef } = initialProps;
const mapStateToProps = (state: AppState) => {
let kernelStatus;
let firstCellId;
let outputs;
const kernel = selectors.kernel(state, { kernelRef });
if (kernel) {
kernelStatus = kernel.status;
}
const content = selectors.content(state, { contentRef });
if (content?.type === "notebook") {
const cellOrder = selectors.notebook.cellOrder(content.model);
if (cellOrder.size > 0) {
firstCellId = cellOrder.first() as string;
const model = selectors.model(state, { contentRef });
if (model && model.type === "notebook") {
const cell = selectors.notebook.cellById(model, { id: firstCellId });
if (cell) {
outputs = cell.get("outputs", Immutable.List());
}
}
}
}
return {
firstCellId,
kernelStatus,
outputs,
};
};
return mapStateToProps;
};
const makeMapDispatchToProps = () => {
const mapDispatchToProps = (dispatch: Dispatch) => {
return {
addTransform: (transform: React.ComponentType & { MIMETYPE: string }) => {
return dispatch(
actions.addTransform({
mediaType: transform.MIMETYPE,
component: transform,
}),
);
},
runCell: (contentRef: ContentRef, cellId: string) => {
return dispatch(
actions.executeCell({
contentRef,
id: cellId,
}),
);
},
updateCell: (text: string, id: string, contentRef: ContentRef) => {
dispatch(actions.updateCellSource({ id, contentRef, value: text }));
},
};
};
return mapDispatchToProps;
};
export default connect(makeMapStateToProps, makeMapDispatchToProps)(SchemaAnalyzer);