diff --git a/src/Internal/Values/Envelope.elm b/src/Internal/Values/Envelope.elm index 96d09cb..dbbc815 100644 --- a/src/Internal/Values/Envelope.elm +++ b/src/Internal/Values/Envelope.elm @@ -1,5 +1,5 @@ module Internal.Values.Envelope exposing - ( Envelope(..), init + ( Envelope, init , map, mapMaybe, mapList , Settings, mapSettings, extractSettings , mapContext @@ -46,6 +46,7 @@ import Internal.Config.Default as Default import Internal.Tools.Decode as D import Internal.Tools.Encode as E import Internal.Values.Context as Context exposing (Context) +import Internal.Values.Settings as Settings import Json.Decode as D import Json.Encode as E @@ -55,26 +56,19 @@ need the same values. The Envelope type wraps settings, tokens and values around each data type so they can all enjoy those values without needing to explicitly define them in their type. -} -type Envelope a - = Envelope - { content : a - , context : Context - , settings : Settings - } +type alias Envelope a = + { content : a + , context : Context + , settings : Settings + } -{-| Custom settings that can be manipulated by the user. These serve as a -configuration for how the Elm SDK should behave. - -Custom settings are always part of the Envelope, allowing all functions to -behave under the user's preferred settings. - +{-| Settings value from +[Internal.Values.Settings](Internal-Values-Settings#Settings). Can be used to +manipulate the Matrix Vault. -} type alias Settings = - { currentVersion : String - , deviceName : String - , syncTime : Int - } + Settings.Settings {-| Decode an enveloped type from a JSON value. The decoder also imports any @@ -82,67 +76,25 @@ potential tokens, values and settings included in the JSON. -} decoder : D.Decoder a -> D.Decoder (Envelope a) decoder xDecoder = - D.map3 (\a b c -> Envelope { content = a, context = b, settings = c }) + D.map3 Envelope (D.field "content" xDecoder) (D.field "context" Context.decoder) - (D.field "settings" decoderSettings) - - -{-| Decode settings from a JSON value. --} -decoderSettings : D.Decoder Settings -decoderSettings = - D.map3 Settings - (D.opFieldWithDefault "currentVersion" Default.currentVersion D.string) - (D.opFieldWithDefault "deviceName" Default.deviceName D.string) - (D.opFieldWithDefault "syncTime" Default.syncTime D.int) + (D.field "settings" Settings.decoder) {-| Encode an enveloped type into a JSON value. The function encodes all non-standard settings, tokens and values. -} encode : (a -> E.Value) -> Envelope a -> E.Value -encode encodeX (Envelope data) = +encode encodeX data = E.object [ ( "content", encodeX data.content ) , ( "context", Context.encode data.context ) - , ( "settings", encodeSettings data.settings ) + , ( "settings", Settings.encode data.settings ) , ( "version", E.string Default.currentVersion ) ] -{-| Encode the settings into a JSON value. --} -encodeSettings : Settings -> E.Value -encodeSettings settings = - let - differentFrom : b -> b -> Maybe b - differentFrom defaultValue currentValue = - if currentValue == defaultValue then - Nothing - - else - Just currentValue - in - E.maybeObject - [ ( "currentVersion" - , settings.currentVersion - |> differentFrom Default.currentVersion - |> Maybe.map E.string - ) - , ( "deviceName" - , settings.deviceName - |> differentFrom Default.deviceName - |> Maybe.map E.string - ) - , ( "syncTime" - , settings.syncTime - |> differentFrom Default.syncTime - |> Maybe.map E.int - ) - ] - - {-| Map a function, then get its content. This is useful for getting information from a data type inside an Envelope. @@ -155,7 +107,7 @@ from a data type inside an Envelope. -} extract : (a -> b) -> Envelope a -> b -extract f (Envelope data) = +extract f data = f data.content @@ -165,7 +117,7 @@ This can be helpful if you have a UI that displays custom settings to a user. -} extractSettings : (Settings -> b) -> Envelope a -> b -extractSettings f (Envelope data) = +extractSettings f data = f data.settings @@ -186,15 +138,10 @@ from the [Internal.Config.Default](Internal-Config-Default) module. -} init : a -> Envelope a init x = - Envelope - { content = x - , context = Context.init - , settings = - { currentVersion = Default.currentVersion - , deviceName = Default.deviceName - , syncTime = Default.syncTime - } - } + { content = x + , context = Context.init + , settings = Settings.init + } {-| Map a function on the content of the Envelope. @@ -208,23 +155,18 @@ init x = -} map : (a -> b) -> Envelope a -> Envelope b -map f (Envelope data) = - Envelope - { content = f data.content - , context = data.context - , settings = data.settings - } +map f data = + { content = f data.content + , context = data.context + , settings = data.settings + } {-| Update the Context in the Envelope. -} mapContext : (Context -> Context) -> Envelope a -> Envelope a -mapContext f (Envelope data) = - Envelope - { content = data.content - , context = f data.context - , settings = data.settings - } +mapContext f data = + { data | context = f data.context } {-| Map the contents of a function, where the result is wrapped in a `List` @@ -279,23 +221,19 @@ mapMaybe f = -} mapSettings : (Settings -> Settings) -> Envelope a -> Envelope a -mapSettings f (Envelope data) = - Envelope - { content = data.content - , context = data.context - , settings = f data.settings - } +mapSettings f data = + { data | settings = f data.settings } toList : Envelope (List a) -> List (Envelope a) -toList (Envelope data) = +toList data = List.map - (\content -> map (always content) (Envelope data)) + (\content -> map (always content) data) data.content toMaybe : Envelope (Maybe a) -> Maybe (Envelope a) -toMaybe (Envelope data) = +toMaybe data = Maybe.map - (\content -> map (always content) (Envelope data)) + (\content -> map (always content) data) data.content diff --git a/src/Internal/Values/Event.elm b/src/Internal/Values/Event.elm index a23a1d4..722867b 100644 --- a/src/Internal/Values/Event.elm +++ b/src/Internal/Values/Event.elm @@ -1,9 +1,7 @@ module Internal.Values.Event exposing ( Event - , content, eventId, eventType, originServerTs, roomId, sender, stateKey , UnsignedData(..), age, prevContent, redactedBecause, transactionId , encode, decoder - , IEvent ) {-| @@ -17,11 +15,6 @@ of a room. @docs Event -## Get information - -@docs content, eventId, eventType, originServerTs, roomId, sender, stateKey - - ## Unsigned data @docs UnsignedData, age, prevContent, redactedBecause, transactionId @@ -37,7 +30,6 @@ import Internal.Config.Default as Default import Internal.Tools.Decode as D import Internal.Tools.Encode as E import Internal.Tools.Timestamp as Timestamp exposing (Timestamp) -import Internal.Values.Envelope as Envelope import Json.Decode as D import Json.Encode as E @@ -45,10 +37,6 @@ import Json.Encode as E {-| The Event type occurs everywhere on a user's timeline. -} type alias Event = - Envelope.Envelope IEvent - - -type alias IEvent = { content : E.Value , eventId : String , originServerTs : Timestamp @@ -67,7 +55,7 @@ type UnsignedData = UnsignedData { age : Maybe Int , prevContent : Maybe E.Value - , redactedBecause : Maybe IEvent + , redactedBecause : Maybe Event , transactionId : Maybe String } @@ -75,33 +63,13 @@ type UnsignedData {-| Get the event's age, if at all provided by the homeserver. -} age : Event -> Maybe Int -age envelope = - Envelope.extract - (\event -> - Maybe.andThen - (\(UnsignedData data) -> data.age) - event.unsigned - ) - envelope +age event = + Maybe.andThen (\(UnsignedData data) -> data.age) event.unsigned -{-| Determine the body of this event, as created by the user that sent it. --} -content : Event -> E.Value -content = - Envelope.extract .content - - -{-| Decode an Event from a JSON value. --} decoder : D.Decoder Event decoder = - Envelope.decoder decoderInternal - - -decoderInternal : D.Decoder IEvent -decoderInternal = - D.map8 IEvent + D.map8 Event (D.field "content" D.value) (D.field "eventId" D.string) (D.field "originServerTs" Timestamp.decoder) @@ -119,19 +87,14 @@ decoderUnsignedData = D.map4 (\a b c d -> UnsignedData { age = a, prevContent = b, redactedBecause = c, transactionId = d }) (D.opField "age" D.int) (D.opField "prevContent" D.value) - (D.opField "redactedBecause" (D.lazy (\_ -> decoderInternal))) + (D.opField "redactedBecause" (D.lazy (\_ -> decoder))) (D.opField "transactionId" D.string) {-| Encode an Event into a JSON value. -} encode : Event -> E.Value -encode = - Envelope.encode encodeInternal - - -encodeInternal : IEvent -> E.Value -encodeInternal event = +encode event = E.maybeObject [ ( "content", Just event.content ) , ( "eventId", Just <| E.string event.eventId ) @@ -152,113 +115,31 @@ encodeUnsignedData (UnsignedData data) = E.maybeObject [ ( "age", Maybe.map E.int data.age ) , ( "prevContent", data.prevContent ) - , ( "redactedBecause", Maybe.map encodeInternal data.redactedBecause ) + , ( "redactedBecause", Maybe.map encode data.redactedBecause ) , ( "transactionId", Maybe.map E.string data.transactionId ) ] -{-| Determine the globally unique identifier for an event. --} -eventId : Event -> String -eventId = - Envelope.extract .eventId - - -{-| To give a hint what the event's [content](#content) might look like, users -can use this eventType value to hint at how the JSON might be decoded. - -Standard examples of event types are `m.room.message`, `m.room.member` and -`me.noordstar.game.chess.move`. - --} -eventType : Event -> String -eventType = - Envelope.extract .eventType - - -{-| Determine the timestamp of at what time the event was originally received by -the original homeserver. - -Generally, this timestamp offers a relatively accurate indicator of when a -message was sent. However, this number isn't completely reliable! The timestamp -can be far in the past due to long network lag, and a (malicious) homeserver can -spoof this number to make it seem like something was sent ridiculously far in -the past - or even in the future. - --} -originServerTs : Event -> Timestamp -originServerTs = - Envelope.extract .originServerTs - - {-| Determine the previous `content` value for this event. This field is only a `Just value` if the event is a state event, and the Matrix Vault has permission to see the previous content. -} prevContent : Event -> Maybe E.Value -prevContent envelope = - Envelope.extract - (\event -> - Maybe.andThen - (\(UnsignedData data) -> data.prevContent) - event.unsigned - ) - envelope +prevContent event = + Maybe.andThen (\(UnsignedData data) -> data.prevContent) event.unsigned {-| If the event has been redacted, the homeserver can display the event that redacted it here. -} redactedBecause : Event -> Maybe Event -redactedBecause = - Envelope.mapMaybe - (\event -> - Maybe.andThen - (\(UnsignedData data) -> data.redactedBecause) - event.unsigned - ) - - -{-| Unique id assigned to the Matrix room. You can use this room id to reference -or look up rooms. --} -roomId : Event -> String -roomId = - Envelope.extract .roomId - - -{-| Determine the fully-qualified ID of the user who sent an event. --} -sender : Event -> String -sender = - Envelope.extract .sender - - -{-| Determine an event's state key. - -It is present if, and only if, the event is a _state_ event. The key makes the -piece of state unique in the room. Note that it is often `Just ""`. If it is not -present, its value is `Nothing`. - -State keys starting with an `@` are reserved for referencing user IDs, such as -room members. With the exception of a few events, state events set with a given -user'd ID as the state key can only be set by that user. - --} -stateKey : Event -> Maybe String -stateKey = - Envelope.extract .stateKey +redactedBecause event = + Maybe.andThen (\(UnsignedData data) -> data.redactedBecause) event.unsigned {-| If the user has sent this event to the homeserver, then the homeserver might display the original transaction id used for the event. -} transactionId : Event -> Maybe String -transactionId envelope = - Envelope.extract - (\event -> - Maybe.andThen - (\(UnsignedData data) -> data.transactionId) - event.unsigned - ) - envelope +transactionId event = + Maybe.andThen (\(UnsignedData data) -> data.transactionId) event.unsigned diff --git a/src/Internal/Values/Vault.elm b/src/Internal/Values/Vault.elm index fd6495b..72df622 100644 --- a/src/Internal/Values/Vault.elm +++ b/src/Internal/Values/Vault.elm @@ -11,5 +11,4 @@ import Internal.Values.Envelope as Envelope {-| This is the Vault type. -} -type alias Vault = - Envelope.Envelope {} +type alias Vault = () diff --git a/src/Matrix/Event.elm b/src/Matrix/Event.elm index 5d9190f..da6e114 100644 --- a/src/Matrix/Event.elm +++ b/src/Matrix/Event.elm @@ -32,6 +32,7 @@ information isn't always applicable, it doesn't always exist. -} +import Internal.Values.Envelope as Envelope import Internal.Values.Event as Internal import Json.Encode import Time @@ -56,14 +57,14 @@ type alias Event = -} content : Event -> Json.Encode.Value content (Event event) = - Internal.content event + Envelope.extract .content event {-| Determine the globally unique identifier for an event. -} eventId : Event -> String eventId (Event event) = - Internal.eventId event + Envelope.extract .eventId event {-| To give a hint what the event's [content](#content) might look like, users @@ -75,7 +76,7 @@ Standard examples of event types are `m.room.message`, `m.room.member` and -} eventType : Event -> String eventType (Event event) = - Internal.eventType event + Envelope.extract .eventType event {-| Determine the timestamp of at what time the event was originally received by @@ -90,7 +91,7 @@ the past - or even in the future. -} originServerTs : Event -> Time.Posix originServerTs (Event event) = - Internal.originServerTs event + Envelope.extract .originServerTs event {-| Determine the previous `content` value for this event. This field is only a @@ -99,7 +100,7 @@ to see the previous content. -} previousContent : Event -> Maybe Json.Encode.Value previousContent (Event event) = - Internal.prevContent event + Envelope.extract Internal.prevContent event {-| If the event has been redacted, the homeserver can display the event that @@ -107,7 +108,7 @@ redacted it here. -} redactedBecause : Event -> Maybe Event redactedBecause (Event event) = - Internal.redactedBecause event + Envelope.mapMaybe Internal.redactedBecause event |> Maybe.map Event @@ -116,14 +117,14 @@ or look up rooms. -} roomId : Event -> String roomId (Event event) = - Internal.roomId event + Envelope.extract .roomId event {-| Determine the fully-qualified ID of the user who sent an event. -} sender : Event -> String sender (Event event) = - Internal.sender event + Envelope.extract .sender event {-| Determine an event's state key. @@ -139,4 +140,4 @@ user'd ID as the state key can only be set by that user. -} stateKey : Event -> Maybe String stateKey (Event event) = - Internal.stateKey event + Envelope.extract .stateKey event diff --git a/src/Types.elm b/src/Types.elm index 36da9e0..242bea7 100644 --- a/src/Types.elm +++ b/src/Types.elm @@ -16,6 +16,7 @@ safely access all exposed data types without risking to create circular imports. -} +import Internal.Values.Envelope as Envelope import Internal.Values.Event as Event import Internal.Values.Vault as Vault @@ -23,10 +24,10 @@ import Internal.Values.Vault as Vault {-| Opaque type for Matrix Event -} type Event - = Event Event.Event + = Event (Envelope.Envelope Event.Event) {-| Opaque type for Matrix Vault -} type Vault - = Vault Vault.Vault + = Vault (Envelope.Envelope Vault.Vault) diff --git a/tests/Envelope.elm b/tests/Envelope.elm deleted file mode 100644 index 85dcdb1..0000000 --- a/tests/Envelope.elm +++ /dev/null @@ -1,42 +0,0 @@ -module Envelope exposing (..) - -import Context as TestContext -import Fuzz exposing (Fuzzer) -import Internal.Config.Default as Default -import Internal.Values.Envelope exposing (Envelope(..), Settings) -import Test exposing (..) - - -fuzzer : Fuzzer a -> Fuzzer (Envelope a) -fuzzer fuzz = - Fuzz.map3 - (\content context settings -> - Envelope - { content = content - , context = context - , settings = settings - } - ) - fuzz - TestContext.fuzzer - fuzzerSettings - - -fuzzerSettings : Fuzzer Settings -fuzzerSettings = - Fuzz.map3 Settings - (Fuzz.oneOf - [ Fuzz.constant Default.currentVersion - , Fuzz.string - ] - ) - (Fuzz.oneOf - [ Fuzz.constant Default.deviceName - , Fuzz.string - ] - ) - (Fuzz.oneOf - [ Fuzz.constant Default.syncTime - , Fuzz.int - ] - ) diff --git a/tests/Event.elm b/tests/Event.elm deleted file mode 100644 index e7cd84d..0000000 --- a/tests/Event.elm +++ /dev/null @@ -1,89 +0,0 @@ -module Event exposing (..) - -import Envelope as TestEnvelope -import Expect -import Fuzz exposing (Fuzzer) -import Iddict as TestIddict -import Internal.Tools.Iddict as Iddict -import Internal.Tools.Timestamp as Timestamp -import Internal.Values.Envelope as Envelope -import Internal.Values.Event as Event -import Json.Decode as D -import Json.Encode as E -import Test exposing (..) -import Timestamp as TestTimestamp - - -{-| Example values that can be used for arbitrary JSON values --} -valueFuzzer : Fuzzer E.Value -valueFuzzer = - Fuzz.oneOf - [ Fuzz.map (Iddict.encode E.int) (TestIddict.fuzzer Fuzz.int) - , Fuzz.map Timestamp.encode TestTimestamp.fuzzer - , Fuzz.map E.int Fuzz.int - , Fuzz.map E.string Fuzz.string - , Fuzz.map (E.list E.int) (Fuzz.list Fuzz.int) - , Fuzz.map (E.list E.string) (Fuzz.list Fuzz.string) - , Fuzz.map Event.encode (Fuzz.lazy (\_ -> TestEnvelope.fuzzer fuzzer)) - ] - - -fuzzer : Fuzzer Event.IEvent -fuzzer = - Fuzz.map8 - (\c ei et o r se sk u -> - { content = c - , eventId = ei - , eventType = et - , originServerTs = o - , roomId = r - , sender = se - , stateKey = sk - , unsigned = u - } - ) - valueFuzzer - Fuzz.string - Fuzz.string - TestTimestamp.fuzzer - Fuzz.string - Fuzz.string - (Fuzz.maybe Fuzz.string) - (Fuzz.maybe unsignedDataFuzzer) - - -fuzzerFull : Fuzzer Event.Event -fuzzerFull = - TestEnvelope.fuzzer fuzzer - - -unsignedDataFuzzer : Fuzzer Event.UnsignedData -unsignedDataFuzzer = - Fuzz.map4 - (\age prev redact trans -> - Event.UnsignedData - { age = age - , prevContent = prev - , redactedBecause = redact - , transactionId = trans - } - ) - (Fuzz.maybe Fuzz.int) - (Fuzz.maybe valueFuzzer) - (Fuzz.maybe <| Fuzz.lazy (\_ -> fuzzer)) - (Fuzz.maybe Fuzz.string) - - -json : Test -json = - describe "JSON tests" - [ fuzz fuzzerFull - "JSON encode + JSON decode" - (\event -> - event - |> Event.encode - |> D.decodeValue Event.decoder - |> Expect.equal (Ok event) - ) - ] diff --git a/tests/Timestamp.elm b/tests/Test/Tools/Timestamp.elm similarity index 82% rename from tests/Timestamp.elm rename to tests/Test/Tools/Timestamp.elm index f8b7dd4..e10a64e 100644 --- a/tests/Timestamp.elm +++ b/tests/Test/Tools/Timestamp.elm @@ -1,4 +1,4 @@ -module Timestamp exposing (..) +module Test.Tools.Timestamp exposing (..) import Fuzz exposing (Fuzzer) import Internal.Tools.Timestamp exposing (Timestamp) diff --git a/tests/Context.elm b/tests/Test/Values/Context.elm similarity index 98% rename from tests/Context.elm rename to tests/Test/Values/Context.elm index f06c911..1b6dc55 100644 --- a/tests/Context.elm +++ b/tests/Test/Values/Context.elm @@ -1,4 +1,4 @@ -module Context exposing (..) +module Test.Values.Context exposing (..) import Expect import Fuzz exposing (Fuzzer) diff --git a/tests/Test/Values/Envelope.elm b/tests/Test/Values/Envelope.elm new file mode 100644 index 0000000..deb5036 --- /dev/null +++ b/tests/Test/Values/Envelope.elm @@ -0,0 +1,65 @@ +module Test.Values.Envelope exposing (..) + +import Expect +import Fuzz exposing (Fuzzer) +import Internal.Config.Default as Default +import Internal.Values.Envelope as Envelope exposing (Envelope) +import Json.Decode as D +import Json.Encode as E +import Test exposing (..) +import Test.Values.Context as TestContext +import Test.Values.Settings as TestSettings + + +fuzzer : Fuzzer a -> Fuzzer (Envelope a) +fuzzer fuz = + Fuzz.map3 Envelope + fuz + TestContext.fuzzer + TestSettings.fuzzer + + +suite : Test +suite = + describe "Envelope value" + [ describe "init" + [ describe "Default settings" + [ fuzz Fuzz.string + "currentVersion" + (\s -> + s + |> Envelope.init + |> Envelope.extractSettings .currentVersion + |> Expect.equal Default.currentVersion + ) + , fuzz Fuzz.string + "deviceName" + (\s -> + s + |> Envelope.init + |> Envelope.extractSettings .deviceName + |> Expect.equal Default.deviceName + ) + , fuzz Fuzz.string + "syncTime" + (\s -> + s + |> Envelope.init + |> Envelope.extractSettings .syncTime + |> Expect.equal Default.syncTime + ) + ] + ] + , describe "JSON" + [ fuzz2 (fuzzer Fuzz.string) + Fuzz.int + "JSON encode -> JSON decode" + (\envelope indent -> + envelope + |> Envelope.encode E.string + |> E.encode indent + |> D.decodeString (Envelope.decoder D.string) + |> Expect.equal (Ok envelope) + ) + ] + ] diff --git a/tests/Test/Values/Event.elm b/tests/Test/Values/Event.elm new file mode 100644 index 0000000..89c80a5 --- /dev/null +++ b/tests/Test/Values/Event.elm @@ -0,0 +1,50 @@ +module Test.Values.Event exposing (..) + +import Fuzz exposing (Fuzzer) +import Internal.Values.Event as Event exposing (Event) +import Json.Encode as E +import Test exposing (..) +import Test.Tools.Timestamp as TestTimestamp + + +fuzzer : Fuzzer Event +fuzzer = + Fuzz.map8 Event + valueFuzzer + Fuzz.string + TestTimestamp.fuzzer + Fuzz.string + Fuzz.string + (Fuzz.maybe Fuzz.string) + Fuzz.string + (Fuzz.maybe unsignedDataFuzzer) + + +unsignedDataFuzzer : Fuzzer Event.UnsignedData +unsignedDataFuzzer = + Fuzz.map4 + (\age prev redact trans -> + Event.UnsignedData + { age = age + , prevContent = prev + , redactedBecause = redact + , transactionId = trans + } + ) + (Fuzz.maybe Fuzz.int) + (Fuzz.maybe valueFuzzer) + (Fuzz.maybe <| Fuzz.lazy (\_ -> fuzzer)) + (Fuzz.maybe Fuzz.string) + + +{-| Example values that can be used for arbitrary JSON values +-} +valueFuzzer : Fuzzer E.Value +valueFuzzer = + Fuzz.oneOf + [ Fuzz.map E.int Fuzz.int + , Fuzz.map E.string Fuzz.string + , Fuzz.map (E.list E.int) (Fuzz.list Fuzz.int) + , Fuzz.map (E.list E.string) (Fuzz.list Fuzz.string) + , Fuzz.map Event.encode (Fuzz.lazy (\_ -> fuzzer)) + ] diff --git a/tests/Vault.elm b/tests/Vault.elm index 31faa04..35a2842 100644 --- a/tests/Vault.elm +++ b/tests/Vault.elm @@ -12,7 +12,7 @@ import Types fuzzer : Fuzzer Matrix.Vault fuzzer = - Fuzz.constant <| Types.Vault <| Envelope.init {} + Fuzz.constant <| Types.Vault <| Envelope.init () settings : Test