Skip to content

Commit

Permalink
Fix null/empty array edge cases fixes #118
Browse files Browse the repository at this point in the history
  • Loading branch information
bartelink committed Mar 27, 2019
1 parent fe74679 commit b7fdb7a
Show file tree
Hide file tree
Showing 5 changed files with 37 additions and 8 deletions.
11 changes: 9 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,13 @@ The `Unreleased` section name is replaced by the expected version of next releas
### Removed
### Fixed

- (Not actually fixed yet, RELEASE BLOCKER) Rendering to Kafka, Cosmos using `dotnet new eqxsync`, `dotnet new eqxprojector` yields invalid json when `Meta` is `[||]` [#18](https://github.com/jet/equinox/issues/118)
<a name="2.0.0"></a>
<a name="2.0.0-preview3"></a>
## [2.0.0-preview3] - 2019-03-27

### Fixed

- Reading `null` from Equinox.Cosmos and then writing that to Kafka yielded invalid json [#18](https://github.com/jet/equinox/issues/118)

<a name="2.0.0"></a>
<a name="2.0.0-preview2"></a>
Expand Down Expand Up @@ -107,7 +113,8 @@ The `Unreleased` section name is replaced by the expected version of next releas

(For information pertaining to earlier releases, see release notes in https://github.com/jet/equinox/releases and/or can someone please add it!)

[Unreleased]: https://github.com/jet/equinox/compare/2.0.0-preview2...HEAD
[Unreleased]: https://github.com/jet/equinox/compare/2.0.0-preview3...HEAD
[2.0.0-preview3]: https://github.com/jet/equinox/compare/2.0.0-preview2...2.0.0-preview3
[2.0.0-preview2]: https://github.com/jet/equinox/compare/2.0.0-preview1...2.0.0-preview2
[2.0.0-preview1]: https://github.com/jet/equinox/compare/1.1.0-preview2...2.0.0-preview1
[1.1.0-preview2]: https://github.com/jet/equinox/compare/1.1.0-preview1...1.1.0-preview2
Expand Down
1 change: 0 additions & 1 deletion samples/Store/Integration/CodecIntegration.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
module Samples.Store.Integration.CodecIntegration

open Domain
open FSharp.UMX
open Swensen.Unquote
open TypeShape.UnionContract

Expand Down
4 changes: 2 additions & 2 deletions src/Equinox.Cosmos/CosmosInternalJson.fs
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ type VerbatimUtf8JsonConverter() =

override __.ReadJson(reader, _, _, _) =
let token = JToken.Load reader
if token = null then null
if token.Type = JTokenType.Null then null
else token |> string |> enc.GetBytes |> box

override __.CanConvert(objectType) =
typeof<byte[]>.Equals(objectType)

override __.WriteJson(writer, value, serializer) =
let array = value :?> byte[]
if array = null then serializer.Serialize(writer, null)
if array = null || array.Length = 0 then serializer.Serialize(writer, null)
else writer.WriteRawValue(enc.GetString(array))

open System.IO
Expand Down
5 changes: 2 additions & 3 deletions src/Equinox.Projection/RenderedEvent.fs
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,11 @@ module Codec =
/// Timestamp of original write
t: DateTimeOffset // ISO 8601

/// Event body, as UTF-8 encoded json ready to be injected into the Json being rendered for DocDb
// TOCONSIDER if we don't inline `h`, we need to inline this
/// Event body, as UTF-8 encoded json ready to be injected directly into the Json being rendered
[<JsonConverter(typeof<Equinox.Cosmos.Internal.Json.VerbatimUtf8JsonConverter>)>]
d: byte[] // required

/// Optional metadata, as UTF-8 encoded json, ready to emit directly (null, not written if missing)
/// Optional metadata, as UTF-8 encoded json, ready to emit directly (entire field is not written if value is null)
[<JsonConverter(typeof<Equinox.Cosmos.Internal.Json.VerbatimUtf8JsonConverter>)>]
[<JsonProperty(Required=Required.Default, NullValueHandling=NullValueHandling.Ignore)>]
m: byte[] }
24 changes: 24 additions & 0 deletions tests/Equinox.Cosmos.Integration/JsonConverterTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,30 @@ type VerbatimUtf8Tests() =
// let decoded = sEncoder.TryDecode encoded |> Option.get
// test <@ x = decoded @>

module VerbatimeUtf8NullHandling =
type [<NoEquality; NoComparison>] EventHolderWithAndWithoutRequired =
{ /// Event body, as UTF-8 encoded json ready to be injected directly into the Json being rendered
[<JsonConverter(typeof<Equinox.Cosmos.Internal.Json.VerbatimUtf8JsonConverter>)>]
d: byte[] // required

/// Optional metadata, as UTF-8 encoded json, ready to emit directly (entire field is not written if value is null)
[<JsonConverter(typeof<Equinox.Cosmos.Internal.Json.VerbatimUtf8JsonConverter>)>]
[<JsonProperty(Required=Required.Default, NullValueHandling=NullValueHandling.Ignore)>]
m: byte[] }

let values : obj[][] =
[| [| null |]
[| [||] |]
[| System.Text.Encoding.UTF8.GetBytes "{}" |] |]

[<Theory; MemberData "values">]
let ``roundtrips nulls and empties consistently`` value =
let e : EventHolderWithAndWithoutRequired = { d = value; m = value }
let ser = JsonConvert.SerializeObject(e)
let des = JsonConvert.DeserializeObject<EventHolderWithAndWithoutRequired>(ser)
test <@ ((e.m = null || e.m.Length = 0) && (des.m = null)) || System.Linq.Enumerable.SequenceEqual(e.m, des.m) @>
test <@ ((e.d = null || e.d.Length = 0) && (des.d = null)) || System.Linq.Enumerable.SequenceEqual(e.d, des.d) @>

type Base64ZipUtf8Tests() =
let unionEncoder = mkUnionEncoder ()

Expand Down

0 comments on commit b7fdb7a

Please sign in to comment.