Rename Elm types
Credentials -> Vault Context (upper level) -> Credentials Context (lower level) remains called Contextpull/1/head
parent
d3637cf45f
commit
e62b6a09c4
|
@ -1,4 +1,5 @@
|
||||||
module Internal.Api.Chain exposing (..)
|
module Internal.Api.Chain exposing (..)
|
||||||
|
|
||||||
{-| This module aims to simplify chaining several API tasks together.
|
{-| This module aims to simplify chaining several API tasks together.
|
||||||
|
|
||||||
Chaining tasks together is usually done through the `Task` submodule of `elm/core`,
|
Chaining tasks together is usually done through the `Task` submodule of `elm/core`,
|
||||||
|
@ -18,23 +19,29 @@ The model is like a snake: _____
|
||||||
/-|------------ | ------- | ------------- | -------- | |\/\/
|
/-|------------ | ------- | ------------- | -------- | |\/\/
|
||||||
< | accessToken | baseUrl | transactionId | API call | |------< Final API call
|
< | accessToken | baseUrl | transactionId | API call | |------< Final API call
|
||||||
\-|------------ | ------- | ------------- | -------- | |/\/\
|
\-|------------ | ------- | ------------- | -------- | |/\/\
|
||||||
------/
|
\-----/
|
||||||
(You're not allowed to judge my ASCII art skills unless you submit a PR with a
|
|
||||||
|
(You're not allowed to judge my ASCII art skills unless you submit a PR with a
|
||||||
superior ASCII snake model.)
|
superior ASCII snake model.)
|
||||||
|
|
||||||
Every task will add another value to an extensible record, which can be used
|
Every task will add another value to an extensible record, which can be used
|
||||||
by later tasks in the chain. Additionally, every subtask can leave a `CredUpdate`
|
by later tasks in the chain. Additionally, every subtask can leave a `CredUpdate`
|
||||||
type as a message to the Credentials to update certain information.
|
type as a message to the Credentials to update certain information.
|
||||||
|
|
||||||
-}
|
-}
|
||||||
|
|
||||||
import Internal.Tools.Exceptions as X
|
|
||||||
import Internal.Api.Context as Context exposing (Context)
|
import Internal.Api.Context as Context exposing (Context)
|
||||||
|
import Internal.Tools.Exceptions as X
|
||||||
import Task exposing (Task)
|
import Task exposing (Task)
|
||||||
|
|
||||||
type alias TaskChain u a b =
|
|
||||||
(Context a -> Task X.Error (TaskChainPiece u a b))
|
|
||||||
|
|
||||||
type alias IdemChain u a = TaskChain u a a
|
type alias TaskChain u a b =
|
||||||
|
Context a -> Task X.Error (TaskChainPiece u a b)
|
||||||
|
|
||||||
|
|
||||||
|
type alias IdemChain u a =
|
||||||
|
TaskChain u a a
|
||||||
|
|
||||||
|
|
||||||
type TaskChainPiece u a b
|
type TaskChainPiece u a b
|
||||||
= TaskChainPiece
|
= TaskChainPiece
|
||||||
|
@ -42,44 +49,47 @@ type TaskChainPiece u a b
|
||||||
, messages : List u
|
, messages : List u
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
{-| Chain two tasks together. The second task will only run if the first one succeeds.
|
{-| Chain two tasks together. The second task will only run if the first one succeeds.
|
||||||
-}
|
-}
|
||||||
andThen : TaskChain u b c -> TaskChain u a b -> TaskChain u a c
|
andThen : TaskChain u b c -> TaskChain u a b -> TaskChain u a c
|
||||||
andThen f2 f1 =
|
andThen f2 f1 =
|
||||||
(\context ->
|
\context ->
|
||||||
f1 context
|
f1 context
|
||||||
|> Task.andThen
|
|> Task.andThen
|
||||||
(\(TaskChainPiece old) ->
|
(\(TaskChainPiece old) ->
|
||||||
context
|
context
|
||||||
|> old.contextChange
|
|> old.contextChange
|
||||||
|> f2
|
|> f2
|
||||||
|> Task.map
|
|> Task.map
|
||||||
(\(TaskChainPiece new) ->
|
(\(TaskChainPiece new) ->
|
||||||
TaskChainPiece
|
TaskChainPiece
|
||||||
{ contextChange = old.contextChange >> new.contextChange
|
{ contextChange = old.contextChange >> new.contextChange
|
||||||
, messages = List.append old.messages new.messages
|
, messages = List.append old.messages new.messages
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
{-| Optionally run a task that may provide additional information.
|
|
||||||
|
{-| Optionally run a task that may provide additional information.
|
||||||
|
|
||||||
If the provided chain fails, it will be ignored. This way, the chain can be tasked
|
If the provided chain fails, it will be ignored. This way, the chain can be tasked
|
||||||
without needlessly breaking the whole chain if anything breaks in here.
|
without needlessly breaking the whole chain if anything breaks in here.
|
||||||
|
|
||||||
You cannot use this function to execute a task chain that adds or removes context.
|
You cannot use this function to execute a task chain that adds or removes context.
|
||||||
|
|
||||||
-}
|
-}
|
||||||
maybe : IdemChain u a -> IdemChain u a
|
maybe : IdemChain u a -> IdemChain u a
|
||||||
maybe f =
|
maybe f =
|
||||||
{ contextChange = identity
|
{ contextChange = identity
|
||||||
, messages = []
|
, messages = []
|
||||||
}
|
}
|
||||||
|> TaskChainPiece
|
|> TaskChainPiece
|
||||||
|> Task.succeed
|
|> Task.succeed
|
||||||
|> always
|
|> always
|
||||||
|> Task.onError
|
|> Task.onError
|
||||||
|> (>>) f
|
|> (>>) f
|
||||||
|
|
||||||
|
|
||||||
{-| If the TaskChain fails, run this task otherwise.
|
{-| If the TaskChain fails, run this task otherwise.
|
||||||
-}
|
-}
|
||||||
|
@ -87,30 +97,33 @@ otherwise : TaskChain u a b -> TaskChain u a b -> TaskChain u a b
|
||||||
otherwise f2 f1 context =
|
otherwise f2 f1 context =
|
||||||
Task.onError (always <| f2 context) (f1 context)
|
Task.onError (always <| f2 context) (f1 context)
|
||||||
|
|
||||||
|
|
||||||
{-| Once all the pieces of the chain have been assembled, you can turn it into a task.
|
{-| Once all the pieces of the chain have been assembled, you can turn it into a task.
|
||||||
|
|
||||||
The compiler will fail if the chain is missing a vital piece of information.
|
The compiler will fail if the chain is missing a vital piece of information.
|
||||||
|
|
||||||
-}
|
-}
|
||||||
toTask : TaskChain u {} b -> Task X.Error (List u)
|
toTask : TaskChain u {} b -> Task X.Error (List u)
|
||||||
toTask f1 =
|
toTask f1 =
|
||||||
Context.init
|
Context.init
|
||||||
|> f1
|
|> f1
|
||||||
|> Task.map
|
|> Task.map
|
||||||
(\(TaskChainPiece data) ->
|
(\(TaskChainPiece data) ->
|
||||||
data.messages
|
data.messages
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
{-| If the TaskChain fails, this function will get it to retry.
|
{-| If the TaskChain fails, this function will get it to retry.
|
||||||
|
|
||||||
When set to 1 or lower, the task will only try once.
|
When set to 1 or lower, the task will only try once.
|
||||||
|
|
||||||
-}
|
-}
|
||||||
tryNTimes : Int -> TaskChain u a b -> TaskChain u a b
|
tryNTimes : Int -> TaskChain u a b -> TaskChain u a b
|
||||||
tryNTimes n f context =
|
tryNTimes n f context =
|
||||||
if n <= 1 then
|
if n <= 1 then
|
||||||
f context
|
f context
|
||||||
|
|
||||||
else
|
else
|
||||||
(\_ -> tryNTimes (n - 1) f context)
|
(\_ -> tryNTimes (n - 1) f context)
|
||||||
|> Task.onError
|
|> Task.onError
|
||||||
|> (|>) (f context)
|
|> (|>) (f context)
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
module Internal.Api.CredUpdate exposing (..)
|
module Internal.Api.CredUpdate exposing (..)
|
||||||
|
|
||||||
import Hash
|
|
||||||
import Html exposing (input)
|
|
||||||
import Internal.Api.Chain as Chain exposing (IdemChain, TaskChain)
|
import Internal.Api.Chain as Chain exposing (IdemChain, TaskChain)
|
||||||
import Internal.Api.Context as Context exposing (VB, VBA, VBAT)
|
import Internal.Api.Context as Context exposing (VB, VBA, VBAT)
|
||||||
import Internal.Api.GetEvent.Main as GetEvent
|
import Internal.Api.GetEvent.Main as GetEvent
|
||||||
|
|
|
@ -1,73 +0,0 @@
|
||||||
module Internal.Context exposing (..)
|
|
||||||
|
|
||||||
{-| The `Context` type serves as an extra layer between the internal Room/Event types
|
|
||||||
and the types that the user may deal with directly.
|
|
||||||
|
|
||||||
Since pointers cannot point to values that the `Credentials` type has,
|
|
||||||
the `Credentials` type passes information down in the form of a `Context` type.
|
|
||||||
|
|
||||||
-}
|
|
||||||
|
|
||||||
import Internal.Api.Versions.V1.Versions as V
|
|
||||||
import Internal.Tools.LoginValues as Login exposing (AccessToken(..))
|
|
||||||
|
|
||||||
|
|
||||||
type Context
|
|
||||||
= Context
|
|
||||||
{ access : AccessToken
|
|
||||||
, homeserver : String
|
|
||||||
, vs : Maybe V.Versions
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
{-| Retrieves the access token from a given `Context` value.
|
|
||||||
-}
|
|
||||||
accessToken : Context -> AccessToken
|
|
||||||
accessToken (Context { access }) =
|
|
||||||
access
|
|
||||||
|
|
||||||
|
|
||||||
{-| Add a new access token to the `Context` type.
|
|
||||||
-}
|
|
||||||
addToken : String -> Context -> Context
|
|
||||||
addToken token (Context ({ access } as data)) =
|
|
||||||
Context { data | access = Login.addToken token access }
|
|
||||||
|
|
||||||
|
|
||||||
{-| Add a username and password to the `Context` type.
|
|
||||||
-}
|
|
||||||
addUsernameAndPassword : { username : String, password : String } -> Context -> Context
|
|
||||||
addUsernameAndPassword uap (Context ({ access } as data)) =
|
|
||||||
Context { data | access = Login.addUsernameAndPassword uap access }
|
|
||||||
|
|
||||||
|
|
||||||
{-| Add known spec versions to the `Context` type.
|
|
||||||
-}
|
|
||||||
addVersions : V.Versions -> Context -> Context
|
|
||||||
addVersions vs (Context data) =
|
|
||||||
Context { data | vs = Just vs }
|
|
||||||
|
|
||||||
|
|
||||||
{-| Retrieves the base url from a given `Context` value.
|
|
||||||
-}
|
|
||||||
baseUrl : Context -> String
|
|
||||||
baseUrl (Context { homeserver }) =
|
|
||||||
homeserver
|
|
||||||
|
|
||||||
|
|
||||||
{-| Creates a `Context` value from a base URL.
|
|
||||||
-}
|
|
||||||
fromBaseUrl : String -> Context
|
|
||||||
fromBaseUrl url =
|
|
||||||
Context
|
|
||||||
{ access = NoAccess
|
|
||||||
, homeserver = url
|
|
||||||
, vs = Nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
{-| Retrieves the spec versions from a given `Context` value.
|
|
||||||
-}
|
|
||||||
versions : Context -> Maybe V.Versions
|
|
||||||
versions (Context { vs }) =
|
|
||||||
vs
|
|
|
@ -1,216 +1,73 @@
|
||||||
module Internal.Credentials exposing (..)
|
module Internal.Credentials exposing (..)
|
||||||
|
|
||||||
{-| The Credentials type is the keychain that stores all tokens, values,
|
{-| The `Credentials` type serves as an extra layer between the internal Room/Event types
|
||||||
numbers and other types that need to be remembered.
|
and the types that the user may deal with directly.
|
||||||
|
|
||||||
This file combines the internal functions with the API endpoints to create a fully functional Credentials keychain.
|
Since pointers cannot point to values that the `Vault` type has,
|
||||||
|
the `Vault` type passes information down in the form of a `Credentials` type.
|
||||||
|
|
||||||
-}
|
-}
|
||||||
|
|
||||||
import Dict
|
import Internal.Api.Versions.V1.Versions as V
|
||||||
import Internal.Api.Task as Api
|
import Internal.Tools.LoginValues as Login exposing (AccessToken(..))
|
||||||
import Internal.Api.CredUpdate exposing (CredUpdate(..))
|
|
||||||
import Internal.Context as Context exposing (Context)
|
|
||||||
import Internal.Event as Event
|
|
||||||
import Internal.Room as Room
|
|
||||||
import Internal.Tools.Exceptions as X
|
|
||||||
import Internal.Values.Credentials as Internal
|
|
||||||
import Internal.Values.Event as IEvent
|
|
||||||
import Internal.Values.Room as IRoom
|
|
||||||
import Internal.Values.StateManager as StateManager
|
|
||||||
import Task exposing (Task)
|
|
||||||
|
|
||||||
|
|
||||||
{-| You can consider the `Credentials` type as a large ring of keys,
|
|
||||||
and Elm will figure out which key to use.
|
|
||||||
If you pass the `Credentials` into any function, then the library will look for
|
|
||||||
the right keys and tokens to get the right information.
|
|
||||||
-}
|
|
||||||
type Credentials
|
type Credentials
|
||||||
= Credentials
|
= Credentials
|
||||||
{ cred : Internal.ICredentials
|
{ access : AccessToken
|
||||||
, context : Context
|
, homeserver : String
|
||||||
|
, vs : Maybe V.Versions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
{-| Get a Credentials type based on an unknown access token.
|
{-| Retrieves the access token from a given `Credentials` value.
|
||||||
|
|
||||||
This is an easier way to connect to a Matrix homeserver, but your access may end
|
|
||||||
when the access token expires, is revoked or something else happens.
|
|
||||||
|
|
||||||
-}
|
-}
|
||||||
fromAccessToken : { baseUrl : String, accessToken : String } -> Credentials
|
accessToken : Credentials -> AccessToken
|
||||||
fromAccessToken { baseUrl, accessToken } =
|
accessToken (Credentials { access }) =
|
||||||
Context.fromBaseUrl baseUrl
|
access
|
||||||
|> Context.addToken accessToken
|
|
||||||
|> (\context ->
|
|
||||||
{ cred = Internal.init, context = context }
|
|
||||||
)
|
|
||||||
|> Credentials
|
|
||||||
|
|
||||||
|
|
||||||
{-| Get a Credentials type using a username and password.
|
{-| Add a new access token to the `Credentials` type.
|
||||||
-}
|
-}
|
||||||
fromLoginCredentials : { username : String, password : String, baseUrl : String } -> Credentials
|
addToken : String -> Credentials -> Credentials
|
||||||
fromLoginCredentials { username, password, baseUrl } =
|
addToken token (Credentials ({ access } as data)) =
|
||||||
Context.fromBaseUrl baseUrl
|
Credentials { data | access = Login.addToken token access }
|
||||||
|> Context.addUsernameAndPassword
|
|
||||||
{ username = username
|
|
||||||
, password = password
|
|
||||||
}
|
|
||||||
|> (\context ->
|
|
||||||
{ cred = Internal.init, context = context }
|
|
||||||
)
|
|
||||||
|> Credentials
|
|
||||||
|
|
||||||
|
|
||||||
{-| Get a room based on its id.
|
{-| Add a username and password to the `Credentials` type.
|
||||||
-}
|
-}
|
||||||
getRoomById : String -> Credentials -> Maybe Room.Room
|
addUsernameAndPassword : { username : String, password : String } -> Credentials -> Credentials
|
||||||
getRoomById roomId (Credentials { cred, context }) =
|
addUsernameAndPassword uap (Credentials ({ access } as data)) =
|
||||||
Internal.getRoomById roomId cred
|
Credentials { data | access = Login.addUsernameAndPassword uap access }
|
||||||
|> Maybe.map (Room.withContext context)
|
|
||||||
|
|
||||||
|
|
||||||
{-| Insert an internal room type into the credentials.
|
{-| Add known spec versions to the `Credentials` type.
|
||||||
-}
|
-}
|
||||||
insertInternalRoom : IRoom.IRoom -> Credentials -> Credentials
|
addVersions : V.Versions -> Credentials -> Credentials
|
||||||
insertInternalRoom iroom (Credentials data) =
|
addVersions vs (Credentials data) =
|
||||||
Credentials { data | cred = Internal.insertRoom iroom data.cred }
|
Credentials { data | vs = Just vs }
|
||||||
|
|
||||||
|
|
||||||
{-| Internal a full room type into the credentials.
|
{-| Retrieves the base url from a given `Credentials` value.
|
||||||
-}
|
-}
|
||||||
insertRoom : Room.Room -> Credentials -> Credentials
|
baseUrl : Credentials -> String
|
||||||
insertRoom =
|
baseUrl (Credentials { homeserver }) =
|
||||||
Room.withoutContext >> insertInternalRoom
|
homeserver
|
||||||
|
|
||||||
|
|
||||||
{-| Update the Credentials type with new values
|
{-| Creates a `Credentials` value from a base URL.
|
||||||
-}
|
-}
|
||||||
updateWith : CredUpdate -> Credentials -> Credentials
|
fromBaseUrl : String -> Credentials
|
||||||
updateWith credUpdate ((Credentials ({ cred, context } as data)) as credentials) =
|
fromBaseUrl url =
|
||||||
case credUpdate of
|
Credentials
|
||||||
MultipleUpdates updates ->
|
{ access = NoAccess
|
||||||
List.foldl updateWith credentials updates
|
, homeserver = url
|
||||||
|
, vs = Nothing
|
||||||
GetEvent input output ->
|
|
||||||
case getRoomById input.roomId credentials of
|
|
||||||
Just room ->
|
|
||||||
output
|
|
||||||
|> Event.initFromGetEvent
|
|
||||||
|> Room.addInternalEvent
|
|
||||||
|> (|>) room
|
|
||||||
|> insertRoom
|
|
||||||
|> (|>) credentials
|
|
||||||
|
|
||||||
Nothing ->
|
|
||||||
credentials
|
|
||||||
|
|
||||||
-- TODO
|
|
||||||
InviteSent _ _ ->
|
|
||||||
credentials
|
|
||||||
|
|
||||||
JoinedMembersToRoom _ _ ->
|
|
||||||
credentials
|
|
||||||
|
|
||||||
-- TODO
|
|
||||||
MessageEventSent _ _ ->
|
|
||||||
credentials
|
|
||||||
|
|
||||||
-- TODO
|
|
||||||
RedactedEvent _ _ ->
|
|
||||||
credentials
|
|
||||||
|
|
||||||
-- TODO
|
|
||||||
StateEventSent _ _ ->
|
|
||||||
credentials
|
|
||||||
|
|
||||||
-- TODO
|
|
||||||
SyncUpdate input output ->
|
|
||||||
let
|
|
||||||
jRooms : List IRoom.IRoom
|
|
||||||
jRooms =
|
|
||||||
output.rooms
|
|
||||||
|> Maybe.map .join
|
|
||||||
|> Maybe.withDefault Dict.empty
|
|
||||||
|> Dict.toList
|
|
||||||
|> List.map
|
|
||||||
(\( roomId, jroom ) ->
|
|
||||||
case getRoomById roomId credentials of
|
|
||||||
-- Update existing room
|
|
||||||
Just room ->
|
|
||||||
case jroom.timeline of
|
|
||||||
Just timeline ->
|
|
||||||
room
|
|
||||||
|> Room.withoutContext
|
|
||||||
|> IRoom.addEvents
|
|
||||||
{ events =
|
|
||||||
List.map
|
|
||||||
(Event.initFromClientEventWithoutRoomId roomId)
|
|
||||||
timeline.events
|
|
||||||
, limited = timeline.limited
|
|
||||||
, nextBatch = output.nextBatch
|
|
||||||
, prevBatch =
|
|
||||||
timeline.prevBatch
|
|
||||||
|> Maybe.withDefault
|
|
||||||
(Maybe.withDefault "" input.since)
|
|
||||||
, stateDelta =
|
|
||||||
jroom.state
|
|
||||||
|> Maybe.map
|
|
||||||
(.events
|
|
||||||
>> List.map (Event.initFromClientEventWithoutRoomId roomId)
|
|
||||||
>> StateManager.fromEventList
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Nothing ->
|
|
||||||
Room.withoutContext room
|
|
||||||
|
|
||||||
-- Add new room
|
|
||||||
Nothing ->
|
|
||||||
jroom
|
|
||||||
|> Room.initFromJoinedRoom { nextBatch = output.nextBatch, roomId = roomId }
|
|
||||||
)
|
|
||||||
in
|
|
||||||
cred
|
|
||||||
|> Internal.addSince output.nextBatch
|
|
||||||
|> List.foldl Internal.insertRoom
|
|
||||||
|> (|>) jRooms
|
|
||||||
|> (\x -> { cred = x, context = context })
|
|
||||||
|> Credentials
|
|
||||||
|
|
||||||
UpdateAccessToken token ->
|
|
||||||
Credentials { data | context = Context.addToken token context }
|
|
||||||
|
|
||||||
UpdateVersions versions ->
|
|
||||||
Credentials { data | context = Context.addVersions versions context }
|
|
||||||
|
|
||||||
-- TODO: Save all info
|
|
||||||
LoggedInWithUsernameAndPassword _ output ->
|
|
||||||
Credentials { data | context = Context.addToken output.accessToken context }
|
|
||||||
|
|
||||||
|
|
||||||
{-| Synchronize credentials
|
|
||||||
-}
|
|
||||||
sync : Credentials -> Task X.Error CredUpdate
|
|
||||||
sync (Credentials { cred, context }) =
|
|
||||||
Api.sync
|
|
||||||
{ accessToken = Context.accessToken context
|
|
||||||
, baseUrl = Context.baseUrl context
|
|
||||||
, filter = Nothing
|
|
||||||
, fullState = Nothing
|
|
||||||
, setPresence = Nothing
|
|
||||||
, since = Internal.getSince cred
|
|
||||||
, timeout = Just 30
|
|
||||||
, versions = Context.versions context
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
{-| Get a list of all synchronised rooms.
|
{-| Retrieves the spec versions from a given `Credentials` value.
|
||||||
-}
|
-}
|
||||||
rooms : Credentials -> List Room.Room
|
versions : Credentials -> Maybe V.Versions
|
||||||
rooms (Credentials { cred, context }) =
|
versions (Credentials { vs }) =
|
||||||
cred
|
vs
|
||||||
|> Internal.getRooms
|
|
||||||
|> List.map (Room.withContext context)
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ resend other events or forward them elsewhere.
|
||||||
import Internal.Api.GetEvent.Main as GetEvent
|
import Internal.Api.GetEvent.Main as GetEvent
|
||||||
import Internal.Api.GetEvent.V1.SpecObjects as GetEventSO
|
import Internal.Api.GetEvent.V1.SpecObjects as GetEventSO
|
||||||
import Internal.Api.Sync.V2.SpecObjects as SyncSO
|
import Internal.Api.Sync.V2.SpecObjects as SyncSO
|
||||||
import Internal.Context exposing (Context)
|
import Internal.Credentials exposing (Credentials)
|
||||||
import Internal.Tools.Timestamp exposing (Timestamp)
|
import Internal.Tools.Timestamp exposing (Timestamp)
|
||||||
import Internal.Values.Event as Internal
|
import Internal.Values.Event as Internal
|
||||||
import Json.Encode as E
|
import Json.Encode as E
|
||||||
|
@ -21,15 +21,15 @@ import Json.Encode as E
|
||||||
type Event
|
type Event
|
||||||
= Event
|
= Event
|
||||||
{ event : Internal.IEvent
|
{ event : Internal.IEvent
|
||||||
, context : Context
|
, context : Credentials
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
{-| Using the credentials' background information and an internal event type,
|
{-| Using the credentials' background information and an internal event type,
|
||||||
create an interactive event type.
|
create an interactive event type.
|
||||||
-}
|
-}
|
||||||
withContext : Context -> Internal.IEvent -> Event
|
withCredentials : Credentials -> Internal.IEvent -> Event
|
||||||
withContext context event =
|
withCredentials context event =
|
||||||
Event
|
Event
|
||||||
{ event = event
|
{ event = event
|
||||||
, context = context
|
, context = context
|
||||||
|
@ -90,8 +90,8 @@ initFromClientEventWithoutRoomId rId output =
|
||||||
|
|
||||||
{-| Get the internal event type that is hidden in the interactive event type.
|
{-| Get the internal event type that is hidden in the interactive event type.
|
||||||
-}
|
-}
|
||||||
withoutContext : Event -> Internal.IEvent
|
withoutCredentials : Event -> Internal.IEvent
|
||||||
withoutContext (Event { event }) =
|
withoutCredentials (Event { event }) =
|
||||||
event
|
event
|
||||||
|
|
||||||
|
|
||||||
|
@ -101,42 +101,42 @@ withoutContext (Event { event }) =
|
||||||
|
|
||||||
content : Event -> E.Value
|
content : Event -> E.Value
|
||||||
content =
|
content =
|
||||||
withoutContext >> Internal.content
|
withoutCredentials >> Internal.content
|
||||||
|
|
||||||
|
|
||||||
eventId : Event -> String
|
eventId : Event -> String
|
||||||
eventId =
|
eventId =
|
||||||
withoutContext >> Internal.eventId
|
withoutCredentials >> Internal.eventId
|
||||||
|
|
||||||
|
|
||||||
originServerTs : Event -> Timestamp
|
originServerTs : Event -> Timestamp
|
||||||
originServerTs =
|
originServerTs =
|
||||||
withoutContext >> Internal.originServerTs
|
withoutCredentials >> Internal.originServerTs
|
||||||
|
|
||||||
|
|
||||||
roomId : Event -> String
|
roomId : Event -> String
|
||||||
roomId =
|
roomId =
|
||||||
withoutContext >> Internal.roomId
|
withoutCredentials >> Internal.roomId
|
||||||
|
|
||||||
|
|
||||||
sender : Event -> String
|
sender : Event -> String
|
||||||
sender =
|
sender =
|
||||||
withoutContext >> Internal.sender
|
withoutCredentials >> Internal.sender
|
||||||
|
|
||||||
|
|
||||||
stateKey : Event -> Maybe String
|
stateKey : Event -> Maybe String
|
||||||
stateKey =
|
stateKey =
|
||||||
withoutContext >> Internal.stateKey
|
withoutCredentials >> Internal.stateKey
|
||||||
|
|
||||||
|
|
||||||
contentType : Event -> String
|
contentType : Event -> String
|
||||||
contentType =
|
contentType =
|
||||||
withoutContext >> Internal.contentType
|
withoutCredentials >> Internal.contentType
|
||||||
|
|
||||||
|
|
||||||
age : Event -> Maybe Int
|
age : Event -> Maybe Int
|
||||||
age =
|
age =
|
||||||
withoutContext >> Internal.age
|
withoutCredentials >> Internal.age
|
||||||
|
|
||||||
|
|
||||||
redactedBecause : Event -> Maybe Event
|
redactedBecause : Event -> Maybe Event
|
||||||
|
@ -151,4 +151,4 @@ redactedBecause (Event data) =
|
||||||
|
|
||||||
transactionId : Event -> Maybe String
|
transactionId : Event -> Maybe String
|
||||||
transactionId =
|
transactionId =
|
||||||
withoutContext >> Internal.transactionId
|
withoutCredentials >> Internal.transactionId
|
||||||
|
|
|
@ -7,7 +7,7 @@ import Dict
|
||||||
import Internal.Api.CredUpdate exposing (CredUpdate)
|
import Internal.Api.CredUpdate exposing (CredUpdate)
|
||||||
import Internal.Api.Sync.V2.SpecObjects as Sync
|
import Internal.Api.Sync.V2.SpecObjects as Sync
|
||||||
import Internal.Api.Task as Api
|
import Internal.Api.Task as Api
|
||||||
import Internal.Context as Context exposing (Context)
|
import Internal.Credentials as Credentials exposing (Credentials)
|
||||||
import Internal.Event as Event exposing (Event)
|
import Internal.Event as Event exposing (Event)
|
||||||
import Internal.Tools.Exceptions as X
|
import Internal.Tools.Exceptions as X
|
||||||
import Internal.Tools.Hashdict as Hashdict
|
import Internal.Tools.Hashdict as Hashdict
|
||||||
|
@ -30,7 +30,7 @@ to it.
|
||||||
type Room
|
type Room
|
||||||
= Room
|
= Room
|
||||||
{ room : Internal.IRoom
|
{ room : Internal.IRoom
|
||||||
, context : Context
|
, context : Credentials
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -98,13 +98,13 @@ addInternalEvent ievent (Room ({ room } as data)) =
|
||||||
-}
|
-}
|
||||||
addEvent : Event -> Room -> Room
|
addEvent : Event -> Room -> Room
|
||||||
addEvent =
|
addEvent =
|
||||||
Event.withoutContext >> addInternalEvent
|
Event.withoutCredentials >> addInternalEvent
|
||||||
|
|
||||||
|
|
||||||
{-| Creates a new `Room` object with the given parameters.
|
{-| Creates a new `Room` object with the given parameters.
|
||||||
-}
|
-}
|
||||||
withContext : Context -> Internal.IRoom -> Room
|
withCredentials : Credentials -> Internal.IRoom -> Room
|
||||||
withContext context room =
|
withCredentials context room =
|
||||||
Room
|
Room
|
||||||
{ context = context
|
{ context = context
|
||||||
, room = room
|
, room = room
|
||||||
|
@ -113,8 +113,8 @@ withContext context room =
|
||||||
|
|
||||||
{-| Retrieves the `Internal.IRoom` type contained within the given `Room`.
|
{-| Retrieves the `Internal.IRoom` type contained within the given `Room`.
|
||||||
-}
|
-}
|
||||||
withoutContext : Room -> Internal.IRoom
|
withoutCredentials : Room -> Internal.IRoom
|
||||||
withoutContext (Room { room }) =
|
withoutCredentials (Room { room }) =
|
||||||
room
|
room
|
||||||
|
|
||||||
|
|
||||||
|
@ -124,14 +124,14 @@ mostRecentEvents : Room -> List Event
|
||||||
mostRecentEvents (Room { context, room }) =
|
mostRecentEvents (Room { context, room }) =
|
||||||
room
|
room
|
||||||
|> Internal.mostRecentEvents
|
|> Internal.mostRecentEvents
|
||||||
|> List.map (Event.withContext context)
|
|> List.map (Event.withCredentials context)
|
||||||
|
|
||||||
|
|
||||||
{-| Retrieves the ID of the Matrix room associated with the given `Room`.
|
{-| Retrieves the ID of the Matrix room associated with the given `Room`.
|
||||||
-}
|
-}
|
||||||
roomId : Room -> String
|
roomId : Room -> String
|
||||||
roomId =
|
roomId =
|
||||||
withoutContext >> Internal.roomId
|
withoutCredentials >> Internal.roomId
|
||||||
|
|
||||||
|
|
||||||
{-| Sends a new event to the Matrix room associated with the given `Room`.
|
{-| Sends a new event to the Matrix room associated with the given `Room`.
|
||||||
|
@ -139,12 +139,12 @@ roomId =
|
||||||
sendEvent : Room -> { eventType : String, content : E.Value } -> Task X.Error CredUpdate
|
sendEvent : Room -> { eventType : String, content : E.Value } -> Task X.Error CredUpdate
|
||||||
sendEvent (Room { context, room }) { eventType, content } =
|
sendEvent (Room { context, room }) { eventType, content } =
|
||||||
Api.sendMessageEvent
|
Api.sendMessageEvent
|
||||||
{ accessToken = Context.accessToken context
|
{ accessToken = Credentials.accessToken context
|
||||||
, baseUrl = Context.baseUrl context
|
, baseUrl = Credentials.baseUrl context
|
||||||
, content = content
|
, content = content
|
||||||
, eventType = eventType
|
, eventType = eventType
|
||||||
, roomId = Internal.roomId room
|
, roomId = Internal.roomId room
|
||||||
, versions = Context.versions context
|
, versions = Credentials.versions context
|
||||||
, extraTransactionNoise = "content-value:<object>"
|
, extraTransactionNoise = "content-value:<object>"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,8 +154,8 @@ sendEvent (Room { context, room }) { eventType, content } =
|
||||||
sendMessage : Room -> String -> Task X.Error CredUpdate
|
sendMessage : Room -> String -> Task X.Error CredUpdate
|
||||||
sendMessage (Room { context, room }) text =
|
sendMessage (Room { context, room }) text =
|
||||||
Api.sendMessageEvent
|
Api.sendMessageEvent
|
||||||
{ accessToken = Context.accessToken context
|
{ accessToken = Credentials.accessToken context
|
||||||
, baseUrl = Context.baseUrl context
|
, baseUrl = Credentials.baseUrl context
|
||||||
, content =
|
, content =
|
||||||
E.object
|
E.object
|
||||||
[ ( "msgtype", E.string "m.text" )
|
[ ( "msgtype", E.string "m.text" )
|
||||||
|
@ -163,6 +163,6 @@ sendMessage (Room { context, room }) text =
|
||||||
]
|
]
|
||||||
, eventType = "m.room.message"
|
, eventType = "m.room.message"
|
||||||
, roomId = Internal.roomId room
|
, roomId = Internal.roomId room
|
||||||
, versions = Context.versions context
|
, versions = Credentials.versions context
|
||||||
, extraTransactionNoise = "literal-message:" ++ text
|
, extraTransactionNoise = "literal-message:" ++ text
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
module Internal.Values.Credentials exposing (..)
|
module Internal.Values.Vault exposing (..)
|
||||||
|
|
||||||
{-| The Credentials type is the keychain of the Matrix SDK.
|
{-| The Credentials type is the keychain of the Matrix SDK.
|
||||||
It handles all communication with the homeserver.
|
It handles all communication with the homeserver.
|
||||||
|
@ -8,8 +8,8 @@ import Internal.Tools.Hashdict as Hashdict exposing (Hashdict)
|
||||||
import Internal.Values.Room as Room exposing (IRoom)
|
import Internal.Values.Room as Room exposing (IRoom)
|
||||||
|
|
||||||
|
|
||||||
type ICredentials
|
type IVault
|
||||||
= ICredentials
|
= IVault
|
||||||
{ rooms : Hashdict IRoom
|
{ rooms : Hashdict IRoom
|
||||||
, since : Maybe String
|
, since : Maybe String
|
||||||
}
|
}
|
||||||
|
@ -17,37 +17,37 @@ type ICredentials
|
||||||
|
|
||||||
{-| Add a new `since` token to sync from.
|
{-| Add a new `since` token to sync from.
|
||||||
-}
|
-}
|
||||||
addSince : String -> ICredentials -> ICredentials
|
addSince : String -> IVault -> IVault
|
||||||
addSince since (ICredentials data) =
|
addSince since (IVault data) =
|
||||||
ICredentials { data | since = Just since }
|
IVault { data | since = Just since }
|
||||||
|
|
||||||
|
|
||||||
{-| Get a room from the Credentials type by the room's id.
|
{-| Get a room from the Credentials type by the room's id.
|
||||||
-}
|
-}
|
||||||
getRoomById : String -> ICredentials -> Maybe IRoom
|
getRoomById : String -> IVault -> Maybe IRoom
|
||||||
getRoomById roomId (ICredentials cred) =
|
getRoomById roomId (IVault cred) =
|
||||||
Hashdict.get roomId cred.rooms
|
Hashdict.get roomId cred.rooms
|
||||||
|
|
||||||
|
|
||||||
{-| Get a list of all synchronised rooms.
|
{-| Get a list of all synchronised rooms.
|
||||||
-}
|
-}
|
||||||
getRooms : ICredentials -> List IRoom
|
getRooms : IVault -> List IRoom
|
||||||
getRooms (ICredentials { rooms }) =
|
getRooms (IVault { rooms }) =
|
||||||
Hashdict.values rooms
|
Hashdict.values rooms
|
||||||
|
|
||||||
|
|
||||||
{-| Get the latest `since` token.
|
{-| Get the latest `since` token.
|
||||||
-}
|
-}
|
||||||
getSince : ICredentials -> Maybe String
|
getSince : IVault -> Maybe String
|
||||||
getSince (ICredentials { since }) =
|
getSince (IVault { since }) =
|
||||||
since
|
since
|
||||||
|
|
||||||
|
|
||||||
{-| Create new empty Credentials.
|
{-| Create new empty Credentials.
|
||||||
-}
|
-}
|
||||||
init : ICredentials
|
init : IVault
|
||||||
init =
|
init =
|
||||||
ICredentials
|
IVault
|
||||||
{ rooms = Hashdict.empty Room.roomId
|
{ rooms = Hashdict.empty Room.roomId
|
||||||
, since = Nothing
|
, since = Nothing
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,7 @@ init =
|
||||||
This function can hence also be used as an update function for rooms.
|
This function can hence also be used as an update function for rooms.
|
||||||
|
|
||||||
-}
|
-}
|
||||||
insertRoom : IRoom -> ICredentials -> ICredentials
|
insertRoom : IRoom -> IVault -> IVault
|
||||||
insertRoom room (ICredentials cred) =
|
insertRoom room (IVault cred) =
|
||||||
ICredentials
|
IVault
|
||||||
{ cred | rooms = Hashdict.insert room cred.rooms }
|
{ cred | rooms = Hashdict.insert room cred.rooms }
|
|
@ -0,0 +1,216 @@
|
||||||
|
module Internal.Vault exposing (..)
|
||||||
|
|
||||||
|
{-| The Vault type is the keychain that stores all tokens, values,
|
||||||
|
numbers and other types that need to be remembered.
|
||||||
|
|
||||||
|
This file combines the internal functions with the API endpoints to create a fully functional Vault keychain.
|
||||||
|
|
||||||
|
-}
|
||||||
|
|
||||||
|
import Dict
|
||||||
|
import Internal.Api.CredUpdate exposing (CredUpdate(..))
|
||||||
|
import Internal.Api.Task as Api
|
||||||
|
import Internal.Context as Context exposing (Context)
|
||||||
|
import Internal.Event as Event
|
||||||
|
import Internal.Room as Room
|
||||||
|
import Internal.Tools.Exceptions as X
|
||||||
|
import Internal.Values.Event as IEvent
|
||||||
|
import Internal.Values.Room as IRoom
|
||||||
|
import Internal.Values.StateManager as StateManager
|
||||||
|
import Internal.Values.Vault as Internal
|
||||||
|
import Task exposing (Task)
|
||||||
|
|
||||||
|
|
||||||
|
{-| You can consider the `Vault` type as a large ring of keys,
|
||||||
|
and Elm will figure out which key to use.
|
||||||
|
If you pass the `Vault` into any function, then the library will look for
|
||||||
|
the right keys and tokens to get the right information.
|
||||||
|
-}
|
||||||
|
type Vault
|
||||||
|
= Vault
|
||||||
|
{ cred : Internal.IVault
|
||||||
|
, context : Context
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{-| Get a Vault type based on an unknown access token.
|
||||||
|
|
||||||
|
This is an easier way to connect to a Matrix homeserver, but your access may end
|
||||||
|
when the access token expires, is revoked or something else happens.
|
||||||
|
|
||||||
|
-}
|
||||||
|
fromAccessToken : { baseUrl : String, accessToken : String } -> Vault
|
||||||
|
fromAccessToken { baseUrl, accessToken } =
|
||||||
|
Context.fromBaseUrl baseUrl
|
||||||
|
|> Context.addToken accessToken
|
||||||
|
|> (\context ->
|
||||||
|
{ cred = Internal.init, context = context }
|
||||||
|
)
|
||||||
|
|> Vault
|
||||||
|
|
||||||
|
|
||||||
|
{-| Get a Vault type using a username and password.
|
||||||
|
-}
|
||||||
|
fromLoginVault : { username : String, password : String, baseUrl : String } -> Vault
|
||||||
|
fromLoginVault { username, password, baseUrl } =
|
||||||
|
Context.fromBaseUrl baseUrl
|
||||||
|
|> Context.addUsernameAndPassword
|
||||||
|
{ username = username
|
||||||
|
, password = password
|
||||||
|
}
|
||||||
|
|> (\context ->
|
||||||
|
{ cred = Internal.init, context = context }
|
||||||
|
)
|
||||||
|
|> Vault
|
||||||
|
|
||||||
|
|
||||||
|
{-| Get a room based on its id.
|
||||||
|
-}
|
||||||
|
getRoomById : String -> Vault -> Maybe Room.Room
|
||||||
|
getRoomById roomId (Vault { cred, context }) =
|
||||||
|
Internal.getRoomById roomId cred
|
||||||
|
|> Maybe.map (Room.withContext context)
|
||||||
|
|
||||||
|
|
||||||
|
{-| Insert an internal room type into the credentials.
|
||||||
|
-}
|
||||||
|
insertInternalRoom : IRoom.IRoom -> Vault -> Vault
|
||||||
|
insertInternalRoom iroom (Vault data) =
|
||||||
|
Vault { data | cred = Internal.insertRoom iroom data.cred }
|
||||||
|
|
||||||
|
|
||||||
|
{-| Internal a full room type into the credentials.
|
||||||
|
-}
|
||||||
|
insertRoom : Room.Room -> Vault -> Vault
|
||||||
|
insertRoom =
|
||||||
|
Room.withoutContext >> insertInternalRoom
|
||||||
|
|
||||||
|
|
||||||
|
{-| Update the Vault type with new values
|
||||||
|
-}
|
||||||
|
updateWith : CredUpdate -> Vault -> Vault
|
||||||
|
updateWith credUpdate ((Vault ({ cred, context } as data)) as credentials) =
|
||||||
|
case credUpdate of
|
||||||
|
MultipleUpdates updates ->
|
||||||
|
List.foldl updateWith credentials updates
|
||||||
|
|
||||||
|
GetEvent input output ->
|
||||||
|
case getRoomById input.roomId credentials of
|
||||||
|
Just room ->
|
||||||
|
output
|
||||||
|
|> Event.initFromGetEvent
|
||||||
|
|> Room.addInternalEvent
|
||||||
|
|> (|>) room
|
||||||
|
|> insertRoom
|
||||||
|
|> (|>) credentials
|
||||||
|
|
||||||
|
Nothing ->
|
||||||
|
credentials
|
||||||
|
|
||||||
|
-- TODO
|
||||||
|
InviteSent _ _ ->
|
||||||
|
credentials
|
||||||
|
|
||||||
|
JoinedMembersToRoom _ _ ->
|
||||||
|
credentials
|
||||||
|
|
||||||
|
-- TODO
|
||||||
|
MessageEventSent _ _ ->
|
||||||
|
credentials
|
||||||
|
|
||||||
|
-- TODO
|
||||||
|
RedactedEvent _ _ ->
|
||||||
|
credentials
|
||||||
|
|
||||||
|
-- TODO
|
||||||
|
StateEventSent _ _ ->
|
||||||
|
credentials
|
||||||
|
|
||||||
|
-- TODO
|
||||||
|
SyncUpdate input output ->
|
||||||
|
let
|
||||||
|
jRooms : List IRoom.IRoom
|
||||||
|
jRooms =
|
||||||
|
output.rooms
|
||||||
|
|> Maybe.map .join
|
||||||
|
|> Maybe.withDefault Dict.empty
|
||||||
|
|> Dict.toList
|
||||||
|
|> List.map
|
||||||
|
(\( roomId, jroom ) ->
|
||||||
|
case getRoomById roomId credentials of
|
||||||
|
-- Update existing room
|
||||||
|
Just room ->
|
||||||
|
case jroom.timeline of
|
||||||
|
Just timeline ->
|
||||||
|
room
|
||||||
|
|> Room.withoutContext
|
||||||
|
|> IRoom.addEvents
|
||||||
|
{ events =
|
||||||
|
List.map
|
||||||
|
(Event.initFromClientEventWithoutRoomId roomId)
|
||||||
|
timeline.events
|
||||||
|
, limited = timeline.limited
|
||||||
|
, nextBatch = output.nextBatch
|
||||||
|
, prevBatch =
|
||||||
|
timeline.prevBatch
|
||||||
|
|> Maybe.withDefault
|
||||||
|
(Maybe.withDefault "" input.since)
|
||||||
|
, stateDelta =
|
||||||
|
jroom.state
|
||||||
|
|> Maybe.map
|
||||||
|
(.events
|
||||||
|
>> List.map (Event.initFromClientEventWithoutRoomId roomId)
|
||||||
|
>> StateManager.fromEventList
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Nothing ->
|
||||||
|
Room.withoutContext room
|
||||||
|
|
||||||
|
-- Add new room
|
||||||
|
Nothing ->
|
||||||
|
jroom
|
||||||
|
|> Room.initFromJoinedRoom { nextBatch = output.nextBatch, roomId = roomId }
|
||||||
|
)
|
||||||
|
in
|
||||||
|
cred
|
||||||
|
|> Internal.addSince output.nextBatch
|
||||||
|
|> List.foldl Internal.insertRoom
|
||||||
|
|> (|>) jRooms
|
||||||
|
|> (\x -> { cred = x, context = context })
|
||||||
|
|> Vault
|
||||||
|
|
||||||
|
UpdateAccessToken token ->
|
||||||
|
Vault { data | context = Context.addToken token context }
|
||||||
|
|
||||||
|
UpdateVersions versions ->
|
||||||
|
Vault { data | context = Context.addVersions versions context }
|
||||||
|
|
||||||
|
-- TODO: Save all info
|
||||||
|
LoggedInWithUsernameAndPassword _ output ->
|
||||||
|
Vault { data | context = Context.addToken output.accessToken context }
|
||||||
|
|
||||||
|
|
||||||
|
{-| Synchronize credentials
|
||||||
|
-}
|
||||||
|
sync : Vault -> Task X.Error CredUpdate
|
||||||
|
sync (Vault { cred, context }) =
|
||||||
|
Api.sync
|
||||||
|
{ accessToken = Context.accessToken context
|
||||||
|
, baseUrl = Context.baseUrl context
|
||||||
|
, filter = Nothing
|
||||||
|
, fullState = Nothing
|
||||||
|
, setPresence = Nothing
|
||||||
|
, since = Internal.getSince cred
|
||||||
|
, timeout = Just 30
|
||||||
|
, versions = Context.versions context
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{-| Get a list of all synchronised rooms.
|
||||||
|
-}
|
||||||
|
rooms : Vault -> List Room.Room
|
||||||
|
rooms (Vault { cred, context }) =
|
||||||
|
cred
|
||||||
|
|> Internal.getRooms
|
||||||
|
|> List.map (Room.withContext context)
|
Loading…
Reference in New Issue