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