Add CredUpdate type

pull/1/head
Bram van den Heuvel 2023-02-17 15:08:57 +01:00
parent faac764c07
commit 305a312b72
15 changed files with 202 additions and 31 deletions

View File

@ -6,39 +6,43 @@ import Internal.Api.SendMessageEvent.Main as SendMessageEvent
import Internal.Api.SendStateKey.Main as SendStateKey import Internal.Api.SendStateKey.Main as SendStateKey
import Internal.Api.Sync.Main as Sync import Internal.Api.Sync.Main as Sync
import Internal.Api.Versions.Main as Versions import Internal.Api.Versions.Main as Versions
import Internal.Tools.Exceptions as X
import Task exposing (Task)
type alias Future a =
Task X.Error a
{-| Get a specific event from the Matrix API. {-| Get a specific event from the Matrix API.
-} -}
getEvent : List String -> Maybe (GetEvent.EventInput -> GetEvent.EventOutput) getEvent : List String -> Maybe (GetEvent.EventInput -> Future GetEvent.EventOutput)
getEvent = getEvent =
GetEvent.getEvent GetEvent.getEvent
{-| Get a list of members who are part of a Matrix room. {-| Get a list of members who are part of a Matrix room.
-} -}
joinedMembers : List String -> Maybe (JoinedMembers.JoinedMembersInput -> JoinedMembers.JoinedMembersOutput) joinedMembers : List String -> Maybe (JoinedMembers.JoinedMembersInput -> Future JoinedMembers.JoinedMembersOutput)
joinedMembers = joinedMembers =
JoinedMembers.joinedMembers JoinedMembers.joinedMembers
{-| Send a message event into a Matrix room. {-| Send a message event into a Matrix room.
-} -}
sendMessageEvent : List String -> Maybe (SendMessageEvent.SendMessageEventInput -> SendMessageEvent.SendMessageEventOutput) sendMessageEvent : List String -> Maybe (SendMessageEvent.SendMessageEventInput -> Future SendMessageEvent.SendMessageEventOutput)
sendMessageEvent = sendMessageEvent =
SendMessageEvent.sendMessageEvent SendMessageEvent.sendMessageEvent
{-| Send a state event into a Matrix room. {-| Send a state event into a Matrix room.
-} -}
sendStateEvent : List String -> Maybe (SendStateKey.SendStateKeyInput -> SendStateKey.SendStateKeyOutput) sendStateEvent : List String -> Maybe (SendStateKey.SendStateKeyInput -> Future SendStateKey.SendStateKeyOutput)
sendStateEvent = sendStateEvent =
SendStateKey.sendStateKey SendStateKey.sendStateKey
{-| Get the latest sync from the Matrix API. {-| Get the latest sync from the Matrix API.
-} -}
syncCredentials : List String -> Maybe (Sync.SyncInput -> Sync.SyncOutput) syncCredentials : List String -> Maybe (Sync.SyncInput -> Future Sync.SyncOutput)
syncCredentials = syncCredentials =
Sync.sync Sync.sync

View File

