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.Settings",
|
||||
"Internal.Config.Default",
|
||||
"Internal.Config.Leaks",
|
||||
"Internal.Config.Text",
|
||||
"Internal.Tools.Decode",
|
||||
"Internal.Tools.Encode",
|
||||
|
@ -15,6 +16,7 @@
|
|||
"Internal.Tools.Iddict",
|
||||
"Internal.Tools.Timestamp",
|
||||
"Internal.Tools.VersionControl",
|
||||
"Internal.Values.Context",
|
||||
"Internal.Values.Envelope",
|
||||
"Internal.Values.Vault",
|
||||
"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
|
||||
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 describe custom values in a human readable format.
|
||||
|
||||
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
|
||||
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
|
||||
|
@ -94,14 +102,14 @@ versionsFailedToDecode =
|
|||
"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 =
|
||||
"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 =
|
||||
|
|
|
@ -3,11 +3,11 @@ module Internal.Tools.Timestamp exposing
|
|||
, 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.
|
||||
|
||||
|
||||
## Timetstamp
|
||||
## Timestamp
|
||||
|
||||
@docs Timestamp
|
||||
|
||||
|
@ -23,7 +23,7 @@ import Json.Encode as E
|
|||
import Time
|
||||
|
||||
|
||||
{-| The Timetstamp data type representing a moment in time.
|
||||
{-| The Timestamp data type representing a moment in time.
|
||||
-}
|
||||
type alias Timestamp =
|
||||
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
|
||||
( Envelope, init
|
||||
, map, mapMaybe
|
||||
, map, mapMaybe, mapList
|
||||
, Settings, mapSettings, extractSettings
|
||||
, mapContext
|
||||
, getContent, extract
|
||||
, encode, decoder
|
||||
)
|
||||
|
@ -17,7 +18,7 @@ settings that can be adjusted manually.
|
|||
|
||||
## Manipulate
|
||||
|
||||
@docs map, mapMaybe
|
||||
@docs map, mapMaybe, mapList
|
||||
|
||||
|
||||
## Settings
|
||||
|
@ -25,6 +26,11 @@ settings that can be adjusted manually.
|
|||
@docs Settings, mapSettings, extractSettings
|
||||
|
||||
|
||||
## Context
|
||||
|
||||
@docs mapContext
|
||||
|
||||
|
||||
## Extract
|
||||
|
||||
@docs getContent, extract
|
||||
|
@ -39,6 +45,7 @@ settings that can be adjusted manually.
|
|||
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 Json.Decode as D
|
||||
import Json.Encode as E
|
||||
|
||||
|
@ -51,6 +58,7 @@ define them in their type.
|
|||
type Envelope a
|
||||
= Envelope
|
||||
{ content : a
|
||||
, context : Context
|
||||
, settings : Settings
|
||||
}
|
||||
|
||||
|
@ -74,8 +82,9 @@ potential tokens, values and settings included in the JSON.
|
|||
-}
|
||||
decoder : D.Decoder a -> D.Decoder (Envelope a)
|
||||
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 "context" Context.decoder)
|
||||
(D.field "settings" decoderSettings)
|
||||
|
||||
|
||||
|
@ -96,6 +105,7 @@ encode : (a -> E.Value) -> Envelope a -> E.Value
|
|||
encode encodeX (Envelope data) =
|
||||
E.object
|
||||
[ ( "content", encodeX data.content )
|
||||
, ( "context", Context.encode data.context )
|
||||
, ( "settings", encodeSettings data.settings )
|
||||
, ( "version", E.string Default.currentVersion )
|
||||
]
|
||||
|
@ -178,6 +188,7 @@ init : a -> Envelope a
|
|||
init x =
|
||||
Envelope
|
||||
{ content = x
|
||||
, context = Context.init
|
||||
, settings =
|
||||
{ currentVersion = Default.currentVersion
|
||||
, deviceName = Default.deviceName
|
||||
|
@ -200,29 +211,42 @@ map : (a -> b) -> Envelope a -> Envelope b
|
|||
map f (Envelope data) =
|
||||
Envelope
|
||||
{ content = f data.content
|
||||
, context = data.context
|
||||
, settings = data.settings
|
||||
}
|
||||
|
||||
|
||||
{-| Update the settings in the Envelope.
|
||||
|
||||
setDeviceName : String -> Envelope a -> Envelope a
|
||||
setDeviceName name envelope =
|
||||
mapSettings
|
||||
(\settings ->
|
||||
{ settings | deviceName = name }
|
||||
)
|
||||
envelope
|
||||
|
||||
{-| Update the Context in the Envelope.
|
||||
-}
|
||||
mapSettings : (Settings -> Settings) -> Envelope a -> Envelope a
|
||||
mapSettings f (Envelope data) =
|
||||
mapContext : (Context -> Context) -> Envelope a -> Envelope a
|
||||
mapContext f (Envelope data) =
|
||||
Envelope
|
||||
{ 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`
|
||||
type. This can be useful when you are not guaranteed to find the value you're
|
||||
looking for.
|
||||
|
@ -243,6 +267,33 @@ mapMaybe f =
|
|||
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 data) =
|
||||
Maybe.map
|
||||
|
|
Loading…
Reference in New Issue