Refactor: connect API tasks to Context

pull/1/head
Bram van den Heuvel 2023-03-14 15:18:23 +01:00
parent e7804b096f
commit e90f823000
20 changed files with 606 additions and 823 deletions

View File

@ -41,6 +41,14 @@ type alias FutureTask =
Task X.Error CredUpdate Task X.Error CredUpdate
{-| Turn an API Task into a taskchain.
-}
toChain : (cout -> Chain.TaskChainPiece CredUpdate ph1 ph2) -> (Context.Context ph1 -> cin -> Task X.Error cout) -> cin -> TaskChain CredUpdate ph1 ph2
toChain transform task input context =
task context input
|> Task.map transform
{-| Turn a chain of tasks into a full executable task. {-| Turn a chain of tasks into a full executable task.
-} -}
toTask : TaskChain CredUpdate {} b -> FutureTask toTask : TaskChain CredUpdate {} b -> FutureTask
@ -86,134 +94,79 @@ accessToken ctoken =
{ username = username, password = password } { username = username, password = password }
type alias GetEventInput =
{ eventId : String, roomId : String }
{-| Get an event from the API. {-| Get an event from the API.
-} -}
getEvent : GetEventInput -> IdemChain CredUpdate (VBA a) getEvent : GetEvent.EventInput -> IdemChain CredUpdate (VBA a)
getEvent { eventId, roomId } context = getEvent input =
let toChain
input = (\output ->
{ accessToken = Context.getAccessToken context Chain.TaskChainPiece
, baseUrl = Context.getBaseUrl context { contextChange = identity
, eventId = eventId , messages = [ GetEvent input output ]
, roomId = roomId }
} )
in GetEvent.getEvent
input input
|> GetEvent.getEvent (Context.getVersions context)
|> Task.map
(\output ->
Chain.TaskChainPiece
{ contextChange = identity
, messages = [ GetEvent input output ]
}
)
{-| Get the supported spec versions from the homeserver. {-| Get the supported spec versions from the homeserver.
-} -}
getVersions : TaskChain CredUpdate { a | baseUrl : () } (VB a) getVersions : TaskChain CredUpdate { a | baseUrl : () } (VB a)
getVersions context = getVersions =
let toChain
input = (\output ->
Context.getBaseUrl context Chain.TaskChainPiece
in { contextChange = Context.setVersions output.versions
Versions.getVersions input , messages = [ UpdateVersions output ]
|> Task.map }
(\output -> )
Chain.TaskChainPiece (\context _ -> Versions.getVersions context)
{ contextChange = Context.setVersions output.versions ()
, messages = [ UpdateVersions output ]
}
)
type alias InviteInput =
{ reason : Maybe String
, roomId : String
, userId : String
}
{-| Invite a user to a room. {-| Invite a user to a room.
-} -}
invite : InviteInput -> IdemChain CredUpdate (VBA a) invite : Invite.InviteInput -> IdemChain CredUpdate (VBA a)
invite { reason, roomId, userId } context = invite input =
let toChain
input = (\output ->
{ accessToken = Context.getAccessToken context Chain.TaskChainPiece
, baseUrl = Context.getBaseUrl context { contextChange = identity
, reason = reason , messages = [ InviteSent input output ]
, roomId = roomId }
, userId = userId )
} Invite.invite
in input
input
|> Invite.invite (Context.getVersions context)
|> Task.map
(\output ->
Chain.TaskChainPiece
{ contextChange = identity
, messages = [ InviteSent input output ]
}
)
type alias JoinedMembersInput = joinedMembers : JoinedMembers.JoinedMembersInput -> IdemChain CredUpdate (VBA a)
{ roomId : String } joinedMembers input =
toChain
(\output ->
Chain.TaskChainPiece
{ contextChange = identity
, messages = [ JoinedMembersToRoom input output ]
}
)
JoinedMembers.joinedMembers
input
joinedMembers : JoinedMembersInput -> IdemChain CredUpdate (VBA a) loginWithUsernameAndPassword : LoginWithUsernameAndPassword.LoginWithUsernameAndPasswordInput -> TaskChain CredUpdate (VB a) (VBA a)
joinedMembers { roomId } context = loginWithUsernameAndPassword input =
let toChain
input = (\output ->
{ accessToken = Context.getAccessToken context Chain.TaskChainPiece
, baseUrl = Context.getBaseUrl context { contextChange =
, roomId = roomId Context.setAccessToken
} { accessToken = output.accessToken
in , usernameAndPassword = Just input
input }
|> JoinedMembers.joinedMembers (Context.getVersions context) , messages = [ LoggedInWithUsernameAndPassword input output ]
|> Task.map }
(\output -> )
Chain.TaskChainPiece LoginWithUsernameAndPassword.loginWithUsernameAndPassword
{ contextChange = identity input
, messages = [ JoinedMembersToRoom input output ]
}
)
type alias LoginWithUsernameAndPasswordInput =
{ password : String
, username : String
}
loginWithUsernameAndPassword : LoginWithUsernameAndPasswordInput -> TaskChain CredUpdate (VB a) (VBA a)
loginWithUsernameAndPassword ({ username, password } as data) context =
let
input =
{ baseUrl = Context.getBaseUrl context
, username = username
, password = password
}
in
input
|> LoginWithUsernameAndPassword.loginWithUsernameAndPassword (Context.getVersions context)
|> Task.map
(\output ->
Chain.TaskChainPiece
{ contextChange =
Context.setAccessToken
{ accessToken = output.accessToken
, usernameAndPassword = Just data
}
, messages = [ LoggedInWithUsernameAndPassword input output ]
}
)
{-| Make a VB-context based chain. {-| Make a VB-context based chain.
@ -244,136 +197,64 @@ makeVBAT toString cred =
|> Chain.andThen (withTransactionId toString) |> Chain.andThen (withTransactionId toString)
type alias RedactInput =
{ eventId : String
, reason : Maybe String
, roomId : String
}
{-| Redact an event from a room. {-| Redact an event from a room.
-} -}
redact : RedactInput -> TaskChain CredUpdate (VBAT a) (VBA a) redact : Redact.RedactInput -> TaskChain CredUpdate (VBAT a) (VBA a)
redact { eventId, reason, roomId } context = redact input =
let toChain
input = (\output ->
{ accessToken = Context.getAccessToken context Chain.TaskChainPiece
, baseUrl = Context.getBaseUrl context { contextChange = Context.removeTransactionId
, eventId = eventId , messages = [ RedactedEvent input output ]
, reason = reason }
, roomId = roomId )
, txnId = Context.getTransactionId context Redact.redact
} input
in
input
|> Redact.redact (Context.getVersions context)
|> Task.map
(\output ->
Chain.TaskChainPiece
{ contextChange = Context.removeTransactionId
, messages = [ RedactedEvent input output ]
}
)
type alias SendMessageEventInput =
{ content : E.Value
, eventType : String
, roomId : String
}
{-| Send a message event to a room. {-| Send a message event to a room.
-} -}
sendMessageEvent : SendMessageEventInput -> TaskChain CredUpdate (VBAT a) (VBA a) sendMessageEvent : SendMessageEvent.SendMessageEventInput -> TaskChain CredUpdate (VBAT a) (VBA a)
sendMessageEvent { content, eventType, roomId } context = sendMessageEvent input =
let toChain
input = (\output ->
{ accessToken = Context.getAccessToken context Chain.TaskChainPiece
, baseUrl = Context.getBaseUrl context { contextChange = Context.removeTransactionId
, content = content , messages = [ MessageEventSent input output ]
, eventType = eventType }
, roomId = roomId )
, transactionId = Context.getTransactionId context SendMessageEvent.sendMessageEvent
} input
in
input
|> SendMessageEvent.sendMessageEvent (Context.getVersions context)
|> Task.map
(\output ->
Chain.TaskChainPiece
{ contextChange = Context.removeTransactionId
, messages = [ MessageEventSent input output ]
}
)
type alias SendStateEventInput =
{ content : E.Value
, eventType : String
, roomId : String
, stateKey : String
}
{-| Send a state key event to a room. {-| Send a state key event to a room.
-} -}
sendStateEvent : SendStateEventInput -> IdemChain CredUpdate (VBA a) sendStateEvent : SendStateKey.SendStateKeyInput -> IdemChain CredUpdate (VBA a)
sendStateEvent { content, eventType, roomId, stateKey } context = sendStateEvent input =
let toChain
input = (\output ->
{ accessToken = Context.getAccessToken context Chain.TaskChainPiece
, baseUrl = Context.getBaseUrl context { contextChange = identity
, content = content , messages = [ StateEventSent input output ]
, eventType = eventType }
, roomId = roomId )
, stateKey = stateKey SendStateKey.sendStateKey
} input
in
input
|> SendStateKey.sendStateKey (Context.getVersions context)
|> Task.map
(\output ->
Chain.TaskChainPiece
{ contextChange = identity
, messages = [ StateEventSent input output ]
}
)
type alias SyncInput =
{ filter : Maybe String
, fullState : Maybe Bool
, setPresence : Maybe Enums.UserPresence
, since : Maybe String
, timeout : Maybe Int
}
{-| Sync the latest updates. {-| Sync the latest updates.
-} -}
sync : SyncInput -> IdemChain CredUpdate (VBA a) sync : Sync.SyncInput -> IdemChain CredUpdate (VBA a)
sync data context = sync input =
let toChain
input = (\output ->
{ accessToken = Context.getAccessToken context Chain.TaskChainPiece
, baseUrl = Context.getBaseUrl context { contextChange = identity
, filter = data.filter , messages = [ SyncUpdate input output ]
, fullState = data.fullState }
, setPresence = data.setPresence )
, since = data.since Sync.sync
, timeout = data.timeout input
}
in
input
|> Sync.sync (Context.getVersions context)
|> Task.map
(\output ->
Chain.TaskChainPiece
{ contextChange = identity
, messages = [ SyncUpdate input output ]
}
)
{-| Insert versions, or get them if they are not provided. {-| Insert versions, or get them if they are not provided.

View File

@ -2,14 +2,13 @@ module Internal.Api.GetEvent.Api exposing (..)
import Internal.Api.GetEvent.V1.SpecObjects as SO1 import Internal.Api.GetEvent.V1.SpecObjects as SO1
import Internal.Api.Request as R import Internal.Api.Request as R
import Internal.Tools.Context exposing (Context)
import Internal.Tools.Exceptions as X import Internal.Tools.Exceptions as X
import Task exposing (Task) import Task exposing (Task)
type alias GetEventInputV1 = type alias GetEventInputV1 =
{ accessToken : String { eventId : String
, baseUrl : String
, eventId : String
, roomId : String , roomId : String
} }
@ -18,19 +17,12 @@ type alias GetEventOutputV1 =
SO1.ClientEvent SO1.ClientEvent
getEventInputV1 : GetEventInputV1 -> Task X.Error GetEventOutputV1 getEventInputV1 : GetEventInputV1 -> Context { a | accessToken : (), baseUrl : () } -> Task X.Error GetEventOutputV1
getEventInputV1 data = getEventInputV1 data =
R.rawApiCall R.callApi "GET" "/_matrix/client/v3/rooms/{roomId}/event/{eventId}"
{ headers = R.WithAccessToken data.accessToken >> R.withAttributes
, method = "GET" [ R.accessToken
, baseUrl = data.baseUrl , R.replaceInUrl "eventId" data.eventId
, path = "/_matrix/client/v3/rooms/{roomId}/event/{eventId}" , R.replaceInUrl "roomId" data.roomId
, pathParams =
[ ( "eventId", data.eventId )
, ( "roomId", data.roomId )
] ]
, queryParams = [] >> R.toTask SO1.clientEventDecoder
, bodyParams = []
, timeout = Nothing
, decoder = \_ -> SO1.clientEventDecoder
}

View File

@ -1,23 +1,20 @@
module Internal.Api.Invite.Api exposing (..) module Internal.Api.Invite.Api exposing (..)
import Internal.Api.Request as R import Internal.Api.Request as R
import Internal.Tools.Context exposing (Context)
import Internal.Tools.Exceptions as X import Internal.Tools.Exceptions as X
import Json.Decode as D import Json.Decode as D
import Task exposing (Task) import Task exposing (Task)
type alias InviteInputV1 = type alias InviteInputV1 =
{ accessToken : String { roomId : String
, baseUrl : String
, roomId : String
, userId : String , userId : String
} }
type alias InviteInputV2 = type alias InviteInputV2 =
{ accessToken : String { reason : Maybe String
, baseUrl : String
, reason : Maybe String
, roomId : String , roomId : String
, userId : String , userId : String
} }
@ -27,40 +24,24 @@ type alias InviteOutputV1 =
() ()
inviteV1 : InviteInputV1 -> Task X.Error InviteOutputV1 inviteV1 : InviteInputV1 -> Context { a | accessToken : (), baseUrl : () } -> Task X.Error InviteOutputV1
inviteV1 data = inviteV1 { roomId, userId } =
R.rawApiCall R.callApi "POST" "/_matrix/client/r0/rooms/{roomId}/invite"
{ headers = R.WithAccessToken data.accessToken >> R.withAttributes
, method = "POST" [ R.accessToken
, baseUrl = data.baseUrl , R.replaceInUrl "roomId" roomId
, path = "/_matrix/client/r0/rooms/{roomId}/invite" , R.bodyString "user_id" userId
, pathParams =
[ ( "roomId", data.roomId )
] ]
, queryParams = [] >> R.toTask (D.map (always ()) D.value)
, bodyParams =
[ R.RequiredString "user_id" data.userId
]
, timeout = Nothing
, decoder = always (D.map (always ()) D.value)
}
inviteV2 : InviteInputV2 -> Task X.Error InviteOutputV1 inviteV2 : InviteInputV2 -> Context { a | accessToken : (), baseUrl : () } -> Task X.Error InviteOutputV1
inviteV2 data = inviteV2 { reason, roomId, userId } =
R.rawApiCall R.callApi "POST" "/_matrix/client/v3/rooms/{roomId}/invite"
{ headers = R.WithAccessToken data.accessToken >> R.withAttributes
, method = "POST" [ R.accessToken
, baseUrl = data.baseUrl , R.replaceInUrl "roomId" roomId
, path = "/_matrix/client/r0/rooms/{roomId}/invite" , R.bodyString "user_id" userId
, pathParams = , R.bodyOpString "reason" reason
[ ( "roomId", data.roomId )
] ]
, queryParams = [] >> R.toTask (D.map (always ()) D.value)
, bodyParams =
[ R.RequiredString "user_id" data.userId
, R.OptionalString "reason" data.reason
]
, timeout = Nothing
, decoder = always (D.map (always ()) D.value)
}

View File

@ -1,13 +1,14 @@
module Internal.Api.Invite.Main exposing (..) module Internal.Api.Invite.Main exposing (..)
import Internal.Api.Invite.Api as Api import Internal.Api.Invite.Api as Api
import Internal.Tools.Context as Context exposing (Context, VBA)
import Internal.Tools.Exceptions as X import Internal.Tools.Exceptions as X
import Internal.Tools.VersionControl as VC import Internal.Tools.VersionControl as VC
import Task exposing (Task) import Task exposing (Task)
invite : List String -> InviteInput -> Task X.Error InviteOutput invite : Context (VBA a) -> InviteInput -> Task X.Error InviteOutput
invite versions = invite context input =
VC.withBottomLayer VC.withBottomLayer
{ current = Api.inviteV1 { current = Api.inviteV1
, version = "r0.0.0" , version = "r0.0.0"
@ -23,9 +24,7 @@ invite versions =
|> VC.addMiddleLayer |> VC.addMiddleLayer
{ downcast = { downcast =
\data -> \data ->
{ accessToken = data.accessToken { roomId = data.roomId
, baseUrl = data.baseUrl
, roomId = data.roomId
, userId = data.userId , userId = data.userId
} }
, current = Api.inviteV2 , current = Api.inviteV2
@ -36,8 +35,10 @@ invite versions =
|> VC.sameForVersion "v1.3" |> VC.sameForVersion "v1.3"
|> VC.sameForVersion "v1.4" |> VC.sameForVersion "v1.4"
|> VC.sameForVersion "v1.5" |> VC.sameForVersion "v1.5"
|> VC.mostRecentFromVersionList versions |> VC.mostRecentFromVersionList (Context.getVersions context)
|> Maybe.withDefault (always <| Task.fail X.UnsupportedSpecVersion) |> Maybe.withDefault (always <| always <| Task.fail X.UnsupportedSpecVersion)
|> (|>) input
|> (|>) context
type alias InviteInput = type alias InviteInput =

View File

@ -2,14 +2,13 @@ module Internal.Api.JoinedMembers.Api exposing (..)
import Internal.Api.JoinedMembers.V1.SpecObjects as SO1 import Internal.Api.JoinedMembers.V1.SpecObjects as SO1
import Internal.Api.Request as R import Internal.Api.Request as R
import Internal.Tools.Context exposing (Context)
import Internal.Tools.Exceptions as X import Internal.Tools.Exceptions as X
import Task exposing (Task) import Task exposing (Task)
type alias JoinedMembersInputV1 = type alias JoinedMembersInputV1 =
{ accessToken : String { roomId : String
, baseUrl : String
, roomId : String
} }
@ -17,35 +16,21 @@ type alias JoinedMembersOutputV1 =
SO1.RoomMemberList SO1.RoomMemberList
joinedMembersV1 : JoinedMembersInputV1 -> Task X.Error JoinedMembersOutputV1 joinedMembersV1 : JoinedMembersInputV1 -> Context { a | accessToken : (), baseUrl : () } -> Task X.Error JoinedMembersOutputV1
joinedMembersV1 data = joinedMembersV1 { roomId } =
R.rawApiCall R.callApi "GET" "/_matrix/client/r0/rooms/{roomId}/joined_members"
{ headers = R.WithAccessToken data.accessToken >> R.withAttributes
, method = "GET" [ R.accessToken
, baseUrl = data.baseUrl , R.replaceInUrl "roomId" roomId
, path = "/_matrix/client/r0/rooms/{roomId}/joined_members"
, pathParams =
[ ( "roomId", data.roomId )
] ]
, queryParams = [] >> R.toTask SO1.roomMemberListDecoder
, bodyParams = []
, timeout = Nothing
, decoder = \_ -> SO1.roomMemberListDecoder
}
joinedMembersV2 : JoinedMembersInputV1 -> Task X.Error JoinedMembersOutputV1 joinedMembersV2 : JoinedMembersInputV1 -> Context { a | accessToken : (), baseUrl : () } -> Task X.Error JoinedMembersOutputV1
joinedMembersV2 data = joinedMembersV2 { roomId } =
R.rawApiCall R.callApi "GET" "/_matrix/client/v3/rooms/{roomId}/joined_members"
{ headers = R.WithAccessToken data.accessToken >> R.withAttributes
, method = "GET" [ R.accessToken
, baseUrl = data.baseUrl , R.replaceInUrl "roomId" roomId
, path = "/_matrix/client/v3/rooms/{roomId}/joined_members"
, pathParams =
[ ( "roomId", data.roomId )
] ]
, queryParams = [] >> R.toTask SO1.roomMemberListDecoder
, bodyParams = []
, timeout = Nothing
, decoder = \_ -> SO1.roomMemberListDecoder
}

View File

@ -1,13 +1,14 @@
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.Context as Context exposing (Context, VBA)
import Internal.Tools.Exceptions as X import Internal.Tools.Exceptions as X
import Internal.Tools.VersionControl as VC import Internal.Tools.VersionControl as VC
import Task exposing (Task) import Task exposing (Task)
joinedMembers : List String -> JoinedMembersInput -> Task X.Error JoinedMembersOutput joinedMembers : Context (VBA a) -> JoinedMembersInput -> Task X.Error JoinedMembersOutput
joinedMembers versions = joinedMembers context input =
VC.withBottomLayer VC.withBottomLayer
{ current = Api.joinedMembersV1 { current = Api.joinedMembersV1
, version = "r0.0.0" , version = "r0.0.0"
@ -30,8 +31,10 @@ joinedMembers versions =
|> VC.sameForVersion "v1.3" |> VC.sameForVersion "v1.3"
|> VC.sameForVersion "v1.4" |> VC.sameForVersion "v1.4"
|> VC.sameForVersion "v1.5" |> VC.sameForVersion "v1.5"
|> VC.mostRecentFromVersionList versions |> VC.mostRecentFromVersionList (Context.getVersions context)
|> Maybe.withDefault (always <| Task.fail X.UnsupportedSpecVersion) |> Maybe.withDefault (always <| always <| Task.fail X.UnsupportedSpecVersion)
|> (|>) input
|> (|>) context
type alias JoinedMembersInput = type alias JoinedMembersInput =

View File

@ -2,14 +2,14 @@ module Internal.Api.LoginWithUsernameAndPassword.Api exposing (..)
import Internal.Api.LoginWithUsernameAndPassword.V1.Login as SO import Internal.Api.LoginWithUsernameAndPassword.V1.Login as SO
import Internal.Api.Request as R import Internal.Api.Request as R
import Internal.Tools.Context exposing (Context)
import Internal.Tools.Exceptions as X import Internal.Tools.Exceptions as X
import Json.Encode as E import Json.Encode as E
import Task exposing (Task) import Task exposing (Task)
type alias LoginWithUsernameAndPasswordInputV1 = type alias LoginWithUsernameAndPasswordInputV1 =
{ baseUrl : String { password : String
, password : String
, username : String , username : String
} }
@ -18,24 +18,16 @@ type alias LoginWithUsernameAndPasswordOutputV1 =
SO.LoggedInResponse SO.LoggedInResponse
loginWithUsernameAndPasswordV1 : LoginWithUsernameAndPasswordInputV1 -> Task X.Error LoginWithUsernameAndPasswordOutputV1 loginWithUsernameAndPasswordV1 : LoginWithUsernameAndPasswordInputV1 -> Context { a | baseUrl : () } -> Task X.Error LoginWithUsernameAndPasswordOutputV1
loginWithUsernameAndPasswordV1 data = loginWithUsernameAndPasswordV1 { username, password } =
R.rawApiCall R.callApi "POST" "/_matrix/client/v3/login"
{ headers = R.NoHeaders >> R.withAttributes
, method = "POST"
, baseUrl = data.baseUrl
, path = "/_matrix/client/v3/login"
, pathParams = []
, queryParams = []
, bodyParams =
[ [ ( "type", E.string "m.id.user" ) [ [ ( "type", E.string "m.id.user" )
, ( "user", E.string data.username ) , ( "user", E.string username )
] ]
|> E.object |> E.object
|> R.RequiredValue "identifier" |> R.bodyValue "identifier"
, R.RequiredString "password" data.password , R.bodyString "password" password
, R.RequiredString "type" "m.login.password" , R.bodyString "type" "m.login.password"
] ]
, timeout = Nothing >> R.toTask SO.loggedInResponseDecoder
, decoder = always SO.loggedInResponseDecoder
}

View File

@ -1,19 +1,22 @@
module Internal.Api.LoginWithUsernameAndPassword.Main exposing (..) module Internal.Api.LoginWithUsernameAndPassword.Main exposing (..)
import Internal.Api.LoginWithUsernameAndPassword.Api as Api import Internal.Api.LoginWithUsernameAndPassword.Api as Api
import Internal.Tools.Context as Context exposing (Context, VBA)
import Internal.Tools.Exceptions as X import Internal.Tools.Exceptions as X
import Internal.Tools.VersionControl as VC import Internal.Tools.VersionControl as VC
import Task exposing (Task) import Task exposing (Task)
loginWithUsernameAndPassword : List String -> LoginWithUsernameAndPasswordInput -> Task X.Error LoginWithUsernameAndPasswordOutput loginWithUsernameAndPassword : Context (VBA a) -> LoginWithUsernameAndPasswordInput -> Task X.Error LoginWithUsernameAndPasswordOutput
loginWithUsernameAndPassword versions = loginWithUsernameAndPassword context input =
VC.withBottomLayer VC.withBottomLayer
{ current = Api.loginWithUsernameAndPasswordV1 { current = Api.loginWithUsernameAndPasswordV1
, version = "v1.5" , version = "v1.5"
} }
|> VC.mostRecentFromVersionList versions |> VC.mostRecentFromVersionList (Context.getVersions context)
|> Maybe.withDefault (always <| Task.fail X.UnsupportedSpecVersion) |> Maybe.withDefault (always <| always <| Task.fail X.UnsupportedSpecVersion)
|> (|>) input
|> (|>) context
type alias LoginWithUsernameAndPasswordInput = type alias LoginWithUsernameAndPasswordInput =

View File

@ -1,90 +0,0 @@
module Internal.Api.PreApi.Main exposing (..)
{-| Certain values are required knowledge for (almost) every endpoint.
Some values aren't known right away, however.
This module takes care of values like access tokens, transaction ids and spec version lists
that the credentials type needs to know about before it can make a request.
-}
import Internal.Api.LoginWithUsernameAndPassword.V1.Login as L
import Internal.Api.Request as R
import Internal.Api.Versions.V1.Versions as V
import Internal.Tools.Exceptions as X
import Internal.Tools.LoginValues exposing (AccessToken(..))
import Internal.Tools.ValueGetter exposing (ValueGetter)
import Json.Encode as E
import Task
import Time
accessToken : String -> AccessToken -> ValueGetter X.Error String
accessToken baseUrl t =
{ value =
case t of
NoAccess ->
Nothing
AccessToken token ->
Just token
UsernameAndPassword { token } ->
token
, getValue =
case t of
UsernameAndPassword { username, password } ->
R.rawApiCall
{ headers = R.NoHeaders
, method = "POST"
, baseUrl = baseUrl
, path = "/_matrix/client/v3/login"
, pathParams = []
, queryParams = []
, bodyParams =
[ [ ( "type", E.string "m.id.user" )
, ( "user", E.string username )
]
|> E.object
|> R.RequiredValue "identifier"
, R.RequiredString "password" password
, R.RequiredString "type" "m.login.password"
]
, timeout = Nothing
, decoder = \_ -> L.loggedInResponseDecoder
}
|> Task.map .accessToken
_ ->
X.NoAccessToken
|> X.SDKException
|> Task.fail
}
transactionId : (Int -> String) -> ValueGetter X.Error String
transactionId seeder =
{ value = Nothing
, getValue =
Time.now
|> Task.map Time.posixToMillis
|> Task.map seeder
}
versions : String -> Maybe V.Versions -> ValueGetter X.Error V.Versions
versions baseUrl mVersions =
{ value = mVersions
, getValue =
R.rawApiCall
{ headers = R.NoHeaders
, method = "GET"
, baseUrl = baseUrl
, path = "/_matrix/client/versions"
, pathParams = []
, queryParams = []
, bodyParams = []
, timeout = Nothing
, decoder = \_ -> V.versionsDecoder
}
}

View File

@ -2,16 +2,14 @@ module Internal.Api.Redact.Api exposing (..)
import Internal.Api.Redact.V1.SpecObjects as SO1 import Internal.Api.Redact.V1.SpecObjects as SO1
import Internal.Api.Request as R import Internal.Api.Request as R
import Internal.Tools.Context exposing (Context)
import Internal.Tools.Exceptions as X import Internal.Tools.Exceptions as X
import Task exposing (Task) import Task exposing (Task)
type alias RedactInputV1 = type alias RedactInputV1 =
{ accessToken : String { roomId : String
, baseUrl : String
, roomId : String
, eventId : String , eventId : String
, txnId : String
, reason : Maybe String , reason : Maybe String
} }
@ -20,41 +18,27 @@ type alias RedactOutputV1 =
SO1.Redaction SO1.Redaction
redactV1 : RedactInputV1 -> Task X.Error RedactOutputV1 redactV1 : RedactInputV1 -> Context { a | accessToken : (), baseUrl : (), transactionId : () } -> Task X.Error RedactOutputV1
redactV1 data = redactV1 { eventId, reason, roomId } =
R.rawApiCall R.callApi "PUT" "/_matrix/client/r0/rooms/{roomId}/redact/{eventId}/{txnId}"
{ headers = R.WithAccessToken data.accessToken >> R.withAttributes
, method = "PUT" [ R.accessToken
, baseUrl = data.baseUrl , R.withTransactionId
, path = "/_matrix/client/r0/rooms/{roomId}/redact/{eventId}/{txnId}" , R.replaceInUrl "eventId" eventId
, pathParams = , R.replaceInUrl "roomId" roomId
[ ( "roomId", data.roomId ) , R.bodyOpString "reason" reason
, ( "eventId", data.eventId )
, ( "txnId", data.txnId )
] ]
, queryParams = [] >> R.toTask SO1.redactionDecoder
, bodyParams =
[ R.OptionalString "reason" data.reason
]
, timeout = Nothing
, decoder = always SO1.redactionDecoder
}
redactV2 : RedactInputV1 -> Task X.Error RedactOutputV1 redactV2 : RedactInputV1 -> Context { a | accessToken : (), baseUrl : (), transactionId : () } -> Task X.Error RedactOutputV1
redactV2 data = redactV2 { eventId, reason, roomId } =
R.rawApiCall R.callApi "PUT" "/_matrix/client/v3/rooms/{roomId}/redact/{eventId}/{txnId}"
{ headers = R.WithAccessToken data.accessToken >> R.withAttributes
, method = "PUT" [ R.accessToken
, baseUrl = data.baseUrl , R.withTransactionId
, path = "/_matrix/client/v3/rooms/{roomId}/redact/{eventId}/{txnId}" , R.replaceInUrl "eventId" eventId
, pathParams = , R.replaceInUrl "roomId" roomId
[ ( "roomId", data.roomId ) , R.bodyOpString "reason" reason
, ( "eventId", data.eventId )
, ( "txnId", data.txnId )
] ]
, queryParams = [] >> R.toTask SO1.redactionDecoder
, bodyParams = []
, timeout = Nothing
, decoder = always SO1.redactionDecoder
}

View File

@ -1,13 +1,14 @@
module Internal.Api.Redact.Main exposing (..) module Internal.Api.Redact.Main exposing (..)
import Internal.Api.Redact.Api as Api import Internal.Api.Redact.Api as Api
import Internal.Tools.Context as Context exposing (Context, VBAT)
import Internal.Tools.Exceptions as X import Internal.Tools.Exceptions as X
import Internal.Tools.VersionControl as VC import Internal.Tools.VersionControl as VC
import Task exposing (Task) import Task exposing (Task)
redact : List String -> RedactInput -> Task X.Error RedactOutput redact : Context (VBAT a) -> RedactInput -> Task X.Error RedactOutput
redact versions = redact context input =
VC.withBottomLayer VC.withBottomLayer
{ current = Api.redactV1 { current = Api.redactV1
, version = "r0.0.0" , version = "r0.0.0"
@ -30,8 +31,10 @@ redact versions =
|> VC.sameForVersion "v1.3" |> VC.sameForVersion "v1.3"
|> VC.sameForVersion "v1.4" |> VC.sameForVersion "v1.4"
|> VC.sameForVersion "v1.5" |> VC.sameForVersion "v1.5"
|> VC.mostRecentFromVersionList versions |> VC.mostRecentFromVersionList (Context.getVersions context)
|> Maybe.withDefault (always <| Task.fail X.UnsupportedSpecVersion) |> Maybe.withDefault (always <| always <| Task.fail X.UnsupportedSpecVersion)
|> (|>) input
|> (|>) context
type alias RedactInput = type alias RedactInput =

View File

@ -1,229 +1,321 @@
module Internal.Api.Request exposing (..) module Internal.Api.Request exposing (..)
import Http import Http
import Internal.Tools.Context as Context exposing (Context)
import Internal.Tools.Exceptions as X import Internal.Tools.Exceptions as X
import Json.Decode as D import Json.Decode as D
import Json.Encode as E import Json.Encode as E
import Process import Process
import Task exposing (Task) import Task exposing (Task)
import Time import Time
import Url
import Url.Builder as UrlBuilder import Url.Builder as UrlBuilder
{-| Make a raw API call to a Matrix API. type ApiCall ph
-} = ApiCall
rawApiCall : { attributes : List ContextAttr
{ headers : Headers , baseUrl : String
, method : String , context : Context ph
, baseUrl : String , method : String
, path : String
, pathParams : List ( String, String )
, queryParams : List QueryParam
, bodyParams : List BodyParam
, timeout : Maybe Float
, decoder : Int -> D.Decoder a
}
-> Task X.Error a
rawApiCall data =
Http.task
{ method = data.method
, headers = fromHeaders data.headers
, url = buildUrl data.baseUrl data.path data.pathParams data.queryParams
, body = toBody data.bodyParams
, resolver = rawApiCallResolver data.decoder
, timeout = data.timeout
} }
withRateLimits : Int -> Task X.Error a -> Task X.Error a type alias Attribute a =
withRateLimits timeout task = Context a -> ContextAttr
Time.now
|> Task.onError
(\_ -> X.CouldntGetTimestamp |> X.SDKException |> Task.fail)
|> Task.andThen
(\now ->
task
|> Task.onError
(\err ->
case err of
X.ServerException (X.M_LIMIT_EXCEEDED data) ->
case data.retryAfterMs of
Just t ->
Process.sleep (toFloat t)
|> Task.andThen (\_ -> Time.now)
|> Task.andThen
(\newNow ->
let
diff : Int
diff =
timeout - (Time.posixToMillis newNow - Time.posixToMillis now)
in
if diff <= 0 then
Task.fail err
else
withRateLimits diff task
)
Nothing -> type ContextAttr
Task.fail err = BodyParam String E.Value
| FullBody E.Value
| Header Http.Header
| NoAttr
| QueryParam UrlBuilder.QueryParameter
| ReplaceInUrl String String
| Timeout Float
| UrlPath String
callApi : String -> String -> Context { a | baseUrl : () } -> ApiCall { a | baseUrl : () }
callApi method path context =
ApiCall
{ attributes =
[ UrlPath path
]
, baseUrl = Context.getBaseUrl context
, context = context
, method = method
}
{- GETTING VALUES
Once a user has finished building the ApiCall, we will build the task.
-}
toTask : D.Decoder a -> ApiCall ph -> Task X.Error a
toTask decoder (ApiCall data) =
Http.task
{ method = data.method
, headers =
List.filterMap
(\attr ->
case attr of
Header h ->
Just h
_ ->
Nothing
)
data.attributes
, url = getUrl (ApiCall data)
, body =
data.attributes
|> List.filterMap
(\attr ->
case attr of
FullBody v ->
Just v
_ ->
Nothing
)
|> List.reverse
|> List.head
|> Maybe.withDefault
(List.filterMap
(\attr ->
case attr of
BodyParam key value ->
Just ( key, value )
_ -> _ ->
Task.fail err Nothing
) )
) data.attributes
|> E.object
)
{-| Potential headers to go along with a Matrix API call.
-}
type Headers
= NoHeaders
| WithAccessToken String
| WithContentType String
| WithBoth { accessToken : String, contentType : String }
{-| Turn Headers into useful values
-}
fromHeaders : Headers -> List Http.Header
fromHeaders h =
(case h of
NoHeaders ->
[ ( "Content-Type", "application/json" ) ]
WithAccessToken token ->
[ ( "Content-Type", "application/json" ), ( "Authorization", "Bearer " ++ token ) ]
WithContentType contentType ->
[ ( "Content-Type", contentType ) ]
WithBoth data ->
[ ( "Content-Type", data.contentType ), ( "Authorization", "Bearer " ++ data.accessToken ) ]
)
|> List.map (\( a, b ) -> Http.header a b)
{-| -}
type QueryParam
= QueryParamString String String
| OpQueryParamString String (Maybe String)
| QueryParamInt String Int
| OpQueryParamInt String (Maybe Int)
| QueryParamBool String Bool
| OpQueryParamBool String (Maybe Bool)
fromQueryParam : QueryParam -> Maybe UrlBuilder.QueryParameter
fromQueryParam param =
case param of
QueryParamString key value ->
Just <| UrlBuilder.string key value
OpQueryParamString key value ->
Maybe.map (UrlBuilder.string key) value
QueryParamInt key value ->
Just <| UrlBuilder.int key value
OpQueryParamInt key value ->
Maybe.map (UrlBuilder.int key) value
QueryParamBool key value ->
if value then
Just <| UrlBuilder.string key "true"
else
Just <| UrlBuilder.string key "false"
OpQueryParamBool key value ->
Maybe.andThen (QueryParamBool key >> fromQueryParam) value
fromQueryParams : List QueryParam -> List UrlBuilder.QueryParameter
fromQueryParams =
List.map fromQueryParam
>> List.filterMap identity
buildUrl : String -> String -> List ( String, String ) -> List QueryParam -> String
buildUrl baseUrl path pathParams queryParams =
let
fullPath : String
fullPath =
List.foldl
(\( a, b ) -> String.replace ("{" ++ a ++ "}") b)
path
pathParams
|> (\s ->
if String.startsWith "/" s then
String.dropLeft 1 s
else
s
)
in
UrlBuilder.crossOrigin baseUrl [ fullPath ] (fromQueryParams queryParams)
{-| Type that gathers all parameters that go in the request body.
-}
type BodyParam
= OptionalString String (Maybe String)
| RequiredString String String
| OptionalInt String (Maybe Int)
| RequiredInt String Int
| OptionalValue String (Maybe E.Value)
| RequiredValue String E.Value
encodeBodyParam : BodyParam -> ( String, Maybe E.Value )
encodeBodyParam b =
case b of
OptionalString h s ->
( h, Maybe.map E.string s )
RequiredString h s ->
( h, Just <| E.string s )
OptionalInt h i ->
( h, Maybe.map E.int i )
RequiredInt h i ->
( h, Just <| E.int i )
OptionalValue h v ->
( h, v )
RequiredValue h v ->
( h, Just v )
toBody : List BodyParam -> Http.Body
toBody params =
case params of
(RequiredValue "*" v) :: [] ->
Http.jsonBody v
_ ->
List.map encodeBodyParam params
|> maybeObject
|> Http.jsonBody |> Http.jsonBody
, resolver = rawApiCallResolver (always decoder)
, timeout =
data.attributes
|> List.filterMap
(\attr ->
case attr of
Timeout t ->
Just t
_ ->
Nothing
)
|> List.reverse
|> List.head
}
{-| Create a body object based on optionally provided values. getUrl : ApiCall a -> String
-} getUrl (ApiCall { baseUrl, attributes }) =
maybeObject : List ( String, Maybe E.Value ) -> E.Value UrlBuilder.crossOrigin
maybeObject = baseUrl
(getPath attributes |> List.singleton)
(getQueryParams attributes)
getPath : List ContextAttr -> String
getPath =
List.foldl
(\attr prior ->
case attr of
UrlPath posterior ->
posterior
ReplaceInUrl from to ->
String.replace from to prior
_ ->
prior
)
""
getQueryParams : List ContextAttr -> List UrlBuilder.QueryParameter
getQueryParams =
List.filterMap List.filterMap
(\( name, value ) -> (\attr ->
case value of case attr of
Just v -> QueryParam q ->
Just ( name, v ) Just q
_ -> _ ->
Nothing Nothing
) )
>> E.object
{- ATTRIBUTES
The following functions can alter how an ApiCall behaves,
and what information it will give to the Matrix API.
-}
withAttributes : List (Attribute a) -> ApiCall a -> ApiCall a
withAttributes attrs (ApiCall data) =
ApiCall
{ attributes =
attrs
|> List.map (\attr -> attr data.context)
|> List.append data.attributes
, baseUrl = data.baseUrl
, context = data.context
, method = data.method
}
accessToken : Attribute { a | accessToken : () }
accessToken =
Context.getAccessToken
>> Http.header "Authorization"
>> Header
bodyBool : String -> Bool -> Attribute a
bodyBool key value =
bodyValue key (E.bool value)
bodyInt : String -> Int -> Attribute a
bodyInt key value =
bodyValue key (E.int value)
bodyOpBool : String -> Maybe Bool -> Attribute a
bodyOpBool key value =
case value of
Just b ->
bodyBool key b
Nothing ->
always NoAttr
bodyOpInt : String -> Maybe Int -> Attribute a
bodyOpInt key value =
case value of
Just i ->
bodyInt key i
Nothing ->
always NoAttr
bodyOpString : String -> Maybe String -> Attribute a
bodyOpString key value =
case value of
Just s ->
bodyString key s
Nothing ->
always NoAttr
bodyOpValue : String -> Maybe E.Value -> Attribute a
bodyOpValue key value =
case value of
Just v ->
bodyValue key v
Nothing ->
always NoAttr
bodyString : String -> String -> Attribute a
bodyString key value =
bodyValue key (E.string value)
bodyValue : String -> E.Value -> Attribute a
bodyValue key value _ =
BodyParam key value
fullBody : E.Value -> Attribute a
fullBody value _ =
FullBody value
queryBool : String -> Bool -> Attribute a
queryBool key value _ =
(if value then
"true"
else
"false"
)
|> UrlBuilder.string key
|> QueryParam
queryOpBool : String -> Maybe Bool -> Attribute a
queryOpBool key value =
case value of
Just b ->
queryBool key b
Nothing ->
always NoAttr
queryInt : String -> Int -> Attribute a
queryInt key value _ =
QueryParam <| UrlBuilder.int key value
queryOpInt : String -> Maybe Int -> Attribute a
queryOpInt key value =
case value of
Just i ->
queryInt key i
Nothing ->
always NoAttr
queryOpString : String -> Maybe String -> Attribute a
queryOpString key value =
case value of
Just s ->
queryString key s
Nothing ->
always NoAttr
queryString : String -> String -> Attribute a
queryString key value _ =
QueryParam <| UrlBuilder.string key value
replaceInUrl : String -> String -> Attribute a
replaceInUrl key value _ =
ReplaceInUrl ("{" ++ key ++ "}") (Url.percentEncode value)
timeout : Maybe Float -> Attribute a
timeout mf _ =
case mf of
Just f ->
Timeout f
Nothing ->
NoAttr
withTransactionId : Attribute { a | transactionId : () }
withTransactionId =
Context.getTransactionId >> ReplaceInUrl "txnId"
rawApiCallResolver : (Int -> D.Decoder a) -> Http.Resolver X.Error a rawApiCallResolver : (Int -> D.Decoder a) -> Http.Resolver X.Error a

View File

@ -2,18 +2,16 @@ module Internal.Api.SendMessageEvent.Api exposing (SendMessageEventInputV1, Send
import Internal.Api.Request as R import Internal.Api.Request as R
import Internal.Api.SendMessageEvent.V1.SpecObjects as SO1 import Internal.Api.SendMessageEvent.V1.SpecObjects as SO1
import Internal.Tools.Context exposing (Context)
import Internal.Tools.Exceptions as X import Internal.Tools.Exceptions as X
import Json.Decode as D import Json.Decode as D
import Task exposing (Task) import Task exposing (Task)
type alias SendMessageEventInputV1 = type alias SendMessageEventInputV1 =
{ accessToken : String { content : D.Value
, baseUrl : String
, content : D.Value
, eventType : String , eventType : String
, roomId : String , roomId : String
, transactionId : String
} }
@ -21,39 +19,27 @@ type alias SendMessageEventOutputV1 =
SO1.EventResponse SO1.EventResponse
sendMessageEventV1 : SendMessageEventInputV1 -> Task X.Error SendMessageEventOutputV1 sendMessageEventV1 : SendMessageEventInputV1 -> Context { a | accessToken : (), baseUrl : (), transactionId : () } -> Task X.Error SendMessageEventOutputV1
sendMessageEventV1 data = sendMessageEventV1 { content, eventType, roomId } =
R.rawApiCall R.callApi "PUT" "/_matrix/client/r0/rooms/{roomId}/send/{eventType}/{txnId}"
{ headers = R.WithAccessToken data.accessToken >> R.withAttributes
, method = "PUT" [ R.accessToken
, baseUrl = data.baseUrl , R.withTransactionId
, path = "/_matrix/client/r0/rooms/{roomId}/send/{eventType}/{txnId}" , R.replaceInUrl "eventType" eventType
, pathParams = , R.replaceInUrl "roomId" roomId
[ ( "eventType", data.eventType ) , R.fullBody content
, ( "roomId", data.roomId )
, ( "txnId", data.transactionId )
] ]
, queryParams = [] >> R.toTask SO1.eventResponseDecoder
, bodyParams = [ R.RequiredValue "*" data.content ]
, timeout = Nothing
, decoder = \_ -> SO1.eventResponseDecoder
}
sendMessageEventV2 : SendMessageEventInputV1 -> Task X.Error SendMessageEventOutputV1 sendMessageEventV2 : SendMessageEventInputV1 -> Context { a | accessToken : (), baseUrl : (), transactionId : () } -> Task X.Error SendMessageEventOutputV1
sendMessageEventV2 data = sendMessageEventV2 { content, eventType, roomId } =
R.rawApiCall R.callApi "PUT" "/_matrix/client/v3/rooms/{roomId}/send/{eventType}/{txnId}"
{ headers = R.WithAccessToken data.accessToken >> R.withAttributes
, method = "PUT" [ R.accessToken
, baseUrl = data.baseUrl , R.withTransactionId
, path = "/_matrix/client/v3/rooms/{roomId}/send/{eventType}/{txnId}" , R.replaceInUrl "eventType" eventType
, pathParams = , R.replaceInUrl "roomId" roomId
[ ( "eventType", data.eventType ) , R.fullBody content
, ( "roomId", data.roomId )
, ( "txnId", data.transactionId )
] ]
, queryParams = [] >> R.toTask SO1.eventResponseDecoder
, bodyParams = [ R.RequiredValue "*" data.content ]
, timeout = Nothing
, decoder = \_ -> SO1.eventResponseDecoder
}

View File

@ -1,13 +1,14 @@
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.Context as Context exposing (Context, VBAT)
import Internal.Tools.Exceptions as X import Internal.Tools.Exceptions as X
import Internal.Tools.VersionControl as VC import Internal.Tools.VersionControl as VC
import Task exposing (Task) import Task exposing (Task)
sendMessageEvent : List String -> SendMessageEventInput -> Task X.Error SendMessageEventOutput sendMessageEvent : Context (VBAT a) -> SendMessageEventInput -> Task X.Error SendMessageEventOutput
sendMessageEvent versions = sendMessageEvent context input =
VC.withBottomLayer VC.withBottomLayer
{ current = Api.sendMessageEventV1 { current = Api.sendMessageEventV1
, version = "r0.0.0" , version = "r0.0.0"
@ -30,8 +31,10 @@ sendMessageEvent versions =
|> VC.sameForVersion "v1.3" |> VC.sameForVersion "v1.3"
|> VC.sameForVersion "v1.4" |> VC.sameForVersion "v1.4"
|> VC.sameForVersion "v1.5" |> VC.sameForVersion "v1.5"
|> VC.mostRecentFromVersionList versions |> VC.mostRecentFromVersionList (Context.getVersions context)
|> Maybe.withDefault (always <| Task.fail X.UnsupportedSpecVersion) |> Maybe.withDefault (always <| always <| Task.fail X.UnsupportedSpecVersion)
|> (|>) input
|> (|>) context
type alias SendMessageEventInput = type alias SendMessageEventInput =

View File

@ -2,15 +2,14 @@ module Internal.Api.SendStateKey.Api exposing (..)
import Internal.Api.Request as R import Internal.Api.Request as R
import Internal.Api.SendStateKey.V1.SpecObjects as SO1 import Internal.Api.SendStateKey.V1.SpecObjects as SO1
import Internal.Tools.Context exposing (Context)
import Internal.Tools.Exceptions as X import Internal.Tools.Exceptions as X
import Json.Decode as D import Json.Decode as D
import Task exposing (Task) import Task exposing (Task)
type alias SendStateKeyInputV1 = type alias SendStateKeyInputV1 =
{ accessToken : String { content : D.Value
, baseUrl : String
, content : D.Value
, eventType : String , eventType : String
, roomId : String , roomId : String
, stateKey : String , stateKey : String
@ -21,39 +20,27 @@ type alias SendStateKeyOutputV1 =
SO1.EventResponse SO1.EventResponse
sendStateKeyV1 : SendStateKeyInputV1 -> Task X.Error SendStateKeyOutputV1 sendStateKeyV1 : SendStateKeyInputV1 -> Context { a | accessToken : (), baseUrl : () } -> Task X.Error SendStateKeyOutputV1
sendStateKeyV1 data = sendStateKeyV1 { content, eventType, roomId, stateKey } =
R.rawApiCall R.callApi "PUT" "/_matrix/client/r0/rooms/{roomId}/state/{eventType}/{stateKey}"
{ headers = R.WithAccessToken data.accessToken >> R.withAttributes
, method = "PUT" [ R.accessToken
, baseUrl = data.baseUrl , R.replaceInUrl "eventType" eventType
, path = "/_matrix/client/r0/rooms/{roomId}/state/{eventType}/{stateKey}" , R.replaceInUrl "roomId" roomId
, pathParams = , R.replaceInUrl "stateKey" stateKey
[ ( "eventType", data.eventType ) , R.fullBody content
, ( "roomId", data.roomId )
, ( "stateKey", data.stateKey )
] ]
, queryParams = [] >> R.toTask SO1.eventResponseDecoder
, bodyParams = [ R.RequiredValue "*" data.content ]
, timeout = Nothing
, decoder = \_ -> SO1.eventResponseDecoder
}
sendStateKeyV2 : SendStateKeyInputV1 -> Task X.Error SendStateKeyOutputV1 sendStateKeyV2 : SendStateKeyInputV1 -> Context { a | accessToken : (), baseUrl : () } -> Task X.Error SendStateKeyOutputV1
sendStateKeyV2 data = sendStateKeyV2 { content, eventType, roomId, stateKey } =
R.rawApiCall R.callApi "PUT" "/_matrix/client/v3/rooms/{roomId}/state/{eventType}/{stateKey}"
{ headers = R.WithAccessToken data.accessToken >> R.withAttributes
, method = "PUT" [ R.accessToken
, baseUrl = data.baseUrl , R.replaceInUrl "eventType" eventType
, path = "/_matrix/client/v3/rooms/{roomId}/state/{eventType}/{stateKey}" , R.replaceInUrl "roomId" roomId
, pathParams = , R.replaceInUrl "stateKey" stateKey
[ ( "eventType", data.eventType ) , R.fullBody content
, ( "roomId", data.roomId )
, ( "stateKey", data.stateKey )
] ]
, queryParams = [] >> R.toTask SO1.eventResponseDecoder
, bodyParams = [ R.RequiredValue "*" data.content ]
, timeout = Nothing
, decoder = \_ -> SO1.eventResponseDecoder
}

View File

@ -1,13 +1,14 @@
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.Context as Context exposing (Context, VBA)
import Internal.Tools.Exceptions as X import Internal.Tools.Exceptions as X
import Internal.Tools.VersionControl as VC import Internal.Tools.VersionControl as VC
import Task exposing (Task) import Task exposing (Task)
sendStateKey : List String -> SendStateKeyInput -> Task X.Error SendStateKeyOutput sendStateKey : Context (VBA a) -> SendStateKeyInput -> Task X.Error SendStateKeyOutput
sendStateKey versions = sendStateKey context input =
VC.withBottomLayer VC.withBottomLayer
{ current = Api.sendStateKeyV1 { current = Api.sendStateKeyV1
, version = "r0.0.0" , version = "r0.0.0"
@ -30,8 +31,10 @@ sendStateKey versions =
|> VC.sameForVersion "v1.3" |> VC.sameForVersion "v1.3"
|> VC.sameForVersion "v1.4" |> VC.sameForVersion "v1.4"
|> VC.sameForVersion "v1.5" |> VC.sameForVersion "v1.5"
|> VC.mostRecentFromVersionList versions |> VC.mostRecentFromVersionList (Context.getVersions context)
|> Maybe.withDefault (always <| Task.fail X.UnsupportedSpecVersion) |> Maybe.withDefault (always <| always <| Task.fail X.UnsupportedSpecVersion)
|> (|>) input
|> (|>) context
type alias SendStateKeyInput = type alias SendStateKeyInput =

View File

@ -3,15 +3,14 @@ module Internal.Api.Sync.Api exposing (..)
import Internal.Api.Request as R import Internal.Api.Request as R
import Internal.Api.Sync.V1.SpecObjects as SO1 import Internal.Api.Sync.V1.SpecObjects as SO1
import Internal.Api.Sync.V2.SpecObjects as SO2 import Internal.Api.Sync.V2.SpecObjects as SO2
import Internal.Tools.Context exposing (Context)
import Internal.Tools.Exceptions as X import Internal.Tools.Exceptions as X
import Internal.Tools.SpecEnums as Enums import Internal.Tools.SpecEnums as Enums
import Task exposing (Task) import Task exposing (Task)
type alias SyncInputV1 = type alias SyncInputV1 =
{ accessToken : String { filter : Maybe String
, baseUrl : String
, filter : Maybe String
, fullState : Maybe Bool , fullState : Maybe Bool
, setPresence : Maybe Enums.UserPresence , setPresence : Maybe Enums.UserPresence
, since : Maybe String , since : Maybe String
@ -27,49 +26,31 @@ type alias SyncOutputV2 =
SO2.Sync SO2.Sync
syncV1 : SyncInputV1 -> Task X.Error SyncOutputV1 syncV1 : SyncInputV1 -> Context { a | accessToken : (), baseUrl : () } -> Task X.Error SyncOutputV1
syncV1 data = syncV1 data =
R.rawApiCall R.callApi "GET" "/_matrix/client/v3/sync"
{ headers = R.WithAccessToken data.accessToken >> R.withAttributes
, method = "GET" [ R.accessToken
, baseUrl = data.baseUrl , R.queryOpString "filter" data.filter
, path = "/_matrix/client/v3/sync" , R.queryOpBool "full_state" data.fullState
, pathParams = [] , R.queryOpString "set_presence" (Maybe.map Enums.fromUserPresence data.setPresence)
, queryParams = , R.queryOpString "since" data.since
[ R.OpQueryParamString "filter" data.filter , R.queryOpInt "timeout" data.timeout
, R.OpQueryParamBool "full_state" data.fullState , R.timeout <| Maybe.map ((*) 1000 >> toFloat) <| data.timeout
, R.OpQueryParamString "set_presence" (Maybe.map Enums.fromUserPresence data.setPresence)
, R.OpQueryParamString "since" data.since
, R.OpQueryParamInt "timeout" data.timeout
] ]
, bodyParams = [] >> R.toTask SO1.syncDecoder
, timeout =
data.timeout
|> Maybe.map ((*) 1000)
|> Maybe.map toFloat
, decoder = \_ -> SO1.syncDecoder
}
syncV2 : SyncInputV1 -> Task X.Error SyncOutputV2 syncV2 : SyncInputV1 -> Context { a | accessToken : (), baseUrl : () } -> Task X.Error SyncOutputV2
syncV2 data = syncV2 data =
R.rawApiCall R.callApi "GET" "/_matrix/client/v3/sync"
{ headers = R.WithAccessToken data.accessToken >> R.withAttributes
, method = "GET" [ R.accessToken
, baseUrl = data.baseUrl , R.queryOpString "filter" data.filter
, path = "/_matrix/client/v3/sync" , R.queryOpBool "full_state" data.fullState
, pathParams = [] , R.queryOpString "set_presence" (Maybe.map Enums.fromUserPresence data.setPresence)
, queryParams = , R.queryOpString "since" data.since
[ R.OpQueryParamString "filter" data.filter , R.queryOpInt "timeout" data.timeout
, R.OpQueryParamBool "full_state" data.fullState , R.timeout <| Maybe.map ((*) 1000 >> toFloat) <| data.timeout
, R.OpQueryParamString "set_presence" (Maybe.map Enums.fromUserPresence data.setPresence)
, R.OpQueryParamString "since" data.since
, R.OpQueryParamInt "timeout" data.timeout
] ]
, bodyParams = [] >> R.toTask SO2.syncDecoder
, timeout =
data.timeout
|> Maybe.map ((*) 1000)
|> Maybe.map toFloat
, decoder = \_ -> SO2.syncDecoder
}

View File

@ -2,13 +2,14 @@ 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.Context as Context exposing (Context, VBA)
import Internal.Tools.Exceptions as X import Internal.Tools.Exceptions as X
import Internal.Tools.VersionControl as VC import Internal.Tools.VersionControl as VC
import Task exposing (Task) import Task exposing (Task)
sync : List String -> SyncInput -> Task X.Error SyncOutput sync : Context (VBA a) -> SyncInput -> Task X.Error SyncOutput
sync versions = sync context input =
VC.withBottomLayer VC.withBottomLayer
{ current = Api.syncV1 { current = Api.syncV1
, version = "v1.2" , version = "v1.2"
@ -21,8 +22,10 @@ sync versions =
, version = "v1.4" , version = "v1.4"
} }
|> VC.sameForVersion "v1.5" |> VC.sameForVersion "v1.5"
|> VC.mostRecentFromVersionList versions |> VC.mostRecentFromVersionList (Context.getVersions context)
|> Maybe.withDefault (always <| Task.fail X.UnsupportedSpecVersion) |> Maybe.withDefault (always <| always <| Task.fail X.UnsupportedSpecVersion)
|> (|>) input
|> (|>) context
type alias SyncInput = type alias SyncInput =

View File

@ -2,20 +2,12 @@ module Internal.Api.Versions.Api exposing (..)
import Internal.Api.Request as R import Internal.Api.Request as R
import Internal.Api.Versions.V1.Versions as SO import Internal.Api.Versions.V1.Versions as SO
import Internal.Tools.Context exposing (Context)
import Internal.Tools.Exceptions as X import Internal.Tools.Exceptions as X
import Task exposing (Task) import Task exposing (Task)
versionsV1 : { baseUrl : String } -> Task X.Error SO.Versions versionsV1 : Context { a | baseUrl : () } -> Task X.Error SO.Versions
versionsV1 data = versionsV1 =
R.rawApiCall R.callApi "GET" "/_matrix/client/versions"
{ headers = R.NoHeaders >> R.toTask SO.versionsDecoder
, method = "GET"
, baseUrl = data.baseUrl
, path = "/_matrix/client/versions"
, pathParams = []
, queryParams = []
, bodyParams = []
, timeout = Nothing
, decoder = always SO.versionsDecoder
}

View File

@ -2,18 +2,19 @@ module Internal.Api.Versions.Main exposing (..)
import Internal.Api.Versions.Api as Api import Internal.Api.Versions.Api as Api
import Internal.Api.Versions.V1.Versions as SO import Internal.Api.Versions.V1.Versions as SO
import Internal.Tools.Context exposing (Context)
import Internal.Tools.Exceptions as X import Internal.Tools.Exceptions as X
import Task exposing (Task) import Task exposing (Task)
type alias VersionsInput = type alias VersionsInput =
String ()
type alias VersionsOutput = type alias VersionsOutput =
SO.Versions SO.Versions
getVersions : VersionsInput -> Task X.Error VersionsOutput getVersions : Context { a | baseUrl : () } -> Task X.Error VersionsOutput
getVersions baseUrl = getVersions context =
Api.versionsV1 { baseUrl = baseUrl } Api.versionsV1 context