@nextage/nx-frame-be 1.0.36 → 1.0.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -243,6 +243,26 @@ export declare class BaseModel<TAttrs, TDoc extends BaseMongoDoc, TMongoModel ex
|
|
|
243
243
|
* @param {*} options
|
|
244
244
|
*/
|
|
245
245
|
updateManyNative(filter: FilterParams, update: mongoose.UpdateQuery<TMongoModel>, options?: mongoose.mongo.UpdateOptions): Promise<mongoose.mongo.UpdateResult<Document>>;
|
|
246
|
+
/**
|
|
247
|
+
* Execute bulkWrite injecting version handling on each operation, so native bulk
|
|
248
|
+
* paths keep the same versioning guarantees as the Mongoose query hooks:
|
|
249
|
+
* - insertOne -> set versionKey to 0 when absent (matches save() baseline)
|
|
250
|
+
* - updateOne/updateMany -> add $inc:{version:1} unless the update already touches version
|
|
251
|
+
*
|
|
252
|
+
* Upsert ops that do not touch version fall back to $inc (an upsert insert lands at
|
|
253
|
+
* version 1). Other op kinds (replaceOne, deleteOne, deleteMany) pass through unchanged.
|
|
254
|
+
*
|
|
255
|
+
* @param {*} bulkOps
|
|
256
|
+
* @param {*} options
|
|
257
|
+
*/
|
|
258
|
+
bulkWriteVersioned(bulkOps: mongoose.AnyBulkWriteOperation<TDoc>[], options?: mongoose.mongo.BulkWriteOptions): Promise<mongoose.mongo.BulkWriteResult>;
|
|
259
|
+
/**
|
|
260
|
+
* Inject the version key into a single bulkWrite operation (see bulkWriteVersioned).
|
|
261
|
+
*
|
|
262
|
+
* @param {*} op
|
|
263
|
+
* @param {*} versionKey
|
|
264
|
+
*/
|
|
265
|
+
private applyVersionToBulkOp;
|
|
246
266
|
/**
|
|
247
267
|
* Update document using Mongoose wrapper method
|
|
248
268
|
*
|
|
@@ -19,8 +19,12 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
19
19
|
}
|
|
20
20
|
return t;
|
|
21
21
|
};
|
|
22
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
23
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
24
|
+
};
|
|
22
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
26
|
exports.BaseModel = void 0;
|
|
27
|
+
const mongoose_1 = __importDefault(require("mongoose"));
|
|
24
28
|
const events_1 = require("events");
|
|
25
29
|
const utils_1 = require("../utils");
|
|
26
30
|
const constants_1 = require("../constants");
|
|
@@ -456,19 +460,35 @@ class BaseModel extends events_1.EventEmitter {
|
|
|
456
460
|
item = yield this.beforeUpdate(item, context);
|
|
457
461
|
//const res = await this.mgModel.findByIdAndUpdate(id, { $set: item }, { new: true });
|
|
458
462
|
//const itemDoc = this.mgModel.build(Object.assign({ id, item }));
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
463
|
+
// read-modify-write with optimistic concurrency: on a VersionError (a concurrent
|
|
464
|
+
// writer bumped the version between get() and save()) re-read the fresh document,
|
|
465
|
+
// re-apply the update payload and retry a bounded number of times.
|
|
466
|
+
const maxAttempts = 3;
|
|
467
|
+
let res;
|
|
468
|
+
for (let attempt = 1;; attempt++) {
|
|
469
|
+
const itemDoc = yield this.get(id, context);
|
|
470
|
+
if (!itemDoc)
|
|
471
|
+
throw new errors_1.AppError('messages.notFound');
|
|
472
|
+
let toApply = item;
|
|
473
|
+
// complete merge with the freshly read document
|
|
474
|
+
if (mergeItem) {
|
|
475
|
+
// item = merge (itemDoc.toObject(), item); // Merge with existing doc
|
|
476
|
+
toApply = (0, utils_1.merge)(itemDoc.toObject(), item, (objValue, srcValue) => {
|
|
477
|
+
if (Array.isArray(objValue))
|
|
478
|
+
return srcValue; // sovrascrivi invece di mergiare
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
itemDoc.set(toApply);
|
|
482
|
+
try {
|
|
483
|
+
res = yield itemDoc.save();
|
|
484
|
+
break;
|
|
485
|
+
}
|
|
486
|
+
catch (err) {
|
|
487
|
+
if (err instanceof mongoose_1.default.Error.VersionError && attempt < maxAttempts)
|
|
488
|
+
continue;
|
|
489
|
+
throw err;
|
|
490
|
+
}
|
|
469
491
|
}
|
|
470
|
-
itemDoc === null || itemDoc === void 0 ? void 0 : itemDoc.set(item);
|
|
471
|
-
const res = yield itemDoc.save();
|
|
472
492
|
if (context)
|
|
473
493
|
context.evtItem = res;
|
|
474
494
|
this.publishCRUDEvent({ type: enums_1.ModelCrudEventType.update, data: { uid: id, model: this.name } }, context);
|
|
@@ -506,6 +526,53 @@ class BaseModel extends events_1.EventEmitter {
|
|
|
506
526
|
updateManyNative(filter, update, options) {
|
|
507
527
|
return this.mgModel.collection.updateMany(filter, update, options);
|
|
508
528
|
}
|
|
529
|
+
/**
|
|
530
|
+
* Execute bulkWrite injecting version handling on each operation, so native bulk
|
|
531
|
+
* paths keep the same versioning guarantees as the Mongoose query hooks:
|
|
532
|
+
* - insertOne -> set versionKey to 0 when absent (matches save() baseline)
|
|
533
|
+
* - updateOne/updateMany -> add $inc:{version:1} unless the update already touches version
|
|
534
|
+
*
|
|
535
|
+
* Upsert ops that do not touch version fall back to $inc (an upsert insert lands at
|
|
536
|
+
* version 1). Other op kinds (replaceOne, deleteOne, deleteMany) pass through unchanged.
|
|
537
|
+
*
|
|
538
|
+
* @param {*} bulkOps
|
|
539
|
+
* @param {*} options
|
|
540
|
+
*/
|
|
541
|
+
bulkWriteVersioned(bulkOps, options) {
|
|
542
|
+
const versionKey = this.mgModel.schema.get('versionKey') || 'version';
|
|
543
|
+
const versioned = bulkOps.map(op => this.applyVersionToBulkOp(op, versionKey));
|
|
544
|
+
return this.mgModel.bulkWrite(versioned, options !== null && options !== void 0 ? options : {});
|
|
545
|
+
}
|
|
546
|
+
/**
|
|
547
|
+
* Inject the version key into a single bulkWrite operation (see bulkWriteVersioned).
|
|
548
|
+
*
|
|
549
|
+
* @param {*} op
|
|
550
|
+
* @param {*} versionKey
|
|
551
|
+
*/
|
|
552
|
+
applyVersionToBulkOp(op, versionKey) {
|
|
553
|
+
var _a, _b;
|
|
554
|
+
const anyOp = op;
|
|
555
|
+
if ((_a = anyOp.insertOne) === null || _a === void 0 ? void 0 : _a.document) {
|
|
556
|
+
const doc = anyOp.insertOne.document;
|
|
557
|
+
if (doc[versionKey] === undefined)
|
|
558
|
+
doc[versionKey] = 0;
|
|
559
|
+
return op;
|
|
560
|
+
}
|
|
561
|
+
const writeOp = (_b = anyOp.updateOne) !== null && _b !== void 0 ? _b : anyOp.updateMany;
|
|
562
|
+
if (!writeOp)
|
|
563
|
+
return op;
|
|
564
|
+
const update = writeOp.update;
|
|
565
|
+
// update pipelines (arrays) are left untouched
|
|
566
|
+
if (!update || Array.isArray(update))
|
|
567
|
+
return op;
|
|
568
|
+
const nextUpdate = update;
|
|
569
|
+
const versionOperators = [nextUpdate, nextUpdate.$set, nextUpdate.$unset, nextUpdate.$inc];
|
|
570
|
+
const touchesVersion = versionOperators.some(operator => !!operator && Object.keys(operator).includes(versionKey));
|
|
571
|
+
if (touchesVersion)
|
|
572
|
+
return op;
|
|
573
|
+
nextUpdate.$inc = nextUpdate.$inc ? Object.assign(Object.assign({}, nextUpdate.$inc), { [versionKey]: 1 }) : { [versionKey]: 1 };
|
|
574
|
+
return op;
|
|
575
|
+
}
|
|
509
576
|
/**
|
|
510
577
|
* Update document using Mongoose wrapper method
|
|
511
578
|
*
|
|
@@ -54,14 +54,16 @@ export declare function loadModel(model: BaseModel<any, any, any>): void;
|
|
|
54
54
|
* @param { name, modelName, schema, collection, mergeSchema } param
|
|
55
55
|
* @param load
|
|
56
56
|
*/
|
|
57
|
-
export declare function createModel<TAttrs, TDoc extends BaseMongoDoc, TMongoModel extends BaseMongoModel<TAttrs, TDoc>, TNxModel>({ name, classDef, modelName, modelParams, collection, schema, mergeSchema }: ModelDef<TDoc, TMongoModel, TNxModel>, load: boolean, logger?: any): TNxModel;
|
|
57
|
+
export declare function createModel<TAttrs, TDoc extends BaseMongoDoc, TMongoModel extends BaseMongoModel<TAttrs, TDoc>, TNxModel>({ name, classDef, modelName, modelParams, collection, schema, mergeSchema, incrementOnSave }: ModelDef<TDoc, TMongoModel, TNxModel>, load: boolean, logger?: any): TNxModel;
|
|
58
58
|
/**
|
|
59
59
|
*
|
|
60
60
|
* @param {*} modelName
|
|
61
61
|
* @param {*} schema
|
|
62
62
|
* @param {*} collection
|
|
63
|
+
* @param {*} version enable optimistic concurrency + query version increment
|
|
64
|
+
* @param {*} incrementOnSave opt-in version bump on save() (Mixed in-place mutations)
|
|
63
65
|
*/
|
|
64
|
-
export declare function loadMongoModel<TAttrs extends BaseDBItem, TDoc extends BaseMongoDoc, TMongoModel extends BaseMongoModel<TAttrs, TDoc>>(modelName: string, schema: any, collection: string, version?: boolean): TMongoModel;
|
|
66
|
+
export declare function loadMongoModel<TAttrs extends BaseDBItem, TDoc extends BaseMongoDoc, TMongoModel extends BaseMongoModel<TAttrs, TDoc>>(modelName: string, schema: any, collection: string, version?: boolean, incrementOnSave?: boolean): TMongoModel;
|
|
65
67
|
/**
|
|
66
68
|
* transform string or string array to ObjectId or ObjectId array
|
|
67
69
|
*
|
|
@@ -58,7 +58,7 @@ function loadModel(model) {
|
|
|
58
58
|
* @param { name, modelName, schema, collection, mergeSchema } param
|
|
59
59
|
* @param load
|
|
60
60
|
*/
|
|
61
|
-
function createModel({ name, classDef, modelName, modelParams, collection, schema, mergeSchema }, load, logger = constants_1.APP.logger) {
|
|
61
|
+
function createModel({ name, classDef, modelName, modelParams, collection, schema, mergeSchema, incrementOnSave }, load, logger = constants_1.APP.logger) {
|
|
62
62
|
let entitySchema;
|
|
63
63
|
if (!name)
|
|
64
64
|
throw new Error('Missing param to create model');
|
|
@@ -68,7 +68,7 @@ function createModel({ name, classDef, modelName, modelParams, collection, schem
|
|
|
68
68
|
entitySchema = exports.codedEntitySchema;
|
|
69
69
|
else
|
|
70
70
|
entitySchema = mergeSchema ? mergeDBSchema(exports.codedEntitySchema, schema) : schema;
|
|
71
|
-
const mgModel = loadMongoModel(modelName, entitySchema, collection);
|
|
71
|
+
const mgModel = loadMongoModel(modelName, entitySchema, collection, true, !!incrementOnSave);
|
|
72
72
|
const model = classDef ? new classDef({ name, model: mgModel, params: modelParams, logger }) : new base_model_1.BaseModel({ name, model: mgModel, params: modelParams, logger });
|
|
73
73
|
if (load)
|
|
74
74
|
loadModel(model);
|
|
@@ -76,7 +76,7 @@ function createModel({ name, classDef, modelName, modelParams, collection, schem
|
|
|
76
76
|
//console.log(`created model ${modelName} (load: ${!!load})`);
|
|
77
77
|
return model;
|
|
78
78
|
}
|
|
79
|
-
function enableQueryVersioning(mgSchema, version) {
|
|
79
|
+
function enableQueryVersioning(mgSchema, version, incrementOnSave = false) {
|
|
80
80
|
if (!version)
|
|
81
81
|
return;
|
|
82
82
|
const versionKey = mgSchema.get('versionKey');
|
|
@@ -97,6 +97,24 @@ function enableQueryVersioning(mgSchema, version) {
|
|
|
97
97
|
mgSchema.pre('findOneAndUpdate', addVersionIncrement);
|
|
98
98
|
mgSchema.pre('updateOne', addVersionIncrement);
|
|
99
99
|
mgSchema.pre('updateMany', addVersionIncrement);
|
|
100
|
+
if (!incrementOnSave)
|
|
101
|
+
return;
|
|
102
|
+
// Opt-in (per-model) version bump on save().
|
|
103
|
+
//
|
|
104
|
+
// With optimisticConcurrency mongoose already increments the version when a
|
|
105
|
+
// tracked path is modified. This hook covers the case of Mixed fields mutated
|
|
106
|
+
// in-place (doc.data.x = y) WITHOUT markModified(): mongoose does not see them
|
|
107
|
+
// as modified, so no increment would happen and the published event would carry
|
|
108
|
+
// a stale version. Enable it only on event-source entities that expose Mixed
|
|
109
|
+
// fields (e.g. companyAsset). increment() shares the same internal version flag
|
|
110
|
+
// as optimisticConcurrency, so the net effect is at most +1 per save().
|
|
111
|
+
mgSchema.pre('save', function () {
|
|
112
|
+
if (this.isNew) // creation: versionKey default acts as v0
|
|
113
|
+
return;
|
|
114
|
+
if (this.isModified(versionKey)) // version explicitly set: respect it
|
|
115
|
+
return;
|
|
116
|
+
this.increment();
|
|
117
|
+
});
|
|
100
118
|
}
|
|
101
119
|
;
|
|
102
120
|
/**
|
|
@@ -104,8 +122,10 @@ function enableQueryVersioning(mgSchema, version) {
|
|
|
104
122
|
* @param {*} modelName
|
|
105
123
|
* @param {*} schema
|
|
106
124
|
* @param {*} collection
|
|
125
|
+
* @param {*} version enable optimistic concurrency + query version increment
|
|
126
|
+
* @param {*} incrementOnSave opt-in version bump on save() (Mixed in-place mutations)
|
|
107
127
|
*/
|
|
108
|
-
function loadMongoModel(modelName, schema, collection, version = true) {
|
|
128
|
+
function loadMongoModel(modelName, schema, collection, version = true, incrementOnSave = false) {
|
|
109
129
|
const mgSchema = new mongoose_1.default.Schema(schema, {
|
|
110
130
|
optimisticConcurrency: version,
|
|
111
131
|
versionKey: 'version',
|
|
@@ -120,7 +140,7 @@ function loadMongoModel(modelName, schema, collection, version = true) {
|
|
|
120
140
|
});
|
|
121
141
|
// change documents' version key from __v to version
|
|
122
142
|
mgSchema.set('versionKey', 'version');
|
|
123
|
-
enableQueryVersioning(mgSchema, version);
|
|
143
|
+
enableQueryVersioning(mgSchema, version, incrementOnSave);
|
|
124
144
|
mgSchema.statics.build = (attrs) => {
|
|
125
145
|
const item = { _id: attrs.id };
|
|
126
146
|
delete attrs.id;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nextage/nx-frame-be",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.38",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "./build/index.js",
|
|
6
6
|
"types": "./build/index.d.ts",
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@apollo/server": "^5.5.0",
|
|
28
28
|
"@apollo/subgraph": "^2.11.2",
|
|
29
|
+
"@apollo/federation-internals": "^2.11.2",
|
|
29
30
|
"@as-integrations/express5": "^1.1.2",
|
|
30
31
|
"@aws-sdk/client-ecs": "^3.592.0",
|
|
31
32
|
"@aws-sdk/client-s3": "^3.1017.0",
|