Skip to content

Commit

Permalink
Codec: Fix EventId, Require timestamp (#84)
Browse files Browse the repository at this point in the history
  • Loading branch information
bartelink authored Sep 6, 2022
1 parent 108089b commit dd205ba
Show file tree
Hide file tree
Showing 10 changed files with 68 additions and 37 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,18 @@ The `Unreleased` section name is replaced by the expected version of next releas
## [Unreleased]

### Added

- `Core.EventData/TimelineEvent`: Exposed default ctors [#83](https:/jet/FsCodec/pull/83)

### Changed

- `Codec.Create`: Made timestamp mandatory in low level `up` / `down` signature [#83](https:/jet/FsCodec/pull/83)

### Removed
### Fixed

- `EventData.Create`: restored defaulting of `EventId` to `Guid.NewGuid` broken in [#82](https:/jet/FsCodec/pull/82) [#83](https:/jet/FsCodec/pull/83)

<a name="3.0.0-rc.6"></a>
## [3.0.0-rc.6] - 2022-09-02

Expand Down
8 changes: 4 additions & 4 deletions src/FsCodec.Box/Codec.fs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Equivalent of FsCodec.NewtonsoftJson/SystemTextJson.Codec intended to provide equivalent calls and functionality, without actually serializing/deserializing as JSON
// Mirror of FsCodec.NewtonsoftJson/SystemTextJson.Codec intended to provide equivalent calls and functionality, without actually serializing/deserializing as JSON
// This is a useful facility for in-memory stores such as Equinox's MemoryStore as it enables you to
// - efficiently test behaviors from an event sourced decision processing perspective (e.g. with Property Based Tests)
// - without paying a serialization cost and/or having to deal with sanitization of generated data in order to make it roundtrippable through same
Expand Down Expand Up @@ -27,7 +27,7 @@ type Codec private () =
// <summary>Maps a fresh Event resulting from a Decision in the Domain representation type down to the TypeShape <c>UnionConverter</c> <c>'Contract</c><br/>
// The function is also expected to derive an optional <c>meta</c> object that will be serialized with the same <c>encoder</c>,
// and <c>eventId</c>, <c>correlationId</c>, <c>causationId</c> and an Event Creation<c>timestamp</c></summary>.
down : struct ('Context * 'Event) -> struct ('Contract * 'Meta voption * Guid * string * string * DateTimeOffset voption),
down : struct ('Context * 'Event) -> struct ('Contract * 'Meta voption * Guid * string * string * DateTimeOffset),
// <summary>Enables one to fail encoder generation if union contains nullary cases. Defaults to <c>false</c>, i.e. permitting them.</summary>
[<Optional; DefaultParameterValue(null)>] ?rejectNullaryCases)
: FsCodec.IEventCodec<'Event, obj, 'Context> =
Expand All @@ -45,7 +45,7 @@ type Codec private () =
// <summary>Maps a fresh Event resulting from a Decision in the Domain representation type down to the TypeShape <c>UnionConverter</c> <c>'Contract</c>
// The function is also expected to derive
// a <c>meta</c> object that will be serialized with the same options (if it's not <c>None</c>)
// and an Event Creation <c>timestamp</c>.</summary>
// and an Event Creation <c>timestamp</c> (Default: DateTimeOffset.UtcNow).</summary>
down : 'Event -> struct ('Contract * 'Meta voption * DateTimeOffset voption),
// <summary>Uses the 'Context passed to the Encode call and the 'Meta emitted by <c>down</c> to a) the final metadata b) the <c>eventId</c> c) the <c>correlationId</c> and d) the <c>causationId</c></summary>
mapCausation : struct ('Context * 'Meta voption) -> struct ('Meta voption * Guid * string * string),
Expand All @@ -66,7 +66,7 @@ type Codec private () =
// <summary>Maps a fresh <c>'Event</c> resulting from a Decision in the Domain representation type down to the TypeShape <c>UnionConverter</c> <c>'Contract</c>
// The function is also expected to derive
// a <c>meta</c> object that will be serialized with the same options (if it's not <c>None</c>)
// and an Event Creation <c>timestamp</c>.</summary>
// and an Event Creation <c>timestamp</c> (Default: DateTimeOffset.UtcNow).</summary>
down : 'Event -> struct ('Contract * 'Meta voption * DateTimeOffset voption),
// <summary>Enables one to fail encoder generation if union contains nullary cases. Defaults to <c>false</c>, i.e. permitting them.</summary>
[<Optional; DefaultParameterValue(null)>] ?rejectNullaryCases)
Expand Down
20 changes: 10 additions & 10 deletions src/FsCodec.Box/CoreCodec.fs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ type Codec private () =
// <summary>Maps a fresh Event resulting from a Decision in the Domain representation type down to the TypeShape <c>UnionConverter</c> <c>'Contract</c><br/>
// The function is also expected to derive an optional <c>meta</c> object that will be serialized with the same <c>encoder</c>,
// and <c>eventId</c>, <c>correlationId</c>, <c>causationId</c> and an Event Creation<c>timestamp</c></summary>.
down : struct ('Context * 'Event) -> struct ('Contract * 'Meta voption * Guid * string * string * DateTimeOffset voption),
down : struct ('Context * 'Event) -> struct ('Contract * 'Meta voption * Guid * string * string * DateTimeOffset),
// <summary>Enables one to fail encoder generation if union contains nullary cases. Defaults to <c>false</c>, i.e. permitting them.</summary>
[<Optional; DefaultParameterValue(null)>] ?rejectNullaryCases)
: FsCodec.IEventCodec<'Event, 'Body, 'Context> =
Expand All @@ -36,12 +36,12 @@ type Codec private () =
allowNullaryCases = not (defaultArg rejectNullaryCases false))

{ new FsCodec.IEventCodec<'Event, 'Body, 'Context> with

member _.Encode(context, event) =
let struct (c, meta : 'Meta voption, eventId, correlationId, causationId, timestamp : DateTimeOffset voption) = down struct (context, event)
let struct (c, meta : 'Meta voption, eventId, correlationId, causationId, timestamp) = down (context, event)
let enc = dataCodec.Encode c
let meta' = match meta with ValueSome x -> encoder.Encode<'Meta> x | ValueNone -> Unchecked.defaultof<_>
let ts = match timestamp with ValueNone -> None | ValueSome v -> Some v
EventData.Create(enc.CaseName, enc.Payload, meta', eventId, correlationId, causationId, ?timestamp = ts)
let meta' = match meta with ValueSome x -> encoder.Encode<'Meta> x | ValueNone -> Unchecked.defaultof<'Body>
EventData(enc.CaseName, enc.Payload, meta', eventId, correlationId, causationId, timestamp)

member _.TryDecode encoded =
match dataCodec.TryDecode { CaseName = encoded.EventType; Payload = encoded.Data } with
Expand All @@ -61,9 +61,9 @@ type Codec private () =
// <summary>Maps a fresh Event resulting from a Decision in the Domain representation type down to the TypeShape <c>UnionConverter</c> <c>'Contract</c>
// The function is also expected to derive
// a <c>meta</c> object that will be serialized with the same options (if it's not <c>None</c>)
// and an Event Creation <c>timestamp</c>.</summary>
// and an Event Creation <c>timestamp</c> (Default: DateTimeOffset.UtcNow).</summary>
down : 'Event -> struct ('Contract * 'Meta voption * DateTimeOffset voption),
// <summary>Uses the 'Context passed to the Encode call and the 'Meta emitted by <c>down</c> to a) the final metadata b) the <c>eventId</c> c) the <c>correlationId</c> and d) the <c>causationId</c></summary>
// <summary>Uses the 'Context passed to the Encode call and the 'Meta emitted by <c>down</c> to produce a) the final metadata b) the <c>eventId</c> c) the <c>correlationId</c> and d) the <c>causationId</c></summary>
mapCausation : struct ('Context * 'Meta voption) -> struct ('Meta voption * Guid * string * string),
// <summary>Enables one to fail encoder generation if union contains nullary cases. Defaults to <c>false</c>, i.e. permitting them.</summary>
[<Optional; DefaultParameterValue(null)>] ?rejectNullaryCases)
Expand All @@ -72,7 +72,7 @@ type Codec private () =
let down struct (context, union) =
let struct (c, m, t) = down union
let struct (m', eventId, correlationId, causationId) = mapCausation (context, m)
struct (c, m', eventId, correlationId, causationId, t)
struct (c, m', eventId, correlationId, causationId, match t with ValueSome t -> t | ValueNone -> DateTimeOffset.Now)
Codec.Create(encoder, up = up, down = down, ?rejectNullaryCases = rejectNullaryCases)

/// <summary>Generate an <code>IEventCodec</code> using the supplied <c>encoder</c>.<br/>
Expand All @@ -88,7 +88,7 @@ type Codec private () =
// <summary>Maps a fresh <c>'Event</c> resulting from a Decision in the Domain representation type down to the TypeShape <c>UnionConverter</c> <c>'Contract</c>
// The function is also expected to derive
// a <c>meta</c> object that will be serialized with the same options (if it's not <c>None</c>)
// and an Event Creation <c>timestamp</c>.</summary>
// and an Event Creation <c>timestamp</c> (Default: DateTimeOffset.UtcNow).</summary>
down : 'Event -> struct ('Contract * 'Meta voption * DateTimeOffset voption),
// <summary>Enables one to fail encoder generation if union contains nullary cases. Defaults to <c>false</c>, i.e. permitting them.</summary>
[<Optional; DefaultParameterValue(null)>] ?rejectNullaryCases)
Expand All @@ -107,5 +107,5 @@ type Codec private () =
: FsCodec.IEventCodec<'Union, 'Body, unit> =

let up struct (_e : FsCodec.ITimelineEvent<'Body>, u : 'Union) : 'Union = u
let down (event : 'Union) = struct (event, ValueNone, ValueNone)
let down (event : 'Union) = struct (event, ValueNone (*Meta*), ValueNone (*Timestamp*))
Codec.Create(encoder, up = up, down = down, ?rejectNullaryCases = rejectNullaryCases)
4 changes: 2 additions & 2 deletions src/FsCodec.NewtonsoftJson/Codec.fs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ type Codec private () =
// <summary>Maps a fresh Event resulting from a Decision in the Domain representation type down to the TypeShape <c>UnionConverter</c> <c>'Contract</c><br/>
// The function is also expected to derive an optional <c>meta</c> object that will be serialized with the same <c>encoder</c>,
// and <c>eventId</c>, <c>correlationId</c>, <c>causationId</c> and an Event Creation<c>timestamp</c></summary>.
down : struct ('Context * 'Event) -> struct ('Contract * 'Meta voption * Guid * string * string * DateTimeOffset voption),
down : struct ('Context * 'Event) -> struct ('Contract * 'Meta voption * Guid * string * string * DateTimeOffset),
// <summary>Configuration to be used by the underlying <c>Newtonsoft.Json</c> Serializer when encoding/decoding. Defaults to same as <c>Options.Default</c></summary>
[<Optional; DefaultParameterValue(null)>] ?options,
// <summary>Enables one to fail encoder generation if union contains nullary cases. Defaults to <c>false</c>, i.e. permitting them.</summary>
Expand All @@ -94,7 +94,7 @@ type Codec private () =
// <summary>Maps a fresh Event resulting from a Decision in the Domain representation type down to the TypeShape <c>UnionConverter</c> <c>'Contract</c>
// The function is also expected to derive
// a <c>meta</c> object that will be serialized with the same options (if it's not <c>None</c>)
// and an Event Creation <c>timestamp</c>.</summary>
// and an Event Creation <c>timestamp</c> (Default: DateTimeOffset.UtcNow).</summary>
down : 'Event -> struct ('Contract * 'Meta voption * DateTimeOffset voption),
// <summary>Uses the 'Context passed to the Encode call and the 'Meta emitted by <c>down</c> to a) the final metadata b) the <c>eventId</c> c) the <c>correlationId</c> and d) the <c>causationId</c></summary>
mapCausation : struct ('Context * 'Meta voption) -> struct ('Meta voption * Guid * string * string),
Expand Down
4 changes: 2 additions & 2 deletions src/FsCodec.SystemTextJson/Codec.fs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ type Codec private () =
// <summary>Maps a fresh Event resulting from a Decision in the Domain representation type down to the TypeShape <c>UnionConverter</c> <c>'Contract</c><br/>
// The function is also expected to derive an optional <c>meta</c> object that will be serialized with the same <c>encoder</c>,
// and <c>eventId</c>, <c>correlationId</c>, <c>causationId</c> and an Event Creation<c>timestamp</c></summary>.
down : struct ('Context * 'Event) -> struct ('Contract * 'Meta voption * Guid * string * string * DateTimeOffset voption),
down : struct ('Context * 'Event) -> struct ('Contract * 'Meta voption * Guid * string * string * DateTimeOffset),
// <summary>Configuration to be used by the underlying <c>System.Text.Json</c> Serializer when encoding/decoding. Defaults to same as <c>Options.Default</c></summary>
[<Optional; DefaultParameterValue(null)>] ?options,
// <summary>Enables one to fail encoder generation if union contains nullary cases. Defaults to <c>false</c>, i.e. permitting them.</summary>
Expand All @@ -64,7 +64,7 @@ type Codec private () =
// <summary>Maps a fresh Event resulting from a Decision in the Domain representation type down to the TypeShape <c>UnionConverter</c> <c>'Contract</c>
// The function is also expected to derive
// a <c>meta</c> object that will be serialized with the same options (if it's not <c>None</c>)
// and an Event Creation <c>timestamp</c>.</summary>
// and an Event Creation <c>timestamp</c> (Default: DateTimeOffset.UtcNow).</summary>
down : 'Event -> struct ('Contract * 'Meta voption * DateTimeOffset voption),
// <summary>Uses the 'Context passed to the Encode call and the 'Meta emitted by <c>down</c> to a) the final metadata b) the <c>eventId</c> c) the <c>correlationId</c> and d) the <c>causationId</c></summary>
mapCausation : struct ('Context * 'Meta voption) -> struct ('Meta voption * Guid * string * string),
Expand Down
4 changes: 2 additions & 2 deletions src/FsCodec.SystemTextJson/CodecJsonElement.fs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ type CodecJsonElement private () =
// <summary>Maps a fresh Event resulting from a Decision in the Domain representation type down to the TypeShape <c>UnionConverter</c> <c>'Contract</c><br/>
// The function is also expected to derive an optional <c>meta</c> object that will be serialized with the same <c>encoder</c>,
// and <c>eventId</c>, <c>correlationId</c>, <c>causationId</c> and an Event Creation<c>timestamp</c></summary>.
down : struct ('Context * 'Event) -> struct ('Contract * 'Meta voption * Guid * string * string * DateTimeOffset voption),
down : struct ('Context * 'Event) -> struct ('Contract * 'Meta voption * Guid * string * string * DateTimeOffset),
// <summary>Configuration to be used by the underlying <c>System.Text.Json</c> Serializer when encoding/decoding. Defaults to same as <c>Options.Default</c></summary>
[<Optional; DefaultParameterValue(null)>] ?options,
// <summary>Enables one to fail encoder generation if union contains nullary cases. Defaults to <c>false</c>, i.e. permitting them.</summary>
Expand All @@ -63,7 +63,7 @@ type CodecJsonElement private () =
// <summary>Maps a fresh Event resulting from a Decision in the Domain representation type down to the TypeShape <c>UnionConverter</c> <c>'Contract</c>
// The function is also expected to derive
// a <c>meta</c> object that will be serialized with the same options (if it's not <c>None</c>)
// and an Event Creation <c>timestamp</c>.</summary>
// and an Event Creation <c>timestamp</c> (Default: DateTimeOffset.UtcNow).</summary>
down : 'Event -> struct ('Contract * 'Meta voption * DateTimeOffset voption),
// <summary>Uses the 'Context passed to the Encode call and the 'Meta emitted by <c>down</c> to a) the final metadata b) the <c>eventId</c> c) the <c>correlationId</c> and d) the <c>causationId</c></summary>
mapCausation : struct ('Context * 'Meta voption) -> struct ('Meta voption * Guid * string * string),
Expand Down
Loading

0 comments on commit dd205ba

Please sign in to comment.