@rvoh/psychic 2.2.1-alpha.2 → 2.2.2
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/cjs/src/controller/index.js +4 -8
- package/dist/cjs/src/generate/helpers/generateControllerContent.js +2 -3
- package/dist/cjs/src/generate/helpers/generateResourceControllerSpecContent.js +24 -34
- package/dist/cjs/src/openapi-renderer/endpoint.js +19 -16
- package/dist/cjs/src/openapi-renderer/helpers/cursorPaginationParamOpenapiProperty.js +6 -0
- package/dist/cjs/src/openapi-renderer/helpers/{safelyAttachScrollPaginationParamsToBodySegment.js → safelyAttachCursorPaginationParamToRequestBodySegment.js} +3 -3
- package/dist/esm/src/controller/index.js +4 -8
- package/dist/esm/src/generate/helpers/generateControllerContent.js +2 -3
- package/dist/esm/src/generate/helpers/generateResourceControllerSpecContent.js +24 -34
- package/dist/esm/src/openapi-renderer/endpoint.js +19 -16
- package/dist/esm/src/openapi-renderer/helpers/cursorPaginationParamOpenapiProperty.js +6 -0
- package/dist/esm/src/openapi-renderer/helpers/{safelyAttachScrollPaginationParamsToBodySegment.js → safelyAttachCursorPaginationParamToRequestBodySegment.js} +3 -3
- package/dist/types/src/openapi-renderer/endpoint.d.ts +30 -7
- package/dist/types/src/openapi-renderer/helpers/cursorPaginationParamOpenapiProperty.d.ts +4 -0
- package/dist/types/src/openapi-renderer/helpers/{safelyAttachScrollPaginationParamsToBodySegment.d.ts → safelyAttachCursorPaginationParamToRequestBodySegment.d.ts} +1 -1
- package/package.json +3 -3
- package/dist/cjs/src/openapi-renderer/helpers/scrollPaginationCursorParamOpenapiProperty.js +0 -6
- package/dist/esm/src/openapi-renderer/helpers/scrollPaginationCursorParamOpenapiProperty.js +0 -6
- package/dist/types/src/openapi-renderer/helpers/scrollPaginationCursorParamOpenapiProperty.d.ts +0 -4
|
@@ -557,14 +557,10 @@ export default class PsychicController {
|
|
|
557
557
|
return data.map(d => this.singleObjectJson(d, opts));
|
|
558
558
|
//
|
|
559
559
|
}
|
|
560
|
-
else if (this.currentOpenapiRenderer?.['paginate']
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
};
|
|
565
|
-
//
|
|
566
|
-
}
|
|
567
|
-
else if (this.currentOpenapiRenderer?.['scrollPaginate'] && Array.isArray(paginatedResults)) {
|
|
560
|
+
else if ((this.currentOpenapiRenderer?.['paginate'] ||
|
|
561
|
+
this.currentOpenapiRenderer?.['cursorPaginate'] ||
|
|
562
|
+
this.currentOpenapiRenderer?.['scrollPaginate']) &&
|
|
563
|
+
Array.isArray(paginatedResults)) {
|
|
568
564
|
return {
|
|
569
565
|
...data,
|
|
570
566
|
results: paginatedResults.map(result => this.singleObjectJson(result, opts)),
|
|
@@ -57,14 +57,13 @@ export default function generateControllerContent({ ancestorName, ancestorImport
|
|
|
57
57
|
status: 200,
|
|
58
58
|
tags: openApiTags,
|
|
59
59
|
description: 'Paginated index of ${pluralize(modelClassName)}',
|
|
60
|
-
|
|
60
|
+
cursorPaginate: true,
|
|
61
61
|
serializerKey: '${forAdmin ? 'adminSummary' : 'summary'}',
|
|
62
62
|
})
|
|
63
63
|
public async index() {
|
|
64
64
|
// const ${pluralizedModelAttributeName} = await ${loadQueryBase}
|
|
65
65
|
// .preloadFor('${forAdmin ? 'adminSummary' : 'summary'}')
|
|
66
|
-
// .
|
|
67
|
-
// .scrollPaginate({ cursor: this.castParam('cursor', 'string', { allowNull: true }) })
|
|
66
|
+
// .cursorPaginate({ cursor: this.castParam('cursor', 'string', { allowNull: true }) })
|
|
68
67
|
// this.ok(${pluralizedModelAttributeName})
|
|
69
68
|
}`;
|
|
70
69
|
else
|
|
@@ -63,6 +63,7 @@ function processAttributes(columnsWithTypes, modelConfig, fullyQualifiedModelNam
|
|
|
63
63
|
dateAttributeIncluded: false,
|
|
64
64
|
datetimeAttributeIncluded: false,
|
|
65
65
|
uuidAttributeIncluded: false,
|
|
66
|
+
uuidAttributes: [],
|
|
66
67
|
uuidArrayAttributes: [],
|
|
67
68
|
dreamImports: [],
|
|
68
69
|
};
|
|
@@ -153,7 +154,7 @@ function processEnumAttribute({ attributeName, isArray, enumValues, dotNotationV
|
|
|
153
154
|
attributeData.comparableOriginalAttributeKeyValues.push(`${attributeName}: ${dotNotationVariable},`);
|
|
154
155
|
if (attributeName !== 'type') {
|
|
155
156
|
attributeData.attributeCreationKeyValues.push(`${attributeName}: ${jsonify(originalEnumValue)},`);
|
|
156
|
-
attributeData.attributeUpdateKeyValues.push(
|
|
157
|
+
attributeData.attributeUpdateKeyValues.push({ attributeName, value: jsonify(updatedEnumValue) });
|
|
157
158
|
attributeData.expectEqualOriginalValue.push(`expect(${dotNotationVariable}).toEqual(${jsonify(originalEnumValue)})`);
|
|
158
159
|
attributeData.expectEqualUpdatedValue.push(`expect(${dotNotationVariable}).toEqual(${jsonify(updatedEnumValue)})`);
|
|
159
160
|
}
|
|
@@ -164,7 +165,7 @@ function processStringAttribute({ attributeName, isArray, dotNotationVariable, f
|
|
|
164
165
|
const originalStringValue = isArray ? [rawOriginalStringValue] : rawOriginalStringValue;
|
|
165
166
|
const updatedStringValue = isArray ? [rawUpdatedStringValue] : rawUpdatedStringValue;
|
|
166
167
|
attributeData.attributeCreationKeyValues.push(`${attributeName}: ${jsonify(originalStringValue)},`);
|
|
167
|
-
attributeData.attributeUpdateKeyValues.push(
|
|
168
|
+
attributeData.attributeUpdateKeyValues.push({ attributeName, value: jsonify(updatedStringValue) });
|
|
168
169
|
attributeData.comparableOriginalAttributeKeyValues.push(`${attributeName}: ${dotNotationVariable},`);
|
|
169
170
|
attributeData.expectEqualOriginalValue.push(`expect(${dotNotationVariable}).toEqual(${jsonify(originalStringValue)})`);
|
|
170
171
|
attributeData.expectEqualUpdatedValue.push(`expect(${dotNotationVariable}).toEqual(${jsonify(updatedStringValue)})`);
|
|
@@ -175,7 +176,7 @@ function processNumericAttribute({ attributeName, attributeType, isArray, dotNot
|
|
|
175
176
|
const originalValue = isArray ? [rawOriginalValue] : rawOriginalValue;
|
|
176
177
|
const updatedValue = isArray ? [rawUpdatedValue] : rawUpdatedValue;
|
|
177
178
|
attributeData.attributeCreationKeyValues.push(`${attributeName}: ${jsonify(originalValue)},`);
|
|
178
|
-
attributeData.attributeUpdateKeyValues.push(
|
|
179
|
+
attributeData.attributeUpdateKeyValues.push({ attributeName, value: jsonify(updatedValue) });
|
|
179
180
|
attributeData.comparableOriginalAttributeKeyValues.push(`${attributeName}: ${dotNotationVariable},`);
|
|
180
181
|
attributeData.expectEqualOriginalValue.push(`expect(${dotNotationVariable}).toEqual(${jsonify(originalValue)})`);
|
|
181
182
|
attributeData.expectEqualUpdatedValue.push(`expect(${dotNotationVariable}).toEqual(${jsonify(updatedValue)})`);
|
|
@@ -184,7 +185,10 @@ function processDateAttribute({ attributeName, isArray, dotNotationVariable, att
|
|
|
184
185
|
attributeData.dreamImports.push('CalendarDate');
|
|
185
186
|
attributeData.dateAttributeIncluded = true;
|
|
186
187
|
attributeData.attributeCreationKeyValues.push(`${attributeName}: ${isArray ? '[today.toISO()]' : 'today.toISO()'},`);
|
|
187
|
-
attributeData.attributeUpdateKeyValues.push(
|
|
188
|
+
attributeData.attributeUpdateKeyValues.push({
|
|
189
|
+
attributeName,
|
|
190
|
+
value: `${isArray ? '[yesterday.toISO()]' : 'yesterday.toISO()'}`,
|
|
191
|
+
});
|
|
188
192
|
attributeData.comparableOriginalAttributeKeyValues.push(`${attributeName}: ${dotNotationVariable}${isArray ? '.map(date => date.toISO())' : '.toISO()'},`);
|
|
189
193
|
attributeData.expectEqualOriginalValue.push(`expect(${dotNotationVariable}${isArray ? '[0]' : ''}).toEqualCalendarDate(today)`);
|
|
190
194
|
attributeData.expectEqualUpdatedValue.push(`expect(${dotNotationVariable}${isArray ? '[0]' : ''}).toEqualCalendarDate(yesterday)`);
|
|
@@ -193,13 +197,17 @@ function processDateTimeAttribute({ attributeName, isArray, dotNotationVariable,
|
|
|
193
197
|
attributeData.dreamImports.push('DateTime');
|
|
194
198
|
attributeData.datetimeAttributeIncluded = true;
|
|
195
199
|
attributeData.attributeCreationKeyValues.push(`${attributeName}: ${isArray ? '[now.toISO()]' : 'now.toISO()'},`);
|
|
196
|
-
attributeData.attributeUpdateKeyValues.push(
|
|
200
|
+
attributeData.attributeUpdateKeyValues.push({
|
|
201
|
+
attributeName,
|
|
202
|
+
value: `${isArray ? '[lastHour.toISO()]' : 'lastHour.toISO()'}`,
|
|
203
|
+
});
|
|
197
204
|
attributeData.comparableOriginalAttributeKeyValues.push(`${attributeName}: ${dotNotationVariable}${isArray ? '.map(datetime => datetime.toISO())' : '.toISO()'},`);
|
|
198
205
|
attributeData.expectEqualOriginalValue.push(`expect(${dotNotationVariable}${isArray ? '[0]' : ''}).toEqualDateTime(now)`);
|
|
199
206
|
attributeData.expectEqualUpdatedValue.push(`expect(${dotNotationVariable}${isArray ? '[0]' : ''}).toEqualDateTime(lastHour)`);
|
|
200
207
|
}
|
|
201
208
|
function processUuidAttribute({ attributeName, isArray, dotNotationVariable, attributeData, }) {
|
|
202
209
|
attributeData.uuidAttributeIncluded = true;
|
|
210
|
+
attributeData.uuidAttributes.push(attributeName);
|
|
203
211
|
if (isArray) {
|
|
204
212
|
attributeData.uuidArrayAttributes.push(attributeName);
|
|
205
213
|
}
|
|
@@ -208,7 +216,7 @@ function processUuidAttribute({ attributeName, isArray, dotNotationVariable, att
|
|
|
208
216
|
const uuidValue = attributeName;
|
|
209
217
|
const newUuidValue = newUuidVariableName;
|
|
210
218
|
attributeData.attributeCreationKeyValues.push(`${attributeName}: ${uuidValue},`);
|
|
211
|
-
attributeData.attributeUpdateKeyValues.push(
|
|
219
|
+
attributeData.attributeUpdateKeyValues.push({ attributeName, value: newUuidValue });
|
|
212
220
|
attributeData.comparableOriginalAttributeKeyValues.push(`${attributeName}: ${dotNotationVariable},`);
|
|
213
221
|
attributeData.expectEqualOriginalValue.push(`expect(${dotNotationVariable}).toEqual(${attributeName})`);
|
|
214
222
|
attributeData.expectEqualUpdatedValue.push(`expect(${dotNotationVariable}).toEqual(${newUuidVariableName})`);
|
|
@@ -336,13 +344,7 @@ function generateCreateActionSpec(options) {
|
|
|
336
344
|
return '';
|
|
337
345
|
const { path, pathParams, modelConfig, fullyQualifiedModelName, forAdmin, singular, attributeData } = options;
|
|
338
346
|
const subjectFunctionName = `create${modelConfig.modelClassName}`;
|
|
339
|
-
const
|
|
340
|
-
.map(kv => {
|
|
341
|
-
const match = kv.match(/^(\w+):\s*\1,?$/);
|
|
342
|
-
return match?.[1];
|
|
343
|
-
})
|
|
344
|
-
.filter((name) => Boolean(name));
|
|
345
|
-
const uuidSetup = uuidAttributeNames
|
|
347
|
+
const uuidSetup = attributeData.uuidAttributes
|
|
346
348
|
.map(attrName => {
|
|
347
349
|
const isArray = attributeData.uuidArrayAttributes.includes(attrName);
|
|
348
350
|
return isArray ? `const ${attrName} = [randomUUID()]` : `const ${attrName} = randomUUID()`;
|
|
@@ -393,17 +395,7 @@ function generateUpdateActionSpec(options) {
|
|
|
393
395
|
return '';
|
|
394
396
|
const { path, pathParams, modelConfig, fullyQualifiedModelName, singular, forAdmin, attributeData } = options;
|
|
395
397
|
const subjectFunctionName = `update${modelConfig.modelClassName}`;
|
|
396
|
-
const
|
|
397
|
-
.filter(kv => {
|
|
398
|
-
const match = kv.match(/^(\w+):\s*new([A-Z]\w+),?$/);
|
|
399
|
-
return match && match[1] && camelize(match[1]) === camelize(match[2]);
|
|
400
|
-
})
|
|
401
|
-
.map(kv => {
|
|
402
|
-
const match = kv.match(/^(\w+):/);
|
|
403
|
-
return match?.[1];
|
|
404
|
-
})
|
|
405
|
-
.filter(Boolean);
|
|
406
|
-
const uuidSetup = uuidAttributeNames
|
|
398
|
+
const uuidSetup = attributeData.uuidAttributes
|
|
407
399
|
.map(attrName => {
|
|
408
400
|
const isArray = attributeData.uuidArrayAttributes.includes(attrName);
|
|
409
401
|
return isArray
|
|
@@ -461,15 +453,13 @@ function generateUpdateActionSpec(options) {
|
|
|
461
453
|
${attributeData.attributeUpdateKeyValues.length
|
|
462
454
|
? attributeData.attributeUpdateKeyValues
|
|
463
455
|
.map(kv => {
|
|
464
|
-
const
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
}
|
|
472
|
-
return kv;
|
|
456
|
+
const isUuid = attributeData.uuidAttributes.includes(kv.attributeName);
|
|
457
|
+
const value = isUuid
|
|
458
|
+
? attributeData.uuidArrayAttributes.includes(kv.attributeName)
|
|
459
|
+
? '[randomUUID()]'
|
|
460
|
+
: 'randomUUID()'
|
|
461
|
+
: kv.value;
|
|
462
|
+
return `${kv.attributeName}: ${value},`;
|
|
473
463
|
})
|
|
474
464
|
.join('\n ')
|
|
475
465
|
: ''}
|
|
@@ -487,7 +477,7 @@ function generateUpdateActionSpec(options) {
|
|
|
487
477
|
${modelConfig.simpleCreationCommand}
|
|
488
478
|
|
|
489
479
|
await ${subjectFunctionName}(${singular ? '' : `${modelConfig.modelVariableName}, `}{
|
|
490
|
-
${attributeData.attributeUpdateKeyValues.length ? attributeData.attributeUpdateKeyValues.join('\n ') : ''}
|
|
480
|
+
${attributeData.attributeUpdateKeyValues.length ? attributeData.attributeUpdateKeyValues.map(kv => `${kv.attributeName}: ${kv.value},`).join('\n ') : ''}
|
|
491
481
|
}, 204)
|
|
492
482
|
|
|
493
483
|
await ${modelConfig.modelVariableName}.reload()
|
|
@@ -9,13 +9,13 @@ import PsychicApp from '../psychic-app/index.js';
|
|
|
9
9
|
import openapiParamNamesForDreamClass from '../server/helpers/openapiParamNamesForDreamClass.js';
|
|
10
10
|
import OpenapiSegmentExpander from './body-segment.js';
|
|
11
11
|
import { DEFAULT_OPENAPI_RESPONSES } from './defaults.js';
|
|
12
|
+
import cursorPaginationParamOpenapiProperty from './helpers/cursorPaginationParamOpenapiProperty.js';
|
|
12
13
|
import { dreamColumnOpenapiShape } from './helpers/dreamAttributeOpenapiShape.js';
|
|
13
14
|
import openapiOpts from './helpers/openapiOpts.js';
|
|
14
15
|
import openapiRoute from './helpers/openapiRoute.js';
|
|
15
16
|
import paginationPageParamOpenapiProperty from './helpers/paginationPageParamOpenapiProperty.js';
|
|
17
|
+
import safelyAttachCursorPaginationParamToRequestBodySegment from './helpers/safelyAttachCursorPaginationParamToRequestBodySegment.js';
|
|
16
18
|
import safelyAttachPaginationParamToRequestBodySegment from './helpers/safelyAttachPaginationParamsToBodySegment.js';
|
|
17
|
-
import safelyAttachScrollPaginationParamToRequestBodySegment from './helpers/safelyAttachScrollPaginationParamsToBodySegment.js';
|
|
18
|
-
import scrollPaginationCursorParamOpenapiProperty from './helpers/scrollPaginationCursorParamOpenapiProperty.js';
|
|
19
19
|
import SerializerOpenapiRenderer from './SerializerOpenapiRenderer.js';
|
|
20
20
|
export default class OpenapiEndpointRenderer {
|
|
21
21
|
dreamsOrSerializers;
|
|
@@ -23,6 +23,7 @@ export default class OpenapiEndpointRenderer {
|
|
|
23
23
|
action;
|
|
24
24
|
many;
|
|
25
25
|
paginate;
|
|
26
|
+
cursorPaginate;
|
|
26
27
|
scrollPaginate;
|
|
27
28
|
responses;
|
|
28
29
|
serializerKey;
|
|
@@ -53,7 +54,7 @@ export default class OpenapiEndpointRenderer {
|
|
|
53
54
|
* const json = JSON.encode(openapiJsonContents, null, 2)
|
|
54
55
|
* ```
|
|
55
56
|
*/
|
|
56
|
-
constructor(dreamsOrSerializers, controllerClass, action, { requestBody, headers, many, paginate, scrollPaginate, query, responses, serializerKey, status, tags, security, pathParams, description, summary, omitDefaultHeaders, omitDefaultResponses, defaultResponse, validate, } = {}) {
|
|
57
|
+
constructor(dreamsOrSerializers, controllerClass, action, { requestBody, headers, many, paginate, cursorPaginate, scrollPaginate, query, responses, serializerKey, status, tags, security, pathParams, description, summary, omitDefaultHeaders, omitDefaultResponses, defaultResponse, validate, } = {}) {
|
|
57
58
|
this.dreamsOrSerializers = dreamsOrSerializers;
|
|
58
59
|
this.controllerClass = controllerClass;
|
|
59
60
|
this.action = action;
|
|
@@ -61,6 +62,7 @@ export default class OpenapiEndpointRenderer {
|
|
|
61
62
|
this.headers = headers;
|
|
62
63
|
this.many = many;
|
|
63
64
|
this.paginate = paginate;
|
|
65
|
+
this.cursorPaginate = cursorPaginate;
|
|
64
66
|
this.scrollPaginate = scrollPaginate;
|
|
65
67
|
this.query = query;
|
|
66
68
|
this.responses = responses;
|
|
@@ -404,13 +406,14 @@ export default class OpenapiEndpointRenderer {
|
|
|
404
406
|
},
|
|
405
407
|
});
|
|
406
408
|
}
|
|
409
|
+
const cursorPaginationName = this.cursorPaginate === true ? 'cursor' : this.cursorPaginate?.query;
|
|
407
410
|
const scrollPaginationName = this.scrollPaginate === true ? 'cursor' : this.scrollPaginate?.query;
|
|
408
|
-
if (scrollPaginationName) {
|
|
411
|
+
if (cursorPaginationName || scrollPaginationName) {
|
|
409
412
|
queryParams.push({
|
|
410
413
|
in: 'query',
|
|
411
414
|
required: false,
|
|
412
|
-
name: scrollPaginationName,
|
|
413
|
-
description: '
|
|
415
|
+
name: (cursorPaginationName || scrollPaginationName),
|
|
416
|
+
description: 'Pagination cursor',
|
|
414
417
|
allowReserved: true,
|
|
415
418
|
schema: {
|
|
416
419
|
type: ['string', 'null'],
|
|
@@ -442,9 +445,9 @@ export default class OpenapiEndpointRenderer {
|
|
|
442
445
|
if (bodyPaginationPageParam) {
|
|
443
446
|
schema = safelyAttachPaginationParamToRequestBodySegment(bodyPaginationPageParam, schema);
|
|
444
447
|
}
|
|
445
|
-
const
|
|
446
|
-
if (
|
|
447
|
-
schema =
|
|
448
|
+
const bodyCursorPaginationParam = this.cursorPaginate?.body ?? this.scrollPaginate?.body;
|
|
449
|
+
if (bodyCursorPaginationParam) {
|
|
450
|
+
schema = safelyAttachCursorPaginationParamToRequestBodySegment(bodyCursorPaginationParam, schema);
|
|
448
451
|
}
|
|
449
452
|
if (!schema)
|
|
450
453
|
return undefined;
|
|
@@ -458,7 +461,7 @@ export default class OpenapiEndpointRenderer {
|
|
|
458
461
|
}
|
|
459
462
|
defaultRequestBody() {
|
|
460
463
|
const bodyPaginationPageParam = this.paginate?.body;
|
|
461
|
-
const
|
|
464
|
+
const bodyCursorPaginationParam = this.cursorPaginate?.body ?? this.scrollPaginate?.body;
|
|
462
465
|
if (bodyPaginationPageParam) {
|
|
463
466
|
return {
|
|
464
467
|
content: {
|
|
@@ -473,7 +476,7 @@ export default class OpenapiEndpointRenderer {
|
|
|
473
476
|
},
|
|
474
477
|
};
|
|
475
478
|
}
|
|
476
|
-
else if (
|
|
479
|
+
else if (bodyCursorPaginationParam) {
|
|
477
480
|
return {
|
|
478
481
|
content: {
|
|
479
482
|
'application/json': {
|
|
@@ -481,7 +484,7 @@ export default class OpenapiEndpointRenderer {
|
|
|
481
484
|
type: 'object',
|
|
482
485
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
483
486
|
properties: {
|
|
484
|
-
[
|
|
487
|
+
[bodyCursorPaginationParam]: cursorPaginationParamOpenapiProperty(),
|
|
485
488
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
486
489
|
},
|
|
487
490
|
},
|
|
@@ -559,9 +562,9 @@ export default class OpenapiEndpointRenderer {
|
|
|
559
562
|
if (bodyPaginationPageParam) {
|
|
560
563
|
processedSchema = safelyAttachPaginationParamToRequestBodySegment(bodyPaginationPageParam, processedSchema);
|
|
561
564
|
}
|
|
562
|
-
const
|
|
563
|
-
if (
|
|
564
|
-
processedSchema =
|
|
565
|
+
const bodyCursorPaginationParam = this.cursorPaginate?.body ?? this.scrollPaginate?.body;
|
|
566
|
+
if (bodyCursorPaginationParam) {
|
|
567
|
+
processedSchema = safelyAttachCursorPaginationParamToRequestBodySegment(bodyCursorPaginationParam, processedSchema);
|
|
565
568
|
}
|
|
566
569
|
return {
|
|
567
570
|
content: {
|
|
@@ -759,7 +762,7 @@ export default class OpenapiEndpointRenderer {
|
|
|
759
762
|
},
|
|
760
763
|
},
|
|
761
764
|
};
|
|
762
|
-
if (this.scrollPaginate)
|
|
765
|
+
if (this.cursorPaginate || this.scrollPaginate)
|
|
763
766
|
return {
|
|
764
767
|
type: 'object',
|
|
765
768
|
required: ['cursor', 'results'],
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import cursorPaginationParamOpenapiProperty from './cursorPaginationParamOpenapiProperty.js';
|
|
2
2
|
/**
|
|
3
3
|
* @internal
|
|
4
4
|
*
|
|
@@ -13,7 +13,7 @@ import scrollPaginationCursorParamOpenapiProperty from './scrollPaginationCursor
|
|
|
13
13
|
* If neither of these apply, it will simply return
|
|
14
14
|
* what was given to it, without any modifications
|
|
15
15
|
*/
|
|
16
|
-
export default function
|
|
16
|
+
export default function safelyAttachCursorPaginationParamToRequestBodySegment(paramName, bodySegment) {
|
|
17
17
|
bodySegment ||= {
|
|
18
18
|
type: 'object',
|
|
19
19
|
properties: {},
|
|
@@ -23,7 +23,7 @@ export default function safelyAttachScrollPaginationParamToRequestBodySegment(pa
|
|
|
23
23
|
;
|
|
24
24
|
bodySegment.properties = {
|
|
25
25
|
...bodySegment.properties,
|
|
26
|
-
[paramName]:
|
|
26
|
+
[paramName]: cursorPaginationParamOpenapiProperty(),
|
|
27
27
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
28
28
|
};
|
|
29
29
|
}
|
|
@@ -557,14 +557,10 @@ export default class PsychicController {
|
|
|
557
557
|
return data.map(d => this.singleObjectJson(d, opts));
|
|
558
558
|
//
|
|
559
559
|
}
|
|
560
|
-
else if (this.currentOpenapiRenderer?.['paginate']
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
};
|
|
565
|
-
//
|
|
566
|
-
}
|
|
567
|
-
else if (this.currentOpenapiRenderer?.['scrollPaginate'] && Array.isArray(paginatedResults)) {
|
|
560
|
+
else if ((this.currentOpenapiRenderer?.['paginate'] ||
|
|
561
|
+
this.currentOpenapiRenderer?.['cursorPaginate'] ||
|
|
562
|
+
this.currentOpenapiRenderer?.['scrollPaginate']) &&
|
|
563
|
+
Array.isArray(paginatedResults)) {
|
|
568
564
|
return {
|
|
569
565
|
...data,
|
|
570
566
|
results: paginatedResults.map(result => this.singleObjectJson(result, opts)),
|
|
@@ -57,14 +57,13 @@ export default function generateControllerContent({ ancestorName, ancestorImport
|
|
|
57
57
|
status: 200,
|
|
58
58
|
tags: openApiTags,
|
|
59
59
|
description: 'Paginated index of ${pluralize(modelClassName)}',
|
|
60
|
-
|
|
60
|
+
cursorPaginate: true,
|
|
61
61
|
serializerKey: '${forAdmin ? 'adminSummary' : 'summary'}',
|
|
62
62
|
})
|
|
63
63
|
public async index() {
|
|
64
64
|
// const ${pluralizedModelAttributeName} = await ${loadQueryBase}
|
|
65
65
|
// .preloadFor('${forAdmin ? 'adminSummary' : 'summary'}')
|
|
66
|
-
// .
|
|
67
|
-
// .scrollPaginate({ cursor: this.castParam('cursor', 'string', { allowNull: true }) })
|
|
66
|
+
// .cursorPaginate({ cursor: this.castParam('cursor', 'string', { allowNull: true }) })
|
|
68
67
|
// this.ok(${pluralizedModelAttributeName})
|
|
69
68
|
}`;
|
|
70
69
|
else
|
|
@@ -63,6 +63,7 @@ function processAttributes(columnsWithTypes, modelConfig, fullyQualifiedModelNam
|
|
|
63
63
|
dateAttributeIncluded: false,
|
|
64
64
|
datetimeAttributeIncluded: false,
|
|
65
65
|
uuidAttributeIncluded: false,
|
|
66
|
+
uuidAttributes: [],
|
|
66
67
|
uuidArrayAttributes: [],
|
|
67
68
|
dreamImports: [],
|
|
68
69
|
};
|
|
@@ -153,7 +154,7 @@ function processEnumAttribute({ attributeName, isArray, enumValues, dotNotationV
|
|
|
153
154
|
attributeData.comparableOriginalAttributeKeyValues.push(`${attributeName}: ${dotNotationVariable},`);
|
|
154
155
|
if (attributeName !== 'type') {
|
|
155
156
|
attributeData.attributeCreationKeyValues.push(`${attributeName}: ${jsonify(originalEnumValue)},`);
|
|
156
|
-
attributeData.attributeUpdateKeyValues.push(
|
|
157
|
+
attributeData.attributeUpdateKeyValues.push({ attributeName, value: jsonify(updatedEnumValue) });
|
|
157
158
|
attributeData.expectEqualOriginalValue.push(`expect(${dotNotationVariable}).toEqual(${jsonify(originalEnumValue)})`);
|
|
158
159
|
attributeData.expectEqualUpdatedValue.push(`expect(${dotNotationVariable}).toEqual(${jsonify(updatedEnumValue)})`);
|
|
159
160
|
}
|
|
@@ -164,7 +165,7 @@ function processStringAttribute({ attributeName, isArray, dotNotationVariable, f
|
|
|
164
165
|
const originalStringValue = isArray ? [rawOriginalStringValue] : rawOriginalStringValue;
|
|
165
166
|
const updatedStringValue = isArray ? [rawUpdatedStringValue] : rawUpdatedStringValue;
|
|
166
167
|
attributeData.attributeCreationKeyValues.push(`${attributeName}: ${jsonify(originalStringValue)},`);
|
|
167
|
-
attributeData.attributeUpdateKeyValues.push(
|
|
168
|
+
attributeData.attributeUpdateKeyValues.push({ attributeName, value: jsonify(updatedStringValue) });
|
|
168
169
|
attributeData.comparableOriginalAttributeKeyValues.push(`${attributeName}: ${dotNotationVariable},`);
|
|
169
170
|
attributeData.expectEqualOriginalValue.push(`expect(${dotNotationVariable}).toEqual(${jsonify(originalStringValue)})`);
|
|
170
171
|
attributeData.expectEqualUpdatedValue.push(`expect(${dotNotationVariable}).toEqual(${jsonify(updatedStringValue)})`);
|
|
@@ -175,7 +176,7 @@ function processNumericAttribute({ attributeName, attributeType, isArray, dotNot
|
|
|
175
176
|
const originalValue = isArray ? [rawOriginalValue] : rawOriginalValue;
|
|
176
177
|
const updatedValue = isArray ? [rawUpdatedValue] : rawUpdatedValue;
|
|
177
178
|
attributeData.attributeCreationKeyValues.push(`${attributeName}: ${jsonify(originalValue)},`);
|
|
178
|
-
attributeData.attributeUpdateKeyValues.push(
|
|
179
|
+
attributeData.attributeUpdateKeyValues.push({ attributeName, value: jsonify(updatedValue) });
|
|
179
180
|
attributeData.comparableOriginalAttributeKeyValues.push(`${attributeName}: ${dotNotationVariable},`);
|
|
180
181
|
attributeData.expectEqualOriginalValue.push(`expect(${dotNotationVariable}).toEqual(${jsonify(originalValue)})`);
|
|
181
182
|
attributeData.expectEqualUpdatedValue.push(`expect(${dotNotationVariable}).toEqual(${jsonify(updatedValue)})`);
|
|
@@ -184,7 +185,10 @@ function processDateAttribute({ attributeName, isArray, dotNotationVariable, att
|
|
|
184
185
|
attributeData.dreamImports.push('CalendarDate');
|
|
185
186
|
attributeData.dateAttributeIncluded = true;
|
|
186
187
|
attributeData.attributeCreationKeyValues.push(`${attributeName}: ${isArray ? '[today.toISO()]' : 'today.toISO()'},`);
|
|
187
|
-
attributeData.attributeUpdateKeyValues.push(
|
|
188
|
+
attributeData.attributeUpdateKeyValues.push({
|
|
189
|
+
attributeName,
|
|
190
|
+
value: `${isArray ? '[yesterday.toISO()]' : 'yesterday.toISO()'}`,
|
|
191
|
+
});
|
|
188
192
|
attributeData.comparableOriginalAttributeKeyValues.push(`${attributeName}: ${dotNotationVariable}${isArray ? '.map(date => date.toISO())' : '.toISO()'},`);
|
|
189
193
|
attributeData.expectEqualOriginalValue.push(`expect(${dotNotationVariable}${isArray ? '[0]' : ''}).toEqualCalendarDate(today)`);
|
|
190
194
|
attributeData.expectEqualUpdatedValue.push(`expect(${dotNotationVariable}${isArray ? '[0]' : ''}).toEqualCalendarDate(yesterday)`);
|
|
@@ -193,13 +197,17 @@ function processDateTimeAttribute({ attributeName, isArray, dotNotationVariable,
|
|
|
193
197
|
attributeData.dreamImports.push('DateTime');
|
|
194
198
|
attributeData.datetimeAttributeIncluded = true;
|
|
195
199
|
attributeData.attributeCreationKeyValues.push(`${attributeName}: ${isArray ? '[now.toISO()]' : 'now.toISO()'},`);
|
|
196
|
-
attributeData.attributeUpdateKeyValues.push(
|
|
200
|
+
attributeData.attributeUpdateKeyValues.push({
|
|
201
|
+
attributeName,
|
|
202
|
+
value: `${isArray ? '[lastHour.toISO()]' : 'lastHour.toISO()'}`,
|
|
203
|
+
});
|
|
197
204
|
attributeData.comparableOriginalAttributeKeyValues.push(`${attributeName}: ${dotNotationVariable}${isArray ? '.map(datetime => datetime.toISO())' : '.toISO()'},`);
|
|
198
205
|
attributeData.expectEqualOriginalValue.push(`expect(${dotNotationVariable}${isArray ? '[0]' : ''}).toEqualDateTime(now)`);
|
|
199
206
|
attributeData.expectEqualUpdatedValue.push(`expect(${dotNotationVariable}${isArray ? '[0]' : ''}).toEqualDateTime(lastHour)`);
|
|
200
207
|
}
|
|
201
208
|
function processUuidAttribute({ attributeName, isArray, dotNotationVariable, attributeData, }) {
|
|
202
209
|
attributeData.uuidAttributeIncluded = true;
|
|
210
|
+
attributeData.uuidAttributes.push(attributeName);
|
|
203
211
|
if (isArray) {
|
|
204
212
|
attributeData.uuidArrayAttributes.push(attributeName);
|
|
205
213
|
}
|
|
@@ -208,7 +216,7 @@ function processUuidAttribute({ attributeName, isArray, dotNotationVariable, att
|
|
|
208
216
|
const uuidValue = attributeName;
|
|
209
217
|
const newUuidValue = newUuidVariableName;
|
|
210
218
|
attributeData.attributeCreationKeyValues.push(`${attributeName}: ${uuidValue},`);
|
|
211
|
-
attributeData.attributeUpdateKeyValues.push(
|
|
219
|
+
attributeData.attributeUpdateKeyValues.push({ attributeName, value: newUuidValue });
|
|
212
220
|
attributeData.comparableOriginalAttributeKeyValues.push(`${attributeName}: ${dotNotationVariable},`);
|
|
213
221
|
attributeData.expectEqualOriginalValue.push(`expect(${dotNotationVariable}).toEqual(${attributeName})`);
|
|
214
222
|
attributeData.expectEqualUpdatedValue.push(`expect(${dotNotationVariable}).toEqual(${newUuidVariableName})`);
|
|
@@ -336,13 +344,7 @@ function generateCreateActionSpec(options) {
|
|
|
336
344
|
return '';
|
|
337
345
|
const { path, pathParams, modelConfig, fullyQualifiedModelName, forAdmin, singular, attributeData } = options;
|
|
338
346
|
const subjectFunctionName = `create${modelConfig.modelClassName}`;
|
|
339
|
-
const
|
|
340
|
-
.map(kv => {
|
|
341
|
-
const match = kv.match(/^(\w+):\s*\1,?$/);
|
|
342
|
-
return match?.[1];
|
|
343
|
-
})
|
|
344
|
-
.filter((name) => Boolean(name));
|
|
345
|
-
const uuidSetup = uuidAttributeNames
|
|
347
|
+
const uuidSetup = attributeData.uuidAttributes
|
|
346
348
|
.map(attrName => {
|
|
347
349
|
const isArray = attributeData.uuidArrayAttributes.includes(attrName);
|
|
348
350
|
return isArray ? `const ${attrName} = [randomUUID()]` : `const ${attrName} = randomUUID()`;
|
|
@@ -393,17 +395,7 @@ function generateUpdateActionSpec(options) {
|
|
|
393
395
|
return '';
|
|
394
396
|
const { path, pathParams, modelConfig, fullyQualifiedModelName, singular, forAdmin, attributeData } = options;
|
|
395
397
|
const subjectFunctionName = `update${modelConfig.modelClassName}`;
|
|
396
|
-
const
|
|
397
|
-
.filter(kv => {
|
|
398
|
-
const match = kv.match(/^(\w+):\s*new([A-Z]\w+),?$/);
|
|
399
|
-
return match && match[1] && camelize(match[1]) === camelize(match[2]);
|
|
400
|
-
})
|
|
401
|
-
.map(kv => {
|
|
402
|
-
const match = kv.match(/^(\w+):/);
|
|
403
|
-
return match?.[1];
|
|
404
|
-
})
|
|
405
|
-
.filter(Boolean);
|
|
406
|
-
const uuidSetup = uuidAttributeNames
|
|
398
|
+
const uuidSetup = attributeData.uuidAttributes
|
|
407
399
|
.map(attrName => {
|
|
408
400
|
const isArray = attributeData.uuidArrayAttributes.includes(attrName);
|
|
409
401
|
return isArray
|
|
@@ -461,15 +453,13 @@ function generateUpdateActionSpec(options) {
|
|
|
461
453
|
${attributeData.attributeUpdateKeyValues.length
|
|
462
454
|
? attributeData.attributeUpdateKeyValues
|
|
463
455
|
.map(kv => {
|
|
464
|
-
const
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
}
|
|
472
|
-
return kv;
|
|
456
|
+
const isUuid = attributeData.uuidAttributes.includes(kv.attributeName);
|
|
457
|
+
const value = isUuid
|
|
458
|
+
? attributeData.uuidArrayAttributes.includes(kv.attributeName)
|
|
459
|
+
? '[randomUUID()]'
|
|
460
|
+
: 'randomUUID()'
|
|
461
|
+
: kv.value;
|
|
462
|
+
return `${kv.attributeName}: ${value},`;
|
|
473
463
|
})
|
|
474
464
|
.join('\n ')
|
|
475
465
|
: ''}
|
|
@@ -487,7 +477,7 @@ function generateUpdateActionSpec(options) {
|
|
|
487
477
|
${modelConfig.simpleCreationCommand}
|
|
488
478
|
|
|
489
479
|
await ${subjectFunctionName}(${singular ? '' : `${modelConfig.modelVariableName}, `}{
|
|
490
|
-
${attributeData.attributeUpdateKeyValues.length ? attributeData.attributeUpdateKeyValues.join('\n ') : ''}
|
|
480
|
+
${attributeData.attributeUpdateKeyValues.length ? attributeData.attributeUpdateKeyValues.map(kv => `${kv.attributeName}: ${kv.value},`).join('\n ') : ''}
|
|
491
481
|
}, 204)
|
|
492
482
|
|
|
493
483
|
await ${modelConfig.modelVariableName}.reload()
|
|
@@ -9,13 +9,13 @@ import PsychicApp from '../psychic-app/index.js';
|
|
|
9
9
|
import openapiParamNamesForDreamClass from '../server/helpers/openapiParamNamesForDreamClass.js';
|
|
10
10
|
import OpenapiSegmentExpander from './body-segment.js';
|
|
11
11
|
import { DEFAULT_OPENAPI_RESPONSES } from './defaults.js';
|
|
12
|
+
import cursorPaginationParamOpenapiProperty from './helpers/cursorPaginationParamOpenapiProperty.js';
|
|
12
13
|
import { dreamColumnOpenapiShape } from './helpers/dreamAttributeOpenapiShape.js';
|
|
13
14
|
import openapiOpts from './helpers/openapiOpts.js';
|
|
14
15
|
import openapiRoute from './helpers/openapiRoute.js';
|
|
15
16
|
import paginationPageParamOpenapiProperty from './helpers/paginationPageParamOpenapiProperty.js';
|
|
17
|
+
import safelyAttachCursorPaginationParamToRequestBodySegment from './helpers/safelyAttachCursorPaginationParamToRequestBodySegment.js';
|
|
16
18
|
import safelyAttachPaginationParamToRequestBodySegment from './helpers/safelyAttachPaginationParamsToBodySegment.js';
|
|
17
|
-
import safelyAttachScrollPaginationParamToRequestBodySegment from './helpers/safelyAttachScrollPaginationParamsToBodySegment.js';
|
|
18
|
-
import scrollPaginationCursorParamOpenapiProperty from './helpers/scrollPaginationCursorParamOpenapiProperty.js';
|
|
19
19
|
import SerializerOpenapiRenderer from './SerializerOpenapiRenderer.js';
|
|
20
20
|
export default class OpenapiEndpointRenderer {
|
|
21
21
|
dreamsOrSerializers;
|
|
@@ -23,6 +23,7 @@ export default class OpenapiEndpointRenderer {
|
|
|
23
23
|
action;
|
|
24
24
|
many;
|
|
25
25
|
paginate;
|
|
26
|
+
cursorPaginate;
|
|
26
27
|
scrollPaginate;
|
|
27
28
|
responses;
|
|
28
29
|
serializerKey;
|
|
@@ -53,7 +54,7 @@ export default class OpenapiEndpointRenderer {
|
|
|
53
54
|
* const json = JSON.encode(openapiJsonContents, null, 2)
|
|
54
55
|
* ```
|
|
55
56
|
*/
|
|
56
|
-
constructor(dreamsOrSerializers, controllerClass, action, { requestBody, headers, many, paginate, scrollPaginate, query, responses, serializerKey, status, tags, security, pathParams, description, summary, omitDefaultHeaders, omitDefaultResponses, defaultResponse, validate, } = {}) {
|
|
57
|
+
constructor(dreamsOrSerializers, controllerClass, action, { requestBody, headers, many, paginate, cursorPaginate, scrollPaginate, query, responses, serializerKey, status, tags, security, pathParams, description, summary, omitDefaultHeaders, omitDefaultResponses, defaultResponse, validate, } = {}) {
|
|
57
58
|
this.dreamsOrSerializers = dreamsOrSerializers;
|
|
58
59
|
this.controllerClass = controllerClass;
|
|
59
60
|
this.action = action;
|
|
@@ -61,6 +62,7 @@ export default class OpenapiEndpointRenderer {
|
|
|
61
62
|
this.headers = headers;
|
|
62
63
|
this.many = many;
|
|
63
64
|
this.paginate = paginate;
|
|
65
|
+
this.cursorPaginate = cursorPaginate;
|
|
64
66
|
this.scrollPaginate = scrollPaginate;
|
|
65
67
|
this.query = query;
|
|
66
68
|
this.responses = responses;
|
|
@@ -404,13 +406,14 @@ export default class OpenapiEndpointRenderer {
|
|
|
404
406
|
},
|
|
405
407
|
});
|
|
406
408
|
}
|
|
409
|
+
const cursorPaginationName = this.cursorPaginate === true ? 'cursor' : this.cursorPaginate?.query;
|
|
407
410
|
const scrollPaginationName = this.scrollPaginate === true ? 'cursor' : this.scrollPaginate?.query;
|
|
408
|
-
if (scrollPaginationName) {
|
|
411
|
+
if (cursorPaginationName || scrollPaginationName) {
|
|
409
412
|
queryParams.push({
|
|
410
413
|
in: 'query',
|
|
411
414
|
required: false,
|
|
412
|
-
name: scrollPaginationName,
|
|
413
|
-
description: '
|
|
415
|
+
name: (cursorPaginationName || scrollPaginationName),
|
|
416
|
+
description: 'Pagination cursor',
|
|
414
417
|
allowReserved: true,
|
|
415
418
|
schema: {
|
|
416
419
|
type: ['string', 'null'],
|
|
@@ -442,9 +445,9 @@ export default class OpenapiEndpointRenderer {
|
|
|
442
445
|
if (bodyPaginationPageParam) {
|
|
443
446
|
schema = safelyAttachPaginationParamToRequestBodySegment(bodyPaginationPageParam, schema);
|
|
444
447
|
}
|
|
445
|
-
const
|
|
446
|
-
if (
|
|
447
|
-
schema =
|
|
448
|
+
const bodyCursorPaginationParam = this.cursorPaginate?.body ?? this.scrollPaginate?.body;
|
|
449
|
+
if (bodyCursorPaginationParam) {
|
|
450
|
+
schema = safelyAttachCursorPaginationParamToRequestBodySegment(bodyCursorPaginationParam, schema);
|
|
448
451
|
}
|
|
449
452
|
if (!schema)
|
|
450
453
|
return undefined;
|
|
@@ -458,7 +461,7 @@ export default class OpenapiEndpointRenderer {
|
|
|
458
461
|
}
|
|
459
462
|
defaultRequestBody() {
|
|
460
463
|
const bodyPaginationPageParam = this.paginate?.body;
|
|
461
|
-
const
|
|
464
|
+
const bodyCursorPaginationParam = this.cursorPaginate?.body ?? this.scrollPaginate?.body;
|
|
462
465
|
if (bodyPaginationPageParam) {
|
|
463
466
|
return {
|
|
464
467
|
content: {
|
|
@@ -473,7 +476,7 @@ export default class OpenapiEndpointRenderer {
|
|
|
473
476
|
},
|
|
474
477
|
};
|
|
475
478
|
}
|
|
476
|
-
else if (
|
|
479
|
+
else if (bodyCursorPaginationParam) {
|
|
477
480
|
return {
|
|
478
481
|
content: {
|
|
479
482
|
'application/json': {
|
|
@@ -481,7 +484,7 @@ export default class OpenapiEndpointRenderer {
|
|
|
481
484
|
type: 'object',
|
|
482
485
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
483
486
|
properties: {
|
|
484
|
-
[
|
|
487
|
+
[bodyCursorPaginationParam]: cursorPaginationParamOpenapiProperty(),
|
|
485
488
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
486
489
|
},
|
|
487
490
|
},
|
|
@@ -559,9 +562,9 @@ export default class OpenapiEndpointRenderer {
|
|
|
559
562
|
if (bodyPaginationPageParam) {
|
|
560
563
|
processedSchema = safelyAttachPaginationParamToRequestBodySegment(bodyPaginationPageParam, processedSchema);
|
|
561
564
|
}
|
|
562
|
-
const
|
|
563
|
-
if (
|
|
564
|
-
processedSchema =
|
|
565
|
+
const bodyCursorPaginationParam = this.cursorPaginate?.body ?? this.scrollPaginate?.body;
|
|
566
|
+
if (bodyCursorPaginationParam) {
|
|
567
|
+
processedSchema = safelyAttachCursorPaginationParamToRequestBodySegment(bodyCursorPaginationParam, processedSchema);
|
|
565
568
|
}
|
|
566
569
|
return {
|
|
567
570
|
content: {
|
|
@@ -759,7 +762,7 @@ export default class OpenapiEndpointRenderer {
|
|
|
759
762
|
},
|
|
760
763
|
},
|
|
761
764
|
};
|
|
762
|
-
if (this.scrollPaginate)
|
|
765
|
+
if (this.cursorPaginate || this.scrollPaginate)
|
|
763
766
|
return {
|
|
764
767
|
type: 'object',
|
|
765
768
|
required: ['cursor', 'results'],
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import cursorPaginationParamOpenapiProperty from './cursorPaginationParamOpenapiProperty.js';
|
|
2
2
|
/**
|
|
3
3
|
* @internal
|
|
4
4
|
*
|
|
@@ -13,7 +13,7 @@ import scrollPaginationCursorParamOpenapiProperty from './scrollPaginationCursor
|
|
|
13
13
|
* If neither of these apply, it will simply return
|
|
14
14
|
* what was given to it, without any modifications
|
|
15
15
|
*/
|
|
16
|
-
export default function
|
|
16
|
+
export default function safelyAttachCursorPaginationParamToRequestBodySegment(paramName, bodySegment) {
|
|
17
17
|
bodySegment ||= {
|
|
18
18
|
type: 'object',
|
|
19
19
|
properties: {},
|
|
@@ -23,7 +23,7 @@ export default function safelyAttachScrollPaginationParamToRequestBodySegment(pa
|
|
|
23
23
|
;
|
|
24
24
|
bodySegment.properties = {
|
|
25
25
|
...bodySegment.properties,
|
|
26
|
-
[paramName]:
|
|
26
|
+
[paramName]: cursorPaginationParamOpenapiProperty(),
|
|
27
27
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
28
28
|
};
|
|
29
29
|
}
|
|
@@ -29,6 +29,7 @@ export default class OpenapiEndpointRenderer<DreamsOrSerializersOrViewModels ext
|
|
|
29
29
|
private action;
|
|
30
30
|
private many;
|
|
31
31
|
private paginate;
|
|
32
|
+
private cursorPaginate;
|
|
32
33
|
private scrollPaginate;
|
|
33
34
|
private responses;
|
|
34
35
|
private serializerKey;
|
|
@@ -59,7 +60,7 @@ export default class OpenapiEndpointRenderer<DreamsOrSerializersOrViewModels ext
|
|
|
59
60
|
* const json = JSON.encode(openapiJsonContents, null, 2)
|
|
60
61
|
* ```
|
|
61
62
|
*/
|
|
62
|
-
constructor(dreamsOrSerializers: DreamsOrSerializersOrViewModels | null, controllerClass: typeof PsychicController, action: string, { requestBody, headers, many, paginate, scrollPaginate, query, responses, serializerKey, status, tags, security, pathParams, description, summary, omitDefaultHeaders, omitDefaultResponses, defaultResponse, validate, }?: OpenapiEndpointRendererOpts<DreamsOrSerializersOrViewModels, ForOption>);
|
|
63
|
+
constructor(dreamsOrSerializers: DreamsOrSerializersOrViewModels | null, controllerClass: typeof PsychicController, action: string, { requestBody, headers, many, paginate, cursorPaginate, scrollPaginate, query, responses, serializerKey, status, tags, security, pathParams, description, summary, omitDefaultHeaders, omitDefaultResponses, defaultResponse, validate, }?: OpenapiEndpointRendererOpts<DreamsOrSerializersOrViewModels, ForOption>);
|
|
63
64
|
/**
|
|
64
65
|
* @internal
|
|
65
66
|
*
|
|
@@ -341,6 +342,28 @@ export interface OpenapiEndpointRendererOpts<I extends DreamSerializable | Dream
|
|
|
341
342
|
*/
|
|
342
343
|
paginate?: boolean | CustomPaginationOpts;
|
|
343
344
|
/**
|
|
345
|
+
* when true, it will render an openapi document which
|
|
346
|
+
* produces an object containing cursorPagination data. This
|
|
347
|
+
* output is formatted to match the format returned
|
|
348
|
+
* when calling the `cursorPaginate` method
|
|
349
|
+
* within Dream, i.e.
|
|
350
|
+
*
|
|
351
|
+
* ```ts
|
|
352
|
+
* \@OpenAPI(Post, {
|
|
353
|
+
* cursorPaginate: true,
|
|
354
|
+
* status: 200,
|
|
355
|
+
* })
|
|
356
|
+
* public async index() {
|
|
357
|
+
* const cursor = this.castParam('cursor', 'string', { allowNull: true })
|
|
358
|
+
* const posts = await Post.where(...).cursorPaginate({ pageSize: 100, cursor })
|
|
359
|
+
* this.ok(posts)
|
|
360
|
+
* }
|
|
361
|
+
* ```
|
|
362
|
+
*/
|
|
363
|
+
cursorPaginate?: boolean | CustomCursorPaginationOpts;
|
|
364
|
+
/**
|
|
365
|
+
* @deprecated Use `cursorPaginate` instead. This option is deprecated in favor of the `cursorPaginate` option.
|
|
366
|
+
*
|
|
344
367
|
* when true, it will render an openapi document which
|
|
345
368
|
* produces an object containing scrollPagination data. This
|
|
346
369
|
* output is formatted to match the format returned
|
|
@@ -359,7 +382,7 @@ export interface OpenapiEndpointRendererOpts<I extends DreamSerializable | Dream
|
|
|
359
382
|
* }
|
|
360
383
|
* ```
|
|
361
384
|
*/
|
|
362
|
-
scrollPaginate?: boolean |
|
|
385
|
+
scrollPaginate?: boolean | CustomCursorPaginationOpts;
|
|
363
386
|
/**
|
|
364
387
|
* specify path params. This is usually not necessary, since path params
|
|
365
388
|
* are automatically computed for your endpoint by matching against the
|
|
@@ -665,14 +688,14 @@ export type CustomPaginationOpts = {
|
|
|
665
688
|
*/
|
|
666
689
|
body: string;
|
|
667
690
|
};
|
|
668
|
-
export type
|
|
691
|
+
export type CustomCursorPaginationOpts = {
|
|
669
692
|
/**
|
|
670
693
|
* if provided, a query param will be added to the
|
|
671
694
|
* openapi spec with the name provided.
|
|
672
695
|
*
|
|
673
696
|
* ```ts
|
|
674
697
|
* @OpenAPI(Post, {
|
|
675
|
-
*
|
|
698
|
+
* cursorPaginate: {
|
|
676
699
|
* query: 'cursor'
|
|
677
700
|
* },
|
|
678
701
|
* })
|
|
@@ -680,7 +703,7 @@ export type CustomScrollPaginationOpts = {
|
|
|
680
703
|
* this.ok(
|
|
681
704
|
* await this.currentUser
|
|
682
705
|
* .associationQuery('posts')
|
|
683
|
-
* .
|
|
706
|
+
* .cursorPaginate({
|
|
684
707
|
* cursor: this.castParam('page', 'string', { allowNull: true })
|
|
685
708
|
* })
|
|
686
709
|
* )
|
|
@@ -695,7 +718,7 @@ export type CustomScrollPaginationOpts = {
|
|
|
695
718
|
*
|
|
696
719
|
* ```ts
|
|
697
720
|
* @OpenAPI(Post, {
|
|
698
|
-
*
|
|
721
|
+
* cursorPaginate: {
|
|
699
722
|
* body: 'page'
|
|
700
723
|
* },
|
|
701
724
|
* })
|
|
@@ -703,7 +726,7 @@ export type CustomScrollPaginationOpts = {
|
|
|
703
726
|
* this.ok(
|
|
704
727
|
* await this.currentUser
|
|
705
728
|
* .associationQuery('posts')
|
|
706
|
-
* .
|
|
729
|
+
* .cursorPaginate({
|
|
707
730
|
* cursor: this.castParam('page', 'string', { allowNull: true })
|
|
708
731
|
* })
|
|
709
732
|
* )
|
|
@@ -12,4 +12,4 @@
|
|
|
12
12
|
* If neither of these apply, it will simply return
|
|
13
13
|
* what was given to it, without any modifications
|
|
14
14
|
*/
|
|
15
|
-
export default function
|
|
15
|
+
export default function safelyAttachCursorPaginationParamToRequestBodySegment<T>(paramName: string, bodySegment: T): T;
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@rvoh/psychic",
|
|
4
4
|
"description": "Typescript web framework",
|
|
5
|
-
"version": "2.2.
|
|
5
|
+
"version": "2.2.2",
|
|
6
6
|
"author": "RVOHealth",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
"yoctocolors": "^2.1.1"
|
|
80
80
|
},
|
|
81
81
|
"peerDependencies": {
|
|
82
|
-
"@rvoh/dream": "^2.
|
|
82
|
+
"@rvoh/dream": "^2.2.0",
|
|
83
83
|
"@types/express": "^5.0.1",
|
|
84
84
|
"commander": "^12.1.0",
|
|
85
85
|
"express": "^5.2.1",
|
|
@@ -88,7 +88,7 @@
|
|
|
88
88
|
"devDependencies": {
|
|
89
89
|
"@eslint/js": "^9.39.1",
|
|
90
90
|
"@jest-mock/express": "^3.0.0",
|
|
91
|
-
"@rvoh/dream": "^2.
|
|
91
|
+
"@rvoh/dream": "^2.2.0",
|
|
92
92
|
"@rvoh/dream-spec-helpers": "^2.0.0",
|
|
93
93
|
"@rvoh/psychic-spec-helpers": "^2.0.0",
|
|
94
94
|
"@types/body-parser": "^1.19.6",
|