UNPKG

ravendb

Version:
438 lines 20.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.BatchOperation = void 0; const index_js_1 = require("../../../Exceptions/index.js"); const Constants_js_1 = require("../../../Constants.js"); const SessionEvents_js_1 = require("../SessionEvents.js"); const CaseInsensitiveKeysMap_js_1 = require("../../../Primitives/CaseInsensitiveKeysMap.js"); const TypeUtil_js_1 = require("../../../Utility/TypeUtil.js"); const ObjectUtil_js_1 = require("../../../Utility/ObjectUtil.js"); const ClusterWideBatchCommand_js_1 = require("../../Commands/Batches/ClusterWideBatchCommand.js"); const SingleNodeBatchCommand_js_1 = require("../../Commands/Batches/SingleNodeBatchCommand.js"); class BatchOperation { _session; constructor(session) { this._session = session; } _entities; _sessionCommandsCount; _allCommandsCount; _onSuccessfulRequest; _modifications; createRequest() { const result = this._session.prepareForSaveChanges(); this._onSuccessfulRequest = result.onSuccess; this._sessionCommandsCount = result.sessionCommands.length; result.sessionCommands.push(...result.deferredCommands); this._session.validateClusterTransaction(result); this._allCommandsCount = result.sessionCommands.length; if (this._allCommandsCount === 0) { return null; } this._session.incrementRequestCount(); this._entities = result.entities; if (this._session.transactionMode === "ClusterWide") { return new ClusterWideBatchCommand_js_1.ClusterWideBatchCommand(this._session.conventions, result.sessionCommands, result.options, this._session.disableAtomicDocumentWritesInClusterWideTransaction); } return new SingleNodeBatchCommand_js_1.SingleNodeBatchCommand(this._session.conventions, result.sessionCommands, result.options); } static _throwOnNullResults() { (0, index_js_1.throwError)("InvalidOperationException", "Received empty response from the server. This is not supposed to happen and is likely a bug."); } setResult(result) { if (!result.results) { BatchOperation._throwOnNullResults(); return; } this._onSuccessfulRequest.clearSessionStateAfterSuccessfulSaveChanges(); if (this._session.transactionMode === "ClusterWide") { if (result.transactionIndex <= 0) { (0, index_js_1.throwError)("ClientVersionMismatchException", "Cluster transaction was send to a node that is not supporting it. " + "So it was executed ONLY on the requested node on " + this._session.requestExecutor.getUrl()); } } const results = result.results; for (let i = 0; i < this._sessionCommandsCount; i++) { const batchResult = results[i]; if (!batchResult) { continue; } const type = getCommandType(batchResult); switch (type) { case "PUT": { this._handlePut(i, batchResult, false); break; } case "ForceRevisionCreation": { this._handleForceRevisionCreation(batchResult); break; } case "DELETE": { this._handleDelete(batchResult); break; } case "CompareExchangePUT": { this._handleCompareExchangePut(batchResult); break; } case "CompareExchangeDELETE": { this._handleCompareExchangeDelete(batchResult); break; } default: { (0, index_js_1.throwError)("InvalidOperationException", `Command '${type}' is not supported.`); } } } for (let i = this._sessionCommandsCount; i < this._allCommandsCount; i++) { const batchResult = result.results[i]; if (!batchResult) { continue; } const type = getCommandType(batchResult); switch (type) { case "PUT": { this._handlePut(i, batchResult, true); break; } case "DELETE": { this._handleDelete(batchResult); break; } case "PATCH": { this._handlePatch(batchResult); break; } case "AttachmentPUT": { this._handleAttachmentPut(batchResult); break; } case "AttachmentDELETE": { this._handleAttachmentDelete(batchResult); break; } case "AttachmentMOVE": { this._handleAttachmentMove(batchResult); break; } case "AttachmentCOPY": { this._handleAttachmentCopy(batchResult); break; } case "CompareExchangePUT": case "CompareExchangeDELETE": case "ForceRevisionCreation": { break; } case "Counters": { this._handleCounters(batchResult); break; } case "TimeSeries": case "TimeSeriesWithIncrements": { //TODO: RavenDB-13474 add to time series cache break; } case "TimeSeriesCopy": { break; } case "BatchPATCH": { break; } default: { (0, index_js_1.throwError)("InvalidOperationException", `Command '${type}' is not supported.`); } } } this._finalizeResults(); } _finalizeResults() { if (!this._modifications) { return; } for (const [id, docInfo] of this._modifications.entries()) { this._applyMetadataModifications(id, docInfo); } } _applyMetadataModifications(id, documentInfo) { documentInfo.metadataInstance = null; documentInfo.metadata = ObjectUtil_js_1.ObjectUtil.deepLiteralClone(documentInfo.metadata); documentInfo.metadata["@change-vector"] = documentInfo.changeVector; const documentCopy = ObjectUtil_js_1.ObjectUtil.deepLiteralClone(documentInfo.document); documentCopy[Constants_js_1.CONSTANTS.Documents.Metadata.KEY] = documentInfo.metadata; documentInfo.document = documentCopy; } _getOrAddModifications(id, documentInfo, applyModifications) { if (!this._modifications) { this._modifications = CaseInsensitiveKeysMap_js_1.CaseInsensitiveKeysMap.create(); } let modifiedDocumentInfo = this._modifications.get(id); if (modifiedDocumentInfo) { if (applyModifications) { this._applyMetadataModifications(id, modifiedDocumentInfo); } } else { modifiedDocumentInfo = documentInfo; this._modifications.set(id, documentInfo); } return modifiedDocumentInfo; } _handleCompareExchangePut(batchResult) { this._handleCompareExchangeInternal("CompareExchangePUT", batchResult); } _handleCompareExchangeDelete(batchResult) { this._handleCompareExchangeInternal("CompareExchangeDELETE", batchResult); } _handleCompareExchangeInternal(commandType, batchResult) { const key = batchResult.key; if (!key) { BatchOperation._throwMissingField(commandType, "Key"); } const index = batchResult.index; if (TypeUtil_js_1.TypeUtil.isNullOrUndefined(index)) { BatchOperation._throwMissingField(commandType, "Index"); } const clusterSession = this._session.clusterSession; clusterSession.updateState(key, index); } _handleAttachmentCopy(batchResult) { this._handleAttachmentPutInternal(batchResult, "AttachmentCOPY", "id", "name", "documentChangeVector"); } _handleAttachmentMove(batchResult) { this._handleAttachmentDeleteInternal(batchResult, "AttachmentMOVE", "id", "name", "documentChangeVector"); this._handleAttachmentPutInternal(batchResult, "AttachmentMOVE", "destinationId", "destinationName", "documentChangeVector"); } _handleAttachmentDelete(batchResult) { this._handleAttachmentDeleteInternal(batchResult, "AttachmentDELETE", Constants_js_1.CONSTANTS.Documents.Metadata.ID, "name", "documentChangeVector"); } _handleAttachmentDeleteInternal(batchResult, type, idFieldName, attachmentNameFieldName, documentChangeVectorFieldName) { const id = BatchOperation._getStringField(batchResult, type, idFieldName); const sessionDocumentInfo = this._session.documentsById.getValue(id); if (!sessionDocumentInfo) { return; } const documentInfo = this._getOrAddModifications(id, sessionDocumentInfo, true); const documentChangeVector = BatchOperation._getStringField(batchResult, type, documentChangeVectorFieldName, false); if (documentChangeVector) { documentInfo.changeVector = documentChangeVector; } const attachmentsJson = documentInfo.metadata["@attachments"]; if (!attachmentsJson || !Object.keys(attachmentsJson).length) { return; } const name = BatchOperation._getStringField(batchResult, type, attachmentNameFieldName); const attachments = []; documentInfo.metadata["@attachments"] = attachments; for (const attachment of attachmentsJson) { const attachmentName = BatchOperation._getStringField(attachment, type, "name"); if (attachmentName === name) { continue; } attachments.push(attachment); } } _handleAttachmentPut(batchResult) { this._handleAttachmentPutInternal(batchResult, "AttachmentPUT", "id", "name", "documentChangeVector"); } _handleAttachmentPutInternal(batchResult, type, idFieldName, attachmentNameFieldName, documentChangeVectorFieldName) { const id = BatchOperation._getStringField(batchResult, type, idFieldName); const sessionDocumentInfo = this._session.documentsById.getValue(id); if (!sessionDocumentInfo) { return; } const documentInfo = this._getOrAddModifications(id, sessionDocumentInfo, false); const documentChangeVector = BatchOperation._getStringField(batchResult, type, documentChangeVectorFieldName, false); if (documentChangeVector) { documentInfo.changeVector = documentChangeVector; } let attachments = documentInfo.metadata["@attachments"]; if (!attachments) { attachments = []; documentInfo.metadata["@attachments"] = attachments; } attachments.push({ changeVector: BatchOperation._getStringField(batchResult, type, "changeVector"), contentType: BatchOperation._getStringField(batchResult, type, "contentType"), hash: BatchOperation._getStringField(batchResult, type, "hash"), name: BatchOperation._getStringField(batchResult, type, "name"), size: BatchOperation._getNumberField(batchResult, type, "size") }); } _handlePatch(batchResult) { const status = batchResult["patchStatus"]; if (!status) { BatchOperation._throwMissingField("PATCH", "PatchStatus"); } switch (status) { case "Created": case "Patched": { const document = batchResult["modifiedDocument"]; if (!document) { return; } const id = BatchOperation._getStringField(batchResult, "PUT", "id"); const sessionDocumentInfo = this._session.documentsById.getValue(id); if (!sessionDocumentInfo) { return; } const documentInfo = this._getOrAddModifications(id, sessionDocumentInfo, true); const changeVector = BatchOperation._getStringField(batchResult, "PATCH", "changeVector"); const lastModified = BatchOperation._getStringField(batchResult, "PATCH", "lastModified"); documentInfo.changeVector = changeVector; documentInfo.metadata[Constants_js_1.CONSTANTS.Documents.Metadata.ID] = id; documentInfo.metadata[Constants_js_1.CONSTANTS.Documents.Metadata.CHANGE_VECTOR] = changeVector; documentInfo.metadata[Constants_js_1.CONSTANTS.Documents.Metadata.LAST_MODIFIED] = lastModified; documentInfo.document = document; this._applyMetadataModifications(id, documentInfo); if (documentInfo.entity) { this._session.entityToJson.populateEntity(documentInfo.entity, id, documentInfo.document); const afterSaveChangesEventArgs = new SessionEvents_js_1.SessionAfterSaveChangesEventArgs(this._session, documentInfo.id, documentInfo.entity); this._session.emit("afterSaveChanges", afterSaveChangesEventArgs); } break; } } } _handleDelete(batchResult) { this._handleDeleteInternal(batchResult, "DELETE"); } _handleDeleteInternal(batchResult, type) { const id = BatchOperation._getStringField(batchResult, type, "id"); const documentInfo = this._session.documentsById.getValue(id); if (!documentInfo) { return; } this._session.documentsById.remove(id); if (documentInfo.entity) { this._session.documentsByEntity.remove(documentInfo.entity); this._session.deletedEntities.remove(documentInfo.entity); } } _handleForceRevisionCreation(batchResult) { // When forcing a revision for a document that does Not have any revisions yet then the HasRevisions flag is added to the document. // In this case we need to update the tracked entities in the session with the document new change-vector. if (!BatchOperation._getBooleanField(batchResult, "ForceRevisionCreation", "revisionCreated")) { // no forced revision was created...nothing to update. return; } const id = BatchOperation._getStringField(batchResult, "ForceRevisionCreation", Constants_js_1.CONSTANTS.Documents.Metadata.ID); const changeVector = BatchOperation._getStringField(batchResult, "ForceRevisionCreation", Constants_js_1.CONSTANTS.Documents.Metadata.CHANGE_VECTOR); const documentInfo = this._session.documentsById.getValue(id); if (!documentInfo) { return; } documentInfo.changeVector = changeVector; this._handleMetadataModifications(documentInfo, batchResult, id, changeVector); const afterSaveChangesEventArgs = new SessionEvents_js_1.SessionAfterSaveChangesEventArgs(this._session, documentInfo.id, documentInfo.entity); this._session.emit("afterSaveChanges", afterSaveChangesEventArgs); } _handlePut(index, batchResult, isDeferred) { let entity = null; let documentInfo = null; if (!isDeferred) { entity = this._entities[index]; documentInfo = this._session.documentsByEntity.get(entity); if (!documentInfo) { return; } } const id = BatchOperation._getStringField(batchResult, "PUT", Constants_js_1.CONSTANTS.Documents.Metadata.ID); const changeVector = BatchOperation._getStringField(batchResult, "PUT", Constants_js_1.CONSTANTS.Documents.Metadata.CHANGE_VECTOR); if (isDeferred) { const sessionDocumentInfo = this._session.documentsById.getValue(id); if (!sessionDocumentInfo) { return; } documentInfo = this._getOrAddModifications(id, sessionDocumentInfo, true); entity = documentInfo.entity; } this._handleMetadataModifications(documentInfo, batchResult, id, changeVector); this._session.documentsById.add(documentInfo); if (entity) { this._session.generateEntityIdOnTheClient.trySetIdentity(entity, id); } const afterSaveChangesEventArgs = new SessionEvents_js_1.SessionAfterSaveChangesEventArgs(this._session, documentInfo.id, documentInfo.entity); this._session.emit("afterSaveChanges", afterSaveChangesEventArgs); } _handleMetadataModifications(documentInfo, batchResult, id, changeVector) { for (const propertyName of Object.keys(batchResult)) { if (propertyName === "type") { continue; } documentInfo.metadata[propertyName] = batchResult[propertyName]; } documentInfo.id = id; documentInfo.changeVector = changeVector; this._applyMetadataModifications(id, documentInfo); } _handleCounters(batchResult) { const docId = BatchOperation._getStringField(batchResult, "Counters", "id"); const countersDetail = batchResult["countersDetail"]; if (!countersDetail) { BatchOperation._throwMissingField("Counters", "CountersDetail"); } const counters = countersDetail["counters"]; if (!counters) { BatchOperation._throwMissingField("Counters", "Counters"); } let cache = this._session.countersByDocId.get(docId); if (!cache) { cache = { gotAll: false, data: CaseInsensitiveKeysMap_js_1.CaseInsensitiveKeysMap.create() }; this._session.countersByDocId.set(docId, cache); } const changeVector = BatchOperation._getStringField(batchResult, "Counters", "documentChangeVector", false); if (changeVector) { const documentInfo = this._session.documentsById.getValue(docId); if (documentInfo) { documentInfo.changeVector = changeVector; } } for (const counter of counters) { const name = counter["counterName"]; const value = counter["totalValue"]; if (name && value) { cache.data.set(name, value); } } } static _getStringField(json, type, fieldName, throwOnMissing = true) { if ((!(fieldName in json) || TypeUtil_js_1.TypeUtil.isNullOrUndefined(json[fieldName])) && throwOnMissing) { BatchOperation._throwMissingField(type, fieldName); } const jsonNode = json[fieldName]; if (jsonNode && !TypeUtil_js_1.TypeUtil.isString(jsonNode)) { (0, index_js_1.throwError)("InvalidOperationException", `Expected response field ${fieldName} to be a string.`); } return jsonNode; } static _getNumberField(json, type, fieldName) { if (!(fieldName in json)) { BatchOperation._throwMissingField(type, fieldName); } const jsonNode = json[fieldName]; return jsonNode; } static _getBooleanField(json, type, fieldName) { if (!(fieldName in json)) { BatchOperation._throwMissingField(type, fieldName); } const jsonNode = json[fieldName]; return jsonNode; } static _throwInvalidValue(arg, fieldName) { (0, index_js_1.throwError)("InvalidOperationException", "'" + arg + "' is not a valid value for field " + fieldName); } static _throwMissingField(type, fieldName) { (0, index_js_1.throwError)("InvalidOperationException", type + " response is invalid. Field '" + fieldName + "' is missing."); } } exports.BatchOperation = BatchOperation; function getCommandType(batchResult) { return batchResult["type"] || "None"; } //# sourceMappingURL=BatchOperation.js.map