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

Rename IUnionEncoder -> IEventCodec w/ examples and docs re #27 #29

Merged
merged 6 commits into from
Jan 14, 2020
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 CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,13 @@ The `Unreleased` section name is replaced by the expected version of next releas
## [Unreleased]

### Added

- Tutorial and Documentation re `IEventCodec` [#29](https://github.com/jet/FsCodec/pull/29)

### Changed

- renamed `IUnionEncoder` to `IEventCodec` [#29](https://github.com/jet/FsCodec/pull/29)
- adjusted return types for `FsCodec.Core.EventData.Create` and `.TimelineEvent.Create` to relevant interfaces [#29](https://github.com/jet/FsCodec/pull/29)
- Update `Microsoft.SourceLink.GitHub`, `Microsoft.Framework.ReferenceAssemblies` to `1.0.0`

### Removed
Expand Down
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<Company>Jet.com</Company>
<Description>F# Union Event Codec with versioning tolerant converters</Description>
<PackageProjectUrl>https://github.com/jet/FsCodec</PackageProjectUrl>
<PackageTags>fsharp unionconverter unionencoder JsonPickler JsonIsomorphism json converters typeshape</PackageTags>
<PackageTags>fsharp unionconverter eventcodec JsonPickler JsonIsomorphism json converters typeshape</PackageTags>
<PackageLicense>Apache-2.0</PackageLicense>
<Copyright>Copyright © 2016-20</Copyright>

Expand Down
281 changes: 276 additions & 5 deletions README.md

Large diffs are not rendered by default.

69 changes: 35 additions & 34 deletions src/FsCodec.NewtonsoftJson/BoxCodec.fs
Original file line number Diff line number Diff line change
Expand Up @@ -7,95 +7,96 @@ namespace FsCodec.Box
open System
open System.Runtime.InteropServices

/// Provides Codecs that extract the Event bodies from a Union, using the conventions implied by using <c>TypeShape.UnionContract.UnionContractEncoder</c>
/// Provides Codecs that encode and/or extract Event bodies from a stream bearing a set of events defined in terms of a Discriminated Union,
/// using the conventions implied by using <c>TypeShape.UnionContract.UnionContractEncoder</c>
/// If you need full control and/or have have your own codecs, see <c>FsCodec.Codec.Create</c> instead
/// See <a href=""https://github.com/eiriktsarpalis/TypeShape/blob/master/tests/TypeShape.Tests/UnionContractTests.fs"></a> for example usage.
type Codec private () =

/// Generate a <code>IUnionEncoder</code> Codec that roundtrips events by holding the boxed form of the Event body.
/// Generate a <code>IEventEncoder</code> Codec that roundtrips events by holding the boxed form of the Event body.
/// Uses <c>up</c> and <c>down</c> functions to facilitate upconversion/downconversion
/// and/or surfacing metadata to the Programming Model by including it in the emitted <c>'Union</c>
/// The Event Type Names are inferred based on either explicit <c>DataMember(Name=</c> Attributes, or (if unspecified) the Discriminated Union Case Name
/// and/or surfacing metadata to the Programming Model by including it in the emitted <c>'Event</c>
/// The Event Type Names are inferred based on either explicit <c>DataMember(Name=</c> Attributes, or, if unspecified, the Discriminated Union Case Name
/// <c>Contract</c> must be tagged with </c>interface TypeShape.UnionContract.IUnionContract</c> to signify this scheme applies.
static member Create<'Union,'Contract,'Meta,'Context when 'Contract :> TypeShape.UnionContract.IUnionContract>
static member Create<'Event,'Contract,'Meta,'Context when 'Contract :> TypeShape.UnionContract.IUnionContract>
( /// Maps from the TypeShape <c>UnionConverter</c> <c>'Contract</c> case the Event has been mapped to (with the raw event data as context)
/// to the <c>'Union</c> representation (typically a Discriminated Union) that is to be presented to the programming model.
up : FsCodec.ITimelineEvent<obj> * 'Contract -> 'Union,
/// Maps a fresh Event resulting from a Decision in the Domain representation type down to the TypeShape <c>UnionConverter</c> <c>'Contract</c>
/// to the <c>'Event</c> representation (typically a Discriminated Union) that is to be presented to the programming model.
up : FsCodec.ITimelineEvent<obj> * 'Contract -> 'Event,
/// Maps a fresh Event resulting from a Decision in the Domain representation type down to the TypeShape <c>UnionContract</c> <c>'Contract</c>
/// The function is also expected to derive a <c>meta</c> object that will be held alongside the data (if it's not <c>None</c>)
/// together with its <c>correlationId</c>, <c>causationId</c> and an Event Creation <c>timestamp</c> (defaults to <c>UtcNow</c>).
down : 'Context option * 'Union -> 'Contract * 'Meta option * string * string * DateTimeOffset option,
/// Enables one to fail encoder generation if union contains nullary cases. Defaults to <c>false</c>, i.e. permitting them
/// together with its <c>correlationId</c>, <c>causationId</c> and an event creation <c>timestamp</c> (defaults to <c>UtcNow</c>).
down : 'Context option * 'Event -> 'Contract * 'Meta option * string * string * DateTimeOffset option,
/// Enables one to fail encoder generation if 'Contract contains nullary cases. Defaults to <c>false</c>, i.e. permitting them
[<Optional;DefaultParameterValue(null)>]?rejectNullaryCases)
: FsCodec.IUnionEncoder<'Union,obj,'Context> =
: FsCodec.IEventCodec<'Event,obj,'Context> =
let boxEncoder : TypeShape.UnionContract.IEncoder<obj> = new TypeShape.UnionContract.BoxEncoder() :> _
let dataCodec =
TypeShape.UnionContract.UnionContractEncoder.Create<'Contract,obj>(
boxEncoder,
requireRecordFields=true,
allowNullaryCases=not (defaultArg rejectNullaryCases false))
{ new FsCodec.IUnionEncoder<'Union,obj,'Context> with
member __.Encode(context,u) =
let (c, meta : 'Meta option, correlationId, causationId, timestamp : DateTimeOffset option) = down (context,u)
{ new FsCodec.IEventCodec<'Event,obj,'Context> with
member __.Encode(context,event) =
let (c, meta : 'Meta option, correlationId, causationId, timestamp : DateTimeOffset option) = down (context,event)
let enc = dataCodec.Encode c
let meta = meta |> Option.map boxEncoder.Encode<'Meta>
FsCodec.Core.EventData.Create(enc.CaseName, enc.Payload, defaultArg meta null, correlationId, causationId, ?timestamp=timestamp) :> _
FsCodec.Core.EventData.Create(enc.CaseName, enc.Payload, defaultArg meta null, correlationId, causationId, ?timestamp=timestamp)
member __.TryDecode encoded =
let cOption = dataCodec.TryDecode { CaseName = encoded.EventType; Payload = encoded.Data }
match cOption with None -> None | Some contract -> let union = up (encoded,contract) in Some union }
match cOption with None -> None | Some contract -> let event = up (encoded,contract) in Some event }

/// Generate a <code>IUnionEncoder</code> Codec that roundtrips events by holding the boxed form of the Event body.
/// Generate an <code>IEventCodec</code> that roundtrips events by holding the boxed form of the Event body.
/// Uses <c>up</c> and <c>down</c> and <c>mapCausation</c> functions to facilitate upconversion/downconversion and correlation/causationId mapping
/// and/or surfacing metadata to the Programming Model by including it in the emitted <c>'Union</c>
/// and/or surfacing metadata to the Programming Model by including it in the emitted <c>'Event</c>
/// The Event Type Names are inferred based on either explicit <c>DataMember(Name=</c> Attributes, or (if unspecified) the Discriminated Union Case Name
/// <c>Contract</c> must be tagged with </c>interface TypeShape.UnionContract.IUnionContract</c> to signify this scheme applies.
static member Create<'Union,'Contract,'Meta,'Context when 'Contract :> TypeShape.UnionContract.IUnionContract>
static member Create<'Event,'Contract,'Meta,'Context when 'Contract :> TypeShape.UnionContract.IUnionContract>
( /// Maps from the TypeShape <c>UnionConverter</c> <c>'Contract</c> case the Event has been mapped to (with the raw event data as context)
/// to the representation (typically a Discriminated Union) that is to be presented to the programming model.
up : FsCodec.ITimelineEvent<obj> * 'Contract -> 'Union,
up : FsCodec.ITimelineEvent<obj> * 'Contract -> 'Event,
/// 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 settings (if it's not <c>None</c>)
/// and an Event Creation <c>timestamp</c>.
down : 'Union -> 'Contract * 'Meta option * DateTimeOffset option,
down : 'Event -> 'Contract * 'Meta option * DateTimeOffset option,
/// Uses the 'Context passed to the Encode call and the 'Meta emitted by <c>down</c> to a) the final metadata b) the <c>correlationId</c> and c) the correlationId
mapCausation : 'Context option * 'Meta option -> 'Meta option * string * string,
/// Enables one to fail encoder generation if union contains nullary cases. Defaults to <c>false</c>, i.e. permitting them
[<Optional;DefaultParameterValue(null)>]?rejectNullaryCases)
: FsCodec.IUnionEncoder<'Union,obj,'Context> =
let down (context,union) =
let c, m, t = down union
: FsCodec.IEventCodec<'Event,obj,'Context> =
let down (context, event) =
let c, m, t = down event
let m', correlationId, causationId = mapCausation (context,m)
c, m', correlationId, causationId, t
Codec.Create(up=up, down=down, ?rejectNullaryCases=rejectNullaryCases)

/// Generate a <code>IUnionEncoder</code> Codec that roundtrips events by holding the boxed form of the Event body.
/// Generate an <code>IEventCodec</code> that roundtrips events by holding the boxed form of the Event body.
/// Uses <c>up</c> and <c>down</c> and <c>mapCausation</c> functions to facilitate upconversion/downconversion and correlation/causationId mapping
/// and/or surfacing metadata to the Programming Model by including it in the emitted <c>'Union</c>
/// and/or surfacing metadata to the Programming Model by including it in the emitted <c>'Event</c>
/// The Event Type Names are inferred based on either explicit <c>DataMember(Name=</c> Attributes, or (if unspecified) the Discriminated Union Case Name
/// <c>Contract</c> must be tagged with </c>interface TypeShape.UnionContract.IUnionContract</c> to signify this scheme applies.
static member Create<'Union,'Contract,'Meta when 'Contract :> TypeShape.UnionContract.IUnionContract>
static member Create<'Event,'Contract,'Meta when 'Contract :> TypeShape.UnionContract.IUnionContract>
( /// Maps from the TypeShape <c>UnionConverter</c> <c>'Contract</c> case the Event has been mapped to (with the raw event data as context)
/// to the representation (typically a Discriminated Union) that is to be presented to the programming model.
up : FsCodec.ITimelineEvent<obj> * 'Contract -> 'Union,
up : FsCodec.ITimelineEvent<obj> * 'Contract -> 'Event,
/// 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 settings (if it's not <c>None</c>)
/// and an Event Creation <c>timestamp</c>.
down : 'Union -> 'Contract * 'Meta option * DateTimeOffset option,
down : 'Event -> 'Contract * 'Meta option * DateTimeOffset option,
/// Enables one to fail encoder generation if union contains nullary cases. Defaults to <c>false</c>, i.e. permitting them
[<Optional;DefaultParameterValue(null)>]?rejectNullaryCases)
: FsCodec.IUnionEncoder<'Union,obj,obj> =
: FsCodec.IEventCodec<'Event,obj,obj> =
let mapCausation (_context : obj, m : ' Meta option) = m,null,null
Codec.Create(up=up, down=down, mapCausation=mapCausation, ?rejectNullaryCases=rejectNullaryCases)

/// Generate a <code>IUnionEncoder</code> Codec that roundtrips events by holding the boxed form of the Event body.
/// Generate an <code>IEventCodec</code> that roundtrips events by holding the boxed form of the Event body.
/// The Event Type Names are inferred based on either explicit <c>DataMember(Name=</c> Attributes, or (if unspecified) the Discriminated Union Case Name
/// <c>'Union</c> must be tagged with <c>interface TypeShape.UnionContract.IUnionContract</c> to signify this scheme applies.
static member Create<'Union when 'Union :> TypeShape.UnionContract.IUnionContract>
( /// Enables one to fail encoder generation if union contains nullary cases. Defaults to <c>false</c>, i.e. permitting them
[<Optional;DefaultParameterValue(null)>]?rejectNullaryCases)
: FsCodec.IUnionEncoder<'Union, obj, obj> =
: FsCodec.IEventCodec<'Union, obj, obj> =
let up : FsCodec.ITimelineEvent<_> * 'Union -> 'Union = snd
let down (u : 'Union) = u, None, None
let down (event : 'Union) = event, None, None
Codec.Create(up=up, down=down, ?rejectNullaryCases=rejectNullaryCases)
Loading