Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

guard against stale values in Model.DateTime fields #3471

Merged
merged 2 commits into from
Aug 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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