DDM in DE for NOSQL (#2224)

* ddm for DE for noSQL

* ddm for DE for noSQL

* ddm for DE for noSQL

* ddm fix for the default case and test fix

* formatting issue

* updated the text change

* added validation errors

---------

Co-authored-by: Sakshi Gupta <sakshig@microsoft.com>
This commit is contained in:
sakshigupta12feb
2025-10-27 19:37:40 +05:30
committed by GitHub
parent ff1eb6a78e
commit 0578910b9e
15 changed files with 1212 additions and 96 deletions

View File

@@ -14,17 +14,30 @@ describe("Collection", () => {
defaultTtl: 1,
indexingPolicy: {} as DataModels.IndexingPolicy,
partitionKey,
_rid: "",
_self: "",
_etag: "",
_rid: "testRid",
_self: "testSelf",
_etag: "testEtag",
_ts: 1,
id: "",
id: "testCollection",
};
};
const generateMockCollectionWithDataModel = (data: DataModels.Collection): Collection => {
const mockContainer = {} as Explorer;
return generateCollection(mockContainer, "abc", data);
const mockContainer = {
isReadOnly: () => false,
isFabricCapable: () => true,
databaseAccount: () => ({
name: () => "testAccount",
id: () => "testAccount",
properties: {
enablePartitionKey: true,
partitionKeyDefinitionVersion: 2,
capabilities: [] as string[],
databaseAccountEndpoint: "test.documents.azure.com",
},
}),
} as unknown as Explorer;
return generateCollection(mockContainer, "testDb", data);
};
describe("Partition key path parsing", () => {
@@ -78,7 +91,7 @@ describe("Collection", () => {
expect(collection.partitionKeyPropertyHeaders[0]).toBe("/somePartitionKey");
});
it("should be null if there is no partition key", () => {
it("should be empty if there is no partition key", () => {
const collectionsDataModel = generateMockCollectionsDataModelWithPartitionKey({
version: 2,
paths: [],
@@ -88,4 +101,103 @@ describe("Collection", () => {
expect(collection.partitionKeyPropertyHeaders.length).toBe(0);
});
});
describe("Collection properties", () => {
let collection: Collection;
const collectionsDataModel = generateMockCollectionsDataModelWithPartitionKey({
paths: ["/id"],
kind: "Hash",
version: 2,
});
beforeEach(() => {
collection = generateMockCollectionWithDataModel(collectionsDataModel);
});
it("should return correct collection id", () => {
expect(collection.id()).toBe("testCollection");
});
it("should return correct rid", () => {
expect(collection.rid).toBe("testRid");
});
it("should return correct self", () => {
expect(collection.self).toBe("testSelf");
});
it("should return correct collection type", () => {
expect(collection.partitionKey).toBeDefined();
});
});
describe("Collection type", () => {
it("should identify large partitioned collection for v2 partition key", () => {
const collectionsDataModel = generateMockCollectionsDataModelWithPartitionKey({
paths: ["/id"],
kind: "Hash",
version: 2,
});
const collection = generateMockCollectionWithDataModel(collectionsDataModel);
expect(collection.partitionKey.version).toBe(2);
expect(collection.partitionKey.paths).toEqual(["/id"]);
});
it("should identify standard partitioned collection for v1 partition key", () => {
const collectionsDataModel = generateMockCollectionsDataModelWithPartitionKey({
paths: ["/id"],
kind: "Hash",
version: 1,
});
const collection = generateMockCollectionWithDataModel(collectionsDataModel);
expect(collection.partitionKey.version).toBe(1);
expect(collection.partitionKey.paths).toEqual(["/id"]);
});
it("should identify collection without partition key", () => {
const collectionsDataModel = generateMockCollectionsDataModelWithPartitionKey({
paths: [],
kind: "Hash",
version: 2,
});
const collection = generateMockCollectionWithDataModel(collectionsDataModel);
expect(collection.partitionKey.paths).toEqual([]);
});
});
describe("Partition key handling", () => {
it("should return correct partition key paths for multiple paths", () => {
const collectionsDataModel = generateMockCollectionsDataModelWithPartitionKey({
paths: ["/id", "/pk"],
kind: "Hash",
version: 2,
});
const collection = generateMockCollectionWithDataModel(collectionsDataModel);
expect(collection.partitionKey.paths).toEqual(["/id", "/pk"]);
expect(collection.partitionKeyProperties).toEqual(["id", "pk"]);
});
it("should handle empty partition key paths", () => {
const collectionsDataModel = generateMockCollectionsDataModelWithPartitionKey({
paths: [],
kind: "Hash",
version: 2,
});
const collection = generateMockCollectionWithDataModel(collectionsDataModel);
expect(collection.partitionKey.paths).toEqual([]);
expect(collection.partitionKeyProperties).toEqual([]);
});
it("should handle undefined partition key", () => {
const collectionData = generateMockCollectionsDataModelWithPartitionKey({
paths: ["/id"],
kind: "Hash",
version: 2,
});
delete collectionData.partitionKey;
const collection = generateMockCollectionWithDataModel(collectionData);
expect(collection.partitionKey).toBeUndefined();
expect(collection.partitionKeyProperties).toEqual([]);
});
});
});

View File

@@ -67,6 +67,7 @@ export default class Collection implements ViewModels.Collection {
public computedProperties: ko.Observable<DataModels.ComputedProperties>;
public materializedViews: ko.Observable<DataModels.MaterializedView[]>;
public materializedViewDefinition: ko.Observable<DataModels.MaterializedViewDefinition>;
public dataMaskingPolicy: ko.Observable<DataModels.DataMaskingPolicy>;
public offer: ko.Observable<DataModels.Offer>;
public conflictResolutionPolicy: ko.Observable<DataModels.ConflictResolutionPolicy>;
@@ -136,25 +137,36 @@ export default class Collection implements ViewModels.Collection {
this.materializedViews = ko.observable(data.materializedViews);
this.materializedViewDefinition = ko.observable(data.materializedViewDefinition);
this.partitionKeyPropertyHeaders = this.partitionKey?.paths;
this.partitionKeyProperties = this.partitionKeyPropertyHeaders?.map((partitionKeyPropertyHeader, i) => {
// TODO fix this to only replace non-excaped single quotes
let partitionKeyProperty = partitionKeyPropertyHeader.replace(/[/]+/g, ".").substring(1).replace(/[']+/g, "");
// Initialize dataMaskingPolicy with default values if not present
const defaultDataMaskingPolicy: DataModels.DataMaskingPolicy = {
includedPaths: Array<{ path: string; strategy: string; startPosition: number; length: number }>(),
excludedPaths: Array<string>(),
policyFormatVersion: 2,
isPolicyEnabled: false,
};
const observablePolicy = ko.observable(data.dataMaskingPolicy || defaultDataMaskingPolicy);
observablePolicy.subscribe(() => {});
this.dataMaskingPolicy = observablePolicy;
this.partitionKeyPropertyHeaders = this.partitionKey?.paths || [];
this.partitionKeyProperties =
this.partitionKeyPropertyHeaders?.map((partitionKeyPropertyHeader, i) => {
// TODO fix this to only replace non-excaped single quotes
let partitionKeyProperty = partitionKeyPropertyHeader.replace(/[/]+/g, ".").substring(1).replace(/[']+/g, "");
if (userContext.apiType === "Mongo" && partitionKeyProperty) {
if (~partitionKeyProperty.indexOf(`"`)) {
partitionKeyProperty = partitionKeyProperty.replace(/["]+/g, "");
if (userContext.apiType === "Mongo" && partitionKeyProperty) {
if (~partitionKeyProperty.indexOf(`"`)) {
partitionKeyProperty = partitionKeyProperty.replace(/["]+/g, "");
}
// TODO #10738269 : Add this logic in a derived class for Mongo
if (partitionKeyProperty.indexOf("$v") > -1) {
// From $v.shard.$v.key.$v > shard.key
partitionKeyProperty = partitionKeyProperty.replace(/.\$v/g, "").replace(/\$v./g, "");
this.partitionKeyPropertyHeaders[i] = "/" + partitionKeyProperty;
}
}
// TODO #10738269 : Add this logic in a derived class for Mongo
if (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;
});
return partitionKeyProperty;
}) || [];
this.documentIds = ko.observableArray<DocumentId>([]);
this.isCollectionExpanded = ko.observable<boolean>(false);
@@ -163,7 +175,6 @@ export default class Collection implements ViewModels.Collection {
this.documentsFocused = ko.observable<boolean>();
this.documentsFocused.subscribe((focus) => {
console.log("Focus set on Documents: " + focus);
this.focusedSubnodeKind(ViewModels.CollectionTabKind.Documents);
});