Remove DocumentsTab and MongoDocumentsTab. Fix build issues.

This commit is contained in:
Laurent Nguyen 2024-05-01 13:55:07 +02:00
parent 1f4d0f21f0
commit b8cbd77069
9 changed files with 43 additions and 1819 deletions

View File

@ -89,10 +89,7 @@ src/Explorer/Tables/TableEntityProcessor.ts
src/Explorer/Tables/Utilities.ts src/Explorer/Tables/Utilities.ts
src/Explorer/Tabs/ConflictsTab.ts src/Explorer/Tabs/ConflictsTab.ts
src/Explorer/Tabs/DatabaseSettingsTab.ts src/Explorer/Tabs/DatabaseSettingsTab.ts
src/Explorer/Tabs/DocumentsTab.test.ts
src/Explorer/Tabs/DocumentsTab.ts
src/Explorer/Tabs/GraphTab.ts src/Explorer/Tabs/GraphTab.ts
src/Explorer/Tabs/MongoDocumentsTab.ts
src/Explorer/Tabs/NotebookV2Tab.ts src/Explorer/Tabs/NotebookV2Tab.ts
src/Explorer/Tabs/ScriptTabBase.ts src/Explorer/Tabs/ScriptTabBase.ts
src/Explorer/Tabs/TabComponents.ts src/Explorer/Tabs/TabComponents.ts

View File

