diff --git a/sdk/cosmosdb/cosmos/CHANGELOG.md b/sdk/cosmosdb/cosmos/CHANGELOG.md index 6a31f4605946..008d521794fb 100644 --- a/sdk/cosmosdb/cosmos/CHANGELOG.md +++ b/sdk/cosmosdb/cosmos/CHANGELOG.md @@ -2,6 +2,7 @@ ## 3.9.3 (Unreleased) +- BUGFIX: Fixes bulk operations with top level partitionKey values that are undefined or null. ## 3.9.2 (2020-09-16) diff --git a/sdk/cosmosdb/cosmos/src/utils/batch.ts b/sdk/cosmosdb/cosmos/src/utils/batch.ts index 15274fa93725..122cbd50c179 100644 --- a/sdk/cosmosdb/cosmos/src/utils/batch.ts +++ b/sdk/cosmosdb/cosmos/src/utils/batch.ts @@ -129,12 +129,19 @@ export function hasResource( export function getPartitionKeyToHash(operation: Operation, partitionProperty: string) { const toHashKey = hasResource(operation) ? (operation.resourceBody as any)[partitionProperty] - : operation.partitionKey.replace(/[\[\]\"\']/g, ""); + : (operation.partitionKey && operation.partitionKey.replace(/[[\]"']/g, "")) || + operation.partitionKey; // We check for empty object since replace will stringify the value // The second check avoids cases where the partitionKey value is actually the string '{}' if (toHashKey === "{}" && operation.partitionKey === "[{}]") { return {}; } + if (toHashKey === "null" && operation.partitionKey === "[null]") { + return null; + } + if (toHashKey === "0" && operation.partitionKey === "[0]") { + return 0; + } return toHashKey; } @@ -151,7 +158,7 @@ export function decorateOperation( operation.resourceBody.id = uuid(); } } - if (operation.partitionKey) { + if ("partitionKey" in operation) { const extracted = extractPartitionKey(operation, { paths: ["/partitionKey"] }); return { ...operation, partitionKey: JSON.stringify(extracted) } as Operation; } else if ( diff --git a/sdk/cosmosdb/cosmos/test/functional/item.spec.ts b/sdk/cosmosdb/cosmos/test/functional/item.spec.ts index 0d31df9373ed..e945b83bca71 100644 --- a/sdk/cosmosdb/cosmos/test/functional/item.spec.ts +++ b/sdk/cosmosdb/cosmos/test/functional/item.spec.ts @@ -441,6 +441,45 @@ describe("bulk item operations", function() { const response = await v2Container.items.bulk(operations); assert.equal(response[0].statusCode, 201); }); + it("handles operations with null, undefined, and 0 partition keys", async function() { + const item1Id = addEntropy("item1"); + const item2Id = addEntropy("item2"); + const item3Id = addEntropy("item2"); + await v2Container.items.create({ + id: item1Id, + key: null, + class: "2010" + }); + await v2Container.items.create({ + id: item2Id, + key: 0 + }); + await v2Container.items.create({ + id: item3Id, + key: undefined + }); + const operations: OperationInput[] = [ + { + operationType: BulkOperationType.Read, + id: item1Id, + partitionKey: null + }, + { + operationType: BulkOperationType.Read, + id: item2Id, + partitionKey: 0 + }, + { + operationType: BulkOperationType.Read, + id: item3Id, + partitionKey: undefined + } + ]; + const response = await v2Container.items.bulk(operations); + assert.equal(response[0].statusCode, 200); + assert.equal(response[1].statusCode, 200); + assert.equal(response[2].statusCode, 200); + }); }); describe("v2 single partition container", async function() { let container: Container;