mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-20 01:11:25 +00:00
* Rev up prettier * Reformat * Remove deprecated tslint * Remove call to tslint and update package-lock.json
234 lines
7.4 KiB
TypeScript
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);
|