@ -3,8 +3,7 @@ import * as _ from "underscore";
import * as DataModels from "../Contracts/DataModels"; import * as DataModels from "../Contracts/DataModels";
import * as ViewModels from "../Contracts/ViewModels"; import * as ViewModels from "../Contracts/ViewModels";
import Explorer from "../Explorer/Explorer"; import Explorer from "../Explorer/Explorer";
import DocumentsTab from "../Explorer/Tabs/DocumentsTab"; import DocumentId, { IDocumentIdContainer } from "../Explorer/Tree/DocumentId";
import DocumentId from "../Explorer/Tree/DocumentId";
import { useDatabases } from "../Explorer/useDatabases"; import { useDatabases } from "../Explorer/useDatabases";
import { userContext } from "../UserContext"; import { userContext } from "../UserContext";
import * as NotificationConsoleUtils from "../Utils/NotificationConsoleUtils"; import * as NotificationConsoleUtils from "../Utils/NotificationConsoleUtils";
@ -162,10 +161,10 @@ export class QueriesClient {
{ {
partitionKey: QueriesClient.PartitionKey, partitionKey: QueriesClient.PartitionKey,
partitionKeyProperties: ["id"], partitionKeyProperties: ["id"],
} as DocumentsTab, } as IDocumentIdContainer,
query, query,
[query.queryName], [query.queryName],
); // TODO: Remove DocumentId's dependency on DocumentsTab );
const options: any = { partitionKey: query.resourceId }; const options: any = { partitionKey: query.resourceId };
return deleteDocument(queriesCollection, documentId) return deleteDocument(queriesCollection, documentId)
.then( .then(

View File

@ -1,239 +0,0 @@
<div
class="tab-pane active tabdocuments flexContainer"
data-bind="
setTemplateReady: true,
attr:{
id: tabId
},
visible: isActive"
role="tabpanel"
>
<!-- ko if: false -->
<!-- Messagebox Ok Cancel- Start -->
<div class="messagebox-background">
<div class="messagebox">
<h2 class="messagebox-title">Title</h2>
<div class="messagebox-text" tabindex="0">Text</div>
<div class="messagebox-buttons">
<div class="messagebox-buttons-container">
<button value="ok" class="messagebox-button-primary">Ok</button>
<button value="cancel" class="messagebox-button-default">Cancel</button>
</div>
</div>
</div>
</div>
<!-- Messagebox OK Cancel - End -->
<!-- /ko -->
<!-- Filter - Start -->
<div class="filterdivs" data-bind="visible: isFilterCreated">
<!-- Read-only Filter - Start -->
<div class="filterDocCollapsed" data-bind="visible: !isFilterExpanded() && !isPreferredApiMongoDB">
<span class="selectQuery">SELECT * FROM c</span>
<span class="appliedQuery" data-bind="text: appliedFilter"></span>
<button class="filterbtnstyle queryButton" data-bind="click: onShowFilterClick">Edit Filter</button>
</div>
<div
class="filterDocCollapsed"
data-bind="
visible: !isFilterExpanded() && isPreferredApiMongoDB"
>
<span
class="selectQuery"
data-bind="
visible: appliedFilter().length > 0"
>Filter :
</span>
<span
class="noFilterApplied"
data-bind="
visible: !appliedFilter().length > 0"
>No filter applied</span
>
<span class="appliedQuery" data-bind="text: appliedFilter"></span>
<button
class="filterbtnstyle queryButton"
data-bind="
click: onShowFilterClick"
>
Edit Filter
</button>
</div>
<!-- Read-only Filter - End -->
<!-- Editable Filter - start -->
<div
class="filterDocExpanded"
data-bind="
visible: isFilterExpanded"
>
<div>
<div class="editFilterContainer">
<span class="filterspan" data-bind="visible: !isPreferredApiMongoDB"> SELECT * FROM c </span>
<input
type="text"
list="filtersList"
class="querydropdown"
title="Type a query predicate or choose one from the list."
data-bind="
attr:{
placeholder: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.'
},
css: { placeholderVisible: filterContent().length === 0 },
textInput: filterContent,
event: { keydown: onFilterKeyDown }"
/>
<datalist
id="filtersList"
data-bind="
foreach: lastFilterContents"
>
<option
data-bind="
value: $data"
></option>
</datalist>
<span class="filterbuttonpad">
<button
class="filterbtnstyle queryButton"
data-bind="
click: refreshDocumentsGrid.bind($data, true),
enable: applyFilterButton.enabled"
aria-label="Apply filter"
tabindex="0"
>
Apply Filter
</button>
</span>
<span class="filterbuttonpad">
<button
class="filterbtnstyle queryButton"
data-bind="
visible: !isPreferredApiMongoDB && isExecuting,
click: onAbortQueryClick"
aria-label="Cancel Query"
tabindex="0"
>
Cancel Query
</button>
</span>
<span
class="filterclose"
role="button"
aria-label="close filter"
tabindex="0"
data-bind="
click: onHideFilterClick, event: { keydown: onCloseButtonKeyDown }"
>
<img src="/close-black.svg" style="height: 14px; width: 14px" alt="Hide filter" />
</span>
</div>
</div>
</div>
<!-- Editable Filter - End -->
</div>
<!-- Filter - End -->
<!-- Ids and Editor - Start -->
<div class="documentsTabGridAndEditor">
<div class="documentsContainerWithSplitter" , data-bind="attr: { id: documentContentsContainerId }">
<div class="flexContainer">
<!-- Document Ids - Start -->
<div
class="documentsGridHeaderContainer tabdocuments scrollable"
data-bind="
attr: {
id: documentContentsGridId,
tabindex: documentIds().length <= 0 ? -1 : 0
},
style: { height: dataContentsGridScrollHeight },
event: { keydown: accessibleDocumentList.onKeyDown }"
>
<table id="tabsTable" class="table table-hover can-select dataTable">
<thead id="theadcontent">
<tr>
<th class="documentsGridHeader" data-bind="text: idHeader" tabindex="0"></th>
<!-- ko if: showPartitionKey -->
<!-- ko foreach: partitionKeyPropertyHeaders -->
<th
class="documentsGridHeader documentsGridPartition evenlySpacedHeader"
data-bind="
attr: {
title: $data
},
text: $data"
tabindex="0"
></th>
<!-- /ko -->
<!-- /ko -->
<th
class="refreshColHeader"
role="button"
aria-label="Refresh documents"
data-bind="event: { keydown: onRefreshButtonKeyDown }"
>
<img
class="refreshcol"
src="/refresh-cosmos.svg"
data-bind="click: refreshDocumentsGrid.bind($data, false)"
alt="Refresh documents"
tabindex="0"
/>
</th>
</tr>
</thead>
<tbody id="tbodycontent">
<!-- ko foreach: documentIds -->
<tr
class="pointer accessibleListElement"
data-bind="
click: $data.click,
css: {
gridRowSelected: $parent.selectedDocumentId && $parent.selectedDocumentId() && $parent.selectedDocumentId().rid === $data.rid,
gridRowHighlighted: $parent.accessibleDocumentList.currentItem() && $parent.accessibleDocumentList.currentItem().rid === $data.rid
}"
tabindex="0"
>
<td class="tabdocumentsGridElement"><a data-bind="text: $data.id, attr: { title: $data.id }"></a></td>
<!-- ko if: $data.partitionKeyProperties -->
<!-- ko foreach: $data.stringPartitionKeyValues -->
<td class="tabdocumentsGridElement" data-bind="colspan: $parent.stringPartitionKeyValues.length + 1">
<a data-bind="text: $data, attr: { title: $data }"></a>
</td>
<!-- /ko -->
<!-- /ko -->
</tr>
<!-- /ko -->
</tbody>
</table>
</div>
<div class="loadMore">
<a
role="button"
data-bind="click: loadNextPage.bind($data, false), event: { keypress: onLoadMoreKeyInput }"
tabindex="0"
>Load more</a
>
</div>
<!-- Document Ids - End -->
<!-- Splitter -->
</div>
<div class="splitter ui-resizable-handle ui-resizable-e colResizePointer" id="h_splitter2"></div>
</div>
<div class="documentWaterMark" data-bind="visible: !shouldShowEditor()">
<p><img src="/DocumentWaterMark.svg" alt="Document WaterMark" /></p>
<p class="documentWaterMarkText">Create new or work with existing document(s).</p>
</div>
<!-- Editor - Start -->
<json-editor
class="editorDivContent"
data-bind="visible: shouldShowEditor, css: { mongoDocumentEditor: isPreferredApiMongoDB }"
params="{content: initialDocumentContent, isReadOnly: false,lineNumbers: 'on',ariaLabel: 'Document editor',
updatedContent: selectedDocumentContent}"
></json-editor>
<!-- Editor - End -->
</div>
<!-- Ids and Editor - End -->
</div>

View File

@ -1,152 +0,0 @@
import * as ko from "knockout";
import { DatabaseAccount } from "../../Contracts/DataModels";
import * as ViewModels from "../../Contracts/ViewModels";
import { updateUserContext } from "../../UserContext";
import Explorer from "../Explorer";
import DocumentId from "../Tree/DocumentId";
import DocumentsTab from "./DocumentsTab";
describe("Documents tab", () => {
describe("buildQuery", () => {
it("should generate the right select query for SQL API", () => {
const documentsTab = new DocumentsTab({
partitionKey: null,
documentIds: ko.observableArray<DocumentId>(),
tabKind: ViewModels.CollectionTabKind.Documents,
title: "",
tabPath: "",
});
expect(documentsTab.buildQuery("")).toContain("select");
});
});
describe("showPartitionKey", () => {
const explorer = new Explorer();
const mongoExplorer = new Explorer();
updateUserContext({
databaseAccount: {
properties: {
capabilities: [{ name: "EnableGremlin" }],
},
} as DatabaseAccount,
});
const collectionWithoutPartitionKey = <ViewModels.Collection>(<unknown>{
id: ko.observable<string>("foo"),
database: {
id: ko.observable<string>("foo"),
},
container: explorer,
});
const collectionWithSystemPartitionKey = <ViewModels.Collection>(<unknown>{
id: ko.observable<string>("foo"),
database: {
id: ko.observable<string>("foo"),
},
partitionKey: {
paths: ["/foo"],
kind: "Hash",
version: 2,
systemKey: true,
},
container: explorer,
});
const collectionWithNonSystemPartitionKey = <ViewModels.Collection>(<unknown>{
id: ko.observable<string>("foo"),
database: {
id: ko.observable<string>("foo"),
},
partitionKey: {
paths: ["/foo"],
kind: "Hash",
version: 2,
systemKey: false,
},
container: explorer,
});
const mongoCollectionWithSystemPartitionKey = <ViewModels.Collection>(<unknown>{
id: ko.observable<string>("foo"),
database: {
id: ko.observable<string>("foo"),
},
partitionKey: {
paths: ["/foo"],
kind: "Hash",
version: 2,
systemKey: true,
},
container: mongoExplorer,
});
it("should be false for null or undefined collection", () => {
const documentsTab = new DocumentsTab({
partitionKey: null,
documentIds: ko.observableArray<DocumentId>(),
tabKind: ViewModels.CollectionTabKind.Documents,
title: "",
tabPath: "",
});
expect(documentsTab.showPartitionKey).toBe(false);
});
it("should be false for null or undefined partitionKey", () => {
const documentsTab = new DocumentsTab({
collection: collectionWithoutPartitionKey,
partitionKey: null,
documentIds: ko.observableArray<DocumentId>(),
tabKind: ViewModels.CollectionTabKind.Documents,
title: "",
tabPath: "",
});
expect(documentsTab.showPartitionKey).toBe(false);
});
it("should be true for non-Mongo accounts with system partitionKey", () => {
const documentsTab = new DocumentsTab({
collection: collectionWithSystemPartitionKey,
partitionKey: null,
documentIds: ko.observableArray<DocumentId>(),
tabKind: ViewModels.CollectionTabKind.Documents,
title: "",
tabPath: "",
});
expect(documentsTab.showPartitionKey).toBe(true);
});
it("should be false for Mongo accounts with system partitionKey", () => {
updateUserContext({
apiType: "Mongo",
});
const documentsTab = new DocumentsTab({
collection: mongoCollectionWithSystemPartitionKey,
partitionKey: null,
documentIds: ko.observableArray<DocumentId>(),
tabKind: ViewModels.CollectionTabKind.Documents,
title: "",
tabPath: "",
});
expect(documentsTab.showPartitionKey).toBe(false);
});
it("should be true for non-system partitionKey", () => {
const documentsTab = new DocumentsTab({
collection: collectionWithNonSystemPartitionKey,
partitionKey: null,
documentIds: ko.observableArray<DocumentId>(),
tabKind: ViewModels.CollectionTabKind.Documents,
title: "",
tabPath: "",
});
expect(documentsTab.showPartitionKey).toBe(true);
});
});
});

File diff suppressed because it is too large Load Diff

View File

@ -15,9 +15,9 @@ import { Platform, configContext } from "ConfigContext";
import { CommandButtonComponentProps } from "Explorer/Controls/CommandButton/CommandButtonComponent"; import { CommandButtonComponentProps } from "Explorer/Controls/CommandButton/CommandButtonComponent";
import { useDialog } from "Explorer/Controls/Dialog"; import { useDialog } from "Explorer/Controls/Dialog";
import { EditorReact } from "Explorer/Controls/Editor/EditorReact"; import { EditorReact } from "Explorer/Controls/Editor/EditorReact";
import Explorer from "Explorer/Explorer";
import { useCommandBar } from "Explorer/Menus/CommandBar/CommandBarComponentAdapter"; import { useCommandBar } from "Explorer/Menus/CommandBar/CommandBarComponentAdapter";
import { querySampleDocuments, readSampleDocument } from "Explorer/QueryCopilot/QueryCopilotUtilities"; import { querySampleDocuments, readSampleDocument } from "Explorer/QueryCopilot/QueryCopilotUtilities";
import DocumentsTab from "Explorer/Tabs/DocumentsTab";
import { getPlatformTheme } from "Explorer/Theme/ThemeUtil"; import { getPlatformTheme } from "Explorer/Theme/ThemeUtil";
import { useSelectedNode } from "Explorer/useSelectedNode"; import { useSelectedNode } from "Explorer/useSelectedNode";
import { KeyboardAction } from "KeyboardShortcuts"; import { KeyboardAction } from "KeyboardShortcuts";
@ -31,6 +31,7 @@ import { format } from "react-string-format";
import { CSSProperties } from "styled-components"; import { CSSProperties } from "styled-components";
import DeleteDocumentIcon from "../../../../images/DeleteDocument.svg"; import DeleteDocumentIcon from "../../../../images/DeleteDocument.svg";
import NewDocumentIcon from "../../../../images/NewDocument.svg"; import NewDocumentIcon from "../../../../images/NewDocument.svg";
import UploadIcon from "../../../../images/Upload_16x16.svg";
import DiscardIcon from "../../../../images/discard.svg"; import DiscardIcon from "../../../../images/discard.svg";
import SaveIcon from "../../../../images/save-cosmos.svg"; import SaveIcon from "../../../../images/save-cosmos.svg";
import * as Constants from "../../../Common/Constants"; import * as Constants from "../../../Common/Constants";
@ -228,6 +229,25 @@ type ButtonsDependencies = {
onDeleteExistingDocumentsClick: UiKeyboardEvent; onDeleteExistingDocumentsClick: UiKeyboardEvent;
}; };
const createUploadButton = (container: Explorer): CommandButtonComponentProps => {
const label = "Upload Item";
return {
id: "uploadItemBtn",
iconSrc: UploadIcon,
iconAlt: label,
onCommandClick: () => {
const selectedCollection: ViewModels.Collection = useSelectedNode.getState().findSelectedCollection();
selectedCollection && container.openUploadItemsPanePane();
},
commandButtonLabel: label,
ariaLabel: label,
hasPopup: true,
disabled:
useSelectedNode.getState().isDatabaseNodeOrNoneSelected() ||
useSelectedNode.getState().isQueryCopilotCollectionSelected(),
};
};
const getTabsButtons = ({ const getTabsButtons = ({
_collection, _collection,
selectedRows, selectedRows,
@ -344,7 +364,7 @@ const getTabsButtons = ({
} }
if (!isPreferredApiMongoDB) { if (!isPreferredApiMongoDB) {
buttons.push(DocumentsTab._createUploadButton(_collection.container)); buttons.push(createUploadButton(_collection.container));
} }
return buttons; return buttons;
@ -1671,12 +1691,12 @@ const DocumentsTabComponent: React.FunctionComponent<{
<div <div
className="tab-pane active" className="tab-pane active"
/* data-bind=" /* data-bind="
setTemplateReady: true, setTemplateReady: true,
attr:{ attr:{
id: tabId id: tabId
}, },
visible: isActive" visible: isActive"
*/ */
role="tabpanel" role="tabpanel"
style={{ display: "flex" }} style={{ display: "flex" }}
> >
@ -1769,9 +1789,9 @@ const DocumentsTabComponent: React.FunctionComponent<{
onClick={() => refreshDocumentsGrid(true)} onClick={() => refreshDocumentsGrid(true)}
disabled={!applyFilterButton.enabled} disabled={!applyFilterButton.enabled}
/* data-bind=" /* data-bind="
click: refreshDocumentsGrid.bind($data, true), click: refreshDocumentsGrid.bind($data, true),
enable: applyFilterButton.enabled" enable: applyFilterButton.enabled"
*/ */
aria-label="Apply filter" aria-label="Apply filter"
tabIndex={0} tabIndex={0}
> >
@ -1784,9 +1804,9 @@ const DocumentsTabComponent: React.FunctionComponent<{
style={filterButtonStyle} style={filterButtonStyle}
appearance="primary" appearance="primary"
/* data-bind=" /* data-bind="
visible: !isPreferredApiMongoDB && isExecuting, visible: !isPreferredApiMongoDB && isExecuting,
click: onAbortQueryClick" click: onAbortQueryClick"
*/ */
aria-label="Cancel Query" aria-label="Cancel Query"
onClick={() => queryAbortController.abort()} onClick={() => queryAbortController.abort()}
tabIndex={0} tabIndex={0}

View File

@ -1,320 +0,0 @@
import { PartitionKey, PartitionKeyDefinition } from "@azure/cosmos";
import { extractPartitionKeyValues } from "Utils/QueryUtils";
import * as ko from "knockout";
import Q from "q";
import * as Constants from "../../Common/Constants";
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
import * as Logger from "../../Common/Logger";
import {
createDocument,
deleteDocument,
queryDocuments,
readDocument,
updateDocument,
} from "../../Common/MongoProxyClient";
import MongoUtility from "../../Common/MongoUtility";
import * as DataModels from "../../Contracts/DataModels";
import * as ViewModels from "../../Contracts/ViewModels";
import { Action } from "../../Shared/Telemetry/TelemetryConstants";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import { useDialog } from "../Controls/Dialog";
import DocumentId from "../Tree/DocumentId";
import ObjectId from "../Tree/ObjectId";
import DocumentsTab from "./DocumentsTab";
export default class MongoDocumentsTab extends DocumentsTab {
public collection: ViewModels.Collection;
private continuationToken: string;
constructor(options: ViewModels.DocumentsTabOptions) {
super(options);
this.lastFilterContents = ko.observableArray<string>(['{"id":"foo"}', "{ qty: { $gte: 20 } }"]);
this.partitionKeyProperties = this.partitionKeyProperties?.map((partitionKeyProperty, i) => {
if (partitionKeyProperty && ~partitionKeyProperty.indexOf(`"`)) {
partitionKeyProperty = partitionKeyProperty.replace(/["]+/g, "");
}
if (partitionKeyProperty && partitionKeyProperty.indexOf("$v") > -1) {
// From $v.shard.$v.key.$v > shard.key
partitionKeyProperty = partitionKeyProperty.replace(/.\$v/g, "").replace(/\$v./g, "");
this.partitionKeyPropertyHeaders[i] = "/" + partitionKeyProperty;
}
return partitionKeyProperty;
});
this.isFilterExpanded = ko.observable<boolean>(true);
super.buildCommandBarOptions.bind(this);
super.buildCommandBarOptions();
}
public onSaveNewDocumentClick = (): Promise<any> => {
const documentContent = JSON.parse(this.selectedDocumentContent());
this.displayedError("");
const startKey: number = TelemetryProcessor.traceStart(Action.CreateDocument, {
dataExplorerArea: Constants.Areas.Tab,
tabTitle: this.tabTitle(),
});
const partitionKeyProperty = this.partitionKeyProperties?.[0];
if (partitionKeyProperty !== "_id" && !this._hasShardKeySpecified(documentContent)) {
const message = `The document is lacking the shard property: ${partitionKeyProperty}`;
this.displayedError(message);
let that = this;
setTimeout(() => {
that.displayedError("");
}, Constants.ClientDefaults.errorNotificationTimeoutMs);
this.isExecutionError(true);
TelemetryProcessor.traceFailure(
Action.CreateDocument,
{
dataExplorerArea: Constants.Areas.Tab,
tabTitle: this.tabTitle(),
error: message,
},
startKey,
);
Logger.logError("Failed to save new document: Document shard key not defined", "MongoDocumentsTab");
throw new Error("Document without shard key");
}
this.isExecutionError(false);
this.isExecuting(true);
return createDocument(
this.collection.databaseId,
this.collection,
this.partitionKeyProperties?.[0],
documentContent,
)
.then(
(savedDocument: any) => {
const partitionKeyArray: PartitionKey[] = extractPartitionKeyValues(
savedDocument,
this._getPartitionKeyDefinition() as PartitionKeyDefinition,
);
let id = new ObjectId(this, savedDocument, partitionKeyArray);
let ids = this.documentIds();
ids.push(id);
delete savedDocument._self;
let value: string = this.renderObjectForEditor(savedDocument || {}, null, 4);
this.selectedDocumentContent.setBaseline(value);
this.selectedDocumentId(id);
this.documentIds(ids);
this.editorState(ViewModels.DocumentExplorerState.exisitingDocumentNoEdits);
TelemetryProcessor.traceSuccess(
Action.CreateDocument,
{
dataExplorerArea: Constants.Areas.Tab,
tabTitle: this.tabTitle(),
},
startKey,
);
},
(error) => {
this.isExecutionError(true);
const errorMessage = getErrorMessage(error);
useDialog.getState().showOkModalDialog("Create document failed", errorMessage);
TelemetryProcessor.traceFailure(
Action.CreateDocument,
{
dataExplorerArea: Constants.Areas.Tab,
tabTitle: this.tabTitle(),
error: errorMessage,
errorStack: getErrorStack(error),
},
startKey,
);
},
)
.finally(() => this.isExecuting(false));
};
public onSaveExistingDocumentClick = (): Promise<any> => {
const selectedDocumentId = this.selectedDocumentId();
const documentContent = this.selectedDocumentContent();
this.isExecutionError(false);
this.isExecuting(true);
const startKey: number = TelemetryProcessor.traceStart(Action.UpdateDocument, {
dataExplorerArea: Constants.Areas.Tab,
tabTitle: this.tabTitle(),
});
return updateDocument(this.collection.databaseId, this.collection, selectedDocumentId, documentContent)
.then(
(updatedDocument: any) => {
let value: string = this.renderObjectForEditor(updatedDocument || {}, null, 4);
this.selectedDocumentContent.setBaseline(value);
this.documentIds().forEach((documentId: DocumentId) => {
if (documentId.rid === updatedDocument._rid) {
const partitionKeyArray: PartitionKey[] = extractPartitionKeyValues(
updatedDocument,
this._getPartitionKeyDefinition() as PartitionKeyDefinition,
);
const id = new ObjectId(this, updatedDocument, partitionKeyArray);
documentId.id(id.id());
}
});
this.editorState(ViewModels.DocumentExplorerState.exisitingDocumentNoEdits);
TelemetryProcessor.traceSuccess(
Action.UpdateDocument,
{
dataExplorerArea: Constants.Areas.Tab,
tabTitle: this.tabTitle(),
},
startKey,
);
},
(error) => {
this.isExecutionError(true);
const errorMessage = getErrorMessage(error);
useDialog.getState().showOkModalDialog("Update document failed", errorMessage);
TelemetryProcessor.traceFailure(
Action.UpdateDocument,
{
dataExplorerArea: Constants.Areas.Tab,
tabTitle: this.tabTitle(),
error: errorMessage,
errorStack: getErrorStack(error),
},
startKey,
);
},
)
.finally(() => this.isExecuting(false));
};
public buildQuery(filter: string): string {
return filter || "{}";
}
public async selectDocument(documentId: DocumentId): Promise<void> {
this.selectedDocumentId(documentId);
const content = await readDocument(this.collection.databaseId, this.collection, documentId);
this.initDocumentEditor(documentId, content);
}
public loadNextPage(): Q.Promise<any> {
this.isExecuting(true);
this.isExecutionError(false);
const filter: string = this.filterContent().trim();
const query: string = this.buildQuery(filter);
return Q(queryDocuments(this.collection.databaseId, this.collection, true, query, this.continuationToken))
.then(
({ continuationToken, documents }) => {
this.continuationToken = continuationToken;
let currentDocuments = this.documentIds();
const currentDocumentsRids = currentDocuments.map((currentDocument) => currentDocument.rid);
const nextDocumentIds = documents
.filter((d: any) => {
return currentDocumentsRids.indexOf(d._rid) < 0;
})
.map((rawDocument: any) => {
const partitionKeyValue = rawDocument._partitionKeyValue;
return new DocumentId(this, rawDocument, [partitionKeyValue]);
});
const merged = currentDocuments.concat(nextDocumentIds);
this.documentIds(merged);
currentDocuments = this.documentIds();
if (this.filterContent().length > 0 && currentDocuments.length > 0) {
currentDocuments[0].click();
} else {
this.selectedDocumentContent("");
this.selectedDocumentId(null);
this.editorState(ViewModels.DocumentExplorerState.noDocumentSelected);
}
if (this.onLoadStartKey != null && this.onLoadStartKey != undefined) {
TelemetryProcessor.traceSuccess(
Action.Tab,
{
databaseName: this.collection.databaseId,
collectionName: this.collection.id(),
dataExplorerArea: Constants.Areas.Tab,
tabTitle: this.tabTitle(),
},
this.onLoadStartKey,
);
this.onLoadStartKey = null;
}
},
(error: any) => {
if (this.onLoadStartKey != null && this.onLoadStartKey != undefined) {
TelemetryProcessor.traceFailure(
Action.Tab,
{
databaseName: this.collection.databaseId,
collectionName: this.collection.id(),
dataExplorerArea: Constants.Areas.Tab,
tabTitle: this.tabTitle(),
error: getErrorMessage(error),
errorStack: getErrorStack(error),
},
this.onLoadStartKey,
);
this.onLoadStartKey = null;
}
},
)
.finally(() => this.isExecuting(false));
}
protected _onEditorContentChange(newContent: string) {
try {
if (
this.editorState() === ViewModels.DocumentExplorerState.newDocumentValid ||
this.editorState() === ViewModels.DocumentExplorerState.newDocumentInvalid
) {
let parsed: any = JSON.parse(newContent);
}
// Mongo uses BSON format for _id, trying to parse it as JSON blocks normal flow in an edit
this.onValidDocumentEdit();
} catch (e) {
this.onInvalidDocumentEdit();
}
}
/** Renders a Javascript object to be displayed inside Monaco Editor */
public renderObjectForEditor(value: any, replacer: any, space: string | number): string {
return MongoUtility.tojson(value, null, false);
}
private _hasShardKeySpecified(document: any): boolean {
return Boolean(extractPartitionKeyValues(document, this._getPartitionKeyDefinition() as PartitionKeyDefinition));
}
private _getPartitionKeyDefinition(): DataModels.PartitionKey {
let partitionKey: DataModels.PartitionKey = this.partitionKey;
if (
this.partitionKey &&
this.partitionKey.paths &&
this.partitionKey.paths.length &&
this.partitionKey.paths.length > 0 &&
this.partitionKey.paths[0].indexOf("$v") > -1
) {
// Convert BsonSchema2 to /path format
partitionKey = {
kind: partitionKey.kind,
paths: ["/" + this.partitionKeyProperties?.[0].replace(/\./g, "/")],
version: partitionKey.version,
};
}
return partitionKey;
}
protected __deleteDocument(documentId: DocumentId): Promise<void> {
return deleteDocument(this.collection.databaseId, this.collection, documentId);
}
}

View File

@ -1,17 +1,17 @@
import { DocumentsTabV2 } from "Explorer/Tabs/DocumentsTabV2/DocumentsTabV2";
import * as ko from "knockout"; import * as ko from "knockout";
import * as ViewModels from "../../Contracts/ViewModels"; import * as ViewModels from "../../Contracts/ViewModels";
import { useTabs } from "../../hooks/useTabs";
import { updateUserContext } from "../../UserContext"; import { updateUserContext } from "../../UserContext";
import { useTabs } from "../../hooks/useTabs";
import { container } from "../Controls/Settings/TestUtils"; import { container } from "../Controls/Settings/TestUtils";
import DocumentId from "../Tree/DocumentId"; import DocumentId from "../Tree/DocumentId";
import DocumentsTab from "./DocumentsTab";
import { NewQueryTab } from "./QueryTab/QueryTab"; import { NewQueryTab } from "./QueryTab/QueryTab";
describe("useTabs tests", () => { describe("useTabs tests", () => {
let database: ViewModels.Database; let database: ViewModels.Database;
let collection: ViewModels.Collection; let collection: ViewModels.Collection;
let queryTab: NewQueryTab; let queryTab: NewQueryTab;
let documentsTab: DocumentsTab; let documentsTab: DocumentsTabV2;
beforeEach(() => { beforeEach(() => {
updateUserContext({ updateUserContext({
@ -56,7 +56,7 @@ describe("useTabs tests", () => {
}, },
); );
documentsTab = new DocumentsTab({ documentsTab = new DocumentsTabV2({
partitionKey: undefined, partitionKey: undefined,
documentIds: ko.observableArray<DocumentId>(), documentIds: ko.observableArray<DocumentId>(),
tabKind: ViewModels.CollectionTabKind.Documents, tabKind: ViewModels.CollectionTabKind.Documents,

View File

@ -1,9 +1,8 @@
import * as ko from "knockout"; import * as ko from "knockout";
import DocumentId from "./DocumentId"; import DocumentId, { IDocumentIdContainer } from "./DocumentId";
import DocumentsTab from "../Tabs/DocumentsTab";
export default class ObjectId extends DocumentId { export default class ObjectId extends DocumentId {
constructor(container: DocumentsTab, data: any, partitionKeyValue: any) { constructor(container: IDocumentIdContainer, data: any, partitionKeyValue: any) {
super(container, data, partitionKeyValue); super(container, data, partitionKeyValue);
if (typeof data._id === "object") { if (typeof data._id === "object") {
this.id = ko.observable(data._id[Object.keys(data._id)[0]]); this.id = ko.observable(data._id[Object.keys(data._id)[0]]);