Compare commits
7 Commits
3b927dc460
...
1d3ceb9b2d
Author | SHA1 | Date |
---|---|---|
|
1d3ceb9b2d | |
|
d9d5760928 | |
|
5bd95699d2 | |
|
447a18ab04 | |
|
87a5919921 | |
|
7254fcfaa4 | |
|
b3479cf2c9 |
2
elm.json
2
elm.json
|
@ -8,6 +8,7 @@
|
||||||
"Matrix",
|
"Matrix",
|
||||||
"Matrix.Settings",
|
"Matrix.Settings",
|
||||||
"Internal.Config.Default",
|
"Internal.Config.Default",
|
||||||
|
"Internal.Config.Leaks",
|
||||||
"Internal.Config.Text",
|
"Internal.Config.Text",
|
||||||
"Internal.Tools.Decode",
|
"Internal.Tools.Decode",
|
||||||
"Internal.Tools.Encode",
|
"Internal.Tools.Encode",
|
||||||
|
@ -15,6 +16,7 @@
|
||||||
"Internal.Tools.Iddict",
|
"Internal.Tools.Iddict",
|
||||||
"Internal.Tools.Timestamp",
|
"Internal.Tools.Timestamp",
|
||||||
"Internal.Tools.VersionControl",
|
"Internal.Tools.VersionControl",
|
||||||
|
"Internal.Values.Context",
|
||||||
"Internal.Values.Envelope",
|
"Internal.Values.Envelope",
|
||||||
"Internal.Values.Vault",
|
"Internal.Values.Vault",
|
||||||
"Types"
|
"Types"
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
module Internal.Config.Leaks exposing (accessToken, baseUrl, transaction, versions)
|
||||||
|
|
||||||
|
{-|
|
||||||
|
|
||||||
|
|
||||||
|
# Leaks module
|
||||||
|
|
||||||
|
The Elm compiler is quite picky when it comes to handling edge cases, which may
|
||||||
|
occasionally result in requiring us to insert values in impossible states.
|
||||||
|
|
||||||
|
This module offers placeholders for those times. The placeholder values are
|
||||||
|
intentionally called "leaks", because they should be used carefully: a wrongful
|
||||||
|
implementation might cause unexpected behaviour, vulnerabilities or even
|
||||||
|
security risks!
|
||||||
|
|
||||||
|
You should not use this module unless you know what you're doing. That is:
|
||||||
|
|
||||||
|
- By exclusively using leaking values in opaque types so a user cannot
|
||||||
|
accidentally reach an impossible state
|
||||||
|
- By exclusively using leaking values in cases where the compiler is the only
|
||||||
|
reason that the leaking value needs to be used
|
||||||
|
- By exclusively using leaking values if there is no way to circumvent the
|
||||||
|
compiler with a reasonable method.
|
||||||
|
|
||||||
|
One such example would be to turn an `Maybe Int` into an `Int` if you already
|
||||||
|
know 100% sure that the value isn't `Nothing`.
|
||||||
|
|
||||||
|
Just 5 |> Maybe.withDefault Leaks.number
|
||||||
|
|
||||||
|
@docs accessToken, baseUrl, transaction, versions
|
||||||
|
|
||||||
|
-}
|
||||||
|
|
||||||
|
|
||||||
|
{-| Placeholder access token.
|
||||||
|
-}
|
||||||
|
accessToken : String
|
||||||
|
accessToken =
|
||||||
|
"elm-sdk-placeholder-access-token-leaks"
|
||||||
|
|
||||||
|
|
||||||
|
{-| Placeholder base URL.
|
||||||
|
-}
|
||||||
|
baseUrl : String
|
||||||
|
baseUrl =
|
||||||
|
"elm-sdk-placeholder-baseurl-leaks.example.org"
|
||||||
|
|
||||||
|
|
||||||
|
{-| Placeholder transaction id.
|
||||||
|
-}
|
||||||
|
transaction : String
|
||||||
|
transaction =
|
||||||
|
"elm-sdk-placeholder-transaction-leaks"
|
||||||
|
|
||||||
|
|
||||||
|
{-| Placeholder versions list.
|
||||||
|
-}
|
||||||
|
versions : List String
|
||||||
|
versions =
|
||||||
|
[ "elm-sdk-placeholder-versions-leaks" ]
|
|
@ -7,12 +7,20 @@ module Internal.Config.Text exposing
|
||||||
{-| Throughout the Elm SDK, there are lots of pieces of text being used for
|
{-| Throughout the Elm SDK, there are lots of pieces of text being used for
|
||||||
various purposes. Some of these are:
|
various purposes. Some of these are:
|
||||||
|
|
||||||
- To log on what is happening during an API call.
|
- To log what is happening during an API call.
|
||||||
- To fail with custom decoder errors.
|
- To fail with custom decoder errors.
|
||||||
- To describe custom values in a human readable format.
|
- To describe custom values in a human readable format.
|
||||||
|
|
||||||
All magic values of text are gathered in this module, to form a monolithic
|
All magic values of text are gathered in this module, to form a monolithic
|
||||||
source of text. This allows people to learn more about the Elm SDK, and it
|
source of text. This allows people to learn more about the Elm SDK, and it
|
||||||
|
offers room for future translations.
|
||||||
|
|
||||||
|
Optionally, developers can even consider taking the values of some of these
|
||||||
|
variables to interpret them automatically when they appear as logs on the other
|
||||||
|
side. This could be used to automatically detect when the Vault is failing to
|
||||||
|
authenticate, for example, so that a new login screen can be shown. **WARNING:**
|
||||||
|
This is a risky feature, keep in mind that even a patch update might break this!
|
||||||
|
You should only do this if you know what you're doing.
|
||||||
|
|
||||||
|
|
||||||
## API Versions
|
## API Versions
|
||||||
|
@ -94,14 +102,14 @@ versionsFailedToDecode =
|
||||||
"Matrix API returned an invalid version list"
|
"Matrix API returned an invalid version list"
|
||||||
|
|
||||||
|
|
||||||
{-| Logs when the Vault remembers how to communicate with the Matrix homeserver
|
{-| Logs when the Vault remembers how to communicate with the Matrix homeserver.
|
||||||
-}
|
-}
|
||||||
versionsFoundLocally : String
|
versionsFoundLocally : String
|
||||||
versionsFoundLocally =
|
versionsFoundLocally =
|
||||||
"Found locally cached version list"
|
"Found locally cached version list"
|
||||||
|
|
||||||
|
|
||||||
{-| Logs when the Matrix API has returned how to best communicate with them
|
{-| Logs when the Matrix API has returned how to best communicate with them.
|
||||||
-}
|
-}
|
||||||
versionsReceived : String
|
versionsReceived : String
|
||||||
versionsReceived =
|
versionsReceived =
|
||||||
|
|
|
@ -3,11 +3,11 @@ module Internal.Tools.Timestamp exposing
|
||||||
, encode, decoder
|
, encode, decoder
|
||||||
)
|
)
|
||||||
|
|
||||||
{-| The Timestamp module is a simplification of the Timetsamp as delivered by
|
{-| The Timestamp module is a simplification of the Timestamp as delivered by
|
||||||
elm/time. This module offers ways to work with the timestamp in meaningful ways.
|
elm/time. This module offers ways to work with the timestamp in meaningful ways.
|
||||||
|
|
||||||
|
|
||||||
## Timetstamp
|
## Timestamp
|
||||||
|
|
||||||
@docs Timestamp
|
@docs Timestamp
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ import Json.Encode as E
|
||||||
import Time
|
import Time
|
||||||
|
|
||||||
|
|
||||||
{-| The Timetstamp data type representing a moment in time.
|
{-| The Timestamp data type representing a moment in time.
|
||||||
-}
|
-}
|
||||||
type alias Timestamp =
|
type alias Timestamp =
|
||||||
Time.Posix
|
Time.Posix
|
||||||
|
|
|
@ -0,0 +1,196 @@
|
||||||
|
module Internal.Values.Context exposing
|
||||||
|
( Context, init, encode, decoder
|
||||||
|
, APIContext, apiFormat
|
||||||
|
, setAccessToken, getAccessToken
|
||||||
|
, setBaseUrl, getBaseUrl
|
||||||
|
, setTransaction, getTransaction
|
||||||
|
, setVersions, getVersions
|
||||||
|
)
|
||||||
|
|
||||||
|
{-| The Context is the set of variables that the user (mostly) cannot control.
|
||||||
|
The Context contains tokens, values and other bits that the Vault receives from
|
||||||
|
the Matrix API.
|
||||||
|
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
@docs Context, init, encode, decoder
|
||||||
|
|
||||||
|
|
||||||
|
## APIContext
|
||||||
|
|
||||||
|
Once the API starts needing information, that's when we use the APIContext type
|
||||||
|
to build the right environment for the API communication to work with.
|
||||||
|
|
||||||
|
@docs APIContext, apiFormat
|
||||||
|
|
||||||
|
Once the APIContext is ready, there's helper functions for each piece of
|
||||||
|
information that can be inserted.
|
||||||
|
|
||||||
|
|
||||||
|
### Access token
|
||||||
|
|
||||||
|
@docs setAccessToken, getAccessToken
|
||||||
|
|
||||||
|
|
||||||
|
### Base URL
|
||||||
|
|
||||||
|
@docs setBaseUrl, getBaseUrl
|
||||||
|
|
||||||
|
|
||||||
|
### Transaction id
|
||||||
|
|
||||||
|
@docs setTransaction, getTransaction
|
||||||
|
|
||||||
|
|
||||||
|
### Versions
|
||||||
|
|
||||||
|
@docs setVersions, getVersions
|
||||||
|
|
||||||
|
-}
|
||||||
|
|
||||||
|
import Internal.Config.Leaks as L
|
||||||
|
import Internal.Tools.Decode as D
|
||||||
|
import Internal.Tools.Encode as E
|
||||||
|
import Json.Decode as D
|
||||||
|
import Json.Encode as E
|
||||||
|
|
||||||
|
|
||||||
|
{-| The Context type stores all the information in the Vault. This data type is
|
||||||
|
static and hence can be passed on easily.
|
||||||
|
-}
|
||||||
|
type alias Context =
|
||||||
|
{ accessToken : Maybe String
|
||||||
|
, baseUrl : Maybe String
|
||||||
|
, password : Maybe String
|
||||||
|
, refreshToken : Maybe String
|
||||||
|
, username : Maybe String
|
||||||
|
, transaction : Maybe String
|
||||||
|
, versions : Maybe (List String)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{-| The APIContext is a separate type that uses a phantom type to trick the
|
||||||
|
compiler into requiring values to be present. This data type is used to gather
|
||||||
|
the right variables (like an access token) before accessing the Matrix API.
|
||||||
|
-}
|
||||||
|
type APIContext ph
|
||||||
|
= APIContext
|
||||||
|
{ accessToken : String
|
||||||
|
, baseUrl : String
|
||||||
|
, context : Context
|
||||||
|
, transaction : String
|
||||||
|
, versions : List String
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{-| Create an unformatted APIContext type.
|
||||||
|
-}
|
||||||
|
apiFormat : Context -> APIContext {}
|
||||||
|
apiFormat context =
|
||||||
|
APIContext
|
||||||
|
{ accessToken = context.accessToken |> Maybe.withDefault L.accessToken
|
||||||
|
, baseUrl = context.baseUrl |> Maybe.withDefault L.baseUrl
|
||||||
|
, context = context
|
||||||
|
, transaction = context.transaction |> Maybe.withDefault L.transaction
|
||||||
|
, versions = context.versions |> Maybe.withDefault L.versions
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{-| Decode a Context type from a JSON value.
|
||||||
|
-}
|
||||||
|
decoder : D.Decoder Context
|
||||||
|
decoder =
|
||||||
|
D.map7 Context
|
||||||
|
(D.opField "accessToken" D.string)
|
||||||
|
(D.opField "baseUrl" D.string)
|
||||||
|
(D.opField "password" D.string)
|
||||||
|
(D.opField "refreshToken" D.string)
|
||||||
|
(D.opField "username" D.string)
|
||||||
|
(D.opField "transaction" D.string)
|
||||||
|
(D.opField "versions" (D.list D.string))
|
||||||
|
|
||||||
|
|
||||||
|
{-| Encode a Context type into a JSON value.
|
||||||
|
-}
|
||||||
|
encode : Context -> E.Value
|
||||||
|
encode context =
|
||||||
|
E.maybeObject
|
||||||
|
[ ( "accessToken", Maybe.map E.string context.accessToken )
|
||||||
|
, ( "baseUrl", Maybe.map E.string context.baseUrl )
|
||||||
|
, ( "password", Maybe.map E.string context.password )
|
||||||
|
, ( "refreshToken", Maybe.map E.string context.refreshToken )
|
||||||
|
, ( "username", Maybe.map E.string context.username )
|
||||||
|
, ( "transaction", Maybe.map E.string context.transaction )
|
||||||
|
, ( "versions", Maybe.map (E.list E.string) context.versions )
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
{-| A basic, untouched version of the Context, containing no information.
|
||||||
|
-}
|
||||||
|
init : Context
|
||||||
|
init =
|
||||||
|
{ accessToken = Nothing
|
||||||
|
, baseUrl = Nothing
|
||||||
|
, refreshToken = Nothing
|
||||||
|
, password = Nothing
|
||||||
|
, username = Nothing
|
||||||
|
, transaction = Nothing
|
||||||
|
, versions = Nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{-| Get an inserted access token.
|
||||||
|
-}
|
||||||
|
getAccessToken : APIContext { a | accessToken : () } -> String
|
||||||
|
getAccessToken (APIContext c) =
|
||||||
|
c.accessToken
|
||||||
|
|
||||||
|
|
||||||
|
{-| Insert an access token into the APIContext.
|
||||||
|
-}
|
||||||
|
setAccessToken : String -> APIContext a -> APIContext { a | accessToken : () }
|
||||||
|
setAccessToken value (APIContext c) =
|
||||||
|
APIContext { c | accessToken = value }
|
||||||
|
|
||||||
|
|
||||||
|
{-| Get an inserted base URL.
|
||||||
|
-}
|
||||||
|
getBaseUrl : APIContext { a | baseUrl : () } -> String
|
||||||
|
getBaseUrl (APIContext c) =
|
||||||
|
c.baseUrl
|
||||||
|
|
||||||
|
|
||||||
|
{-| Insert a base URL into the APIContext.
|
||||||
|
-}
|
||||||
|
setBaseUrl : String -> APIContext a -> APIContext { a | baseUrl : () }
|
||||||
|
setBaseUrl value (APIContext c) =
|
||||||
|
APIContext { c | baseUrl = value }
|
||||||
|
|
||||||
|
|
||||||
|
{-| Get an inserted transaction id.
|
||||||
|
-}
|
||||||
|
getTransaction : APIContext { a | transaction : () } -> String
|
||||||
|
getTransaction (APIContext c) =
|
||||||
|
c.transaction
|
||||||
|
|
||||||
|
|
||||||
|
{-| Insert a transaction id into the APIContext.
|
||||||
|
-}
|
||||||
|
setTransaction : String -> APIContext a -> APIContext { a | transaction : () }
|
||||||
|
setTransaction value (APIContext c) =
|
||||||
|
APIContext { c | transaction = value }
|
||||||
|
|
||||||
|
|
||||||
|
{-| Get an inserted versions list.
|
||||||
|
-}
|
||||||
|
getVersions : APIContext { a | versions : () } -> List String
|
||||||
|
getVersions (APIContext c) =
|
||||||
|
c.versions
|
||||||
|
|
||||||
|
|
||||||
|
{-| Insert a versions list into the APIContext.
|
||||||
|
-}
|
||||||
|
setVersions : List String -> APIContext a -> APIContext { a | versions : () }
|
||||||
|
setVersions value (APIContext c) =
|
||||||
|
APIContext { c | versions = value }
|
|
@ -1,7 +1,8 @@
|
||||||
module Internal.Values.Envelope exposing
|
module Internal.Values.Envelope exposing
|
||||||
( Envelope, init
|
( Envelope, init
|
||||||
, map, mapMaybe
|
, map, mapMaybe, mapList
|
||||||
, Settings, mapSettings, extractSettings
|
, Settings, mapSettings, extractSettings
|
||||||
|
, mapContext
|
||||||
, getContent, extract
|
, getContent, extract
|
||||||
, encode, decoder
|
, encode, decoder
|
||||||
)
|
)
|
||||||
|
@ -17,7 +18,7 @@ settings that can be adjusted manually.
|
||||||
|
|
||||||
## Manipulate
|
## Manipulate
|
||||||
|
|
||||||
@docs map, mapMaybe
|
@docs map, mapMaybe, mapList
|
||||||
|
|
||||||
|
|
||||||
## Settings
|
## Settings
|
||||||
|
@ -25,6 +26,11 @@ settings that can be adjusted manually.
|
||||||
@docs Settings, mapSettings, extractSettings
|
@docs Settings, mapSettings, extractSettings
|
||||||
|
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
@docs mapContext
|
||||||
|
|
||||||
|
|
||||||
## Extract
|
## Extract
|
||||||
|
|
||||||
@docs getContent, extract
|
@docs getContent, extract
|
||||||
|
@ -39,6 +45,7 @@ settings that can be adjusted manually.
|
||||||
import Internal.Config.Default as Default
|
import Internal.Config.Default as Default
|
||||||
import Internal.Tools.Decode as D
|
import Internal.Tools.Decode as D
|
||||||
import Internal.Tools.Encode as E
|
import Internal.Tools.Encode as E
|
||||||
|
import Internal.Values.Context as Context exposing (Context)
|
||||||
import Json.Decode as D
|
import Json.Decode as D
|
||||||
import Json.Encode as E
|
import Json.Encode as E
|
||||||
|
|
||||||
|
@ -51,6 +58,7 @@ define them in their type.
|
||||||
type Envelope a
|
type Envelope a
|
||||||
= Envelope
|
= Envelope
|
||||||
{ content : a
|
{ content : a
|
||||||
|
, context : Context
|
||||||
, settings : Settings
|
, settings : Settings
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,8 +82,9 @@ potential tokens, values and settings included in the JSON.
|
||||||
-}
|
-}
|
||||||
decoder : D.Decoder a -> D.Decoder (Envelope a)
|
decoder : D.Decoder a -> D.Decoder (Envelope a)
|
||||||
decoder xDecoder =
|
decoder xDecoder =
|
||||||
D.map2 (\a b -> Envelope { content = a, settings = b })
|
D.map3 (\a b c -> Envelope { content = a, context = b, settings = c })
|
||||||
(D.field "content" xDecoder)
|
(D.field "content" xDecoder)
|
||||||
|
(D.field "context" Context.decoder)
|
||||||
(D.field "settings" decoderSettings)
|
(D.field "settings" decoderSettings)
|
||||||
|
|
||||||
|
|
||||||
|
@ -96,6 +105,7 @@ encode : (a -> E.Value) -> Envelope a -> E.Value
|
||||||
encode encodeX (Envelope data) =
|
encode encodeX (Envelope data) =
|
||||||
E.object
|
E.object
|
||||||
[ ( "content", encodeX data.content )
|
[ ( "content", encodeX data.content )
|
||||||
|
, ( "context", Context.encode data.context )
|
||||||
, ( "settings", encodeSettings data.settings )
|
, ( "settings", encodeSettings data.settings )
|
||||||
, ( "version", E.string Default.currentVersion )
|
, ( "version", E.string Default.currentVersion )
|
||||||
]
|
]
|
||||||
|
@ -178,6 +188,7 @@ init : a -> Envelope a
|
||||||
init x =
|
init x =
|
||||||
Envelope
|
Envelope
|
||||||
{ content = x
|
{ content = x
|
||||||
|
, context = Context.init
|
||||||
, settings =
|
, settings =
|
||||||
{ currentVersion = Default.currentVersion
|
{ currentVersion = Default.currentVersion
|
||||||
, deviceName = Default.deviceName
|
, deviceName = Default.deviceName
|
||||||
|
@ -200,29 +211,42 @@ map : (a -> b) -> Envelope a -> Envelope b
|
||||||
map f (Envelope data) =
|
map f (Envelope data) =
|
||||||
Envelope
|
Envelope
|
||||||
{ content = f data.content
|
{ content = f data.content
|
||||||
|
, context = data.context
|
||||||
, settings = data.settings
|
, settings = data.settings
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
{-| Update the settings in the Envelope.
|
{-| Update the Context in the Envelope.
|
||||||
|
|
||||||
setDeviceName : String -> Envelope a -> Envelope a
|
|
||||||
setDeviceName name envelope =
|
|
||||||
mapSettings
|
|
||||||
(\settings ->
|
|
||||||
{ settings | deviceName = name }
|
|
||||||
)
|
|
||||||
envelope
|
|
||||||
|
|
||||||
-}
|
-}
|
||||||
mapSettings : (Settings -> Settings) -> Envelope a -> Envelope a
|
mapContext : (Context -> Context) -> Envelope a -> Envelope a
|
||||||
mapSettings f (Envelope data) =
|
mapContext f (Envelope data) =
|
||||||
Envelope
|
Envelope
|
||||||
{ content = data.content
|
{ content = data.content
|
||||||
, settings = f data.settings
|
, context = f data.context
|
||||||
|
, settings = data.settings
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{-| Map the contents of a function, where the result is wrapped in a `List`
|
||||||
|
type. This can be useful when you are mapping to a list of individual values
|
||||||
|
that you would all like to see enveloped.
|
||||||
|
|
||||||
|
type alias User =
|
||||||
|
{ name : String, age : Int }
|
||||||
|
|
||||||
|
type alias Company =
|
||||||
|
{ name : String, employees : List User }
|
||||||
|
|
||||||
|
getEmployees : Envelope Company -> List (Envelope User)
|
||||||
|
getEmployees envelope =
|
||||||
|
mapList .employees envelope
|
||||||
|
|
||||||
|
-}
|
||||||
|
mapList : (a -> List b) -> Envelope a -> List (Envelope b)
|
||||||
|
mapList f =
|
||||||
|
map f >> toList
|
||||||
|
|
||||||
|
|
||||||
{-| Map the contents of a function, where the result is wrapped in a `Maybe`
|
{-| Map the contents of a function, where the result is wrapped in a `Maybe`
|
||||||
type. This can be useful when you are not guaranteed to find the value you're
|
type. This can be useful when you are not guaranteed to find the value you're
|
||||||
looking for.
|
looking for.
|
||||||
|
@ -243,6 +267,33 @@ mapMaybe f =
|
||||||
map f >> toMaybe
|
map f >> toMaybe
|
||||||
|
|
||||||
|
|
||||||
|
{-| Update the settings in the Envelope.
|
||||||
|
|
||||||
|
setDeviceName : String -> Envelope a -> Envelope a
|
||||||
|
setDeviceName name envelope =
|
||||||
|
mapSettings
|
||||||
|
(\settings ->
|
||||||
|
{ settings | deviceName = name }
|
||||||
|
)
|
||||||
|
envelope
|
||||||
|
|
||||||
|
-}
|
||||||
|
mapSettings : (Settings -> Settings) -> Envelope a -> Envelope a
|
||||||
|
mapSettings f (Envelope data) =
|
||||||
|
Envelope
|
||||||
|
{ content = data.content
|
||||||
|
, context = data.context
|
||||||
|
, settings = f data.settings
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
toList : Envelope (List a) -> List (Envelope a)
|
||||||
|
toList (Envelope data) =
|
||||||
|
List.map
|
||||||
|
(\content -> map (always content) (Envelope data))
|
||||||
|
data.content
|
||||||
|
|
||||||
|
|
||||||
toMaybe : Envelope (Maybe a) -> Maybe (Envelope a)
|
toMaybe : Envelope (Maybe a) -> Maybe (Envelope a)
|
||||||
toMaybe (Envelope data) =
|
toMaybe (Envelope data) =
|
||||||
Maybe.map
|
Maybe.map
|
||||||
|
|
Loading…
Reference in New Issue