@rvoh/dream 2.41.0 → 2.42.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/Dream.js +155 -13
- package/dist/cjs/src/cli/index.js +39 -5
- package/dist/cjs/src/db/DreamDbConnection.js +19 -1
- package/dist/cjs/src/dream/DreamClassTransactionBuilder.js +34 -2
- package/dist/cjs/src/dream/Query.js +178 -14
- package/dist/cjs/src/dream/QueryDriver/Base.js +80 -0
- package/dist/cjs/src/dream/QueryDriver/Kysely.js +35 -29
- package/dist/cjs/src/dream/QueryDriver/Postgres.js +83 -1
- package/dist/cjs/src/dream/internal/findOrCreateBy.js +6 -1
- package/dist/cjs/src/dream/internal/updateOrCreateBy.js +6 -1
- package/dist/cjs/src/encrypt/algorithms/aes-gcm/decryptAESGCM.js +24 -10
- package/dist/cjs/src/errors/CreateOrFindByFailedToCreateAndFind.js +63 -6
- package/dist/cjs/src/errors/CreateOrUpdateByFailedToCreateAndUpdate.js +41 -6
- package/dist/cjs/src/errors/GeneratedFilesAlreadyExist.js +38 -0
- package/dist/cjs/src/errors/schema-builder/ColumnNamesDoNotRoundTrip.js +28 -0
- package/dist/cjs/src/helpers/cli/ASTConnectionBuilder.js +62 -10
- package/dist/cjs/src/helpers/cli/generateDream.js +109 -18
- package/dist/cjs/src/helpers/cli/generateMigration.js +12 -8
- package/dist/cjs/src/helpers/cli/writeGeneratedFile.js +3 -3
- package/dist/cjs/src/helpers/db/normalizeDataForDb.js +17 -2
- package/dist/esm/src/Dream.js +155 -13
- package/dist/esm/src/cli/index.js +39 -5
- package/dist/esm/src/db/DreamDbConnection.js +19 -1
- package/dist/esm/src/dream/DreamClassTransactionBuilder.js +34 -2
- package/dist/esm/src/dream/Query.js +178 -14
- package/dist/esm/src/dream/QueryDriver/Base.js +80 -0
- package/dist/esm/src/dream/QueryDriver/Kysely.js +35 -29
- package/dist/esm/src/dream/QueryDriver/Postgres.js +83 -1
- package/dist/esm/src/dream/internal/findOrCreateBy.js +6 -1
- package/dist/esm/src/dream/internal/updateOrCreateBy.js +6 -1
- package/dist/esm/src/encrypt/algorithms/aes-gcm/decryptAESGCM.js +24 -10
- package/dist/esm/src/errors/CreateOrFindByFailedToCreateAndFind.js +63 -6
- package/dist/esm/src/errors/CreateOrUpdateByFailedToCreateAndUpdate.js +41 -6
- package/dist/esm/src/errors/GeneratedFilesAlreadyExist.js +38 -0
- package/dist/esm/src/errors/schema-builder/ColumnNamesDoNotRoundTrip.js +28 -0
- package/dist/esm/src/helpers/cli/ASTConnectionBuilder.js +62 -10
- package/dist/esm/src/helpers/cli/generateDream.js +109 -18
- package/dist/esm/src/helpers/cli/generateMigration.js +12 -8
- package/dist/esm/src/helpers/cli/writeGeneratedFile.js +3 -3
- package/dist/esm/src/helpers/db/normalizeDataForDb.js +17 -2
- package/dist/types/src/Dream.d.ts +109 -6
- package/dist/types/src/bin/index.d.ts +11 -0
- package/dist/types/src/cli/index.d.ts +27 -0
- package/dist/types/src/db/DreamDbConnection.d.ts +15 -1
- package/dist/types/src/dream/DreamClassTransactionBuilder.d.ts +34 -2
- package/dist/types/src/dream/Query.d.ts +101 -6
- package/dist/types/src/dream/QueryDriver/Base.d.ts +59 -0
- package/dist/types/src/dream/QueryDriver/Kysely.d.ts +9 -0
- package/dist/types/src/dream/QueryDriver/Postgres.d.ts +29 -1
- package/dist/types/src/dream/internal/executeDatabaseQuery.d.ts +2 -1
- package/dist/types/src/errors/CreateOrFindByFailedToCreateAndFind.d.ts +40 -1
- package/dist/types/src/errors/CreateOrUpdateByFailedToCreateAndUpdate.d.ts +8 -1
- package/dist/types/src/errors/GeneratedFilesAlreadyExist.d.ts +18 -0
- package/dist/types/src/errors/schema-builder/ColumnNamesDoNotRoundTrip.d.ts +12 -0
- package/dist/types/src/helpers/cli/ASTConnectionBuilder.d.ts +23 -1
- package/dist/types/src/helpers/cli/generateDream.d.ts +25 -0
- package/dist/types/src/helpers/cli/generateMigration.d.ts +20 -2
- package/dist/types/src/helpers/cli/syncWithFailureContext.d.ts +7 -2
- package/dist/types/src/helpers/cli/writeGeneratedFile.d.ts +6 -1
- package/dist/types/src/helpers/db/normalizeDataForDb.d.ts +7 -0
- package/docs/assets/hierarchy.js +1 -1
- package/docs/assets/search.js +1 -1
- package/docs/classes/db.DreamMigrationHelpers.html +11 -11
- package/docs/classes/db.KyselyQueryDriver.html +87 -50
- package/docs/classes/db.PostgresQueryDriver.html +76 -50
- package/docs/classes/db.QueryDriverBase.html +87 -50
- 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 +6 -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 +203 -133
- package/docs/classes/index.DreamApp.html +13 -13
- package/docs/classes/index.DreamTransaction.html +3 -3
- package/docs/classes/index.Env.html +5 -5
- package/docs/classes/index.Query.html +147 -100
- package/docs/classes/system.CliFileWriter.html +4 -4
- package/docs/classes/system.DreamBin.html +7 -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 +11 -11
- package/docs/classes/system.ObjectSerializerBuilder.html +8 -8
- 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 +1 -1
- package/docs/functions/index.ObjectSerializer.html +1 -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 +3 -3
- 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 +7 -7
- package/docs/interfaces/types.IntrospectedDatabase.html +2 -2
- package/docs/interfaces/types.IntrospectedEnum.html +2 -2
- package/docs/interfaces/types.IntrospectedTable.html +5 -5
- package/docs/interfaces/types.SerializerRendererOpts.html +2 -2
- 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 +1 -1
- 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 +1 -1
- package/dist/cjs/src/helpers/cli/generateFactory.js +0 -10
- package/dist/cjs/src/helpers/cli/generateSerializer.js +0 -18
- package/dist/cjs/src/helpers/cli/generateUnitSpec.js +0 -10
- package/dist/esm/src/helpers/cli/generateFactory.js +0 -10
- package/dist/esm/src/helpers/cli/generateSerializer.js +0 -18
- package/dist/esm/src/helpers/cli/generateUnitSpec.js +0 -10
- package/dist/types/src/helpers/cli/generateFactory.d.ts +0 -5
- package/dist/types/src/helpers/cli/generateSerializer.d.ts +0 -9
- package/dist/types/src/helpers/cli/generateUnitSpec.d.ts +0 -3
package/dist/cjs/src/Dream.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import yoctocolors from 'yoctocolors';
|
|
2
|
-
import {
|
|
2
|
+
import { STI_SCOPE_NAME } from './decorators/class/STI.js';
|
|
3
3
|
import associationToGetterSetterProp from './decorators/field/association/associationToGetterSetterProp.js';
|
|
4
4
|
import { blankAssociationsFactory } from './decorators/field/association/shared.js';
|
|
5
5
|
import { blankHooksFactory } from './decorators/field/lifecycle/shared.js';
|
|
@@ -49,6 +49,7 @@ import DreamMissingRequiredOverride from './errors/DreamMissingRequiredOverride.
|
|
|
49
49
|
import NonExistentScopeProvidedToResort from './errors/NonExistentScopeProvidedToResort.js';
|
|
50
50
|
import RecordNotFound from './errors/RecordNotFound.js';
|
|
51
51
|
import areEqual from './helpers/areEqual.js';
|
|
52
|
+
import camelize from './helpers/camelize.js';
|
|
52
53
|
import cloneDeepSafe from './helpers/cloneDeepSafe.js';
|
|
53
54
|
import isJsonColumn from './helpers/db/types/isJsonColumn.js';
|
|
54
55
|
import notEqual from './helpers/notEqual.js';
|
|
@@ -1267,6 +1268,18 @@ export default class Dream {
|
|
|
1267
1268
|
* IMPORTANT: A unique index/uniqueness constraint must exist on
|
|
1268
1269
|
* at least one of the provided attributes
|
|
1269
1270
|
*
|
|
1271
|
+
* The lookup applies the model's default scopes, so a conflicting
|
|
1272
|
+
* record they hide (for example, a soft-deleted one under a plain
|
|
1273
|
+
* unique index) is never returned. When the lookup finds nothing,
|
|
1274
|
+
* `CreateOrFindByFailedToCreateAndFind` is thrown. When the database
|
|
1275
|
+
* identifies the violated unique index (Postgres does, unless it is an
|
|
1276
|
+
* expression or partial index), its message names the `createWith`
|
|
1277
|
+
* attributes that index covers as the likely cause, unless the index
|
|
1278
|
+
* also covers every find attribute. When the index covers only find
|
|
1279
|
+
* attributes, or every find attribute, and the record is found with the
|
|
1280
|
+
* model's default scopes removed, the message names the default scope
|
|
1281
|
+
* likely hiding it. Otherwise it lists the possible causes.
|
|
1282
|
+
*
|
|
1270
1283
|
* ```ts
|
|
1271
1284
|
* const logEntry = await LogEntry.createOrFindBy({ externalId }, { createWith: params })
|
|
1272
1285
|
* ```
|
|
@@ -1288,10 +1301,12 @@ export default class Dream {
|
|
|
1288
1301
|
return dreamModel;
|
|
1289
1302
|
}
|
|
1290
1303
|
catch (err) {
|
|
1291
|
-
if (
|
|
1292
|
-
const
|
|
1304
|
+
if (Query.dbDriverClass(this.prototype.connectionName).isUniqueConstraintViolation(err)) {
|
|
1305
|
+
const lookup = findOrCreateLookupStatement(this, attributes);
|
|
1306
|
+
// where().first() rather than findBy: a value the database rejects is raised, never read as no match
|
|
1307
|
+
const dreamModel = await this.where(lookup).first();
|
|
1293
1308
|
if (!dreamModel)
|
|
1294
|
-
throw new CreateOrFindByFailedToCreateAndFind(this);
|
|
1309
|
+
throw new CreateOrFindByFailedToCreateAndFind(this, await this.createOrFindFailureCause(err, lookup, extraOpts.createWith));
|
|
1295
1310
|
return dreamModel;
|
|
1296
1311
|
}
|
|
1297
1312
|
throw err;
|
|
@@ -1327,6 +1342,18 @@ export default class Dream {
|
|
|
1327
1342
|
* IMPORTANT: A unique index/uniqueness constraint must exist on
|
|
1328
1343
|
* at least one of the provided attributes
|
|
1329
1344
|
*
|
|
1345
|
+
* The lookup applies the model's default scopes, so a conflicting
|
|
1346
|
+
* record they hide (for example, a soft-deleted one under a plain
|
|
1347
|
+
* unique index) is never updated. When the lookup finds nothing,
|
|
1348
|
+
* `CreateOrUpdateByFailedToCreateAndUpdate` is thrown. When the database
|
|
1349
|
+
* identifies the violated unique index (Postgres does, unless it is an
|
|
1350
|
+
* expression or partial index), its message names the `with`
|
|
1351
|
+
* attributes that index covers as the likely cause, unless the index
|
|
1352
|
+
* also covers every find attribute. When the index covers only find
|
|
1353
|
+
* attributes, or every find attribute, and the record is found with the
|
|
1354
|
+
* model's default scopes removed, the message names the default scope
|
|
1355
|
+
* likely hiding it. Otherwise it lists the possible causes.
|
|
1356
|
+
*
|
|
1330
1357
|
* @param attributes - The base attributes for finding which record to update, also used when creating
|
|
1331
1358
|
* @param extraOpts - Additional options
|
|
1332
1359
|
* @param extraOpts.with - additional attributes to persist when updating and creating, but not used for finding
|
|
@@ -1342,10 +1369,12 @@ export default class Dream {
|
|
|
1342
1369
|
}, skipHooks ? { skipHooks } : undefined);
|
|
1343
1370
|
}
|
|
1344
1371
|
catch (err) {
|
|
1345
|
-
if (
|
|
1346
|
-
const
|
|
1372
|
+
if (Query.dbDriverClass(this.prototype.connectionName).isUniqueConstraintViolation(err)) {
|
|
1373
|
+
const lookup = findOrCreateLookupStatement(this, attributes);
|
|
1374
|
+
// where().first() rather than findBy: a value the database rejects is raised, never read as no match
|
|
1375
|
+
const existingRecord = await this.where(lookup).first();
|
|
1347
1376
|
if (!existingRecord)
|
|
1348
|
-
throw new CreateOrUpdateByFailedToCreateAndUpdate(this);
|
|
1377
|
+
throw new CreateOrUpdateByFailedToCreateAndUpdate(this, await this.createOrFindFailureCause(err, lookup, extraOpts.with));
|
|
1349
1378
|
const { with: attrs } = extraOpts;
|
|
1350
1379
|
if (existingRecord) {
|
|
1351
1380
|
existingRecord.assignAttributes(attrs ?? {});
|
|
@@ -1355,6 +1384,64 @@ export default class Dream {
|
|
|
1355
1384
|
throw err;
|
|
1356
1385
|
}
|
|
1357
1386
|
}
|
|
1387
|
+
/**
|
|
1388
|
+
* @internal
|
|
1389
|
+
*
|
|
1390
|
+
* Used by createOrFindBy and createOrUpdateBy after their default-scoped
|
|
1391
|
+
* lookup finds nothing, to tell why. The driver names the columns of the
|
|
1392
|
+
* unique index the insert violated, when it can. When that index covers a
|
|
1393
|
+
* `createWith`/`with` attribute but not every find attribute, one of those
|
|
1394
|
+
* values likely conflicted with another record. When it covers only find
|
|
1395
|
+
* attributes, or every find attribute, a record matching them that the
|
|
1396
|
+
* default scopes hide is the record the insert conflicted with. Otherwise
|
|
1397
|
+
* the cause is undetermined, and a hidden record matching the find
|
|
1398
|
+
* attributes is only a candidate.
|
|
1399
|
+
*/
|
|
1400
|
+
static async createOrFindFailureCause(error, lookup, extraAttributes) {
|
|
1401
|
+
const connectionName = this.prototype.connectionName;
|
|
1402
|
+
const indexColumns = await Query.dbDriverClass(connectionName).violatedUniqueIndexColumns(connectionName, error, this.table);
|
|
1403
|
+
const indexAttributes = indexColumns?.map(column => camelize(column)) ?? null;
|
|
1404
|
+
const extraAttributeNames = Object.keys(extraAttributes ? this.extractAttributesFromUpdateableProperties(extraAttributes) : {});
|
|
1405
|
+
// the conflicting record holds the inserted value in every column of the index, so when the
|
|
1406
|
+
// index covers every find attribute, that record matches them and is not a different record
|
|
1407
|
+
const indexCoversEveryFindAttribute = !!indexAttributes && Object.keys(lookup).every(attribute => indexAttributes.includes(attribute));
|
|
1408
|
+
if (!indexCoversEveryFindAttribute) {
|
|
1409
|
+
const conflictingAttributes = (indexAttributes ?? []).filter(attribute => extraAttributeNames.includes(attribute));
|
|
1410
|
+
if (conflictingAttributes.length)
|
|
1411
|
+
return { kind: 'conflictingAttributes', attributes: conflictingAttributes };
|
|
1412
|
+
}
|
|
1413
|
+
const hidingDefaultScopes = await this.defaultScopesHidingLookup(lookup);
|
|
1414
|
+
const indexCoversOnlyFindAttributes = !!indexAttributes?.every(attribute => attribute in lookup);
|
|
1415
|
+
if (hidingDefaultScopes.length && (indexCoversOnlyFindAttributes || indexCoversEveryFindAttribute))
|
|
1416
|
+
return { kind: 'hiddenByDefaultScope', defaultScopes: hidingDefaultScopes };
|
|
1417
|
+
return {
|
|
1418
|
+
kind: 'undetermined',
|
|
1419
|
+
hidingDefaultScopes,
|
|
1420
|
+
extraAttributesMayConflict: indexAttributes === null && extraAttributeNames.length > 0,
|
|
1421
|
+
};
|
|
1422
|
+
}
|
|
1423
|
+
/**
|
|
1424
|
+
* @internal
|
|
1425
|
+
*
|
|
1426
|
+
* Used by createOrFindFailureCause. Returns the model's removable default scopes when a
|
|
1427
|
+
* record matching `lookup` exists once they are all removed, and an empty
|
|
1428
|
+
* array otherwise. The STI type discriminator is never removed. One probe
|
|
1429
|
+
* with every removable scope removed cannot tell which of several scopes
|
|
1430
|
+
* hid the record, so all of them are returned as candidates.
|
|
1431
|
+
*/
|
|
1432
|
+
static async defaultScopesHidingLookup(lookup) {
|
|
1433
|
+
const removableDefaultScopes = this['scopes'].default
|
|
1434
|
+
.map(scope => scope.method)
|
|
1435
|
+
.filter(scopeName => scopeName !== STI_SCOPE_NAME);
|
|
1436
|
+
if (!removableDefaultScopes.length)
|
|
1437
|
+
return [];
|
|
1438
|
+
let query = this.query();
|
|
1439
|
+
for (const scopeName of removableDefaultScopes) {
|
|
1440
|
+
query = query.removeDefaultScope(scopeName);
|
|
1441
|
+
}
|
|
1442
|
+
// where() rather than findBy: a value the database rejects is raised, never read as no match
|
|
1443
|
+
return (await query.where(lookup).exists()) ? removableDefaultScopes : [];
|
|
1444
|
+
}
|
|
1358
1445
|
/**
|
|
1359
1446
|
* Returns a new query instance with the distinct query applied.
|
|
1360
1447
|
* If no columnName is provided, then distinct will apply to the
|
|
@@ -1372,12 +1459,25 @@ export default class Dream {
|
|
|
1372
1459
|
}
|
|
1373
1460
|
/**
|
|
1374
1461
|
* Finds a record for the corresponding model with the
|
|
1375
|
-
* specified primary key. Returns null if not found
|
|
1462
|
+
* specified primary key. Returns null if not found,
|
|
1463
|
+
* including when the primary key is a value its column
|
|
1464
|
+
* cannot hold (for example, `'abc'` for an integer
|
|
1465
|
+
* primary key).
|
|
1466
|
+
*
|
|
1467
|
+
* When the database rejects a value elsewhere in the query (for
|
|
1468
|
+
* example, in a `whereNot`, `whereAny` or `leftJoinPreload`
|
|
1469
|
+
* clause), that rejection is raised as `DataTypeColumnTypeMismatch`
|
|
1470
|
+
* or `ColumnOverflow`, since a record may still match. Inside a
|
|
1471
|
+
* transaction, a primary key the database rejects is raised that
|
|
1472
|
+
* way too, because the database has then aborted the transaction.
|
|
1376
1473
|
*
|
|
1377
1474
|
* ```ts
|
|
1378
1475
|
* await User.find(123)
|
|
1379
1476
|
* // User{id: 123}
|
|
1380
1477
|
*
|
|
1478
|
+
* await User.find('abc')
|
|
1479
|
+
* // null
|
|
1480
|
+
*
|
|
1381
1481
|
* await User.find(null)
|
|
1382
1482
|
* // null
|
|
1383
1483
|
*
|
|
@@ -1393,15 +1493,26 @@ export default class Dream {
|
|
|
1393
1493
|
}
|
|
1394
1494
|
/**
|
|
1395
1495
|
* Finds a record for the corresponding model with the
|
|
1396
|
-
* specified primary key. If not found,
|
|
1496
|
+
* specified primary key. If not found, `RecordNotFound` is
|
|
1497
|
+
* raised, including when the primary key is a value its
|
|
1498
|
+
* column cannot hold (for example, `'abc'` for an integer
|
|
1499
|
+
* primary key).
|
|
1500
|
+
*
|
|
1501
|
+
* When the database rejects a value elsewhere in the query (for
|
|
1502
|
+
* example, in a `whereNot`, `whereAny` or `leftJoinPreload`
|
|
1503
|
+
* clause), that rejection is raised as `DataTypeColumnTypeMismatch`
|
|
1504
|
+
* or `ColumnOverflow`, since a record may still match. Inside a
|
|
1505
|
+
* transaction, a primary key the database rejects is raised that
|
|
1506
|
+
* way too, because the database has then aborted the transaction.
|
|
1397
1507
|
*
|
|
1398
1508
|
* ```ts
|
|
1399
|
-
* await User.
|
|
1509
|
+
* await User.findOrFail(123)
|
|
1400
1510
|
* // User{id: 123}
|
|
1401
1511
|
* ```
|
|
1402
1512
|
*
|
|
1403
1513
|
* @param primaryKey - The primaryKey of the record to look up
|
|
1404
|
-
* @returns
|
|
1514
|
+
* @returns The found record
|
|
1515
|
+
* @throws RecordNotFound if no record is found
|
|
1405
1516
|
*/
|
|
1406
1517
|
static async findOrFail(primaryKey) {
|
|
1407
1518
|
return await this.query().findOrFail(primaryKey);
|
|
@@ -1541,7 +1652,22 @@ export default class Dream {
|
|
|
1541
1652
|
/**
|
|
1542
1653
|
* Finds the first record—ordered by primary key—matching
|
|
1543
1654
|
* the corresponding model and the specified where statement.
|
|
1544
|
-
* If not found, null is returned
|
|
1655
|
+
* If not found, null is returned, including when a value the where
|
|
1656
|
+
* statement compares for equality (a plain value or
|
|
1657
|
+
* `ops.equal`) is one its column cannot hold: for example,
|
|
1658
|
+
* `'abc'` for a uuid column, or a date string Dream cannot
|
|
1659
|
+
* parse for a date column.
|
|
1660
|
+
*
|
|
1661
|
+
* A value the database rejects anywhere else (in a list of values,
|
|
1662
|
+
* under another operator such as `ops.not.equal` or `ops.lessThan`,
|
|
1663
|
+
* or elsewhere in the query, such as a `whereNot` or `whereAny`
|
|
1664
|
+
* clause) is raised as `DataTypeColumnTypeMismatch` or
|
|
1665
|
+
* `ColumnOverflow`, since a record may still match; so is a date,
|
|
1666
|
+
* datetime or time string there that Dream cannot parse. Inside a
|
|
1667
|
+
* transaction, an equality value the database rejects is raised that
|
|
1668
|
+
* way too, because the database has then aborted the transaction; a
|
|
1669
|
+
* date, datetime or time string Dream cannot parse never reaches the
|
|
1670
|
+
* database, so it is treated as not found there as well.
|
|
1545
1671
|
*
|
|
1546
1672
|
* ```ts
|
|
1547
1673
|
* await User.findBy({ email: 'how@yadoin' })
|
|
@@ -1557,7 +1683,22 @@ export default class Dream {
|
|
|
1557
1683
|
/**
|
|
1558
1684
|
* Finds the first record—ordered by primary key—matching
|
|
1559
1685
|
* the corresponding model and the specified where statement.
|
|
1560
|
-
* If not found,
|
|
1686
|
+
* If not found, `RecordNotFound` is raised, including when
|
|
1687
|
+
* a value the where statement compares for equality (a plain
|
|
1688
|
+
* value or `ops.equal`) is one its column cannot hold: for
|
|
1689
|
+
* example, `'abc'` for a uuid column, or a date string Dream
|
|
1690
|
+
* cannot parse for a date column.
|
|
1691
|
+
*
|
|
1692
|
+
* A value the database rejects anywhere else (in a list of values,
|
|
1693
|
+
* under another operator such as `ops.not.equal` or `ops.lessThan`,
|
|
1694
|
+
* or elsewhere in the query, such as a `whereNot` or `whereAny`
|
|
1695
|
+
* clause) is raised as `DataTypeColumnTypeMismatch` or
|
|
1696
|
+
* `ColumnOverflow`, since a record may still match; so is a date,
|
|
1697
|
+
* datetime or time string there that Dream cannot parse. Inside a
|
|
1698
|
+
* transaction, an equality value the database rejects is raised that
|
|
1699
|
+
* way too, because the database has then aborted the transaction; a
|
|
1700
|
+
* date, datetime or time string Dream cannot parse never reaches the
|
|
1701
|
+
* database, so it is treated as not found there as well.
|
|
1561
1702
|
*
|
|
1562
1703
|
* ```ts
|
|
1563
1704
|
* await User.findOrFailBy({ email: 'how@yadoin' })
|
|
@@ -1566,6 +1707,7 @@ export default class Dream {
|
|
|
1566
1707
|
*
|
|
1567
1708
|
* @param whereStatement - The where statement used to locate the record
|
|
1568
1709
|
* @returns The first model found matching the whereStatement
|
|
1710
|
+
* @throws RecordNotFound if no record is found
|
|
1569
1711
|
*/
|
|
1570
1712
|
static async findOrFailBy(whereStatement) {
|
|
1571
1713
|
return await this.query().findOrFailBy(whereStatement);
|
|
@@ -252,6 +252,10 @@ ${INDENT} # model is named GroupDanceLesson instead of LessonDanceGroup`)
|
|
|
252
252
|
.option('--admin-serializers', 'also generate AdminSerializer and AdminSummarySerializer variants for admin-facing API endpoints that may expose additional fields', false)
|
|
253
253
|
.option('--internal-serializers', 'also generate InternalSerializer and InternalSummarySerializer variants for internal API endpoints that may expose additional fields', false)
|
|
254
254
|
.option('--no-soft-delete', `skip generating the @SoftDelete() decorator and the corresponding nullable \`deleted_at\` column. By default, generated models use soft-delete semantics (rows are marked deleted via \`deleted_at\` instead of being removed from the database). Pass this flag when you want records to be hard-deleted.`)
|
|
255
|
+
.option('--overwrite', 'replace the model, spec, factory and serializer files when they already exist. Without it or --skip-existing, the generator writes nothing and lists the existing files when any of them, or the migration it would write, already exists. An existing migration is kept even with --overwrite, never rewritten or duplicated, since it may already have run; edit it by hand if the columns changed', false)
|
|
256
|
+
.addOption(new Option('--skip-existing', 'keep the model, spec, factory, serializer and migration files that already exist, write only the missing ones, and list each file kept. No migration is written when the model file already exists, even if no migration has the name the generator would use, since the model may already have one under another name. Cannot be combined with --overwrite')
|
|
257
|
+
.default(false)
|
|
258
|
+
.conflicts('overwrite'))
|
|
255
259
|
.argument('<modelName>', `The fully qualified model name, using / for namespacing. This determines the model class name (may be overridden with \`--model-name\`), table name, and file path under src/app/models/.
|
|
256
260
|
${INDENT}
|
|
257
261
|
${INDENT}Examples:
|
|
@@ -261,7 +265,11 @@ ${INDENT} Settings/CommunicationPreferences # src/app/models/Settings/Communi
|
|
|
261
265
|
.argument('[columnsWithTypes...]', columnsWithTypesDescription)
|
|
262
266
|
.action(async (modelName, columnsWithTypes, options) => {
|
|
263
267
|
await initializeDreamApp({ bypassDreamIntegrityChecks: true, bypassDbConnectionsDuringInit: true });
|
|
264
|
-
|
|
268
|
+
const { overwrite, skipExisting, ...generatorOptions } = options;
|
|
269
|
+
await DreamBin.generateDream(modelName, columnsWithTypes, {
|
|
270
|
+
...generatorOptions,
|
|
271
|
+
existingFiles: existingFilesMode({ overwrite, skipExisting }),
|
|
272
|
+
});
|
|
265
273
|
process.exit();
|
|
266
274
|
});
|
|
267
275
|
program
|
|
@@ -290,6 +298,10 @@ ${INDENT} pnpm psy g:sti-child --model-name=GroupDanceLesson Lesson/Dance/Group
|
|
|
290
298
|
${INDENT} # model is named GroupDanceLesson instead of LessonDanceGroup`)
|
|
291
299
|
.option('--admin-serializers', 'also generate AdminSerializer and AdminSummarySerializer variants for admin-facing API endpoints that may expose additional fields', false)
|
|
292
300
|
.option('--internal-serializers', 'also generate InternalSerializer and InternalSummarySerializer variants for internal API endpoints that may expose additional fields', false)
|
|
301
|
+
.option('--overwrite', 'replace the model, spec, factory and serializer files when they already exist. Without it or --skip-existing, the generator writes nothing and lists the existing files when any of them, or the migration it would write, already exists. An existing migration is kept even with --overwrite, never rewritten or duplicated, since it may already have run; edit it by hand if the columns changed', false)
|
|
302
|
+
.addOption(new Option('--skip-existing', 'keep the model, spec, factory, serializer and migration files that already exist, write only the missing ones, and list each file kept. No migration is written when the model file already exists, even if no migration has the name the generator would use, since the model may already have one under another name. Cannot be combined with --overwrite')
|
|
303
|
+
.default(false)
|
|
304
|
+
.conflicts('overwrite'))
|
|
293
305
|
.argument('<childModelName>', `The namespaced child model path. By convention, children are nested under the parent name.
|
|
294
306
|
${INDENT}
|
|
295
307
|
${INDENT}Examples:
|
|
@@ -307,7 +319,11 @@ ${INDENT} Health/Coach # extends src/app/models/Health/Coach.ts`)
|
|
|
307
319
|
await initializeDreamApp({ bypassDreamIntegrityChecks: true, bypassDbConnectionsDuringInit: true });
|
|
308
320
|
if (extendsWord !== 'extends')
|
|
309
321
|
throw new Error('Expecting: `<child-name> extends <parent-name> <columns-and-types>');
|
|
310
|
-
|
|
322
|
+
const { overwrite, skipExisting, ...generatorOptions } = options;
|
|
323
|
+
await DreamBin.generateStiChild(childModelName, parentModelName, columnsWithTypes, {
|
|
324
|
+
...generatorOptions,
|
|
325
|
+
existingFiles: existingFilesMode({ overwrite, skipExisting }),
|
|
326
|
+
});
|
|
311
327
|
process.exit();
|
|
312
328
|
});
|
|
313
329
|
program
|
|
@@ -364,7 +380,12 @@ ${INDENT} pnpm psy db:migrate`)
|
|
|
364
380
|
await syncWithFailureContext({
|
|
365
381
|
commandName: 'db:migrate',
|
|
366
382
|
completedWork: 'Every pending migration ran successfully',
|
|
367
|
-
|
|
383
|
+
// never `sync` on its own here: the failed sync reverted the
|
|
384
|
+
// generated types to their pre-migration content, and a Psychic
|
|
385
|
+
// app's `psy sync` boots with the integrity checks on, so it
|
|
386
|
+
// fails against them as soon as a model or serializer uses what
|
|
387
|
+
// the migration added. db:migrate boots with them bypassed
|
|
388
|
+
recoveryAdvice: 're-run `db:migrate` — the migrations it already applied are not re-applied, so it goes straight back to the sync',
|
|
368
389
|
}, async () => await DreamBin.sync(onSync));
|
|
369
390
|
}
|
|
370
391
|
process.exit();
|
|
@@ -444,8 +465,10 @@ ${INDENT}Warning: all existing data will be lost. The seed file (db/seed.ts) wil
|
|
|
444
465
|
// hook directly (below), while the `db:seed` *command* refuses to
|
|
445
466
|
// seed under NODE_ENV=test unless DREAM_SEED_DB_IN_TEST=1 — and
|
|
446
467
|
// NODE_ENV=test is the only environment DreamBin.sync does any
|
|
447
|
-
// work in, so it is the only environment this advice is printed in
|
|
448
|
-
|
|
468
|
+
// work in, so it is the only environment this advice is printed in.
|
|
469
|
+
// The second route re-runs db:migrate rather than `sync` for the
|
|
470
|
+
// reason given at db:migrate's own recovery advice
|
|
471
|
+
recoveryAdvice: 're-run `db:reset`, which seeds directly and so picks up the skipped seeding step, or re-run `db:migrate` followed by `DREAM_SEED_DB_IN_TEST=1 db:seed` — this sync only runs under `NODE_ENV=test`, and a bare `db:seed` does not seed there',
|
|
449
472
|
}, async () => await DreamBin.sync(onSync));
|
|
450
473
|
DreamCLI.logger.log(arrows, { logPrefix: ' ' });
|
|
451
474
|
DreamCLI.logger.log(colorize('db:seed', { color: 'green' }), {
|
|
@@ -577,6 +600,17 @@ ${INDENT}Examples: User, Place, Room/Bedroom, Settings/CommunicationPreferences`
|
|
|
577
600
|
}
|
|
578
601
|
static _logger = undefined;
|
|
579
602
|
}
|
|
603
|
+
/**
|
|
604
|
+
* commander rejects --overwrite together with --skip-existing before an
|
|
605
|
+
* action runs, so at most one of them is set here.
|
|
606
|
+
*/
|
|
607
|
+
function existingFilesMode({ overwrite, skipExisting, }) {
|
|
608
|
+
if (overwrite)
|
|
609
|
+
return 'overwrite';
|
|
610
|
+
if (skipExisting)
|
|
611
|
+
return 'skip';
|
|
612
|
+
return 'refuse';
|
|
613
|
+
}
|
|
580
614
|
function myParseInt(value) {
|
|
581
615
|
const parsedValue = parseInt(value, 10);
|
|
582
616
|
if (isNaN(parsedValue)) {
|
|
@@ -11,6 +11,24 @@ import DreamApp from '../dream-app/index.js';
|
|
|
11
11
|
import { claimedTestDatabaseIndexOrNull } from './testDatabasePool.js';
|
|
12
12
|
import protectAgainstPollutingAssignment from '../helpers/protectAgainstPollutingAssignment.js';
|
|
13
13
|
import { installDbConnectionLeakDiagnosticsIfEnabled, reportLeakedDbConnections, } from './dbConnectionLeakDiagnostics.js';
|
|
14
|
+
/**
|
|
15
|
+
* @internal
|
|
16
|
+
*
|
|
17
|
+
* the CamelCasePlugin every Dream connection carries: it turns each
|
|
18
|
+
* attribute name a query names into the column name it queries, and each
|
|
19
|
+
* column name a result row carries into a camelized key
|
|
20
|
+
*/
|
|
21
|
+
export class DreamCamelCasePlugin extends CamelCasePlugin {
|
|
22
|
+
constructor() {
|
|
23
|
+
super({ underscoreBetweenUppercaseLetters: true });
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* the column name a query naming the given attribute targets
|
|
27
|
+
*/
|
|
28
|
+
columnNameForAttribute(attributeName) {
|
|
29
|
+
return this.snakeCase(attributeName);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
14
32
|
let connections = {};
|
|
15
33
|
export default class DreamDbConnection {
|
|
16
34
|
static getConnection(connectionName, connectionType, dialectProvider) {
|
|
@@ -32,7 +50,7 @@ export default class DreamDbConnection {
|
|
|
32
50
|
});
|
|
33
51
|
},
|
|
34
52
|
dialect: dialectProvider(connectionConf),
|
|
35
|
-
plugins: [new
|
|
53
|
+
plugins: [new DreamCamelCasePlugin()],
|
|
36
54
|
});
|
|
37
55
|
const protectedName = protectAgainstPollutingAssignment(connectionName);
|
|
38
56
|
connections[protectedName] ||= {};
|
|
@@ -294,6 +294,12 @@ export default class DreamClassTransactionBuilder {
|
|
|
294
294
|
* Finds a record for the corresponding model with the
|
|
295
295
|
* specified primary key. If not found, null is returned.
|
|
296
296
|
*
|
|
297
|
+
* A value the database rejects as one its column cannot hold,
|
|
298
|
+
* whether the primary key (for example, `'abc'` for an integer
|
|
299
|
+
* primary key) or a value elsewhere in the query, raises
|
|
300
|
+
* `DataTypeColumnTypeMismatch` or `ColumnOverflow`, because the
|
|
301
|
+
* database has then aborted the transaction.
|
|
302
|
+
*
|
|
297
303
|
* ```ts
|
|
298
304
|
* await ApplicationModel.transaction(async txn => {
|
|
299
305
|
* await User.txn(txn).find(123)
|
|
@@ -317,6 +323,12 @@ export default class DreamClassTransactionBuilder {
|
|
|
317
323
|
* Finds a record for the corresponding model with the
|
|
318
324
|
* specified primary key. If not found, an exception is raised.
|
|
319
325
|
*
|
|
326
|
+
* A value the database rejects as one its column cannot hold,
|
|
327
|
+
* whether the primary key (for example, `'abc'` for an integer
|
|
328
|
+
* primary key) or a value elsewhere in the query, raises
|
|
329
|
+
* `DataTypeColumnTypeMismatch` or `ColumnOverflow`, because the
|
|
330
|
+
* database has then aborted the transaction.
|
|
331
|
+
*
|
|
320
332
|
* ```ts
|
|
321
333
|
* await ApplicationModel.transaction(async txn => {
|
|
322
334
|
* await User.txn(txn).findOrFail(123)
|
|
@@ -333,7 +345,17 @@ export default class DreamClassTransactionBuilder {
|
|
|
333
345
|
/**
|
|
334
346
|
* Finds the first record—ordered by primary key—matching
|
|
335
347
|
* the corresponding model and the specified where statement.
|
|
336
|
-
* If not found, null is returned
|
|
348
|
+
* If not found, null is returned, including when a value the where
|
|
349
|
+
* statement compares for equality (a plain value or `ops.equal`)
|
|
350
|
+
* is a date, datetime or time string Dream cannot parse for its
|
|
351
|
+
* column, which never reaches the database.
|
|
352
|
+
*
|
|
353
|
+
* A value the database rejects as one its column cannot hold
|
|
354
|
+
* (for example, `'abc'` for a uuid column) raises
|
|
355
|
+
* `DataTypeColumnTypeMismatch` or `ColumnOverflow`, because the
|
|
356
|
+
* database has then aborted the transaction. A date, datetime or
|
|
357
|
+
* time string Dream cannot parse anywhere else in the query raises
|
|
358
|
+
* `DataTypeColumnTypeMismatch`.
|
|
337
359
|
*
|
|
338
360
|
* ```ts
|
|
339
361
|
* await ApplicationModel.transaction(async txn => {
|
|
@@ -351,7 +373,17 @@ export default class DreamClassTransactionBuilder {
|
|
|
351
373
|
/**
|
|
352
374
|
* Finds the first record—ordered by primary key—matching
|
|
353
375
|
* the corresponding model and the specified where statement.
|
|
354
|
-
* If not found,
|
|
376
|
+
* If not found, `RecordNotFound` is raised, including when a
|
|
377
|
+
* value the where statement compares for equality (a plain value
|
|
378
|
+
* or `ops.equal`) is a date, datetime or time string Dream cannot
|
|
379
|
+
* parse for its column, which never reaches the database.
|
|
380
|
+
*
|
|
381
|
+
* A value the database rejects as one its column cannot hold
|
|
382
|
+
* (for example, `'abc'` for a uuid column) raises
|
|
383
|
+
* `DataTypeColumnTypeMismatch` or `ColumnOverflow`, because the
|
|
384
|
+
* database has then aborted the transaction. A date, datetime or
|
|
385
|
+
* time string Dream cannot parse anywhere else in the query raises
|
|
386
|
+
* `DataTypeColumnTypeMismatch`.
|
|
355
387
|
*
|
|
356
388
|
* ```ts
|
|
357
389
|
* await ApplicationModel.transaction(async txn => {
|