@decaf-ts/for-fabric 0.1.119 → 0.2.0
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.
- package/dist/for-fabric.cjs +1 -1
- package/dist/for-fabric.cjs.map +1 -1
- package/dist/for-fabric.js +1 -1
- package/dist/for-fabric.js.map +1 -1
- package/lib/client/FabricClientAdapter.cjs +4 -7
- package/lib/client/FabricClientAdapter.js.map +1 -1
- package/lib/client/services/FabricIdentityService.cjs +3 -2
- package/lib/client/services/FabricIdentityService.js.map +1 -1
- package/lib/contract/OtherMarketContract.cjs +30 -0
- package/lib/contract/OtherMarketContract.d.ts +5 -0
- package/lib/contract/OtherMarketContract.js.map +1 -0
- package/lib/contract/OtherProductStrengthContract.cjs +30 -0
- package/lib/contract/OtherProductStrengthContract.d.ts +5 -0
- package/lib/contract/OtherProductStrengthContract.js.map +1 -0
- package/lib/contract/index.cjs +4 -0
- package/lib/contract/index.js.map +1 -1
- package/lib/contract/models/Audit.js.map +1 -1
- package/lib/contract/models/OtherAudit.cjs +105 -0
- package/lib/contract/models/OtherAudit.d.ts +13 -0
- package/lib/contract/models/OtherAudit.js.map +1 -0
- package/lib/contract/models/OtherMarket.cjs +78 -0
- package/lib/contract/models/OtherMarket.d.ts +12 -0
- package/lib/contract/models/OtherMarket.js.map +1 -0
- package/lib/contract/models/OtherProductShared.cjs +15 -4
- package/lib/contract/models/OtherProductShared.d.ts +4 -0
- package/lib/contract/models/OtherProductShared.js.map +1 -1
- package/lib/contract/models/OtherProductStrength.cjs +81 -0
- package/lib/contract/models/OtherProductStrength.d.ts +11 -0
- package/lib/contract/models/OtherProductStrength.js.map +1 -0
- package/lib/contract/models/Product.cjs +1 -1
- package/lib/contract/models/Product.js.map +1 -1
- package/lib/contract/models/ProductStrength.js.map +1 -1
- package/lib/contract/models/decorators-private.cjs +88 -0
- package/lib/contract/models/decorators-private.d.ts +16 -0
- package/lib/contract/models/decorators-private.js.map +1 -0
- package/lib/contract/models/decorators.cjs +31 -2
- package/lib/contract/models/decorators.d.ts +6 -1
- package/lib/contract/models/decorators.js.map +1 -1
- package/lib/contracts/ContractAdapter.cjs +259 -409
- package/lib/contracts/ContractAdapter.d.ts +5 -29
- package/lib/contracts/ContractAdapter.js.map +1 -1
- package/lib/contracts/ContractContext.cjs +41 -13
- package/lib/contracts/ContractContext.d.ts +13 -8
- package/lib/contracts/ContractContext.js.map +1 -1
- package/lib/contracts/FabricContractRepository.cjs +70 -0
- package/lib/contracts/FabricContractRepository.d.ts +6 -0
- package/lib/contracts/FabricContractRepository.js.map +1 -1
- package/lib/contracts/FabricContractSequence.cjs +7 -23
- package/lib/contracts/FabricContractSequence.js.map +1 -1
- package/lib/contracts/FabricContractStatement.cjs +136 -1
- package/lib/contracts/FabricContractStatement.d.ts +5 -2
- package/lib/contracts/FabricContractStatement.js.map +1 -1
- package/lib/contracts/crud/crud-contract.cjs +42 -17
- package/lib/contracts/crud/crud-contract.d.ts +6 -5
- package/lib/contracts/crud/crud-contract.js.map +1 -1
- package/lib/contracts/types.d.ts +9 -2
- package/lib/esm/client/FabricClientAdapter.js +4 -7
- package/lib/esm/client/FabricClientAdapter.js.map +1 -1
- package/lib/esm/client/services/FabricIdentityService.js +3 -2
- package/lib/esm/client/services/FabricIdentityService.js.map +1 -1
- package/lib/esm/contract/OtherMarketContract.d.ts +5 -0
- package/lib/esm/contract/OtherMarketContract.js +27 -0
- package/lib/esm/contract/OtherMarketContract.js.map +1 -0
- package/lib/esm/contract/OtherProductStrengthContract.d.ts +5 -0
- package/lib/esm/contract/OtherProductStrengthContract.js +27 -0
- package/lib/esm/contract/OtherProductStrengthContract.js.map +1 -0
- package/lib/esm/contract/index.js +4 -0
- package/lib/esm/contract/index.js.map +1 -1
- package/lib/esm/contract/models/Audit.js +1 -1
- package/lib/esm/contract/models/Audit.js.map +1 -1
- package/lib/esm/contract/models/OtherAudit.d.ts +13 -0
- package/lib/esm/contract/models/OtherAudit.js +102 -0
- package/lib/esm/contract/models/OtherAudit.js.map +1 -0
- package/lib/esm/contract/models/OtherMarket.d.ts +12 -0
- package/lib/esm/contract/models/OtherMarket.js +75 -0
- package/lib/esm/contract/models/OtherMarket.js.map +1 -0
- package/lib/esm/contract/models/OtherProductShared.d.ts +4 -0
- package/lib/esm/contract/models/OtherProductShared.js +16 -5
- package/lib/esm/contract/models/OtherProductShared.js.map +1 -1
- package/lib/esm/contract/models/OtherProductStrength.d.ts +11 -0
- package/lib/esm/contract/models/OtherProductStrength.js +78 -0
- package/lib/esm/contract/models/OtherProductStrength.js.map +1 -0
- package/lib/esm/contract/models/Product.js +1 -1
- package/lib/esm/contract/models/Product.js.map +1 -1
- package/lib/esm/contract/models/ProductStrength.js +1 -1
- package/lib/esm/contract/models/ProductStrength.js.map +1 -1
- package/lib/esm/contract/models/decorators-private.d.ts +16 -0
- package/lib/esm/contract/models/decorators-private.js +81 -0
- package/lib/esm/contract/models/decorators-private.js.map +1 -0
- package/lib/esm/contract/models/decorators.d.ts +6 -1
- package/lib/esm/contract/models/decorators.js +30 -2
- package/lib/esm/contract/models/decorators.js.map +1 -1
- package/lib/esm/contracts/ContractAdapter.d.ts +5 -29
- package/lib/esm/contracts/ContractAdapter.js +262 -412
- package/lib/esm/contracts/ContractAdapter.js.map +1 -1
- package/lib/esm/contracts/ContractContext.d.ts +13 -8
- package/lib/esm/contracts/ContractContext.js +41 -13
- package/lib/esm/contracts/ContractContext.js.map +1 -1
- package/lib/esm/contracts/FabricContractRepository.d.ts +6 -0
- package/lib/esm/contracts/FabricContractRepository.js +70 -0
- package/lib/esm/contracts/FabricContractRepository.js.map +1 -1
- package/lib/esm/contracts/FabricContractSequence.js +9 -25
- package/lib/esm/contracts/FabricContractSequence.js.map +1 -1
- package/lib/esm/contracts/FabricContractStatement.d.ts +5 -2
- package/lib/esm/contracts/FabricContractStatement.js +135 -0
- package/lib/esm/contracts/FabricContractStatement.js.map +1 -1
- package/lib/esm/contracts/crud/crud-contract.d.ts +6 -5
- package/lib/esm/contracts/crud/crud-contract.js +42 -17
- package/lib/esm/contracts/crud/crud-contract.js.map +1 -1
- package/lib/esm/contracts/types.d.ts +9 -2
- package/lib/esm/shared/decorators.d.ts +8 -0
- package/lib/esm/shared/decorators.js +90 -83
- package/lib/esm/shared/decorators.js.map +1 -1
- package/lib/esm/shared/overrides/overrides.js +7 -6
- package/lib/esm/shared/overrides/overrides.js.map +1 -1
- package/lib/esm/shared/types.d.ts +3 -2
- package/lib/esm/version.d.ts +1 -1
- package/lib/esm/version.js +1 -1
- package/lib/shared/decorators.cjs +91 -82
- package/lib/shared/decorators.d.ts +8 -0
- package/lib/shared/decorators.js.map +1 -1
- package/lib/shared/overrides/overrides.cjs +7 -6
- package/lib/shared/overrides/overrides.js.map +1 -1
- package/lib/shared/types.d.ts +3 -2
- package/lib/version.cjs +1 -1
- package/lib/version.d.ts +1 -1
- package/package.json +3 -3
- package/lib/contracts/ContractPrivateDataAdapter.cjs +0 -426
- package/lib/contracts/ContractPrivateDataAdapter.d.ts +0 -0
- package/lib/contracts/ContractPrivateDataAdapter.js.map +0 -1
- package/lib/contracts/FabricConstruction.cjs +0 -441
- package/lib/contracts/FabricConstruction.d.ts +0 -305
- package/lib/contracts/FabricConstruction.js.map +0 -1
- package/lib/contracts/private-data.cjs +0 -204
- package/lib/contracts/private-data.d.ts +0 -0
- package/lib/contracts/private-data.js.map +0 -1
- package/lib/esm/contracts/ContractPrivateDataAdapter.d.ts +0 -0
- package/lib/esm/contracts/ContractPrivateDataAdapter.js +0 -426
- package/lib/esm/contracts/ContractPrivateDataAdapter.js.map +0 -1
- package/lib/esm/contracts/FabricConstruction.d.ts +0 -305
- package/lib/esm/contracts/FabricConstruction.js +0 -433
- package/lib/esm/contracts/FabricConstruction.js.map +0 -1
- package/lib/esm/contracts/private-data.d.ts +0 -0
- package/lib/esm/contracts/private-data.js +0 -204
- package/lib/esm/contracts/private-data.js.map +0 -1
|
@@ -17,7 +17,6 @@ const SimpleDeterministicSerializer_1 = require("./../shared/SimpleDeterministic
|
|
|
17
17
|
const decoration_1 = require("@decaf-ts/decoration");
|
|
18
18
|
const FabricContractPaginator_1 = require("./FabricContractPaginator.cjs");
|
|
19
19
|
const errors_1 = require("./../shared/errors.cjs");
|
|
20
|
-
const MIRROR_SKIP_FLAG_PREFIX = "mirror:skip:";
|
|
21
20
|
/**
|
|
22
21
|
* @description Sets the creator or updater field in a model based on the user in the context
|
|
23
22
|
* @summary Callback function used in decorators to automatically set the created_by or updated_by fields
|
|
@@ -100,12 +99,6 @@ class FabricContractAdapter extends for_couchdb_1.CouchDBAdapter {
|
|
|
100
99
|
*/
|
|
101
100
|
static { this.textDecoder = new TextDecoder("utf8"); }
|
|
102
101
|
static { this.serializer = new SimpleDeterministicSerializer_1.SimpleDeterministicSerializer(); }
|
|
103
|
-
setSequenceSegregation(seqName, fullySegregated, collections) {
|
|
104
|
-
this._sequenceSegregation.set(seqName, { fullySegregated, collections });
|
|
105
|
-
}
|
|
106
|
-
getSequenceSegregation(seqName) {
|
|
107
|
-
return this._sequenceSegregation.get(seqName);
|
|
108
|
-
}
|
|
109
102
|
/**
|
|
110
103
|
* @description Context constructor for this adapter
|
|
111
104
|
* @summary Overrides the base Context constructor with FabricContractContext
|
|
@@ -136,17 +129,18 @@ class FabricContractAdapter extends for_couchdb_1.CouchDBAdapter {
|
|
|
136
129
|
*/
|
|
137
130
|
constructor(scope, alias) {
|
|
138
131
|
super(scope, constants_1.FabricFlavour, alias);
|
|
139
|
-
/**
|
|
140
|
-
* @description Stores segregation metadata per sequence name
|
|
141
|
-
* @summary Needed because the Sequence creates its own context (via logCtx),
|
|
142
|
-
* losing flags set by extractSegregatedCollections on the handler context.
|
|
143
|
-
* The adapter persists across operations, making it a reliable store.
|
|
144
|
-
*/
|
|
145
|
-
this._sequenceSegregation = new Map();
|
|
146
132
|
}
|
|
147
133
|
for(config, ...args) {
|
|
148
134
|
return super.for(config, ...args);
|
|
149
135
|
}
|
|
136
|
+
getModelDefaults(clazz) {
|
|
137
|
+
const m = new clazz();
|
|
138
|
+
return (decoration_1.Metadata.properties(clazz) || []).reduce((acc, p) => {
|
|
139
|
+
if (typeof m[p] !== "undefined")
|
|
140
|
+
acc[p] = m[p];
|
|
141
|
+
return acc;
|
|
142
|
+
}, {});
|
|
143
|
+
}
|
|
150
144
|
/**
|
|
151
145
|
* @description Creates a record in the state database
|
|
152
146
|
* @summary Serializes a model and stores it in the Fabric state database
|
|
@@ -164,6 +158,10 @@ class FabricContractAdapter extends for_couchdb_1.CouchDBAdapter {
|
|
|
164
158
|
const tableName = decorator_validation_1.Model.tableName(clazz);
|
|
165
159
|
const composedKey = ctx.stub.createCompositeKey(tableName, [String(id)]);
|
|
166
160
|
const isMirror = ctx.getOrUndefined("mirror");
|
|
161
|
+
const segregated = isMirror
|
|
162
|
+
? ctx.getOrUndefined("segregated")
|
|
163
|
+
: undefined;
|
|
164
|
+
const fullySegregated = ctx.isFullySegregated && !segregated;
|
|
167
165
|
if (!isMirror) {
|
|
168
166
|
let existing;
|
|
169
167
|
try {
|
|
@@ -179,30 +177,45 @@ class FabricContractAdapter extends for_couchdb_1.CouchDBAdapter {
|
|
|
179
177
|
throw new db_decorators_1.ConflictError(`record with id ${id} in table ${tableName} already exists`);
|
|
180
178
|
}
|
|
181
179
|
try {
|
|
182
|
-
const sanitizedModel = this.normalizeForPublic(clazz, model, ctx);
|
|
183
180
|
log.info(`adding entry to ${tableName} table with pk ${id}`);
|
|
184
|
-
if (
|
|
185
|
-
|
|
186
|
-
|
|
181
|
+
if (segregated) {
|
|
182
|
+
// Mirror write: route the full model to the mirror collection via forPrivate
|
|
183
|
+
model = await this.forPrivate(segregated).putState(composedKey, model, ctx);
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
const defaults = this.getModelDefaults(clazz);
|
|
187
|
+
// handle public data if not fully segregated
|
|
188
|
+
if (!fullySegregated) {
|
|
189
|
+
if (Object.keys(model).filter((k) => {
|
|
190
|
+
if (k === for_couchdb_1.CouchDBKeys.TABLE)
|
|
191
|
+
return false;
|
|
192
|
+
return !(defaults &&
|
|
193
|
+
k in defaults &&
|
|
194
|
+
defaults[k] === model[k]);
|
|
195
|
+
}).length)
|
|
196
|
+
model = await this.putState(composedKey, model, ctx);
|
|
197
|
+
}
|
|
198
|
+
// handle segregated writes
|
|
199
|
+
const data = ctx.getFromChildren("segregatedData");
|
|
200
|
+
if (data) {
|
|
201
|
+
for (const collection in data) {
|
|
202
|
+
Object.assign(model, await this.forPrivate(collection).putState(composedKey, data[collection][id], ctx));
|
|
203
|
+
}
|
|
204
|
+
}
|
|
187
205
|
}
|
|
188
|
-
model = await this.putState(composedKey, sanitizedModel, ctx);
|
|
189
206
|
}
|
|
190
207
|
catch (e) {
|
|
191
208
|
throw this.parseError(e);
|
|
192
209
|
}
|
|
193
210
|
return model;
|
|
194
211
|
}
|
|
195
|
-
|
|
196
|
-
if (model
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
? model
|
|
203
|
-
: decorator_validation_1.Model.build(model, clazz.name);
|
|
204
|
-
const prepared = this.prepare(instance, ctx);
|
|
205
|
-
return prepared.record;
|
|
212
|
+
async createAll(tableName, id, model, ...args) {
|
|
213
|
+
if (id.length !== model.length)
|
|
214
|
+
throw new db_decorators_1.InternalError("Ids and models must have the same length");
|
|
215
|
+
const { log, ctxArgs } = this.logCtx(args, this.createAll);
|
|
216
|
+
const tableLabel = decorator_validation_1.Model.tableName(tableName);
|
|
217
|
+
log.debug(`Creating ${id.length} entries ${tableLabel} table`);
|
|
218
|
+
return Promise.all(id.map((i, count) => this.create(tableName, i, model[count], ...ctxArgs)));
|
|
206
219
|
}
|
|
207
220
|
/**
|
|
208
221
|
* @description Reads a record from the state database
|
|
@@ -219,22 +232,18 @@ class FabricContractAdapter extends for_couchdb_1.CouchDBAdapter {
|
|
|
219
232
|
const composedKey = ctx.stub.createCompositeKey(tableName, [String(id)]);
|
|
220
233
|
let model;
|
|
221
234
|
try {
|
|
222
|
-
model =
|
|
235
|
+
model = ctx.isFullySegregated
|
|
236
|
+
? {}
|
|
237
|
+
: await this.readState(composedKey, ctx);
|
|
223
238
|
}
|
|
224
239
|
catch (e) {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
model = {};
|
|
232
|
-
}
|
|
233
|
-
else {
|
|
234
|
-
throw this.parseError(e);
|
|
235
|
-
}
|
|
240
|
+
throw this.parseError(e);
|
|
241
|
+
}
|
|
242
|
+
const collections = ctx.getReadCollections();
|
|
243
|
+
if (collections && collections.length) {
|
|
244
|
+
for (const col of collections)
|
|
245
|
+
Object.assign(model, await this.forPrivate(col).readState(composedKey, ctx));
|
|
236
246
|
}
|
|
237
|
-
model = await this.mergeSegregatedReads(ctx, composedKey, model);
|
|
238
247
|
return model;
|
|
239
248
|
}
|
|
240
249
|
/**
|
|
@@ -250,16 +259,38 @@ class FabricContractAdapter extends for_couchdb_1.CouchDBAdapter {
|
|
|
250
259
|
async update(clazz, id, model, ...args) {
|
|
251
260
|
const { ctx, log } = this.logCtx(args, this.update);
|
|
252
261
|
this.enforceMirrorAuthorization(clazz, ctx);
|
|
262
|
+
log.info(`in ADAPTER update with args ${args}`);
|
|
253
263
|
const tableName = decorator_validation_1.Model.tableName(clazz);
|
|
254
264
|
const composedKey = ctx.stub.createCompositeKey(tableName, [String(id)]);
|
|
265
|
+
const isMirror = ctx.getOrUndefined("mirror");
|
|
266
|
+
const segregated = isMirror
|
|
267
|
+
? ctx.getOrUndefined("segregated")
|
|
268
|
+
: undefined;
|
|
255
269
|
try {
|
|
256
|
-
log.
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
await this.
|
|
260
|
-
|
|
270
|
+
log.info(`updating entry in ${tableName} table with pk ${id}`);
|
|
271
|
+
if (segregated) {
|
|
272
|
+
// Mirror/segregated write: route the full model to the private collection
|
|
273
|
+
model = await this.forPrivate(segregated).putState(composedKey, model, ctx);
|
|
274
|
+
}
|
|
275
|
+
else {
|
|
276
|
+
const defaults = this.getModelDefaults(clazz);
|
|
277
|
+
// handle public data
|
|
278
|
+
if (Object.keys(model).filter((k) => {
|
|
279
|
+
if (k === for_couchdb_1.CouchDBKeys.TABLE)
|
|
280
|
+
return false;
|
|
281
|
+
return !(defaults &&
|
|
282
|
+
k in defaults &&
|
|
283
|
+
defaults[k] === model[k]);
|
|
284
|
+
}).length)
|
|
285
|
+
model = await this.putState(composedKey, model, ctx);
|
|
286
|
+
// handle segregated writes
|
|
287
|
+
const data = ctx.getFromChildren("segregatedData");
|
|
288
|
+
if (data) {
|
|
289
|
+
for (const collection in data) {
|
|
290
|
+
Object.assign(model, await this.forPrivate(collection).putState(composedKey, data[collection][id], ctx));
|
|
291
|
+
}
|
|
292
|
+
}
|
|
261
293
|
}
|
|
262
|
-
model = await this.putState(composedKey, sanitizedModel, ctx);
|
|
263
294
|
}
|
|
264
295
|
catch (e) {
|
|
265
296
|
throw this.parseError(e);
|
|
@@ -275,40 +306,48 @@ class FabricContractAdapter extends for_couchdb_1.CouchDBAdapter {
|
|
|
275
306
|
* @return {Promise<Record<string, any>>} Promise resolving to the deleted record
|
|
276
307
|
*/
|
|
277
308
|
async delete(clazz, id, ...args) {
|
|
278
|
-
const { ctx
|
|
309
|
+
const { ctx } = this.logCtx(args, this.delete);
|
|
279
310
|
this.enforceMirrorAuthorization(clazz, ctx);
|
|
280
311
|
const tableName = decorator_validation_1.Model.tableName(clazz);
|
|
281
|
-
const
|
|
312
|
+
const composedKey = ctx.stub.createCompositeKey(tableName, [String(id)]);
|
|
282
313
|
const isMirror = ctx.getOrUndefined("mirror");
|
|
314
|
+
const segregated = isMirror
|
|
315
|
+
? ctx.getOrUndefined("segregated")
|
|
316
|
+
: undefined;
|
|
283
317
|
let model;
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
shouldDeletePublicState = this.hasPublicState(model, clazz);
|
|
290
|
-
}
|
|
291
|
-
else {
|
|
292
|
-
model = { [pkProp]: id };
|
|
318
|
+
if (segregated) {
|
|
319
|
+
// Mirror/segregated delete: route through forPrivate
|
|
320
|
+
try {
|
|
321
|
+
model = await this.forPrivate(segregated).readState(composedKey, ctx);
|
|
322
|
+
await this.forPrivate(segregated).deleteState(composedKey, ctx);
|
|
293
323
|
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
await this.deleteState(composedKey, ctx);
|
|
324
|
+
catch (e) {
|
|
325
|
+
throw this.parseError(e);
|
|
297
326
|
}
|
|
298
|
-
await this.deleteSegregatedCollections(ctx, composedKey);
|
|
299
327
|
}
|
|
300
|
-
|
|
301
|
-
|
|
328
|
+
else {
|
|
329
|
+
try {
|
|
330
|
+
model = ctx.isFullySegregated
|
|
331
|
+
? {}
|
|
332
|
+
: await this.readState(composedKey, ctx);
|
|
333
|
+
if (!ctx.isFullySegregated)
|
|
334
|
+
await this.deleteState(composedKey, ctx);
|
|
335
|
+
}
|
|
336
|
+
catch (e) {
|
|
337
|
+
throw this.parseError(e);
|
|
338
|
+
}
|
|
339
|
+
const collections = ctx.getReadCollections();
|
|
340
|
+
if (collections && collections.length) {
|
|
341
|
+
for (const col of collections) {
|
|
342
|
+
Object.assign(model, await this.forPrivate(col).readState(composedKey, ctx));
|
|
343
|
+
await this.forPrivate(col).deleteState(composedKey, ctx);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
302
346
|
}
|
|
303
347
|
return model;
|
|
304
348
|
}
|
|
305
349
|
async deleteState(id, context) {
|
|
306
|
-
|
|
307
|
-
const collection = ctx.getOrUndefined("segregated");
|
|
308
|
-
if (collection)
|
|
309
|
-
await ctx.stub.deletePrivateData(collection, id);
|
|
310
|
-
else
|
|
311
|
-
await ctx.stub.deleteState(id);
|
|
350
|
+
await context.stub.deleteState(id);
|
|
312
351
|
}
|
|
313
352
|
forPrivate(collection) {
|
|
314
353
|
const toOverride = [
|
|
@@ -353,55 +392,62 @@ class FabricContractAdapter extends for_couchdb_1.CouchDBAdapter {
|
|
|
353
392
|
}
|
|
354
393
|
case "queryResult": {
|
|
355
394
|
const [stub, rawInput] = argsList;
|
|
356
|
-
return stub.getPrivateDataQueryResult(collection, rawInput);
|
|
395
|
+
return stub.getPrivateDataQueryResult(collection, JSON.stringify(rawInput));
|
|
357
396
|
}
|
|
358
397
|
case "queryResultPaginated": {
|
|
359
|
-
const [stub, rawInput, limit, skip] = argsList;
|
|
360
|
-
const iterator = await stub.getPrivateDataQueryResult(collection, rawInput);
|
|
398
|
+
const [stub, rawInput, limit, skip, bookmark] = argsList;
|
|
399
|
+
const iterator = await stub.getPrivateDataQueryResult(collection, JSON.stringify(rawInput));
|
|
361
400
|
const results = [];
|
|
362
401
|
let count = 0;
|
|
363
|
-
|
|
402
|
+
const skipKey = bookmark || skip;
|
|
403
|
+
let reachedBookmark = skipKey ? false : true;
|
|
364
404
|
let lastKey = null;
|
|
365
405
|
while (true) {
|
|
366
406
|
const res = await iterator.next();
|
|
367
407
|
if (res.value && res.value.value.toString()) {
|
|
368
408
|
const recordKey = res.value.key;
|
|
369
409
|
const recordValue = res.value.value.toString("utf8");
|
|
370
|
-
// If we have a skip, skip until we
|
|
410
|
+
// If we have a bookmark/skip, skip until we pass it
|
|
371
411
|
if (!reachedBookmark) {
|
|
372
|
-
if (recordKey ===
|
|
412
|
+
if (recordKey === skipKey?.toString()) {
|
|
373
413
|
reachedBookmark = true;
|
|
374
414
|
}
|
|
375
415
|
continue;
|
|
376
416
|
}
|
|
377
417
|
results.push({
|
|
378
|
-
|
|
379
|
-
|
|
418
|
+
key: recordKey,
|
|
419
|
+
value: Buffer.from(recordValue),
|
|
380
420
|
});
|
|
381
421
|
lastKey = recordKey;
|
|
382
422
|
count++;
|
|
383
423
|
if (count >= limit) {
|
|
384
424
|
await iterator.close();
|
|
385
|
-
|
|
386
|
-
iterator: results,
|
|
387
|
-
metadata: {
|
|
388
|
-
fetchedRecordsCount: results.length,
|
|
389
|
-
bookmark: lastKey,
|
|
390
|
-
},
|
|
391
|
-
};
|
|
425
|
+
break;
|
|
392
426
|
}
|
|
393
427
|
}
|
|
394
428
|
if (res.done) {
|
|
395
429
|
await iterator.close();
|
|
396
|
-
|
|
397
|
-
iterator: results,
|
|
398
|
-
metadata: {
|
|
399
|
-
fetchedRecordsCount: results.length,
|
|
400
|
-
bookmark: "",
|
|
401
|
-
},
|
|
402
|
-
};
|
|
430
|
+
break;
|
|
403
431
|
}
|
|
404
432
|
}
|
|
433
|
+
// Wrap results in an async iterator compatible with resultIterator()
|
|
434
|
+
let idx = 0;
|
|
435
|
+
const arrayIterator = {
|
|
436
|
+
async next() {
|
|
437
|
+
if (idx < results.length) {
|
|
438
|
+
return { value: results[idx++], done: false };
|
|
439
|
+
}
|
|
440
|
+
return { value: undefined, done: true };
|
|
441
|
+
},
|
|
442
|
+
async close() { },
|
|
443
|
+
};
|
|
444
|
+
return {
|
|
445
|
+
iterator: arrayIterator,
|
|
446
|
+
metadata: {
|
|
447
|
+
fetchedRecordsCount: results.length,
|
|
448
|
+
bookmark: count >= limit ? lastKey || "" : "",
|
|
449
|
+
},
|
|
450
|
+
};
|
|
405
451
|
}
|
|
406
452
|
default:
|
|
407
453
|
throw new db_decorators_1.InternalError(`Unsupported method override ${String(prop)}`);
|
|
@@ -413,156 +459,22 @@ class FabricContractAdapter extends for_couchdb_1.CouchDBAdapter {
|
|
|
413
459
|
}
|
|
414
460
|
async putState(id, model, ctx) {
|
|
415
461
|
let data;
|
|
416
|
-
const { log } = this.logCtx([ctx], this.putState);
|
|
417
462
|
try {
|
|
418
463
|
data = Buffer.from(FabricContractAdapter.serializer.serialize(model, false));
|
|
419
464
|
}
|
|
420
465
|
catch (e) {
|
|
421
466
|
throw new db_decorators_1.SerializationError(`Failed to serialize record with id ${id}: ${e}`);
|
|
422
467
|
}
|
|
423
|
-
|
|
424
|
-
if (collection) {
|
|
425
|
-
if (ctx.getOrUndefined("mirror")) {
|
|
426
|
-
const cacheKey = `segregatedRecord:${collection}:${id.toString()}`;
|
|
427
|
-
let existingRecord = ctx.cache.get(cacheKey);
|
|
428
|
-
ctx.cache.remove(cacheKey);
|
|
429
|
-
if (!existingRecord) {
|
|
430
|
-
const existing = await ctx.stub.getPrivateData(collection, id.toString());
|
|
431
|
-
if (existing) {
|
|
432
|
-
try {
|
|
433
|
-
existingRecord = FabricContractAdapter.serializer.deserialize(existing.toString());
|
|
434
|
-
}
|
|
435
|
-
catch {
|
|
436
|
-
existingRecord = undefined;
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
if (existingRecord) {
|
|
441
|
-
const merged = Object.assign({}, existingRecord, model);
|
|
442
|
-
data = Buffer.from(FabricContractAdapter.serializer.serialize(merged, false));
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
await ctx.stub.putPrivateData(collection, id.toString(), data);
|
|
446
|
-
}
|
|
447
|
-
else {
|
|
448
|
-
await ctx.stub.putState(id.toString(), data);
|
|
449
|
-
await this.flushSegregatedWrites(ctx, id);
|
|
450
|
-
}
|
|
451
|
-
log.silly(`state stored${collection ? ` in ${collection} collection` : ""} under id ${id}`);
|
|
452
|
-
return model;
|
|
453
|
-
}
|
|
454
|
-
async flushSegregatedWrites(ctx, compositeKey) {
|
|
455
|
-
const writeCollections = ctx.getWriteCollections();
|
|
456
|
-
if (!writeCollections.length)
|
|
457
|
-
return;
|
|
458
|
-
const writes = ctx._segregateWrite;
|
|
459
|
-
if (!writes)
|
|
460
|
-
return;
|
|
461
|
-
for (const collection of writeCollections) {
|
|
462
|
-
const entries = writes[collection];
|
|
463
|
-
if (!entries || !entries.length)
|
|
464
|
-
continue;
|
|
465
|
-
const record = this.buildSegregatedRecord(entries, collection);
|
|
466
|
-
if (!record || !Object.keys(record).length)
|
|
467
|
-
continue;
|
|
468
|
-
const cacheKey = `segregatedRecord:${collection}:${compositeKey}`;
|
|
469
|
-
ctx.cache.put(cacheKey, record);
|
|
470
|
-
const privateCtx = ctx.override({
|
|
471
|
-
segregated: collection,
|
|
472
|
-
});
|
|
473
|
-
await this.putState(compositeKey, record, privateCtx);
|
|
474
|
-
}
|
|
475
|
-
ctx.cache.remove("segregateWrite");
|
|
476
|
-
}
|
|
477
|
-
buildSegregatedRecord(entries, collection) {
|
|
478
|
-
if (!entries || !entries.length)
|
|
479
|
-
return undefined;
|
|
480
|
-
const firstModel = entries.find((entry) => !!entry.model)?.model;
|
|
481
|
-
if (!firstModel)
|
|
482
|
-
return undefined;
|
|
483
|
-
const clazz = firstModel.constructor;
|
|
484
|
-
const pkProp = decorator_validation_1.Model.pk(clazz);
|
|
485
|
-
const keys = new Set();
|
|
486
|
-
entries.forEach((entry) => entry.keys.forEach((key) => keys.add(key)));
|
|
487
|
-
const metadataFields = this.getFieldsForCollection(firstModel, collection);
|
|
488
|
-
metadataFields.forEach((key) => keys.add(key));
|
|
489
|
-
if (pkProp)
|
|
490
|
-
keys.add(pkProp);
|
|
491
|
-
const record = {};
|
|
492
|
-
keys.forEach((key) => {
|
|
493
|
-
const value = entries
|
|
494
|
-
.map((entry) => entry.model[key])
|
|
495
|
-
.find((val) => typeof val !== "undefined");
|
|
496
|
-
if (typeof value === "undefined")
|
|
497
|
-
return;
|
|
498
|
-
const mappedProp = decorator_validation_1.Model.columnName(clazz, key);
|
|
499
|
-
if (this.isReserved(mappedProp))
|
|
500
|
-
throw new db_decorators_1.InternalError(`Property name ${mappedProp} is reserved`);
|
|
501
|
-
record[mappedProp] =
|
|
502
|
-
value instanceof Date ? new Date(value) : value;
|
|
503
|
-
});
|
|
504
|
-
return record;
|
|
505
|
-
}
|
|
506
|
-
async mergeSegregatedReads(ctx, id, model) {
|
|
507
|
-
// Use getReadCollections which reads from the private _segregateRead property
|
|
508
|
-
const reads = ctx.getReadCollections();
|
|
509
|
-
if (!reads?.length)
|
|
510
|
-
return model;
|
|
511
|
-
for (const collection of reads) {
|
|
512
|
-
const skipFlagKey = `${MIRROR_SKIP_FLAG_PREFIX}${collection}`;
|
|
513
|
-
if (ctx.getOrUndefined(skipFlagKey))
|
|
514
|
-
continue;
|
|
515
|
-
const privateRecord = await this.readPrivateRecord(ctx, collection, id);
|
|
516
|
-
if (!privateRecord)
|
|
517
|
-
continue;
|
|
518
|
-
Object.entries(privateRecord).forEach(([key, value]) => {
|
|
519
|
-
if (typeof value === "undefined")
|
|
520
|
-
return;
|
|
521
|
-
model[key] = value;
|
|
522
|
-
});
|
|
523
|
-
}
|
|
468
|
+
await ctx.stub.putState(id.toString(), data);
|
|
524
469
|
return model;
|
|
525
470
|
}
|
|
526
|
-
async readPrivateRecord(ctx, collection, id) {
|
|
527
|
-
const data = await ctx.stub.getPrivateData(collection, id);
|
|
528
|
-
if (!data)
|
|
529
|
-
return undefined;
|
|
530
|
-
const text = data.toString();
|
|
531
|
-
if (!text)
|
|
532
|
-
return undefined;
|
|
533
|
-
try {
|
|
534
|
-
return FabricContractAdapter.serializer.deserialize(text);
|
|
535
|
-
}
|
|
536
|
-
catch {
|
|
537
|
-
return undefined;
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
async deleteSegregatedCollections(ctx, id) {
|
|
541
|
-
// Use getReadCollections which reads from the private _segregateRead property
|
|
542
|
-
const reads = ctx.getReadCollections();
|
|
543
|
-
if (!reads?.length)
|
|
544
|
-
return;
|
|
545
|
-
for (const collection of reads) {
|
|
546
|
-
try {
|
|
547
|
-
await ctx.stub.deletePrivateData(collection, id);
|
|
548
|
-
}
|
|
549
|
-
catch {
|
|
550
|
-
// ignore missing private data
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
471
|
async readState(id, ctx) {
|
|
555
472
|
let result;
|
|
556
473
|
const { log } = this.logCtx([ctx], this.readState);
|
|
557
|
-
|
|
558
|
-
const collection = ctx.get("segregated");
|
|
559
|
-
if (collection)
|
|
560
|
-
res = (await ctx.stub.getPrivateData(collection, id.toString())).toString();
|
|
561
|
-
else
|
|
562
|
-
res = (await ctx.stub.getState(id.toString())).toString();
|
|
474
|
+
const res = (await ctx.stub.getState(id.toString())).toString();
|
|
563
475
|
if (!res)
|
|
564
|
-
throw new db_decorators_1.NotFoundError(`Record with id ${id}
|
|
565
|
-
log.silly(`state retrieved
|
|
476
|
+
throw new db_decorators_1.NotFoundError(`Record with id ${id} not found`);
|
|
477
|
+
log.silly(`state retrieved under id ${id}`);
|
|
566
478
|
try {
|
|
567
479
|
result = FabricContractAdapter.serializer.deserialize(res.toString());
|
|
568
480
|
}
|
|
@@ -571,84 +483,15 @@ class FabricContractAdapter extends for_couchdb_1.CouchDBAdapter {
|
|
|
571
483
|
}
|
|
572
484
|
return result;
|
|
573
485
|
}
|
|
574
|
-
async queryResult(stub, rawInput,
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
if (collection)
|
|
579
|
-
res = await ctx.stub.getPrivateDataQueryResult(collection, JSON.stringify(rawInput));
|
|
580
|
-
else
|
|
581
|
-
res = await stub.getQueryResult(JSON.stringify(rawInput));
|
|
582
|
-
return res;
|
|
583
|
-
}
|
|
584
|
-
createRowsIterator(rows) {
|
|
585
|
-
let index = 0;
|
|
586
|
-
return {
|
|
587
|
-
// @ts-expect-error typeing of iterator?
|
|
588
|
-
async next() {
|
|
589
|
-
if (index < rows.length) {
|
|
590
|
-
const row = rows[index++];
|
|
591
|
-
return {
|
|
592
|
-
value: { key: row.key, value: row.value },
|
|
593
|
-
done: false,
|
|
594
|
-
};
|
|
595
|
-
}
|
|
596
|
-
return { done: true };
|
|
597
|
-
},
|
|
598
|
-
async close() {
|
|
599
|
-
// noop
|
|
600
|
-
},
|
|
601
|
-
};
|
|
602
|
-
}
|
|
603
|
-
async queryResultPaginated(stub, rawInput, limit = 250, page, bookmark, ...args) {
|
|
604
|
-
const { ctx } = this.logCtx(args, this.readState);
|
|
605
|
-
let res;
|
|
606
|
-
const collection = ctx.get("segregated");
|
|
607
|
-
if (collection) {
|
|
608
|
-
const clonedInput = JSON.parse(JSON.stringify(rawInput));
|
|
609
|
-
clonedInput.selector = clonedInput.selector || {};
|
|
610
|
-
if (bookmark)
|
|
611
|
-
clonedInput.selector._id = { $gt: bookmark.toString() };
|
|
612
|
-
const limitValue = typeof limit === "number" && limit > 0 ? limit : Number.MAX_VALUE;
|
|
613
|
-
const iterator = await stub.getPrivateDataQueryResult(collection, JSON.stringify(clonedInput));
|
|
614
|
-
const rows = [];
|
|
615
|
-
let lastKey = "";
|
|
616
|
-
while (rows.length < limitValue) {
|
|
617
|
-
const resRow = await iterator.next();
|
|
618
|
-
if (resRow.done)
|
|
619
|
-
break;
|
|
620
|
-
if (resRow.value && resRow.value.value) {
|
|
621
|
-
rows.push({
|
|
622
|
-
key: resRow.value.key,
|
|
623
|
-
value: resRow.value.value,
|
|
624
|
-
});
|
|
625
|
-
lastKey = resRow.value.key;
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
await iterator.close();
|
|
629
|
-
res = {
|
|
630
|
-
iterator: this.createRowsIterator(rows),
|
|
631
|
-
metadata: {
|
|
632
|
-
fetchedRecordsCount: rows.length,
|
|
633
|
-
bookmark: rows.length ? lastKey : "",
|
|
634
|
-
},
|
|
635
|
-
};
|
|
636
|
-
}
|
|
637
|
-
else
|
|
638
|
-
res = await stub.getQueryResultWithPagination(JSON.stringify(rawInput), limit, bookmark?.toString());
|
|
639
|
-
return res;
|
|
486
|
+
async queryResult(stub, rawInput,
|
|
487
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
488
|
+
...args) {
|
|
489
|
+
return stub.getQueryResult(JSON.stringify(rawInput));
|
|
640
490
|
}
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
return accum;
|
|
646
|
-
}, {});
|
|
647
|
-
let finalModel = results.pop();
|
|
648
|
-
for (const res of results) {
|
|
649
|
-
finalModel = Object.assign({}, extract(finalModel), extract(res));
|
|
650
|
-
}
|
|
651
|
-
return finalModel;
|
|
491
|
+
async queryResultPaginated(stub, rawInput, limit = 250, page, bookmark,
|
|
492
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
493
|
+
...args) {
|
|
494
|
+
return stub.getQueryResultWithPagination(JSON.stringify(rawInput), limit, bookmark?.toString());
|
|
652
495
|
}
|
|
653
496
|
/**
|
|
654
497
|
* @description Decodes binary data to string
|
|
@@ -673,6 +516,7 @@ class FabricContractAdapter extends for_couchdb_1.CouchDBAdapter {
|
|
|
673
516
|
let baseFlags = Object.assign({
|
|
674
517
|
segregated: false,
|
|
675
518
|
rebuildWithTransient: false,
|
|
519
|
+
fullySegregated: false,
|
|
676
520
|
}, flags);
|
|
677
521
|
const stubFromFlags = flags.stub || flags.stub;
|
|
678
522
|
const identityFromFlags = flags.identity ||
|
|
@@ -797,15 +641,16 @@ class FabricContractAdapter extends for_couchdb_1.CouchDBAdapter {
|
|
|
797
641
|
* FabricContractAdapter-->>Caller: results
|
|
798
642
|
*/
|
|
799
643
|
async raw(rawInput, docsOnly = true, ...args) {
|
|
800
|
-
const { log, ctx } = this.logCtx(args, this.raw);
|
|
644
|
+
const { log, ctx, ctxArgs } = this.logCtx(args, this.raw);
|
|
645
|
+
const enableSegregates = !args.length || args[0] !== true;
|
|
801
646
|
const { skip, limit } = rawInput;
|
|
802
647
|
let iterator;
|
|
803
|
-
|
|
648
|
+
let resp = { docs: [], bookmark: undefined };
|
|
804
649
|
if (limit || skip) {
|
|
805
650
|
delete rawInput["limit"];
|
|
806
651
|
delete rawInput["skip"];
|
|
807
652
|
log.debug(`Retrieving paginated iterator: limit: ${limit}/ skip: ${skip}`);
|
|
808
|
-
const response = (await this.queryResultPaginated(ctx.stub, rawInput, limit || Number.MAX_VALUE, skip?.toString(), rawInput["bookmark"], ctx));
|
|
653
|
+
const response = (await this.queryResultPaginated(ctx.stub, rawInput, limit || Number.MAX_VALUE, skip?.toString(), rawInput["bookmark"], ...[ctx]));
|
|
809
654
|
resp.bookmark = response.metadata.bookmark;
|
|
810
655
|
iterator = response.iterator;
|
|
811
656
|
}
|
|
@@ -816,8 +661,30 @@ class FabricContractAdapter extends for_couchdb_1.CouchDBAdapter {
|
|
|
816
661
|
log.debug("Iterator acquired");
|
|
817
662
|
resp.docs = (await this.resultIterator(log, iterator));
|
|
818
663
|
log.debug(`returning ${Array.isArray(resp.docs) ? resp.docs.length : 1} results`);
|
|
819
|
-
|
|
664
|
+
const collections = enableSegregates ? ctx.getReadCollections() : undefined;
|
|
665
|
+
const segregated = [];
|
|
666
|
+
if (collections && collections.length) {
|
|
667
|
+
// Restore limit/skip for segregated queries (they were removed above)
|
|
668
|
+
const segregatedInput = { ...rawInput };
|
|
669
|
+
if (limit)
|
|
670
|
+
segregatedInput.limit = limit;
|
|
671
|
+
if (skip)
|
|
672
|
+
segregatedInput.skip = skip;
|
|
673
|
+
for (const collection of collections) {
|
|
674
|
+
segregated.push(await this.forPrivate(collection).raw(segregatedInput, false, true, ...ctxArgs));
|
|
675
|
+
}
|
|
676
|
+
// choose the response with the most results
|
|
677
|
+
resp = segregated.reduce((acc, curr) => {
|
|
678
|
+
if (!acc)
|
|
679
|
+
return curr;
|
|
680
|
+
if (curr.docs && curr.docs.length >= acc?.docs.length)
|
|
681
|
+
return curr;
|
|
682
|
+
return acc;
|
|
683
|
+
}, resp);
|
|
684
|
+
}
|
|
685
|
+
if (docsOnly) {
|
|
820
686
|
return resp.docs;
|
|
687
|
+
}
|
|
821
688
|
return resp;
|
|
822
689
|
}
|
|
823
690
|
async view(
|
|
@@ -834,14 +701,6 @@ class FabricContractAdapter extends for_couchdb_1.CouchDBAdapter {
|
|
|
834
701
|
Statement() {
|
|
835
702
|
return new FabricContractStatement_1.FabricStatement(this);
|
|
836
703
|
}
|
|
837
|
-
async createAll(tableName, id, model, ...args) {
|
|
838
|
-
if (id.length !== model.length)
|
|
839
|
-
throw new db_decorators_1.InternalError("Ids and models must have the same length");
|
|
840
|
-
const { log, ctxArgs } = this.logCtx(args, this.createAll);
|
|
841
|
-
const tableLabel = decorator_validation_1.Model.tableName(tableName);
|
|
842
|
-
log.debug(`Creating ${id.length} entries ${tableLabel} table`);
|
|
843
|
-
return Promise.all(id.map((i, count) => this.create(tableName, i, model[count], ...ctxArgs)));
|
|
844
|
-
}
|
|
845
704
|
async updateAll(tableName, id, model, ...args) {
|
|
846
705
|
if (id.length !== model.length)
|
|
847
706
|
throw new db_decorators_1.InternalError("Ids and models must have the same length");
|
|
@@ -858,115 +717,98 @@ class FabricContractAdapter extends for_couchdb_1.CouchDBAdapter {
|
|
|
858
717
|
*/
|
|
859
718
|
prepare(model, ...args) {
|
|
860
719
|
const { log, ctx } = this.logCtx(args, this.prepare);
|
|
720
|
+
const split = decorator_validation_1.Model.segregate(model);
|
|
861
721
|
const tableName = decorator_validation_1.Model.tableName(model.constructor);
|
|
862
722
|
const pk = decorator_validation_1.Model.pk(model.constructor);
|
|
863
|
-
const
|
|
723
|
+
const id = model[pk];
|
|
864
724
|
const isMirror = ctx.getOrUndefined("mirror");
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
if (this.isReserved(mappedProp))
|
|
887
|
-
throw new db_decorators_1.InternalError(`Property name ${mappedProp} is reserved`);
|
|
888
|
-
val = val instanceof Date ? new Date(val) : val;
|
|
889
|
-
accum[mappedProp] = val;
|
|
890
|
-
return accum;
|
|
891
|
-
}, {});
|
|
892
|
-
// Add table identifier
|
|
893
|
-
record[for_couchdb_1.CouchDBKeys.TABLE] = tableName;
|
|
725
|
+
const mapToRecord = function (obj, keysOverride) {
|
|
726
|
+
if (keysOverride)
|
|
727
|
+
keysOverride = [...new Set([...keysOverride, pk])];
|
|
728
|
+
const result = Object.entries(obj).reduce((accum, [key, val]) => {
|
|
729
|
+
if (typeof val === "undefined")
|
|
730
|
+
return accum;
|
|
731
|
+
if (keysOverride && !keysOverride.includes(key))
|
|
732
|
+
return accum;
|
|
733
|
+
const mappedProp = decorator_validation_1.Model.columnName(model, key);
|
|
734
|
+
if (this.isReserved(mappedProp))
|
|
735
|
+
throw new db_decorators_1.InternalError(`Property name ${mappedProp} is reserved`);
|
|
736
|
+
val = val instanceof Date ? new Date(val) : val;
|
|
737
|
+
accum[mappedProp] = val;
|
|
738
|
+
return accum;
|
|
739
|
+
}, {});
|
|
740
|
+
if (Object.keys(result).filter((k) => Boolean(result[k])).length) {
|
|
741
|
+
// Add table identifier
|
|
742
|
+
result[for_couchdb_1.CouchDBKeys.TABLE] = tableName;
|
|
743
|
+
}
|
|
744
|
+
return result;
|
|
745
|
+
}.bind(this);
|
|
894
746
|
log.silly(`Preparing record for ${tableName} table with pk ${model[pk]}`);
|
|
747
|
+
const segregatedWriteKeys = ctx.getSegregatedWrites();
|
|
748
|
+
const segregatedWrites = {};
|
|
749
|
+
if (segregatedWriteKeys) {
|
|
750
|
+
for (const collection in segregatedWriteKeys) {
|
|
751
|
+
segregatedWrites[collection] = segregatedWrites[collection] || {};
|
|
752
|
+
segregatedWrites[collection][id] = mapToRecord(ctx.getOrUndefined("forceSegregateWrite")
|
|
753
|
+
? split.model
|
|
754
|
+
: split.transient, segregatedWriteKeys[collection]);
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
// In mirror mode, the record should contain ALL model properties (full copy)
|
|
758
|
+
const record = isMirror ? mapToRecord(model) : mapToRecord(split.model);
|
|
895
759
|
return {
|
|
896
760
|
record,
|
|
897
761
|
id: model[pk],
|
|
898
|
-
transient:
|
|
762
|
+
transient: !isMirror && split.transient && Object.keys(split.transient).length
|
|
763
|
+
? mapToRecord(split.transient)
|
|
764
|
+
: undefined,
|
|
765
|
+
segregated: isMirror ? undefined : segregatedWrites,
|
|
899
766
|
};
|
|
900
767
|
}
|
|
901
|
-
/**
|
|
902
|
-
* @description Gets the property names that belong to a specific private/shared collection
|
|
903
|
-
* @summary Looks up per-property metadata set by @privateData/@sharedData decorators
|
|
904
|
-
* to determine which properties are decorated for the given collection name.
|
|
905
|
-
*/
|
|
906
|
-
getFieldsForCollection(model, collection) {
|
|
907
|
-
const constr = model.constructor;
|
|
908
|
-
const properties = decoration_1.Metadata.validatableProperties(constr);
|
|
909
|
-
if (!properties)
|
|
910
|
-
return [];
|
|
911
|
-
return properties.filter((prop) => {
|
|
912
|
-
const privateKey = decoration_1.Metadata.key(constants_1.FabricModelKeys.PRIVATE, prop);
|
|
913
|
-
const privateMeta = decoration_1.Metadata.get(constr, privateKey);
|
|
914
|
-
if (privateMeta?.collections?.includes(collection))
|
|
915
|
-
return true;
|
|
916
|
-
const sharedKey = decoration_1.Metadata.key(constants_1.FabricModelKeys.SHARED, prop);
|
|
917
|
-
const sharedMeta = decoration_1.Metadata.get(constr, sharedKey);
|
|
918
|
-
if (sharedMeta?.collections?.includes(collection))
|
|
919
|
-
return true;
|
|
920
|
-
return false;
|
|
921
|
-
});
|
|
922
|
-
}
|
|
923
768
|
revert(obj, clazz, id, transient, ...args) {
|
|
924
769
|
const { log, ctx } = this.logCtx(args, this.revert);
|
|
925
770
|
const ob = {};
|
|
926
771
|
const pk = decorator_validation_1.Model.pk(clazz);
|
|
772
|
+
const pkProps = decorator_validation_1.Model.pkProps(clazz);
|
|
773
|
+
if (pkProps?.type === Number && typeof id === "string") {
|
|
774
|
+
id = Number(id);
|
|
775
|
+
}
|
|
927
776
|
ob[pk] = id;
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
return
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
777
|
+
log.silly(`Rebuilding model ${clazz.name} id ${id}`);
|
|
778
|
+
function mapToModel(r) {
|
|
779
|
+
const m = (typeof clazz === "string" ? decorator_validation_1.Model.build(ob, clazz) : new clazz(ob));
|
|
780
|
+
const attributes = decorator_validation_1.Model.getAttributes(clazz);
|
|
781
|
+
const keys = attributes.length ? attributes : Object.keys(m);
|
|
782
|
+
return keys
|
|
783
|
+
.filter((k) => k !== pk)
|
|
784
|
+
.reduce((accum, key) => {
|
|
785
|
+
accum[key] =
|
|
786
|
+
r[decorator_validation_1.Model.columnName(accum, key)];
|
|
787
|
+
return accum;
|
|
788
|
+
}, m);
|
|
789
|
+
}
|
|
790
|
+
let result = mapToModel(obj);
|
|
791
|
+
if (transient && !this.shouldRebuildWithTransient(ctx)) {
|
|
792
|
+
log.debug(`filtering transient properties: ${Object.keys(transient).join(", ")}`);
|
|
793
|
+
result = Object.entries(result).reduce((acc, [key, v]) => {
|
|
794
|
+
if (key === pk || !(key in transient)) {
|
|
795
|
+
acc[key] = v;
|
|
796
|
+
}
|
|
797
|
+
return acc;
|
|
798
|
+
}, new clazz());
|
|
943
799
|
}
|
|
944
800
|
return result;
|
|
945
801
|
}
|
|
946
|
-
shouldRebuildWithTransient(ctx
|
|
802
|
+
shouldRebuildWithTransient(ctx) {
|
|
947
803
|
if (!ctx)
|
|
948
804
|
return false;
|
|
949
805
|
if (ctx.getOrUndefined("rebuildWithTransient"))
|
|
950
806
|
return true;
|
|
807
|
+
const operation = ctx.getOrUndefined("operation");
|
|
951
808
|
if (!operation)
|
|
952
|
-
return
|
|
809
|
+
return true;
|
|
953
810
|
const op = operation.toString().toLowerCase();
|
|
954
|
-
return (
|
|
955
|
-
op.includes("find") ||
|
|
956
|
-
op.includes("query") ||
|
|
957
|
-
op.includes("statement") ||
|
|
958
|
-
op.includes("page"));
|
|
959
|
-
}
|
|
960
|
-
hasPublicState(model, clazz) {
|
|
961
|
-
if (!model)
|
|
962
|
-
return false;
|
|
963
|
-
const instance = decorator_validation_1.Model.isModel(model)
|
|
964
|
-
? model
|
|
965
|
-
: decorator_validation_1.Model.build(model, clazz.name);
|
|
966
|
-
const split = decorator_validation_1.Model.segregate(instance);
|
|
967
|
-
const pkProp = decorator_validation_1.Model.pk(clazz);
|
|
968
|
-
return Object.keys(split.model).some((key) => key !== pkProp &&
|
|
969
|
-
typeof split.model[key] !== "undefined");
|
|
811
|
+
return !core_1.TransactionOperationKeys.map((k) => k.toLowerCase()).includes(op);
|
|
970
812
|
}
|
|
971
813
|
getContextMsp(context) {
|
|
972
814
|
const identity = context.get("identity");
|
|
@@ -1099,11 +941,19 @@ class FabricContractAdapter extends for_couchdb_1.CouchDBAdapter {
|
|
|
1099
941
|
super.decoration();
|
|
1100
942
|
decoration_1.Decoration.flavouredAs(constants_1.FabricFlavour)
|
|
1101
943
|
.for(core_1.PersistenceKeys.CREATED_BY)
|
|
1102
|
-
.define(
|
|
944
|
+
.define({
|
|
945
|
+
decorator: function createdBy() {
|
|
946
|
+
return (0, decoration_1.apply)((0, db_decorators_1.onCreate)(createdByOnFabricCreateUpdate), (0, decoration_1.propMetadata)(core_1.PersistenceKeys.CREATED_BY, {}));
|
|
947
|
+
},
|
|
948
|
+
})
|
|
1103
949
|
.apply();
|
|
1104
950
|
decoration_1.Decoration.flavouredAs(constants_1.FabricFlavour)
|
|
1105
951
|
.for(core_1.PersistenceKeys.UPDATED_BY)
|
|
1106
|
-
.define(
|
|
952
|
+
.define({
|
|
953
|
+
decorator: function createdBy() {
|
|
954
|
+
return (0, decoration_1.apply)((0, db_decorators_1.onCreateUpdate)(createdByOnFabricCreateUpdate), (0, decoration_1.propMetadata)(core_1.PersistenceKeys.UPDATED_BY, {}));
|
|
955
|
+
},
|
|
956
|
+
})
|
|
1107
957
|
.apply();
|
|
1108
958
|
decoration_1.Decoration.flavouredAs(constants_1.FabricFlavour)
|
|
1109
959
|
.for(core_1.PersistenceKeys.COLUMN)
|