UNPKG

@azure/cosmos

Version:
215 lines • 9.02 kB
// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. import { extractPartitionKeys, undefinedPartitionKey } from "../extractPartitionKey.js"; import { NonePartitionKeyLiteral, convertToInternalPartitionKey } from "../documents/index.js"; import { assertNotUndefined } from "./typeChecks.js"; import { bodyFromData } from "../request/request.js"; import { Constants } from "../common/constants.js"; import { randomUUID } from "@azure/core-util"; export function isKeyInRange(min, max, key) { const isAfterMinInclusive = key.localeCompare(min) >= 0; const isBeforeMax = key.localeCompare(max) < 0; return isAfterMinInclusive && isBeforeMax; } export const BulkOperationType = { Create: "Create", Upsert: "Upsert", Read: "Read", Delete: "Delete", Replace: "Replace", Patch: "Patch", }; export function hasResource(operation) { return (operation.operationType !== "Patch" && operation.resourceBody !== undefined); } /** * Maps OperationInput to Operation by * - generating Ids if needed. * - choosing partitionKey which can be used to choose which batch this * operation should be part of. The order is - * 1. If the operationInput itself has partitionKey field set it is used. * 2. Other wise for create/replace/upsert it is extracted from resource body. * 3. For read/delete/patch type operations undefined partitionKey is used. * - Here one nuance is that, the partitionKey field inside Operation needs to * be serialized as a JSON string. * @param operationInput - OperationInput * @param definition - PartitionKeyDefinition * @param options - RequestOptions * @returns */ export function prepareOperations(operationInput, definition, options = {}) { populateIdsIfNeeded(operationInput, options); let partitionKey; if (Object.prototype.hasOwnProperty.call(operationInput, "partitionKey")) { if (operationInput.partitionKey === undefined) { partitionKey = definition.paths.map(() => NonePartitionKeyLiteral); } else { partitionKey = convertToInternalPartitionKey(operationInput.partitionKey); } } else { switch (operationInput.operationType) { case BulkOperationType.Create: case BulkOperationType.Replace: case BulkOperationType.Upsert: partitionKey = assertNotUndefined(extractPartitionKeys(operationInput.resourceBody, definition), "Unexpected undefined Partition Key Found."); break; case BulkOperationType.Read: case BulkOperationType.Delete: case BulkOperationType.Patch: partitionKey = undefinedPartitionKey(definition); break; } } return { operation: { ...operationInput, partitionKey: JSON.stringify(partitionKey) }, partitionKey, }; } /** * For operations requiring Id genrate random uuids. * @param operationInput - OperationInput to be checked. * @param options - RequestOptions */ function populateIdsIfNeeded(operationInput, options) { if (operationInput.operationType === BulkOperationType.Create || operationInput.operationType === BulkOperationType.Upsert) { if ((operationInput.resourceBody.id === undefined || operationInput.resourceBody.id === "") && !options.disableAutomaticIdGeneration) { operationInput.resourceBody.id = randomUUID(); } } } /** * Splits a batch into array of batches based on cumulative size of its operations by making sure * cumulative size of an individual batch is not larger than {@link Constants.DefaultMaxBulkRequestBodySizeInBytes}. * If a single operation itself is larger than {@link Constants.DefaultMaxBulkRequestBodySizeInBytes}, that * operation would be moved into a batch containing only that operation. * @param originalBatch - A batch of operations needed to be checked. * @returns * @hidden */ export function splitBatchBasedOnBodySize(originalBatch) { if (originalBatch?.operations === undefined || originalBatch.operations.length < 1) return []; let currentBatchSize = calculateObjectSizeInBytes(originalBatch.operations[0]); let currentBatch = { ...originalBatch, operations: [originalBatch.operations[0]], indexes: [originalBatch.indexes[0]], }; const processedBatches = []; processedBatches.push(currentBatch); for (let index = 1; index < originalBatch.operations.length; index++) { const operation = originalBatch.operations[index]; const currentOpSize = calculateObjectSizeInBytes(operation); if (currentBatchSize + currentOpSize > Constants.DefaultMaxBulkRequestBodySizeInBytes) { currentBatch = { ...originalBatch, operations: [], indexes: [], }; processedBatches.push(currentBatch); currentBatchSize = 0; } currentBatch.operations.push(operation); currentBatch.indexes.push(originalBatch.indexes[index]); currentBatchSize += currentOpSize; } return processedBatches; } /** * Calculates size of an JSON object in bytes with utf-8 encoding. * @hidden */ export function calculateObjectSizeInBytes(obj) { return new TextEncoder().encode(bodyFromData(obj)).length; } export function decorateBatchOperation(operation, options = {}) { if (operation.operationType === BulkOperationType.Create || operation.operationType === BulkOperationType.Upsert) { if ((operation.resourceBody.id === undefined || operation.resourceBody.id === "") && !options.disableAutomaticIdGeneration) { operation.resourceBody.id = randomUUID(); } } return operation; } export function isSuccessStatusCode(statusCode) { return statusCode >= 200 && statusCode <= 299; } export class TaskCompletionSource { promise; resolveFn; rejectFn; constructor() { this.promise = new Promise((resolve, reject) => { this.resolveFn = resolve; this.rejectFn = reject; }); } get task() { return this.promise; } setResult(value) { this.resolveFn(value); } setException(error) { this.rejectFn(error); } } export async function encryptOperationInput(encryptionProcessor, operation, totalPropertiesEncryptedCount) { if (Object.prototype.hasOwnProperty.call(operation, "partitionKey")) { const partitionKeyInternal = convertToInternalPartitionKey(operation.partitionKey); const { partitionKeyList, encryptedCount } = await encryptionProcessor.getEncryptedPartitionKeyValue(partitionKeyInternal); operation.partitionKey = partitionKeyList; totalPropertiesEncryptedCount += encryptedCount; } switch (operation.operationType) { case BulkOperationType.Create: case BulkOperationType.Upsert: { const { body, propertiesEncryptedCount } = await encryptionProcessor.encrypt(operation.resourceBody); operation.resourceBody = body; totalPropertiesEncryptedCount += propertiesEncryptedCount; break; } case BulkOperationType.Read: case BulkOperationType.Delete: if (await encryptionProcessor.isPathEncrypted("/id")) { operation.id = await encryptionProcessor.getEncryptedId(operation.id); totalPropertiesEncryptedCount++; } break; case BulkOperationType.Replace: { if (await encryptionProcessor.isPathEncrypted("/id")) { operation.id = await encryptionProcessor.getEncryptedId(operation.id); totalPropertiesEncryptedCount++; } const { body, propertiesEncryptedCount } = await encryptionProcessor.encrypt(operation.resourceBody); operation.resourceBody = body; totalPropertiesEncryptedCount += propertiesEncryptedCount; break; } case BulkOperationType.Patch: { if (await encryptionProcessor.isPathEncrypted("/id")) { operation.id = await encryptionProcessor.getEncryptedId(operation.id); totalPropertiesEncryptedCount++; } const body = operation.resourceBody; const patchRequestBody = Array.isArray(body) ? body : body.operations; for (const patchOperation of patchRequestBody) { if ("value" in patchOperation) { if (await encryptionProcessor.isPathEncrypted(patchOperation.path)) { patchOperation.value = await encryptionProcessor.encryptProperty(patchOperation.path, patchOperation.value); totalPropertiesEncryptedCount++; } } } break; } } return { operation, totalPropertiesEncryptedCount }; } //# sourceMappingURL=batch.js.map