@rvoh/dream 2.36.1 → 2.38.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/src/db/dataTypes.js +0 -8
- package/dist/cjs/src/dream/QueryDriver/Base.js +17 -24
- package/dist/cjs/src/dream/QueryDriver/Kysely.js +22 -23
- package/dist/cjs/src/dream/QueryDriver/Postgres.js +113 -31
- package/dist/cjs/src/dream-app/index.js +5 -0
- package/dist/cjs/src/helpers/cli/ASTBuilder.js +11 -202
- package/dist/cjs/src/helpers/cli/ASTConnectionBuilder.js +97 -75
- package/dist/cjs/src/helpers/cli/ASTDbTypesBuilder.js +132 -0
- package/dist/cjs/src/helpers/cli/ASTSchemaBuilder.js +37 -81
- package/dist/cjs/src/helpers/cli/dbTypesNaming.js +90 -0
- package/dist/cjs/src/helpers/cli/tableMatchesPattern.js +21 -0
- package/dist/cjs/src/serializer/DreamSerializer.js +23 -0
- package/dist/cjs/src/serializer/ObjectSerializer.js +22 -0
- package/dist/cjs/src/serializer/SerializerRenderer.js +36 -54
- package/dist/cjs/src/serializer/builders/DreamSerializerBuilder.js +32 -10
- package/dist/cjs/src/serializer/builders/ObjectSerializerBuilder.js +32 -10
- package/dist/cjs/src/serializer/helpers/nullFlattenedSerializerKeys.js +257 -0
- package/dist/esm/src/db/dataTypes.js +0 -8
- package/dist/esm/src/dream/QueryDriver/Base.js +17 -24
- package/dist/esm/src/dream/QueryDriver/Kysely.js +22 -23
- package/dist/esm/src/dream/QueryDriver/Postgres.js +113 -31
- package/dist/esm/src/dream-app/index.js +5 -0
- package/dist/esm/src/helpers/cli/ASTBuilder.js +11 -202
- package/dist/esm/src/helpers/cli/ASTConnectionBuilder.js +97 -75
- package/dist/esm/src/helpers/cli/ASTDbTypesBuilder.js +132 -0
- package/dist/esm/src/helpers/cli/ASTSchemaBuilder.js +37 -81
- package/dist/esm/src/helpers/cli/dbTypesNaming.js +90 -0
- package/dist/esm/src/helpers/cli/tableMatchesPattern.js +21 -0
- package/dist/esm/src/serializer/DreamSerializer.js +23 -0
- package/dist/esm/src/serializer/ObjectSerializer.js +22 -0
- package/dist/esm/src/serializer/SerializerRenderer.js +36 -54
- package/dist/esm/src/serializer/builders/DreamSerializerBuilder.js +32 -10
- package/dist/esm/src/serializer/builders/ObjectSerializerBuilder.js +32 -10
- package/dist/esm/src/serializer/helpers/nullFlattenedSerializerKeys.js +257 -0
- package/dist/types/src/db/dataTypes.d.ts +0 -2
- package/dist/types/src/dream/QueryDriver/Base.d.ts +16 -18
- package/dist/types/src/dream/QueryDriver/Kysely.d.ts +0 -9
- package/dist/types/src/dream/QueryDriver/Postgres.d.ts +9 -9
- package/dist/types/src/dream-app/index.d.ts +17 -12
- package/dist/types/src/helpers/cli/ASTBuilder.d.ts +11 -101
- package/dist/types/src/helpers/cli/ASTConnectionBuilder.d.ts +40 -37
- package/dist/types/src/helpers/cli/ASTDbTypesBuilder.d.ts +26 -0
- package/dist/types/src/helpers/cli/ASTSchemaBuilder.d.ts +5 -15
- package/dist/types/src/helpers/cli/dbTypesNaming.d.ts +37 -0
- package/dist/types/src/helpers/cli/tableMatchesPattern.d.ts +15 -0
- package/dist/types/src/package-exports/types.d.ts +1 -1
- package/dist/types/src/serializer/DreamSerializer.d.ts +23 -0
- package/dist/types/src/serializer/ObjectSerializer.d.ts +22 -0
- package/dist/types/src/serializer/SerializerRenderer.d.ts +1 -13
- package/dist/types/src/serializer/builders/DreamSerializerBuilder.d.ts +34 -12
- package/dist/types/src/serializer/builders/ObjectSerializerBuilder.d.ts +34 -12
- package/dist/types/src/serializer/helpers/nullFlattenedSerializerKeys.d.ts +75 -0
- package/dist/types/src/types/db.d.ts +71 -0
- package/dist/types/src/types/types/db.ts +91 -0
- package/docs/assets/hierarchy.js +1 -1
- package/docs/assets/navigation.js +1 -1
- package/docs/assets/search.js +1 -1
- package/docs/classes/db.DreamMigrationHelpers.html +11 -11
- package/docs/classes/db.KyselyQueryDriver.html +55 -54
- package/docs/classes/db.PostgresQueryDriver.html +56 -56
- package/docs/classes/db.QueryDriverBase.html +60 -59
- package/docs/classes/errors.CannotSaveMissingDream.html +2 -2
- package/docs/classes/errors.CheckConstraintViolation.html +3 -3
- package/docs/classes/errors.ColumnOverflow.html +3 -3
- package/docs/classes/errors.CreateOrFindByFailedToCreateAndFind.html +3 -3
- package/docs/classes/errors.DataIncompatibleWithDatabaseField.html +3 -3
- package/docs/classes/errors.DataTypeColumnTypeMismatch.html +3 -3
- package/docs/classes/errors.DecryptionError.html +2 -2
- package/docs/classes/errors.DecryptionParseError.html +2 -2
- package/docs/classes/errors.DecryptionRotationError.html +3 -3
- package/docs/classes/errors.GlobalNameNotSet.html +3 -3
- package/docs/classes/errors.InvalidCalendarDate.html +2 -2
- package/docs/classes/errors.InvalidClockTime.html +2 -2
- package/docs/classes/errors.InvalidClockTimeTz.html +2 -2
- package/docs/classes/errors.InvalidDateTime.html +2 -2
- package/docs/classes/errors.MissingSerializersDefinition.html +3 -3
- package/docs/classes/errors.NonLoadedAssociation.html +3 -3
- package/docs/classes/errors.NotNullViolation.html +3 -3
- package/docs/classes/errors.RecordNotFound.html +3 -3
- package/docs/classes/errors.SortableScopeDidNotStabilize.html +3 -3
- package/docs/classes/errors.SortableScopeLockWaitTimedOut.html +3 -3
- package/docs/classes/errors.ValidationError.html +3 -3
- package/docs/classes/index.CalendarDate.html +33 -33
- package/docs/classes/index.ClockTime.html +32 -32
- package/docs/classes/index.ClockTimeTz.html +35 -35
- package/docs/classes/index.DateTime.html +86 -86
- package/docs/classes/index.Decorators.html +19 -19
- package/docs/classes/index.Dream.html +125 -125
- package/docs/classes/index.DreamApp.html +11 -11
- package/docs/classes/index.DreamTransaction.html +3 -3
- package/docs/classes/index.Env.html +5 -5
- package/docs/classes/index.Query.html +65 -65
- package/docs/classes/system.CliFileWriter.html +4 -4
- package/docs/classes/system.DreamBin.html +3 -3
- package/docs/classes/system.DreamCLI.html +7 -7
- package/docs/classes/system.DreamImporter.html +2 -2
- package/docs/classes/system.DreamLogos.html +2 -2
- package/docs/classes/system.DreamSerializerBuilder.html +43 -21
- package/docs/classes/system.ObjectSerializerBuilder.html +42 -20
- package/docs/classes/system.PathHelpers.html +3 -3
- package/docs/classes/utils.Encrypt.html +4 -4
- package/docs/classes/utils.Range.html +2 -2
- package/docs/functions/db.closeAllDbConnections.html +1 -1
- package/docs/functions/db.dreamDbConnections.html +1 -1
- package/docs/functions/db.untypedDb.html +1 -1
- package/docs/functions/db.validateColumn.html +1 -1
- package/docs/functions/db.validateTable.html +1 -1
- package/docs/functions/errors.pgErrorType.html +1 -1
- package/docs/functions/index.DreamSerializer.html +18 -1
- package/docs/functions/index.ObjectSerializer.html +17 -1
- package/docs/functions/index.ReplicaSafe.html +1 -1
- package/docs/functions/index.STI.html +1 -1
- package/docs/functions/index.SoftDelete.html +1 -1
- package/docs/functions/utils.camelize.html +1 -1
- package/docs/functions/utils.capitalize.html +1 -1
- package/docs/functions/utils.cloneDeepSafe.html +1 -1
- package/docs/functions/utils.compact.html +1 -1
- package/docs/functions/utils.groupBy.html +1 -1
- package/docs/functions/utils.hyphenize.html +1 -1
- package/docs/functions/utils.intersection.html +1 -1
- package/docs/functions/utils.isEmpty.html +1 -1
- package/docs/functions/utils.normalizeUnicode.html +1 -1
- package/docs/functions/utils.pascalize.html +1 -1
- package/docs/functions/utils.percent.html +1 -1
- package/docs/functions/utils.range.html +1 -1
- package/docs/functions/utils.round.html +1 -1
- package/docs/functions/utils.sanitizeString.html +1 -1
- package/docs/functions/utils.snakeify.html +1 -1
- package/docs/functions/utils.sort.html +1 -1
- package/docs/functions/utils.sortBy.html +1 -1
- package/docs/functions/utils.sortObjectByKey.html +1 -1
- package/docs/functions/utils.sortObjectByValue.html +1 -1
- package/docs/functions/utils.uncapitalize.html +1 -1
- package/docs/functions/utils.uniq.html +1 -1
- package/docs/hierarchy.html +1 -1
- package/docs/interfaces/openapi.OpenapiDescription.html +2 -2
- package/docs/interfaces/openapi.OpenapiSchemaProperties.html +1 -1
- package/docs/interfaces/openapi.OpenapiSchemaPropertiesShorthand.html +1 -1
- package/docs/interfaces/openapi.OpenapiTypeFieldObject.html +1 -1
- package/docs/interfaces/types.BelongsToStatement.html +2 -2
- package/docs/interfaces/types.DecoratorContext.html +2 -2
- package/docs/interfaces/types.DecryptCallbacks.html +2 -2
- package/docs/interfaces/types.DreamAppInitOptions.html +2 -2
- package/docs/interfaces/types.DreamAppOpts.html +2 -2
- package/docs/interfaces/types.DreamDbConfig.html +6 -6
- package/docs/interfaces/types.DurationObject.html +2 -2
- package/docs/interfaces/types.EncryptOptions.html +2 -2
- package/docs/interfaces/types.EncryptionLegacyKeyUsedEvent.html +4 -4
- package/docs/interfaces/types.InternalAnyTypedSerializerRendersMany.html +2 -2
- package/docs/interfaces/types.InternalAnyTypedSerializerRendersOne.html +2 -2
- package/docs/interfaces/types.IntrospectedColumn.html +17 -0
- package/docs/interfaces/types.IntrospectedDatabase.html +7 -0
- package/docs/interfaces/types.IntrospectedEnum.html +5 -0
- package/docs/interfaces/types.IntrospectedTable.html +11 -0
- package/docs/interfaces/types.SerializerRendererOpts.html +2 -2
- package/docs/modules/types.html +1 -1
- package/docs/types/openapi.CommonOpenapiSchemaObjectFields.html +1 -1
- package/docs/types/openapi.OpenapiAllTypes.html +1 -1
- package/docs/types/openapi.OpenapiFormats.html +1 -1
- package/docs/types/openapi.OpenapiNumberFormats.html +1 -1
- package/docs/types/openapi.OpenapiPrimitiveBaseTypes.html +1 -1
- package/docs/types/openapi.OpenapiPrimitiveTypes.html +1 -1
- package/docs/types/openapi.OpenapiSchemaArray.html +1 -1
- package/docs/types/openapi.OpenapiSchemaArrayShorthand.html +1 -1
- package/docs/types/openapi.OpenapiSchemaBase.html +1 -1
- package/docs/types/openapi.OpenapiSchemaBody.html +1 -1
- package/docs/types/openapi.OpenapiSchemaBodyShorthand.html +1 -1
- package/docs/types/openapi.OpenapiSchemaCombinatorFields.html +1 -1
- package/docs/types/openapi.OpenapiSchemaCommonFields.html +1 -1
- package/docs/types/openapi.OpenapiSchemaExpressionAllOf.html +1 -1
- package/docs/types/openapi.OpenapiSchemaExpressionAnyOf.html +1 -1
- package/docs/types/openapi.OpenapiSchemaExpressionOneOf.html +1 -1
- package/docs/types/openapi.OpenapiSchemaExpressionRef.html +2 -2
- package/docs/types/openapi.OpenapiSchemaExpressionRefSchemaShorthand.html +2 -2
- package/docs/types/openapi.OpenapiSchemaInteger.html +1 -1
- package/docs/types/openapi.OpenapiSchemaNull.html +2 -2
- package/docs/types/openapi.OpenapiSchemaNumber.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObject.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectAllOf.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectAllOfShorthand.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectBase.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectBaseShorthand.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectShorthand.html +1 -1
- package/docs/types/openapi.OpenapiSchemaPrimitiveGeneric.html +1 -1
- package/docs/types/openapi.OpenapiSchemaShorthandExpressionAllOf.html +1 -1
- package/docs/types/openapi.OpenapiSchemaShorthandExpressionAnyOf.html +1 -1
- package/docs/types/openapi.OpenapiSchemaShorthandExpressionOneOf.html +1 -1
- package/docs/types/openapi.OpenapiSchemaShorthandExpressionSerializableRef.html +2 -2
- package/docs/types/openapi.OpenapiSchemaShorthandExpressionSerializerRef.html +2 -2
- package/docs/types/openapi.OpenapiSchemaShorthandPrimitiveGeneric.html +1 -1
- package/docs/types/openapi.OpenapiSchemaString.html +1 -1
- package/docs/types/openapi.OpenapiShorthandAllTypes.html +1 -1
- package/docs/types/openapi.OpenapiShorthandPrimitiveBaseTypes.html +1 -1
- package/docs/types/openapi.OpenapiShorthandPrimitiveTypes.html +1 -1
- package/docs/types/openapi.OpenapiTypeField.html +1 -1
- package/docs/types/system.DreamAppAllowedPackageManagersEnum.html +1 -1
- package/docs/types/types.CalendarDateDurationUnit.html +1 -1
- package/docs/types/types.CalendarDateObject.html +1 -1
- package/docs/types/types.Camelized.html +1 -1
- package/docs/types/types.ClockTimeObject.html +1 -1
- package/docs/types/types.DbConnectionType.html +1 -1
- package/docs/types/types.DbTypes.html +1 -1
- package/docs/types/types.DreamAssociationMetadata.html +1 -1
- package/docs/types/types.DreamAttributes.html +1 -1
- package/docs/types/types.DreamClassAssociationAndStatement.html +1 -1
- package/docs/types/types.DreamClassColumn.html +1 -1
- package/docs/types/types.DreamColumn.html +1 -1
- package/docs/types/types.DreamColumnNames.html +1 -1
- package/docs/types/types.DreamLogLevel.html +1 -1
- package/docs/types/types.DreamLogger.html +2 -2
- package/docs/types/types.DreamModelSerializerType.html +1 -1
- package/docs/types/types.DreamOrViewModelClassSerializerKey.html +1 -1
- package/docs/types/types.DreamOrViewModelSerializerKey.html +1 -1
- package/docs/types/types.DreamParamSafeAttributes.html +1 -1
- package/docs/types/types.DreamParamSafeColumnNames.html +1 -1
- package/docs/types/types.DreamSerializable.html +1 -1
- package/docs/types/types.DreamSerializableArray.html +1 -1
- package/docs/types/types.DreamSerializerKey.html +1 -1
- package/docs/types/types.DreamSerializers.html +1 -1
- package/docs/types/types.DreamVirtualColumns.html +1 -1
- package/docs/types/types.DurationUnit.html +1 -1
- package/docs/types/types.EncryptAlgorithm.html +1 -1
- package/docs/types/types.HasManyStatement.html +1 -1
- package/docs/types/types.HasOneStatement.html +1 -1
- package/docs/types/types.Hyphenized.html +1 -1
- package/docs/types/types.IntrospectedValueType.html +5 -0
- package/docs/types/types.Pascalized.html +1 -1
- package/docs/types/types.PrimaryKeyType.html +1 -1
- package/docs/types/types.RoundingPrecision.html +1 -1
- package/docs/types/types.SerializerCasing.html +1 -1
- package/docs/types/types.SimpleObjectSerializerType.html +1 -1
- package/docs/types/types.Snakeified.html +1 -1
- package/docs/types/types.StrictInterface.html +1 -1
- package/docs/types/types.UpdateableAssociationProperties.html +1 -1
- package/docs/types/types.UpdateableProperties.html +1 -1
- package/docs/types/types.ValidationType.html +1 -1
- package/docs/types/types.ViewModel.html +2 -2
- package/docs/types/types.ViewModelClass.html +1 -1
- package/docs/types/types.WeekdayName.html +1 -1
- package/docs/types/types.WhereStatementForDream.html +1 -1
- package/docs/types/types.WhereStatementForDreamClass.html +1 -1
- package/docs/variables/index.DreamConst.html +1 -1
- package/docs/variables/index.ops.html +1 -1
- package/docs/variables/openapi.openapiPrimitiveTypes.html +1 -1
- package/docs/variables/openapi.openapiShorthandPrimitiveTypes.html +1 -1
- package/docs/variables/system.DreamAppAllowedPackageManagersEnumValues.html +1 -1
- package/docs/variables/system.primaryKeyTypes.html +1 -1
- package/package.json +6 -3
- package/dist/cjs/src/db/helpers/syncDbTypesFiles.js +0 -41
- package/dist/cjs/src/helpers/cli/ASTKyselyCodegenEnhancer.js +0 -380
- package/dist/esm/src/db/helpers/syncDbTypesFiles.js +0 -41
- package/dist/esm/src/helpers/cli/ASTKyselyCodegenEnhancer.js +0 -380
- package/dist/types/src/db/helpers/syncDbTypesFiles.d.ts +0 -1
- package/dist/types/src/helpers/cli/ASTKyselyCodegenEnhancer.d.ts +0 -94
|
@@ -1,4 +1,27 @@
|
|
|
1
1
|
import DreamSerializerBuilder from './builders/DreamSerializerBuilder.js';
|
|
2
|
+
/**
|
|
3
|
+
* Builds a serializer for a Dream model. A serializer is a function that takes the
|
|
4
|
+
* data to render and declares the keys to render it with:
|
|
5
|
+
*
|
|
6
|
+
* ```typescript
|
|
7
|
+
* export const UserSerializer = (user: User) =>
|
|
8
|
+
* DreamSerializer(User, user).attribute('email').rendersMany('posts')
|
|
9
|
+
* ```
|
|
10
|
+
*
|
|
11
|
+
* A serializer must not read its data while being built: what it declares may not
|
|
12
|
+
* depend on its data. Dream and Psychic build it without data to read its shape (the
|
|
13
|
+
* OpenAPI schema, and the null and other-variant keys of a flattened association or
|
|
14
|
+
* `customAttribute` that uses it), so a serializer that reads its data while being
|
|
15
|
+
* built throws when a flattened association or `customAttribute` that uses it is
|
|
16
|
+
* rendered. Read the data inside a `customAttribute` callback, which runs only when
|
|
17
|
+
* the serializer renders, not while it is built.
|
|
18
|
+
*
|
|
19
|
+
* @param dreamClass - The Dream model class the serializer renders
|
|
20
|
+
* @param data - The Dream instance to render
|
|
21
|
+
* @param passthroughData - Data available to this serializer and passed down to the serializers
|
|
22
|
+
* it renders
|
|
23
|
+
* @returns The serializer builder for method chaining
|
|
24
|
+
*/
|
|
2
25
|
export default function DreamSerializer(dreamClass, data, passthroughData) {
|
|
3
26
|
return new DreamSerializerBuilder(dreamClass, data, passthroughData);
|
|
4
27
|
}
|
|
@@ -1,4 +1,26 @@
|
|
|
1
1
|
import ObjectSerializerBuilder from './builders/ObjectSerializerBuilder.js';
|
|
2
|
+
/**
|
|
3
|
+
* Builds a serializer for a plain object or view model. A serializer is a function
|
|
4
|
+
* that takes the data to render and declares the keys to render it with:
|
|
5
|
+
*
|
|
6
|
+
* ```typescript
|
|
7
|
+
* export const AddressSerializer = (address: Address) =>
|
|
8
|
+
* ObjectSerializer(address).attribute('city', { openapi: 'string' })
|
|
9
|
+
* ```
|
|
10
|
+
*
|
|
11
|
+
* A serializer must not read its data while being built: what it declares may not
|
|
12
|
+
* depend on its data. Dream and Psychic build it without data to read its shape (the
|
|
13
|
+
* OpenAPI schema, and the null and other-variant keys of a flattened association or
|
|
14
|
+
* `customAttribute` that uses it), so a serializer that reads its data while being
|
|
15
|
+
* built throws when a flattened association or `customAttribute` that uses it is
|
|
16
|
+
* rendered. Read the data inside a `customAttribute` callback, which runs only when
|
|
17
|
+
* the serializer renders, not while it is built.
|
|
18
|
+
*
|
|
19
|
+
* @param data - The object to render
|
|
20
|
+
* @param passthroughData - Data available to this serializer and passed down to the serializers
|
|
21
|
+
* it renders
|
|
22
|
+
* @returns The serializer builder for method chaining
|
|
23
|
+
*/
|
|
2
24
|
export default function ObjectSerializer(data, passthroughData) {
|
|
3
25
|
return new ObjectSerializerBuilder(data, passthroughData);
|
|
4
26
|
}
|
|
@@ -9,6 +9,7 @@ import { DateTime } from '../utils/datetime/DateTime.js';
|
|
|
9
9
|
import DreamSerializerBuilder from './builders/DreamSerializerBuilder.js';
|
|
10
10
|
import ObjectSerializerBuilder from './builders/ObjectSerializerBuilder.js';
|
|
11
11
|
import inferSerializerFromDreamOrViewModel from './helpers/inferSerializerFromDreamOrViewModel.js';
|
|
12
|
+
import { nullFlattenedOpenapiKeys, nullFlattenedOpenapiKeysForValue, nullFlattenedSerializerKeys, nullFlattenedSerializerKeysOfOtherVariants, } from './helpers/nullFlattenedSerializerKeys.js';
|
|
12
13
|
import { serializersForAssociatedClass } from './helpers/serializersForAssociatedClass.js';
|
|
13
14
|
export default class SerializerRenderer {
|
|
14
15
|
serializerBuilder;
|
|
@@ -77,9 +78,30 @@ export default class SerializerRenderer {
|
|
|
77
78
|
if (value === undefined && attribute.options?.required === false)
|
|
78
79
|
return accumulator;
|
|
79
80
|
if (attribute.options.flatten) {
|
|
81
|
+
const flattenOptions = { casing: this.renderOpts.casing, passthroughData };
|
|
82
|
+
// a flattened customAttribute's keys are the parent's own keys, cased like them: a
|
|
83
|
+
// callback that returns null or undefined, or a serializer built over null or undefined
|
|
84
|
+
// data (which renders null), renders each key its openapi requires as null, as a null
|
|
85
|
+
// flattened rendersOne does, and one that returns a value of one variant of its openapi
|
|
86
|
+
// renders as null each key another variant requires that the rendered one does not
|
|
87
|
+
// declare, and each key the rendered one requires that a returned plain object leaves out
|
|
88
|
+
const valueIsSerializerBuilder = value instanceof DreamSerializerBuilder || value instanceof ObjectSerializerBuilder;
|
|
89
|
+
const valueRendersNull = value === null ||
|
|
90
|
+
value === undefined ||
|
|
91
|
+
(valueIsSerializerBuilder &&
|
|
92
|
+
(value['data'] ?? null) === null);
|
|
93
|
+
if (valueRendersNull)
|
|
94
|
+
return {
|
|
95
|
+
...accumulator,
|
|
96
|
+
...nullFlattenedOpenapiKeys(attribute.options.openapi, flattenOptions),
|
|
97
|
+
};
|
|
98
|
+
const renderedValue = applyRenderingOptionsToAttribute(null, value, attribute.name, {}, this.passthroughData, this.renderOpts);
|
|
80
99
|
return {
|
|
81
100
|
...accumulator,
|
|
82
|
-
...
|
|
101
|
+
...nullFlattenedOpenapiKeysForValue(attribute.options.openapi, value, flattenOptions),
|
|
102
|
+
// a returned serializer renders its keys in the render's casing; a returned plain
|
|
103
|
+
// object's top-level keys are cased here, its nested values left as returned
|
|
104
|
+
...(valueIsSerializerBuilder ? renderedValue : this.setCaseOfKeys(renderedValue)),
|
|
83
105
|
};
|
|
84
106
|
}
|
|
85
107
|
else {
|
|
@@ -99,7 +121,7 @@ export default class SerializerRenderer {
|
|
|
99
121
|
if (!associatedObject && attribute.options.flatten) {
|
|
100
122
|
return {
|
|
101
123
|
...accumulator,
|
|
102
|
-
...
|
|
124
|
+
...nullFlattenedSerializerKeys(serializersForAssociatedClass(data instanceof Dream ? data.constructor : null, attribute.name, attribute.options), { casing: this.renderOpts.casing, passthroughData }),
|
|
103
125
|
};
|
|
104
126
|
}
|
|
105
127
|
const serializer = associatedObject
|
|
@@ -112,6 +134,12 @@ export default class SerializerRenderer {
|
|
|
112
134
|
if (attribute.options.flatten) {
|
|
113
135
|
return {
|
|
114
136
|
...accumulator,
|
|
137
|
+
// every payload carries the keys of every serializer the association may render with
|
|
138
|
+
// (each STI child, each polymorphic target), so each key another one requires that the
|
|
139
|
+
// rendered one does not declare renders as null
|
|
140
|
+
...(serializer
|
|
141
|
+
? nullFlattenedSerializerKeysOfOtherVariants(serializersForAssociatedClass(data instanceof Dream ? data.constructor : null, attribute.name, attribute.options), serializer, { casing: this.renderOpts.casing, passthroughData })
|
|
142
|
+
: {}),
|
|
115
143
|
// passthrough data must be passed both into the serializer and render
|
|
116
144
|
// because, if the serializer does accept passthrough data, then passing it in is how
|
|
117
145
|
// it gets into the serializer, but if it does not accept passthrough data, and therefore
|
|
@@ -176,58 +204,12 @@ export default class SerializerRenderer {
|
|
|
176
204
|
}, renderedAttributes);
|
|
177
205
|
return renderedAttributes;
|
|
178
206
|
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
* Each serializer is built with `undefined` data to read its declarations, as Psychic's OpenAPI
|
|
186
|
-
* generation and `preloadFor` build it, and nothing is rendered: no attribute callback runs and
|
|
187
|
-
* no association of the missing object is read. A flattened `rendersOne` it declares adds its
|
|
188
|
-
* own serializers' keys the same way. A flattened `customAttribute` adds none, since only its
|
|
189
|
-
* callback knows its keys.
|
|
190
|
-
*/
|
|
191
|
-
nullFlattenedAttributes(serializers, passthroughData, serializersBeingFlattened = new Set()) {
|
|
192
|
-
return serializers.reduce((flattenedKeys, serializer) => {
|
|
193
|
-
// a serializer reached again, e.g. one that flattens itself directly or through others, or
|
|
194
|
-
// one that two polymorphic targets or two STI children both flatten, adds no keys the first
|
|
195
|
-
// walk through it did not
|
|
196
|
-
if (serializersBeingFlattened.has(serializer))
|
|
197
|
-
return flattenedKeys;
|
|
198
|
-
serializersBeingFlattened.add(serializer);
|
|
199
|
-
const serializerBuilder = serializer(undefined, passthroughData);
|
|
200
|
-
const dreamClass = serializerBuilder instanceof DreamSerializerBuilder
|
|
201
|
-
? serializerBuilder['$typeForOpenapi']
|
|
202
|
-
: null;
|
|
203
|
-
return serializerBuilder['attributes'].reduce((keys, attribute) => {
|
|
204
|
-
const attributeType = attribute.type;
|
|
205
|
-
switch (attributeType) {
|
|
206
|
-
case 'attribute':
|
|
207
|
-
case 'delegatedAttribute':
|
|
208
|
-
case 'rendersMany':
|
|
209
|
-
keys[this.setCase(attribute.options?.as ?? attribute.name)] = null;
|
|
210
|
-
return keys;
|
|
211
|
-
case 'customAttribute':
|
|
212
|
-
if (!attribute.options.flatten)
|
|
213
|
-
keys[this.setCase(attribute.name)] = null;
|
|
214
|
-
return keys;
|
|
215
|
-
case 'rendersOne':
|
|
216
|
-
if (!attribute.options.flatten) {
|
|
217
|
-
keys[this.setCase(attribute.options.as ?? attribute.name)] = null;
|
|
218
|
-
return keys;
|
|
219
|
-
}
|
|
220
|
-
return {
|
|
221
|
-
...keys,
|
|
222
|
-
...this.nullFlattenedAttributes(serializersForAssociatedClass(dreamClass, attribute.name, attribute.options), passthroughData, serializersBeingFlattened),
|
|
223
|
-
};
|
|
224
|
-
default: {
|
|
225
|
-
// protection so that if a new ValidationType is ever added, this will throw a type error at build time
|
|
226
|
-
const _never = attributeType;
|
|
227
|
-
throw new Error(`Unhandled serializer attribute type: ${_never}`);
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
}, flattenedKeys);
|
|
207
|
+
setCaseOfKeys(value) {
|
|
208
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value))
|
|
209
|
+
return value;
|
|
210
|
+
return Object.keys(value).reduce((casedValue, key) => {
|
|
211
|
+
casedValue[this.setCase(key)] = value[key];
|
|
212
|
+
return casedValue;
|
|
231
213
|
}, {});
|
|
232
214
|
}
|
|
233
215
|
setCase(attr) {
|
|
@@ -115,8 +115,21 @@ export default class DreamSerializerBuilder {
|
|
|
115
115
|
* property bodies (`type: 'object'`, `properties`, and `required` listing the keys that
|
|
116
116
|
* are always present), or `{ $serializer: SomeSerializer }` when the callback returns
|
|
117
117
|
* what that serializer renders. A serializer ref with `many: true` does not type-check
|
|
118
|
-
* here, since a list has no properties to spread. A `null`
|
|
119
|
-
*
|
|
118
|
+
* here, since a list has no properties to spread. A `null` return, a serializer built over
|
|
119
|
+
* `null` or `undefined` data, or an `undefined` return without `required: false` renders as
|
|
120
|
+
* `null` each key the `openapi` requires: a key an object shape lists in its `required`
|
|
121
|
+
* array, a key a `$serializer` or `$serializable` ref's serializer declares without
|
|
122
|
+
* `required: false`, and each such key of an `allOf`, `anyOf` or `oneOf` member. Its
|
|
123
|
+
* optional keys stay absent, and an `undefined` return with `required: false` adds no keys.
|
|
124
|
+
* A returned plain object also renders as `null` each key its shape requires that it
|
|
125
|
+
* leaves out, and its optional keys it leaves out stay absent. When the `openapi` is an
|
|
126
|
+
* `anyOf` or `oneOf`, or a `$serializable` ref to an STI base, a present value also renders
|
|
127
|
+
* as `null` each key another variant requires that the rendered one does not declare, so a
|
|
128
|
+
* client sees the same keys whichever variant is rendered.
|
|
129
|
+
* Every key it adds follows the render's casing like the parent's own keys: a returned
|
|
130
|
+
* plain object's top-level keys and the `openapi`'s `properties` keys are cased (so
|
|
131
|
+
* `casing: 'snake'` renders `phoneNumber` as `phone_number`, present or `null`), while
|
|
132
|
+
* nested values render as returned. Attributes render in declaration order, so a key the returned object shares with
|
|
120
133
|
* another attribute takes the value of whichever is declared later; under the `allOf`,
|
|
121
134
|
* that value must match both schemas
|
|
122
135
|
* - `required` - Set to `false` to mark the attribute as optional in the OpenAPI schema;
|
|
@@ -168,14 +181,23 @@ export default class DreamSerializerBuilder {
|
|
|
168
181
|
* declaration order, so when the parent and the association share a key (e.g., `id`),
|
|
169
182
|
* whichever is declared later wins: the association's value replaces an attribute declared
|
|
170
183
|
* before the `rendersOne`, and an attribute declared after it replaces the association's.
|
|
171
|
-
* When the association is `null`,
|
|
172
|
-
*
|
|
173
|
-
*
|
|
174
|
-
*
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
*
|
|
178
|
-
*
|
|
184
|
+
* When the association is `null`, each key its serializer requires renders as `null`: an
|
|
185
|
+
* attribute, delegatedAttribute or customAttribute declared without `required: false`, and
|
|
186
|
+
* every non-flattened `rendersOne` and `rendersMany` (for a polymorphic `BelongsTo`, each
|
|
187
|
+
* key the serializer of any of its target classes requires, and for an association to an
|
|
188
|
+
* STI base, each key the serializer of any of its STI children requires), in the same order
|
|
189
|
+
* (so an `id` declared before the `rendersOne` becomes `null`), while a key declared with
|
|
190
|
+
* `required: false` stays absent. A flattened `customAttribute` in that serializer adds the
|
|
191
|
+
* keys its `openapi` requires: a key an object shape lists in its `required` array, a key a
|
|
192
|
+
* `$serializer` or `$serializable` ref's serializer declares without `required: false`, and
|
|
193
|
+
* each such key of an `allOf`, `anyOf` or `oneOf` member; a ref with `many: true` adds none,
|
|
194
|
+
* and a flattened `customAttribute` declared with `required: false` adds none. Nothing in
|
|
195
|
+
* that serializer is rendered, so its custom attribute callbacks do not run. When the
|
|
196
|
+
* association to an STI base or polymorphic `BelongsTo` is present, each key another STI
|
|
197
|
+
* child's or target's serializer requires that the held one's serializer does not declare
|
|
198
|
+
* also renders as `null`, so a client sees the same keys whichever is held. Psychic adds
|
|
199
|
+
* the association's OpenAPI schema to the parent's as an `allOf` branch, so a shared key's
|
|
200
|
+
* value must match both schemas, whichever declaration wins
|
|
179
201
|
* - `optional` - When `true`, allows the association to be null/missing without causing
|
|
180
202
|
* an `OpenapiResponseValidationFailure` during Psychic controller unit specs. By default,
|
|
181
203
|
* `rendersOne` expects the association to be present (mirroring the `optional` option on
|
|
@@ -151,8 +151,21 @@ export default class ObjectSerializerBuilder {
|
|
|
151
151
|
* property bodies (`type: 'object'`, `properties`, and `required` listing the keys that
|
|
152
152
|
* are always present), or `{ $serializer: SomeSerializer }` when the callback returns
|
|
153
153
|
* what that serializer renders. A serializer ref with `many: true` does not type-check
|
|
154
|
-
* here, since a list has no properties to spread. A `null`
|
|
155
|
-
*
|
|
154
|
+
* here, since a list has no properties to spread. A `null` return, a serializer built over
|
|
155
|
+
* `null` or `undefined` data, or an `undefined` return without `required: false` renders as
|
|
156
|
+
* `null` each key the `openapi` requires: a key an object shape lists in its `required`
|
|
157
|
+
* array, a key a `$serializer` or `$serializable` ref's serializer declares without
|
|
158
|
+
* `required: false`, and each such key of an `allOf`, `anyOf` or `oneOf` member. Its
|
|
159
|
+
* optional keys stay absent, and an `undefined` return with `required: false` adds no keys.
|
|
160
|
+
* A returned plain object also renders as `null` each key its shape requires that it
|
|
161
|
+
* leaves out, and its optional keys it leaves out stay absent. When the `openapi` is an
|
|
162
|
+
* `anyOf` or `oneOf`, or a `$serializable` ref to an STI base, a present value also renders
|
|
163
|
+
* as `null` each key another variant requires that the rendered one does not declare, so a
|
|
164
|
+
* client sees the same keys whichever variant is rendered.
|
|
165
|
+
* Every key it adds follows the render's casing like the parent's own keys: a returned
|
|
166
|
+
* plain object's top-level keys and the `openapi`'s `properties` keys are cased (so
|
|
167
|
+
* `casing: 'snake'` renders `phoneNumber` as `phone_number`, present or `null`), while
|
|
168
|
+
* nested values render as returned. Attributes render in declaration order, so a key the returned object shares with
|
|
156
169
|
* another attribute takes the value of whichever is declared later; under the `allOf`,
|
|
157
170
|
* that value must match both schemas
|
|
158
171
|
* - `required` - Set to `false` to mark the attribute as optional in the OpenAPI schema;
|
|
@@ -204,14 +217,23 @@ export default class ObjectSerializerBuilder {
|
|
|
204
217
|
* declaration order, so when the parent and the association share a key (e.g., `id`),
|
|
205
218
|
* whichever is declared later wins: the association's value replaces an attribute declared
|
|
206
219
|
* before the `rendersOne`, and an attribute declared after it replaces the association's.
|
|
207
|
-
* When the association is `null`,
|
|
208
|
-
*
|
|
209
|
-
*
|
|
210
|
-
*
|
|
211
|
-
*
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
*
|
|
220
|
+
* When the association is `null`, each key its serializer requires renders as `null`: an
|
|
221
|
+
* attribute, delegatedAttribute or customAttribute declared without `required: false`, and
|
|
222
|
+
* every non-flattened `rendersOne` and `rendersMany` (for a polymorphic `BelongsTo`, each
|
|
223
|
+
* key the serializer of any of its target classes requires, and for an association to an
|
|
224
|
+
* STI base, each key the serializer of any of its STI children requires), in the same order
|
|
225
|
+
* (so an `id` declared before the `rendersOne` becomes `null`), while a key declared with
|
|
226
|
+
* `required: false` stays absent. A flattened `customAttribute` in that serializer adds the
|
|
227
|
+
* keys its `openapi` requires: a key an object shape lists in its `required` array, a key a
|
|
228
|
+
* `$serializer` or `$serializable` ref's serializer declares without `required: false`, and
|
|
229
|
+
* each such key of an `allOf`, `anyOf` or `oneOf` member; a ref with `many: true` adds none,
|
|
230
|
+
* and a flattened `customAttribute` declared with `required: false` adds none. Nothing in
|
|
231
|
+
* that serializer is rendered, so its custom attribute callbacks do not run. When the
|
|
232
|
+
* association to an STI base or polymorphic `BelongsTo` is present, each key another STI
|
|
233
|
+
* child's or target's serializer requires that the held one's serializer does not declare
|
|
234
|
+
* also renders as `null`, so a client sees the same keys whichever is held. Psychic adds
|
|
235
|
+
* the association's OpenAPI schema to the parent's as an `allOf` branch, so a shared key's
|
|
236
|
+
* value must match both schemas, whichever declaration wins
|
|
215
237
|
* - `optional` - When `true`, allows the association to be null/missing without causing
|
|
216
238
|
* an `OpenapiResponseValidationFailure` during Psychic controller unit specs. By default,
|
|
217
239
|
* `rendersOne` expects the association to be present (mirroring the `optional` option on
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import snakeify from '../../helpers/snakeify.js';
|
|
2
|
+
import DreamSerializerBuilder from '../builders/DreamSerializerBuilder.js';
|
|
3
|
+
import ObjectSerializerBuilder from '../builders/ObjectSerializerBuilder.js';
|
|
4
|
+
import { inferSerializersFromDreamClassOrViewModelClass } from './inferSerializerFromDreamOrViewModel.js';
|
|
5
|
+
import { serializersForAssociatedClass } from './serializersForAssociatedClass.js';
|
|
6
|
+
/**
|
|
7
|
+
* The keys a flattened `rendersOne` adds to the parent's payload when its associated object is
|
|
8
|
+
* null, returned as an object with each key set to `null`, ready to spread into the parent's
|
|
9
|
+
* payload (`Object.keys` of it for the key names alone): each key one of `serializers` requires.
|
|
10
|
+
* As a property read on an empty object is `undefined`, a null value carries no key it may omit:
|
|
11
|
+
* an attribute, delegatedAttribute or customAttribute declared with `required: false` adds no key.
|
|
12
|
+
* Every key is cased per `casing`, as the renderer cases it. There are several serializers when
|
|
13
|
+
* the association is a polymorphic BelongsTo, one for each target class, or when the associated
|
|
14
|
+
* class is an STI base, one for each child, and their required keys are unioned.
|
|
15
|
+
*
|
|
16
|
+
* Each serializer is built with `undefined` data to read its declarations, as Psychic's OpenAPI
|
|
17
|
+
* generation and `preloadFor` build it, and nothing is rendered: no attribute callback runs and
|
|
18
|
+
* no association of the missing object is read. A non-flattened `rendersOne` or a `rendersMany`
|
|
19
|
+
* always adds its key. A flattened `rendersOne` it declares adds its own serializers' required keys
|
|
20
|
+
* the same way. A flattened `customAttribute` adds the keys its `openapi` requires: each key of
|
|
21
|
+
* its `properties` that the shape's `required` array lists, the required keys of the serializer a
|
|
22
|
+
* `$serializer` ref names or of every serializer a `$serializable` ref resolves to (one per child
|
|
23
|
+
* for an STI base), walked the same way, and the union of the keys every member of an `allOf`,
|
|
24
|
+
* `anyOf` or `oneOf` requires, with each key an object shape's own `required` array lists beside
|
|
25
|
+
* its members; one declared with `required: false` adds none. A ref with `many: true` adds none,
|
|
26
|
+
* since a list has no keys to spread. A serializer reached again within its own walk, as when one
|
|
27
|
+
* flattens itself, adds nothing, so the walk always terminates; one reached again along another
|
|
28
|
+
* path is walked again, so each path adds its keys and their requirements.
|
|
29
|
+
*
|
|
30
|
+
* `passthroughData` is handed to each serializer as it is built, as when rendering; outside a
|
|
31
|
+
* render it may be left out.
|
|
32
|
+
*/
|
|
33
|
+
export function nullFlattenedSerializerKeys(serializers, options = {}) {
|
|
34
|
+
return nullRequiredKeys(walkSerializers(serializers, newWalk(options)));
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* The keys a flattened `customAttribute` with this `openapi` adds to the parent's payload when
|
|
38
|
+
* its callback returns null (or `undefined` without `required: false`), or a serializer built over
|
|
39
|
+
* null data, each set to `null`: each key `openapi` requires, walked as `nullFlattenedSerializerKeys`
|
|
40
|
+
* walks a flattened `customAttribute` and cased per `casing`.
|
|
41
|
+
*/
|
|
42
|
+
export function nullFlattenedOpenapiKeys(openapi, options = {}) {
|
|
43
|
+
return nullRequiredKeys(walkOpenapi(openapi, newWalk(options)));
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The keys a present flattened `rendersOne` adds to the parent's payload, each set to `null`,
|
|
47
|
+
* beside what `renderedSerializer` renders: each key one of `serializers` (every serializer the
|
|
48
|
+
* association may render with) requires that `renderedSerializer` does not declare at all. A key
|
|
49
|
+
* `renderedSerializer` declares is its own to render or, when `required: false`, to omit.
|
|
50
|
+
*/
|
|
51
|
+
export function nullFlattenedSerializerKeysOfOtherVariants(serializers, renderedSerializer, options = {}) {
|
|
52
|
+
return nullKeysRequiredByOthers(walkSerializers(serializers, newWalk(options)), Object.keys(walkSerializers([renderedSerializer], newWalk(options))));
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* The keys a flattened `customAttribute` with this `openapi` adds to the parent's payload, each
|
|
56
|
+
* set to `null`, when it renders `renderedVariant`: each key a variant of `openapi` requires that
|
|
57
|
+
* `renderedVariant` does not declare. `renderedVariant` is any `openapi` shorthand: one member of
|
|
58
|
+
* an `anyOf` or `oneOf`, or `{ $serializer: childSerializer }` for one child of a `$serializable`
|
|
59
|
+
* ref to an STI base. A key requires its value when an object shape's `required` array lists it,
|
|
60
|
+
* whether the shape's own `properties` declare it or a member of its `allOf`, `anyOf` or `oneOf`
|
|
61
|
+
* does, or when a serializer declares it without `required: false`.
|
|
62
|
+
*/
|
|
63
|
+
export function nullFlattenedOpenapiKeysOfOtherVariants(openapi, renderedVariant, options = {}) {
|
|
64
|
+
return nullKeysRequiredByOthers(walkOpenapi(openapi, newWalk(options)), Object.keys(walkOpenapi(renderedVariant, newWalk(options))));
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* The keys a flattened `customAttribute` with this `openapi` adds to the parent's payload, each
|
|
68
|
+
* set to `null`, beside the non-null `value` its callback returned, from which the rendered
|
|
69
|
+
* variant is read. A serializer's declarations are the rendered variant, so a `$serializable` ref
|
|
70
|
+
* to an STI base adds the keys of every other child, as `nullFlattenedOpenapiKeysOfOtherVariants`
|
|
71
|
+
* reads them; the serializer renders each of its own keys. A plain object renders only the keys
|
|
72
|
+
* it has, so it adds each key the rendered variant requires that the object leaves out as well as
|
|
73
|
+
* each key another variant requires that the rendered one does not declare. An `openapi` with one
|
|
74
|
+
* variant is the rendered one. Of several variants (the members of an `anyOf` or `oneOf`, or one
|
|
75
|
+
* `$serializer` per child of a `$serializable` ref to an STI base, a member with several variants
|
|
76
|
+
* of its own contributing each of them, and an `allOf` beside an `anyOf` or `oneOf` joining each
|
|
77
|
+
* of its variants), the rendered one is the variant that declares every key the object has; when
|
|
78
|
+
* several do, the first of them whose required keys the object all has, and when none is found,
|
|
79
|
+
* the object adds each key any variant requires. The object's top-level keys render cased per
|
|
80
|
+
* `casing`, so they are compared cased, and a key it has is never added.
|
|
81
|
+
*/
|
|
82
|
+
export function nullFlattenedOpenapiKeysForValue(openapi, value, options = {}) {
|
|
83
|
+
if (value instanceof DreamSerializerBuilder || value instanceof ObjectSerializerBuilder)
|
|
84
|
+
return nullKeysRequiredByOthers(walkOpenapi(openapi, newWalk(options)), Object.keys(walkSerializerBuilder(value, newWalk(options))));
|
|
85
|
+
if (typeof value !== 'object' || value === null)
|
|
86
|
+
return {};
|
|
87
|
+
const { casing } = newWalk(options);
|
|
88
|
+
const valueKeys = Object.keys(value).map(key => setCase(key, casing));
|
|
89
|
+
const variants = openapiVariants(openapi);
|
|
90
|
+
const candidates = variants
|
|
91
|
+
.map(variant => ({ variant, requirements: walkOpenapi(variant, newWalk(options)) }))
|
|
92
|
+
.filter(({ requirements }) => valueKeys.every(key => Object.prototype.hasOwnProperty.call(requirements, key)));
|
|
93
|
+
const renderedVariant = variants.length < 2
|
|
94
|
+
? openapi
|
|
95
|
+
: candidates.length === 1
|
|
96
|
+
? candidates[0].variant
|
|
97
|
+
: candidates.find(({ requirements }) => Object.keys(requirements).every(key => !requirements[key] || valueKeys.includes(key)))?.variant;
|
|
98
|
+
const requiredKeys = renderedVariant
|
|
99
|
+
? {
|
|
100
|
+
...nullFlattenedOpenapiKeysOfOtherVariants(openapi, renderedVariant, options),
|
|
101
|
+
...nullFlattenedOpenapiKeys(renderedVariant, options),
|
|
102
|
+
}
|
|
103
|
+
: nullFlattenedOpenapiKeys(openapi, options);
|
|
104
|
+
valueKeys.forEach(key => delete requiredKeys[key]);
|
|
105
|
+
return requiredKeys;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* The shapes a value of `openapi` may take, each one a variant a plain object may render: a
|
|
109
|
+
* `$serializer` ref to each serializer a `$serializable` ref resolves to, or the members of an
|
|
110
|
+
* `anyOf` or `oneOf`, each member that itself has several variants (a nested `anyOf` or `oneOf`,
|
|
111
|
+
* or a `$serializable` ref to an STI base) contributing those variants in its place. An `allOf`
|
|
112
|
+
* beside the `anyOf` or `oneOf` describes every value, so each variant is that `allOf` with the
|
|
113
|
+
* variant added to its members.
|
|
114
|
+
*/
|
|
115
|
+
function openapiVariants(openapi) {
|
|
116
|
+
if (typeof openapi !== 'object' || Array.isArray(openapi))
|
|
117
|
+
return [];
|
|
118
|
+
if ('$serializable' in openapi)
|
|
119
|
+
return openapi.many
|
|
120
|
+
? []
|
|
121
|
+
: inferSerializersFromDreamClassOrViewModelClass(openapi.$serializable, openapi.$serializableSerializerKey ?? openapi.key).map(serializer => ({ $serializer: serializer }));
|
|
122
|
+
const variants = [
|
|
123
|
+
...(('anyOf' in openapi && openapi.anyOf) || []),
|
|
124
|
+
...(('oneOf' in openapi && openapi.oneOf) || []),
|
|
125
|
+
].flatMap(member => {
|
|
126
|
+
const memberVariants = openapiVariants(member);
|
|
127
|
+
return memberVariants.length > 1 ? memberVariants : [member];
|
|
128
|
+
});
|
|
129
|
+
const sharedMembers = ('allOf' in openapi && openapi.allOf) || [];
|
|
130
|
+
return sharedMembers.length ? variants.map(variant => ({ allOf: [...sharedMembers, variant] })) : variants;
|
|
131
|
+
}
|
|
132
|
+
function newWalk({ casing = 'camel', passthroughData = {}, }) {
|
|
133
|
+
return { casing, passthroughData, serializersBeingFlattened: new Set() };
|
|
134
|
+
}
|
|
135
|
+
function nullRequiredKeys(requirements) {
|
|
136
|
+
return Object.keys(requirements).reduce((keys, key) => {
|
|
137
|
+
if (requirements[key])
|
|
138
|
+
keys[key] = null;
|
|
139
|
+
return keys;
|
|
140
|
+
}, {});
|
|
141
|
+
}
|
|
142
|
+
function nullKeysRequiredByOthers(requirements, renderedKeys) {
|
|
143
|
+
return Object.keys(requirements).reduce((keys, key) => {
|
|
144
|
+
if (requirements[key] && !renderedKeys.includes(key))
|
|
145
|
+
keys[key] = null;
|
|
146
|
+
return keys;
|
|
147
|
+
}, {});
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* A key is required when any of its declarations requires it.
|
|
151
|
+
*/
|
|
152
|
+
function mergeRequirements(requirements, other) {
|
|
153
|
+
Object.keys(other).forEach(key => {
|
|
154
|
+
requirements[key] = (requirements[key] ?? false) || other[key];
|
|
155
|
+
});
|
|
156
|
+
return requirements;
|
|
157
|
+
}
|
|
158
|
+
function walkSerializers(serializers, walk) {
|
|
159
|
+
return serializers.reduce((flattenedKeys, serializer) => {
|
|
160
|
+
// a serializer that flattens itself, directly or through others, adds no keys its walk in
|
|
161
|
+
// progress does not; one reached again along another path is walked again, since a path that
|
|
162
|
+
// requires its keys must not lose them to an earlier path that left them optional
|
|
163
|
+
if (walk.serializersBeingFlattened.has(serializer))
|
|
164
|
+
return flattenedKeys;
|
|
165
|
+
walk.serializersBeingFlattened.add(serializer);
|
|
166
|
+
const serializerBuilder = serializer(undefined, walk.passthroughData);
|
|
167
|
+
const serializerKeys = walkSerializerBuilder(serializerBuilder, walk);
|
|
168
|
+
walk.serializersBeingFlattened.delete(serializer);
|
|
169
|
+
return mergeRequirements(flattenedKeys, serializerKeys);
|
|
170
|
+
}, {});
|
|
171
|
+
}
|
|
172
|
+
function walkSerializerBuilder(serializerBuilder, walk) {
|
|
173
|
+
const dreamClass = serializerBuilder instanceof DreamSerializerBuilder
|
|
174
|
+
? serializerBuilder['$typeForOpenapi']
|
|
175
|
+
: null;
|
|
176
|
+
return serializerBuilder['attributes'].reduce((keys, attribute) => {
|
|
177
|
+
const attributeType = attribute.type;
|
|
178
|
+
switch (attributeType) {
|
|
179
|
+
case 'attribute':
|
|
180
|
+
case 'delegatedAttribute':
|
|
181
|
+
return mergeRequirements(keys, {
|
|
182
|
+
[setCase(attribute.options?.as ?? attribute.name, walk.casing)]: attribute.options?.required !== false,
|
|
183
|
+
});
|
|
184
|
+
case 'rendersMany':
|
|
185
|
+
return mergeRequirements(keys, {
|
|
186
|
+
[setCase(attribute.options?.as ?? attribute.name, walk.casing)]: true,
|
|
187
|
+
});
|
|
188
|
+
case 'customAttribute': {
|
|
189
|
+
if (!attribute.options.flatten)
|
|
190
|
+
return mergeRequirements(keys, {
|
|
191
|
+
[setCase(attribute.name, walk.casing)]: attribute.options.required !== false,
|
|
192
|
+
});
|
|
193
|
+
const flattenedKeys = walkOpenapi(attribute.options.openapi, walk);
|
|
194
|
+
// a callback that may return undefined with `required: false` adds none of its keys
|
|
195
|
+
if (attribute.options.required === false)
|
|
196
|
+
Object.keys(flattenedKeys).forEach(key => (flattenedKeys[key] = false));
|
|
197
|
+
return mergeRequirements(keys, flattenedKeys);
|
|
198
|
+
}
|
|
199
|
+
case 'rendersOne':
|
|
200
|
+
if (!attribute.options.flatten)
|
|
201
|
+
return mergeRequirements(keys, {
|
|
202
|
+
[setCase(attribute.options.as ?? attribute.name, walk.casing)]: true,
|
|
203
|
+
});
|
|
204
|
+
return mergeRequirements(keys, walkSerializers(serializersForAssociatedClass(dreamClass, attribute.name, attribute.options), walk));
|
|
205
|
+
default: {
|
|
206
|
+
// protection so that if a new ValidationType is ever added, this will throw a type error at build time
|
|
207
|
+
const _never = attributeType;
|
|
208
|
+
throw new Error(`Unhandled serializer attribute type: ${_never}`);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}, {});
|
|
212
|
+
}
|
|
213
|
+
function walkOpenapi(openapi, walk) {
|
|
214
|
+
if (typeof openapi !== 'object' || Array.isArray(openapi))
|
|
215
|
+
return {};
|
|
216
|
+
if ('$serializer' in openapi) {
|
|
217
|
+
if (openapi.many)
|
|
218
|
+
return {};
|
|
219
|
+
return walkSerializers([openapi.$serializer], walk);
|
|
220
|
+
}
|
|
221
|
+
if ('$serializable' in openapi) {
|
|
222
|
+
if (openapi.many)
|
|
223
|
+
return {};
|
|
224
|
+
return walkSerializers(inferSerializersFromDreamClassOrViewModelClass(openapi.$serializable, openapi.$serializableSerializerKey ?? openapi.key), walk);
|
|
225
|
+
}
|
|
226
|
+
const keys = Object.keys(('properties' in openapi && openapi.properties) || {}).reduce((propertyKeys, key) => {
|
|
227
|
+
propertyKeys[setCase(key, walk.casing)] = false;
|
|
228
|
+
return propertyKeys;
|
|
229
|
+
}, {});
|
|
230
|
+
const members = [
|
|
231
|
+
...(('allOf' in openapi && openapi.allOf) || []),
|
|
232
|
+
...(('anyOf' in openapi && openapi.anyOf) || []),
|
|
233
|
+
...(('oneOf' in openapi && openapi.oneOf) || []),
|
|
234
|
+
];
|
|
235
|
+
const shapeKeys = members.reduce((keys, member) => mergeRequirements(keys, walkOpenapi(member, walk)), keys);
|
|
236
|
+
// the shape's own `required` array requires a key its `properties` declare or a member declares
|
|
237
|
+
const requiredKeys = ('required' in openapi && openapi.required) || [];
|
|
238
|
+
requiredKeys.forEach(key => {
|
|
239
|
+
const casedKey = setCase(key, walk.casing);
|
|
240
|
+
if (Object.prototype.hasOwnProperty.call(shapeKeys, casedKey))
|
|
241
|
+
shapeKeys[casedKey] = true;
|
|
242
|
+
});
|
|
243
|
+
return shapeKeys;
|
|
244
|
+
}
|
|
245
|
+
function setCase(attr, casing) {
|
|
246
|
+
switch (casing) {
|
|
247
|
+
case 'camel':
|
|
248
|
+
return attr;
|
|
249
|
+
case 'snake':
|
|
250
|
+
return snakeify(attr);
|
|
251
|
+
default: {
|
|
252
|
+
// protection so that if a new Casing is ever added, this will throw a type error at build time
|
|
253
|
+
const _never = casing;
|
|
254
|
+
throw new Error(`Unhandled Casing: ${_never}`);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
@@ -1,11 +1,3 @@
|
|
|
1
|
-
export default function dataTypes() {
|
|
2
|
-
// In the future, when we support multiple db drivers,
|
|
3
|
-
// this will need to be updated
|
|
4
|
-
return postgresDatatypes;
|
|
5
|
-
}
|
|
6
|
-
export function isPrimitiveDataType(type) {
|
|
7
|
-
return dataTypes().includes(type.replace(/\[\]$/, ''));
|
|
8
|
-
}
|
|
9
1
|
export const postgresDatatypes = [
|
|
10
2
|
'bigint',
|
|
11
3
|
'bigserial',
|
|
@@ -91,14 +91,12 @@ export default class QueryDriverBase {
|
|
|
91
91
|
* comlpex to override. You will need to do the following
|
|
92
92
|
* when overriding this method:
|
|
93
93
|
*
|
|
94
|
-
* 1.
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
* to achieve.
|
|
94
|
+
* 1. generate a types/db.ts file in the same shape as the existing one.
|
|
95
|
+
* KyselyQueryDriver does this with the ASTDbTypesBuilder, which writes
|
|
96
|
+
* it from {@link introspectDatabase}.
|
|
98
97
|
* 2. generate a types/dream.ts file in the same shape as the existing
|
|
99
|
-
* one.
|
|
100
|
-
*
|
|
101
|
-
* database engine.
|
|
98
|
+
* one. KyselyQueryDriver does this with the ASTSchemaBuilder, which
|
|
99
|
+
* also reads the connection through {@link introspectDatabase}.
|
|
102
100
|
*/
|
|
103
101
|
// eslint-disable-next-line @typescript-eslint/require-await
|
|
104
102
|
static async sync(connectionName, _,
|
|
@@ -292,15 +290,20 @@ export default class QueryDriverBase {
|
|
|
292
290
|
keys) {
|
|
293
291
|
throw new Error('override acquireAdvisoryTransactionLocks in child class');
|
|
294
292
|
}
|
|
293
|
+
/**
|
|
294
|
+
* Reads the tables, columns and enums of the connection's database, from
|
|
295
|
+
* which `sync` writes the connection's `types/db.ts` and `types/dream.ts`.
|
|
296
|
+
* It returns every table the connection can read, sorted by schema and
|
|
297
|
+
* name; `sync` applies `tableIncludePattern` and `tableExcludePattern`
|
|
298
|
+
* itself.
|
|
299
|
+
*
|
|
300
|
+
* @param connectionName - the connection to read
|
|
301
|
+
*/
|
|
295
302
|
// eslint-disable-next-line @typescript-eslint/require-await
|
|
296
|
-
static async
|
|
297
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
298
|
-
connectionName,
|
|
303
|
+
static async introspectDatabase(
|
|
299
304
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
allTableAssociationData) {
|
|
303
|
-
throw new Error('implement getColumnData in child class');
|
|
305
|
+
connectionName) {
|
|
306
|
+
throw new Error('implement introspectDatabase in child class');
|
|
304
307
|
}
|
|
305
308
|
/**
|
|
306
309
|
* @internal
|
|
@@ -695,16 +698,6 @@ export default class QueryDriverBase {
|
|
|
695
698
|
reallyDestroy) {
|
|
696
699
|
throw new Error('implement destroyDream in child class');
|
|
697
700
|
}
|
|
698
|
-
static get syncDialect() {
|
|
699
|
-
return 'postgres';
|
|
700
|
-
}
|
|
701
|
-
/** @internal Resolve the credential used by the database type generator. */
|
|
702
|
-
static codegenPassword(password) {
|
|
703
|
-
if (typeof password === 'function') {
|
|
704
|
-
throw new Error('This database adapter does not support a password provider for type generation');
|
|
705
|
-
}
|
|
706
|
-
return Promise.resolve(password);
|
|
707
|
-
}
|
|
708
701
|
/**
|
|
709
702
|
* Returns the sql that would be executed by this Query
|
|
710
703
|
*
|