Compare commits
8 Commits
2e8185841a
...
becd3bcdb1
Author | SHA1 | Date |
---|---|---|
Bram | becd3bcdb1 | |
Bram | e786bebeb2 | |
Bram | 3ee6debf44 | |
Bram | b6e4396138 | |
Bram | 568afed458 | |
Bram | 2714b53a2d | |
Bram | 3fdd25d6d6 | |
Bram | e49a0e3dc3 |
|
@ -1,4 +1,7 @@
|
|||
module Internal.Api.Api exposing (..)
|
||||
module Internal.Api.Api exposing
|
||||
( TaskChain, request
|
||||
, VersionControl, startWithVersion, startWithUnstableFeature, forVersion, sameForVersion, forUnstableFeature, versionChain
|
||||
)
|
||||
|
||||
{-|
|
||||
|
||||
|
@ -7,35 +10,55 @@ module Internal.Api.Api exposing (..)
|
|||
|
||||
The API module is a front-end for implementing API endpoints according to spec.
|
||||
|
||||
This module is imported by various API endpoint implementations to keep the
|
||||
implementation simple and understandable.
|
||||
|
||||
@docs TaskChain, request
|
||||
|
||||
|
||||
## Spec versions
|
||||
|
||||
To respect spec versions, there is often a variety of ways to communicate with
|
||||
the homeserver. For this reason, users can differentiate spec versions using
|
||||
these functions.
|
||||
|
||||
@docs VersionControl, startWithVersion, startWithUnstableFeature, forVersion, sameForVersion, forUnstableFeature, versionChain
|
||||
|
||||
-}
|
||||
|
||||
import Internal.Api.Chain as C
|
||||
import Internal.Api.Request as R
|
||||
import Internal.Config.Log exposing (log)
|
||||
import Internal.Config.Log exposing (Log, log)
|
||||
import Internal.Tools.Json as Json
|
||||
import Internal.Values.Context exposing (APIContext)
|
||||
import Internal.Values.Context as Context exposing (APIContext, Versions)
|
||||
import Internal.Values.Envelope as E
|
||||
import Internal.Values.Vault as V
|
||||
import Recursion
|
||||
import Set
|
||||
|
||||
|
||||
{-| A TaskChain helps create a chain of HTTP requests.
|
||||
-}
|
||||
type alias TaskChain ph1 ph2 =
|
||||
C.TaskChain R.Error V.VaultUpdate { ph1 | baseUrl : () } { ph2 | baseUrl : () }
|
||||
C.TaskChain R.Error (E.EnvelopeUpdate V.VaultUpdate) { ph1 | baseUrl : () } { ph2 | baseUrl : () }
|
||||
|
||||
|
||||
{-| Make an HTTP request that adheres to the Matrix spec rules.
|
||||
-}
|
||||
request :
|
||||
{ attributes : List (R.Attribute { ph1 | baseUrl : () })
|
||||
, coder : Json.Coder V.VaultUpdate
|
||||
, contextChange : V.VaultUpdate -> (APIContext { ph1 | baseUrl : () } -> APIContext { ph2 | baseUrl : () })
|
||||
, coder : Json.Coder returnValue
|
||||
, contextChange : returnValue -> (APIContext { ph1 | baseUrl : () } -> APIContext { ph2 | baseUrl : () })
|
||||
, method : String
|
||||
, path : List String
|
||||
, toUpdate : returnValue -> ( E.EnvelopeUpdate V.VaultUpdate, List Log )
|
||||
}
|
||||
-> TaskChain ph1 ph2
|
||||
request data =
|
||||
R.toChain
|
||||
{ logHttp =
|
||||
\r ->
|
||||
( V.HttpRequest r
|
||||
( E.HttpRequest r
|
||||
, String.concat
|
||||
-- TODO: Move this to Internal.Config.Text module
|
||||
[ "Matrix HTTP: "
|
||||
|
@ -54,4 +77,120 @@ request data =
|
|||
}
|
||||
|> R.withAttributes data.attributes
|
||||
, toContextChange = data.contextChange
|
||||
, toUpdate = data.toUpdate
|
||||
}
|
||||
|
||||
|
||||
{-| This type allows different definitions for different spec versions,
|
||||
allowing the Elm SDK to communicate differently to the server depending on
|
||||
how up-to-date the server is.
|
||||
-}
|
||||
type VersionControl a ph1 ph2
|
||||
= VC
|
||||
{ name : VersionType
|
||||
, chain : a -> TaskChain (WithV ph1) (WithV ph2)
|
||||
, prev : Maybe (VersionControl a ph1 ph2)
|
||||
}
|
||||
|
||||
|
||||
type VersionType
|
||||
= SpecVersion String
|
||||
| UnstableFeature String
|
||||
|
||||
|
||||
type alias WithV ph =
|
||||
{ ph | versions : () }
|
||||
|
||||
|
||||
{-| Start with a given spec version supporting a given API endpoint.
|
||||
-}
|
||||
startWithVersion : String -> (a -> TaskChain (WithV ph1) (WithV ph2)) -> VersionControl a ph1 ph2
|
||||
startWithVersion name chain =
|
||||
VC
|
||||
{ name = SpecVersion name
|
||||
, chain = chain
|
||||
, prev = Nothing
|
||||
}
|
||||
|
||||
|
||||
{-| Start with a given unstable feature supporting a given API endpoint.
|
||||
-}
|
||||
startWithUnstableFeature : String -> (a -> TaskChain (WithV ph1) (WithV ph2)) -> VersionControl a ph1 ph2
|
||||
startWithUnstableFeature name chain =
|
||||
VC
|
||||
{ name = UnstableFeature name
|
||||
, chain = chain
|
||||
, prev = Nothing
|
||||
}
|
||||
|
||||
|
||||
{-| Add a new unstable feature that supports a different version of the API endpoint.
|
||||
-}
|
||||
forUnstableFeature : String -> (a -> TaskChain (WithV ph1) (WithV ph2)) -> VersionControl a ph1 ph2 -> VersionControl a ph1 ph2
|
||||
forUnstableFeature name chain prev =
|
||||
VC
|
||||
{ name = UnstableFeature name
|
||||
, chain = chain
|
||||
, prev = Just prev
|
||||
}
|
||||
|
||||
|
||||
{-| Add a new spec version that supports a different version of the API endpoint.
|
||||
-}
|
||||
forVersion : String -> (a -> TaskChain (WithV ph1) (WithV ph2)) -> VersionControl a ph1 ph2 -> VersionControl a ph1 ph2
|
||||
forVersion name chain prev =
|
||||
VC
|
||||
{ name = SpecVersion name
|
||||
, chain = chain
|
||||
, prev = Just prev
|
||||
}
|
||||
|
||||
|
||||
{-| Add another spec version that has the API endpoint defined the same as the previous API endpoint.
|
||||
-}
|
||||
sameForVersion : String -> VersionControl a ph1 ph2 -> VersionControl a ph1 ph2
|
||||
sameForVersion name (VC data) =
|
||||
VC
|
||||
{ name = SpecVersion name
|
||||
, chain = data.chain
|
||||
, prev = Just (VC data)
|
||||
}
|
||||
|
||||
|
||||
supportedVersion : Versions -> VersionType -> Bool
|
||||
supportedVersion { versions, unstableFeatures } name =
|
||||
case name of
|
||||
SpecVersion n ->
|
||||
List.member n versions
|
||||
|
||||
UnstableFeature n ->
|
||||
Set.member n unstableFeatures
|
||||
|
||||
|
||||
|
||||
-- NOTE: Interesting detail! For some reason, I cannot add the `context`
|
||||
-- NOTE: variable to the top line of the defined input values!
|
||||
-- NOTE: Maybe this is a bug?
|
||||
|
||||
|
||||
{-| Once you are done, turn a VersionControl type into a Task Chain.
|
||||
-}
|
||||
versionChain : VersionControl a ph1 ph2 -> a -> TaskChain (WithV ph1) (WithV ph2)
|
||||
versionChain vc input =
|
||||
\context ->
|
||||
case Context.getVersions context of
|
||||
versions ->
|
||||
Recursion.runRecursion
|
||||
(\mvc ->
|
||||
case mvc of
|
||||
Nothing ->
|
||||
Recursion.base (C.fail R.NoSupportedVersion context)
|
||||
|
||||
Just (VC data) ->
|
||||
if supportedVersion versions data.name then
|
||||
Recursion.base (data.chain input context)
|
||||
|
||||
else
|
||||
Recursion.recurse data.prev
|
||||
)
|
||||
(Just vc)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
module Internal.Api.Chain exposing
|
||||
( TaskChain, CompleteChain
|
||||
, IdemChain, toTask
|
||||
, fail, succeed, andThen
|
||||
)
|
||||
|
||||
{-|
|
||||
|
@ -23,10 +24,15 @@ avoid leaking values passing through the API in unexpected ways.
|
|||
|
||||
@docs IdemChain, toTask
|
||||
|
||||
|
||||
## Operations
|
||||
|
||||
@docs fail, succeed, andThen
|
||||
|
||||
-}
|
||||
|
||||
import Internal.Config.Log exposing (Log)
|
||||
import Internal.Values.Context as Context exposing (APIContext)
|
||||
import Internal.Values.Context exposing (APIContext)
|
||||
import Task
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,246 @@
|
|||
module Internal.Api.GetEvent.Api exposing (GetEventInput, getEvent)
|
||||
|
||||
{-|
|
||||
|
||||
|
||||
# Get event
|
||||
|
||||
Get a single event based on `roomId/eventId`. You must have permission to
|
||||
retrieve this event e.g. by being a member in the room for this event.
|
||||
|
||||
@docs GetEventInput, getEvent
|
||||
|
||||
-}
|
||||
|
||||
import Internal.Api.Api as A
|
||||
import Internal.Api.Request as R
|
||||
import Internal.Config.Log exposing (log)
|
||||
import Internal.Tools.Json as Json
|
||||
import Internal.Tools.Timestamp as Timestamp
|
||||
import Internal.Values.Envelope as E
|
||||
import Internal.Values.Event as Event exposing (Event)
|
||||
import Internal.Values.Room as Room
|
||||
import Internal.Values.User as User
|
||||
import Internal.Values.Vault as V
|
||||
|
||||
|
||||
{-| Input for getting an event.
|
||||
-}
|
||||
type alias GetEventInput =
|
||||
{ eventId : String, roomId : String }
|
||||
|
||||
|
||||
{-| Standard input for version 1 of the GetEvent API endpoint.
|
||||
-}
|
||||
type alias GetEventInputV1 a =
|
||||
{ a | eventId : String, roomId : String }
|
||||
|
||||
|
||||
{-| Universal phantom type encompassing all versions of this API endpoint.
|
||||
-}
|
||||
type alias Phantom a =
|
||||
PhantomV1 { a | versions : () }
|
||||
|
||||
|
||||
{-| Phantom values necessary for version 1 of the GetEvent API endpoint.
|
||||
-}
|
||||
type alias PhantomV1 a =
|
||||
{ a | accessToken : (), baseUrl : () }
|
||||
|
||||
|
||||
{-| Get an event based on a room id and event id.
|
||||
-}
|
||||
getEvent : GetEventInput -> A.TaskChain (Phantom a) (Phantom a)
|
||||
getEvent =
|
||||
A.startWithVersion "r0.5.0" getEventV1
|
||||
|> A.sameForVersion "r0.6.0"
|
||||
|> A.sameForVersion "r0.6.1"
|
||||
|> A.forVersion "v1.1" getEventV2
|
||||
|> A.sameForVersion "v1.2"
|
||||
|> A.sameForVersion "v1.3"
|
||||
|> A.sameForVersion "v1.4"
|
||||
|> A.sameForVersion "v1.5"
|
||||
|> A.sameForVersion "v1.6"
|
||||
|> A.sameForVersion "v1.7"
|
||||
|> A.sameForVersion "v1.8"
|
||||
|> A.sameForVersion "v1.9"
|
||||
|> A.sameForVersion "v1.10"
|
||||
|> A.versionChain
|
||||
|
||||
|
||||
{-| Version 1 of the GetEvent API endpoint
|
||||
-}
|
||||
getEventV1 : GetEventInputV1 input -> A.TaskChain (PhantomV1 a) (PhantomV1 a)
|
||||
getEventV1 { eventId, roomId } =
|
||||
A.request
|
||||
{ attributes =
|
||||
[ R.accessToken
|
||||
, R.onStatusCode 404 "M_NOT_FOUND"
|
||||
]
|
||||
, coder = getEventCoderV1
|
||||
, contextChange = always identity
|
||||
, method = "GET"
|
||||
, path = [ "_matrix", "client", "r0", "rooms", roomId, "event", eventId ]
|
||||
, toUpdate =
|
||||
\event ->
|
||||
( E.ContentUpdate <| V.MapRoom roomId (Room.AddEvent event)
|
||||
, event.eventId
|
||||
|> (++) "Received event id "
|
||||
|> log.debug
|
||||
|> List.singleton
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
{-| Version 2 of the GetEvent API endpoint
|
||||
-}
|
||||
getEventV2 : GetEventInputV1 input -> A.TaskChain (PhantomV1 a) (PhantomV1 a)
|
||||
getEventV2 { eventId, roomId } =
|
||||
A.request
|
||||
{ attributes =
|
||||
[ R.accessToken
|
||||
, R.onStatusCode 404 "M_NOT_FOUND"
|
||||
]
|
||||
, coder = getEventCoderV1
|
||||
, contextChange = always identity
|
||||
, method = "GET"
|
||||
, path = [ "_matrix", "client", "v3", "rooms", roomId, "event", eventId ]
|
||||
, toUpdate =
|
||||
\event ->
|
||||
( E.ContentUpdate <| V.MapRoom roomId (Room.AddEvent event)
|
||||
, event.eventId
|
||||
|> (++) "Received event id "
|
||||
|> log.debug
|
||||
|> List.singleton
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
getEventCoderV1 : Json.Coder Event
|
||||
getEventCoderV1 =
|
||||
Json.object8
|
||||
{ name = "ClientEvent"
|
||||
, description =
|
||||
[ "ClientEvent as described by the Matrix spec"
|
||||
, "https://spec.matrix.org/v1.10/client-server-api/#get_matrixclientv3roomsroomideventeventid"
|
||||
]
|
||||
, init = Event
|
||||
}
|
||||
(Json.field.required
|
||||
{ fieldName = "content"
|
||||
, toField = .content
|
||||
, description =
|
||||
[ "The body of this event, as created by the client which sent it."
|
||||
]
|
||||
, coder = Json.value
|
||||
}
|
||||
)
|
||||
(Json.field.required
|
||||
{ fieldName = "event_id"
|
||||
, toField = .eventId
|
||||
, description =
|
||||
[ "The globally unique identifier for this event."
|
||||
]
|
||||
, coder = Json.string
|
||||
}
|
||||
)
|
||||
(Json.field.required
|
||||
{ fieldName = "origin_server_ts"
|
||||
, toField = .originServerTs
|
||||
, description =
|
||||
[ "Timestamp (in milliseconds since the unix epoch) on originating homeserver when this event was sent."
|
||||
]
|
||||
, coder = Timestamp.coder
|
||||
}
|
||||
)
|
||||
(Json.field.required
|
||||
{ fieldName = "room_id"
|
||||
, toField = .roomId
|
||||
, description =
|
||||
[ "The ID of the room associated with this event."
|
||||
]
|
||||
, coder = Json.string
|
||||
}
|
||||
)
|
||||
(Json.field.required
|
||||
{ fieldName = "sender"
|
||||
, toField = .sender
|
||||
, description =
|
||||
[ "Contains the fully-qualified ID of the user who sent this event."
|
||||
]
|
||||
, coder = User.coder
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "state_key"
|
||||
, toField = .stateKey
|
||||
, description =
|
||||
[ "Present if, and only if, this event is a state event. The key making this piece of state unique in the room. Note that it is often an empty string."
|
||||
, "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’s ID as the state key MUST only be set by that user."
|
||||
]
|
||||
, coder = Json.string
|
||||
}
|
||||
)
|
||||
(Json.field.required
|
||||
{ fieldName = "type"
|
||||
, toField = .eventType
|
||||
, description =
|
||||
[ "The type of the event."
|
||||
]
|
||||
, coder = Json.string
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "unsigned"
|
||||
, toField = .unsigned
|
||||
, description =
|
||||
[ "Contains optional extra information about the event."
|
||||
]
|
||||
, coder =
|
||||
Json.object4
|
||||
{ name = "UnsignedData"
|
||||
, description =
|
||||
[ "UnsignedData as described by the Matrix spec"
|
||||
, "https://spec.matrix.org/v1.10/client-server-api/#get_matrixclientv3roomsroomideventeventid"
|
||||
]
|
||||
, init = \a b c d -> Event.UnsignedData { age = a, prevContent = b, redactedBecause = c, transactionId = d }
|
||||
}
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "age"
|
||||
, toField = \(Event.UnsignedData data) -> data.age
|
||||
, description =
|
||||
[ "The time in milliseconds that has elapsed since the event was sent. This field is generated by the local homeserver, and may be incorrect if the local time on at least one of the two servers is out of sync, which can cause the age to either be negative or greater than it actually is."
|
||||
]
|
||||
, coder = Json.int
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "prev_content"
|
||||
, toField = \(Event.UnsignedData data) -> data.prevContent
|
||||
, description =
|
||||
[ " The previous content for this event. This field is generated by the local homeserver, and is only returned if the event is a state event, and the client has permission to see the previous content."
|
||||
, "Changed in v1.2: Previously, this field was specified at the top level of returned events rather than in unsigned (with the exception of the GET .../notifications endpoint), though in practice no known server implementations honoured this."
|
||||
]
|
||||
, coder = Json.value
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "redacted_because"
|
||||
, toField = \(Event.UnsignedData data) -> data.redactedBecause
|
||||
, description =
|
||||
[ "The event that redacted this event, if any."
|
||||
]
|
||||
, coder = Json.lazy (\() -> getEventCoderV1)
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "transaction_id"
|
||||
, toField = \(Event.UnsignedData data) -> data.transactionId
|
||||
, description =
|
||||
[ "The client-supplied transaction ID, for example, provided via PUT /_matrix/client/v3/rooms/{roomId}/send/{eventType}/{txnId}, if the client being given the event is the same one which sent it."
|
||||
]
|
||||
, coder = Json.string
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
|
@ -0,0 +1,138 @@
|
|||
module Internal.Api.Invite.Api exposing (InviteInput, Phantom, invite)
|
||||
|
||||
{-|
|
||||
|
||||
|
||||
# Invite
|
||||
|
||||
This API invites a user to participate in a particular room. They do not start
|
||||
participating in the room until they actually join the room.
|
||||
|
||||
Only users currently in a particular room can invite other users to join that
|
||||
room.
|
||||
|
||||
If the user was invited to the room, the homeserver will append a m.room.member
|
||||
event to the room.
|
||||
|
||||
@docs InviteInput, Phantom, invite
|
||||
|
||||
-}
|
||||
|
||||
import Internal.Api.Api as A
|
||||
import Internal.Api.Request as R
|
||||
import Internal.Config.Log exposing (log)
|
||||
import Internal.Tools.Json as Json
|
||||
import Internal.Values.Envelope as E
|
||||
import Internal.Values.Room as Room
|
||||
import Internal.Values.User as User exposing (User)
|
||||
import Internal.Values.Vault as V
|
||||
|
||||
|
||||
{-| Invite a user to a room.
|
||||
-}
|
||||
invite : InviteInput -> A.TaskChain (Phantom ph1) (Phantom ph1)
|
||||
invite =
|
||||
A.startWithVersion "r0.0.0" inviteV1
|
||||
|> A.sameForVersion "r0.0.1"
|
||||
|> A.sameForVersion "r0.1.0"
|
||||
|> A.sameForVersion "r0.2.0"
|
||||
|> A.sameForVersion "r0.3.0"
|
||||
|> A.sameForVersion "r0.4.0"
|
||||
|> A.sameForVersion "r0.5.0"
|
||||
|> A.sameForVersion "r0.6.0"
|
||||
|> A.sameForVersion "r0.6.1"
|
||||
|> A.forVersion "v1.1" inviteV2
|
||||
|> A.sameForVersion "v1.2"
|
||||
|> A.sameForVersion "v1.3"
|
||||
|> A.sameForVersion "v1.4"
|
||||
|> A.sameForVersion "v1.5"
|
||||
|> A.sameForVersion "v1.6"
|
||||
|> A.sameForVersion "v1.7"
|
||||
|> A.sameForVersion "v1.8"
|
||||
|> A.sameForVersion "v1.9"
|
||||
|> A.sameForVersion "v1.10"
|
||||
|> A.versionChain
|
||||
|
||||
|
||||
{-| Context needed for inviting a user.
|
||||
-}
|
||||
type alias Phantom a =
|
||||
{ a | accessToken : (), versions : () }
|
||||
|
||||
|
||||
type alias PhantomV1 a =
|
||||
{ a | accessToken : () }
|
||||
|
||||
|
||||
{-| Input for inviting a user.
|
||||
-}
|
||||
type alias InviteInput =
|
||||
{ reason : Maybe String, roomId : String, user : User }
|
||||
|
||||
|
||||
type alias InviteInputV1 a =
|
||||
{ a | roomId : String, user : User }
|
||||
|
||||
|
||||
type alias InviteInputV2 a =
|
||||
{ a | roomId : String, user : User, reason : Maybe String }
|
||||
|
||||
|
||||
inviteV1 : InviteInputV1 a -> A.TaskChain (PhantomV1 ph1) (PhantomV1 ph1)
|
||||
inviteV1 { roomId, user } =
|
||||
A.request
|
||||
{ attributes =
|
||||
[ R.accessToken
|
||||
, R.bodyString "user_id" (User.toString user)
|
||||
, R.onStatusCode 400 "M_UNKNOWN"
|
||||
, R.onStatusCode 403 "M_FORBIDDEN"
|
||||
, R.onStatusCode 429 "M_LIMIT_EXCEEDED"
|
||||
]
|
||||
, coder = Json.value
|
||||
, contextChange = always identity
|
||||
, method = "POST"
|
||||
, path = [ "_matrix", "client", "r0", "rooms", roomId, "invite" ]
|
||||
, toUpdate =
|
||||
always
|
||||
( E.ContentUpdate <| V.MapRoom roomId (Room.Invite user)
|
||||
, String.concat
|
||||
-- TODO: Move to Internal.Config.Text
|
||||
[ "Invited user "
|
||||
, User.toString user
|
||||
, " to room "
|
||||
, roomId
|
||||
]
|
||||
|> log.debug
|
||||
|> List.singleton
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
inviteV2 : InviteInputV2 a -> A.TaskChain (PhantomV1 ph1) (PhantomV1 ph1)
|
||||
inviteV2 { reason, roomId, user } =
|
||||
A.request
|
||||
{ attributes =
|
||||
[ R.bodyOpString "reason" reason
|
||||
, R.bodyString "user_id" (User.toString user)
|
||||
, R.onStatusCode 400 "M_UNKNOWN"
|
||||
, R.onStatusCode 403 "M_FORBIDDEN"
|
||||
, R.onStatusCode 429 "M_LIMIT_EXCEEDED"
|
||||
]
|
||||
, coder = Json.value
|
||||
, contextChange = always identity
|
||||
, method = "POST"
|
||||
, path = [ "_matrix", "client", "v3", "rooms", roomId, "invite" ]
|
||||
, toUpdate =
|
||||
always
|
||||
( E.ContentUpdate <| V.MapRoom roomId (Room.Invite user)
|
||||
, String.concat
|
||||
-- TODO: Move to Internal.Config.Text
|
||||
[ "Invited user "
|
||||
, User.toString user
|
||||
, " to room "
|
||||
, roomId
|
||||
]
|
||||
|> log.debug
|
||||
|> List.singleton
|
||||
)
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
module Internal.Api.Request exposing
|
||||
( ApiCall, ApiPlan, Attribute, callAPI, withAttributes, toChain
|
||||
, Request, Error(..)
|
||||
, accessToken, withTransactionId, timeout
|
||||
, accessToken, withTransactionId, timeout, onStatusCode
|
||||
, fullBody, bodyBool, bodyInt, bodyString, bodyValue, bodyOpBool, bodyOpInt, bodyOpString, bodyOpValue
|
||||
, queryBool, queryInt, queryString, queryOpBool, queryOpInt, queryOpString
|
||||
)
|
||||
|
@ -28,7 +28,7 @@ Sometimes, APIs might fail. As a result, you may receive an error.
|
|||
|
||||
### General attributes
|
||||
|
||||
@docs accessToken, withTransactionId, timeout
|
||||
@docs accessToken, withTransactionId, timeout, onStatusCode
|
||||
|
||||
|
||||
### Body
|
||||
|
@ -98,7 +98,9 @@ type ContextAttr
|
|||
-}
|
||||
type Error
|
||||
= InternetException Http.Error
|
||||
| NoSupportedVersion
|
||||
| ServerReturnsBadJSON String
|
||||
| ServerReturnsError String Json.Value
|
||||
|
||||
|
||||
{-| Ordinary shape of an HTTP request.
|
||||
|
@ -396,43 +398,28 @@ getUrl { attributes, baseUrl, path } =
|
|||
(getQueryParams attributes)
|
||||
|
||||
|
||||
{-| Resolve the response of a Matrix API call.
|
||||
{-| When the HTTP request cannot be deciphered but the status code is known,
|
||||
return with a given default error.
|
||||
-}
|
||||
rawApiCallResolver : Json.Coder a -> Dict.Dict Int ( Error, List Log ) -> Http.Resolver ( Error, List Log ) ( a, List Log )
|
||||
rawApiCallResolver coder statusCodeErrors =
|
||||
Http.stringResolver
|
||||
(\response ->
|
||||
case response of
|
||||
Http.BadUrl_ s ->
|
||||
Http.BadUrl s
|
||||
|> InternetException
|
||||
|> Tuple.pair
|
||||
|> (|>) []
|
||||
|> Err
|
||||
|
||||
Http.Timeout_ ->
|
||||
Http.Timeout
|
||||
|> InternetException
|
||||
|> Tuple.pair
|
||||
|> (|>) []
|
||||
|> Err
|
||||
|
||||
Http.NetworkError_ ->
|
||||
Http.NetworkError
|
||||
|> InternetException
|
||||
|> Tuple.pair
|
||||
|> (|>) []
|
||||
|> Err
|
||||
|
||||
Http.BadStatus_ metadata body ->
|
||||
statusCodeErrors
|
||||
|> Dict.get metadata.statusCode
|
||||
|> decodeServerResponse (Json.decode coder) body
|
||||
|
||||
Http.GoodStatus_ metadata body ->
|
||||
statusCodeErrors
|
||||
|> Dict.get metadata.statusCode
|
||||
|> decodeServerResponse (Json.decode coder) body
|
||||
onStatusCode : Int -> String -> Attribute a
|
||||
onStatusCode code err _ =
|
||||
StatusCodeResponse code
|
||||
( err
|
||||
|> E.string
|
||||
|> Tuple.pair "errcode"
|
||||
|> List.singleton
|
||||
|> E.object
|
||||
|> ServerReturnsError err
|
||||
, String.concat
|
||||
-- TODO: Move to Internal.Config.Text
|
||||
[ "Received an invalid HTTP response from Matrix server "
|
||||
, "but managed to decode it using the status code "
|
||||
, String.fromInt code
|
||||
, ": Default to errcode "
|
||||
, err
|
||||
]
|
||||
|> log.warn
|
||||
|> List.singleton
|
||||
)
|
||||
|
||||
|
||||
|
@ -500,6 +487,48 @@ queryString key value _ =
|
|||
QueryParam <| UrlBuilder.string key value
|
||||
|
||||
|
||||
{-| Resolve the response of a Matrix API call.
|
||||
-}
|
||||
rawApiCallResolver : D.Decoder ( a, List Log ) -> Dict.Dict Int ( Error, List Log ) -> Http.Resolver ( Error, List Log ) ( a, List Log )
|
||||
rawApiCallResolver decoder statusCodeErrors =
|
||||
Http.stringResolver
|
||||
(\response ->
|
||||
case response of
|
||||
Http.BadUrl_ s ->
|
||||
Http.BadUrl s
|
||||
|> InternetException
|
||||
|> Tuple.pair
|
||||
|> (|>) []
|
||||
|> Err
|
||||
|
||||
Http.Timeout_ ->
|
||||
Http.Timeout
|
||||
|> InternetException
|
||||
|> Tuple.pair
|
||||
|> (|>) []
|
||||
|> Err
|
||||
|
||||
Http.NetworkError_ ->
|
||||
Http.NetworkError
|
||||
|> InternetException
|
||||
|> Tuple.pair
|
||||
|> (|>) []
|
||||
|> Err
|
||||
|
||||
Http.BadStatus_ metadata body ->
|
||||
statusCodeErrors
|
||||
|> Dict.get metadata.statusCode
|
||||
|> decodeServerResponse decoder body
|
||||
|
||||
Http.GoodStatus_ metadata body ->
|
||||
statusCodeErrors
|
||||
|> Dict.get metadata.statusCode
|
||||
|> decodeServerResponse decoder body
|
||||
)
|
||||
|
||||
|
||||
{-| Configure the HTTP request to time out after a given expiry time.
|
||||
-}
|
||||
timeout : Float -> Attribute a
|
||||
timeout f _ =
|
||||
Timeout f
|
||||
|
@ -509,34 +538,57 @@ timeout f _ =
|
|||
-}
|
||||
toChain :
|
||||
{ logHttp : Request ( Error, List Log ) ( update, List Log ) -> ( update, List Log )
|
||||
, coder : Json.Coder update
|
||||
, coder : Json.Coder httpOut
|
||||
, request : ApiPlan ph1
|
||||
, toContextChange : update -> (APIContext ph1 -> APIContext ph2)
|
||||
, toContextChange : httpOut -> (APIContext ph1 -> APIContext ph2)
|
||||
, toUpdate : httpOut -> ( update, List Log )
|
||||
}
|
||||
-> C.TaskChain Error update ph1 ph2
|
||||
toChain data apiContext =
|
||||
data.request apiContext
|
||||
|> (\call ->
|
||||
let
|
||||
r : Request ( Error, List Log ) ( update, List Log )
|
||||
r : Request ( Error, List Log ) ( httpOut, List Log )
|
||||
r =
|
||||
{ method = call.method
|
||||
, headers = getHeaders call.attributes
|
||||
, url = getUrl call
|
||||
, body = Http.jsonBody (getBody call.attributes)
|
||||
, resolver = rawApiCallResolver data.coder (getStatusCodes call.attributes)
|
||||
, resolver = rawApiCallResolver (Json.decode data.coder) (getStatusCodes call.attributes)
|
||||
, timeout = getTimeout call.attributes
|
||||
}
|
||||
|
||||
logR : Request ( Error, List Log ) ( update, List Log )
|
||||
logR =
|
||||
{ method = call.method
|
||||
, headers = getHeaders call.attributes
|
||||
, url = getUrl call
|
||||
, body = Http.jsonBody (getBody call.attributes)
|
||||
, resolver =
|
||||
rawApiCallResolver
|
||||
(Json.decode data.coder
|
||||
|> D.map
|
||||
(\( out, logs ) ->
|
||||
case data.toUpdate out of
|
||||
( u, uLogs ) ->
|
||||
( u, List.append logs uLogs )
|
||||
)
|
||||
)
|
||||
(getStatusCodes call.attributes)
|
||||
, timeout = getTimeout call.attributes
|
||||
}
|
||||
in
|
||||
case data.logHttp r of
|
||||
case data.logHttp logR of
|
||||
( httpU, httpLogs ) ->
|
||||
Http.task r
|
||||
|> Task.map
|
||||
(\( u, logs ) ->
|
||||
{ contextChange = data.toContextChange u
|
||||
, logs = List.append httpLogs logs
|
||||
, messages = [ httpU, u ]
|
||||
}
|
||||
(\( httpO, logs ) ->
|
||||
case data.toUpdate httpO of
|
||||
( u, uLogs ) ->
|
||||
{ contextChange = data.toContextChange httpO
|
||||
, logs = List.concat [ httpLogs, logs, uLogs ]
|
||||
, messages = [ httpU, u ]
|
||||
}
|
||||
)
|
||||
|> Task.mapError
|
||||
(\( err, logs ) ->
|
||||
|
|
|
@ -1,4 +1,23 @@
|
|||
module Internal.Api.Task exposing (..)
|
||||
module Internal.Api.Task exposing (Task, run)
|
||||
|
||||
{-|
|
||||
|
||||
|
||||
# Task module
|
||||
|
||||
This module is used to define how API calls are made. These completed API tasks
|
||||
can be directly converted to Cmd types that the end user of the SDK can access.
|
||||
|
||||
These tasks do not affect the `Vault` directly, but instead, return a
|
||||
`VaultUpdate` type that the user can apply to keep their `Vault` type
|
||||
up-to-date.
|
||||
|
||||
|
||||
## Use
|
||||
|
||||
@docs Task, run
|
||||
|
||||
-}
|
||||
|
||||
import Internal.Api.Chain as C
|
||||
import Internal.Api.Request as Request
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
module Internal.Api.Versions.Api exposing (versions, Phantom)
|
||||
|
||||
{-|
|
||||
|
||||
|
||||
# Versions
|
||||
|
||||
Ask the Matrix API which versions it supports.
|
||||
|
||||
@docs versions, Phantom
|
||||
|
||||
-}
|
||||
|
||||
import Dict
|
||||
import Internal.Api.Api as A
|
||||
import Internal.Tools.Json as Json
|
||||
import Internal.Values.Context as Context exposing (Versions)
|
||||
import Internal.Values.Envelope as E
|
||||
import Set
|
||||
|
||||
|
||||
{-| Task chain to ask which spec versions the Matrix API supports.
|
||||
-}
|
||||
versions : A.TaskChain (Phantom ph) (Phantom { ph | versions : () })
|
||||
versions =
|
||||
A.request
|
||||
{ attributes = []
|
||||
, coder = versionsCoder
|
||||
, contextChange = Context.setVersions
|
||||
, method = "GET"
|
||||
, path = [ "_matrix", "client", "versions" ]
|
||||
, toUpdate = \v -> ( E.SetVersions v, [] )
|
||||
}
|
||||
|
||||
|
||||
{-| Context needed for asking the server's available spec versions
|
||||
-}
|
||||
type alias Phantom a =
|
||||
{ a | baseUrl : () }
|
||||
|
||||
|
||||
versionsCoder : Json.Coder Versions
|
||||
versionsCoder =
|
||||
Json.object2
|
||||
{ name = "Versions"
|
||||
, description =
|
||||
[ "Gets the versions of the specification supported by the server."
|
||||
, "Values will take the form vX.Y or rX.Y.Z in historical cases. See the Specification Versioning for more information."
|
||||
, "The server may additionally advertise experimental features it supports through unstable_features. These features should be namespaced and may optionally include version information within their name if desired. Features listed here are not for optionally toggling parts of the Matrix specification and should only be used to advertise support for a feature which has not yet landed in the spec. For example, a feature currently undergoing the proposal process may appear here and eventually be taken off this list once the feature lands in the spec and the server deems it reasonable to do so. Servers can choose to enable some features only for some users, so clients should include authentication in the request to get all the features available for the logged-in user. If no authentication is provided, the server should only return the features available to all users. Servers may wish to keep advertising features here after they’ve been released into the spec to give clients a chance to upgrade appropriately. Additionally, clients should avoid using unstable features in their stable releases."
|
||||
]
|
||||
, init = Versions
|
||||
}
|
||||
(Json.field.required
|
||||
{ fieldName = "versions"
|
||||
, toField = .versions
|
||||
, description =
|
||||
[ "The supported versions."
|
||||
]
|
||||
, coder = Json.list Json.string
|
||||
}
|
||||
)
|
||||
(Json.field.optional.withDefault
|
||||
{ fieldName = "unstable_features"
|
||||
, toField = .unstableFeatures
|
||||
, description =
|
||||
[ "Experimental features the server supports. Features not listed here, or the lack of this property all together, indicate that a feature is not supported."
|
||||
]
|
||||
, coder =
|
||||
Json.bool
|
||||
|> Json.slowDict
|
||||
|> Json.map
|
||||
{ name = "Dict to set"
|
||||
, description =
|
||||
[ "Turn a dictionary of supported values into a set that contains only supported values"
|
||||
]
|
||||
, back = Set.foldl (\k d -> Dict.insert k True d) Dict.empty
|
||||
, forth =
|
||||
Dict.foldl
|
||||
(\k v s ->
|
||||
if v then
|
||||
Set.insert k s
|
||||
|
||||
else
|
||||
s
|
||||
)
|
||||
Set.empty
|
||||
}
|
||||
, default = ( Set.empty, [] )
|
||||
, defaultToString = always "{}"
|
||||
}
|
||||
)
|
|
@ -1,5 +1,5 @@
|
|||
module Internal.Config.Leaks exposing
|
||||
( accessToken, baseUrl, transaction, versions
|
||||
( accessToken, baseUrl, field, transaction, versions
|
||||
, allLeaks
|
||||
)
|
||||
|
||||
|
@ -30,7 +30,7 @@ know 100% sure that the value isn't `Nothing`.
|
|||
|
||||
Just 5 |> Maybe.withDefault Leaks.number
|
||||
|
||||
@docs accessToken, baseUrl, transaction, versions
|
||||
@docs accessToken, baseUrl, field, transaction, versions
|
||||
|
||||
For safety purposes, all leaking values are stored in the following value:
|
||||
|
||||
|
@ -52,14 +52,15 @@ accessToken =
|
|||
-}
|
||||
allLeaks : Set String
|
||||
allLeaks =
|
||||
Set.union
|
||||
(Set.fromList versions)
|
||||
(Set.fromList
|
||||
[ accessToken
|
||||
, baseUrl
|
||||
, transaction
|
||||
]
|
||||
)
|
||||
Set.fromList
|
||||
[ accessToken
|
||||
, baseUrl
|
||||
, field
|
||||
, transaction
|
||||
, "elm-sdk-placeholder-versions-leaks" -- Old leaking value
|
||||
]
|
||||
|> Set.union (Set.fromList versions.versions)
|
||||
|> Set.union versions.unstableFeatures
|
||||
|
||||
|
||||
{-| Placeholder base URL.
|
||||
|
@ -69,6 +70,13 @@ baseUrl =
|
|||
"elm-sdk-placeholder-baseurl-leaks.example.org"
|
||||
|
||||
|
||||
{-| Placeholder JSON field.
|
||||
-}
|
||||
field : String
|
||||
field =
|
||||
"elm-sdk-placeholder-json-field"
|
||||
|
||||
|
||||
{-| Placeholder transaction id.
|
||||
-}
|
||||
transaction : String
|
||||
|
@ -78,6 +86,8 @@ transaction =
|
|||
|
||||
{-| Placeholder versions list.
|
||||
-}
|
||||
versions : List String
|
||||
versions : { versions : List String, unstableFeatures : Set String }
|
||||
versions =
|
||||
[ "elm-sdk-placeholder-versions-leaks" ]
|
||||
{ versions = [ "elm-sdk-placeholder-versions-versions-leaks" ]
|
||||
, unstableFeatures = Set.singleton "elm-sdk-placeholder-versions-unstableFeatures-leaks"
|
||||
}
|
||||
|
|
|
@ -247,9 +247,11 @@ fields :
|
|||
{ context :
|
||||
{ accessToken : Desc
|
||||
, baseUrl : Desc
|
||||
, experimental : Desc
|
||||
, password : Desc
|
||||
, refreshToken : Desc
|
||||
, username : Desc
|
||||
, serverName : Desc
|
||||
, transaction : Desc
|
||||
, versions : Desc
|
||||
}
|
||||
|
@ -329,6 +331,9 @@ fields =
|
|||
, baseUrl =
|
||||
[ "The base URL of the Matrix server."
|
||||
]
|
||||
, experimental =
|
||||
[ "Experimental features supported by the homeserver."
|
||||
]
|
||||
, password =
|
||||
[ "The user's password for authentication purposes."
|
||||
]
|
||||
|
@ -338,6 +343,10 @@ fields =
|
|||
, username =
|
||||
[ "The username of the Matrix account."
|
||||
]
|
||||
, serverName =
|
||||
[ "The homeserver that the user is trying to communicate with."
|
||||
, "This name doesn't need to be the address. For example, the name might be `matrix.org` even though the homeserver is at a different location."
|
||||
]
|
||||
, transaction =
|
||||
[ "A unique identifier for a transaction initiated by the user."
|
||||
]
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
module Internal.Values.Context exposing
|
||||
( Context, init, coder, encode, decoder
|
||||
, APIContext, apiFormat
|
||||
, APIContext, apiFormat, fromApiFormat
|
||||
, setAccessToken, getAccessToken
|
||||
, setBaseUrl, getBaseUrl
|
||||
, setTransaction, getTransaction
|
||||
, setVersions, getVersions
|
||||
, Versions, setVersions, getVersions
|
||||
)
|
||||
|
||||
{-| The Context is the set of variables that the user (mostly) cannot control.
|
||||
|
@ -22,7 +22,7 @@ the Matrix API.
|
|||
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
|
||||
@docs APIContext, apiFormat, fromApiFormat
|
||||
|
||||
Once the APIContext is ready, there's helper functions for each piece of
|
||||
information that can be inserted.
|
||||
|
@ -45,13 +45,15 @@ information that can be inserted.
|
|||
|
||||
### Versions
|
||||
|
||||
@docs setVersions, getVersions
|
||||
@docs Versions, setVersions, getVersions
|
||||
|
||||
-}
|
||||
|
||||
import Internal.Config.Leaks as L
|
||||
import Internal.Config.Text as Text
|
||||
import Internal.Tools.Json as Json
|
||||
import Json.Encode as E
|
||||
import Set exposing (Set)
|
||||
|
||||
|
||||
{-| The Context type stores all the information in the Vault. This data type is
|
||||
|
@ -62,9 +64,10 @@ type alias Context =
|
|||
, baseUrl : Maybe String
|
||||
, password : Maybe String
|
||||
, refreshToken : Maybe String
|
||||
, username : Maybe String
|
||||
, serverName : String
|
||||
, transaction : Maybe String
|
||||
, versions : Maybe (List String)
|
||||
, username : Maybe String
|
||||
, versions : Maybe Versions
|
||||
}
|
||||
|
||||
|
||||
|
@ -78,10 +81,14 @@ type APIContext ph
|
|||
, baseUrl : String
|
||||
, context : Context
|
||||
, transaction : String
|
||||
, versions : List String
|
||||
, versions : Versions
|
||||
}
|
||||
|
||||
|
||||
type alias Versions =
|
||||
{ versions : List String, unstableFeatures : Set String }
|
||||
|
||||
|
||||
{-| Create an unformatted APIContext type.
|
||||
-}
|
||||
apiFormat : Context -> APIContext {}
|
||||
|
@ -95,11 +102,19 @@ apiFormat context =
|
|||
}
|
||||
|
||||
|
||||
{-| Get the original context that contains all values from before any were
|
||||
gotten from the Matrix API.
|
||||
-}
|
||||
fromApiFormat : APIContext a -> Context
|
||||
fromApiFormat (APIContext c) =
|
||||
c.context
|
||||
|
||||
|
||||
{-| Define how a Context can be encoded to and decoded from a JSON object.
|
||||
-}
|
||||
coder : Json.Coder Context
|
||||
coder =
|
||||
Json.object7
|
||||
Json.object8
|
||||
{ name = Text.docs.context.name
|
||||
, description = Text.docs.context.description
|
||||
, init = Context
|
||||
|
@ -132,10 +147,10 @@ coder =
|
|||
, coder = Json.string
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "username"
|
||||
, toField = .username
|
||||
, description = Text.fields.context.username
|
||||
(Json.field.required
|
||||
{ fieldName = "serverName"
|
||||
, toField = .serverName
|
||||
, description = Text.fields.context.serverName
|
||||
, coder = Json.string
|
||||
}
|
||||
)
|
||||
|
@ -146,11 +161,18 @@ coder =
|
|||
, coder = Json.string
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "username"
|
||||
, toField = .username
|
||||
, description = Text.fields.context.username
|
||||
, coder = Json.string
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "versions"
|
||||
, toField = .versions
|
||||
, description = Text.fields.context.versions
|
||||
, coder = Json.list Json.string
|
||||
, coder = versionsCoder
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -171,14 +193,15 @@ encode =
|
|||
|
||||
{-| A basic, untouched version of the Context, containing no information.
|
||||
-}
|
||||
init : Context
|
||||
init =
|
||||
init : String -> Context
|
||||
init sn =
|
||||
{ accessToken = Nothing
|
||||
, baseUrl = Nothing
|
||||
, refreshToken = Nothing
|
||||
, password = Nothing
|
||||
, username = Nothing
|
||||
, serverName = sn
|
||||
, transaction = Nothing
|
||||
, username = Nothing
|
||||
, versions = Nothing
|
||||
}
|
||||
|
||||
|
@ -227,13 +250,38 @@ setTransaction value (APIContext c) =
|
|||
|
||||
{-| Get an inserted versions list.
|
||||
-}
|
||||
getVersions : APIContext { a | versions : () } -> List String
|
||||
getVersions : APIContext { a | versions : () } -> Versions
|
||||
getVersions (APIContext c) =
|
||||
c.versions
|
||||
|
||||
|
||||
{-| Insert a versions list into the APIContext.
|
||||
-}
|
||||
setVersions : List String -> APIContext a -> APIContext { a | versions : () }
|
||||
setVersions : Versions -> APIContext a -> APIContext { a | versions : () }
|
||||
setVersions value (APIContext c) =
|
||||
APIContext { c | versions = value }
|
||||
|
||||
|
||||
versionsCoder : Json.Coder Versions
|
||||
versionsCoder =
|
||||
Json.object2
|
||||
{ name = Debug.todo "Add name" -- Text.docs.versions.name
|
||||
, description = Debug.todo "Add description" -- Text.docs.versions.description
|
||||
, init = Versions
|
||||
}
|
||||
(Json.field.required
|
||||
{ fieldName = "versions"
|
||||
, toField = .versions
|
||||
, description = Debug.todo "Add description"
|
||||
, coder = Json.list Json.string
|
||||
}
|
||||
)
|
||||
(Json.field.optional.withDefault
|
||||
{ fieldName = "unstableFeatures"
|
||||
, toField = .unstableFeatures
|
||||
, description = Debug.todo "Add description"
|
||||
, coder = Json.set Json.string
|
||||
, default = ( Set.empty, [] )
|
||||
, defaultToString = Json.encode (Json.set Json.string) >> E.encode 0
|
||||
}
|
||||
)
|
||||
|
|
|
@ -48,9 +48,11 @@ settings that can be adjusted manually.
|
|||
|
||||
-}
|
||||
|
||||
import Internal.Api.Request as Request
|
||||
import Internal.Config.Log exposing (Log)
|
||||
import Internal.Config.Text as Text
|
||||
import Internal.Tools.Json as Json
|
||||
import Internal.Values.Context as Context exposing (Context)
|
||||
import Internal.Values.Context as Context exposing (Context, Versions)
|
||||
import Internal.Values.Settings as Settings
|
||||
|
||||
|
||||
|
@ -70,10 +72,12 @@ type alias Envelope a =
|
|||
-}
|
||||
type EnvelopeUpdate a
|
||||
= ContentUpdate a
|
||||
| HttpRequest (Request.Request ( Request.Error, List Log ) ( EnvelopeUpdate a, List Log ))
|
||||
| More (List (EnvelopeUpdate a))
|
||||
| SetAccessToken String
|
||||
| SetBaseUrl String
|
||||
| SetRefreshToken String
|
||||
| SetVersions (List String)
|
||||
| SetVersions Versions
|
||||
|
||||
|
||||
{-| Settings value from
|
||||
|
@ -286,12 +290,18 @@ update updateContent eu ({ context } as data) =
|
|||
ContentUpdate v ->
|
||||
{ data | content = updateContent v data.content }
|
||||
|
||||
HttpRequest _ ->
|
||||
data
|
||||
|
||||
More items ->
|
||||
List.foldl (update updateContent) data items
|
||||
|
||||
SetAccessToken a ->
|
||||
{ data | context = { context | accessToken = Just a } }
|
||||
|
||||
SetBaseUrl b ->
|
||||
{ data | context = { context | baseUrl = Just b } }
|
||||
|
||||
SetRefreshToken r ->
|
||||
{ data | context = { context | refreshToken = Just r } }
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
module Internal.Values.Room exposing
|
||||
( Room, init
|
||||
, RoomUpdate, update
|
||||
, RoomUpdate(..), update
|
||||
, Batch, addBatch, addSync, addEvents, mostRecentEvents
|
||||
, getAccountData, setAccountData
|
||||
, coder, encode, decode
|
||||
|
@ -56,6 +56,7 @@ import Internal.Tools.Json as Json
|
|||
import Internal.Values.Event as Event exposing (Event)
|
||||
import Internal.Values.StateManager as StateManager exposing (StateManager)
|
||||
import Internal.Values.Timeline as Timeline exposing (Timeline)
|
||||
import Internal.Values.User as User exposing (User)
|
||||
import Json.Encode as E
|
||||
|
||||
|
||||
|
@ -81,7 +82,9 @@ type alias Room =
|
|||
from the Matrix API.
|
||||
-}
|
||||
type RoomUpdate
|
||||
= AddSync Batch
|
||||
= AddEvent Event
|
||||
| AddSync Batch
|
||||
| Invite User
|
||||
| More (List RoomUpdate)
|
||||
| SetAccountData String Json.Value
|
||||
|
||||
|
@ -245,9 +248,17 @@ setAccountData key value room =
|
|||
update : RoomUpdate -> Room -> Room
|
||||
update ru room =
|
||||
case ru of
|
||||
AddEvent _ ->
|
||||
-- TODO: Add event
|
||||
room
|
||||
|
||||
AddSync batch ->
|
||||
addSync batch room
|
||||
|
||||
Invite user ->
|
||||
-- TODO: Invite user
|
||||
room
|
||||
|
||||
More items ->
|
||||
List.foldl update room items
|
||||
|
||||
|
|
|
@ -54,7 +54,6 @@ based on new information provided by the Matrix API.
|
|||
-}
|
||||
type VaultUpdate
|
||||
= CreateRoomIfNotExists String
|
||||
| HttpRequest (Request.Request ( Request.Error, List Log ) ( VaultUpdate, List Log ))
|
||||
| MapRoom String Room.RoomUpdate
|
||||
| More (List VaultUpdate)
|
||||
| SetAccountData String Json.Value
|
||||
|
@ -129,11 +128,6 @@ update vu vault =
|
|||
(Maybe.withDefault (Room.init roomId) >> Maybe.Just)
|
||||
vault
|
||||
|
||||
-- The HTTP request currently isn't used anywhere other than for
|
||||
-- auditing the requests that the Vault is making
|
||||
HttpRequest _ ->
|
||||
vault
|
||||
|
||||
MapRoom roomId ru ->
|
||||
mapRoom roomId (Room.update ru) vault
|
||||
|
||||
|
|
Loading…
Reference in New Issue