{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Web.Scim.Server.Mock where
import Control.Monad
import Control.Monad.Morph
import Control.Monad.Reader
import Control.Monad.STM (STM, atomically)
import Data.Aeson
import qualified Data.CaseInsensitive as CI
import Data.Hashable
import Data.Text (Text, pack)
import Data.Time.Calendar
import Data.Time.Clock
import GHC.Exts (sortWith)
import ListT
import qualified Network.URI as URI
import Servant
import qualified StmContainers.Map as STMMap
import Text.Read (readMaybe)
import Web.Scim.Class.Auth
import Web.Scim.Class.Group hiding (value)
import Web.Scim.Class.User
import Web.Scim.Filter (AttrPath (..), CompValue (..), Filter (..), compareStr)
import Web.Scim.Handler
import Web.Scim.Schema.Common (WithId (WithId, value))
import qualified Web.Scim.Schema.Common as Common
import Web.Scim.Schema.Error
import Web.Scim.Schema.ListResponse
import Web.Scim.Schema.Meta
import Web.Scim.Schema.ResourceType
import Web.Scim.Schema.Schema (Schema (User20))
import Web.Scim.Schema.User
data Mock
newtype Id = Id {Id -> Int
unId :: Int}
deriving (Id -> Id -> Bool
(Id -> Id -> Bool) -> (Id -> Id -> Bool) -> Eq Id
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Id -> Id -> Bool
== :: Id -> Id -> Bool
$c/= :: Id -> Id -> Bool
/= :: Id -> Id -> Bool
Eq, Int -> Id -> ShowS
[Id] -> ShowS
Id -> String
(Int -> Id -> ShowS)
-> (Id -> String) -> ([Id] -> ShowS) -> Show Id
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Id -> ShowS
showsPrec :: Int -> Id -> ShowS
$cshow :: Id -> String
show :: Id -> String
$cshowList :: [Id] -> ShowS
showList :: [Id] -> ShowS
Show, Eq Id
Eq Id =>
(Id -> Id -> Ordering)
-> (Id -> Id -> Bool)
-> (Id -> Id -> Bool)
-> (Id -> Id -> Bool)
-> (Id -> Id -> Bool)
-> (Id -> Id -> Id)
-> (Id -> Id -> Id)
-> Ord Id
Id -> Id -> Bool
Id -> Id -> Ordering
Id -> Id -> Id
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
$ccompare :: Id -> Id -> Ordering
compare :: Id -> Id -> Ordering
$c< :: Id -> Id -> Bool
< :: Id -> Id -> Bool
$c<= :: Id -> Id -> Bool
<= :: Id -> Id -> Bool
$c> :: Id -> Id -> Bool
> :: Id -> Id -> Bool
$c>= :: Id -> Id -> Bool
>= :: Id -> Id -> Bool
$cmax :: Id -> Id -> Id
max :: Id -> Id -> Id
$cmin :: Id -> Id -> Id
min :: Id -> Id -> Id
Ord, Eq Id
Eq Id => (Int -> Id -> Int) -> (Id -> Int) -> Hashable Id
Int -> Id -> Int
Id -> Int
forall a. Eq a => (Int -> a -> Int) -> (a -> Int) -> Hashable a
$chashWithSalt :: Int -> Id -> Int
hashWithSalt :: Int -> Id -> Int
$chash :: Id -> Int
hash :: Id -> Int
Hashable, Id -> Text
Id -> ByteString
Id -> Builder
(Id -> Text)
-> (Id -> Builder)
-> (Id -> ByteString)
-> (Id -> Text)
-> (Id -> Builder)
-> ToHttpApiData Id
forall a.
(a -> Text)
-> (a -> Builder)
-> (a -> ByteString)
-> (a -> Text)
-> (a -> Builder)
-> ToHttpApiData a
$ctoUrlPiece :: Id -> Text
toUrlPiece :: Id -> Text
$ctoEncodedUrlPiece :: Id -> Builder
toEncodedUrlPiece :: Id -> Builder
$ctoHeader :: Id -> ByteString
toHeader :: Id -> ByteString
$ctoQueryParam :: Id -> Text
toQueryParam :: Id -> Text
$ctoEncodedQueryParam :: Id -> Builder
toEncodedQueryParam :: Id -> Builder
ToHttpApiData, Text -> Either Text Id
ByteString -> Either Text Id
(Text -> Either Text Id)
-> (ByteString -> Either Text Id)
-> (Text -> Either Text Id)
-> FromHttpApiData Id
forall a.
(Text -> Either Text a)
-> (ByteString -> Either Text a)
-> (Text -> Either Text a)
-> FromHttpApiData a
$cparseUrlPiece :: Text -> Either Text Id
parseUrlPiece :: Text -> Either Text Id
$cparseHeader :: ByteString -> Either Text Id
parseHeader :: ByteString -> Either Text Id
$cparseQueryParam :: Text -> Either Text Id
parseQueryParam :: Text -> Either Text Id
FromHttpApiData)
instance ToJSON Id where
toJSON :: Id -> Value
toJSON = String -> Value
forall a. ToJSON a => a -> Value
toJSON (String -> Value) -> (Id -> String) -> Id -> Value
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> String
forall a. Show a => a -> String
show (Int -> String) -> (Id -> Int) -> Id -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Id -> Int
unId
instance FromJSON Id where
parseJSON :: Value -> Parser Id
parseJSON = Parser Id -> (Int -> Parser Id) -> Maybe Int -> Parser Id
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (String -> Parser Id
forall a. String -> Parser a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"not a number") (Id -> Parser Id
forall a. a -> Parser a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Id -> Parser Id) -> (Int -> Id) -> Int -> Parser Id
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> Id
Id) (Maybe Int -> Parser Id)
-> (String -> Maybe Int) -> String -> Parser Id
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Maybe Int
forall a. Read a => String -> Maybe a
readMaybe (String -> Parser Id)
-> (Value -> Parser String) -> Value -> Parser Id
forall (m :: * -> *) b c a.
Monad m =>
(b -> m c) -> (a -> m b) -> a -> m c
<=< Value -> Parser String
forall a. FromJSON a => Value -> Parser a
parseJSON
type UserStorage = STMMap.Map Id (StoredUser Mock)
type GroupStorage = STMMap.Map Id (StoredGroup Mock)
data TestStorage = TestStorage
{ TestStorage -> UserStorage
userDB :: UserStorage,
TestStorage -> GroupStorage
groupDB :: GroupStorage
}
emptyTestStorage :: IO TestStorage
emptyTestStorage :: IO TestStorage
emptyTestStorage =
UserStorage -> GroupStorage -> TestStorage
Map Id (WithMeta (WithId Id (User Mock)))
-> Map Id (WithMeta (WithId Id Group)) -> TestStorage
TestStorage (Map Id (WithMeta (WithId Id (User Mock)))
-> Map Id (WithMeta (WithId Id Group)) -> TestStorage)
-> IO (Map Id (WithMeta (WithId Id (User Mock))))
-> IO (Map Id (WithMeta (WithId Id Group)) -> TestStorage)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO (Map Id (WithMeta (WithId Id (User Mock))))
forall key value. IO (Map key value)
STMMap.newIO IO (Map Id (WithMeta (WithId Id Group)) -> TestStorage)
-> IO (Map Id (WithMeta (WithId Id Group))) -> IO TestStorage
forall a b. IO (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> IO (Map Id (WithMeta (WithId Id Group)))
forall key value. IO (Map key value)
STMMap.newIO
type TestServer = ReaderT TestStorage Handler
liftSTM :: (MonadIO m) => STM a -> m a
liftSTM :: forall (m :: * -> *) a. MonadIO m => STM a -> m a
liftSTM = IO a -> m a
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO a -> m a) -> (STM a -> IO a) -> STM a -> m a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. STM a -> IO a
forall a. STM a -> IO a
atomically
hoistSTM :: (MFunctor t, MonadIO m) => t STM a -> t m a
hoistSTM :: forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MFunctor t, MonadIO m) =>
t STM a -> t m a
hoistSTM = (forall a. STM a -> m a) -> t STM a -> t m a
forall {k} (t :: (* -> *) -> k -> *) (m :: * -> *) (n :: * -> *)
(b :: k).
(MFunctor t, Monad m) =>
(forall a. m a -> n a) -> t m b -> t n b
forall (m :: * -> *) (n :: * -> *) b.
Monad m =>
(forall a. m a -> n a) -> t m b -> t n b
hoist STM a -> m a
forall a. STM a -> m a
forall (m :: * -> *) a. MonadIO m => STM a -> m a
liftSTM
instance UserTypes Mock where
type UserId Mock = Id
type Mock = NoUserExtra
supportedSchemas :: [Schema]
supportedSchemas = [Schema
User20]
instance UserDB Mock TestServer where
getUsers :: AuthInfo Mock
-> Maybe Filter
-> ScimHandler TestServer (ListResponse (StoredUser Mock))
getUsers () Maybe Filter
mbFilter = do
Map Id (WithMeta (WithId Id (User Mock)))
m <- (TestStorage -> Map Id (WithMeta (WithId Id (User Mock))))
-> ExceptT
ScimError TestServer (Map Id (WithMeta (WithId Id (User Mock))))
forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks TestStorage -> UserStorage
TestStorage -> Map Id (WithMeta (WithId Id (User Mock)))
userDB
[(Id, WithMeta (WithId Id (User Mock)))]
users <- STM [(Id, WithMeta (WithId Id (User Mock)))]
-> ExceptT
ScimError TestServer [(Id, WithMeta (WithId Id (User Mock)))]
forall (m :: * -> *) a. MonadIO m => STM a -> m a
liftSTM (STM [(Id, WithMeta (WithId Id (User Mock)))]
-> ExceptT
ScimError TestServer [(Id, WithMeta (WithId Id (User Mock)))])
-> STM [(Id, WithMeta (WithId Id (User Mock)))]
-> ExceptT
ScimError TestServer [(Id, WithMeta (WithId Id (User Mock)))]
forall a b. (a -> b) -> a -> b
$ ListT STM (Id, WithMeta (WithId Id (User Mock)))
-> STM [(Id, WithMeta (WithId Id (User Mock)))]
forall (m :: * -> *) a. Monad m => ListT m a -> m [a]
ListT.toList (ListT STM (Id, WithMeta (WithId Id (User Mock)))
-> STM [(Id, WithMeta (WithId Id (User Mock)))])
-> ListT STM (Id, WithMeta (WithId Id (User Mock)))
-> STM [(Id, WithMeta (WithId Id (User Mock)))]
forall a b. (a -> b) -> a -> b
$ Map Id (WithMeta (WithId Id (User Mock)))
-> ListT STM (Id, WithMeta (WithId Id (User Mock)))
forall key value. Map key value -> ListT STM (key, value)
STMMap.listT Map Id (WithMeta (WithId Id (User Mock)))
m
let check :: WithMeta (WithId Id (User Mock))
-> ExceptT ScimError TestServer Bool
check WithMeta (WithId Id (User Mock))
user = case Maybe Filter
mbFilter of
Maybe Filter
Nothing -> Bool -> ExceptT ScimError TestServer Bool
forall a. a -> ExceptT ScimError TestServer a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Bool
True
Just Filter
filter_ -> do
let user' :: User Mock
user' = WithId Id (User Mock) -> User Mock
forall id a. WithId id a -> a
value (WithMeta (WithId Id (User Mock)) -> WithId Id (User Mock)
forall a. WithMeta a -> a
thing WithMeta (WithId Id (User Mock))
user)
case Filter -> User Mock -> Either Text Bool
forall extra. Filter -> User extra -> Either Text Bool
filterUser Filter
filter_ User Mock
user' of
Right Bool
res -> Bool -> ExceptT ScimError TestServer Bool
forall a. a -> ExceptT ScimError TestServer a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Bool
res
Left Text
err -> ScimError -> ExceptT ScimError TestServer Bool
forall (m :: * -> *) a. Monad m => ScimError -> ScimHandler m a
throwScim (ScimErrorType -> Maybe Text -> ScimError
badRequest ScimErrorType
InvalidFilter (Text -> Maybe Text
forall a. a -> Maybe a
Just Text
err))
[WithMeta (WithId Id (User Mock))]
-> ListResponse (WithMeta (WithId Id (User Mock)))
forall a. [a] -> ListResponse a
fromList ([WithMeta (WithId Id (User Mock))]
-> ListResponse (WithMeta (WithId Id (User Mock))))
-> ([WithMeta (WithId Id (User Mock))]
-> [WithMeta (WithId Id (User Mock))])
-> [WithMeta (WithId Id (User Mock))]
-> ListResponse (WithMeta (WithId Id (User Mock)))
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (WithMeta (WithId Id (User Mock)) -> Id)
-> [WithMeta (WithId Id (User Mock))]
-> [WithMeta (WithId Id (User Mock))]
forall b a. Ord b => (a -> b) -> [a] -> [a]
sortWith (WithId Id (User Mock) -> Id
forall id a. WithId id a -> id
Common.id (WithId Id (User Mock) -> Id)
-> (WithMeta (WithId Id (User Mock)) -> WithId Id (User Mock))
-> WithMeta (WithId Id (User Mock))
-> Id
forall b c a. (b -> c) -> (a -> b) -> a -> c
. WithMeta (WithId Id (User Mock)) -> WithId Id (User Mock)
forall a. WithMeta a -> a
thing) ([WithMeta (WithId Id (User Mock))]
-> ListResponse (WithMeta (WithId Id (User Mock))))
-> ExceptT ScimError TestServer [WithMeta (WithId Id (User Mock))]
-> ExceptT
ScimError
TestServer
(ListResponse (WithMeta (WithId Id (User Mock))))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (WithMeta (WithId Id (User Mock))
-> ExceptT ScimError TestServer Bool)
-> [WithMeta (WithId Id (User Mock))]
-> ExceptT ScimError TestServer [WithMeta (WithId Id (User Mock))]
forall (m :: * -> *) a.
Applicative m =>
(a -> m Bool) -> [a] -> m [a]
filterM WithMeta (WithId Id (User Mock))
-> ExceptT ScimError TestServer Bool
check ((Id, WithMeta (WithId Id (User Mock)))
-> WithMeta (WithId Id (User Mock))
forall a b. (a, b) -> b
snd ((Id, WithMeta (WithId Id (User Mock)))
-> WithMeta (WithId Id (User Mock)))
-> [(Id, WithMeta (WithId Id (User Mock)))]
-> [WithMeta (WithId Id (User Mock))]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [(Id, WithMeta (WithId Id (User Mock)))]
users)
getUser :: AuthInfo Mock
-> UserId Mock -> ScimHandler TestServer (StoredUser Mock)
getUser () UserId Mock
uid = do
Map Id (WithMeta (WithId Id (User Mock)))
m <- (TestStorage -> Map Id (WithMeta (WithId Id (User Mock))))
-> ExceptT
ScimError TestServer (Map Id (WithMeta (WithId Id (User Mock))))
forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks TestStorage -> UserStorage
TestStorage -> Map Id (WithMeta (WithId Id (User Mock)))
userDB
STM (Maybe (WithMeta (WithId Id (User Mock))))
-> ExceptT
ScimError TestServer (Maybe (WithMeta (WithId Id (User Mock))))
forall (m :: * -> *) a. MonadIO m => STM a -> m a
liftSTM (Id
-> Map Id (WithMeta (WithId Id (User Mock)))
-> STM (Maybe (WithMeta (WithId Id (User Mock))))
forall key value.
Hashable key =>
key -> Map key value -> STM (Maybe value)
STMMap.lookup UserId Mock
Id
uid Map Id (WithMeta (WithId Id (User Mock)))
m) ExceptT
ScimError TestServer (Maybe (WithMeta (WithId Id (User Mock))))
-> (Maybe (WithMeta (WithId Id (User Mock)))
-> ExceptT ScimError TestServer (WithMeta (WithId Id (User Mock))))
-> ExceptT ScimError TestServer (WithMeta (WithId Id (User Mock)))
forall a b.
ExceptT ScimError TestServer a
-> (a -> ExceptT ScimError TestServer b)
-> ExceptT ScimError TestServer b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
Maybe (WithMeta (WithId Id (User Mock)))
Nothing -> ScimError
-> ExceptT ScimError TestServer (WithMeta (WithId Id (User Mock)))
forall (m :: * -> *) a. Monad m => ScimError -> ScimHandler m a
throwScim (Text -> Text -> ScimError
notFound Text
"User" (String -> Text
pack (Id -> String
forall a. Show a => a -> String
show UserId Mock
Id
uid)))
Just WithMeta (WithId Id (User Mock))
x -> WithMeta (WithId Id (User Mock))
-> ExceptT ScimError TestServer (WithMeta (WithId Id (User Mock)))
forall a. a -> ExceptT ScimError TestServer a
forall (f :: * -> *) a. Applicative f => a -> f a
pure WithMeta (WithId Id (User Mock))
x
postUser :: AuthInfo Mock
-> User Mock -> ScimHandler TestServer (StoredUser Mock)
postUser () User Mock
user = do
Map Id (WithMeta (WithId Id (User Mock)))
m <- (TestStorage -> Map Id (WithMeta (WithId Id (User Mock))))
-> ExceptT
ScimError TestServer (Map Id (WithMeta (WithId Id (User Mock))))
forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks TestStorage -> UserStorage
TestStorage -> Map Id (WithMeta (WithId Id (User Mock)))
userDB
Id
uid <- Int -> Id
Id (Int -> Id)
-> ExceptT ScimError TestServer Int
-> ExceptT ScimError TestServer Id
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> STM Int -> ExceptT ScimError TestServer Int
forall (m :: * -> *) a. MonadIO m => STM a -> m a
liftSTM (Map Id (WithMeta (WithId Id (User Mock))) -> STM Int
forall key value. Map key value -> STM Int
STMMap.size Map Id (WithMeta (WithId Id (User Mock)))
m)
let newUser :: WithMeta (WithId Id (User Mock))
newUser = Meta -> WithId Id (User Mock) -> WithMeta (WithId Id (User Mock))
forall a. Meta -> a -> WithMeta a
WithMeta (ResourceType -> Meta
createMeta ResourceType
UserResource) (WithId Id (User Mock) -> WithMeta (WithId Id (User Mock)))
-> WithId Id (User Mock) -> WithMeta (WithId Id (User Mock))
forall a b. (a -> b) -> a -> b
$ Id -> User Mock -> WithId Id (User Mock)
forall id a. id -> a -> WithId id a
WithId Id
uid User Mock
user
STM () -> ScimHandler TestServer ()
forall (m :: * -> *) a. MonadIO m => STM a -> m a
liftSTM (STM () -> ScimHandler TestServer ())
-> STM () -> ScimHandler TestServer ()
forall a b. (a -> b) -> a -> b
$ WithMeta (WithId Id (User Mock))
-> Id -> Map Id (WithMeta (WithId Id (User Mock))) -> STM ()
forall key value.
Hashable key =>
value -> key -> Map key value -> STM ()
STMMap.insert WithMeta (WithId Id (User Mock))
newUser Id
uid Map Id (WithMeta (WithId Id (User Mock)))
m
WithMeta (WithId Id (User Mock))
-> ExceptT ScimError TestServer (WithMeta (WithId Id (User Mock)))
forall a. a -> ExceptT ScimError TestServer a
forall (f :: * -> *) a. Applicative f => a -> f a
pure WithMeta (WithId Id (User Mock))
newUser
putUser :: AuthInfo Mock
-> UserId Mock
-> User Mock
-> ScimHandler TestServer (StoredUser Mock)
putUser () UserId Mock
uid User Mock
user = do
Map Id (WithMeta (WithId Id (User Mock)))
m <- (TestStorage -> Map Id (WithMeta (WithId Id (User Mock))))
-> ExceptT
ScimError TestServer (Map Id (WithMeta (WithId Id (User Mock))))
forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks TestStorage -> UserStorage
TestStorage -> Map Id (WithMeta (WithId Id (User Mock)))
userDB
STM (Maybe (WithMeta (WithId Id (User Mock))))
-> ExceptT
ScimError TestServer (Maybe (WithMeta (WithId Id (User Mock))))
forall (m :: * -> *) a. MonadIO m => STM a -> m a
liftSTM (Id
-> Map Id (WithMeta (WithId Id (User Mock)))
-> STM (Maybe (WithMeta (WithId Id (User Mock))))
forall key value.
Hashable key =>
key -> Map key value -> STM (Maybe value)
STMMap.lookup UserId Mock
Id
uid Map Id (WithMeta (WithId Id (User Mock)))
m) ExceptT
ScimError TestServer (Maybe (WithMeta (WithId Id (User Mock))))
-> (Maybe (WithMeta (WithId Id (User Mock)))
-> ExceptT ScimError TestServer (WithMeta (WithId Id (User Mock))))
-> ExceptT ScimError TestServer (WithMeta (WithId Id (User Mock)))
forall a b.
ExceptT ScimError TestServer a
-> (a -> ExceptT ScimError TestServer b)
-> ExceptT ScimError TestServer b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
Maybe (WithMeta (WithId Id (User Mock)))
Nothing -> ScimError
-> ExceptT ScimError TestServer (WithMeta (WithId Id (User Mock)))
forall (m :: * -> *) a. Monad m => ScimError -> ScimHandler m a
throwScim (Text -> Text -> ScimError
notFound Text
"User" (String -> Text
pack (Id -> String
forall a. Show a => a -> String
show UserId Mock
Id
uid)))
Just WithMeta (WithId Id (User Mock))
stored -> do
let newUser :: WithMeta (WithId Id (User Mock))
newUser = Meta -> WithId Id (User Mock) -> WithMeta (WithId Id (User Mock))
forall a. Meta -> a -> WithMeta a
WithMeta (WithMeta (WithId Id (User Mock)) -> Meta
forall a. WithMeta a -> Meta
meta WithMeta (WithId Id (User Mock))
stored) (WithId Id (User Mock) -> WithMeta (WithId Id (User Mock)))
-> WithId Id (User Mock) -> WithMeta (WithId Id (User Mock))
forall a b. (a -> b) -> a -> b
$ Id -> User Mock -> WithId Id (User Mock)
forall id a. id -> a -> WithId id a
WithId UserId Mock
Id
uid User Mock
user
STM () -> ScimHandler TestServer ()
forall (m :: * -> *) a. MonadIO m => STM a -> m a
liftSTM (STM () -> ScimHandler TestServer ())
-> STM () -> ScimHandler TestServer ()
forall a b. (a -> b) -> a -> b
$ WithMeta (WithId Id (User Mock))
-> Id -> Map Id (WithMeta (WithId Id (User Mock))) -> STM ()
forall key value.
Hashable key =>
value -> key -> Map key value -> STM ()
STMMap.insert WithMeta (WithId Id (User Mock))
newUser UserId Mock
Id
uid Map Id (WithMeta (WithId Id (User Mock)))
m
WithMeta (WithId Id (User Mock))
-> ExceptT ScimError TestServer (WithMeta (WithId Id (User Mock)))
forall a. a -> ExceptT ScimError TestServer a
forall (f :: * -> *) a. Applicative f => a -> f a
pure WithMeta (WithId Id (User Mock))
newUser
deleteUser :: AuthInfo Mock -> UserId Mock -> ScimHandler TestServer ()
deleteUser () UserId Mock
uid = do
Map Id (WithMeta (WithId Id (User Mock)))
m <- (TestStorage -> Map Id (WithMeta (WithId Id (User Mock))))
-> ExceptT
ScimError TestServer (Map Id (WithMeta (WithId Id (User Mock))))
forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks TestStorage -> UserStorage
TestStorage -> Map Id (WithMeta (WithId Id (User Mock)))
userDB
STM (Maybe (WithMeta (WithId Id (User Mock))))
-> ExceptT
ScimError TestServer (Maybe (WithMeta (WithId Id (User Mock))))
forall (m :: * -> *) a. MonadIO m => STM a -> m a
liftSTM (Id
-> Map Id (WithMeta (WithId Id (User Mock)))
-> STM (Maybe (WithMeta (WithId Id (User Mock))))
forall key value.
Hashable key =>
key -> Map key value -> STM (Maybe value)
STMMap.lookup UserId Mock
Id
uid Map Id (WithMeta (WithId Id (User Mock)))
m) ExceptT
ScimError TestServer (Maybe (WithMeta (WithId Id (User Mock))))
-> (Maybe (WithMeta (WithId Id (User Mock)))
-> ScimHandler TestServer ())
-> ScimHandler TestServer ()
forall a b.
ExceptT ScimError TestServer a
-> (a -> ExceptT ScimError TestServer b)
-> ExceptT ScimError TestServer b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
Maybe (WithMeta (WithId Id (User Mock)))
Nothing -> () -> ScimHandler TestServer ()
forall a. a -> ExceptT ScimError TestServer a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
Just WithMeta (WithId Id (User Mock))
_ -> STM () -> ScimHandler TestServer ()
forall (m :: * -> *) a. MonadIO m => STM a -> m a
liftSTM (STM () -> ScimHandler TestServer ())
-> STM () -> ScimHandler TestServer ()
forall a b. (a -> b) -> a -> b
$ Id -> Map Id (WithMeta (WithId Id (User Mock))) -> STM ()
forall key value. Hashable key => key -> Map key value -> STM ()
STMMap.delete UserId Mock
Id
uid Map Id (WithMeta (WithId Id (User Mock)))
m
assertMutability :: User Mock -> StoredUser Mock -> Bool
assertMutability :: User Mock -> StoredUser Mock -> Bool
assertMutability User Mock
_newUser StoredUser Mock
_stored = Bool
True
instance GroupTypes Mock where
type GroupId Mock = Id
instance GroupDB Mock TestServer where
getGroups :: AuthInfo Mock
-> ScimHandler TestServer (ListResponse (StoredGroup Mock))
getGroups () = do
Map Id (WithMeta (WithId Id Group))
m <- (TestStorage -> Map Id (WithMeta (WithId Id Group)))
-> ExceptT
ScimError TestServer (Map Id (WithMeta (WithId Id Group)))
forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks TestStorage -> GroupStorage
TestStorage -> Map Id (WithMeta (WithId Id Group))
groupDB
[(Id, WithMeta (WithId Id Group))]
groups <- STM [(Id, WithMeta (WithId Id Group))]
-> ExceptT ScimError TestServer [(Id, WithMeta (WithId Id Group))]
forall (m :: * -> *) a. MonadIO m => STM a -> m a
liftSTM (STM [(Id, WithMeta (WithId Id Group))]
-> ExceptT ScimError TestServer [(Id, WithMeta (WithId Id Group))])
-> STM [(Id, WithMeta (WithId Id Group))]
-> ExceptT ScimError TestServer [(Id, WithMeta (WithId Id Group))]
forall a b. (a -> b) -> a -> b
$ ListT STM (Id, WithMeta (WithId Id Group))
-> STM [(Id, WithMeta (WithId Id Group))]
forall (m :: * -> *) a. Monad m => ListT m a -> m [a]
ListT.toList (ListT STM (Id, WithMeta (WithId Id Group))
-> STM [(Id, WithMeta (WithId Id Group))])
-> ListT STM (Id, WithMeta (WithId Id Group))
-> STM [(Id, WithMeta (WithId Id Group))]
forall a b. (a -> b) -> a -> b
$ Map Id (WithMeta (WithId Id Group))
-> ListT STM (Id, WithMeta (WithId Id Group))
forall key value. Map key value -> ListT STM (key, value)
STMMap.listT Map Id (WithMeta (WithId Id Group))
m
ListResponse (WithMeta (WithId Id Group))
-> ExceptT
ScimError TestServer (ListResponse (WithMeta (WithId Id Group)))
forall a. a -> ExceptT ScimError TestServer a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (ListResponse (WithMeta (WithId Id Group))
-> ExceptT
ScimError TestServer (ListResponse (WithMeta (WithId Id Group))))
-> ListResponse (WithMeta (WithId Id Group))
-> ExceptT
ScimError TestServer (ListResponse (WithMeta (WithId Id Group)))
forall a b. (a -> b) -> a -> b
$ [WithMeta (WithId Id Group)]
-> ListResponse (WithMeta (WithId Id Group))
forall a. [a] -> ListResponse a
fromList ([WithMeta (WithId Id Group)]
-> ListResponse (WithMeta (WithId Id Group)))
-> ([WithMeta (WithId Id Group)] -> [WithMeta (WithId Id Group)])
-> [WithMeta (WithId Id Group)]
-> ListResponse (WithMeta (WithId Id Group))
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (WithMeta (WithId Id Group) -> Id)
-> [WithMeta (WithId Id Group)] -> [WithMeta (WithId Id Group)]
forall b a. Ord b => (a -> b) -> [a] -> [a]
sortWith (WithId Id Group -> Id
forall id a. WithId id a -> id
Common.id (WithId Id Group -> Id)
-> (WithMeta (WithId Id Group) -> WithId Id Group)
-> WithMeta (WithId Id Group)
-> Id
forall b c a. (b -> c) -> (a -> b) -> a -> c
. WithMeta (WithId Id Group) -> WithId Id Group
forall a. WithMeta a -> a
thing) ([WithMeta (WithId Id Group)]
-> ListResponse (WithMeta (WithId Id Group)))
-> [WithMeta (WithId Id Group)]
-> ListResponse (WithMeta (WithId Id Group))
forall a b. (a -> b) -> a -> b
$ (Id, WithMeta (WithId Id Group)) -> WithMeta (WithId Id Group)
forall a b. (a, b) -> b
snd ((Id, WithMeta (WithId Id Group)) -> WithMeta (WithId Id Group))
-> [(Id, WithMeta (WithId Id Group))]
-> [WithMeta (WithId Id Group)]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [(Id, WithMeta (WithId Id Group))]
groups
getGroup :: AuthInfo Mock
-> GroupId Mock -> ScimHandler TestServer (StoredGroup Mock)
getGroup () GroupId Mock
gid = do
Map Id (WithMeta (WithId Id Group))
m <- (TestStorage -> Map Id (WithMeta (WithId Id Group)))
-> ExceptT
ScimError TestServer (Map Id (WithMeta (WithId Id Group)))
forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks TestStorage -> GroupStorage
TestStorage -> Map Id (WithMeta (WithId Id Group))
groupDB
STM (Maybe (WithMeta (WithId Id Group)))
-> ExceptT
ScimError TestServer (Maybe (WithMeta (WithId Id Group)))
forall (m :: * -> *) a. MonadIO m => STM a -> m a
liftSTM (Id
-> Map Id (WithMeta (WithId Id Group))
-> STM (Maybe (WithMeta (WithId Id Group)))
forall key value.
Hashable key =>
key -> Map key value -> STM (Maybe value)
STMMap.lookup GroupId Mock
Id
gid Map Id (WithMeta (WithId Id Group))
m) ExceptT ScimError TestServer (Maybe (WithMeta (WithId Id Group)))
-> (Maybe (WithMeta (WithId Id Group))
-> ExceptT ScimError TestServer (WithMeta (WithId Id Group)))
-> ExceptT ScimError TestServer (WithMeta (WithId Id Group))
forall a b.
ExceptT ScimError TestServer a
-> (a -> ExceptT ScimError TestServer b)
-> ExceptT ScimError TestServer b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
Maybe (WithMeta (WithId Id Group))
Nothing -> ScimError
-> ExceptT ScimError TestServer (WithMeta (WithId Id Group))
forall (m :: * -> *) a. Monad m => ScimError -> ScimHandler m a
throwScim (Text -> Text -> ScimError
notFound Text
"Group" (String -> Text
pack (Id -> String
forall a. Show a => a -> String
show GroupId Mock
Id
gid)))
Just WithMeta (WithId Id Group)
grp -> WithMeta (WithId Id Group)
-> ExceptT ScimError TestServer (WithMeta (WithId Id Group))
forall a. a -> ExceptT ScimError TestServer a
forall (f :: * -> *) a. Applicative f => a -> f a
pure WithMeta (WithId Id Group)
grp
postGroup :: AuthInfo Mock -> Group -> ScimHandler TestServer (StoredGroup Mock)
postGroup () Group
grp = do
Map Id (WithMeta (WithId Id Group))
m <- (TestStorage -> Map Id (WithMeta (WithId Id Group)))
-> ExceptT
ScimError TestServer (Map Id (WithMeta (WithId Id Group)))
forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks TestStorage -> GroupStorage
TestStorage -> Map Id (WithMeta (WithId Id Group))
groupDB
Id
gid <- Int -> Id
Id (Int -> Id)
-> ExceptT ScimError TestServer Int
-> ExceptT ScimError TestServer Id
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> STM Int -> ExceptT ScimError TestServer Int
forall (m :: * -> *) a. MonadIO m => STM a -> m a
liftSTM (Map Id (WithMeta (WithId Id Group)) -> STM Int
forall key value. Map key value -> STM Int
STMMap.size Map Id (WithMeta (WithId Id Group))
m)
let newGroup :: WithMeta (WithId Id Group)
newGroup = Meta -> WithId Id Group -> WithMeta (WithId Id Group)
forall a. Meta -> a -> WithMeta a
WithMeta (ResourceType -> Meta
createMeta ResourceType
GroupResource) (WithId Id Group -> WithMeta (WithId Id Group))
-> WithId Id Group -> WithMeta (WithId Id Group)
forall a b. (a -> b) -> a -> b
$ Id -> Group -> WithId Id Group
forall id a. id -> a -> WithId id a
WithId Id
gid Group
grp
STM () -> ScimHandler TestServer ()
forall (m :: * -> *) a. MonadIO m => STM a -> m a
liftSTM (STM () -> ScimHandler TestServer ())
-> STM () -> ScimHandler TestServer ()
forall a b. (a -> b) -> a -> b
$ WithMeta (WithId Id Group)
-> Id -> Map Id (WithMeta (WithId Id Group)) -> STM ()
forall key value.
Hashable key =>
value -> key -> Map key value -> STM ()
STMMap.insert WithMeta (WithId Id Group)
newGroup Id
gid Map Id (WithMeta (WithId Id Group))
m
WithMeta (WithId Id Group)
-> ExceptT ScimError TestServer (WithMeta (WithId Id Group))
forall a. a -> ExceptT ScimError TestServer a
forall (f :: * -> *) a. Applicative f => a -> f a
pure WithMeta (WithId Id Group)
newGroup
putGroup :: AuthInfo Mock
-> GroupId Mock
-> Group
-> ScimHandler TestServer (StoredGroup Mock)
putGroup () GroupId Mock
gid Group
grp = do
Map Id (WithMeta (WithId Id Group))
m <- (TestStorage -> Map Id (WithMeta (WithId Id Group)))
-> ExceptT
ScimError TestServer (Map Id (WithMeta (WithId Id Group)))
forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks TestStorage -> GroupStorage
TestStorage -> Map Id (WithMeta (WithId Id Group))
groupDB
STM (Maybe (WithMeta (WithId Id Group)))
-> ExceptT
ScimError TestServer (Maybe (WithMeta (WithId Id Group)))
forall (m :: * -> *) a. MonadIO m => STM a -> m a
liftSTM (Id
-> Map Id (WithMeta (WithId Id Group))
-> STM (Maybe (WithMeta (WithId Id Group)))
forall key value.
Hashable key =>
key -> Map key value -> STM (Maybe value)
STMMap.lookup GroupId Mock
Id
gid Map Id (WithMeta (WithId Id Group))
m) ExceptT ScimError TestServer (Maybe (WithMeta (WithId Id Group)))
-> (Maybe (WithMeta (WithId Id Group))
-> ExceptT ScimError TestServer (WithMeta (WithId Id Group)))
-> ExceptT ScimError TestServer (WithMeta (WithId Id Group))
forall a b.
ExceptT ScimError TestServer a
-> (a -> ExceptT ScimError TestServer b)
-> ExceptT ScimError TestServer b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
Maybe (WithMeta (WithId Id Group))
Nothing -> ScimError
-> ExceptT ScimError TestServer (WithMeta (WithId Id Group))
forall (m :: * -> *) a. Monad m => ScimError -> ScimHandler m a
throwScim (Text -> Text -> ScimError
notFound Text
"Group" (String -> Text
pack (Id -> String
forall a. Show a => a -> String
show GroupId Mock
Id
gid)))
Just WithMeta (WithId Id Group)
stored -> do
let newGroup :: WithMeta (WithId Id Group)
newGroup = Meta -> WithId Id Group -> WithMeta (WithId Id Group)
forall a. Meta -> a -> WithMeta a
WithMeta (WithMeta (WithId Id Group) -> Meta
forall a. WithMeta a -> Meta
meta WithMeta (WithId Id Group)
stored) (WithId Id Group -> WithMeta (WithId Id Group))
-> WithId Id Group -> WithMeta (WithId Id Group)
forall a b. (a -> b) -> a -> b
$ Id -> Group -> WithId Id Group
forall id a. id -> a -> WithId id a
WithId GroupId Mock
Id
gid Group
grp
STM () -> ScimHandler TestServer ()
forall (m :: * -> *) a. MonadIO m => STM a -> m a
liftSTM (STM () -> ScimHandler TestServer ())
-> STM () -> ScimHandler TestServer ()
forall a b. (a -> b) -> a -> b
$ WithMeta (WithId Id Group)
-> Id -> Map Id (WithMeta (WithId Id Group)) -> STM ()
forall key value.
Hashable key =>
value -> key -> Map key value -> STM ()
STMMap.insert WithMeta (WithId Id Group)
newGroup GroupId Mock
Id
gid Map Id (WithMeta (WithId Id Group))
m
WithMeta (WithId Id Group)
-> ExceptT ScimError TestServer (WithMeta (WithId Id Group))
forall a. a -> ExceptT ScimError TestServer a
forall (f :: * -> *) a. Applicative f => a -> f a
pure WithMeta (WithId Id Group)
newGroup
patchGroup :: AuthInfo Mock
-> GroupId Mock
-> Value
-> ScimHandler TestServer (StoredGroup Mock)
patchGroup AuthInfo Mock
_ GroupId Mock
_ Value
_ = ScimError
-> ExceptT ScimError TestServer (WithMeta (WithId Id Group))
forall (m :: * -> *) a. Monad m => ScimError -> ScimHandler m a
throwScim (Text -> ScimError
serverError Text
"PATCH /Users not implemented")
deleteGroup :: AuthInfo Mock -> GroupId Mock -> ScimHandler TestServer ()
deleteGroup () GroupId Mock
gid = do
Map Id (WithMeta (WithId Id Group))
m <- (TestStorage -> Map Id (WithMeta (WithId Id Group)))
-> ExceptT
ScimError TestServer (Map Id (WithMeta (WithId Id Group)))
forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks TestStorage -> GroupStorage
TestStorage -> Map Id (WithMeta (WithId Id Group))
groupDB
STM (Maybe (WithMeta (WithId Id Group)))
-> ExceptT
ScimError TestServer (Maybe (WithMeta (WithId Id Group)))
forall (m :: * -> *) a. MonadIO m => STM a -> m a
liftSTM (Id
-> Map Id (WithMeta (WithId Id Group))
-> STM (Maybe (WithMeta (WithId Id Group)))
forall key value.
Hashable key =>
key -> Map key value -> STM (Maybe value)
STMMap.lookup GroupId Mock
Id
gid Map Id (WithMeta (WithId Id Group))
m) ExceptT ScimError TestServer (Maybe (WithMeta (WithId Id Group)))
-> (Maybe (WithMeta (WithId Id Group))
-> ScimHandler TestServer ())
-> ScimHandler TestServer ()
forall a b.
ExceptT ScimError TestServer a
-> (a -> ExceptT ScimError TestServer b)
-> ExceptT ScimError TestServer b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
Maybe (WithMeta (WithId Id Group))
Nothing -> ScimError -> ScimHandler TestServer ()
forall (m :: * -> *) a. Monad m => ScimError -> ScimHandler m a
throwScim (Text -> Text -> ScimError
notFound Text
"Group" (String -> Text
pack (Id -> String
forall a. Show a => a -> String
show GroupId Mock
Id
gid)))
Just WithMeta (WithId Id Group)
_ -> STM () -> ScimHandler TestServer ()
forall (m :: * -> *) a. MonadIO m => STM a -> m a
liftSTM (STM () -> ScimHandler TestServer ())
-> STM () -> ScimHandler TestServer ()
forall a b. (a -> b) -> a -> b
$ Id -> Map Id (WithMeta (WithId Id Group)) -> STM ()
forall key value. Hashable key => key -> Map key value -> STM ()
STMMap.delete GroupId Mock
Id
gid Map Id (WithMeta (WithId Id Group))
m
instance AuthTypes Mock where
type AuthData Mock = Text
type AuthInfo Mock = ()
instance AuthDB Mock TestServer where
authCheck :: Maybe (AuthData Mock) -> ScimHandler TestServer (AuthInfo Mock)
authCheck = \case
Just AuthData Mock
"authorized" -> () -> ScimHandler TestServer ()
forall a. a -> ExceptT ScimError TestServer a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
Maybe (AuthData Mock)
_ -> ScimError -> ScimHandler TestServer ()
forall (m :: * -> *) a. Monad m => ScimError -> ScimHandler m a
throwScim (Text -> ScimError
unauthorized Text
"expected 'authorized'")
testDate :: UTCTime
testDate :: UTCTime
testDate =
UTCTime
{ utctDay :: Day
utctDay = Integer -> Day
ModifiedJulianDay Integer
58119,
utctDayTime :: DiffTime
utctDayTime = DiffTime
0
}
createMeta :: ResourceType -> Meta
createMeta :: ResourceType -> Meta
createMeta ResourceType
rType =
Meta
{ resourceType :: ResourceType
resourceType = ResourceType
rType,
created :: UTCTime
created = UTCTime
testDate,
lastModified :: UTCTime
lastModified = UTCTime
testDate,
version :: ETag
version = Text -> ETag
Weak Text
"testVersion",
location :: URI
location =
URI -> URI
Common.URI (URI -> URI) -> URI -> URI
forall a b. (a -> b) -> a -> b
$
String -> Maybe URIAuth -> String -> String -> String -> URI
URI String
"https:" (URIAuth -> Maybe URIAuth
forall a. a -> Maybe a
Just (URIAuth -> Maybe URIAuth) -> URIAuth -> Maybe URIAuth
forall a b. (a -> b) -> a -> b
$ String -> String -> String -> URIAuth
URI.URIAuth String
"" String
"example.com" String
"") String
"/Users/id" String
"" String
""
}
nt :: TestStorage -> ScimHandler TestServer a -> Handler a
nt :: forall a. TestStorage -> ScimHandler TestServer a -> Handler a
nt TestStorage
storage =
(ReaderT TestStorage Handler a -> TestStorage -> Handler a)
-> TestStorage -> ReaderT TestStorage Handler a -> Handler a
forall a b c. (a -> b -> c) -> b -> a -> c
flip ReaderT TestStorage Handler a -> TestStorage -> Handler a
forall r (m :: * -> *) a. ReaderT r m a -> r -> m a
runReaderT TestStorage
storage
(ReaderT TestStorage Handler a -> Handler a)
-> (ScimHandler TestServer a -> ReaderT TestStorage Handler a)
-> ScimHandler TestServer a
-> Handler a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (forall a. ScimError -> TestServer a)
-> forall a. ScimHandler TestServer a -> TestServer a
forall (m :: * -> *).
Monad m =>
(forall a. ScimError -> m a) -> forall a. ScimHandler m a -> m a
fromScimHandler (Handler a -> ReaderT TestStorage Handler a
forall (m :: * -> *) a. Monad m => m a -> ReaderT TestStorage m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (Handler a -> ReaderT TestStorage Handler a)
-> (ScimError -> Handler a)
-> ScimError
-> ReaderT TestStorage Handler a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ServerError -> Handler a
forall a. ServerError -> Handler a
forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError (ServerError -> Handler a)
-> (ScimError -> ServerError) -> ScimError -> Handler a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ScimError -> ServerError
scimToServerError)
filterUser :: Filter -> User extra -> Either Text Bool
filterUser :: forall extra. Filter -> User extra -> Either Text Bool
filterUser (FilterAttrCompare (AttrPath Maybe Schema
schema' AttrName
attrib Maybe SubAttr
subAttr) CompareOp
op CompValue
val) User extra
user
| Maybe Schema -> Bool
isUserSchema Maybe Schema
schema' =
case (Maybe SubAttr
subAttr, CompValue
val) of
(Maybe SubAttr
Nothing, ValString Text
str)
| AttrName
attrib AttrName -> AttrName -> Bool
forall a. Eq a => a -> a -> Bool
== AttrName
"userName" ->
Bool -> Either Text Bool
forall a b. b -> Either a b
Right (CompareOp -> Text -> Text -> Bool
compareStr CompareOp
op (Text -> Text
forall s. FoldCase s => s -> s
CI.foldCase (User extra -> Text
forall tag. User tag -> Text
userName User extra
user)) (Text -> Text
forall s. FoldCase s => s -> s
CI.foldCase Text
str))
(Maybe SubAttr
Nothing, CompValue
_)
| AttrName
attrib AttrName -> AttrName -> Bool
forall a. Eq a => a -> a -> Bool
== AttrName
"userName" ->
Text -> Either Text Bool
forall a b. a -> Either a b
Left Text
"usernames can only be compared with strings"
(Maybe SubAttr
_, CompValue
_) ->
Text -> Either Text Bool
forall a b. a -> Either a b
Left Text
"Only search on usernames is currently supported"
| Bool
otherwise = Text -> Either Text Bool
forall a b. a -> Either a b
Left Text
"Invalid schema. Only user schema is supported"