@ -0,0 +1,104 @@
module Internal.Api.CredUpdate exposing (getEvent, joinedMembers, sendMessage, sendState, sync)
{-| Sometimes, the `Credentials` type needs to refresh its tokens, log in again,
change some state or adjust its values to be able to keep talking to the server.
That's what the `CredUpdate` type is for. It is a list of changes that the
`Credentials` type needs to make.
-}
import Internal.Api.GetEvent.Main as GetEvent
import Internal.Api.Helpers as H
import Internal.Api.JoinedMembers.Main as JoinedMembers
import Internal.Api.SendMessageEvent.Main as SendMessageEvent
import Internal.Api.SendStateKey.Main as SendStateKey
import Internal.Api.Sync.Main as Sync
import Internal.Api.Versions.Main as Versions
import Internal.Tools.Exceptions as X
import Task exposing (Task)
type CredUpdate
= MultipleChanges (List CredUpdate)
| EventDetails GetEvent.EventOutput
| RoomMemberList JoinedMembers.JoinedMembersOutput
| MessageEventSent SendMessageEvent.SendMessageEventOutput
| StateEventSent SendStateKey.SendStateKeyOutput
| SyncReceived Sync.SyncOutput
| VersionReceived Versions.VersionsOutput
type alias Updater = Task X.Error CredUpdate
getEvent : Maybe (List String) -> GetEvent.EventInput -> Updater
getEvent versions =
maybeWithVersions
{ maybeVersions = versions
, f = GetEvent.getEvent
, toUpdate = EventDetails
}
>> H.retryTask 2
joinedMembers : Maybe (List String) -> JoinedMembers.JoinedMembersInput -> Updater
joinedMembers versions =
maybeWithVersions
{ maybeVersions = versions
, f = JoinedMembers.joinedMembers
, toUpdate = RoomMemberList
}
sendMessage : Maybe (List String) -> SendMessageEvent.SendMessageEventInput -> Updater
sendMessage versions =
maybeWithVersions
{ maybeVersions = versions
, f = SendMessageEvent.sendMessageEvent
, toUpdate = MessageEventSent
}
>> H.retryTask 5
sendState : Maybe (List String) -> SendStateKey.SendStateKeyInput -> Updater
sendState versions =
maybeWithVersions
{ maybeVersions = versions
, f = SendStateKey.sendStateKey
, toUpdate = StateEventSent
}
>> H.retryTask 5
sync : Maybe (List String) -> Sync.SyncInput -> Updater
sync versions =
maybeWithVersions
{ maybeVersions = versions
, f = Sync.sync
, toUpdate = SyncReceived
}
>> H.retryTask 1
maybeWithVersions :
{ maybeVersions : Maybe (List String)
, f : (List String -> Maybe ({ in | baseUrl : String } -> Task X.Error out))
, toUpdate : (out -> CredUpdate)
} ->
{ in | baseUrl : String } -> Updater
maybeWithVersions {maybeVersions, f, toUpdate} params =
case maybeVersions of
Just versions ->
case f versions of
Just task ->
task params
|> Task.map toUpdate
Nothing ->
Task.fail X.UnsupportedSpecVersion
Nothing ->
Versions.getVersions params.baseUrl
|> Task.andThen
(\versions ->
maybeWithVersions (Just versions.supportedVersions) f toUpdate params
|> Task.map
(\update ->
MultipleChanges
[ update
, VersionReceived versions
]
)
)

View File

