Skip to content

Commit 1a09dc7

Browse files
nvmdTristanCacqueray
authored andcommitted
Update login API, use DeriveGeneric, remove aeson-casing
1 parent 53ae502 commit 1a09dc7

4 files changed

Lines changed: 42 additions & 35 deletions

File tree

matrix-client.cabal

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ source-repository head
3232
common common-options
3333
build-depends:
3434
aeson >=1.0.0.0 && <3,
35-
aeson-casing >=0.2.0.0 && <0.3.0.0,
3635
base >=4.11.0.0 && <5,
3736

3837
ghc-options:

src/Network/Matrix/Client.hs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -141,8 +141,6 @@ import Control.Applicative
141141
import Control.Monad (mzero)
142142
import Control.Monad.IO.Class (MonadIO (liftIO))
143143
import Data.Aeson (FromJSON (..), ToJSON (..), Value (Object, String), encode, genericParseJSON, genericToJSON, object, withObject, withText, (.:), (.:?), (.=))
144-
import qualified Data.Aeson as Aeson
145-
import Data.Aeson.Casing (aesonPrefix, snakeCase)
146144
import Data.Aeson.Types (Parser)
147145
import Data.Bifunctor (bimap)
148146
import qualified Data.ByteString as B
@@ -177,8 +175,9 @@ data LoginCredentials = LoginCredentials
177175
}
178176

179177
mkLoginRequest :: LoginCredentials -> IO HTTP.Request
180-
mkLoginRequest LoginCredentials{..} =
181-
mkLoginRequest' lBaseUrl lDeviceId lInitialDeviceDisplayName lUsername lLoginSecret
178+
mkLoginRequest LoginCredentials{..} = let
179+
enableRefreshTokens = False
180+
in mkLoginRequest' lBaseUrl lDeviceId lInitialDeviceDisplayName enableRefreshTokens lUsername lLoginSecret
182181

183182
-- | 'login' allows you to generate a session token.
184183
login :: LoginCredentials -> IO ClientSession
@@ -926,9 +925,6 @@ defaultEventFilter = EventFilter Nothing Nothing Nothing Nothing Nothing
926925
eventFilterAll :: EventFilter
927926
eventFilterAll = defaultEventFilter{efLimit = Just 0, efNotTypes = Just ["*"]}
928927

929-
aesonOptions :: Aeson.Options
930-
aesonOptions = (aesonPrefix snakeCase){Aeson.omitNothingFields = True}
931-
932928
instance ToJSON EventFilter where
933929
toJSON = genericToJSON aesonOptions
934930

src/Network/Matrix/Internal.hs

