@rvoh/psychic 0.37.0-beta.3 → 0.37.0-beta.4
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/bin/index.js +2 -2
- package/dist/cjs/src/controller/helpers/isPaginatedResult.js +5 -2
- package/dist/cjs/src/error/openapi/AttemptedToDeriveDescendentSerializersFromNonSerializer.js +16 -0
- package/dist/cjs/src/error/openapi/ExpectedSerializerForRendersOneOrManyOption.js +36 -0
- package/dist/cjs/src/error/openapi/NoSerializerFoundForRendersOneAndMany.js +16 -0
- package/dist/cjs/src/error/openapi/NonSerializerPassedToSerializerOpenapiRenderer.js +16 -0
- package/dist/cjs/src/error/openapi/NonSerializerSerializerOverrideProvided.js +23 -0
- package/dist/cjs/src/error/openapi/ObjectSerializerRendersOneAndManyRequireClassType.js +19 -0
- package/dist/cjs/src/helpers/isObject.js +12 -0
- package/dist/cjs/src/helpers/pathifyNestedObject.js +5 -2
- package/dist/cjs/src/openapi-renderer/SerializerOpenapiRenderer.js +362 -0
- package/dist/cjs/src/openapi-renderer/body-segment.js +5 -3
- package/dist/cjs/src/openapi-renderer/endpoint.js +6 -5
- package/dist/cjs/src/openapi-renderer/helpers/allSerializersFromHandWrittenOpenapi.js +34 -0
- package/dist/cjs/src/openapi-renderer/helpers/allSerializersToRefsInOpenapi.js +52 -0
- package/dist/cjs/src/openapi-renderer/helpers/dreamAttributeOpenapiShape.js +145 -0
- package/dist/cjs/src/openapi-renderer/helpers/isOpenapiShorthand.js +16 -0
- package/dist/cjs/src/openapi-renderer/helpers/maybeNullOpenapiShorthandToOpenapiShorthand.js +17 -0
- package/dist/cjs/src/openapi-renderer/helpers/openapiShorthandToOpenapi.js +111 -0
- package/dist/cjs/src/openapi-renderer/helpers/primitiveOpenapiStatementToOpenapi.js +5 -2
- package/dist/cjs/src/server/params.js +4 -4
- package/dist/esm/src/bin/index.js +1 -1
- package/dist/esm/src/controller/helpers/isPaginatedResult.js +1 -1
- package/dist/esm/src/error/openapi/AttemptedToDeriveDescendentSerializersFromNonSerializer.js +13 -0
- package/dist/esm/src/error/openapi/ExpectedSerializerForRendersOneOrManyOption.js +33 -0
- package/dist/esm/src/error/openapi/NoSerializerFoundForRendersOneAndMany.js +13 -0
- package/dist/esm/src/error/openapi/NonSerializerPassedToSerializerOpenapiRenderer.js +13 -0
- package/dist/esm/src/error/openapi/NonSerializerSerializerOverrideProvided.js +20 -0
- package/dist/esm/src/error/openapi/ObjectSerializerRendersOneAndManyRequireClassType.js +16 -0
- package/dist/esm/src/helpers/isObject.js +9 -0
- package/dist/esm/src/helpers/pathifyNestedObject.js +1 -1
- package/dist/esm/src/openapi-renderer/SerializerOpenapiRenderer.js +356 -0
- package/dist/esm/src/openapi-renderer/body-segment.js +3 -1
- package/dist/esm/src/openapi-renderer/endpoint.js +2 -1
- package/dist/esm/src/openapi-renderer/helpers/allSerializersFromHandWrittenOpenapi.js +28 -0
- package/dist/esm/src/openapi-renderer/helpers/allSerializersToRefsInOpenapi.js +46 -0
- package/dist/esm/src/openapi-renderer/helpers/dreamAttributeOpenapiShape.js +136 -0
- package/dist/esm/src/openapi-renderer/helpers/isOpenapiShorthand.js +10 -0
- package/dist/esm/src/openapi-renderer/helpers/maybeNullOpenapiShorthandToOpenapiShorthand.js +14 -0
- package/dist/esm/src/openapi-renderer/helpers/openapiShorthandToOpenapi.js +102 -0
- package/dist/esm/src/openapi-renderer/helpers/primitiveOpenapiStatementToOpenapi.js +1 -1
- package/dist/esm/src/server/params.js +3 -3
- package/dist/types/src/error/openapi/AttemptedToDeriveDescendentSerializersFromNonSerializer.d.ts +5 -0
- package/dist/types/src/error/openapi/ExpectedSerializerForRendersOneOrManyOption.d.ts +8 -0
- package/dist/types/src/error/openapi/NoSerializerFoundForRendersOneAndMany.d.ts +5 -0
- package/dist/types/src/error/openapi/NonSerializerPassedToSerializerOpenapiRenderer.d.ts +5 -0
- package/dist/types/src/error/openapi/NonSerializerSerializerOverrideProvided.d.ts +6 -0
- package/dist/types/src/error/openapi/ObjectSerializerRendersOneAndManyRequireClassType.d.ts +5 -0
- package/dist/types/src/helpers/isObject.d.ts +1 -0
- package/dist/types/src/openapi-renderer/SerializerOpenapiRenderer.d.ts +25 -0
- package/dist/types/src/openapi-renderer/helpers/allSerializersFromHandWrittenOpenapi.d.ts +2 -0
- package/dist/types/src/openapi-renderer/helpers/allSerializersToRefsInOpenapi.d.ts +2 -0
- package/dist/types/src/openapi-renderer/helpers/dreamAttributeOpenapiShape.d.ts +780 -0
- package/dist/types/src/openapi-renderer/helpers/isOpenapiShorthand.d.ts +1 -0
- package/dist/types/src/openapi-renderer/helpers/maybeNullOpenapiShorthandToOpenapiShorthand.d.ts +2 -0
- package/dist/types/src/openapi-renderer/helpers/openapiShorthandToOpenapi.d.ts +16 -0
- package/package.json +2 -2
- package/dist/cjs/src/helpers/typechecks.js +0 -20
- package/dist/esm/src/helpers/typechecks.js +0 -16
- package/dist/types/src/helpers/typechecks.d.ts +0 -2
|
@@ -32,7 +32,7 @@ const path = __importStar(require("node:path"));
|
|
|
32
32
|
const TypesBuilder_js_1 = __importDefault(require("../cli/helpers/TypesBuilder.js"));
|
|
33
33
|
const controller_js_1 = __importDefault(require("../generate/controller.js"));
|
|
34
34
|
const resource_js_1 = __importDefault(require("../generate/resource.js"));
|
|
35
|
-
const
|
|
35
|
+
const isObject_js_1 = __importDefault(require("../helpers/isObject.js"));
|
|
36
36
|
const app_js_1 = __importDefault(require("../openapi-renderer/app.js"));
|
|
37
37
|
const index_js_1 = __importDefault(require("../psychic-app/index.js"));
|
|
38
38
|
const index_js_2 = __importDefault(require("../server/index.js"));
|
|
@@ -75,7 +75,7 @@ class PsychicBin {
|
|
|
75
75
|
for (const hook of psychicApp.specialHooks.cliSync) {
|
|
76
76
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
77
77
|
const res = await hook();
|
|
78
|
-
if ((0,
|
|
78
|
+
if ((0, isObject_js_1.default)(res)) {
|
|
79
79
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
80
80
|
output = { ...output, ...res };
|
|
81
81
|
}
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.default = isPaginatedResult;
|
|
4
|
-
const
|
|
7
|
+
const isObject_js_1 = __importDefault(require("../../helpers/isObject.js"));
|
|
5
8
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
6
9
|
function isPaginatedResult(result) {
|
|
7
|
-
if (!(0,
|
|
10
|
+
if (!(0, isObject_js_1.default)(result))
|
|
8
11
|
return false;
|
|
9
12
|
const paginatedFields = ['currentPage', 'pageCount', 'recordCount', 'results'];
|
|
10
13
|
const keys = Object.keys(result);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const node_util_1 = require("node:util");
|
|
4
|
+
class AttemptedToDeriveDescendentSerializersFromNonSerializer extends Error {
|
|
5
|
+
serializer;
|
|
6
|
+
constructor(serializer) {
|
|
7
|
+
super();
|
|
8
|
+
this.serializer = serializer;
|
|
9
|
+
}
|
|
10
|
+
get message() {
|
|
11
|
+
return `
|
|
12
|
+
Attempted to derive descendant serializers from non serializer:
|
|
13
|
+
${(0, node_util_1.inspect)(this.serializer)}`;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
exports.default = AttemptedToDeriveDescendentSerializersFromNonSerializer;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
class ExpectedSerializerForRendersOneOrManyOption extends Error {
|
|
4
|
+
rendersOneOrMany;
|
|
5
|
+
referencingSerializerName;
|
|
6
|
+
attribute;
|
|
7
|
+
constructor(rendersOneOrMany, referencingSerializerName, attribute) {
|
|
8
|
+
super();
|
|
9
|
+
this.rendersOneOrMany = rendersOneOrMany;
|
|
10
|
+
this.referencingSerializerName = referencingSerializerName;
|
|
11
|
+
this.attribute = attribute;
|
|
12
|
+
}
|
|
13
|
+
get message() {
|
|
14
|
+
const baseString = `
|
|
15
|
+
The \`serializer\` option on the \`${this.attribute.name}\` \`${this.rendersOneOrMany}\`
|
|
16
|
+
on serializer \`${this.referencingSerializerName}\``;
|
|
17
|
+
if (this.attribute.options?.serializer) {
|
|
18
|
+
return `${baseString}
|
|
19
|
+
specifies something other than a serializer.
|
|
20
|
+
It should look something like the following:
|
|
21
|
+
|
|
22
|
+
\`\`\`
|
|
23
|
+
.${this.rendersOneOrMany}('${this.attribute.name}', {
|
|
24
|
+
serializer: MySerializer
|
|
25
|
+
})
|
|
26
|
+
\`\`\`
|
|
27
|
+
`;
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
return `${baseString}
|
|
31
|
+
is throwing an error.
|
|
32
|
+
`;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
exports.default = ExpectedSerializerForRendersOneOrManyOption;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
class NoSerializerFoundForRendersOneAndMany extends Error {
|
|
4
|
+
associationName;
|
|
5
|
+
constructor(associationName) {
|
|
6
|
+
super();
|
|
7
|
+
this.associationName = associationName;
|
|
8
|
+
}
|
|
9
|
+
get message() {
|
|
10
|
+
return `
|
|
11
|
+
Attempted to render \`rendersOne\` / \`rendersMany\`
|
|
12
|
+
\`${this.associationName}\`, but could not locate a
|
|
13
|
+
serializer.`;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
exports.default = NoSerializerFoundForRendersOneAndMany;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const node_util_1 = require("node:util");
|
|
4
|
+
class NonSerializerPassedToSerializerOpenapiRenderer extends Error {
|
|
5
|
+
serializer;
|
|
6
|
+
constructor(serializer) {
|
|
7
|
+
super();
|
|
8
|
+
this.serializer = serializer;
|
|
9
|
+
}
|
|
10
|
+
get message() {
|
|
11
|
+
return `
|
|
12
|
+
Non-serializer passed to SerializerOpenapiRenderer:
|
|
13
|
+
${(0, node_util_1.inspect)(this.serializer)}`;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
exports.default = NonSerializerPassedToSerializerOpenapiRenderer;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const node_util_1 = require("node:util");
|
|
4
|
+
class NonSerializerSerializerOverrideProvided extends Error {
|
|
5
|
+
rendersAssociation;
|
|
6
|
+
serializer;
|
|
7
|
+
constructor(rendersAssociation, serializer) {
|
|
8
|
+
super();
|
|
9
|
+
this.rendersAssociation = rendersAssociation;
|
|
10
|
+
this.serializer = serializer;
|
|
11
|
+
}
|
|
12
|
+
get message() {
|
|
13
|
+
return `
|
|
14
|
+
Non-serializer passed to \`serializerOverride\` of a rendersOne/Many declaration:
|
|
15
|
+
|
|
16
|
+
rendersOne/Many declaration:
|
|
17
|
+
${(0, node_util_1.inspect)(this.rendersAssociation)}
|
|
18
|
+
|
|
19
|
+
non-serializer passed to serializerOverride:
|
|
20
|
+
${(0, node_util_1.inspect)(this.serializer)}`;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
exports.default = NonSerializerSerializerOverrideProvided;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
class ObjectSerializerRendersOneAndManyRequireClassType extends Error {
|
|
4
|
+
associationName;
|
|
5
|
+
constructor(associationName) {
|
|
6
|
+
super();
|
|
7
|
+
this.associationName = associationName;
|
|
8
|
+
}
|
|
9
|
+
get message() {
|
|
10
|
+
return `
|
|
11
|
+
ObjectSerializer \`rendersOne\` and \`rendersMany\`
|
|
12
|
+
options must include \`dreamClass\`, \`viewModelClass\`, or
|
|
13
|
+
\`serializer\`.
|
|
14
|
+
|
|
15
|
+
rendersOne/Many name: ${this.associationName}
|
|
16
|
+
`;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
exports.default = ObjectSerializerRendersOneAndManyRequireClassType;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = isObject;
|
|
4
|
+
// also in Dream
|
|
5
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
6
|
+
function isObject(x) {
|
|
7
|
+
if (x === null)
|
|
8
|
+
return false;
|
|
9
|
+
if (Array.isArray(x))
|
|
10
|
+
return false;
|
|
11
|
+
return typeof x === 'object';
|
|
12
|
+
}
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.default = pathifyNestedObject;
|
|
4
|
-
const
|
|
7
|
+
const isObject_js_1 = __importDefault(require("./isObject.js"));
|
|
5
8
|
function pathifyNestedObject(obj, prefix = '') {
|
|
6
9
|
return Object.keys(obj).reduce((acc, k) => {
|
|
7
10
|
const pre = prefix.length ? prefix + '/' : '';
|
|
8
|
-
if ((0,
|
|
11
|
+
if ((0, isObject_js_1.default)(obj[k]))
|
|
9
12
|
Object.assign(acc, pathifyNestedObject(obj[k], pre + k));
|
|
10
13
|
else
|
|
11
14
|
acc[pre + k] = obj[k];
|
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
|
7
|
+
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
|
8
|
+
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
|
9
|
+
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
|
10
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
11
|
+
const dream_1 = require("@rvoh/dream");
|
|
12
|
+
const node_util_1 = require("node:util");
|
|
13
|
+
const AttemptedToDeriveDescendentSerializersFromNonSerializer_js_1 = __importDefault(require("../error/openapi/AttemptedToDeriveDescendentSerializersFromNonSerializer.js"));
|
|
14
|
+
const ExpectedSerializerForRendersOneOrManyOption_js_1 = __importDefault(require("../error/openapi/ExpectedSerializerForRendersOneOrManyOption.js"));
|
|
15
|
+
const NonSerializerPassedToSerializerOpenapiRenderer_js_1 = __importDefault(require("../error/openapi/NonSerializerPassedToSerializerOpenapiRenderer.js"));
|
|
16
|
+
const NonSerializerSerializerOverrideProvided_js_1 = __importDefault(require("../error/openapi/NonSerializerSerializerOverrideProvided.js"));
|
|
17
|
+
const NoSerializerFoundForRendersOneAndMany_js_1 = __importDefault(require("../error/openapi/NoSerializerFoundForRendersOneAndMany.js"));
|
|
18
|
+
const ObjectSerializerRendersOneAndManyRequireClassType_js_1 = __importDefault(require("../error/openapi/ObjectSerializerRendersOneAndManyRequireClassType.js"));
|
|
19
|
+
const allSerializersFromHandWrittenOpenapi_js_1 = __importDefault(require("./helpers/allSerializersFromHandWrittenOpenapi.js"));
|
|
20
|
+
const allSerializersToRefsInOpenapi_js_1 = __importDefault(require("./helpers/allSerializersToRefsInOpenapi.js"));
|
|
21
|
+
const dreamAttributeOpenapiShape_js_1 = require("./helpers/dreamAttributeOpenapiShape.js");
|
|
22
|
+
const openapiShorthandToOpenapi_js_1 = __importDefault(require("./helpers/openapiShorthandToOpenapi.js"));
|
|
23
|
+
const NULL_OBJECT_OPENAPI = { type: 'null' };
|
|
24
|
+
class SerializerOpenapiRenderer {
|
|
25
|
+
serializer;
|
|
26
|
+
casing;
|
|
27
|
+
suppressResponseEnums;
|
|
28
|
+
allOfSiblings = [];
|
|
29
|
+
constructor(serializer, { casing = 'camel', suppressResponseEnums = false, } = {}) {
|
|
30
|
+
this.serializer = serializer;
|
|
31
|
+
if (!(0, dream_1.isDreamSerializer)(this.serializer))
|
|
32
|
+
throw new NonSerializerPassedToSerializerOpenapiRenderer_js_1.default(this.serializer);
|
|
33
|
+
this.casing = casing;
|
|
34
|
+
this.suppressResponseEnums = suppressResponseEnums;
|
|
35
|
+
}
|
|
36
|
+
get globalName() {
|
|
37
|
+
return this.serializer.globalName ?? '--unnamed--';
|
|
38
|
+
}
|
|
39
|
+
get openapiName() {
|
|
40
|
+
return this.serializer.openapiName ?? '--unnamed--';
|
|
41
|
+
}
|
|
42
|
+
get serializerRef() {
|
|
43
|
+
return {
|
|
44
|
+
$ref: `#/components/schemas/${this.openapiName}`,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
_serializerBuilder;
|
|
48
|
+
get serializerBuilder() {
|
|
49
|
+
if (this._serializerBuilder)
|
|
50
|
+
return this._serializerBuilder;
|
|
51
|
+
this._serializerBuilder = this.serializer(undefined, undefined);
|
|
52
|
+
return this._serializerBuilder;
|
|
53
|
+
}
|
|
54
|
+
renderedOpenapi(alreadyExtractedDescendantSerializers = {}) {
|
|
55
|
+
alreadyExtractedDescendantSerializers[this.serializer.globalName] = true;
|
|
56
|
+
const referencedSerializersAndOpenapiSchemaBodyShorthand = this._renderedOpenapi(alreadyExtractedDescendantSerializers);
|
|
57
|
+
if (this.allOfSiblings.length) {
|
|
58
|
+
const openapi = referencedSerializersAndOpenapiSchemaBodyShorthand.openapi;
|
|
59
|
+
return {
|
|
60
|
+
...referencedSerializersAndOpenapiSchemaBodyShorthand,
|
|
61
|
+
openapi: {
|
|
62
|
+
allOf: [openapi, ...this.allOfSiblings],
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
return referencedSerializersAndOpenapiSchemaBodyShorthand;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
_renderedOpenapi(alreadyExtractedDescendantSerializers) {
|
|
71
|
+
const referencedSerializersAndAttributes = this.renderedOpenapiAttributes(alreadyExtractedDescendantSerializers);
|
|
72
|
+
const requiredProperties = (0, dream_1.compact)(this.serializerBuilder['attributes'].map(attribute => {
|
|
73
|
+
const attributeType = attribute.type;
|
|
74
|
+
switch (attributeType) {
|
|
75
|
+
case 'attribute': {
|
|
76
|
+
return attribute.options?.as ?? attribute.name;
|
|
77
|
+
}
|
|
78
|
+
case 'delegatedAttribute': {
|
|
79
|
+
return attribute.options?.as ?? attribute.name;
|
|
80
|
+
}
|
|
81
|
+
case 'customAttribute': {
|
|
82
|
+
return attribute.options.flatten ? null : attribute.name;
|
|
83
|
+
}
|
|
84
|
+
case 'rendersOne': {
|
|
85
|
+
return attribute.options.flatten ? null : (attribute.options?.as ?? attribute.name);
|
|
86
|
+
}
|
|
87
|
+
case 'rendersMany': {
|
|
88
|
+
return attribute.options?.as ?? attribute.name;
|
|
89
|
+
}
|
|
90
|
+
default: {
|
|
91
|
+
// protection so that if a new ValidationType is ever added, this will throw a type error at build time
|
|
92
|
+
const _never = attributeType;
|
|
93
|
+
throw new Error(`Unhandled serializer attribute type: ${_never}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}));
|
|
97
|
+
return {
|
|
98
|
+
referencedSerializers: referencedSerializersAndAttributes.referencedSerializers,
|
|
99
|
+
openapi: {
|
|
100
|
+
type: 'object',
|
|
101
|
+
required: (0, dream_1.sort)((0, dream_1.uniq)(requiredProperties.map(property => this.setCase(property)))),
|
|
102
|
+
properties: (0, dream_1.sortObjectByKey)(referencedSerializersAndAttributes.attributes),
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
renderedOpenapiAttributes(alreadyExtractedDescendantSerializers = {}) {
|
|
107
|
+
const $typeForOpenapi = this.serializerBuilder['$typeForOpenapi'];
|
|
108
|
+
const DataTypeForOpenapi = $typeForOpenapi;
|
|
109
|
+
let referencedSerializers = [];
|
|
110
|
+
let renderedOpenapi = {};
|
|
111
|
+
const openapiRenderingOpts = {
|
|
112
|
+
casing: this.casing,
|
|
113
|
+
suppressResponseEnums: this.suppressResponseEnums,
|
|
114
|
+
};
|
|
115
|
+
renderedOpenapi = this.serializerBuilder['attributes'].reduce((accumulator, attribute) => {
|
|
116
|
+
const attributeType = attribute.type;
|
|
117
|
+
let newlyReferencedSerializers = [];
|
|
118
|
+
accumulator = (() => {
|
|
119
|
+
switch (attributeType) {
|
|
120
|
+
////////////////
|
|
121
|
+
// attributes //
|
|
122
|
+
////////////////
|
|
123
|
+
case 'attribute': {
|
|
124
|
+
const outputAttributeName = this.setCase(attribute.options?.as ?? attribute.name);
|
|
125
|
+
const openapi = attribute.options.openapi;
|
|
126
|
+
newlyReferencedSerializers = (0, allSerializersFromHandWrittenOpenapi_js_1.default)(openapi);
|
|
127
|
+
accumulator[outputAttributeName] = DataTypeForOpenapi?.isDream
|
|
128
|
+
? (0, dreamAttributeOpenapiShape_js_1.dreamColumnOpenapiShape)(DataTypeForOpenapi, attribute.name, openapi, {
|
|
129
|
+
suppressResponseEnums: this.suppressResponseEnums,
|
|
130
|
+
})
|
|
131
|
+
: (0, allSerializersToRefsInOpenapi_js_1.default)((0, openapiShorthandToOpenapi_js_1.default)(openapi));
|
|
132
|
+
return accumulator;
|
|
133
|
+
}
|
|
134
|
+
/////////////////////
|
|
135
|
+
// end: attributes //
|
|
136
|
+
/////////////////////
|
|
137
|
+
///////////////////////
|
|
138
|
+
// custom attributes //
|
|
139
|
+
///////////////////////
|
|
140
|
+
case 'customAttribute': {
|
|
141
|
+
const outputAttributeName = this.setCase(attribute.name);
|
|
142
|
+
const openapi = attribute.options.openapi;
|
|
143
|
+
newlyReferencedSerializers = (0, allSerializersFromHandWrittenOpenapi_js_1.default)(openapi);
|
|
144
|
+
if (attribute.options.flatten) {
|
|
145
|
+
this.allOfSiblings.push((0, allSerializersToRefsInOpenapi_js_1.default)((0, openapiShorthandToOpenapi_js_1.default)(openapi)));
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
accumulator[outputAttributeName] = (0, allSerializersToRefsInOpenapi_js_1.default)((0, openapiShorthandToOpenapi_js_1.default)(openapi));
|
|
149
|
+
}
|
|
150
|
+
return accumulator;
|
|
151
|
+
}
|
|
152
|
+
////////////////////////////
|
|
153
|
+
// end: custom attributes //
|
|
154
|
+
////////////////////////////
|
|
155
|
+
//////////////////////////
|
|
156
|
+
// delegated attributes //
|
|
157
|
+
//////////////////////////
|
|
158
|
+
case 'delegatedAttribute': {
|
|
159
|
+
const outputAttributeName = this.setCase(attribute.options?.as ?? attribute.name);
|
|
160
|
+
const openapi = attribute.options.openapi;
|
|
161
|
+
newlyReferencedSerializers = (0, allSerializersFromHandWrittenOpenapi_js_1.default)(openapi);
|
|
162
|
+
accumulator[outputAttributeName] = (0, allSerializersToRefsInOpenapi_js_1.default)((0, openapiShorthandToOpenapi_js_1.default)(openapi));
|
|
163
|
+
return accumulator;
|
|
164
|
+
}
|
|
165
|
+
///////////////////////////////
|
|
166
|
+
// end: delegated attributes //
|
|
167
|
+
///////////////////////////////
|
|
168
|
+
//////////////////
|
|
169
|
+
// rendersOnes //
|
|
170
|
+
//////////////////
|
|
171
|
+
case 'rendersOne': {
|
|
172
|
+
try {
|
|
173
|
+
const outputAttributeName = this.setCase(attribute.options.as ?? attribute.name);
|
|
174
|
+
const referencedSerializersAndOpenapiSchemaBodyShorthand = associationOpenapi(attribute, DataTypeForOpenapi, alreadyExtractedDescendantSerializers, openapiRenderingOpts);
|
|
175
|
+
newlyReferencedSerializers =
|
|
176
|
+
referencedSerializersAndOpenapiSchemaBodyShorthand.referencedSerializers;
|
|
177
|
+
if (attribute.options.flatten && attribute.options.optional) {
|
|
178
|
+
this.allOfSiblings.push({
|
|
179
|
+
anyOf: [referencedSerializersAndOpenapiSchemaBodyShorthand.openapi, NULL_OBJECT_OPENAPI],
|
|
180
|
+
});
|
|
181
|
+
//
|
|
182
|
+
}
|
|
183
|
+
else if (attribute.options.flatten) {
|
|
184
|
+
this.allOfSiblings.push(referencedSerializersAndOpenapiSchemaBodyShorthand.openapi);
|
|
185
|
+
//
|
|
186
|
+
}
|
|
187
|
+
else if (attribute.options.optional) {
|
|
188
|
+
accumulator[outputAttributeName] = {
|
|
189
|
+
anyOf: [referencedSerializersAndOpenapiSchemaBodyShorthand.openapi, NULL_OBJECT_OPENAPI],
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
accumulator[outputAttributeName] = referencedSerializersAndOpenapiSchemaBodyShorthand.openapi;
|
|
194
|
+
}
|
|
195
|
+
return accumulator;
|
|
196
|
+
}
|
|
197
|
+
catch (error) {
|
|
198
|
+
if (error instanceof CallingSerializersThrewError)
|
|
199
|
+
return accumulator;
|
|
200
|
+
if (error instanceof AttemptedToDeriveDescendentSerializersFromNonSerializer_js_1.default)
|
|
201
|
+
throw new ExpectedSerializerForRendersOneOrManyOption_js_1.default('rendersOne', this.globalName, attribute);
|
|
202
|
+
throw error;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
///////////////////////
|
|
206
|
+
// end: rendersOnes //
|
|
207
|
+
///////////////////////
|
|
208
|
+
///////////////////
|
|
209
|
+
// rendersManys //
|
|
210
|
+
///////////////////
|
|
211
|
+
case 'rendersMany': {
|
|
212
|
+
try {
|
|
213
|
+
const outputAttributeName = this.setCase(attribute.options.as ?? attribute.name);
|
|
214
|
+
const referencedSerializersAndOpenapiSchemaBodyShorthand = associationOpenapi(attribute, DataTypeForOpenapi, alreadyExtractedDescendantSerializers, openapiRenderingOpts);
|
|
215
|
+
newlyReferencedSerializers =
|
|
216
|
+
referencedSerializersAndOpenapiSchemaBodyShorthand.referencedSerializers;
|
|
217
|
+
accumulator[outputAttributeName] = {
|
|
218
|
+
type: 'array',
|
|
219
|
+
items: referencedSerializersAndOpenapiSchemaBodyShorthand.openapi,
|
|
220
|
+
};
|
|
221
|
+
return accumulator;
|
|
222
|
+
}
|
|
223
|
+
catch (error) {
|
|
224
|
+
if (error instanceof CallingSerializersThrewError)
|
|
225
|
+
return accumulator;
|
|
226
|
+
if (error instanceof AttemptedToDeriveDescendentSerializersFromNonSerializer_js_1.default)
|
|
227
|
+
throw new ExpectedSerializerForRendersOneOrManyOption_js_1.default('rendersMany', this.globalName, attribute);
|
|
228
|
+
throw error;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
////////////////////////
|
|
232
|
+
// end: rendersManys //
|
|
233
|
+
////////////////////////
|
|
234
|
+
default: {
|
|
235
|
+
// protection so that if a new ValidationType is ever added, this will throw a type error at build time
|
|
236
|
+
const _never = attributeType;
|
|
237
|
+
throw new Error(`Unhandled serializer attribute type: ${_never}`);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
})();
|
|
241
|
+
const recursiveNewlyReferencedSerializers = newlyReferencedSerializers.flatMap(serializer => descendantSerializers(serializer, alreadyExtractedDescendantSerializers, openapiRenderingOpts));
|
|
242
|
+
referencedSerializers = [
|
|
243
|
+
...referencedSerializers,
|
|
244
|
+
...newlyReferencedSerializers,
|
|
245
|
+
...recursiveNewlyReferencedSerializers,
|
|
246
|
+
];
|
|
247
|
+
return accumulator;
|
|
248
|
+
}, renderedOpenapi);
|
|
249
|
+
return {
|
|
250
|
+
referencedSerializers: (0, dream_1.uniq)(referencedSerializers, serializer => serializer.globalName),
|
|
251
|
+
attributes: renderedOpenapi,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
setCase(attr) {
|
|
255
|
+
switch (this.casing) {
|
|
256
|
+
case 'camel':
|
|
257
|
+
return attr;
|
|
258
|
+
case 'snake':
|
|
259
|
+
return (0, dream_1.snakeify)(attr);
|
|
260
|
+
default: {
|
|
261
|
+
// protection so that if a new Casing is ever added, this will throw a type error at build time
|
|
262
|
+
const _never = this.casing;
|
|
263
|
+
throw new Error(`Unhandled Casing: ${_never}`);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
exports.default = SerializerOpenapiRenderer;
|
|
269
|
+
function associationOpenapi(attribute, DataTypeForOpenapi, alreadyExtractedDescendantSerializers, opts) {
|
|
270
|
+
const serializerOverride = attribute.options.serializer;
|
|
271
|
+
if (serializerOverride) {
|
|
272
|
+
try {
|
|
273
|
+
return {
|
|
274
|
+
referencedSerializers: [
|
|
275
|
+
serializerOverride,
|
|
276
|
+
...descendantSerializers(serializerOverride, alreadyExtractedDescendantSerializers, opts),
|
|
277
|
+
],
|
|
278
|
+
openapi: new SerializerOpenapiRenderer(serializerOverride, opts).serializerRef,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
catch (error) {
|
|
282
|
+
if (error instanceof NonSerializerPassedToSerializerOpenapiRenderer_js_1.default)
|
|
283
|
+
throw new NonSerializerSerializerOverrideProvided_js_1.default(attribute, serializerOverride);
|
|
284
|
+
throw error;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
let associatedClasses;
|
|
288
|
+
const association = DataTypeForOpenapi?.isDream &&
|
|
289
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
|
290
|
+
DataTypeForOpenapi['getAssociationMetadata'](attribute.name);
|
|
291
|
+
if (association) {
|
|
292
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
|
293
|
+
associatedClasses = (0, dream_1.expandStiClasses)(association.modelCB());
|
|
294
|
+
//
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
const associatedClass = attribute.options.dreamClass ?? attribute.options.viewModelClass;
|
|
298
|
+
if (associatedClass === undefined) {
|
|
299
|
+
let serializerCheck;
|
|
300
|
+
try {
|
|
301
|
+
;
|
|
302
|
+
DataTypeForOpenapi?.prototype?.serializers;
|
|
303
|
+
}
|
|
304
|
+
catch {
|
|
305
|
+
throw new CallingSerializersThrewError();
|
|
306
|
+
}
|
|
307
|
+
if (serializerCheck)
|
|
308
|
+
throw new ObjectSerializerRendersOneAndManyRequireClassType_js_1.default(attribute.name);
|
|
309
|
+
throw new ObjectSerializerRendersOneAndManyRequireClassType_js_1.default(attribute.name);
|
|
310
|
+
}
|
|
311
|
+
if (associatedClass?.isDream) {
|
|
312
|
+
associatedClasses = (0, dream_1.expandStiClasses)(associatedClass);
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
associatedClasses = [associatedClass];
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
const serializersOpenapi = associatedClasses.flatMap(associatedClass => (0, dream_1.inferSerializersFromDreamClassOrViewModelClass)(associatedClass, attribute.options.serializerKey));
|
|
319
|
+
if (serializersOpenapi.length === 0)
|
|
320
|
+
throw new NoSerializerFoundForRendersOneAndMany_js_1.default(attribute.name);
|
|
321
|
+
if (serializersOpenapi.length === 1) {
|
|
322
|
+
const serializer = serializersOpenapi[0];
|
|
323
|
+
return {
|
|
324
|
+
referencedSerializers: [
|
|
325
|
+
serializer,
|
|
326
|
+
...descendantSerializers(serializer, alreadyExtractedDescendantSerializers, opts),
|
|
327
|
+
],
|
|
328
|
+
openapi: new SerializerOpenapiRenderer(serializer, opts).serializerRef,
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
return {
|
|
332
|
+
referencedSerializers: [
|
|
333
|
+
...serializersOpenapi,
|
|
334
|
+
...serializersOpenapi.flatMap(serializer => descendantSerializers(serializer, alreadyExtractedDescendantSerializers, opts)),
|
|
335
|
+
],
|
|
336
|
+
openapi: {
|
|
337
|
+
anyOf: (0, dream_1.sortBy)((0, dream_1.uniq)(serializersOpenapi.map(serializer => new SerializerOpenapiRenderer(serializer, opts).serializerRef), ref => ref['$ref']), ref => (ref['$ref'] ? ref['$ref'] : (0, node_util_1.inspect)(ref, { depth: 2 }))),
|
|
338
|
+
},
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
function descendantSerializers(serializer, alreadyExtractedDescendantSerializers, opts) {
|
|
342
|
+
// alreadyExtractedDescendantSerializers is used not only to avoid duplicate
|
|
343
|
+
// work (and thereby speed up OpenAPI spec generation), but also to avoid
|
|
344
|
+
// infinite loops (a recursive OpenAPI structure is valid)
|
|
345
|
+
if (alreadyExtractedDescendantSerializers[serializer.globalName])
|
|
346
|
+
return [];
|
|
347
|
+
if (!(0, dream_1.isDreamSerializer)(serializer))
|
|
348
|
+
throw new AttemptedToDeriveDescendentSerializersFromNonSerializer_js_1.default(serializer);
|
|
349
|
+
const immediateDescendantSerializers = new SerializerOpenapiRenderer(serializer, opts).renderedOpenapi(alreadyExtractedDescendantSerializers).referencedSerializers;
|
|
350
|
+
return [
|
|
351
|
+
...immediateDescendantSerializers,
|
|
352
|
+
...immediateDescendantSerializers.flatMap(descendantSerializer => descendantSerializers(descendantSerializer, alreadyExtractedDescendantSerializers, opts)),
|
|
353
|
+
];
|
|
354
|
+
}
|
|
355
|
+
// When attempting to expand STI children, we might call `.serializers` on
|
|
356
|
+
// an instance that throws an error just by calling `.serializers` (so that
|
|
357
|
+
// they can be sure to define serializers on the STI children, but in this
|
|
358
|
+
// case, there might be STI children that are intermediaries to the intended
|
|
359
|
+
// STI children, so they don't have serializers and calling `.serializers`
|
|
360
|
+
// throws an error)
|
|
361
|
+
class CallingSerializersThrewError extends Error {
|
|
362
|
+
}
|
|
@@ -8,8 +8,10 @@ const dream_1 = require("@rvoh/dream");
|
|
|
8
8
|
const NonSerializerSuppliedToSerializerBodySegment_js_1 = __importDefault(require("../error/openapi/NonSerializerSuppliedToSerializerBodySegment.js"));
|
|
9
9
|
const isArrayParamName_js_1 = __importDefault(require("../helpers/isArrayParamName.js"));
|
|
10
10
|
const isBlankDescription_js_1 = __importDefault(require("./helpers/isBlankDescription.js"));
|
|
11
|
+
const maybeNullOpenapiShorthandToOpenapiShorthand_js_1 = __importDefault(require("./helpers/maybeNullOpenapiShorthandToOpenapiShorthand.js"));
|
|
11
12
|
const primitiveOpenapiStatementToOpenapi_js_1 = __importDefault(require("./helpers/primitiveOpenapiStatementToOpenapi.js"));
|
|
12
13
|
const schemaToRef_js_1 = __importDefault(require("./helpers/schemaToRef.js"));
|
|
14
|
+
const SerializerOpenapiRenderer_js_1 = __importDefault(require("./SerializerOpenapiRenderer.js"));
|
|
13
15
|
class OpenapiBodySegmentRenderer {
|
|
14
16
|
bodySegment;
|
|
15
17
|
casing;
|
|
@@ -133,7 +135,7 @@ class OpenapiBodySegmentRenderer {
|
|
|
133
135
|
else if (this.maybeNullTypeToType(arrayBodySegment) === 'array')
|
|
134
136
|
return 'array';
|
|
135
137
|
else {
|
|
136
|
-
const primitiveString = (0,
|
|
138
|
+
const primitiveString = (0, maybeNullOpenapiShorthandToOpenapiShorthand_js_1.default)(bodySegment);
|
|
137
139
|
if (typeof primitiveString === 'string' && dream_1.openapiShorthandPrimitiveTypes.includes(primitiveString))
|
|
138
140
|
return 'openapi_primitive_literal';
|
|
139
141
|
if (typeof bodySegment === 'object') {
|
|
@@ -340,7 +342,7 @@ class OpenapiBodySegmentRenderer {
|
|
|
340
342
|
}
|
|
341
343
|
}
|
|
342
344
|
typeIsOpenapiArrayPrimitive(openapiType) {
|
|
343
|
-
return (0, isArrayParamName_js_1.default)((0,
|
|
345
|
+
return (0, isArrayParamName_js_1.default)((0, maybeNullOpenapiShorthandToOpenapiShorthand_js_1.default)(openapiType));
|
|
344
346
|
}
|
|
345
347
|
applyConfigurationOptions(obj) {
|
|
346
348
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
@@ -386,7 +388,7 @@ The following values will be allowed:
|
|
|
386
388
|
const serializer = serializerRefBodySegment.$serializer;
|
|
387
389
|
if (!(0, dream_1.isDreamSerializer)(serializer))
|
|
388
390
|
throw new NonSerializerSuppliedToSerializerBodySegment_js_1.default(this.bodySegment, serializer);
|
|
389
|
-
const serializerRef = new
|
|
391
|
+
const serializerRef = new SerializerOpenapiRenderer_js_1.default(serializer, {
|
|
390
392
|
casing: this.casing,
|
|
391
393
|
suppressResponseEnums: this.suppressResponseEnums,
|
|
392
394
|
}).serializerRef;
|
|
@@ -16,6 +16,7 @@ const openapiRoute_js_1 = __importDefault(require("./helpers/openapiRoute.js"));
|
|
|
16
16
|
const pageParamOpenapiProperty_js_1 = __importDefault(require("./helpers/pageParamOpenapiProperty.js"));
|
|
17
17
|
const primitiveOpenapiStatementToOpenapi_js_1 = __importDefault(require("./helpers/primitiveOpenapiStatementToOpenapi.js"));
|
|
18
18
|
const safelyAttachPaginationParamsToBodySegment_js_1 = __importDefault(require("./helpers/safelyAttachPaginationParamsToBodySegment.js"));
|
|
19
|
+
const SerializerOpenapiRenderer_js_1 = __importDefault(require("./SerializerOpenapiRenderer.js"));
|
|
19
20
|
class OpenapiEndpointRenderer {
|
|
20
21
|
dreamsOrSerializers;
|
|
21
22
|
controllerClass;
|
|
@@ -716,7 +717,7 @@ class OpenapiEndpointRenderer {
|
|
|
716
717
|
if (!(0, dream_1.isDreamSerializer)(serializer)) {
|
|
717
718
|
throw new SerializerForEndpointNotAFunction_js_1.default(this.controllerClass, this.action, serializer);
|
|
718
719
|
}
|
|
719
|
-
const serializerOpenapiRenderer = new
|
|
720
|
+
const serializerOpenapiRenderer = new SerializerOpenapiRenderer_js_1.default(serializer, renderOpts);
|
|
720
721
|
const finalOutput = {
|
|
721
722
|
content: {
|
|
722
723
|
'application/json': {
|
|
@@ -789,10 +790,10 @@ class OpenapiEndpointRenderer {
|
|
|
789
790
|
if (!(0, dream_1.isDreamSerializer)(serializer))
|
|
790
791
|
throw new NonSerializerDerivedInOpenapiEndpointRenderer_js_1.default(this.controllerClass, this.action, serializer);
|
|
791
792
|
});
|
|
792
|
-
const sortedSerializerClasses = (0, dream_1.sortBy)(serializers, serializer => new
|
|
793
|
+
const sortedSerializerClasses = (0, dream_1.sortBy)(serializers, serializer => new SerializerOpenapiRenderer_js_1.default(serializer, renderOpts).openapiName);
|
|
793
794
|
let referencedSerializers = [];
|
|
794
795
|
sortedSerializerClasses.forEach(serializer => {
|
|
795
|
-
const serializerOpenapiRenderer = new
|
|
796
|
+
const serializerOpenapiRenderer = new SerializerOpenapiRenderer_js_1.default(serializer, renderOpts);
|
|
796
797
|
anyOf.anyOf.push(serializerOpenapiRenderer.serializerRef);
|
|
797
798
|
referencedSerializers = [
|
|
798
799
|
...referencedSerializers,
|
|
@@ -977,12 +978,12 @@ function serializersToSchemaObjects(controllerClass, actionName, serializers, {
|
|
|
977
978
|
if (!(0, dream_1.isDreamSerializer)(serializer))
|
|
978
979
|
throw new NonSerializerDerivedInToSchemaObjects_js_1.default(controllerClass, actionName, serializer);
|
|
979
980
|
});
|
|
980
|
-
serializers = serializers.filter(serializer => !renderedSchemasOpenapi[new
|
|
981
|
+
serializers = serializers.filter(serializer => !renderedSchemasOpenapi[new SerializerOpenapiRenderer_js_1.default(serializer, renderOpts).openapiName]);
|
|
981
982
|
if (!serializers.length)
|
|
982
983
|
return;
|
|
983
984
|
let dependentOnSerializers = [];
|
|
984
985
|
serializers.forEach(serializer => {
|
|
985
|
-
const renderer = new
|
|
986
|
+
const renderer = new SerializerOpenapiRenderer_js_1.default(serializer, renderOpts);
|
|
986
987
|
const results = renderer.renderedOpenapi(alreadyExtractedDescendantSerializers);
|
|
987
988
|
const segmentRendererResults = new body_segment_js_1.default(results.openapi, {
|
|
988
989
|
openapiName,
|