@ -15,10 +15,10 @@ type alias GetEventInputV1 =
type alias GetEventOutputV1 = type alias GetEventOutputV1 =
Task X.Error SO1.ClientEvent SO1.ClientEvent
getEventInputV1 : GetEventInputV1 -> GetEventOutputV1 getEventInputV1 : GetEventInputV1 -> Task X.Error GetEventOutputV1
getEventInputV1 data = getEventInputV1 data =
R.rawApiCall R.rawApiCall
{ headers = R.WithAccessToken data.accessToken { headers = R.WithAccessToken data.accessToken

View File

@ -1,10 +1,12 @@
module Internal.Api.GetEvent.Main exposing (..) module Internal.Api.GetEvent.Main exposing (..)
import Internal.Api.GetEvent.Api as Api import Internal.Api.GetEvent.Api as Api
import Internal.Tools.Exceptions as X
import Internal.Tools.VersionControl as VC import Internal.Tools.VersionControl as VC
import Task exposing (Task)
getEvent : List String -> Maybe (EventInput -> EventOutput) getEvent : List String -> Maybe (EventInput -> Task X.Error EventOutput)
getEvent versions = getEvent versions =
VC.withBottomLayer VC.withBottomLayer
{ current = Api.getEventInputV1 { current = Api.getEventInputV1

View File

@ -0,0 +1,50 @@
module Internal.Api.Helpers exposing (..)
import Internal.Tools.Exceptions as X
import Process
import Task exposing (Task)
import Http
{-| Sometimes, a URL endpoint might be ratelimited. In such a case,
the homeserver tells the SDK to wait for a while and then send its response again.
-}
ratelimited : Task X.Error a -> Task X.Error a
ratelimited task =
task
|> Task.onError
(\e ->
case e of
X.ServerException (X.M_LIMIT_EXCEEDED { retryAfterMs }) ->
case retryAfterMs of
Just interval ->
interval
|> (+) 1
|> toFloat
|> Process.sleep
|> Task.andThen (\_ -> task)
|> ratelimited
Nothing ->
Task.fail e
X.InternetException (Http.BadStatus 429) ->
1000
|> Process.sleep
|> Task.andThen (\_ -> task)
|> ratelimited
_ ->
Task.fail e
)
{-| Sometimes, you don't really care if something went wrong - you just want to try again.
This task will only return an error if it went wrong on the n'th attempt.
-}
retryTask : Int -> Task x a -> Task x a
retryTask n task =
if n <= 0 then
task
else
Task.onError (\_ -> retryTask (n - 1) task ) task

View File

@ -14,10 +14,10 @@ type alias JoinedMembersInputV1 =
type alias JoinedMembersOutputV1 = type alias JoinedMembersOutputV1 =
Task X.Error SO1.RoomMemberList SO1.RoomMemberList
joinedMembersV1 : JoinedMembersInputV1 -> JoinedMembersOutputV1 joinedMembersV1 : JoinedMembersInputV1 -> Task X.Error JoinedMembersOutputV1
joinedMembersV1 data = joinedMembersV1 data =
R.rawApiCall R.rawApiCall
{ headers = R.WithAccessToken data.accessToken { headers = R.WithAccessToken data.accessToken
@ -34,7 +34,7 @@ joinedMembersV1 data =
} }
joinedMembersV2 : JoinedMembersInputV1 -> JoinedMembersOutputV1 joinedMembersV2 : JoinedMembersInputV1 -> Task X.Error JoinedMembersOutputV1
joinedMembersV2 data = joinedMembersV2 data =
R.rawApiCall R.rawApiCall
{ headers = R.WithAccessToken data.accessToken { headers = R.WithAccessToken data.accessToken

View File

@ -1,10 +1,12 @@
module Internal.Api.JoinedMembers.Main exposing (..) module Internal.Api.JoinedMembers.Main exposing (..)
import Internal.Api.JoinedMembers.Api as Api import Internal.Api.JoinedMembers.Api as Api
import Internal.Tools.Exceptions as X
import Internal.Tools.VersionControl as VC import Internal.Tools.VersionControl as VC
import Task exposing (Task)
joinedMembers : List String -> Maybe (JoinedMembersInput -> JoinedMembersOutput) joinedMembers : List String -> Maybe (JoinedMembersInput -> Task X.Error JoinedMembersOutput)
joinedMembers versions = joinedMembers versions =
VC.withBottomLayer VC.withBottomLayer
{ current = Api.joinedMembersV1 { current = Api.joinedMembersV1

View File

@ -18,10 +18,10 @@ type alias SendMessageEventInputV1 =
type alias SendMessageEventOutputV1 = type alias SendMessageEventOutputV1 =
Task X.Error SO1.EventResponse SO1.EventResponse
sendMessageEventV1 : SendMessageEventInputV1 -> SendMessageEventOutputV1 sendMessageEventV1 : SendMessageEventInputV1 -> Task X.Error SendMessageEventOutputV1
sendMessageEventV1 data = sendMessageEventV1 data =
R.rawApiCall R.rawApiCall
{ headers = R.WithAccessToken data.accessToken { headers = R.WithAccessToken data.accessToken
@ -40,7 +40,7 @@ sendMessageEventV1 data =
} }
sendMessageEventV2 : SendMessageEventInputV1 -> SendMessageEventOutputV1 sendMessageEventV2 : SendMessageEventInputV1 -> Task X.Error SendMessageEventOutputV1
sendMessageEventV2 data = sendMessageEventV2 data =
R.rawApiCall R.rawApiCall
{ headers = R.WithAccessToken data.accessToken { headers = R.WithAccessToken data.accessToken

View File

@ -1,10 +1,11 @@
module Internal.Api.SendMessageEvent.Main exposing (..) module Internal.Api.SendMessageEvent.Main exposing (..)
import Internal.Api.SendMessageEvent.Api as Api import Internal.Api.SendMessageEvent.Api as Api
import Internal.Tools.Exceptions as X
import Internal.Tools.VersionControl as VC import Internal.Tools.VersionControl as VC
import Task exposing (Task)
sendMessageEvent : List String -> Maybe (SendMessageEventInput -> Task X.Error SendMessageEventOutput)
sendMessageEvent : List String -> Maybe (SendMessageEventInput -> SendMessageEventOutput)
sendMessageEvent versions = sendMessageEvent versions =
VC.withBottomLayer VC.withBottomLayer
{ current = Api.sendMessageEventV1 { current = Api.sendMessageEventV1

View File

@ -18,10 +18,10 @@ type alias SendStateKeyInputV1 =
type alias SendStateKeyOutputV1 = type alias SendStateKeyOutputV1 =
Task X.Error SO1.EventResponse SO1.EventResponse
sendStateKeyV1 : SendStateKeyInputV1 -> SendStateKeyOutputV1 sendStateKeyV1 : SendStateKeyInputV1 -> Task X.Error SendStateKeyOutputV1
sendStateKeyV1 data = sendStateKeyV1 data =
R.rawApiCall R.rawApiCall
{ headers = R.WithAccessToken data.accessToken { headers = R.WithAccessToken data.accessToken
@ -40,7 +40,7 @@ sendStateKeyV1 data =
} }
sendStateKeyV2 : SendStateKeyInputV1 -> SendStateKeyOutputV1 sendStateKeyV2 : SendStateKeyInputV1 -> Task X.Error SendStateKeyOutputV1
sendStateKeyV2 data = sendStateKeyV2 data =
R.rawApiCall R.rawApiCall
{ headers = R.WithAccessToken data.accessToken { headers = R.WithAccessToken data.accessToken

View File

@ -1,10 +1,11 @@
module Internal.Api.SendStateKey.Main exposing (..) module Internal.Api.SendStateKey.Main exposing (..)
import Internal.Api.SendStateKey.Api as Api import Internal.Api.SendStateKey.Api as Api
import Internal.Tools.Exceptions as X
import Internal.Tools.VersionControl as VC import Internal.Tools.VersionControl as VC
import Task exposing (Task)
sendStateKey : List String -> Maybe (SendStateKeyInput -> Task X.Error SendStateKeyOutput)
sendStateKey : List String -> Maybe (SendStateKeyInput -> SendStateKeyOutput)
sendStateKey versions = sendStateKey versions =
VC.withBottomLayer VC.withBottomLayer
{ current = Api.sendStateKeyV1 { current = Api.sendStateKeyV1

View File

@ -20,14 +20,14 @@ type alias SyncInputV1 =
type alias SyncOutputV1 = type alias SyncOutputV1 =
Task X.Error SO1.Sync SO1.Sync
type alias SyncOutputV2 = type alias SyncOutputV2 =
Task X.Error SO2.Sync SO2.Sync
syncV1 : SyncInputV1 -> SyncOutputV1 syncV1 : SyncInputV1 -> Task X.Error SyncOutputV1
syncV1 data = syncV1 data =
R.rawApiCall R.rawApiCall
{ headers = R.WithAccessToken data.accessToken { headers = R.WithAccessToken data.accessToken
@ -51,7 +51,7 @@ syncV1 data =
} }
syncV2 : SyncInputV1 -> SyncOutputV2 syncV2 : SyncInputV1 -> Task X.Error SyncOutputV2
syncV2 data = syncV2 data =
R.rawApiCall R.rawApiCall
{ headers = R.WithAccessToken data.accessToken { headers = R.WithAccessToken data.accessToken

View File

@ -2,11 +2,12 @@ module Internal.Api.Sync.Main exposing (..)
import Internal.Api.Sync.Api as Api import Internal.Api.Sync.Api as Api
import Internal.Api.Sync.V2.Upcast as U2 import Internal.Api.Sync.V2.Upcast as U2
import Internal.Tools.Exceptions as X
import Internal.Tools.VersionControl as VC import Internal.Tools.VersionControl as VC
import Task import Task exposing (Task)
sync : List String -> Maybe (SyncInput -> SyncOutput) sync : List String -> Maybe (SyncInput -> Task X.Error SyncOutput)
sync versions = sync versions =
VC.withBottomLayer VC.withBottomLayer
{ current = Api.syncV1 { current = Api.syncV1

View File

@ -11,9 +11,9 @@ type alias VersionsInput =
type alias VersionsOutput = type alias VersionsOutput =
Task X.Error O.Versions O.Versions
getVersions : VersionsInput -> VersionsOutput getVersions : VersionsInput -> Task X.Error VersionsOutput
getVersions baseUrl = getVersions baseUrl =
Api.versionsV1 { baseUrl = baseUrl } Api.versionsV1 { baseUrl = baseUrl }

View File

@ -10,12 +10,18 @@ import Internal.Tools.DecodeExtra exposing (opField)
import Json.Decode as D import Json.Decode as D
import Json.Encode as E import Json.Encode as E
{-| Errors that may return in any circumstance:
- `InternetException` Errors that the `elm/http` library might raise.
- `SDKException` Errors that this SDK might raise if it doesn't like its own input
- `ServerException` Errors that the homeserver might bring
- `UnsupportedSpecVersion` This SDK does not support the needed spec versions for certain operations - usually because a homeserver is extremely old.
-}
type Error type Error
= InternetException Http.Error = InternetException Http.Error
| SDKException ClientError | SDKException ClientError
| ServerException ServerError | ServerException ServerError
| UnsupportedVersion | UnsupportedSpecVersion
{-| Errors that this SDK might return if it doesn't like its own input, if it {-| Errors that this SDK might return if it doesn't like its own input, if it