Add update delete sqlDocumentTab

This commit is contained in:
sunilyadav840
2021-07-05 19:22:03 +05:30
parent c01a382b4f
commit a456318099
5 changed files with 365 additions and 64 deletions

View File

@@ -3096,6 +3096,10 @@ settings-pane {
width: 80%; width: 80%;
margin: 15px; margin: 15px;
} }
.documentSqlTabSearchBar{
width: 68%;
margin: 15px;
}
.documentTabFiltetButton{ .documentTabFiltetButton{
margin-top: 15px; margin-top: 15px;
} }
@@ -3111,6 +3115,9 @@ settings-pane {
width: 20%; width: 20%;
height: auto; height: auto;
} }
.sqlFilterSuggestions {
margin-left: 10%;
}
.documentTabSuggestions { .documentTabSuggestions {
padding: 5px; padding: 5px;
cursor: pointer; cursor: pointer;
@@ -3149,3 +3156,8 @@ settings-pane {
.splitterWrapper .splitter-layout .layout-pane.layout-pane-primary{ .splitterWrapper .splitter-layout .layout-pane.layout-pane-primary{
max-height: 60%; max-height: 60%;
} }
.queryText {
padding: 20px;
padding-left: 10px;
padding-right: 0px;
}

View File

@@ -1,12 +1,13 @@
import { CollectionBase } from "../../Contracts/ViewModels"; import { CollectionBase } from "../../Contracts/ViewModels";
import DocumentId from "../../Explorer/Tree/DocumentId";
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { client } from "../CosmosClient"; import { client } from "../CosmosClient";
import { getEntityName } from "../DocumentUtility"; import { getEntityName } from "../DocumentUtility";
import { handleError } from "../ErrorHandlingUtils"; import { handleError } from "../ErrorHandlingUtils";
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import DocumentId from "../../Explorer/Tree/DocumentId";
export const deleteDocument = async (collection: CollectionBase, documentId: DocumentId): Promise<void> => { export const deleteDocument = async (collection: CollectionBase, documentId: DocumentId): Promise<void> => {
const entityName: string = getEntityName(); const entityName: string = getEntityName();
console.log("documemt", documentId);
const clearMessage = logConsoleProgress(`Deleting ${entityName} ${documentId.id()}`); const clearMessage = logConsoleProgress(`Deleting ${entityName} ${documentId.id()}`);
try { try {

View File

@@ -1,4 +1,5 @@
import { extractPartitionKey, PartitionKeyDefinition } from "@azure/cosmos"; import { extractPartitionKey, PartitionKeyDefinition } from "@azure/cosmos";
import { Resource } from "../../../src/Contracts/DataModels";
import * as DataModels from "../../Contracts/DataModels"; import * as DataModels from "../../Contracts/DataModels";
import DocumentId from "../Tree/DocumentId"; import DocumentId from "../Tree/DocumentId";
@@ -49,3 +50,48 @@ export function formatDocumentContent(row: DocumentId): string {
const formattedDocumentContent = documentContent.replace(/,/g, ",\n").replace("{", "{\n").replace("}", "\n}"); const formattedDocumentContent = documentContent.replace(/,/g, ",\n").replace("{", "{\n").replace("}", "\n}");
return formattedDocumentContent; return formattedDocumentContent;
} }
export function formatSqlDocumentContent(row: Resource): string {
const { id, _rid, _self, _ts, _etag } = row;
const documentContent = JSON.stringify({
id: id || "",
_rid: _rid || "",
_self: _self || "",
_ts: _ts || "",
_etag: _etag || "",
});
const formattedDocumentContent = documentContent.replace(/,/g, ",\n").replace("{", "{\n").replace("}", "\n}");
return formattedDocumentContent;
}
export function getFilterPlaceholder(isPreferredApiMongoDB: boolean): string {
const filterPlaceholder = isPreferredApiMongoDB
? "Type a query predicate (e.g., {´a´:´foo´}), or choose one from the drop down list, or leave empty to query all documents."
: "Type a query predicate (e.g., WHERE c.id=´1´), or choose one from the drop down list, or leave empty to query all documents.";
return filterPlaceholder;
}
export function getFilterSuggestions(isPreferredApiMongoDB: boolean): { value: string }[] {
const filterSuggestions = isPreferredApiMongoDB
? [{ value: `{"id": "foo"}` }, { value: "{ qty: { $gte: 20 } }" }]
: [
{ value: 'WHERE c.id = "foo"' },
{ value: "ORDER BY c._ts DESC" },
{ value: 'WHERE c.id = "foo" ORDER BY c._ts DESC' },
];
return filterSuggestions;
}
export function getDocumentItems(
isPreferredApiMongoDB: boolean,
documentIds: Array<DocumentId>,
documentSqlIds: Array<Resource>,
isAllDocumentsVisible: boolean
): Array<DocumentId> | Array<Resource> {
if (isPreferredApiMongoDB) {
const documentItems = documentIds.reverse();
return isAllDocumentsVisible ? documentItems : documentItems.slice(0, 5);
}
const documentSqlItems = documentSqlIds.reverse();
return isAllDocumentsVisible ? documentSqlItems : documentSqlItems.slice(0, 5);
}

View File

@@ -46,11 +46,10 @@ export default class DocumentsTab extends TabsBase {
// public documentIds: ko.ObservableArray<DocumentId>; // public documentIds: ko.ObservableArray<DocumentId>;
// private _documentsIterator: QueryIterator<ItemDefinition & Resource>; // private _documentsIterator: QueryIterator<ItemDefinition & Resource>;
// private _resourceTokenPartitionKey: string; public _resourceTokenPartitionKey: string;
constructor(options: ViewModels.DocumentsTabOptions) { constructor(options: ViewModels.DocumentsTabOptions) {
super(options); super(options);
// this.isPreferredApiMongoDB = userContext.apiType === "Mongo" || options.isPreferredApiMongoDB; // this.isPreferredApiMongoDB = userContext.apiType === "Mongo" || options.isPreferredApiMongoDB;
// this.idHeader = this.isPreferredApiMongoDB ? "_id" : "id"; // this.idHeader = this.isPreferredApiMongoDB ? "_id" : "id";
@@ -64,7 +63,7 @@ export default class DocumentsTab extends TabsBase {
// this.selectedDocumentContent = editable.observable<string>(""); // this.selectedDocumentContent = editable.observable<string>("");
// this.initialDocumentContent = ko.observable<string>(""); // this.initialDocumentContent = ko.observable<string>("");
this.partitionKey = options.partitionKey || (this.collection && this.collection.partitionKey); this.partitionKey = options.partitionKey || (this.collection && this.collection.partitionKey);
// this._resourceTokenPartitionKey = options.resourceTokenPartitionKey; this._resourceTokenPartitionKey = options.resourceTokenPartitionKey;
// this.documentIds = options.documentIds; // this.documentIds = options.documentIds;
this.partitionKeyPropertyHeader = this.partitionKeyPropertyHeader =

View File

@@ -22,22 +22,37 @@ import DiscardIcon from "../../../images/discard.svg";
import DocumentWaterMark from "../../../images/DocumentWaterMark.svg"; import DocumentWaterMark from "../../../images/DocumentWaterMark.svg";
import NewDocumentIcon from "../../../images/NewDocument.svg"; import NewDocumentIcon from "../../../images/NewDocument.svg";
import SaveIcon from "../../../images/save-cosmos.svg"; import SaveIcon from "../../../images/save-cosmos.svg";
import UploadIcon from "../../../images/Upload_16x16.svg";
import { Resource } from "../../../src/Contracts/DataModels";
import { Areas } from "../../Common/Constants"; import { Areas } from "../../Common/Constants";
// import { createDocument } from "../../Common/dataAccess/createDocument"; import { createDocument as createSqlDocuments } from "../../Common/dataAccess/createDocument";
import { deleteDocument as deleteSqlDocument } from "../../Common/dataAccess/deleteDocument";
import { queryDocuments as querySqlDocuments } from "../../Common/dataAccess/queryDocuments";
import { updateDocument as updateSqlDocuments } from "../../Common/dataAccess/updateDocument";
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils"; import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
import * as HeadersUtility from "../../Common/HeadersUtility";
import { logError } from "../../Common/Logger"; import { logError } from "../../Common/Logger";
import { createDocument, deleteDocument, queryDocuments, updateDocument } from "../../Common/MongoProxyClient"; import { createDocument, deleteDocument, queryDocuments, updateDocument } from "../../Common/MongoProxyClient";
import * as ViewModels from "../../Contracts/ViewModels"; import * as ViewModels from "../../Contracts/ViewModels";
import { Action } from "../../Shared/Telemetry/TelemetryConstants"; import { Action } 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 * as QueryUtils from "../../Utils/QueryUtils";
import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent"; import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent";
import { EditorReact } from "../Controls/Editor/EditorReact"; import { EditorReact } from "../Controls/Editor/EditorReact";
import { useCommandBar } from "../Menus/CommandBar/CommandBarComponentAdapter"; import { useCommandBar } from "../Menus/CommandBar/CommandBarComponentAdapter";
import DocumentId from "../Tree/DocumentId"; import DocumentId from "../Tree/DocumentId";
import ObjectId from "../Tree/ObjectId"; import ObjectId from "../Tree/ObjectId";
import DocumentsTab from "./DocumentsTab1"; import DocumentsTab from "./DocumentsTab1";
import { formatDocumentContent, getPartitionKeyDefinition, hasShardKeySpecified } from "./DocumentTabUtils"; import {
formatDocumentContent,
formatSqlDocumentContent,
getDocumentItems,
getFilterPlaceholder,
getFilterSuggestions,
getPartitionKeyDefinition,
hasShardKeySpecified
} from "./DocumentTabUtils";
const filterIcon: IIconProps = { iconName: "Filter" }; const filterIcon: IIconProps = { iconName: "Filter" };
@@ -52,9 +67,16 @@ export interface IDocumentsTabContentState {
isEditorVisible: boolean; isEditorVisible: boolean;
documentContent: string; documentContent: string;
documentIds: Array<DocumentId>; documentIds: Array<DocumentId>;
documentSqlIds: Array<Resource>;
editorKey: string; editorKey: string;
selectedDocumentId?: DocumentId; selectedDocumentId?: DocumentId;
selectedSqlDocumentId?: Resource;
isEditorContentEdited: boolean; isEditorContentEdited: boolean;
isAllDocumentsVisible: boolean;
}
export interface IDocumentsTabContentProps extends DocumentsTab {
_resourceTokenPartitionKey: string;
} }
export interface IDocument { export interface IDocument {
@@ -75,9 +97,7 @@ const imageProps: Partial<IImageProps> = {
style: { marginTop: "15px" }, style: { marginTop: "15px" },
}; };
const filterSuggestions = [{ value: `{"id": "foo"}` }, { value: "{ qty: { $gte: 20 } }" }];
const intitalDocumentContent = `{ \n "id": "replace_with_new_document_id" \n }`; const intitalDocumentContent = `{ \n "id": "replace_with_new_document_id" \n }`;
const idHeader = userContext.apiType === "Mongo" ? "_id" : "id";
export default class DocumentsTabContent extends React.Component<DocumentsTab, IDocumentsTabContentState> { export default class DocumentsTabContent extends React.Component<DocumentsTab, IDocumentsTabContentState> {
public newDocumentButton: IButton; public newDocumentButton: IButton;
@@ -118,7 +138,7 @@ export default class DocumentsTabContent extends React.Component<DocumentsTab, I
const columns: IColumn[] = [ const columns: IColumn[] = [
{ {
key: "_id", key: "_id",
name: idHeader, name: userContext.apiType === "Mongo" ? "_id" : "id",
minWidth: 90, minWidth: 90,
maxWidth: 140, maxWidth: 140,
isResizable: true, isResizable: true,
@@ -127,7 +147,7 @@ export default class DocumentsTabContent extends React.Component<DocumentsTab, I
onRender: (item: DocumentId) => { onRender: (item: DocumentId) => {
return ( return (
<div onClick={() => this.handleRow(item)} className="documentIdItem"> <div onClick={() => this.handleRow(item)} className="documentIdItem">
{item.rid} {userContext.apiType === "Mongo" ? item.rid : item.id}
</div> </div>
); );
}, },
@@ -155,8 +175,10 @@ export default class DocumentsTabContent extends React.Component<DocumentsTab, I
isEditorVisible: false, isEditorVisible: false,
documentContent: intitalDocumentContent, documentContent: intitalDocumentContent,
documentIds: [], documentIds: [],
documentSqlIds: [],
editorKey: "", editorKey: "",
isEditorContentEdited: false, isEditorContentEdited: false,
isAllDocumentsVisible: false,
}; };
} }
@@ -165,9 +187,40 @@ export default class DocumentsTabContent extends React.Component<DocumentsTab, I
this.updateTabButton(); this.updateTabButton();
if (userContext.apiType === "Mongo") { if (userContext.apiType === "Mongo") {
this.queryDocumentsData(); this.queryDocumentsData();
} else {
this.querySqlDocumentsData();
} }
} }
public buildQuery(filter: string): string {
return QueryUtils.buildDocumentsQuery(filter, this.props.partitionKeyProperty, this.props.partitionKey);
}
querySqlDocumentsData = async (): Promise<void> => {
this.props.isExecuting(true);
this.props.isExecutionError(false);
const { filter } = this.state;
const query: string = this.buildQuery(filter);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const options: any = {};
options.enableCrossPartitionQuery = HeadersUtility.shouldEnableCrossPartitionKey();
if (this.props._resourceTokenPartitionKey) {
options.partitionKey = this.props._resourceTokenPartitionKey;
}
try {
const sqlQuery = querySqlDocuments(this.props.collection.databaseId, this.props.collection.id(), query, options);
const querySqlDocumentsData = await sqlQuery.fetchNext();
this.setState({ documentSqlIds: querySqlDocumentsData.resources.length ? querySqlDocumentsData.resources : [] });
this.props.isExecuting(false);
} catch (error) {
this.props.isExecuting(false);
this.props.isExecutionError(true);
const errorMessage = getErrorMessage(error);
window.alert(errorMessage);
}
};
queryDocumentsData = async (): Promise<void> => { queryDocumentsData = async (): Promise<void> => {
this.props.isExecuting(true); this.props.isExecuting(true);
this.props.isExecutionError(false); this.props.isExecutionError(false);
@@ -223,9 +276,9 @@ export default class DocumentsTabContent extends React.Component<DocumentsTab, I
} }
}; };
handleRow = (row: DocumentId): void => { handleRow = (row: DocumentId | Resource): void => {
if (this.state.isEditorContentEdited) { if (this.state.isEditorContentEdited) {
const isChangesConfirmed = window.confirm("Your unsaved changes will be lost.") const isChangesConfirmed = window.confirm("Your unsaved changes will be lost.");
if (isChangesConfirmed) { if (isChangesConfirmed) {
this.handleRowContent(row); this.handleRowContent(row);
this.setState({ isEditorContentEdited: false }); this.setState({ isEditorContentEdited: false });
@@ -236,11 +289,14 @@ export default class DocumentsTabContent extends React.Component<DocumentsTab, I
} }
}; };
handleRowContent = (row: DocumentId): void => { handleRowContent = (row: DocumentId | Resource): void => {
const formattedDocumentContent = formatDocumentContent(row); const formattedDocumentContent =
userContext.apiType === "Mongo"
? formatDocumentContent(row as DocumentId)
: formatSqlDocumentContent(row as Resource);
this.newDocumentButton = { this.newDocumentButton = {
visible: false, visible: true,
enabled: false, enabled: true,
}; };
this.saveNewDocumentButton = { this.saveNewDocumentButton = {
visible: false, visible: false,
@@ -262,6 +318,12 @@ export default class DocumentsTabContent extends React.Component<DocumentsTab, I
visible: true, visible: true,
enabled: true, enabled: true,
}; };
userContext.apiType === "Mongo"
? this.updateContent(row as DocumentId, formattedDocumentContent)
: this.updateSqlContent(row as Resource, formattedDocumentContent);
};
updateContent = (row: DocumentId, formattedDocumentContent: string): void => {
this.setState( this.setState(
{ {
documentContent: formattedDocumentContent, documentContent: formattedDocumentContent,
@@ -273,29 +335,86 @@ export default class DocumentsTabContent extends React.Component<DocumentsTab, I
this.updateTabButton(); this.updateTabButton();
} }
); );
} };
formatDocumentContent = (row: DocumentId): string => { updateSqlContent = (row: Resource, formattedDocumentContent: string): void => {
const { partitionKeyProperty, partitionKeyValue, rid, self, stringPartitionKeyValue, ts } = row; this.setState(
const documentContent = JSON.stringify({ {
partitionKeyProperty: partitionKeyProperty || "", documentContent: formattedDocumentContent,
partitionKeyValue: partitionKeyValue || "", isEditorVisible: true,
rid: rid || "", editorKey: row._rid,
self: self || "", selectedSqlDocumentId: row,
stringPartitionKeyValue: stringPartitionKeyValue || "", },
ts: ts || "", () => {
}); this.updateTabButton();
const formattedDocumentContent = documentContent.replace(/,/g, ",\n").replace("{", "{\n").replace("}", "\n}"); }
return formattedDocumentContent; );
}; };
handleFilter = (): void => { handleFilter = (): void => {
this.queryDocumentsData(); userContext.apiType === "Mongo" ? this.queryDocumentsData() : this.querySqlDocumentsData();
this.setState({ this.setState({
isSuggestionVisible: false, isSuggestionVisible: false,
}); });
}; };
async updateSqlDocument(): Promise<void> {
const { partitionKey, partitionKeyProperty, isExecutionError, isExecuting, tabTitle, collection } = this.props;
const { documentContent } = this.state;
const partitionKeyArray = extractPartitionKey(
this.state.selectedSqlDocumentId,
getPartitionKeyDefinition(partitionKey, partitionKeyProperty) as PartitionKeyDefinition
);
const partitionKeyValue = partitionKeyArray && partitionKeyArray[0];
const selectedDocumentId = new DocumentId(
this.props as DocumentsTab,
this.state.selectedSqlDocumentId,
partitionKeyValue
);
isExecutionError(false);
const startKey: number = TelemetryProcessor.traceStart(Action.UpdateDocument, {
dataExplorerArea: Areas.Tab,
tabTitle: tabTitle(),
});
try {
isExecuting(true);
const updateSqlDocumentRes = await updateSqlDocuments(
collection as ViewModels.Collection,
selectedDocumentId,
documentContent
);
if (updateSqlDocumentRes) {
TelemetryProcessor.traceSuccess(
Action.UpdateDocument,
{
dataExplorerArea: Areas.Tab,
tabTitle: tabTitle(),
},
startKey
);
this.querySqlDocumentsData();
isExecuting(false);
}
} catch (error) {
isExecutionError(true);
isExecuting(false);
const errorMessage = getErrorMessage(error);
window.alert(errorMessage);
TelemetryProcessor.traceFailure(
Action.UpdateDocument,
{
dataExplorerArea: Areas.Tab,
tabTitle: tabTitle(),
error: errorMessage,
errorStack: getErrorStack(error),
},
startKey
);
}
}
private async updateMongoDocument(): Promise<void> { private async updateMongoDocument(): Promise<void> {
const { selectedDocumentId, documentContent, documentIds } = this.state; const { selectedDocumentId, documentContent, documentIds } = this.state;
const { isExecutionError, isExecuting, tabTitle, collection, partitionKey, partitionKeyProperty } = this.props; const { isExecutionError, isExecuting, tabTitle, collection, partitionKey, partitionKeyProperty } = this.props;
@@ -319,9 +438,7 @@ export default class DocumentsTabContent extends React.Component<DocumentsTab, I
updatedDocument, updatedDocument,
getPartitionKeyDefinition(partitionKey, partitionKeyProperty) as PartitionKeyDefinition getPartitionKeyDefinition(partitionKey, partitionKeyProperty) as PartitionKeyDefinition
); );
const partitionKeyValue = partitionKeyArray && partitionKeyArray[0]; const partitionKeyValue = partitionKeyArray && partitionKeyArray[0];
const id = new ObjectId(this.props as DocumentsTab, updatedDocument, partitionKeyValue); const id = new ObjectId(this.props as DocumentsTab, updatedDocument, partitionKeyValue);
documentId.id(id.id()); documentId.id(id.id());
} }
@@ -356,7 +473,7 @@ export default class DocumentsTabContent extends React.Component<DocumentsTab, I
protected getTabsButtons(): CommandButtonComponentProps[] { protected getTabsButtons(): CommandButtonComponentProps[] {
const buttons: CommandButtonComponentProps[] = []; const buttons: CommandButtonComponentProps[] = [];
const label = "New Document"; const label = userContext.apiType === "Mongo" ? "New Document" : "New Item";
if (this.newDocumentButton.visible) { if (this.newDocumentButton.visible) {
buttons.push({ buttons.push({
iconSrc: NewDocumentIcon, iconSrc: NewDocumentIcon,
@@ -400,6 +517,8 @@ export default class DocumentsTabContent extends React.Component<DocumentsTab, I
buttons.push({ buttons.push({
...this, ...this,
updateMongoDocument: this.updateMongoDocument, updateMongoDocument: this.updateMongoDocument,
updateSqlDocument: this.updateSqlDocument,
setState: this.setState,
iconSrc: SaveIcon, iconSrc: SaveIcon,
iconAlt: label, iconAlt: label,
onCommandClick: this.onSaveExisitingDocumentClick, onCommandClick: this.onSaveExisitingDocumentClick,
@@ -429,6 +548,7 @@ export default class DocumentsTabContent extends React.Component<DocumentsTab, I
const label = "Delete"; const label = "Delete";
buttons.push({ buttons.push({
...this, ...this,
setState: this.setState,
iconSrc: DeleteDocumentIcon, iconSrc: DeleteDocumentIcon,
iconAlt: label, iconAlt: label,
onCommandClick: this.onDeleteExisitingDocumentClick, onCommandClick: this.onDeleteExisitingDocumentClick,
@@ -438,34 +558,89 @@ export default class DocumentsTabContent extends React.Component<DocumentsTab, I
disabled: !this.deleteExisitingDocumentButton.enabled, disabled: !this.deleteExisitingDocumentButton.enabled,
}); });
} }
if (userContext.apiType !== "Mongo") {
const { collection } = this.props;
const label = "Upload Item";
buttons.push({
iconSrc: UploadIcon,
iconAlt: label,
onCommandClick: () => {
const selectedCollection: ViewModels.Collection = collection.container.findSelectedCollection();
selectedCollection && collection.container.openUploadItemsPanePane();
},
commandButtonLabel: label,
ariaLabel: label,
hasPopup: true,
disabled: collection.container.isDatabaseNodeOrNoneSelected(),
});
}
return buttons; return buttons;
} }
private onSaveExisitingDocumentClick(): void { private onSaveExisitingDocumentClick(): void {
this.updateMongoDocument(); userContext.apiType === "Mongo" ? this.updateMongoDocument() : this.updateSqlDocument();
} }
private async onDeleteExisitingDocumentClick(): Promise<void> { private async onDeleteExisitingDocumentClick(): Promise<void> {
const msg = userContext.apiType !== "Mongo" const msg =
userContext.apiType !== "Mongo"
? "Are you sure you want to delete the selected item ?" ? "Are you sure you want to delete the selected item ?"
: "Are you sure you want to delete the selected document ?"; : "Are you sure you want to delete the selected document ?";
const { const { isExecutionError, isExecuting, collection, tabTitle, partitionKey, partitionKeyProperty } = this.props;
isExecutionError, const partitionKeyArray = extractPartitionKey(
isExecuting, this.state.selectedSqlDocumentId,
collection, getPartitionKeyDefinition(partitionKey, partitionKeyProperty) as PartitionKeyDefinition
} = this.props; );
const partitionKeyValue = partitionKeyArray && partitionKeyArray[0];
const selectedDocumentId = new DocumentId(
this.props as DocumentsTab,
this.state.selectedSqlDocumentId,
partitionKeyValue
);
const startKey: number = TelemetryProcessor.traceStart(Action.DeleteDocument, {
dataExplorerArea: Areas.Tab,
tabTitle: tabTitle(),
});
if (window.confirm(msg)) { if (window.confirm(msg)) {
try { try {
isExecuting(true) isExecuting(true);
await deleteDocument(collection.databaseId, collection as ViewModels.Collection, this.state.selectedDocumentId); if (userContext.apiType === "Mongo") {
isExecuting(false) await deleteDocument(
collection.databaseId,
collection as ViewModels.Collection,
this.state.selectedDocumentId
);
} else {
await deleteSqlDocument(collection as ViewModels.Collection, selectedDocumentId);
}
TelemetryProcessor.traceSuccess(
Action.DeleteDocument,
{
dataExplorerArea: Areas.Tab,
tabTitle: tabTitle(),
},
startKey
);
this.setState({ isEditorVisible: false });
isExecuting(false);
this.querySqlDocumentsData();
} catch (error) { } catch (error) {
console.error(error);
isExecutionError(true); isExecutionError(true);
isExecuting(false) isExecuting(false);
TelemetryProcessor.traceFailure(
Action.DeleteDocument,
{
dataExplorerArea: Areas.Tab,
tabTitle: tabTitle(),
error: getErrorMessage(error),
errorStack: getErrorStack(error),
},
startKey
);
} }
} }
} }
@@ -491,6 +666,19 @@ export default class DocumentsTabContent extends React.Component<DocumentsTab, I
visible: true, visible: true,
enabled: true, enabled: true,
}; };
this.saveExisitingDocumentButton = {
visible: false,
enabled: false,
};
this.discardExisitingDocumentChangesButton = {
visible: false,
enabled: false,
};
this.deleteExisitingDocumentButton = {
visible: false,
enabled: false,
};
this.updateTabButton(); this.updateTabButton();
this.setState({ this.setState({
documentContent: intitalDocumentContent, documentContent: intitalDocumentContent,
@@ -502,6 +690,52 @@ export default class DocumentsTabContent extends React.Component<DocumentsTab, I
private onSaveNewDocumentClick = () => { private onSaveNewDocumentClick = () => {
if (userContext.apiType === "Mongo") { if (userContext.apiType === "Mongo") {
this.onSaveNewMongoDocumentClick(); this.onSaveNewMongoDocumentClick();
} else {
this.onSaveSqlNewMongoDocumentClick();
}
};
public onSaveSqlNewMongoDocumentClick = async (): Promise<void> => {
const { isExecutionError, tabTitle, isExecuting, collection } = this.props;
isExecutionError(false);
const startKey: number = TelemetryProcessor.traceStart(Action.CreateDocument, {
dataExplorerArea: Areas.Tab,
tabTitle: tabTitle(),
});
const document = JSON.parse(this.state.documentContent);
isExecuting(true);
try {
const savedDocument = await createSqlDocuments(collection, document);
if (savedDocument) {
this.handleRowContent(savedDocument as Resource);
TelemetryProcessor.traceSuccess(
Action.CreateDocument,
{
dataExplorerArea: Areas.Tab,
tabTitle: tabTitle(),
},
startKey
);
}
isExecuting(false);
this.querySqlDocumentsData();
} catch (error) {
isExecutionError(true);
const errorMessage = getErrorMessage(error);
window.alert(errorMessage);
TelemetryProcessor.traceFailure(
Action.CreateDocument,
{
dataExplorerArea: Areas.Tab,
tabTitle: tabTitle(),
error: errorMessage,
errorStack: getErrorStack(error),
},
startKey
);
isExecuting(false);
} }
}; };
@@ -604,7 +838,6 @@ export default class DocumentsTabContent extends React.Component<DocumentsTab, I
isEditorVisible: false, isEditorVisible: false,
isEditorContentEdited: false, isEditorContentEdited: false,
}); });
}; };
onRenderCell = (item: { value: string }): JSX.Element => { onRenderCell = (item: { value: string }): JSX.Element => {
@@ -624,8 +857,11 @@ export default class DocumentsTabContent extends React.Component<DocumentsTab, I
}; };
handleLoadMoreDocument = (): void => { handleLoadMoreDocument = (): void => {
this.queryDocumentsData(); userContext.apiType === "Mongo" ? this.queryDocumentsData() : this.querySqlDocumentsData();
this.setState({ isSuggestionVisible: false }); this.setState({
isSuggestionVisible: false,
isAllDocumentsVisible: true,
});
}; };
private updateTabButton = (): void => { private updateTabButton = (): void => {
@@ -642,14 +878,17 @@ export default class DocumentsTabContent extends React.Component<DocumentsTab, I
visible: true, visible: true,
enabled: true, enabled: true,
}; };
this.updateTabButton();
} }
this.setState({ this.setState(
{
documentContent: newContent, documentContent: newContent,
isEditorContentEdited: true, isEditorContentEdited: true,
}); },
() => {
this.updateTabButton();
}
);
}; };
public render(): JSX.Element { public render(): JSX.Element {
@@ -662,19 +901,23 @@ export default class DocumentsTabContent extends React.Component<DocumentsTab, I
isEditorVisible, isEditorVisible,
documentContent, documentContent,
documentIds, documentIds,
documentSqlIds,
editorKey, editorKey,
isAllDocumentsVisible,
} = this.state; } = this.state;
const isPreferredApiMongoDB = userContext.apiType === "Mongo";
return ( return (
<div> <div>
{isFilterOptionVisible && ( {isFilterOptionVisible && (
<div> <div>
<div> <div>
<Stack horizontal verticalFill wrap> <Stack horizontal verticalFill wrap>
{!isPreferredApiMongoDB && <Text className="queryText">SELECT * FROM c</Text>}
<TextField <TextField
iconProps={filterIcon} iconProps={filterIcon}
placeholder="Type a query predicate (e.g., {´a´:´foo´}), or choose one from the drop down list, or leave empty to query all documents." placeholder={getFilterPlaceholder(isPreferredApiMongoDB)}
className="documentTabSearchBar" className={isPreferredApiMongoDB ? "documentTabSearchBar" : "documentSqlTabSearchBar"}
onFocus={() => this.setState({ isSuggestionVisible: true })} onFocus={() => this.setState({ isSuggestionVisible: true })}
onChange={(_event, newInput?: string) => { onChange={(_event, newInput?: string) => {
this.setState({ filter: newInput }); this.setState({ filter: newInput });
@@ -691,15 +934,15 @@ export default class DocumentsTabContent extends React.Component<DocumentsTab, I
</Stack> </Stack>
</div> </div>
{isSuggestionVisible && ( {isSuggestionVisible && (
<div className="filterSuggestions"> <div className={isPreferredApiMongoDB ? "filterSuggestions" : "filterSuggestions sqlFilterSuggestions"}>
<List items={filterSuggestions} onRenderCell={this.onRenderCell} /> <List items={getFilterSuggestions(isPreferredApiMongoDB)} onRenderCell={this.onRenderCell} />
</div> </div>
)} )}
</div> </div>
)} )}
{!isFilterOptionVisible && ( {!isFilterOptionVisible && (
<Stack horizontal verticalFill wrap className="documentTabNoFilterView"> <Stack horizontal verticalFill wrap className="documentTabNoFilterView">
<Text className="noFilterText">No filter applied</Text> <Text className="noFilterText">{isPreferredApiMongoDB ? "No filter applied" : "Select * from C"}</Text>
<PrimaryButton text="Edit Filter" onClick={() => this.setState({ isFilterOptionVisible: true })} /> <PrimaryButton text="Edit Filter" onClick={() => this.setState({ isFilterOptionVisible: true })} />
</Stack> </Stack>
)} )}
@@ -707,7 +950,7 @@ export default class DocumentsTabContent extends React.Component<DocumentsTab, I
<SplitterLayout primaryIndex={0} secondaryInitialSize={1000}> <SplitterLayout primaryIndex={0} secondaryInitialSize={1000}>
<div className="leftSplitter"> <div className="leftSplitter">
<DetailsList <DetailsList
items={documentIds} items={getDocumentItems(isPreferredApiMongoDB, documentIds, documentSqlIds, isAllDocumentsVisible)}
compact={isCompactMode} compact={isCompactMode}
columns={columns} columns={columns}
selectionMode={SelectionMode.none} selectionMode={SelectionMode.none}