Lines changed: 37 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
{-# LANGUAGE DeriveGeneric #-}
12
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
23
{-# LANGUAGE LambdaCase #-}
34
{-# LANGUAGE NumericUnderscores #-}
@@ -7,14 +8,15 @@
78
-- | This module contains low-level HTTP utility
89
module Network.Matrix.Internal where
910

11+
import GHC.Generics (Generic)
1012
import Control.Concurrent (threadDelay)
1113
import Control.Exception (Exception, throw, throwIO)
1214
import Control.Monad (mzero, unless, void)
1315
import Control.Monad.Catch (Handler (Handler), MonadMask)
1416
import Control.Monad.IO.Class (MonadIO, liftIO)
1517
import Control.Retry (RetryStatus (..))
1618
import qualified Control.Retry as Retry
17-
import Data.Aeson (FromJSON (..), FromJSONKey (..), Value (Object), eitherDecode, encode, object, withObject, (.:), (.:?), (.=))
19+
import Data.Aeson (FromJSON (..), FromJSONKey (..), Value (Object), eitherDecode, encode, object, (.:), (.=), defaultOptions, Options (..), camelTo2, genericParseJSON)
1820
import Data.ByteString.Lazy (ByteString, toStrict)
1921
import Data.Hashable (Hashable)
2022
import Data.Maybe (catMaybes, fromMaybe)
@@ -27,27 +29,26 @@ import Network.HTTP.Types (Status (..))
2729
import Network.HTTP.Types.Status (statusIsSuccessful)
2830
import System.Environment (getEnv)
2931
import System.IO (stderr)
32+
import Data.Char (isUpper)
3033

3134
newtype MatrixToken = MatrixToken Text
3235
newtype Username = Username {username :: Text}
3336
newtype DeviceId = DeviceId {deviceId :: Text}
3437
newtype InitialDeviceDisplayName = InitialDeviceDisplayName {initialDeviceDisplayName :: Text}
3538
data LoginSecret = Password Text | Token Text
3639

40+
-- https://spec.matrix.org/v1.17/client-server-api/#post_matrixclientv3login
3741
data LoginResponse = LoginResponse
38-
{ lrUserId :: Text
39-
, lrAccessToken :: Text
40-
, lrHomeServer :: Maybe Text
42+
{ lrAccessToken :: Text
4143
, lrDeviceId :: Text
42-
}
44+
, lrExpiresInMs :: Maybe Int -- Added in v1.3
45+
, lrHomeServer :: Maybe Text
46+
, lrRefreshToken :: Maybe Text -- Added in v1.3
47+
, lrUserId :: Text
48+
} deriving (Generic, Show)
4349

4450
instance FromJSON LoginResponse where
45-
parseJSON = withObject "LoginResponse" $ \v -> do
46-
userId' <- v .: "user_id"
47-
accessToken' <- v .: "access_token"
48-
homeServer' <- v .:? "home_server"
49-
deviceId' <- v .: "device_id"
50-
pure $ LoginResponse userId' accessToken' homeServer' deviceId'
51+
parseJSON = genericParseJSON aesonOptions
5152

5253
getTokenFromEnv ::
5354
-- | The envirnoment variable name
@@ -85,9 +86,10 @@ mkRequest' baseUrl (MatrixToken token) auth path = do
8586
authHeaders =
8687
[("Authorization", "Bearer " <> encodeUtf8 token) | auth]
8788

88-
mkLoginRequest' :: Text -> Maybe DeviceId -> Maybe InitialDeviceDisplayName -> Username -> LoginSecret -> IO HTTP.Request
89-
mkLoginRequest' baseUrl did idn (Username name) secret' = do
90-
let path = "/_matrix/client/r0/login"
89+
-- https://spec.matrix.org/v1.17/client-server-api/#post_matrixclientv3login
90+
mkLoginRequest' :: Text -> Maybe DeviceId -> Maybe InitialDeviceDisplayName -> Bool -> Username -> LoginSecret -> IO HTTP.Request
91+
mkLoginRequest' baseUrl did idn enableRefreshTokens (Username name) secret' = do
92+
let path = "/_matrix/client/v3/login"
9193
initRequest <- HTTP.parseUrlThrow (unpack $ baseUrl <> path)
9294

9395
let (secretKey, secret, secretType) = case secret' of
@@ -100,6 +102,7 @@ mkLoginRequest' baseUrl did idn (Username name) secret' = do
100102
object $
101103
[ "identifier" .= object ["type" .= ("m.id.user" :: Text), "user" .= name]
102104
, secretKey .= secret
105+
, "refresh_token" .= enableRefreshTokens -- Added in v1.3
103106
, "type" .= (secretType :: Text)
104107
]
105108
<> catMaybes
@@ -111,7 +114,7 @@ mkLoginRequest' baseUrl did idn (Username name) secret' = do
111114

112115
mkLogoutRequest' :: Text -> MatrixToken -> IO HTTP.Request
113116
mkLogoutRequest' baseUrl (MatrixToken token) = do
114-
let path = "/_matrix/client/r0/logout"
117+
let path = "/_matrix/client/v3/logout"
115118
initRequest <- HTTP.parseUrlThrow (unpack $ baseUrl <> path)
116119
let headers = [("Authorization", encodeUtf8 $ "Bearer " <> token)]
117120
pure $ initRequest{HTTP.method = "POST", HTTP.requestHeaders = headers}
@@ -145,20 +148,15 @@ data MatrixError = MatrixError
145148
, meError :: Text
146149
, meRetryAfterMS :: Maybe Int
147150
}
148-
deriving (Show, Eq)
151+
deriving (Generic, Show, Eq)
152+
153+
instance FromJSON MatrixError where
154+
parseJSON = genericParseJSON aesonOptions
149155

150156
data MatrixException = MatrixRateLimit deriving (Show)
151157

152158
instance Exception MatrixException
153159

154-
instance FromJSON MatrixError where
155-
parseJSON (Object v) =
156-
MatrixError
157-
<$> v .: "errcode"
158-
<*> v .: "error"
159-
<*> v .:? "retry_after_ms"
160-
parseJSON _ = mzero
161-
162160
-- | 'MatrixIO' is a convenient type alias for server response
163161
type MatrixIO a = MatrixM IO a
164162

@@ -211,3 +209,18 @@ retryWithLog limit logRetry action =
211209

212210
retry :: (MonadIO m, MonadMask m) => MatrixM m a -> MatrixM m a
213211
retry = retryWithLog 7 (liftIO . hPutStrLn stderr)
212+
213+
-------------------------------------------------------------------------------
214+
-- Utils
215+
216+
aesonOptions :: Options
217+
aesonOptions = defaultOptions
218+
{ fieldLabelModifier = camelTo2 '_' . dropPrefix
219+
, omitNothingFields = True
220+
}
221+
where
222+
-- drops lower case prefix
223+
dropPrefix :: String -> String
224+
dropPrefix [] = []
225+
dropPrefix (x:xs) | isUpper x = x : xs
226+
| otherwise = dropPrefix xs

src/Network/Matrix/Room.hs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@
44
-- | Matrix room related data types
55
module Network.Matrix.Room (RoomCreatePreset (..), RoomCreateRequest (..)) where
66

7+
import Network.Matrix.Internal (aesonOptions)
78
import Data.Aeson (ToJSON (..), Value (..), genericToJSON)
8-
import qualified Data.Aeson as Aeson
9-
import Data.Aeson.Casing (aesonPrefix, snakeCase)
109
import Data.Text (Text)
1110
import GHC.Generics (Generic)
1211

@@ -32,4 +31,4 @@ data RoomCreateRequest = RoomCreateRequest
3231
deriving (Eq, Show, Generic)
3332

3433
instance ToJSON RoomCreateRequest where
35-
toJSON = genericToJSON $ (aesonPrefix snakeCase){Aeson.omitNothingFields = True}
34+
toJSON = genericToJSON aesonOptions

0 commit comments

Comments
 (0)