Skip to content

Commit

Permalink
guard against stale values in Model.DateTime fields (#3471)
Browse files Browse the repository at this point in the history
  • Loading branch information
tim-smart authored Aug 17, 2024
1 parent 9efe0e5 commit c3446d3
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 22 deletions.
5 changes: 5 additions & 0 deletions .changeset/lovely-deers-shout.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@effect/sql": patch
---

guard against stale values in Model.DateTime fields
5 changes: 5 additions & 0 deletions .changeset/plenty-pianos-breathe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@effect/experimental": patch
---

add VariantSchema.Overrideable, for creating fields with optional defaults
36 changes: 35 additions & 1 deletion packages/experimental/src/VariantSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@
* @since 1.0.0
*/
import type * as AST from "@effect/schema/AST"
import * as ParseResult from "@effect/schema/ParseResult"
import * as Schema from "@effect/schema/Schema"
import { dual } from "effect/Function"
import type { Brand } from "effect/Brand"
import type * as Effect from "effect/Effect"
import { constUndefined, dual } from "effect/Function"
import * as Option from "effect/Option"

/**
* @since 1.0.0
Expand Down Expand Up @@ -339,3 +343,33 @@ export const make = <
Class
} as any
}

/**
* @since 1.0.0
* @category overrideable
*/
export const Override = <A>(value: A): A & Brand<"Override"> => value as any

/**
* @since 1.0.0
* @category overrideable
*/
export interface Overrideable<To, From, R = never>
extends Schema.PropertySignature<":", (To & Brand<"Override">) | undefined, never, ":", From, true, R>
{}

/**
* @since 1.0.0
* @category overrideable
*/
export const Overrideable = <From, IFrom, RFrom, To, ITo, R>(
from: Schema.Schema<From, IFrom, RFrom>,
to: Schema.Schema<To, ITo>,
options: {
readonly generate: (_: Option.Option<ITo>) => Effect.Effect<From, ParseResult.ParseIssue, R>
}
): Overrideable<To, IFrom, RFrom | R> =>
Schema.transformOrFail(from, Schema.Union(Schema.Undefined, to.pipe(Schema.brand("Override"))), {
decode: (_) => ParseResult.succeed(undefined),
encode: (dt) => options.generate(dt === undefined ? Option.none() : Option.some(dt))
}).pipe(Schema.propertySignature, Schema.withConstructorDefault(constUndefined))
69 changes: 48 additions & 21 deletions packages/sql/src/Model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
*/
import * as VariantSchema from "@effect/experimental/VariantSchema"
import * as Schema from "@effect/schema/Schema"
import type { Brand } from "effect/Brand"
import * as DateTime from "effect/DateTime"
import * as Effect from "effect/Effect"
import * as Option from "effect/Option"

const {
Class,
Expand Down Expand Up @@ -87,6 +90,12 @@ export {
*/
export const fields: <A extends VariantSchema.Struct<any>>(self: A) => A[VariantSchema.TypeId] = VariantSchema.fields

/**
* @since 1.0.0
* @category overrideable
*/
export const Override: <A>(value: A) => A & Brand<"Override"> = VariantSchema.Override

/**
* @since 1.0.0
* @category models
Expand Down Expand Up @@ -200,20 +209,38 @@ export const DateTimeFromDate: DateTimeFromDate = Schema.transform(
}
)

const DateTimeWithNow = Schema.DateTimeUtc.pipe(
Schema.propertySignature,
Schema.withConstructorDefault(DateTime.unsafeNow)
)
/**
* @since 1.0.0
* @category schemas
*/
export const DateTimeWithNow = VariantSchema.Overrideable(Schema.String, Schema.DateTimeUtcFromSelf, {
generate: Option.match({
onNone: () => Effect.map(DateTime.now, DateTime.formatIso),
onSome: (dt) => Effect.succeed(DateTime.formatIso(dt))
})
})

const DateTimeFromDateWithNow = DateTimeFromDate.pipe(
Schema.propertySignature,
Schema.withConstructorDefault(DateTime.unsafeNow)
)
/**
* @since 1.0.0
* @category schemas
*/
export const DateTimeFromDateWithNow = VariantSchema.Overrideable(Schema.DateFromSelf, Schema.DateTimeUtcFromSelf, {
generate: Option.match({
onNone: () => Effect.map(DateTime.now, DateTime.toDateUtc),
onSome: (dt) => Effect.succeed(DateTime.toDateUtc(dt))
})
})

const DateTimeFromNumberWithNow = Schema.DateTimeUtcFromNumber.pipe(
Schema.propertySignature,
Schema.withConstructorDefault(DateTime.unsafeNow)
)
/**
* @since 1.0.0
* @category schemas
*/
export const DateTimeFromNumberWithNow = VariantSchema.Overrideable(Schema.Number, Schema.DateTimeUtcFromSelf, {
generate: Option.match({
onNone: () => Effect.map(DateTime.now, DateTime.toEpochMillis),
onSome: (dt) => Effect.succeed(DateTime.toEpochMillis(dt))
})
})

/**
* @since 1.0.0
Expand All @@ -222,7 +249,7 @@ const DateTimeFromNumberWithNow = Schema.DateTimeUtcFromNumber.pipe(
export interface DateTimeInsert extends
VariantSchema.Field<{
readonly select: typeof Schema.DateTimeUtc
readonly insert: Schema.PropertySignature<":", DateTime.Utc, never, ":", string, true>
readonly insert: VariantSchema.Overrideable<DateTime.Utc, string>
readonly json: typeof Schema.DateTimeUtc
}>
{}
Expand All @@ -249,7 +276,7 @@ export const DateTimeInsert: DateTimeInsert = Field({
export interface DateTimeInsertFromDate extends
VariantSchema.Field<{
readonly select: DateTimeFromDate
readonly insert: Schema.PropertySignature<":", DateTime.Utc, never, ":", Date, true>
readonly insert: VariantSchema.Overrideable<DateTime.Utc, Date>
readonly json: typeof Schema.DateTimeUtc
}>
{}
Expand All @@ -276,7 +303,7 @@ export const DateTimeInsertFromDate: DateTimeInsertFromDate = Field({
export interface DateTimeInsertFromNumber extends
VariantSchema.Field<{
readonly select: typeof Schema.DateTimeUtcFromNumber
readonly insert: Schema.PropertySignature<":", DateTime.Utc, never, ":", number, true>
readonly insert: VariantSchema.Overrideable<DateTime.Utc, number>
readonly json: typeof Schema.DateTimeUtcFromNumber
}>
{}
Expand All @@ -303,8 +330,8 @@ export const DateTimeInsertFromNumber: DateTimeInsertFromNumber = Field({
export interface DateTimeUpdate extends
VariantSchema.Field<{
readonly select: typeof Schema.DateTimeUtc
readonly insert: Schema.PropertySignature<":", DateTime.Utc, never, ":", string, true>
readonly update: Schema.PropertySignature<":", DateTime.Utc, never, ":", string, true>
readonly insert: VariantSchema.Overrideable<DateTime.Utc, string>
readonly update: VariantSchema.Overrideable<DateTime.Utc, string>
readonly json: typeof Schema.DateTimeUtc
}>
{}
Expand Down Expand Up @@ -333,8 +360,8 @@ export const DateTimeUpdate: DateTimeUpdate = Field({
export interface DateTimeUpdateFromDate extends
VariantSchema.Field<{
readonly select: DateTimeFromDate
readonly insert: Schema.PropertySignature<":", DateTime.Utc, never, ":", Date, true>
readonly update: Schema.PropertySignature<":", DateTime.Utc, never, ":", Date, true>
readonly insert: VariantSchema.Overrideable<DateTime.Utc, Date>
readonly update: VariantSchema.Overrideable<DateTime.Utc, Date>
readonly json: typeof Schema.DateTimeUtc
}>
{}
Expand Down Expand Up @@ -363,8 +390,8 @@ export const DateTimeUpdateFromDate: DateTimeUpdateFromDate = Field({
export interface DateTimeUpdateFromNumber extends
VariantSchema.Field<{
readonly select: typeof Schema.DateTimeUtcFromNumber
readonly insert: Schema.PropertySignature<":", DateTime.Utc, never, ":", number, true>
readonly update: Schema.PropertySignature<":", DateTime.Utc, never, ":", number, true>
readonly insert: VariantSchema.Overrideable<DateTime.Utc, number>
readonly update: VariantSchema.Overrideable<DateTime.Utc, number>
readonly json: typeof Schema.DateTimeUtcFromNumber
}>
{}
Expand Down

0 comments on commit c3446d3

Please sign in to comment.