Migrate Stored Procedure tab to react (#894)
Co-authored-by: Steve Faulkner <southpolesteve@gmail.com>
This commit is contained in:
parent
f255387ccd
commit
b392bed1b0
|
@ -146,7 +146,7 @@ src/Explorer/Tabs/MongoDocumentsTab.ts
|
||||||
# src/Explorer/Tabs/MongoShellTab.ts
|
# src/Explorer/Tabs/MongoShellTab.ts
|
||||||
src/Explorer/Tabs/NotebookV2Tab.ts
|
src/Explorer/Tabs/NotebookV2Tab.ts
|
||||||
src/Explorer/Tabs/ScriptTabBase.ts
|
src/Explorer/Tabs/ScriptTabBase.ts
|
||||||
src/Explorer/Tabs/StoredProcedureTab.ts
|
# src/Explorer/Tabs/StoredProcedureTab.ts
|
||||||
src/Explorer/Tabs/TabComponents.ts
|
src/Explorer/Tabs/TabComponents.ts
|
||||||
src/Explorer/Tabs/TabsBase.ts
|
src/Explorer/Tabs/TabsBase.ts
|
||||||
src/Explorer/Tabs/TriggerTab.ts
|
src/Explorer/Tabs/TriggerTab.ts
|
||||||
|
|
|
@ -724,45 +724,24 @@ execute-sproc-params-pane {
|
||||||
|
|
||||||
.results-container,
|
.results-container,
|
||||||
.errors-container {
|
.errors-container {
|
||||||
padding: @MediumSpace 0px 0px @MediumSpace;
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
.flex-display();
|
.flex-display();
|
||||||
.flex-direction();
|
.flex-direction();
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
.toggles {
|
|
||||||
height: @ToggleHeight;
|
|
||||||
width: @ToggleWidth;
|
|
||||||
margin-left: @MediumSpace;
|
|
||||||
|
|
||||||
&:focus {
|
|
||||||
.focus();
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab {
|
|
||||||
margin-right: @MediumSpace;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toggleSwitch {
|
|
||||||
.toggleSwitch();
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectedToggle {
|
|
||||||
.selectedToggle();
|
|
||||||
}
|
|
||||||
|
|
||||||
.unselectedToggle {
|
|
||||||
.unselectedToggle();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.enterInputParameters {
|
.enterInputParameters {
|
||||||
padding: @LargeSpace @MediumSpace;
|
padding: @LargeSpace @MediumSpace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div[role="tabpanel"] {
|
||||||
|
height: 100%;
|
||||||
|
padding-bottom: 50px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.errors-container {
|
.errors-container {
|
||||||
padding-left: (2 * @MediumSpace);
|
padding-left: (2 * @MediumSpace);
|
||||||
|
padding: @MediumSpace 0px 0px @MediumSpace;
|
||||||
.errors-header {
|
.errors-header {
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-size: @DefaultFontSize;
|
font-size: @DefaultFontSize;
|
||||||
|
|
|
@ -1,89 +0,0 @@
|
||||||
<div class="tab-pane flexContainer stored-procedure-tab" data-bind="attr:{ id: tabId }" role="tabpanel">
|
|
||||||
<!-- Stored Procedure Tab Form - Start -->
|
|
||||||
<div class="storedTabForm flexContainer">
|
|
||||||
<div class="formTitleFirst">Stored Procedure Id</div>
|
|
||||||
<span class="formTitleTextbox">
|
|
||||||
<input
|
|
||||||
class="formTree"
|
|
||||||
type="text"
|
|
||||||
required
|
|
||||||
pattern="[^/?#\\]*[^/?# \\]"
|
|
||||||
title="May not end with space nor contain characters '\' '/' '#' '?'"
|
|
||||||
aria-label="Stored procedure id"
|
|
||||||
placeholder="Enter the new stored procedure id"
|
|
||||||
size="40"
|
|
||||||
data-bind="
|
|
||||||
textInput: id"
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
<div class="spUdfTriggerHeader">Stored Procedure Body</div>
|
|
||||||
<editor
|
|
||||||
params="{
|
|
||||||
content: originalSprocBody,
|
|
||||||
contentType: 'javascript',
|
|
||||||
isReadOnly: false,
|
|
||||||
ariaLabel: 'Stored procedure body',
|
|
||||||
lineNumbers: 'on',
|
|
||||||
updatedContent: editorContent,
|
|
||||||
theme: _theme
|
|
||||||
}"
|
|
||||||
data-bind="attr: { id: editorId }"
|
|
||||||
></editor>
|
|
||||||
<!-- Results & Errors Content - Start-->
|
|
||||||
<div class="results-container" data-bind="visible: hasResults">
|
|
||||||
<div
|
|
||||||
class="toggles"
|
|
||||||
id="execute-storedproc-toggles"
|
|
||||||
aria-label="Successful execution of stored procedure"
|
|
||||||
data-bind="event: { keydown: onToggleKeyDown }"
|
|
||||||
tabindex="0"
|
|
||||||
>
|
|
||||||
<div class="tab">
|
|
||||||
<input type="radio" class="radio" value="result" />
|
|
||||||
<span
|
|
||||||
class="toggleSwitch"
|
|
||||||
role="button"
|
|
||||||
tabindex="0"
|
|
||||||
data-bind="click: toggleResult, css:{ selectedToggle: isResultToggled(), unselectedToggle: !isResultToggled() }"
|
|
||||||
aria-label="Result"
|
|
||||||
>Result</span
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<div class="tab">
|
|
||||||
<input type="radio" class="radio" value="logs" />
|
|
||||||
<span
|
|
||||||
class="toggleSwitch"
|
|
||||||
role="button"
|
|
||||||
tabindex="0"
|
|
||||||
data-bind="click: toggleLogs, css:{ selectedToggle: isLogsToggled(), unselectedToggle: !isLogsToggled() }"
|
|
||||||
aria-label="console.log"
|
|
||||||
>console.log</span
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<json-editor
|
|
||||||
params="{ content: resultsData, isReadOnly: true, ariaLabel: 'Execute stored procedure result' }"
|
|
||||||
data-bind="attr: { id: executeResultsEditorId }, visible: hasResults() && isResultToggled()"
|
|
||||||
>
|
|
||||||
</json-editor>
|
|
||||||
<json-editor
|
|
||||||
params="{ content: logsData, isReadOnly: true, ariaLabel: 'Execute stored procedure logs' }"
|
|
||||||
data-bind="attr: { id: executeLogsEditorId }, visible: hasResults() && isLogsToggled()"
|
|
||||||
></json-editor>
|
|
||||||
</div>
|
|
||||||
<div class="errors-container" data-bind="visible: hasErrors">
|
|
||||||
<div class="errors-header">Errors:</div>
|
|
||||||
<div class="errorContent">
|
|
||||||
<span class="errorMessage" data-bind="text: error"></span>
|
|
||||||
<span class="errorDetailsLink">
|
|
||||||
<a
|
|
||||||
data-bind="click: $data.onErrorDetailsClick, event: { keypress: $data.onErrorDetailsKeyPress }"
|
|
||||||
aria-label="Error details link"
|
|
||||||
>More details</a
|
|
||||||
>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- Results & Errors Content - End-->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -1,287 +0,0 @@
|
||||||
import { Resource, StoredProcedureDefinition } from "@azure/cosmos";
|
|
||||||
import * as ko from "knockout";
|
|
||||||
import Q from "q";
|
|
||||||
import * as _ from "underscore";
|
|
||||||
import ExecuteQueryIcon from "../../../images/ExecuteQuery.svg";
|
|
||||||
import * as Constants from "../../Common/Constants";
|
|
||||||
import { createStoredProcedure } from "../../Common/dataAccess/createStoredProcedure";
|
|
||||||
import { updateStoredProcedure } from "../../Common/dataAccess/updateStoredProcedure";
|
|
||||||
import editable from "../../Common/EditableUtility";
|
|
||||||
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
|
|
||||||
import * as ViewModels from "../../Contracts/ViewModels";
|
|
||||||
import { useNotificationConsole } from "../../hooks/useNotificationConsole";
|
|
||||||
import { Action } from "../../Shared/Telemetry/TelemetryConstants";
|
|
||||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
|
||||||
import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent";
|
|
||||||
import StoredProcedure from "../Tree/StoredProcedure";
|
|
||||||
import ScriptTabBase from "./ScriptTabBase";
|
|
||||||
import template from "./StoredProcedureTab.html";
|
|
||||||
|
|
||||||
enum ToggleState {
|
|
||||||
Result = "result",
|
|
||||||
Logs = "logs",
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class StoredProcedureTab extends ScriptTabBase {
|
|
||||||
public readonly html = template;
|
|
||||||
public collection: ViewModels.Collection;
|
|
||||||
public node: StoredProcedure;
|
|
||||||
public executeResultsEditorId: string;
|
|
||||||
public executeLogsEditorId: string;
|
|
||||||
public toggleState: ko.Observable<ToggleState>;
|
|
||||||
public originalSprocBody: ViewModels.Editable<string>;
|
|
||||||
public resultsData: ko.Observable<string>;
|
|
||||||
public logsData: ko.Observable<string>;
|
|
||||||
public error: ko.Observable<string>;
|
|
||||||
public hasResults: ko.Observable<boolean>;
|
|
||||||
public hasErrors: ko.Observable<boolean>;
|
|
||||||
|
|
||||||
constructor(options: ViewModels.ScriptTabOption) {
|
|
||||||
super(options);
|
|
||||||
super.onActivate.bind(this);
|
|
||||||
|
|
||||||
this.executeResultsEditorId = `executestoredprocedureresults${this.tabId}`;
|
|
||||||
this.executeLogsEditorId = `executestoredprocedurelogs${this.tabId}`;
|
|
||||||
this.toggleState = ko.observable<ToggleState>(ToggleState.Result);
|
|
||||||
this.originalSprocBody = editable.observable<string>(this.editorContent());
|
|
||||||
this.resultsData = ko.observable<string>();
|
|
||||||
this.logsData = ko.observable<string>();
|
|
||||||
this.error = ko.observable<string>();
|
|
||||||
this.hasResults = ko.observable<boolean>(false);
|
|
||||||
this.hasErrors = ko.observable<boolean>(false);
|
|
||||||
this.error.subscribe((error: string) => {
|
|
||||||
this.hasErrors(error != null);
|
|
||||||
this.hasResults(error == null);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.ariaLabel("Stored Procedure Body");
|
|
||||||
this.buildCommandBarOptions();
|
|
||||||
}
|
|
||||||
|
|
||||||
public onSaveClick = (): Promise<StoredProcedureDefinition & Resource> => {
|
|
||||||
return this._createStoredProcedure({
|
|
||||||
id: this.id(),
|
|
||||||
body: this.editorContent(),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
public onDiscard = (): Q.Promise<any> => {
|
|
||||||
this.setBaselines();
|
|
||||||
const original = this.editorContent.getEditableOriginalValue();
|
|
||||||
this.originalSprocBody(original);
|
|
||||||
this.originalSprocBody.valueHasMutated(); // trigger a re-render of the editor
|
|
||||||
|
|
||||||
return Q();
|
|
||||||
};
|
|
||||||
|
|
||||||
public onUpdateClick = (): Promise<any> => {
|
|
||||||
const data = this._getResource();
|
|
||||||
|
|
||||||
this.isExecutionError(false);
|
|
||||||
this.isExecuting(true);
|
|
||||||
const startKey: number = TelemetryProcessor.traceStart(Action.UpdateStoredProcedure, {
|
|
||||||
dataExplorerArea: Constants.Areas.Tab,
|
|
||||||
tabTitle: this.tabTitle(),
|
|
||||||
});
|
|
||||||
return updateStoredProcedure(this.collection.databaseId, this.collection.id(), data)
|
|
||||||
.then(
|
|
||||||
(updatedResource) => {
|
|
||||||
this.resource(updatedResource);
|
|
||||||
this.tabTitle(updatedResource.id);
|
|
||||||
this.node.id(updatedResource.id);
|
|
||||||
this.node.body(updatedResource.body as string);
|
|
||||||
this.setBaselines();
|
|
||||||
|
|
||||||
const editorModel = this.editor() && this.editor().getModel();
|
|
||||||
editorModel && editorModel.setValue(updatedResource.body as string);
|
|
||||||
this.editorContent.setBaseline(updatedResource.body as string);
|
|
||||||
TelemetryProcessor.traceSuccess(
|
|
||||||
Action.UpdateStoredProcedure,
|
|
||||||
{
|
|
||||||
dataExplorerArea: Constants.Areas.Tab,
|
|
||||||
tabTitle: this.tabTitle(),
|
|
||||||
},
|
|
||||||
startKey
|
|
||||||
);
|
|
||||||
},
|
|
||||||
(error: any) => {
|
|
||||||
this.isExecutionError(true);
|
|
||||||
TelemetryProcessor.traceFailure(
|
|
||||||
Action.UpdateStoredProcedure,
|
|
||||||
{
|
|
||||||
dataExplorerArea: Constants.Areas.Tab,
|
|
||||||
tabTitle: this.tabTitle(),
|
|
||||||
error: getErrorMessage(error),
|
|
||||||
errorStack: getErrorStack(error),
|
|
||||||
},
|
|
||||||
startKey
|
|
||||||
);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.finally(() => this.isExecuting(false));
|
|
||||||
};
|
|
||||||
|
|
||||||
public onExecuteSprocsResult(result: any, logsData: any): void {
|
|
||||||
const resultData: string = this.renderObjectForEditor(_.omit(result, "scriptLogs").result, null, 4);
|
|
||||||
const scriptLogs: string = (result.scriptLogs && decodeURIComponent(result.scriptLogs)) || "";
|
|
||||||
const logs: string = this.renderObjectForEditor(scriptLogs, null, 4);
|
|
||||||
this.error(null);
|
|
||||||
this.resultsData(resultData);
|
|
||||||
this.logsData(logs);
|
|
||||||
}
|
|
||||||
|
|
||||||
public onExecuteSprocsError(error: string): void {
|
|
||||||
this.isExecutionError(true);
|
|
||||||
console.error(error);
|
|
||||||
this.error(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
public onErrorDetailsClick = (src: any, event: MouseEvent): boolean => {
|
|
||||||
useNotificationConsole.getState().expandConsole();
|
|
||||||
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
public onErrorDetailsKeyPress = (src: any, event: KeyboardEvent): boolean => {
|
|
||||||
if (event.keyCode === Constants.KeyCodes.Space || event.keyCode === Constants.KeyCodes.Enter) {
|
|
||||||
this.onErrorDetailsClick(src, null);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
public toggleResult(): void {
|
|
||||||
this.toggleState(ToggleState.Result);
|
|
||||||
this.resultsData.valueHasMutated(); // needed to refresh the json-editor component
|
|
||||||
}
|
|
||||||
|
|
||||||
public toggleLogs(): void {
|
|
||||||
this.toggleState(ToggleState.Logs);
|
|
||||||
this.logsData.valueHasMutated(); // needed to refresh the json-editor component
|
|
||||||
}
|
|
||||||
|
|
||||||
public onToggleKeyDown = (source: any, event: KeyboardEvent): boolean => {
|
|
||||||
if (event.keyCode === Constants.KeyCodes.LeftArrow) {
|
|
||||||
this.toggleResult();
|
|
||||||
event.stopPropagation();
|
|
||||||
return false;
|
|
||||||
} else if (event.keyCode === Constants.KeyCodes.RightArrow) {
|
|
||||||
this.toggleLogs();
|
|
||||||
event.stopPropagation();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
public isResultToggled(): boolean {
|
|
||||||
return this.toggleState() === ToggleState.Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public isLogsToggled(): boolean {
|
|
||||||
return this.toggleState() === ToggleState.Logs;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected updateSelectedNode(): void {
|
|
||||||
if (this.collection == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const database: ViewModels.Database = this.collection.getDatabase();
|
|
||||||
if (!database.isDatabaseExpanded()) {
|
|
||||||
this.collection.container.selectedNode(database);
|
|
||||||
} else if (!this.collection.isCollectionExpanded() || !this.collection.isStoredProceduresExpanded()) {
|
|
||||||
this.collection.container.selectedNode(this.collection);
|
|
||||||
} else {
|
|
||||||
this.collection.container.selectedNode(this.node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected buildCommandBarOptions(): void {
|
|
||||||
ko.computed(() => ko.toJSON([this.isNew, this.formIsDirty])).subscribe(() => this.updateNavbarWithTabsButtons());
|
|
||||||
super.buildCommandBarOptions();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected getTabsButtons(): CommandButtonComponentProps[] {
|
|
||||||
const label = "Execute";
|
|
||||||
return super.getTabsButtons().concat({
|
|
||||||
iconSrc: ExecuteQueryIcon,
|
|
||||||
iconAlt: label,
|
|
||||||
onCommandClick: () => {
|
|
||||||
this.collection && this.collection.container.openExecuteSprocParamsPanel(this.node);
|
|
||||||
},
|
|
||||||
commandButtonLabel: label,
|
|
||||||
ariaLabel: label,
|
|
||||||
hasPopup: false,
|
|
||||||
disabled: this.isNew() || this.formIsDirty(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private _getResource() {
|
|
||||||
return {
|
|
||||||
id: this.id(),
|
|
||||||
body: this.editorContent(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private _createStoredProcedure(resource: StoredProcedureDefinition): Promise<StoredProcedureDefinition & Resource> {
|
|
||||||
this.isExecutionError(false);
|
|
||||||
this.isExecuting(true);
|
|
||||||
const startKey: number = TelemetryProcessor.traceStart(Action.CreateStoredProcedure, {
|
|
||||||
dataExplorerArea: Constants.Areas.Tab,
|
|
||||||
tabTitle: this.tabTitle(),
|
|
||||||
});
|
|
||||||
|
|
||||||
return createStoredProcedure(this.collection.databaseId, this.collection.id(), resource)
|
|
||||||
.then(
|
|
||||||
(createdResource) => {
|
|
||||||
this.tabTitle(createdResource.id);
|
|
||||||
this.isNew(false);
|
|
||||||
this.resource(createdResource);
|
|
||||||
this.hashLocation(
|
|
||||||
`${Constants.HashRoutePrefixes.collectionsWithIds(
|
|
||||||
this.collection.databaseId,
|
|
||||||
this.collection.id()
|
|
||||||
)}/sprocs/${createdResource.id}`
|
|
||||||
);
|
|
||||||
this.setBaselines();
|
|
||||||
|
|
||||||
const editorModel = this.editor() && this.editor().getModel();
|
|
||||||
editorModel && editorModel.setValue(createdResource.body as string);
|
|
||||||
this.editorContent.setBaseline(createdResource.body as string);
|
|
||||||
this.node = this.collection.createStoredProcedureNode(createdResource);
|
|
||||||
TelemetryProcessor.traceSuccess(
|
|
||||||
Action.CreateStoredProcedure,
|
|
||||||
{
|
|
||||||
dataExplorerArea: Constants.Areas.Tab,
|
|
||||||
tabTitle: this.tabTitle(),
|
|
||||||
},
|
|
||||||
startKey
|
|
||||||
);
|
|
||||||
this.editorState(ViewModels.ScriptEditorState.exisitingNoEdits);
|
|
||||||
return createdResource;
|
|
||||||
},
|
|
||||||
(createError) => {
|
|
||||||
this.isExecutionError(true);
|
|
||||||
TelemetryProcessor.traceFailure(
|
|
||||||
Action.CreateStoredProcedure,
|
|
||||||
{
|
|
||||||
dataExplorerArea: Constants.Areas.Tab,
|
|
||||||
tabTitle: this.tabTitle(),
|
|
||||||
error: getErrorMessage(createError),
|
|
||||||
errorStack: getErrorStack(createError),
|
|
||||||
},
|
|
||||||
startKey
|
|
||||||
);
|
|
||||||
return Promise.reject(createError);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.finally(() => this.isExecuting(false));
|
|
||||||
}
|
|
||||||
|
|
||||||
public onDelete(): Q.Promise<any> {
|
|
||||||
// TODO
|
|
||||||
return Q();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
import React from "react";
|
||||||
|
import { ExecuteSprocResult } from "../../../Common/dataAccess/executeStoredProcedure";
|
||||||
|
import * as DataModels from "../../../Contracts/DataModels";
|
||||||
|
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||||
|
import Explorer from "../../Explorer";
|
||||||
|
import StoredProcedure from "../../Tree/StoredProcedure";
|
||||||
|
import ScriptTabBase from "../ScriptTabBase";
|
||||||
|
import StoredProcedureTabComponent, {
|
||||||
|
IStoredProcTabComponentProps,
|
||||||
|
IStorProcTabComponentAccessor,
|
||||||
|
} from "./StoredProcedureTabComponent";
|
||||||
|
|
||||||
|
export interface IStoredProcTabProps {
|
||||||
|
container: Explorer;
|
||||||
|
collection: ViewModels.Collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class NewStoredProcedureTab extends ScriptTabBase {
|
||||||
|
public queryText: string;
|
||||||
|
public currentQuery: string;
|
||||||
|
public partitionKey: DataModels.PartitionKey;
|
||||||
|
public iStoredProcTabComponentProps: IStoredProcTabComponentProps;
|
||||||
|
public iStoreProcAccessor: IStorProcTabComponentAccessor;
|
||||||
|
public node: StoredProcedure;
|
||||||
|
public onSaveClick: () => void;
|
||||||
|
public onUpdateClick: () => Promise<void>;
|
||||||
|
|
||||||
|
constructor(options: ViewModels.ScriptTabOption, private props: IStoredProcTabProps) {
|
||||||
|
super(options);
|
||||||
|
this.partitionKey = options.partitionKey;
|
||||||
|
|
||||||
|
this.iStoredProcTabComponentProps = {
|
||||||
|
resource: options.resource,
|
||||||
|
isNew: options.isNew,
|
||||||
|
tabKind: options.tabKind,
|
||||||
|
title: options.title,
|
||||||
|
tabPath: options.tabPath,
|
||||||
|
collectionBase: options.collection,
|
||||||
|
node: options.node,
|
||||||
|
hasLocation: options.hashLocation,
|
||||||
|
scriptTabBaseInstance: this,
|
||||||
|
collection: props.collection,
|
||||||
|
iStorProcTabComponentAccessor: (instance: IStorProcTabComponentAccessor) => {
|
||||||
|
this.iStoreProcAccessor = instance;
|
||||||
|
},
|
||||||
|
container: props.container,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public render(): JSX.Element {
|
||||||
|
return <StoredProcedureTabComponent {...this.iStoredProcTabComponentProps} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
public onTabClick(): void {
|
||||||
|
this.manager?.activateTab(this);
|
||||||
|
this.iStoreProcAccessor.onTabClickEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public onCloseTabButtonClick(): void {
|
||||||
|
this.manager?.closeTab(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public onExecuteSprocsResult(result: ExecuteSprocResult): void {
|
||||||
|
this.iStoreProcAccessor.onExecuteSprocsResultEvent(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public onExecuteSprocsError(error: string): void {
|
||||||
|
this.iStoreProcAccessor.onExecuteSprocsErrorEvent(error);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,604 @@
|
||||||
|
import { Resource, StoredProcedureDefinition } from "@azure/cosmos";
|
||||||
|
import { Pivot, PivotItem } from "@fluentui/react";
|
||||||
|
import React from "react";
|
||||||
|
import DiscardIcon from "../../../../images/discard.svg";
|
||||||
|
import ExecuteQueryIcon from "../../../../images/ExecuteQuery.svg";
|
||||||
|
import SaveIcon from "../../../../images/save-cosmos.svg";
|
||||||
|
import * as Constants from "../../../Common/Constants";
|
||||||
|
import { NormalizedEventKey } from "../../../Common/Constants";
|
||||||
|
import { createStoredProcedure } from "../../../Common/dataAccess/createStoredProcedure";
|
||||||
|
import { ExecuteSprocResult } from "../../../Common/dataAccess/executeStoredProcedure";
|
||||||
|
import { updateStoredProcedure } from "../../../Common/dataAccess/updateStoredProcedure";
|
||||||
|
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||||
|
import { useNotificationConsole } from "../../../hooks/useNotificationConsole";
|
||||||
|
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
|
||||||
|
import { EditorReact } from "../../Controls/Editor/EditorReact";
|
||||||
|
import Explorer from "../../Explorer";
|
||||||
|
import { useCommandBar } from "../../Menus/CommandBar/CommandBarComponentAdapter";
|
||||||
|
import StoredProcedure from "../../Tree/StoredProcedure";
|
||||||
|
import ScriptTabBase from "../ScriptTabBase";
|
||||||
|
|
||||||
|
export interface IStorProcTabComponentAccessor {
|
||||||
|
onExecuteSprocsResultEvent: (result: ExecuteSprocResult) => void;
|
||||||
|
onExecuteSprocsErrorEvent: (error: string) => void;
|
||||||
|
onTabClickEvent: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Button {
|
||||||
|
visible: boolean;
|
||||||
|
enabled: boolean;
|
||||||
|
isSelected?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IStoredProcTabComponentStates {
|
||||||
|
hasResults: boolean;
|
||||||
|
hasErrors: boolean;
|
||||||
|
error: string;
|
||||||
|
resultData: string;
|
||||||
|
logsData: string;
|
||||||
|
originalSprocBody: string;
|
||||||
|
initialEditorContent: string;
|
||||||
|
sProcEditorContent: string;
|
||||||
|
id: string;
|
||||||
|
executeButton: Button;
|
||||||
|
saveButton: Button;
|
||||||
|
updateButton: Button;
|
||||||
|
discardButton: Button;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IStoredProcTabComponentProps {
|
||||||
|
resource: StoredProcedureDefinition;
|
||||||
|
isNew: boolean;
|
||||||
|
tabKind: ViewModels.CollectionTabKind;
|
||||||
|
title: string;
|
||||||
|
tabPath: string;
|
||||||
|
collectionBase: ViewModels.CollectionBase;
|
||||||
|
//eslint-disable-next-line
|
||||||
|
node?: any;
|
||||||
|
hasLocation: string;
|
||||||
|
scriptTabBaseInstance: ScriptTabBase;
|
||||||
|
collection: ViewModels.Collection;
|
||||||
|
iStorProcTabComponentAccessor: (instance: IStorProcTabComponentAccessor) => void;
|
||||||
|
container: Explorer;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class StoredProcedureTabComponent extends React.Component<
|
||||||
|
IStoredProcTabComponentProps,
|
||||||
|
IStoredProcTabComponentStates
|
||||||
|
> {
|
||||||
|
public node: StoredProcedure;
|
||||||
|
public executeResultsEditorId: string;
|
||||||
|
public executeLogsEditorId: string;
|
||||||
|
public collection: ViewModels.Collection;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
public storedProcTabCompProps: IStoredProcTabComponentProps,
|
||||||
|
private storedProcTabCompStates: IStoredProcTabComponentStates
|
||||||
|
) {
|
||||||
|
super(storedProcTabCompProps);
|
||||||
|
this.state = {
|
||||||
|
error: "",
|
||||||
|
hasErrors: false,
|
||||||
|
hasResults: false,
|
||||||
|
resultData: "",
|
||||||
|
logsData: "",
|
||||||
|
originalSprocBody: this.props.resource.body.toString(),
|
||||||
|
initialEditorContent: this.props.resource.body.toString(),
|
||||||
|
sProcEditorContent: this.props.resource.body.toString(),
|
||||||
|
id: this.props.resource.id,
|
||||||
|
executeButton: {
|
||||||
|
enabled: !this.props.scriptTabBaseInstance.isNew(),
|
||||||
|
visible: true,
|
||||||
|
},
|
||||||
|
saveButton: {
|
||||||
|
enabled: (() => {
|
||||||
|
if (!this.props.scriptTabBaseInstance.formIsValid()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!this.props.scriptTabBaseInstance.formIsDirty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
})(),
|
||||||
|
visible: this.props.scriptTabBaseInstance.isNew(),
|
||||||
|
},
|
||||||
|
updateButton: {
|
||||||
|
enabled: (() => {
|
||||||
|
if (!this.props.scriptTabBaseInstance.formIsValid()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!this.props.scriptTabBaseInstance.formIsDirty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
})(),
|
||||||
|
visible: !this.props.scriptTabBaseInstance.isNew(),
|
||||||
|
},
|
||||||
|
discardButton: {
|
||||||
|
enabled: (() => {
|
||||||
|
if (!this.props.scriptTabBaseInstance.formIsValid()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!this.props.scriptTabBaseInstance.formIsDirty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
})(),
|
||||||
|
visible: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
this.collection = this.props.collection;
|
||||||
|
this.executeResultsEditorId = `executestoredprocedureresults${this.props.scriptTabBaseInstance.tabId}`;
|
||||||
|
this.executeLogsEditorId = `executestoredprocedurelogs${this.props.scriptTabBaseInstance.tabId}`;
|
||||||
|
this.props.scriptTabBaseInstance.ariaLabel("Stored Procedure Body");
|
||||||
|
|
||||||
|
this.props.iStorProcTabComponentAccessor({
|
||||||
|
onExecuteSprocsResultEvent: this.onExecuteSprocsResult.bind(this),
|
||||||
|
onExecuteSprocsErrorEvent: this.onExecuteSprocsError.bind(this),
|
||||||
|
onTabClickEvent: this.onTabClick.bind(this),
|
||||||
|
});
|
||||||
|
|
||||||
|
this.node = this.props.node;
|
||||||
|
|
||||||
|
this.buildCommandBarOptions();
|
||||||
|
}
|
||||||
|
|
||||||
|
public onTabClick(): void {
|
||||||
|
if (this.props.container.tabsManager.openedTabs().length > 0) {
|
||||||
|
useCommandBar.getState().setContextButtons(this.getTabsButtons());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public onSaveClick = (): Promise<StoredProcedureDefinition & Resource> => {
|
||||||
|
return this._createStoredProcedure({
|
||||||
|
id: this.state.id,
|
||||||
|
body: this.state.sProcEditorContent,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
public onDiscard = (): Promise<unknown> => {
|
||||||
|
const onDiscardPromise = new Promise(() => {
|
||||||
|
this.props.scriptTabBaseInstance.setBaselines();
|
||||||
|
const original = this.props.scriptTabBaseInstance.editorContent.getEditableOriginalValue();
|
||||||
|
if (this.state.updateButton.visible) {
|
||||||
|
this.setState({
|
||||||
|
updateButton: {
|
||||||
|
enabled: false,
|
||||||
|
visible: true,
|
||||||
|
},
|
||||||
|
sProcEditorContent: original,
|
||||||
|
discardButton: {
|
||||||
|
enabled: false,
|
||||||
|
visible: true,
|
||||||
|
},
|
||||||
|
executeButton: {
|
||||||
|
enabled: true,
|
||||||
|
visible: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.setState({
|
||||||
|
saveButton: {
|
||||||
|
enabled: false,
|
||||||
|
visible: true,
|
||||||
|
},
|
||||||
|
sProcEditorContent: original,
|
||||||
|
discardButton: {
|
||||||
|
enabled: false,
|
||||||
|
visible: true,
|
||||||
|
},
|
||||||
|
executeButton: {
|
||||||
|
enabled: false,
|
||||||
|
visible: true,
|
||||||
|
},
|
||||||
|
id: "",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
useCommandBar.getState().setContextButtons(this.getTabsButtons());
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
return onDiscardPromise;
|
||||||
|
};
|
||||||
|
|
||||||
|
public onUpdateClick = (): Promise<void> => {
|
||||||
|
const data = this._getResource();
|
||||||
|
|
||||||
|
this.props.scriptTabBaseInstance.isExecutionError(false);
|
||||||
|
this.props.scriptTabBaseInstance.isExecuting(true);
|
||||||
|
|
||||||
|
return updateStoredProcedure(
|
||||||
|
this.props.scriptTabBaseInstance.collection.databaseId,
|
||||||
|
this.props.scriptTabBaseInstance.collection.id(),
|
||||||
|
data
|
||||||
|
)
|
||||||
|
.then(
|
||||||
|
(updatedResource) => {
|
||||||
|
this.props.scriptTabBaseInstance.resource(updatedResource);
|
||||||
|
this.props.scriptTabBaseInstance.tabTitle(updatedResource.id);
|
||||||
|
this.node.id(updatedResource.id);
|
||||||
|
this.node.body(updatedResource.body as string);
|
||||||
|
this.props.scriptTabBaseInstance.setBaselines();
|
||||||
|
|
||||||
|
const editorModel =
|
||||||
|
this.props.scriptTabBaseInstance.editor() && this.props.scriptTabBaseInstance.editor().getModel();
|
||||||
|
editorModel && editorModel.setValue(updatedResource.body as string);
|
||||||
|
this.props.scriptTabBaseInstance.editorContent.setBaseline(updatedResource.body as string);
|
||||||
|
this.setState({
|
||||||
|
discardButton: {
|
||||||
|
enabled: false,
|
||||||
|
visible: true,
|
||||||
|
},
|
||||||
|
updateButton: {
|
||||||
|
enabled: false,
|
||||||
|
visible: true,
|
||||||
|
},
|
||||||
|
executeButton: {
|
||||||
|
enabled: true,
|
||||||
|
visible: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
useCommandBar.getState().setContextButtons(this.getTabsButtons());
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
this.props.scriptTabBaseInstance.isExecutionError(true);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.finally(() => this.props.scriptTabBaseInstance.isExecuting(false));
|
||||||
|
};
|
||||||
|
|
||||||
|
public onExecuteSprocsResult(result: ExecuteSprocResult): void {
|
||||||
|
const resultData: string = this.props.scriptTabBaseInstance.renderObjectForEditor(result.result, undefined, 4);
|
||||||
|
const scriptLogs: string = (result.scriptLogs && decodeURIComponent(result.scriptLogs)) || "";
|
||||||
|
const logs: string = this.props.scriptTabBaseInstance.renderObjectForEditor(scriptLogs, undefined, 4);
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
hasResults: false,
|
||||||
|
});
|
||||||
|
setTimeout(() => {
|
||||||
|
this.setState({
|
||||||
|
error: undefined,
|
||||||
|
resultData: resultData,
|
||||||
|
logsData: logs,
|
||||||
|
hasResults: resultData ? true : false,
|
||||||
|
hasErrors: false,
|
||||||
|
});
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
public onExecuteSprocsError(error: string): void {
|
||||||
|
this.props.scriptTabBaseInstance.isExecutionError(true);
|
||||||
|
console.error(error);
|
||||||
|
this.setState({
|
||||||
|
error: error,
|
||||||
|
hasErrors: true,
|
||||||
|
hasResults: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public onErrorDetailsClick = (): boolean => {
|
||||||
|
useNotificationConsole.getState().expandConsole();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
public onErrorDetailsKeyPress = (event: React.KeyboardEvent<HTMLAnchorElement>): boolean => {
|
||||||
|
if (event.key === NormalizedEventKey.Space || event.key === NormalizedEventKey.Enter) {
|
||||||
|
this.onErrorDetailsClick();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
protected updateSelectedNode(): void {
|
||||||
|
if (this.props.collectionBase === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const database: ViewModels.Database = this.props.collectionBase.getDatabase();
|
||||||
|
if (!database.isDatabaseExpanded()) {
|
||||||
|
this.props.collectionBase.container.selectedNode(database);
|
||||||
|
} else if (!this.props.collectionBase.isCollectionExpanded() || !this.collection.isStoredProceduresExpanded()) {
|
||||||
|
this.props.collectionBase.container.selectedNode(this.props.collectionBase);
|
||||||
|
} else {
|
||||||
|
this.props.collectionBase.container.selectedNode(this.node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected buildCommandBarOptions(): void {
|
||||||
|
useCommandBar.getState().setContextButtons(this.getTabsButtons());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getTabsButtons(): CommandButtonComponentProps[] {
|
||||||
|
const buttons: CommandButtonComponentProps[] = [];
|
||||||
|
const label = "Save";
|
||||||
|
if (this.state.saveButton.visible) {
|
||||||
|
buttons.push({
|
||||||
|
iconSrc: SaveIcon,
|
||||||
|
iconAlt: label,
|
||||||
|
onCommandClick: this.onSaveClick,
|
||||||
|
commandButtonLabel: label,
|
||||||
|
ariaLabel: label,
|
||||||
|
hasPopup: false,
|
||||||
|
disabled: !this.state.saveButton.enabled,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.state.updateButton.visible) {
|
||||||
|
const label = "Update";
|
||||||
|
buttons.push({
|
||||||
|
iconSrc: SaveIcon,
|
||||||
|
iconAlt: label,
|
||||||
|
onCommandClick: this.onUpdateClick,
|
||||||
|
commandButtonLabel: label,
|
||||||
|
ariaLabel: label,
|
||||||
|
hasPopup: false,
|
||||||
|
disabled: !this.state.updateButton.enabled,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.state.discardButton.visible) {
|
||||||
|
const label = "Discard";
|
||||||
|
buttons.push({
|
||||||
|
iconSrc: DiscardIcon,
|
||||||
|
iconAlt: label,
|
||||||
|
onCommandClick: this.onDiscard,
|
||||||
|
commandButtonLabel: label,
|
||||||
|
ariaLabel: label,
|
||||||
|
hasPopup: false,
|
||||||
|
disabled: !this.state.discardButton.enabled,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.state.executeButton.visible) {
|
||||||
|
const label = "Execute";
|
||||||
|
buttons.push({
|
||||||
|
iconSrc: ExecuteQueryIcon,
|
||||||
|
iconAlt: label,
|
||||||
|
onCommandClick: () => {
|
||||||
|
this.collection.container.openExecuteSprocParamsPanel(this.node);
|
||||||
|
},
|
||||||
|
commandButtonLabel: label,
|
||||||
|
ariaLabel: label,
|
||||||
|
hasPopup: false,
|
||||||
|
disabled: !this.state.executeButton.enabled,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return buttons;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _getResource() {
|
||||||
|
return {
|
||||||
|
id: this.state.id,
|
||||||
|
body: this.state.sProcEditorContent,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private _createStoredProcedure(resource: StoredProcedureDefinition): Promise<StoredProcedureDefinition & Resource> {
|
||||||
|
this.props.scriptTabBaseInstance.isExecutionError(false);
|
||||||
|
this.props.scriptTabBaseInstance.isExecuting(true);
|
||||||
|
|
||||||
|
return createStoredProcedure(this.props.collectionBase.databaseId, this.props.collectionBase.id(), resource)
|
||||||
|
.then(
|
||||||
|
(createdResource) => {
|
||||||
|
this.props.scriptTabBaseInstance.tabTitle(createdResource.id);
|
||||||
|
this.props.scriptTabBaseInstance.isNew(false);
|
||||||
|
this.props.scriptTabBaseInstance.resource(createdResource);
|
||||||
|
this.props.scriptTabBaseInstance.hashLocation(
|
||||||
|
`${Constants.HashRoutePrefixes.collectionsWithIds(
|
||||||
|
this.props.collectionBase.databaseId,
|
||||||
|
this.props.collectionBase.id()
|
||||||
|
)}/sprocs/${createdResource.id}`
|
||||||
|
);
|
||||||
|
this.props.scriptTabBaseInstance.setBaselines();
|
||||||
|
|
||||||
|
const editorModel =
|
||||||
|
this.props.scriptTabBaseInstance.editor() && this.props.scriptTabBaseInstance.editor().getModel();
|
||||||
|
editorModel && editorModel.setValue(createdResource.body as string);
|
||||||
|
this.props.scriptTabBaseInstance.editorContent.setBaseline(createdResource.body as string);
|
||||||
|
this.node = this.collection.createStoredProcedureNode(createdResource);
|
||||||
|
this.props.container.tabsManager.openedTabs()[
|
||||||
|
this.props.container.tabsManager.openedTabs().length - 1
|
||||||
|
].node = this.node;
|
||||||
|
|
||||||
|
this.props.scriptTabBaseInstance.editorState(ViewModels.ScriptEditorState.exisitingNoEdits);
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
executeButton: {
|
||||||
|
enabled: false,
|
||||||
|
visible: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
setTimeout(() => {
|
||||||
|
this.setState({
|
||||||
|
executeButton: {
|
||||||
|
enabled: true,
|
||||||
|
visible: true,
|
||||||
|
},
|
||||||
|
updateButton: {
|
||||||
|
enabled: false,
|
||||||
|
visible: true,
|
||||||
|
},
|
||||||
|
saveButton: {
|
||||||
|
enabled: false,
|
||||||
|
visible: false,
|
||||||
|
},
|
||||||
|
discardButton: {
|
||||||
|
enabled: false,
|
||||||
|
visible: true,
|
||||||
|
},
|
||||||
|
sProcEditorContent: this.state.sProcEditorContent,
|
||||||
|
});
|
||||||
|
useCommandBar.getState().setContextButtons(this.getTabsButtons());
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
return createdResource;
|
||||||
|
},
|
||||||
|
(createError) => {
|
||||||
|
this.props.scriptTabBaseInstance.isExecutionError(true);
|
||||||
|
|
||||||
|
return Promise.reject(createError);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.finally(() => this.props.scriptTabBaseInstance.isExecuting(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
public onDelete(): Promise<unknown> {
|
||||||
|
const isDeleted = false;
|
||||||
|
const onDeletePromise = new Promise((resolve) => {
|
||||||
|
resolve(isDeleted);
|
||||||
|
});
|
||||||
|
return onDeletePromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
public handleIdOnChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||||
|
if (this.state.saveButton.visible) {
|
||||||
|
this.setState({
|
||||||
|
id: event.target.value,
|
||||||
|
saveButton: {
|
||||||
|
enabled: true,
|
||||||
|
visible: this.props.scriptTabBaseInstance.isNew(),
|
||||||
|
},
|
||||||
|
discardButton: {
|
||||||
|
enabled: true,
|
||||||
|
visible: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
useCommandBar.getState().setContextButtons(this.getTabsButtons());
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public onChangeContent(newConent: string): void {
|
||||||
|
if (this.state.updateButton.visible) {
|
||||||
|
this.setState({
|
||||||
|
updateButton: {
|
||||||
|
enabled: true,
|
||||||
|
visible: true,
|
||||||
|
},
|
||||||
|
discardButton: {
|
||||||
|
enabled: true,
|
||||||
|
visible: true,
|
||||||
|
},
|
||||||
|
executeButton: {
|
||||||
|
enabled: false,
|
||||||
|
visible: true,
|
||||||
|
},
|
||||||
|
sProcEditorContent: newConent,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.setState({
|
||||||
|
saveButton: {
|
||||||
|
enabled: false,
|
||||||
|
visible: this.props.scriptTabBaseInstance.isNew(),
|
||||||
|
},
|
||||||
|
executeButton: {
|
||||||
|
enabled: false,
|
||||||
|
visible: true,
|
||||||
|
},
|
||||||
|
discardButton: {
|
||||||
|
enabled: true,
|
||||||
|
visible: true,
|
||||||
|
},
|
||||||
|
sProcEditorContent: newConent,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
useCommandBar.getState().setContextButtons(this.getTabsButtons());
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
render(): JSX.Element {
|
||||||
|
return (
|
||||||
|
<div className="tab-pane flexContainer stored-procedure-tab" role="tabpanel">
|
||||||
|
<div className="storedTabForm flexContainer">
|
||||||
|
<div className="formTitleFirst">Stored Procedure Id</div>
|
||||||
|
<span className="formTitleTextbox">
|
||||||
|
<input
|
||||||
|
className="formTree"
|
||||||
|
type="text"
|
||||||
|
required
|
||||||
|
pattern="[^/?#\\]*[^/?# \\]"
|
||||||
|
title="May not end with space nor contain characters '\' '/' '#' '?'"
|
||||||
|
aria-label="Stored procedure id"
|
||||||
|
placeholder="Enter the new stored procedure id"
|
||||||
|
size={40}
|
||||||
|
value={this.state.id}
|
||||||
|
onChange={(event: React.ChangeEvent<HTMLInputElement>) => this.handleIdOnChange(event)}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<div className="spUdfTriggerHeader">Stored Procedure Body</div>
|
||||||
|
<EditorReact
|
||||||
|
language={"javascript"}
|
||||||
|
content={this.state.sProcEditorContent}
|
||||||
|
isReadOnly={false}
|
||||||
|
ariaLabel={"Stored procedure body"}
|
||||||
|
lineNumbers={"on"}
|
||||||
|
theme={"_theme"}
|
||||||
|
onContentChanged={(newContent: string) => this.onChangeContent(newContent)}
|
||||||
|
/>
|
||||||
|
{this.state.hasResults && (
|
||||||
|
<div className="results-container">
|
||||||
|
<Pivot aria-label="Successful execution of stored procedure" style={{ height: "100%" }}>
|
||||||
|
<PivotItem
|
||||||
|
headerText="Result"
|
||||||
|
headerButtonProps={{
|
||||||
|
"data-order": 1,
|
||||||
|
"data-title": "Result",
|
||||||
|
}}
|
||||||
|
style={{ height: "100%" }}
|
||||||
|
>
|
||||||
|
<EditorReact
|
||||||
|
language={"javascript"}
|
||||||
|
content={this.state.resultData}
|
||||||
|
isReadOnly={true}
|
||||||
|
ariaLabel={"Execute stored procedure result"}
|
||||||
|
/>
|
||||||
|
</PivotItem>
|
||||||
|
<PivotItem
|
||||||
|
headerText="console.log"
|
||||||
|
headerButtonProps={{
|
||||||
|
"data-order": 2,
|
||||||
|
"data-title": "console.log",
|
||||||
|
}}
|
||||||
|
style={{ height: "100%" }}
|
||||||
|
>
|
||||||
|
<EditorReact
|
||||||
|
language={"javascript"}
|
||||||
|
content={this.state.logsData}
|
||||||
|
isReadOnly={true}
|
||||||
|
ariaLabel={"Execute stored procedure logs"}
|
||||||
|
/>
|
||||||
|
</PivotItem>
|
||||||
|
</Pivot>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{this.state.hasErrors && (
|
||||||
|
<div className="errors-container">
|
||||||
|
<div className="errors-header">Errors:</div>
|
||||||
|
<div className="errorContent">
|
||||||
|
<span className="errorMessage">{this.state.error}</span>
|
||||||
|
<span className="errorDetailsLink">
|
||||||
|
<a
|
||||||
|
aria-label="Error details link"
|
||||||
|
onClick={() => this.onErrorDetailsClick()}
|
||||||
|
onKeyPress={(event: React.KeyboardEvent<HTMLAnchorElement>) => this.onErrorDetailsKeyPress(event)}
|
||||||
|
>
|
||||||
|
More details
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,13 +3,13 @@ import * as ko from "knockout";
|
||||||
import * as Constants from "../../Common/Constants";
|
import * as Constants from "../../Common/Constants";
|
||||||
import { deleteStoredProcedure } from "../../Common/dataAccess/deleteStoredProcedure";
|
import { deleteStoredProcedure } from "../../Common/dataAccess/deleteStoredProcedure";
|
||||||
import { executeStoredProcedure } from "../../Common/dataAccess/executeStoredProcedure";
|
import { executeStoredProcedure } from "../../Common/dataAccess/executeStoredProcedure";
|
||||||
import { getErrorMessage } from "../../Common/ErrorHandlingUtils";
|
|
||||||
import * as ViewModels from "../../Contracts/ViewModels";
|
import * as ViewModels from "../../Contracts/ViewModels";
|
||||||
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
|
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
|
||||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||||
import { userContext } from "../../UserContext";
|
import { userContext } from "../../UserContext";
|
||||||
import Explorer from "../Explorer";
|
import Explorer from "../Explorer";
|
||||||
import StoredProcedureTab from "../Tabs/StoredProcedureTab";
|
import { getErrorMessage } from "../Tables/Utilities";
|
||||||
|
import { NewStoredProcedureTab } from "../Tabs/StoredProcedureTab/StoredProcedureTab";
|
||||||
import TabsBase from "../Tabs/TabsBase";
|
import TabsBase from "../Tabs/TabsBase";
|
||||||
|
|
||||||
const sampleStoredProcedureBody: string = `// SAMPLE STORED PROCEDURE
|
const sampleStoredProcedureBody: string = `// SAMPLE STORED PROCEDURE
|
||||||
|
@ -67,7 +67,8 @@ export default class StoredProcedure {
|
||||||
body: sampleStoredProcedureBody,
|
body: sampleStoredProcedureBody,
|
||||||
};
|
};
|
||||||
|
|
||||||
const storedProcedureTab: StoredProcedureTab = new StoredProcedureTab({
|
const storedProcedureTab: NewStoredProcedureTab = new NewStoredProcedureTab(
|
||||||
|
{
|
||||||
resource: storedProcedure,
|
resource: storedProcedure,
|
||||||
isNew: true,
|
isNew: true,
|
||||||
tabKind: ViewModels.CollectionTabKind.StoredProcedures,
|
tabKind: ViewModels.CollectionTabKind.StoredProcedures,
|
||||||
|
@ -76,7 +77,12 @@ export default class StoredProcedure {
|
||||||
collection: source,
|
collection: source,
|
||||||
node: source,
|
node: source,
|
||||||
hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(source.databaseId, source.id())}/sproc`,
|
hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(source.databaseId, source.id())}/sproc`,
|
||||||
});
|
},
|
||||||
|
{
|
||||||
|
collection: source,
|
||||||
|
container: source.container,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
source.container.tabsManager.activateNewTab(storedProcedureTab);
|
source.container.tabsManager.activateNewTab(storedProcedureTab);
|
||||||
}
|
}
|
||||||
|
@ -93,11 +99,11 @@ export default class StoredProcedure {
|
||||||
public open = () => {
|
public open = () => {
|
||||||
this.select();
|
this.select();
|
||||||
|
|
||||||
const storedProcedureTabs: StoredProcedureTab[] = this.container.tabsManager.getTabs(
|
const storedProcedureTabs: NewStoredProcedureTab[] = this.container.tabsManager.getTabs(
|
||||||
ViewModels.CollectionTabKind.StoredProcedures,
|
ViewModels.CollectionTabKind.StoredProcedures,
|
||||||
(tab: TabsBase) => tab.node && tab.node.rid === this.rid
|
(tab: TabsBase) => tab.node && tab.node.rid === this.rid
|
||||||
) as StoredProcedureTab[];
|
) as NewStoredProcedureTab[];
|
||||||
let storedProcedureTab: StoredProcedureTab = storedProcedureTabs && storedProcedureTabs[0];
|
let storedProcedureTab: NewStoredProcedureTab = storedProcedureTabs && storedProcedureTabs[0];
|
||||||
|
|
||||||
if (storedProcedureTab) {
|
if (storedProcedureTab) {
|
||||||
this.container.tabsManager.activateTab(storedProcedureTab);
|
this.container.tabsManager.activateTab(storedProcedureTab);
|
||||||
|
@ -109,7 +115,8 @@ export default class StoredProcedure {
|
||||||
body: this.body(),
|
body: this.body(),
|
||||||
};
|
};
|
||||||
|
|
||||||
storedProcedureTab = new StoredProcedureTab({
|
storedProcedureTab = new NewStoredProcedureTab(
|
||||||
|
{
|
||||||
resource: storedProcedureData,
|
resource: storedProcedureData,
|
||||||
isNew: false,
|
isNew: false,
|
||||||
tabKind: ViewModels.CollectionTabKind.StoredProcedures,
|
tabKind: ViewModels.CollectionTabKind.StoredProcedures,
|
||||||
|
@ -121,12 +128,16 @@ export default class StoredProcedure {
|
||||||
this.collection.databaseId,
|
this.collection.databaseId,
|
||||||
this.collection.id()
|
this.collection.id()
|
||||||
)}/sprocs/${this.id()}`,
|
)}/sprocs/${this.id()}`,
|
||||||
});
|
},
|
||||||
|
{
|
||||||
|
collection: this.collection,
|
||||||
|
container: this.container,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
this.container.tabsManager.activateNewTab(storedProcedureTab);
|
this.container.tabsManager.activateNewTab(storedProcedureTab);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public delete() {
|
public delete() {
|
||||||
if (!window.confirm("Are you sure you want to delete the stored procedure?")) {
|
if (!window.confirm("Are you sure you want to delete the stored procedure?")) {
|
||||||
return;
|
return;
|
||||||
|
@ -142,19 +153,19 @@ export default class StoredProcedure {
|
||||||
}
|
}
|
||||||
|
|
||||||
public execute(params: string[], partitionKeyValue?: string): void {
|
public execute(params: string[], partitionKeyValue?: string): void {
|
||||||
const sprocTabs = this.container.tabsManager.getTabs(
|
const sprocTabs: NewStoredProcedureTab[] = this.container.tabsManager.getTabs(
|
||||||
ViewModels.CollectionTabKind.StoredProcedures,
|
ViewModels.CollectionTabKind.StoredProcedures,
|
||||||
(tab: TabsBase) => tab.node && tab.node.rid === this.rid
|
(tab: TabsBase) => tab.node && tab.node.rid === this.rid
|
||||||
) as StoredProcedureTab[];
|
) as NewStoredProcedureTab[];
|
||||||
const sprocTab = sprocTabs && sprocTabs.length > 0 && sprocTabs[0];
|
const sprocTab: NewStoredProcedureTab = sprocTabs && sprocTabs.length > 0 && sprocTabs[0];
|
||||||
sprocTab.isExecuting(true);
|
sprocTab.isExecuting(true);
|
||||||
this.container &&
|
this.container &&
|
||||||
executeStoredProcedure(this.collection, this, partitionKeyValue, params)
|
executeStoredProcedure(this.collection, this, partitionKeyValue, params)
|
||||||
.then(
|
.then(
|
||||||
(result: any) => {
|
(result) => {
|
||||||
sprocTab.onExecuteSprocsResult(result, result.scriptLogs);
|
sprocTab.onExecuteSprocsResult(result);
|
||||||
},
|
},
|
||||||
(error: any) => {
|
(error) => {
|
||||||
sprocTab.onExecuteSprocsError(getErrorMessage(error));
|
sprocTab.onExecuteSprocsError(getErrorMessage(error));
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue