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

Move Equinox.Codec out to FsCodec #156

Merged
merged 13 commits into from
Aug 30, 2019
Prev Previous commit
Next Next commit
clean genCodec usage
  • Loading branch information
bartelink committed Aug 29, 2019
commit 0016a4b0b0099e3f3e12e33572f5ae0d44b381e6
1 change: 1 addition & 0 deletions build.proj
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
<Target Name="Pack">
<Exec Command="dotnet pack src/Equinox $(Cfg) $(PackOptions)" />
<Exec Command="dotnet pack src/Gardelloyd $(Cfg) $(PackOptions)" />
<Exec Command="dotnet pack src/Gardelloyd.NewtonsoftJson $(Cfg) $(PackOptions)" />
<Exec Command="dotnet pack src/Equinox.Cosmos $(Cfg) $(PackOptions)" />
<Exec Command="dotnet pack src/Equinox.EventStore $(Cfg) $(PackOptions)" />
<Exec Command="dotnet pack src/Equinox.MemoryStore $(Cfg) $(PackOptions)" />
Expand Down
34 changes: 12 additions & 22 deletions samples/Infrastructure/Services.fs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
module Samples.Infrastructure.Services

open Domain
open Microsoft.Extensions.DependencyInjection
open System

Expand All @@ -20,40 +21,29 @@ type StreamResolver(storage) =
let accessStrategy = if unfolds then Equinox.Cosmos.AccessStrategy.Snapshot snapshot |> Some else None
Equinox.Cosmos.Resolver<'event,'state>(store, codec, fold, initial, caching, ?access = accessStrategy).Resolve

type ICodecGen =
abstract Generate<'Union when 'Union :> TypeShape.UnionContract.IUnionContract> : unit -> Gardelloyd.IUnionEncoder<'Union,byte[]>

type ServiceBuilder(storageConfig, handlerLog, codecGen : ICodecGen) =
type ServiceBuilder(storageConfig, handlerLog) =
let resolver = StreamResolver(storageConfig)

member __.CreateFavoritesService() =
let codec = codecGen.Generate<Domain.Favorites.Events.Event>()
let fold, initial = Domain.Favorites.Folds.fold, Domain.Favorites.Folds.initial
let snapshot = Domain.Favorites.Folds.isOrigin,Domain.Favorites.Folds.compact
Backend.Favorites.Service(handlerLog, resolver.Resolve(codec,fold,initial,snapshot))
let fold, initial = Favorites.Folds.fold, Favorites.Folds.initial
let snapshot = Favorites.Folds.isOrigin,Favorites.Folds.compact
Backend.Favorites.Service(handlerLog, resolver.Resolve(Favorites.Events.codec,fold,initial,snapshot))

member __.CreateSaveForLaterService() =
let codec = codecGen.Generate<Domain.SavedForLater.Events.Event>()
let fold, initial = Domain.SavedForLater.Folds.fold, Domain.SavedForLater.Folds.initial
let snapshot = Domain.SavedForLater.Folds.isOrigin,Domain.SavedForLater.Folds.compact
Backend.SavedForLater.Service(handlerLog, resolver.Resolve(codec,fold,initial,snapshot), maxSavedItems=50)
let fold, initial = SavedForLater.Folds.fold, SavedForLater.Folds.initial
let snapshot = SavedForLater.Folds.isOrigin,SavedForLater.Folds.compact
Backend.SavedForLater.Service(handlerLog, resolver.Resolve(SavedForLater.Events.codec,fold,initial,snapshot), maxSavedItems=50)

member __.CreateTodosService() =
let codec = codecGen.Generate<TodoBackend.Events.Event>()
let fold, initial = TodoBackend.Folds.fold, TodoBackend.Folds.initial
let snapshot = TodoBackend.Folds.isOrigin, TodoBackend.Folds.compact
TodoBackend.Service(handlerLog, resolver.Resolve(codec,fold,initial,snapshot))
TodoBackend.Service(handlerLog, resolver.Resolve(TodoBackend.Events.codec,fold,initial,snapshot))

let register (services : IServiceCollection, storageConfig, handlerLog, codecGen : ICodecGen) =
let register (services : IServiceCollection, storageConfig, handlerLog) =
let regF (factory : IServiceProvider -> 'T) = services.AddSingleton<'T>(fun (sp: IServiceProvider) -> factory sp) |> ignore

regF <| fun _sp -> ServiceBuilder(storageConfig, handlerLog, codecGen)
regF <| fun _sp -> ServiceBuilder(storageConfig, handlerLog)

regF <| fun sp -> sp.GetService<ServiceBuilder>().CreateFavoritesService()
regF <| fun sp -> sp.GetService<ServiceBuilder>().CreateSaveForLaterService()
regF <| fun sp -> sp.GetService<ServiceBuilder>().CreateTodosService()

let serializationSettings = Newtonsoft.Json.Converters.FSharp.Settings.CreateCorrect()
type NewtonsoftJsonCodecGen() =
interface ICodecGen with
member __.Generate() = Gardelloyd.NewtonsoftJson.Codec.Create<'Union>(serializationSettings)
regF <| fun sp -> sp.GetService<ServiceBuilder>().CreateTodosService()
1 change: 1 addition & 0 deletions samples/Store/Domain/Cart.fs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ module Events =
| ItemQuantityChanged of ItemQuantityChangeInfo
| ItemWaiveReturnsChanged of ItemWaiveReturnsInfo
interface TypeShape.UnionContract.IUnionContract
let codec = Gardelloyd.NewtonsoftJson.Codec.Create<Event>()

module Folds =
type ItemInfo = { skuId: SkuId; quantity: int; returnsWaived: bool }
Expand Down
1 change: 1 addition & 0 deletions samples/Store/Domain/ContactPreferences.fs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ module Events =
type Event =
| [<System.Runtime.Serialization.DataMember(Name = "contactPreferencesChanged")>]Updated of Value
interface TypeShape.UnionContract.IUnionContract
let codec = Gardelloyd.NewtonsoftJson.Codec.Create<Event>()

module Folds =
type State = Events.Preferences
Expand Down
5 changes: 5 additions & 0 deletions samples/Store/Domain/Domain.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,17 @@
<Compile Include="InventoryItem.fs" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\src\Gardelloyd.NewtonsoftJson\Gardelloyd.NewtonsoftJson.fsproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="FSharp.Core" Version="3.1.2.5" Condition=" '$(TargetFramework)' == 'net461' " />
<PackageReference Include="FSharp.Core" Version="4.3.4" Condition=" '$(TargetFramework)' == 'netstandard2.0' " />

<PackageReference Include="FSharp.UMX" Version="1.0.0" />
<PackageReference Include="Jet.JsonNet.Converters" Version="0.2.2" />
<PackageReference Include="Jet.JsonNet.Converters" Version="0.2.2" />
<PackageReference Include="TypeShape" Version="7.0.0" />
</ItemGroup>

Expand Down
1 change: 1 addition & 0 deletions samples/Store/Domain/Favorites.fs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ module Events =
| Favorited of Favorited
| Unfavorited of Unfavorited
interface TypeShape.UnionContract.IUnionContract
let codec = Gardelloyd.NewtonsoftJson.Codec.Create<Event>()

module Folds =
type State = Events.Favorited []
Expand Down
1 change: 1 addition & 0 deletions samples/Store/Domain/SavedForLater.fs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ module Events =
/// Addition of a collection of skus to the list
| Added of Added
interface TypeShape.UnionContract.IUnionContract
let codec = Gardelloyd.NewtonsoftJson.Codec.Create<Event>()

module Folds =
open Events
Expand Down
2 changes: 1 addition & 1 deletion samples/Store/Integration/CartIntegration.fs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ let createMemoryStore () =
let createServiceMemory log store =
Backend.Cart.Service(log, Resolver(store, fold, initial).Resolve)

let codec = Equinox.EventStore.Integration.EventStoreIntegration.genCodec<Domain.Cart.Events.Event>()
let codec = Domain.Cart.Events.codec

let resolveGesStreamWithRollingSnapshots gateway =
EventStore.Resolver(gateway, codec, fold, initial, access = AccessStrategy.RollingSnapshots snapshot).Resolve
Expand Down
13 changes: 4 additions & 9 deletions samples/Store/Integration/CodecIntegration.fs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,6 @@ open Domain
open Swensen.Unquote
open TypeShape.UnionContract

let serializationSettings =
Newtonsoft.Json.Converters.FSharp.Settings.CreateCorrect(converters=
[| // Don't let json.net treat 't option as the DU it is internally
Newtonsoft.Json.Converters.FSharp.OptionConverter() |])

let genCodec<'Union when 'Union :> TypeShape.UnionContract.IUnionContract>() =
Gardelloyd.NewtonsoftJson.Codec.Create<'Union>(serializationSettings)

type EventWithId = { id : CartId } // where CartId uses FSharp.UMX

type EventWithOption = { age : int option }
Expand Down Expand Up @@ -46,7 +38,10 @@ let render = function
//| EventE i -> string i
//| EventF s -> Newtonsoft.Json.JsonConvert.SerializeObject s

let codec = genCodec<SimpleDu>()
let codec =
// Don't let json.net treat 't option as the DU it is internally
let settingsWithOptionSupport = Gardelloyd.NewtonsoftJson.Settings.Create(Newtonsoft.Json.Converters.FSharp.OptionConverter())
Gardelloyd.NewtonsoftJson.Codec.Create(settingsWithOptionSupport)

[<AutoData(MaxTest=100)>]
let ``Can roundtrip, rendering correctly`` (x: SimpleDu) =
Expand Down
2 changes: 1 addition & 1 deletion samples/Store/Integration/ContactPreferencesIntegration.fs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ let createMemoryStore () =
let createServiceMemory log store =
Backend.ContactPreferences.Service(log, MemoryStore.Resolver(store, fold, initial).Resolve)

let codec = genCodec<Domain.ContactPreferences.Events.Event>()
let codec = Domain.ContactPreferences.Events.codec
let resolveStreamGesWithOptimizedStorageSemantics gateway =
EventStore.Resolver(gateway 1, codec, fold, initial, access = EventStore.AccessStrategy.EventsAreState).Resolve
let resolveStreamGesWithoutAccessStrategy gateway =
Expand Down
3 changes: 0 additions & 3 deletions samples/Store/Integration/EventStoreIntegration.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,8 @@
module Samples.Store.Integration.EventStoreIntegration

open Equinox.EventStore
open Gardelloyd.NewtonsoftJson
open System

let genCodec<'Union when 'Union :> TypeShape.UnionContract.IUnionContract>() = Codec.Create<'Union>(Settings.Create())

/// Connect with Gossip based cluster discovery using the default Commercial edition Manager port config
/// Such a config can be simulated on a single node with zero config via the EventStore OSS package:-
/// 1. cinst eventstore-oss -y # where cinst is an invocation of the Chocolatey Package Installer on Windows
Expand Down
2 changes: 1 addition & 1 deletion samples/Store/Integration/FavoritesIntegration.fs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ let createMemoryStore () =
let createServiceMemory log store =
Backend.Favorites.Service(log, MemoryStore.Resolver(store, fold, initial).Resolve)

let codec = genCodec<Domain.Favorites.Events.Event>()
let codec = Domain.Favorites.Events.codec
let createServiceGes gateway log =
let resolveStream = EventStore.Resolver(gateway, codec, fold, initial, access = EventStore.AccessStrategy.RollingSnapshots snapshot).Resolve
Backend.Favorites.Service(log, resolveStream)
Expand Down
1 change: 1 addition & 0 deletions samples/TodoBackend/Todo.fs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ module Events =
| Cleared
| Compacted of CompactedInfo
interface TypeShape.UnionContract.IUnionContract
let codec = Gardelloyd.NewtonsoftJson.Codec.Create<Event>()

module Folds =
type State = { items : Todo list; nextId : int }
Expand Down
5 changes: 3 additions & 2 deletions samples/Tutorial/Cosmos.fsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#r "Newtonsoft.Json.dll"
#r "TypeShape.dll"
#r "Equinox.dll"
#r "Gardelloyd.dll"
#r "Gardelloyd.NewtonsoftJson.dll"
#r "FSharp.Control.AsyncSeq.dll"
#r "Microsoft.Azure.DocumentDb.Core.dll"
Expand All @@ -24,6 +25,7 @@ module Favorites =
| Added of string
| Removed of string
interface TypeShape.UnionContract.IUnionContract
let codec = Gardelloyd.NewtonsoftJson.Codec.Create<Event>()
let initial : string list = []
let evolve state = function
| Added sku -> sku :: state
Expand Down Expand Up @@ -74,8 +76,7 @@ module Store =
let cacheStrategy = CachingStrategy.SlidingWindow (cache, TimeSpan.FromMinutes 20.) // OR CachingStrategy.NoCaching

module FavoritesCategory =
let codec = Gardelloyd.NewtonsoftJson.Codec.Create<Favorites.Event>(Newtonsoft.Json.JsonSerializerSettings())
let resolve = Resolver(Store.context, codec, Favorites.fold, Favorites.initial, Store.cacheStrategy).Resolve
let resolve = Resolver(Store.context, Favorites.codec, Favorites.fold, Favorites.initial, Store.cacheStrategy).Resolve

let service = Favorites.Service(Log.log, FavoritesCategory.resolve)

Expand Down
3 changes: 3 additions & 0 deletions samples/Tutorial/Favorites.fsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
type Event =
| Added of string
| Removed of string
// No IUnionContract or Codec required as we're using MemoryStore in this part
// interface TypeShape.UnionContract.IUnionContract
//let codec = Gardelloyd.NewtonsoftJson.Codec.Create<Event>()

let initial : string list = []
let evolve state = function
Expand Down
3 changes: 2 additions & 1 deletion samples/Tutorial/Todo.fsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#r "Newtonsoft.Json.dll"
#r "TypeShape.dll"
#r "Equinox.dll"
#r "Gardelloyd.dll"
#r "Gardelloyd.NewtonsoftJson.dll"
#r "FSharp.Control.AsyncSeq.dll"
#r "Microsoft.Azure.DocumentDb.Core.dll"
Expand All @@ -30,6 +31,7 @@ type Event =
| Cleared
| Compacted of CompactedInfo
interface TypeShape.UnionContract.IUnionContract
let codec = Gardelloyd.NewtonsoftJson.Codec.Create<Event>()

type State = { items : Todo list; nextId : int }
let initial = { items = []; nextId = 0 }
Expand Down Expand Up @@ -116,7 +118,6 @@ module Store =
let cacheStrategy = CachingStrategy.SlidingWindow (cache, TimeSpan.FromMinutes 20.)

module TodosCategory =
let codec = Gardelloyd.NewtonsoftJson.Codec.Create<Event>(Newtonsoft.Json.JsonSerializerSettings())
let access = AccessStrategy.Snapshot (isOrigin,compact)
let resolve = Resolver(Store.store, codec, fold, initial, Store.cacheStrategy, access=access).Resolve

Expand Down
3 changes: 1 addition & 2 deletions samples/Web/Startup.fs
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,7 @@ type Startup() =
| _ | Some (Memory _) ->
log.Fatal("Web App is using Volatile Store; Storage options: {options:l}", options)
Storage.MemoryStore.config (), log
let codecGen : Services.ICodecGen = Services.NewtonsoftJsonCodecGen() :> _
Services.register(services, storeConfig, storeLog, codecGen)
Services.register(services, storeConfig, storeLog)

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
static member Configure(app: IApplicationBuilder, env: IHostingEnvironment) : unit =
Expand Down
14 changes: 8 additions & 6 deletions src/Gardelloyd.NewtonsoftJson/NewtonsoftJson.fs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
namespace Gardelloyd.NewtonsoftJson

open Newtonsoft.Json
open Newtonsoft.Json.Serialization
open System
open System.IO
open System.Runtime.InteropServices

Expand Down Expand Up @@ -50,19 +52,22 @@ module Core =
/// `TypeShape.UnionContract.UnionContractEncoder` - if you need full control and/or have have your own codecs, see `Gardelloyd.Custom.Create` instead
type Codec private () =

static let defaultSettings = lazy Settings.Create()

/// <summary>
/// Generate a codec suitable for use with <c>Equinox.EventStore</c> or <c>Equinox.Cosmos</c>,
/// using the supplied `Newtonsoft.Json` <c>settings</c>.
/// The Event Type Names are inferred based on either explicit `DataMember(Name=` Attributes,
/// or (if unspecified) the Discriminated Union Case Name
/// The Union must be tagged with `interface TypeShape.UnionContract.IUnionContract` to signify this scheme applies.
/// See https://github.com/eiriktsarpalis/TypeShape/blob/master/tests/TypeShape.Tests/UnionContractTests.fs for example usage.</summary>
/// <param name="settings">Configuration to be used by the underlying <c>Newtonsoft.Json</c> Serializer when encoding/decoding.</param>
/// <param name="settings">Configuration to be used by the underlying <c>Newtonsoft.Json</c> Serializer when encoding/decoding. Defaults to same as `Settings.Create()`</param>
/// <param name="allowNullaryCases">Fail encoder generation if union contains nullary cases. Defaults to <c>true</c>.</param>
static member Create<'Union when 'Union :> TypeShape.UnionContract.IUnionContract>
( settings,
( ?settings,
[<Optional;DefaultParameterValue(null)>]?allowNullaryCases)
: Gardelloyd.IUnionEncoder<'Union,byte[]> =
let settings = match settings with Some x -> x | None -> defaultSettings.Value
let dataCodec =
TypeShape.UnionContract.UnionContractEncoder.Create<'Union,byte[]>(
new Core.BytesEncoder(settings),
Expand All @@ -75,10 +80,7 @@ type Codec private () =
member __.TryDecode encoded =
dataCodec.TryDecode { CaseName = encoded.EventType; Payload = encoded.Data } }

open Newtonsoft.Json.Serialization
open System

type Settings private () =
and Settings private () =
/// <summary>
/// Creates a default set of serializer settings used by Json serialization. When used with no args, same as JsonSerializerSettings.CreateDefault()
/// </summary>
Expand Down
9 changes: 2 additions & 7 deletions tests/Equinox.Cosmos.Integration/CosmosIntegration.fs
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,14 @@ open Domain
open Equinox.Cosmos
open Equinox.Cosmos.Integration.Infrastructure
open FSharp.UMX
open Gardelloyd.NewtonsoftJson
open Swensen.Unquote
open System.Threading
open System

let defaultSettings = Settings.CreateDefault()
let genCodec<'Union when 'Union :> TypeShape.UnionContract.IUnionContract>() =
Codec.Create<'Union>(defaultSettings)

module Cart =
let fold, initial = Domain.Cart.Folds.fold, Domain.Cart.Folds.initial
let snapshot = Domain.Cart.Folds.isOrigin, Domain.Cart.Folds.compact
let codec = genCodec<Domain.Cart.Events.Event>()
let codec = Domain.Cart.Events.codec
let createServiceWithoutOptimization connection batchSize log =
let store = createCosmosContext connection batchSize
let resolveStream = Resolver(store, codec, fold, initial, CachingStrategy.NoCaching).Resolve
Expand Down Expand Up @@ -45,7 +40,7 @@ module Cart =

module ContactPreferences =
let fold, initial = Domain.ContactPreferences.Folds.fold, Domain.ContactPreferences.Folds.initial
let codec = genCodec<Domain.ContactPreferences.Events.Event>()
let codec = Domain.ContactPreferences.Events.codec
let createServiceWithoutOptimization createGateway defaultBatchSize log _ignoreWindowSize _ignoreCompactionPredicate =
let gateway = createGateway defaultBatchSize
let resolveStream = Resolver(gateway, codec, fold, initial, CachingStrategy.NoCaching).Resolve
Expand Down
10 changes: 4 additions & 6 deletions tests/Equinox.Cosmos.Integration/JsonConverterTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

open Equinox.Cosmos
open FsCheck.Xunit
open Gardelloyd.NewtonsoftJson
open Newtonsoft.Json
open Swensen.Unquote
open System
Expand All @@ -14,9 +13,6 @@ type Union =
| B of Embedded
interface TypeShape.UnionContract.IUnionContract

let defaultSettings = Settings.Create()
let mkUnionEncoder () = Codec.Create<Union>(defaultSettings)

type EmbeddedString = { embed : string }
type EmbeddedDate = { embed : DateTime }
type EmbeddedDateTimeOffset = { embed : DateTimeOffset }
Expand All @@ -36,8 +32,10 @@ type US =
| SS of string
interface TypeShape.UnionContract.IUnionContract

let defaultSettings = Gardelloyd.NewtonsoftJson.Settings.CreateDefault()

type VerbatimUtf8Tests() =
let unionEncoder = mkUnionEncoder ()
let unionEncoder = Gardelloyd.NewtonsoftJson.Codec.Create()

[<Fact>]
let ``encodes correctly`` () =
Expand Down Expand Up @@ -102,7 +100,7 @@ module VerbatimeUtf8NullHandling =
test <@ ((e.d = null || e.d.Length = 0) && (des.d = null)) || System.Linq.Enumerable.SequenceEqual(e.d, des.d) @>

type Base64ZipUtf8Tests() =
let unionEncoder = mkUnionEncoder ()
let unionEncoder = Gardelloyd.NewtonsoftJson.Codec.Create(defaultSettings)

[<Fact>]
let ``serializes, achieving compression`` () =
Expand Down
Loading