diff --git a/src/Internal/Api/Ban/Api.elm b/src/Internal/Api/Ban/Api.elm index b449fd8..650c505 100644 --- a/src/Internal/Api/Ban/Api.elm +++ b/src/Internal/Api/Ban/Api.elm @@ -1,6 +1,7 @@ module Internal.Api.Ban.Api exposing (..) import Internal.Api.Request as R +import Internal.Config.SpecErrors as SE import Internal.Tools.Context exposing (Context) import Internal.Tools.Exceptions as X import Json.Decode as D @@ -26,6 +27,7 @@ banV1 { reason, roomId, userId } = , R.replaceInUrl "roomId" roomId , R.bodyOpString "reason" reason , R.bodyString "user_id" userId + , R.onStatusCode 403 (X.M_FORBIDDEN { error = Just SE.banNotAllowed }) ] >> R.toTask (D.map (always ()) D.value) @@ -38,5 +40,6 @@ banV2 { reason, roomId, userId } = , R.replaceInUrl "roomId" roomId , R.bodyOpString "reason" reason , R.bodyString "user_id" userId + , R.onStatusCode 403 (X.M_FORBIDDEN { error = Just SE.banNotAllowed }) ] >> R.toTask (D.map (always ()) D.value) diff --git a/src/Internal/Api/GetEvent/Api.elm b/src/Internal/Api/GetEvent/Api.elm index ac9c1dd..b611ca1 100644 --- a/src/Internal/Api/GetEvent/Api.elm +++ b/src/Internal/Api/GetEvent/Api.elm @@ -2,6 +2,7 @@ module Internal.Api.GetEvent.Api exposing (..) import Internal.Api.GetEvent.V1.SpecObjects as SO1 import Internal.Api.Request as R +import Internal.Config.SpecErrors as SE import Internal.Tools.Context as Context exposing (Context) import Internal.Tools.Exceptions as X import Task exposing (Task) @@ -24,5 +25,6 @@ getEventInputV1 data context = [ R.accessToken , R.replaceInUrl "eventId" (Context.getSentEvent context) , R.replaceInUrl "roomId" data.roomId + , R.onStatusCode 404 (X.M_NOT_FOUND { error = Just SE.eventNotFound }) ] |> R.toTask SO1.clientEventDecoder diff --git a/src/Internal/Api/GetMessages/Api.elm b/src/Internal/Api/GetMessages/Api.elm index c6e81a6..321ceed 100644 --- a/src/Internal/Api/GetMessages/Api.elm +++ b/src/Internal/Api/GetMessages/Api.elm @@ -5,6 +5,7 @@ import Internal.Api.GetMessages.V2.SpecObjects as SO2 import Internal.Api.GetMessages.V3.SpecObjects as SO3 import Internal.Api.GetMessages.V4.SpecObjects as SO4 import Internal.Api.Request as R +import Internal.Config.SpecErrors as SE import Internal.Tools.Context exposing (Context) import Internal.Tools.Exceptions as X import Internal.Tools.SpecEnums as Enums @@ -75,6 +76,7 @@ getMessagesV1 { direction, from, limit, roomId } = , R.queryString "dir" (Enums.fromEventOrder direction) , R.queryString "from" f , R.queryOpInt "limit" limit + , R.onStatusCode 403 (X.M_FORBIDDEN { error = Just SE.notInRoom }) ] >> R.toTask SO1.messagesResponseDecoder @@ -94,6 +96,7 @@ getMessagesV2 { direction, from, limit, roomId, to } = , R.queryString "from" f , R.queryOpInt "limit" limit , R.queryOpString "to" to + , R.onStatusCode 403 (X.M_FORBIDDEN { error = Just SE.notInRoom }) ] >> R.toTask SO1.messagesResponseDecoder @@ -114,6 +117,7 @@ getMessagesV3 { direction, filter, from, limit, roomId, to } = , R.queryOpInt "limit" limit , R.queryOpString "filter" filter , R.queryOpString "to" to + , R.onStatusCode 403 (X.M_FORBIDDEN { error = Just SE.notInRoom }) ] >> R.toTask SO1.messagesResponseDecoder @@ -134,6 +138,7 @@ getMessagesV4 { direction, filter, from, limit, roomId, to } = , R.queryOpInt "limit" limit , R.queryOpString "filter" filter , R.queryOpString "to" to + , R.onStatusCode 403 (X.M_FORBIDDEN { error = Just SE.notInRoom }) ] >> R.toTask SO2.messagesResponseDecoder @@ -154,6 +159,7 @@ getMessagesV5 { direction, filter, from, limit, roomId, to } = , R.queryOpInt "limit" limit , R.queryOpString "filter" filter , R.queryOpString "to" to + , R.onStatusCode 403 (X.M_FORBIDDEN { error = Just SE.notInRoom }) ] >> R.toTask SO3.messagesResponseDecoder @@ -174,6 +180,7 @@ getMessagesV6 { direction, filter, from, limit, roomId, to } = , R.queryOpInt "limit" limit , R.queryOpString "filter" filter , R.queryOpString "to" to + , R.onStatusCode 403 (X.M_FORBIDDEN { error = Just SE.notInRoom }) ] >> R.toTask SO3.messagesResponseDecoder @@ -194,6 +201,7 @@ getMessagesV7 { direction, filter, from, limit, roomId, to } = , R.queryOpInt "limit" limit , R.queryOpString "filter" filter , R.queryOpString "to" to + , R.onStatusCode 403 (X.M_FORBIDDEN { error = Just SE.notInRoom }) ] >> R.toTask SO4.messagesResponseDecoder @@ -212,5 +220,6 @@ getMessagesV8 { direction, filter, from, limit, roomId, to } = , R.queryOpInt "limit" limit , R.queryOpString "filter" filter , R.queryOpString "to" to + , R.onStatusCode 403 (X.M_FORBIDDEN { error = Just SE.notInRoom }) ] >> R.toTask SO4.messagesResponseDecoder diff --git a/src/Internal/Api/Invite/Api.elm b/src/Internal/Api/Invite/Api.elm index 63ed950..8a7eee1 100644 --- a/src/Internal/Api/Invite/Api.elm +++ b/src/Internal/Api/Invite/Api.elm @@ -1,6 +1,7 @@ module Internal.Api.Invite.Api exposing (..) import Internal.Api.Request as R +import Internal.Config.SpecErrors as SE import Internal.Tools.Context exposing (Context) import Internal.Tools.Exceptions as X import Json.Decode as D @@ -31,6 +32,7 @@ inviteV1 { roomId, userId } = [ R.accessToken , R.replaceInUrl "roomId" roomId , R.bodyString "user_id" userId + , R.onStatusCode 403 (X.M_FORBIDDEN { error = Just SE.inviteNotAllowed }) ] >> R.toTask (D.map (always ()) D.value) @@ -43,5 +45,8 @@ inviteV2 { reason, roomId, userId } = , R.replaceInUrl "roomId" roomId , R.bodyString "user_id" userId , R.bodyOpString "reason" reason + , R.onStatusCode 400 (X.M_INVALID_PARAM { error = Just SE.invalidRequest }) + , R.onStatusCode 403 (X.M_FORBIDDEN { error = Just SE.inviteNotAllowed }) + , R.onStatusCode 429 (X.M_LIMIT_EXCEEDED { error = Just SE.ratelimited, retryAfterMs = Nothing }) ] >> R.toTask (D.map (always ()) D.value) diff --git a/src/Internal/Api/JoinRoomById/Api.elm b/src/Internal/Api/JoinRoomById/Api.elm index 390d8ec..2dd3390 100644 --- a/src/Internal/Api/JoinRoomById/Api.elm +++ b/src/Internal/Api/JoinRoomById/Api.elm @@ -1,6 +1,7 @@ module Internal.Api.JoinRoomById.Api exposing (..) import Internal.Api.Request as R +import Internal.Config.SpecErrors as SE import Internal.Tools.Context exposing (Context, VBA) import Internal.Tools.Exceptions as X import Json.Decode as D @@ -25,6 +26,8 @@ joinRoomByIdV1 { roomId } = >> R.withAttributes [ R.accessToken , R.replaceInUrl "roomId" roomId + , R.onStatusCode 403 (X.M_FORBIDDEN { error = Just SE.joinNotAllowed }) + , R.onStatusCode 429 (X.M_LIMIT_EXCEEDED { error = Just SE.ratelimited, retryAfterMs = Nothing }) ] >> R.toTask (D.map (\r -> { roomId = r }) (D.field "room_id" D.string)) @@ -36,5 +39,7 @@ joinRoomByIdV2 { roomId, reason } = [ R.accessToken , R.replaceInUrl "roomId" roomId , R.bodyOpString "reason" reason + , R.onStatusCode 403 (X.M_FORBIDDEN { error = Just SE.joinNotAllowed }) + , R.onStatusCode 429 (X.M_LIMIT_EXCEEDED { error = Just SE.ratelimited, retryAfterMs = Nothing }) ] >> R.toTask (D.map (\r -> { roomId = r }) (D.field "room_id" D.string)) diff --git a/src/Internal/Api/JoinedMembers/Api.elm b/src/Internal/Api/JoinedMembers/Api.elm index 64172c5..72f2ad2 100644 --- a/src/Internal/Api/JoinedMembers/Api.elm +++ b/src/Internal/Api/JoinedMembers/Api.elm @@ -2,6 +2,7 @@ module Internal.Api.JoinedMembers.Api exposing (..) import Internal.Api.JoinedMembers.V1.SpecObjects as SO1 import Internal.Api.Request as R +import Internal.Config.SpecErrors as SE import Internal.Tools.Context exposing (Context) import Internal.Tools.Exceptions as X import Task exposing (Task) @@ -22,6 +23,7 @@ joinedMembersV1 { roomId } = >> R.withAttributes [ R.accessToken , R.replaceInUrl "roomId" roomId + , R.onStatusCode 403 (X.M_FORBIDDEN { error = Just SE.notInRoom }) ] >> R.toTask SO1.roomMemberListDecoder @@ -32,5 +34,6 @@ joinedMembersV2 { roomId } = >> R.withAttributes [ R.accessToken , R.replaceInUrl "roomId" roomId + , R.onStatusCode 403 (X.M_FORBIDDEN { error = Just SE.notInRoom }) ] >> R.toTask SO1.roomMemberListDecoder diff --git a/src/Internal/Api/Leave/Api.elm b/src/Internal/Api/Leave/Api.elm index 7837be0..3e70e8c 100644 --- a/src/Internal/Api/Leave/Api.elm +++ b/src/Internal/Api/Leave/Api.elm @@ -1,6 +1,7 @@ module Internal.Api.Leave.Api exposing (..) import Internal.Api.Request as R +import Internal.Config.SpecErrors as SE import Internal.Tools.Context exposing (Context) import Internal.Tools.Exceptions as X import Json.Decode as D @@ -25,16 +26,18 @@ leaveV1 { roomId } = >> R.withAttributes [ R.accessToken , R.replaceInUrl "roomId" roomId + , R.onStatusCode 429 (X.M_LIMIT_EXCEEDED { error = Just SE.ratelimited, retryAfterMs = Nothing }) ] >> R.toTask (D.map (always ()) D.value) leaveV2 : LeaveInputV2 -> Context { a | accessToken : (), baseUrl : () } -> Task X.Error LeaveOutputV1 leaveV2 { roomId, reason } = - R.callApi "POST" "/_matrix/client/r0/rooms/{roomId}/leave" + R.callApi "POST" "/_matrix/client/v3/rooms/{roomId}/leave" >> R.withAttributes [ R.accessToken , R.replaceInUrl "roomId" roomId , R.bodyOpString "reason" reason + , R.onStatusCode 429 (X.M_LIMIT_EXCEEDED { error = Just SE.ratelimited, retryAfterMs = Nothing }) ] >> R.toTask (D.map (always ()) D.value) diff --git a/src/Internal/Api/LoginWithUsernameAndPassword/Api.elm b/src/Internal/Api/LoginWithUsernameAndPassword/Api.elm index b98ce25..e766008 100644 --- a/src/Internal/Api/LoginWithUsernameAndPassword/Api.elm +++ b/src/Internal/Api/LoginWithUsernameAndPassword/Api.elm @@ -6,6 +6,7 @@ import Internal.Api.LoginWithUsernameAndPassword.V3.SpecObjects as SO3 import Internal.Api.LoginWithUsernameAndPassword.V4.SpecObjects as SO4 import Internal.Api.LoginWithUsernameAndPassword.V5.Login as SO5 import Internal.Api.Request as R +import Internal.Config.SpecErrors as SE import Internal.Tools.Context exposing (Context) import Internal.Tools.Exceptions as X import Json.Encode as E @@ -53,6 +54,7 @@ loginWithUsernameAndPasswordV1 { username, password } = [ R.bodyString "password" password , R.bodyString "type" "m.login.password" , R.bodyString "user" username + , R.onStatusCode 429 (X.M_LIMIT_EXCEEDED { error = Just SE.ratelimited, retryAfterMs = Nothing }) ] >> R.toTask SO1.loggedInResponseDecoder @@ -66,6 +68,7 @@ loginWithUsernameAndPasswordV2 { deviceId, initialDeviceDisplayName, password, u , R.bodyString "password" password , R.bodyOpString "device_id" deviceId , R.bodyOpString "initial_device_display_name" initialDeviceDisplayName + , R.onStatusCode 429 (X.M_LIMIT_EXCEEDED { error = Just SE.ratelimited, retryAfterMs = Nothing }) ] >> R.toTask SO2.loggedInResponseDecoder @@ -83,6 +86,7 @@ loginWithUsernameAndPasswordV3 { deviceId, initialDeviceDisplayName, password, u ] |> E.object |> R.bodyValue "identifier" + , R.onStatusCode 429 (X.M_LIMIT_EXCEEDED { error = Just SE.ratelimited, retryAfterMs = Nothing }) ] >> R.toTask SO3.loggedInResponseDecoder @@ -100,6 +104,7 @@ loginWithUsernameAndPasswordV4 { deviceId, initialDeviceDisplayName, password, u ] |> E.object |> R.bodyValue "identifier" + , R.onStatusCode 429 (X.M_LIMIT_EXCEEDED { error = Just SE.ratelimited, retryAfterMs = Nothing }) ] >> R.toTask SO4.loggedInResponseDecoder @@ -117,6 +122,7 @@ loginWithUsernameAndPasswordV5 { deviceId, initialDeviceDisplayName, password, u ] |> E.object |> R.bodyValue "identifier" + , R.onStatusCode 429 (X.M_LIMIT_EXCEEDED { error = Just SE.ratelimited, retryAfterMs = Nothing }) ] >> R.toTask SO4.loggedInResponseDecoder @@ -135,21 +141,6 @@ loginWithUsernameAndPasswordV6 { deviceId, initialDeviceDisplayName, password, u ] |> E.object |> R.bodyValue "identifier" + , R.onStatusCode 429 (X.M_LIMIT_EXCEEDED { error = Just SE.ratelimited, retryAfterMs = Nothing }) ] >> R.toTask SO5.loggedInResponseDecoder - - - --- loginWithUsernameAndPasswordV5 : LoginWithUsernameAndPasswordInputV1 -> Context { a | baseUrl : () } -> Task X.Error LoginWithUsernameAndPasswordOutputV5 --- loginWithUsernameAndPasswordV5 { username, password } = --- R.callApi "POST" "/_matrix/client/v3/login" --- >> R.withAttributes --- [ [ ( "type", E.string "m.id.user" ) --- , ( "user", E.string username ) --- ] --- |> E.object --- |> R.bodyValue "identifier" --- , R.bodyString "password" password --- , R.bodyString "type" "m.login.password" --- ] --- >> R.toTask SO.loggedInResponseDecoder diff --git a/src/Internal/Api/Request.elm b/src/Internal/Api/Request.elm index 129a2bb..dbdfcee 100644 --- a/src/Internal/Api/Request.elm +++ b/src/Internal/Api/Request.elm @@ -1,5 +1,9 @@ module Internal.Api.Request exposing (..) +{-| The request module builds HTTP tasks that are designed around calling the Matrix API. +-} + +import Dict exposing (Dict) import Http import Internal.Api.Helpers as Helpers import Internal.Tools.Context as Context exposing (Context) @@ -31,6 +35,7 @@ type ContextAttr | NoAttr | QueryParam UrlBuilder.QueryParameter | ReplaceInUrl String String + | StatusCodeResponse Int X.Error | Timeout Float | UrlPath String @@ -97,7 +102,19 @@ toTask decoder (ApiCall data) = |> E.object ) |> Http.jsonBody - , resolver = rawApiCallResolver (always decoder) + , resolver = + data.attributes + |> List.filterMap + (\attr -> + case attr of + StatusCodeResponse code err -> + Just ( code, err ) + + _ -> + Nothing + ) + |> Dict.fromList + |> rawApiCallResolver decoder , timeout = data.attributes |> List.filterMap @@ -260,6 +277,14 @@ fullBody value _ = FullBody value +{-| If the Matrix API does not fit the Matrix spec but returns the following status code, +you may interpret it as the given error. +-} +onStatusCode : Int -> X.ServerError -> Attribute a +onStatusCode code err _ = + StatusCodeResponse code (X.ServerException err) + + queryBool : String -> Bool -> Attribute a queryBool key value _ = (if value then @@ -332,8 +357,8 @@ withTransactionId = Context.getTransactionId >> ReplaceInUrl "txnId" -rawApiCallResolver : (Int -> D.Decoder a) -> Http.Resolver X.Error a -rawApiCallResolver decoder = +rawApiCallResolver : D.Decoder a -> Dict Int X.Error -> Http.Resolver X.Error a +rawApiCallResolver decoder statusCodeErrors = Http.stringResolver (\response -> case response of @@ -353,15 +378,19 @@ rawApiCallResolver decoder = |> Err Http.BadStatus_ metadata body -> - decodeServerResponse (decoder metadata.statusCode) body + statusCodeErrors + |> Dict.get metadata.statusCode + |> decodeServerResponse decoder body Http.GoodStatus_ metadata body -> - decodeServerResponse (decoder metadata.statusCode) body + statusCodeErrors + |> Dict.get metadata.statusCode + |> decodeServerResponse decoder body ) -decodeServerResponse : D.Decoder a -> String -> Result X.Error a -decodeServerResponse decoder body = +decodeServerResponse : D.Decoder a -> String -> Maybe X.Error -> Result X.Error a +decodeServerResponse decoder body statusCodeError = case D.decodeString D.value body of Err e -> e @@ -378,11 +407,17 @@ decodeServerResponse decoder body = Err err -> -- The response is not valid! -- Check if it is a valid error type as defined in spec. - case D.decodeString X.errorCatches body of - Ok v -> + case ( D.decodeString X.errorCatches body, statusCodeError ) of + ( Ok v, _ ) -> Err (X.ServerException v) - Err _ -> + -- It does not compute as a valid spec error. Therefore, + -- we will look at its status code before ultimately giving up + -- on the validity of the response. + ( Err _, Just sce ) -> + Err sce + + ( Err _, Nothing ) -> err |> D.errorToString |> X.ServerReturnsBadJSON diff --git a/src/Internal/Api/SendStateKey/Api.elm b/src/Internal/Api/SendStateKey/Api.elm index c8b29b4..70eb616 100644 --- a/src/Internal/Api/SendStateKey/Api.elm +++ b/src/Internal/Api/SendStateKey/Api.elm @@ -2,6 +2,7 @@ module Internal.Api.SendStateKey.Api exposing (..) import Internal.Api.Request as R import Internal.Api.SendStateKey.V1.SpecObjects as SO1 +import Internal.Config.SpecErrors as SE import Internal.Tools.Context exposing (Context) import Internal.Tools.Exceptions as X import Json.Decode as D @@ -29,6 +30,8 @@ sendStateKeyV1 { content, eventType, roomId, stateKey } = , R.replaceInUrl "roomId" roomId , R.replaceInUrl "stateKey" stateKey , R.fullBody content + , R.onStatusCode 400 (X.M_INVALID_PARAM { error = Just SE.invalidRequest }) + , R.onStatusCode 403 (X.M_FORBIDDEN { error = Just SE.sendNotAllowed }) ] >> R.toTask SO1.eventResponseDecoder @@ -42,5 +45,7 @@ sendStateKeyV2 { content, eventType, roomId, stateKey } = , R.replaceInUrl "roomId" roomId , R.replaceInUrl "stateKey" stateKey , R.fullBody content + , R.onStatusCode 400 (X.M_INVALID_PARAM { error = Just SE.invalidRequest }) + , R.onStatusCode 403 (X.M_FORBIDDEN { error = Just SE.sendNotAllowed }) ] >> R.toTask SO1.eventResponseDecoder diff --git a/src/Internal/Api/SetAccountData/Api.elm b/src/Internal/Api/SetAccountData/Api.elm index 783fb46..b53bb57 100644 --- a/src/Internal/Api/SetAccountData/Api.elm +++ b/src/Internal/Api/SetAccountData/Api.elm @@ -1,6 +1,7 @@ module Internal.Api.SetAccountData.Api exposing (..) import Internal.Api.Request as R +import Internal.Config.SpecErrors as SE import Internal.Tools.Context as Context exposing (Context) import Internal.Tools.Exceptions as X import Json.Decode as D @@ -33,6 +34,9 @@ setAccountDataV1 { content, eventType, roomId } context = , R.replaceInUrl "type" eventType , R.replaceInUrl "userId" (Context.getUserId context) , R.fullBody content + , R.onStatusCode 400 (X.M_INVALID_PARAM { error = Just SE.invalidRequest }) + , R.onStatusCode 403 (X.M_FORBIDDEN { error = Just SE.accountDataSetNotAllowed }) + , R.onStatusCode 405 (X.M_BAD_JSON { error = Just SE.accountDataControlledByServer }) ] >> R.toTask (D.map (always ()) D.value) |> (|>) context @@ -53,6 +57,9 @@ setAccountDataV2 { content, eventType, roomId } context = , R.replaceInUrl "type" eventType , R.replaceInUrl "userId" (Context.getUserId context) , R.fullBody content + , R.onStatusCode 400 (X.M_INVALID_PARAM { error = Just SE.invalidRequest }) + , R.onStatusCode 403 (X.M_FORBIDDEN { error = Just SE.accountDataSetNotAllowed }) + , R.onStatusCode 405 (X.M_BAD_JSON { error = Just SE.accountDataControlledByServer }) ] >> R.toTask (D.map (always ()) D.value) |> (|>) context diff --git a/src/Internal/Api/WhoAmI/Api.elm b/src/Internal/Api/WhoAmI/Api.elm index 815f5b1..51af837 100644 --- a/src/Internal/Api/WhoAmI/Api.elm +++ b/src/Internal/Api/WhoAmI/Api.elm @@ -4,6 +4,7 @@ import Internal.Api.Request as R import Internal.Api.WhoAmI.V1.SpecObjects as SO1 import Internal.Api.WhoAmI.V2.SpecObjects as SO2 import Internal.Api.WhoAmI.V3.SpecObjects as SO3 +import Internal.Config.SpecErrors as SE import Internal.Tools.Context exposing (Context) import Internal.Tools.Exceptions as X import Task exposing (Task) @@ -28,19 +29,34 @@ type alias WhoAmIOutputV3 = whoAmIV1 : WhoAmIInputV1 -> Context { a | accessToken : (), baseUrl : () } -> Task X.Error WhoAmIOutputV1 whoAmIV1 _ = R.callApi "GET" "/_matrix/client/r0/account/whoami" - >> R.withAttributes [ R.accessToken ] + >> R.withAttributes + [ R.accessToken + , R.onStatusCode 401 (X.M_UNKNOWN_TOKEN { error = Just SE.accessTokenNotRecognized, soft_logout = Nothing }) + , R.onStatusCode 403 (X.M_FORBIDDEN { error = Just SE.appserviceCannotMasquerade }) + , R.onStatusCode 429 (X.M_LIMIT_EXCEEDED { error = Just SE.ratelimited, retryAfterMs = Nothing }) + ] >> R.toTask SO1.whoAmIResponseDecoder whoAmIV2 : WhoAmIInputV1 -> Context { a | accessToken : (), baseUrl : () } -> Task X.Error WhoAmIOutputV2 whoAmIV2 _ = R.callApi "GET" "/_matrix/client/v3/account/whoami" - >> R.withAttributes [ R.accessToken ] + >> R.withAttributes + [ R.accessToken + , R.onStatusCode 401 (X.M_UNKNOWN_TOKEN { error = Just SE.accessTokenNotRecognized, soft_logout = Nothing }) + , R.onStatusCode 403 (X.M_FORBIDDEN { error = Just SE.appserviceCannotMasquerade }) + , R.onStatusCode 429 (X.M_LIMIT_EXCEEDED { error = Just SE.ratelimited, retryAfterMs = Nothing }) + ] >> R.toTask SO2.whoAmIResponseDecoder whoAmIV3 : WhoAmIInputV1 -> Context { a | accessToken : (), baseUrl : () } -> Task X.Error WhoAmIOutputV3 whoAmIV3 _ = R.callApi "GET" "/_matrix/client/v3/account/whoami" - >> R.withAttributes [ R.accessToken ] + >> R.withAttributes + [ R.accessToken + , R.onStatusCode 401 (X.M_UNKNOWN_TOKEN { error = Just SE.accessTokenNotRecognized, soft_logout = Nothing }) + , R.onStatusCode 403 (X.M_FORBIDDEN { error = Just SE.appserviceCannotMasquerade }) + , R.onStatusCode 429 (X.M_LIMIT_EXCEEDED { error = Just SE.ratelimited, retryAfterMs = Nothing }) + ] >> R.toTask SO3.whoAmIResponseDecoder diff --git a/src/Internal/Config/SpecErrors.elm b/src/Internal/Config/SpecErrors.elm new file mode 100644 index 0000000..b14f961 --- /dev/null +++ b/src/Internal/Config/SpecErrors.elm @@ -0,0 +1,107 @@ +module Internal.Config.SpecErrors exposing (..) + +{-| Sometimes, the Matrix spec suggests to interpret an HTTP response as a certain +error, even if the server doesn't explicitly say so. + +In such cases, the following constants are used to indicate that such an error has occurred. + +-} + + +{-| A user attempts to get information but the homeserver does not recognize the access token that the user has provided. +-} +accessTokenNotRecognized : String +accessTokenNotRecognized = + "Unrecognised access token." + + +{-| A user attempts to change account data that is controlled by the server. +-} +accountDataControlledByServer : String +accountDataControlledByServer = + "You cannot change this account data type as it's controlled by the server." + + +{-| A user tries to set account data, but they are not allowed to do so. +-} +accountDataSetNotAllowed : String +accountDataSetNotAllowed = + "You are not authorized to set this account data." + + +{-| The appservice cannot masquerade as the user or has not registered them. +-} +appserviceCannotMasquerade : String +appserviceCannotMasquerade = + "Application service has not registered this user." + + +{-| The user attempts to ban another user, but they are not allowed to do so. +For example, + + - The banner is not currently in the room. + - The banner’s power level is insufficient to ban users from the room. + +-} +banNotAllowed : String +banNotAllowed = + "You do not have permission to ban someone in this room." + + +{-| A user tries to access to an event, but either it doesn't exist or they lack permission to read it. +-} +eventNotFound : String +eventNotFound = + "The event was not found or you do not have permission to read this event." + + +{-| A user made a request that is considered invalid. For example: + + - The request body is malformed + - The user tried to interact with users from a homeserver that do not support the action + +-} +invalidRequest : String +invalidRequest = + "The request is invalid." + + +{-| A user tries to invite another user to a room, but they lack the permission to do so. Example reasons for rejection are: + + - The invitee has been banned from the room. + - The invitee is already a member of the room. + - The inviter is not currently in the room. + - The inviter's power level is insufficient to invite users to the room. + +-} +inviteNotAllowed : String +inviteNotAllowed = + "You do not have permission to invite the user to the room." + + +{-| A user tries to join a room that they're not allowed to join. +-} +joinNotAllowed : String +joinNotAllowed = + "You do not have permission to join the room." + + +{-| A user tries to access information from a room that they don't have access to. +-} +notInRoom : String +notInRoom = + "You aren't a member of the room." + + +{-| A user tries to make an HTTP request, but it was ratelimited. +-} +ratelimited : String +ratelimited = + "This request was rate-limited." + + +{-| A user is trying to send an invite to a room, but they're not allowed to do so. +-} +sendNotAllowed : String +sendNotAllowed = + "You don't have permission to send the event into the room."