@decaf-ts/for-fabric 0.13.9 → 0.13.11
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/lib/cjs/contracts/ContractAdapter.cjs +194 -148
- package/lib/cjs/contracts/ContractAdapter.cjs.map +1 -1
- package/lib/cjs/contracts/FabricContractRepository.cjs +10 -2
- package/lib/cjs/contracts/FabricContractRepository.cjs.map +1 -1
- package/lib/cjs/version.cjs +3 -3
- package/lib/esm/contracts/ContractAdapter.js +196 -150
- package/lib/esm/contracts/ContractAdapter.js.map +1 -1
- package/lib/esm/contracts/FabricContractRepository.js +10 -2
- package/lib/esm/contracts/FabricContractRepository.js.map +1 -1
- package/lib/esm/version.js +3 -3
- package/lib/types/contracts/ContractAdapter.d.cts +6 -3
- package/lib/types/contracts/ContractAdapter.d.mts +6 -3
- package/lib/types/shared/types.d.cts +11 -0
- package/lib/types/shared/types.d.mts +11 -0
- package/lib/types/version.d.cts +3 -3
- package/lib/types/version.d.mts +3 -3
- package/package.json +1 -1
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { CouchDBAdapter, CouchDBKeys, } from "@decaf-ts/for-couchdb";
|
|
1
|
+
import { CouchDBAdapter, CouchDBKeys, ensureDeterministicSort as couchEnsureDeterministicSort, getSortDirection, getSortFields, warnScanProneMangoOperators, } from "@decaf-ts/for-couchdb";
|
|
2
2
|
import { Model, ValidationKeys } from "@decaf-ts/decorator-validation";
|
|
3
3
|
import { createHash } from "crypto";
|
|
4
4
|
import { FabricContractContext } from "./ContractContext.js";
|
|
5
5
|
import { BadRequestError, BulkCrudOperationKeys, ConflictError, InternalError, NotFoundError, onCreate, onCreateUpdate, SerializationError, } from "@decaf-ts/db-decorators";
|
|
6
6
|
import { Object as FabricObject, Property, Property as FabricProperty, } from "fabric-contract-api";
|
|
7
7
|
import { Logging } from "@decaf-ts/logging";
|
|
8
|
-
import { PersistenceKeys, UnsupportedError, Adapter, QueryError, PagingError, MigrationError, ObserverError, AuthorizationError, ForbiddenError, ConnectionError, Context, TransactionOperationKeys, promiseSequence, resolveBulkSequenceResult, } from "@decaf-ts/core";
|
|
8
|
+
import { PersistenceKeys, UnsupportedError, Adapter, QueryError, PagingError, MigrationError, ObserverError, AuthorizationError, ForbiddenError, ConnectionError, Context, OrderDirection, TransactionOperationKeys, promiseSequence, resolveBulkSequenceResult, } from "@decaf-ts/core";
|
|
9
9
|
import { FabricContractRepository } from "./FabricContractRepository.js";
|
|
10
10
|
import { FabricStatement } from "./FabricContractStatement.js";
|
|
11
11
|
import { FabricContractSequence } from "./FabricContractSequence.js";
|
|
@@ -116,114 +116,104 @@ export class FabricContractAdapter extends CouchDBAdapter {
|
|
|
116
116
|
.update(FabricContractAdapter.stableStringify(relevant))
|
|
117
117
|
.digest("base64url");
|
|
118
118
|
}
|
|
119
|
-
static
|
|
120
|
-
if (typeof bookmark !== "string")
|
|
121
|
-
return undefined;
|
|
122
|
-
if (!bookmark.startsWith(FabricContractAdapter.PRIVATE_BOOKMARK_PREFIX))
|
|
123
|
-
return undefined;
|
|
124
|
-
const raw = bookmark.slice(FabricContractAdapter.PRIVATE_BOOKMARK_PREFIX.length);
|
|
125
|
-
try {
|
|
126
|
-
const parsed = JSON.parse(Buffer.from(raw, "base64url").toString("utf8"));
|
|
127
|
-
if (!parsed ||
|
|
128
|
-
typeof parsed !== "object" ||
|
|
129
|
-
typeof parsed.sortField !== "string" ||
|
|
130
|
-
typeof parsed.direction !== "string" ||
|
|
131
|
-
typeof parsed.idField !== "string" ||
|
|
132
|
-
typeof parsed.lastId !== "string"
|
|
133
|
-
|| typeof parsed.queryHash !== "string") {
|
|
134
|
-
return undefined;
|
|
135
|
-
}
|
|
136
|
-
return {
|
|
137
|
-
sortField: parsed.sortField,
|
|
138
|
-
direction: parsed.direction === "desc" ? "desc" : "asc",
|
|
139
|
-
idField: parsed.idField,
|
|
140
|
-
lastValue: parsed.lastValue,
|
|
141
|
-
lastId: parsed.lastId,
|
|
142
|
-
queryHash: parsed.queryHash,
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
catch {
|
|
146
|
-
return undefined;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
static buildSyntheticPrivateBookmark(cursor) {
|
|
150
|
-
return `${FabricContractAdapter.PRIVATE_BOOKMARK_PREFIX}${Buffer.from(JSON.stringify(cursor)).toString("base64url")}`;
|
|
151
|
-
}
|
|
152
|
-
static buildPrivateCursorSelector(selector, sortField, direction, cursor) {
|
|
119
|
+
static buildPrivateContinuationSelector(selector, sortField, direction, idField, lastValue, lastId, variant) {
|
|
153
120
|
const cmp = direction === "desc" ? "$lt" : "$gt";
|
|
154
|
-
|
|
121
|
+
const idCmp = direction === "desc" ? "$lt" : "$gt";
|
|
122
|
+
if (idField === sortField) {
|
|
155
123
|
const continuation = {
|
|
156
124
|
[sortField]: {
|
|
157
|
-
[cmp]:
|
|
125
|
+
[cmp]: lastValue,
|
|
158
126
|
},
|
|
159
127
|
};
|
|
160
|
-
if (!Object.keys(selector
|
|
128
|
+
if (!selector || !Object.keys(selector).length) {
|
|
161
129
|
return continuation;
|
|
130
|
+
}
|
|
162
131
|
return {
|
|
163
132
|
$and: [selector, continuation],
|
|
164
133
|
};
|
|
165
134
|
}
|
|
166
|
-
const
|
|
167
|
-
|
|
135
|
+
const clauses = variant === "same"
|
|
136
|
+
? [
|
|
168
137
|
{
|
|
169
138
|
[sortField]: {
|
|
170
|
-
|
|
139
|
+
$eq: lastValue,
|
|
171
140
|
},
|
|
172
141
|
},
|
|
173
142
|
{
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
$eq: cursor.lastValue,
|
|
178
|
-
},
|
|
179
|
-
},
|
|
180
|
-
{
|
|
181
|
-
[cursor.idField]: {
|
|
182
|
-
[cmp]: cursor.lastId,
|
|
183
|
-
},
|
|
184
|
-
},
|
|
185
|
-
],
|
|
143
|
+
[idField]: {
|
|
144
|
+
[idCmp]: lastId,
|
|
145
|
+
},
|
|
186
146
|
},
|
|
187
|
-
]
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
147
|
+
]
|
|
148
|
+
: [
|
|
149
|
+
{
|
|
150
|
+
[sortField]: {
|
|
151
|
+
[cmp]: lastValue,
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
];
|
|
155
|
+
if (!selector || !Object.keys(selector).length) {
|
|
156
|
+
return clauses.length === 1
|
|
157
|
+
? clauses[0]
|
|
158
|
+
: {
|
|
159
|
+
$and: clauses,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
191
162
|
return {
|
|
192
|
-
$and: [selector,
|
|
163
|
+
$and: [selector, ...clauses],
|
|
193
164
|
};
|
|
194
165
|
}
|
|
195
|
-
static
|
|
196
|
-
|
|
197
|
-
|
|
166
|
+
static buildOrlessKeysetContinuationQueries(query, cursor, pageSize) {
|
|
167
|
+
const baseQuery = { ...query };
|
|
168
|
+
delete baseQuery.skip;
|
|
169
|
+
delete baseQuery.bookmark;
|
|
170
|
+
delete baseQuery.limit;
|
|
171
|
+
if (cursor.idField === cursor.sortField) {
|
|
172
|
+
const single = {
|
|
173
|
+
...baseQuery,
|
|
174
|
+
selector: FabricContractAdapter.buildPrivateContinuationSelector((baseQuery.selector || {}), cursor.sortField, cursor.direction, cursor.idField, cursor.lastValue, cursor.lastId, "same"),
|
|
175
|
+
limit: pageSize + 1,
|
|
176
|
+
};
|
|
177
|
+
return [single];
|
|
198
178
|
}
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
|
|
179
|
+
const first = {
|
|
180
|
+
...baseQuery,
|
|
181
|
+
selector: FabricContractAdapter.buildPrivateContinuationSelector((baseQuery.selector || {}), cursor.sortField, cursor.direction, cursor.idField, cursor.lastValue, cursor.lastId, "same"),
|
|
182
|
+
limit: pageSize + 1,
|
|
183
|
+
};
|
|
184
|
+
const second = {
|
|
185
|
+
...baseQuery,
|
|
186
|
+
selector: FabricContractAdapter.buildPrivateContinuationSelector((baseQuery.selector || {}), cursor.sortField, cursor.direction, cursor.idField, cursor.lastValue, cursor.lastId, "next"),
|
|
187
|
+
limit: pageSize + 1,
|
|
188
|
+
};
|
|
189
|
+
return [first, second];
|
|
190
|
+
}
|
|
191
|
+
static ensureDeterministicPrivateSort(query, tieBreaker) {
|
|
192
|
+
if (!Array.isArray(query.sort) || !query.sort.length) {
|
|
193
|
+
throw new PagingError("Private Mango pagination requires an explicit sort. Add orderBy(...) so a stable generated index can be selected.");
|
|
202
194
|
}
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
195
|
+
const direction = (getSortDirection(query) ||
|
|
196
|
+
OrderDirection.ASC);
|
|
197
|
+
couchEnsureDeterministicSort(query, tieBreaker, direction);
|
|
198
|
+
const sortFields = getSortFields(query);
|
|
199
|
+
const sortField = sortFields[0];
|
|
200
|
+
if (!sortField) {
|
|
201
|
+
throw new PagingError("Private Mango pagination requires a valid first sort field.");
|
|
207
202
|
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
const [field, dir] = Object.entries(entry || {})[0] || [];
|
|
211
|
-
return (field === idField && String(dir || "").toLowerCase() === direction);
|
|
212
|
-
});
|
|
213
|
-
if (!hasIdTieBreaker && idField !== sortField) {
|
|
214
|
-
query.sort = [...query.sort, { [idField]: direction }];
|
|
203
|
+
if (!sortFields.includes(tieBreaker)) {
|
|
204
|
+
throw new PagingError(`Private Mango pagination requires tie-breaker sort field "${tieBreaker}".`);
|
|
215
205
|
}
|
|
216
206
|
return {
|
|
217
207
|
sortField,
|
|
218
208
|
direction: direction,
|
|
219
|
-
idField,
|
|
209
|
+
idField: tieBreaker,
|
|
220
210
|
};
|
|
221
211
|
}
|
|
222
212
|
static parsePrivateResultValue(value) {
|
|
223
213
|
try {
|
|
224
214
|
const parsed = JSON.parse(value.toString("utf8"));
|
|
225
|
-
if (!parsed || typeof parsed !== "object") {
|
|
226
|
-
throw new Error("Private query result is not
|
|
215
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
216
|
+
throw new Error("Private query result is not a JSON object");
|
|
227
217
|
}
|
|
228
218
|
return parsed;
|
|
229
219
|
}
|
|
@@ -231,6 +221,102 @@ export class FabricContractAdapter extends CouchDBAdapter {
|
|
|
231
221
|
throw new SerializationError(`Failed to parse private query result while building bookmark: ${e}`);
|
|
232
222
|
}
|
|
233
223
|
}
|
|
224
|
+
static async executePrivateMangoPageQueries(stub, collection, queries, pageSize, log) {
|
|
225
|
+
const paged = [];
|
|
226
|
+
for (const query of queries) {
|
|
227
|
+
if (paged.length >= pageSize + 1) {
|
|
228
|
+
break;
|
|
229
|
+
}
|
|
230
|
+
const remaining = pageSize + 1 - paged.length;
|
|
231
|
+
const queryForExecution = {
|
|
232
|
+
...query,
|
|
233
|
+
limit: remaining,
|
|
234
|
+
};
|
|
235
|
+
delete queryForExecution.skip;
|
|
236
|
+
delete queryForExecution.bookmark;
|
|
237
|
+
log.debug(`Querying collection ${collection} for ${JSON.stringify(queryForExecution)}`);
|
|
238
|
+
const response = await stub.getPrivateDataQueryResult(collection, JSON.stringify(queryForExecution));
|
|
239
|
+
const iterator = (response.iterator ||
|
|
240
|
+
response);
|
|
241
|
+
try {
|
|
242
|
+
while (paged.length < pageSize + 1) {
|
|
243
|
+
const res = await iterator.next();
|
|
244
|
+
if (res.done)
|
|
245
|
+
break;
|
|
246
|
+
if (!res.value || !res.value.value)
|
|
247
|
+
continue;
|
|
248
|
+
paged.push({
|
|
249
|
+
key: res.value.key,
|
|
250
|
+
value: Buffer.isBuffer(res.value.value)
|
|
251
|
+
? res.value.value
|
|
252
|
+
: Buffer.from(res.value.value.toString("utf8")),
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
finally {
|
|
257
|
+
await iterator.close();
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return paged;
|
|
261
|
+
}
|
|
262
|
+
static toFabricPaginationResponse(docs, bookmark) {
|
|
263
|
+
let idx = 0;
|
|
264
|
+
const iterator = {
|
|
265
|
+
async next() {
|
|
266
|
+
if (idx < docs.length) {
|
|
267
|
+
return {
|
|
268
|
+
value: docs[idx++],
|
|
269
|
+
done: false,
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
return {
|
|
273
|
+
value: undefined,
|
|
274
|
+
done: true,
|
|
275
|
+
};
|
|
276
|
+
},
|
|
277
|
+
async close() { },
|
|
278
|
+
};
|
|
279
|
+
return {
|
|
280
|
+
iterator: iterator,
|
|
281
|
+
metadata: {
|
|
282
|
+
fetchedRecordsCount: docs.length,
|
|
283
|
+
bookmark,
|
|
284
|
+
},
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
static parseSyntheticPrivateBookmark(bookmark) {
|
|
288
|
+
if (typeof bookmark !== "string")
|
|
289
|
+
return undefined;
|
|
290
|
+
if (!bookmark.startsWith(FabricContractAdapter.PRIVATE_BOOKMARK_PREFIX))
|
|
291
|
+
return undefined;
|
|
292
|
+
const raw = bookmark.slice(FabricContractAdapter.PRIVATE_BOOKMARK_PREFIX.length);
|
|
293
|
+
try {
|
|
294
|
+
const parsed = JSON.parse(Buffer.from(raw, "base64url").toString("utf8"));
|
|
295
|
+
if (!parsed ||
|
|
296
|
+
typeof parsed !== "object" ||
|
|
297
|
+
typeof parsed.sortField !== "string" ||
|
|
298
|
+
typeof parsed.direction !== "string" ||
|
|
299
|
+
typeof parsed.idField !== "string" ||
|
|
300
|
+
typeof parsed.lastId !== "string"
|
|
301
|
+
|| typeof parsed.queryHash !== "string") {
|
|
302
|
+
return undefined;
|
|
303
|
+
}
|
|
304
|
+
return {
|
|
305
|
+
sortField: parsed.sortField,
|
|
306
|
+
direction: parsed.direction === "desc" ? "desc" : "asc",
|
|
307
|
+
idField: parsed.idField,
|
|
308
|
+
lastValue: parsed.lastValue,
|
|
309
|
+
lastId: parsed.lastId,
|
|
310
|
+
queryHash: parsed.queryHash,
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
catch {
|
|
314
|
+
return undefined;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
static buildSyntheticPrivateBookmark(cursor) {
|
|
318
|
+
return `${FabricContractAdapter.PRIVATE_BOOKMARK_PREFIX}${Buffer.from(JSON.stringify(cursor)).toString("base64url")}`;
|
|
319
|
+
}
|
|
234
320
|
getClient() {
|
|
235
321
|
throw new UnsupportedError("Client is not supported in Fabric contracts");
|
|
236
322
|
}
|
|
@@ -623,76 +709,48 @@ export class FabricContractAdapter extends CouchDBAdapter {
|
|
|
623
709
|
}
|
|
624
710
|
case "queryResultPaginated": {
|
|
625
711
|
const [stub, rawInput, limit, skip, bookmark, ...args] = argsList;
|
|
626
|
-
const { log } = thisArg["logCtx"](args, prop);
|
|
712
|
+
const { log, ctx } = thisArg["logCtx"](args, prop);
|
|
627
713
|
if (skip !== undefined && skip !== null && Number(skip) > 0) {
|
|
628
|
-
throw new PagingError("Private
|
|
714
|
+
throw new PagingError("Private Mango pagination does not support skip/offset pagination. Use the returned synthetic bookmark instead.");
|
|
629
715
|
}
|
|
630
716
|
const pageSize = Math.max(1, Number(limit) || 250);
|
|
631
717
|
const query = { ...rawInput };
|
|
632
|
-
const
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
718
|
+
const queryInput = query;
|
|
719
|
+
const queryPkField = typeof queryInput["__pkField"] === "string" &&
|
|
720
|
+
queryInput["__pkField"].trim().length
|
|
721
|
+
? String(queryInput["__pkField"])
|
|
722
|
+
: String(ctx?.getOrUndefined("privatePaginationTieBreaker") ||
|
|
723
|
+
"id");
|
|
724
|
+
delete queryInput["__pkField"];
|
|
638
725
|
const syntheticCursor = FabricContractAdapter.parseSyntheticPrivateBookmark(bookmark);
|
|
639
726
|
if (bookmark !== undefined &&
|
|
640
727
|
bookmark !== null &&
|
|
641
728
|
bookmark !== "" &&
|
|
642
729
|
!syntheticCursor) {
|
|
643
|
-
throw new PagingError("Private
|
|
730
|
+
throw new PagingError("Private Mango pagination only supports adapter-generated synthetic bookmarks.");
|
|
731
|
+
}
|
|
732
|
+
if (!Array.isArray(query.sort) || !query.sort.length) {
|
|
733
|
+
throw new PagingError("Private Mango pagination requires an explicit sort. Add orderBy(...) so a stable generated index can be selected.");
|
|
644
734
|
}
|
|
735
|
+
const { sortField, direction: sortDirection, idField } = FabricContractAdapter.ensureDeterministicPrivateSort(query, queryPkField);
|
|
736
|
+
warnScanProneMangoOperators(query.selector || {}, log);
|
|
645
737
|
log.debug(`Private paginated query input collection=${collection} limit=${limit} skip=${skip} bookmark=${bookmark} sortField=${sortField} direction=${sortDirection} synthetic=${Boolean(syntheticCursor)}`);
|
|
646
|
-
delete query.skip;
|
|
647
|
-
delete query.bookmark;
|
|
648
738
|
query.limit = pageSize + 1;
|
|
649
739
|
const queryHash = FabricContractAdapter.privateQueryHash(query);
|
|
650
740
|
if (syntheticCursor) {
|
|
651
741
|
if (syntheticCursor.queryHash !== queryHash) {
|
|
652
|
-
throw new PagingError("Private
|
|
653
|
-
}
|
|
654
|
-
query.selector = FabricContractAdapter.buildPrivateCursorSelector(query.selector || {}, syntheticCursor.sortField, syntheticCursor.direction, syntheticCursor);
|
|
655
|
-
}
|
|
656
|
-
log.debug(`Querying collection ${collection} for ${JSON.stringify(query)}`);
|
|
657
|
-
const response = await stub.getPrivateDataQueryResult(collection, JSON.stringify(query));
|
|
658
|
-
const iterator = (response.iterator ||
|
|
659
|
-
response);
|
|
660
|
-
const responseMetadata = response.metadata ||
|
|
661
|
-
iterator.metadata ||
|
|
662
|
-
{};
|
|
663
|
-
log.debug(`Private paginated response collection=${collection} metadata=${JSON.stringify(responseMetadata)}`);
|
|
664
|
-
log.verbose(`iterator from collection ${collection} received`);
|
|
665
|
-
const paged = [];
|
|
666
|
-
let hasMore = false;
|
|
667
|
-
try {
|
|
668
|
-
while (true) {
|
|
669
|
-
const res = await iterator.next();
|
|
670
|
-
if (res.done)
|
|
671
|
-
break;
|
|
672
|
-
if (res.value && res.value.value) {
|
|
673
|
-
const key = res.value.key;
|
|
674
|
-
if (paged.length < pageSize) {
|
|
675
|
-
paged.push({
|
|
676
|
-
key,
|
|
677
|
-
value: Buffer.isBuffer(res.value.value)
|
|
678
|
-
? res.value.value
|
|
679
|
-
: Buffer.from(res.value.value.toString("utf8")),
|
|
680
|
-
});
|
|
681
|
-
}
|
|
682
|
-
else {
|
|
683
|
-
hasMore = true;
|
|
684
|
-
break; // early exit
|
|
685
|
-
}
|
|
686
|
-
}
|
|
742
|
+
throw new PagingError("Private Mango bookmark does not match the current query.");
|
|
687
743
|
}
|
|
688
744
|
}
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
745
|
+
const queries = syntheticCursor
|
|
746
|
+
? FabricContractAdapter.buildOrlessKeysetContinuationQueries(query, syntheticCursor, pageSize)
|
|
747
|
+
: [{ ...query, limit: pageSize + 1 }];
|
|
748
|
+
const paged = await FabricContractAdapter.executePrivateMangoPageQueries(stub, collection, queries, pageSize, log);
|
|
749
|
+
const hasMore = paged.length > pageSize;
|
|
750
|
+
const docs = hasMore ? paged.slice(0, pageSize) : paged;
|
|
693
751
|
let nextBookmark = "";
|
|
694
|
-
if (hasMore &&
|
|
695
|
-
const last =
|
|
752
|
+
if (hasMore && docs.length) {
|
|
753
|
+
const last = docs[docs.length - 1];
|
|
696
754
|
const lastDoc = FabricContractAdapter.parsePrivateResultValue(last.value);
|
|
697
755
|
if (!(sortField in lastDoc)) {
|
|
698
756
|
throw new PagingError(`Cannot build private pagination bookmark: sorted field "${sortField}" is missing from the last result`);
|
|
@@ -712,24 +770,7 @@ export class FabricContractAdapter extends CouchDBAdapter {
|
|
|
712
770
|
queryHash,
|
|
713
771
|
});
|
|
714
772
|
}
|
|
715
|
-
|
|
716
|
-
let idx = 0;
|
|
717
|
-
const arrayIterator = {
|
|
718
|
-
async next() {
|
|
719
|
-
if (idx < paged.length) {
|
|
720
|
-
return { value: paged[idx++], done: false };
|
|
721
|
-
}
|
|
722
|
-
return { value: undefined, done: true };
|
|
723
|
-
},
|
|
724
|
-
async close() { },
|
|
725
|
-
};
|
|
726
|
-
return {
|
|
727
|
-
iterator: arrayIterator,
|
|
728
|
-
metadata: {
|
|
729
|
-
fetchedRecordsCount: paged.length,
|
|
730
|
-
bookmark: nextBookmark,
|
|
731
|
-
},
|
|
732
|
-
};
|
|
773
|
+
return FabricContractAdapter.toFabricPaginationResponse(docs, nextBookmark);
|
|
733
774
|
}
|
|
734
775
|
default:
|
|
735
776
|
throw new InternalError(`Unsupported method override ${String(prop)}`);
|
|
@@ -799,6 +840,8 @@ export class FabricContractAdapter extends CouchDBAdapter {
|
|
|
799
840
|
segregated: false,
|
|
800
841
|
rebuildWithTransient: false,
|
|
801
842
|
fullySegregated: false,
|
|
843
|
+
strictPrivateMangoPagination: true,
|
|
844
|
+
privatePaginationTieBreaker: "id",
|
|
802
845
|
};
|
|
803
846
|
if (flags instanceof FabricContractContext || flags instanceof Context) {
|
|
804
847
|
flags = flags.toOverrides();
|
|
@@ -933,6 +976,8 @@ export class FabricContractAdapter extends CouchDBAdapter {
|
|
|
933
976
|
const originalInput = { ...rawInput };
|
|
934
977
|
const { skip, limit } = originalInput;
|
|
935
978
|
const bookmark = originalInput["bookmark"];
|
|
979
|
+
const hasSort = Array.isArray(originalInput.sort) &&
|
|
980
|
+
originalInput.sort.length > 0;
|
|
936
981
|
const pkField = typeof originalInput["__pkField"] === "string" &&
|
|
937
982
|
originalInput["__pkField"].trim().length
|
|
938
983
|
? String(originalInput["__pkField"])
|
|
@@ -940,7 +985,8 @@ export class FabricContractAdapter extends CouchDBAdapter {
|
|
|
940
985
|
const hasSkip = skip !== undefined && skip !== null && Number(skip) > 0;
|
|
941
986
|
const hasBookmark = bookmark !== undefined && bookmark !== null && bookmark !== "";
|
|
942
987
|
const paginationActive = Boolean(limit || hasSkip || hasBookmark);
|
|
943
|
-
const shouldPaginate = Boolean(paginationActive &&
|
|
988
|
+
const shouldPaginate = Boolean(paginationActive &&
|
|
989
|
+
(enableSegregates || hasSkip || hasBookmark || hasSort));
|
|
944
990
|
const baseInput = { ...originalInput };
|
|
945
991
|
delete baseInput["skip"];
|
|
946
992
|
delete baseInput["bookmark"];
|