yam 0.6.0 → 0.6.1
raw patch · 13 files changed
+347/−67 lines, 13 filesdep +exceptionsdep +http-clientdep +servant-clientdep ~salak
Dependencies added: exceptions, http-client, servant-client, transformers
Dependency ranges changed: salak
Files
- README.md +1/−7
- src/Yam.hs +64/−53
- src/Yam/App.hs +3/−0
- src/Yam/Logger.hs +5/−3
- src/Yam/Middleware.hs +34/−0
- src/Yam/Middleware/Client.hs +61/−0
- src/Yam/Middleware/Error.hs +3/−1
- src/Yam/Prelude.hs +35/−0
- src/Yam/Server.hs +38/−0
- src/Yam/Server/Health.hs +36/−0
- src/Yam/Server/Refresh.hs +21/−0
- src/Yam/Swagger.hs +25/−0
- yam.cabal +21/−3
README.md view
@@ -15,11 +15,9 @@ ```Haskell -import Salak import Salak.Yaml import Servant import Yam-import qualified Control.Category as C import Data.Version type API = "hello" :> Get '[PlainText] Text@@ -27,10 +25,6 @@ service :: ServerT API AppSimple service = return "world" -main = runSalakWith "app" YAML $ do- al <- require "yam.application"- sw <- require "yam.swagger"- lc <- requireD "yam.logging"- start al sw (makeVersion []) lc spanNoNotifier emptyAM serveWarp (Proxy @API) service+main = start "app" YAML (makeVersion []) (return emptyAM) (Proxy @API) (return service) ```
src/Yam.hs view
@@ -17,6 +17,7 @@ -- * Yam Server start+ , start' , serveWarp -- ** Application Configuration , AppConfig(..)@@ -36,6 +37,10 @@ , simpleConfig , simpleConfig' , simpleMiddleware+ -- *** Health+ , HealthStatus(..)+ , HealthResult(..)+ , mergeHealth -- * Modules -- ** Logger , LogConfig(..)@@ -52,6 +57,7 @@ , SwaggerConfig(..) , serveWithContextAndSwagger , baseInfo+ , SwaggerTag -- * Reexport , spanNoNotifier , Span(..)@@ -67,11 +73,18 @@ , liftIO , fromMaybe , throw+ , logInfo+ , logError+ , logWarn+ , logDebug+ , RunSalak(..) ) where import qualified Control.Category as C+import Control.Monad.IO.Unlift import Control.Monad.Logger.CallStack import Data.Opentracing+import Data.Text (pack) import Network.Wai import Salak import Servant@@ -79,66 +92,70 @@ import Yam.App import Yam.Config import Yam.Logger+import Yam.Middleware import Yam.Middleware.Error import Yam.Middleware.Trace import Yam.Prelude+import Yam.Server+import Yam.Server.Health import Yam.Swagger --- | Application Middleware.-newtype AppMiddleware a b = AppMiddleware- { runAM :: Context a -> Middleware -> (Context b -> Middleware -> LoggingT IO ()) -> LoggingT IO () }--instance C.Category AppMiddleware where- id = AppMiddleware $ \a m f -> f a m- (AppMiddleware fbc) . (AppMiddleware fab) = AppMiddleware $ \a m f -> fab a m $ \b m1 -> fbc b m1 f---- | Simple Application Middleware, just provide a config to context.-simpleContext :: a -> AppMiddleware cxt (a ': cxt)-simpleContext a = AppMiddleware $ \c m f -> f (a :. c) m---- | Simple Application Middleware, just provide a config to context.-simpleConfig' :: (HasSalak cxt, FromProp a) => Text -> (a -> AppT cxt (LoggingT IO) b) -> AppMiddleware cxt (b ': cxt)-simpleConfig' key g = AppMiddleware $ \c m f -> runAppT c (require key) >>= \a -> runAppT c (g a) >>= \b -> f (b :. c) m---- | Simple Application Middleware, just provide a config to context.-simpleConfig :: (HasSalak cxt, FromProp a) => Text -> AppMiddleware cxt (a ': cxt)-simpleConfig key = simpleConfig' key return---- | Simple Application Middleware, promote a 'Middleware' to 'AppMiddleware'-simpleMiddleware :: Middleware -> AppMiddleware cxt cxt-simpleMiddleware m = AppMiddleware $ \c m2 f -> f c (m . m2)- -- | Standard Starter of Yam. start- :: forall api cxt- . ( HasServer api cxt+ :: forall file api cxt+ . ( HasLoad file+ , HasLogger cxt+ , HasServer api cxt , HasSwagger api)- => AppConfig -- ^ Application Config- -> SwaggerConfig -- ^ SwaggerConfig- -> Version -- ^ Application Version- -> IO LogConfig -- ^ Logger Config- -> (Span -> AppV cxt IO ()) -- ^ Opentracing notifier- -> AppMiddleware Simple cxt -- ^ Application Middleware- -> (AppConfig -> Application -> IO ()) -- ^ Run Application+ => String -- ^ File config name+ -> file -- ^ Config file format+ -> Version -- ^ Version+ -> RunSalak (AppMiddleware Simple cxt) -- ^ Application Middleware -> Proxy api -- ^ Application API Proxy- -> ServerT api (AppV cxt IO) -- ^ Application API Server+ -> RunSalak (ServerT api (AppV cxt IO)) -- ^ Application API Server -> IO ()-start ac@AppConfig{..} sw@SwaggerConfig{..} vs logConfig f am runHttp p api =- withLogger name logConfig $ \logger -> do- logInfo $ "Start Service [" <> name <> "] ..."+start cfg file = start' (loadSalakFile cfg file)++-- | Standard Starter of Yam.+start'+ :: forall api cxt+ .(HasLogger cxt+ , HasServer api cxt+ , HasSwagger api)+ => LoadSalakT IO ()+ -> Version+ -> RunSalak (AppMiddleware Simple cxt)+ -> Proxy api+ -> RunSalak (ServerT api (AppV cxt IO))+ -> IO ()+start' load ver mkAMD pSer mkSer = loadAndRunSalak load $ do+ c <- requireD "logging"+ app@AppConfig{..} <- require "application"+ sw@SwaggerConfig{..} <- require "swagger"+ withLogger name c $ \logger -> unSalak $ do let portText = showText port- baseCxt = LF logger :. EmptyContext- runAM am baseCxt id $ \cxt middleware -> do+ baseCxt = (LF logger :. EmptyContext)+ logInfo $ "Start Service [" <> name <> "] ..."+ amd <- mkAMD+ ser <- mkSer+ f <- askUnliftIO+ liftX $ runAM amd baseCxt id emptyHealth $ \cxt middleware hr -> lift $ unliftIO f $ do+ (en, aep) <- actuatorEndpoint hr+ readLogs >>= mapM_ (logInfo . ("Parsing " <>)) when enabled $ logInfo $ "Swagger enabled: http://localhost:" <> portText <> "/" <> pack urlDir logInfo $ "Servant started on port(s): " <> portText- liftIO- $ runHttp ac- $ traceMiddleware (\v -> runAppT (VH v :. cxt) . f)- $ middleware- $ errorMiddleware baseCxt- $ serveWithContextAndSwagger sw (baseInfo hostname name vs port) (Proxy @(Vault :> api)) cxt- $ \v -> hoistServerWithContext p (Proxy @cxt) (nt cxt v) api+ let go :: forall a. (HasSwagger a, HasServer a cxt) => Proxy a -> ServerT a (AppV cxt IO) -> RunSalak ()+ go x y = liftIO+ $ serveWarp app+ $ traceMiddleware (\v -> runAppT (VH v :. cxt) . spanNoNotifier)+ $ middleware+ $ errorMiddleware baseCxt+ $ serveWithContextAndSwagger sw (baseInfo hostname name ver port) (Proxy @(Vault :> a)) cxt+ $ \v -> hoistServerWithContext x (Proxy @cxt) (nt cxt v) y+ if en+ then go (Proxy @(api :<|> ActuatorEndpoint)) (ser :<|> aep)+ else go pSer ser -- | default http server by warp. serveWarp :: AppConfig -> Application -> IO ()@@ -166,11 +183,9 @@ -- $use ----- > import Salak -- > import Salak.Yaml -- > import Servant -- > import Yam--- > import qualified Control.Category as C -- > import Data.Version -- > -- > type API = "hello" :> Get '[PlainText] Text@@ -178,9 +193,5 @@ -- > service :: ServerT API AppSimple -- > service = return "world" -- >--- > main = runSalakWith "app" YAML $ do--- > al <- require "yam.application"--- > sw <- require "yam.swagger"--- > lc <- requireD "yam.logging"--- > start al sw (makeVersion []) lc spanNoNotifier emptyAM serveWarp (Proxy @API) service+-- > main = start "app" YAML (makeVersion []) (return emptyAM) (Proxy @API) (return service)
src/Yam/App.hs view
@@ -19,6 +19,9 @@ -- | Application with 'Vault' type AppV cxt = AppT (VaultHolder : cxt) +-- | Application with 'SourcePack'+type AppS cxt = AppV (SourcePack : cxt)+ instance MonadTrans (AppT cxt) where lift = AppT . lift
src/Yam/Logger.hs view
@@ -9,7 +9,7 @@ , LogFuncHolder(..) , VaultHolder(..) ) where-+import Control.Monad.IO.Unlift import Control.Monad.Logger.CallStack import qualified Data.Vault.Lazy as L import Data.Word@@ -70,8 +70,10 @@ let locate = if ll /= LevelError then "" else " @" <> toLogStr loc_filename <> toLogStr (show loc_start) in toLogStr t <> " " <> toStr ll <> xn <> toLogStr loc_module <> locate <> " - " <> s <> "\n" -withLogger :: Text -> IO LogConfig -> (LogFunc -> LoggingT IO a) -> IO a-withLogger n lc action = bracket (newLogger n lc) snd $ \(f,_) -> runLoggingT (askLoggerIO >>= action) f+withLogger :: (MonadUnliftIO m) => Text -> IO LogConfig -> (LogFunc -> LoggingT m a) -> m a+withLogger n lc action = do+ f <- askRunInIO+ liftIO $ bracket (newLogger n lc) (snd) $ f . runLoggingT (askLoggerIO >>= action) . fst addTrace :: LogFunc -> Text -> LogFunc addTrace f tid a b c d = let p = "[" <> toLogStr tid <> "] " in f a b c (p <> d)
+ src/Yam/Middleware.hs view
@@ -0,0 +1,34 @@+module Yam.Middleware where++import qualified Control.Category as C+import Control.Monad.Logger.CallStack+import Data.Text (Text)+import Network.Wai+import Salak+import Servant+import Yam.App+import Yam.Server.Health++-- | Application Middleware.+newtype AppMiddleware a b = AppMiddleware+ { runAM :: Context a -> Middleware -> IO HealthResult -> (Context b -> Middleware -> IO HealthResult -> LoggingT IO ()) -> LoggingT IO () }++instance C.Category AppMiddleware where+ id = AppMiddleware $ \a m h f -> f a m h+ (AppMiddleware fbc) . (AppMiddleware fab) = AppMiddleware $ \a m h f -> fab a m h $ \b m1 h1 -> fbc b m1 h1 f++-- | Simple Application Middleware, just provide a config to context.+simpleContext :: a -> AppMiddleware cxt (a ': cxt)+simpleContext a = AppMiddleware $ \c m h f -> f (a :. c) m h++-- | Simple Application Middleware, just provide a config to context.+simpleConfig' :: (HasSalak cxt, FromProp a) => Text -> (a -> AppT cxt (LoggingT IO) b) -> AppMiddleware cxt (b ': cxt)+simpleConfig' key g = AppMiddleware $ \c m h f -> runAppT c (require key) >>= \a -> runAppT c (g a) >>= \b -> f (b :. c) m h++-- | Simple Application Middleware, just provide a config to context.+simpleConfig :: (HasSalak cxt, FromProp a) => Text -> AppMiddleware cxt (a ': cxt)+simpleConfig key = simpleConfig' key return++-- | Simple Application Middleware, promote a 'Middleware' to 'AppMiddleware'+simpleMiddleware :: Middleware -> AppMiddleware cxt cxt+simpleMiddleware m = AppMiddleware $ \c m2 h f -> f c (m . m2) h
+ src/Yam/Middleware/Client.hs view
@@ -0,0 +1,61 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}+{-# LANGUAGE AllowAmbiguousTypes #-}+module Yam.Middleware.Client(+ HttpClient+ , HasHttpClient+ , runClient+ , hoistC+ , clientMiddleware+ , clientMiddleware'+ ) where++import Data.Default+import Data.Proxy+import Network.HTTP.Client hiding (Proxy)+import Salak+import Servant+import Servant.Client+import Yam.App+import Yam.Logger+import Yam.Middleware+import Yam.Prelude++instance Default ManagerSettings where+ def = defaultManagerSettings++instance FromProp ResponseTimeout where+ fromProp = responseTimeoutMicro <$> fromProp++instance FromProp ManagerSettings where+ fromProp = do+ connCount <- "max-conns" .?: managerConnCount+ timeout <- "timeout" .?: managerResponseTimeout+ idleCount <- "idle-conns" .?: managerIdleConnectionCount+ return def+ { managerConnCount = connCount+ , managerResponseTimeout = timeout+ , managerIdleConnectionCount = idleCount+ }++data HttpClient = HttpClient Manager++type HasHttpClient cxt = (HasLogger cxt, HasContextEntry cxt HttpClient)++runClient :: HasHttpClient cxt => Proxy cxt -> BaseUrl -> ClientM a -> AppT cxt IO a+runClient _ url cma = do+ HttpClient m <- getEntry+ v <- liftIO $ runClientM cma (mkClientEnv m url)+ case v of+ Left e -> throwS err400 $ showText e+ Right r -> return r++hoistC :: forall cxt api. (HasHttpClient cxt, HasClient ClientM api) => Proxy cxt -> Proxy api -> BaseUrl -> Client (AppT cxt IO) api+hoistC pc p url = hoistClient p (runClient pc url) (client p)++clientMiddleware :: RunSalak (AppMiddleware cxt (HttpClient : cxt))+clientMiddleware = clientMiddleware' id++clientMiddleware' :: (ManagerSettings -> ManagerSettings) -> RunSalak (AppMiddleware cxt (HttpClient : cxt))+clientMiddleware' f = do+ ms <- require "client" >>= liftIO . newManager . f+ return $ simpleContext $ HttpClient ms
src/Yam/Middleware/Error.hs view
@@ -1,6 +1,8 @@ {-# OPTIONS_GHC -fno-warn-orphans #-}+{-# LANGUAGE ImplicitParams #-} module Yam.Middleware.Error where +import Control.Monad.Logger.CallStack import Network.Wai import Servant import Yam.App@@ -10,4 +12,4 @@ errorMiddleware :: HasLogger cxt => Context cxt -> Middleware errorMiddleware cxt app req resH = app req resH `catch` (\e -> go e >> resH (whenException e)) where- go = runVault cxt (vault req) . logError . showText+ go = runVault cxt (vault req) . logErrorCS ?callStack . showText
src/Yam/Prelude.hs view
@@ -23,6 +23,7 @@ , try , catch , when+ , lift , (<>) , LogLevel(..) , logInfo@@ -40,6 +41,9 @@ , fromJust , Version , Middleware+ , RunSalak(..)+ , liftSalak+ , liftX ) where import Control.Exception hiding (Handler)@@ -63,10 +67,41 @@ import Data.Word import GHC.Stack import Network.Wai+import Salak import Servant import Servant.Server.Internal.ServerError import System.IO.Unsafe (unsafePerformIO) import System.Random.MWC++newtype RunSalak a = RunSalak { unSalak :: LoggingT (RunSalakT IO) a } deriving (Functor, Applicative, Monad)++instance HasSourcePack RunSalak where+ askSourcePack = liftSalak askSourcePack+ logSP = liftSalak . logSP+ readLogs = liftSalak readLogs++instance MonadIO RunSalak where+ liftIO = RunSalak . liftIO++instance MonadLogger RunSalak where+ monadLoggerLog a b c d = RunSalak (monadLoggerLog a b c d)++instance MonadLoggerIO RunSalak where+ askLoggerIO = RunSalak askLoggerIO++instance MonadUnliftIO RunSalak where+ askUnliftIO = RunSalak $ do+ f <- lift askUnliftIO+ lf <- askLoggerIO+ return $ UnliftIO $ \ma -> unliftIO f (runLoggingT (unSalak ma) lf)++liftSalak :: RunSalakT IO a -> RunSalak a+liftSalak = RunSalak . lift++liftX :: LoggingT IO a -> RunSalak a+liftX f = do+ lf <- askLoggerIO+ liftIO $ runLoggingT f lf type LogFunc = Loc -> LogSource -> LogLevel -> LogStr -> IO ()
+ src/Yam/Server.hs view
@@ -0,0 +1,38 @@+module Yam.Server(+ actuatorEndpoint+ , ActuatorEndpoint+ ) where++import Salak+import Servant+import Yam.App+import Yam.Logger+import Yam.Prelude+import Yam.Server.Health+import Yam.Server.Refresh+import Yam.Swagger++data ActuatorConfig = ActuatorConfig+ { enabled :: Bool+ , refresh :: Bool+ , health :: Bool+ }++instance FromProp ActuatorConfig where+ fromProp = ActuatorConfig+ <$> "enabled" .?= False+ <*> "refresh.enabled" .?= True+ <*> "health.enabled" .?= True++type ActuatorEndpoint = SwaggerTag "actuator" "Actuator API" :> "actuator" :>+ ( RefreshEndpoint+ :<|> HealthEndpoint+ )++endpoint :: HasLogger cxt => ActuatorConfig -> IO HealthResult -> IO ReloadResult -> ServerT ActuatorEndpoint (AppV cxt IO)+endpoint ActuatorConfig{..} hr rr = refreshEndpoint rr refresh :<|> healthEndpoint hr health++actuatorEndpoint :: HasLogger cxt => IO HealthResult -> RunSalak (Bool, ServerT ActuatorEndpoint (AppV cxt IO))+actuatorEndpoint hr = do+ ac@ActuatorConfig{..} <- require "actuator"+ (enabled,) <$> liftSalak (exec $ return . endpoint ac hr)
+ src/Yam/Server/Health.hs view
@@ -0,0 +1,36 @@+{-# LANGUAGE DeriveAnyClass #-}+module Yam.Server.Health where++import Data.Aeson+import qualified Data.HashMap.Strict as M+import Data.Swagger+import GHC.Generics+import Servant+import Yam.App+import Yam.Prelude++type HealthEndpoint = "health" :> Get '[JSON] HealthResult++data HealthStatus = UP | DOWN deriving (Eq, Show, Generic, ToJSON)++instance ToSchema HealthStatus+instance ToSchema HealthResult++data HealthResult = HealthResult+ { status :: HealthStatus+ , errMsg :: Maybe String+ , details :: M.HashMap Text HealthResult+ } deriving (Eq, Show, Generic, ToJSON)++emptyHealth :: IO HealthResult+emptyHealth = return (HealthResult UP Nothing M.empty)++mergeHealth :: IO HealthStatus -> Text -> IO HealthResult -> IO HealthResult+mergeHealth ios na ior = do+ (err,s) <- ((Nothing,) <$> ios) `catch` (\(e :: SomeException) -> return (Just (show e), DOWN))+ HealthResult{..} <- ior+ return (HealthResult (if s == DOWN then s else status) Nothing $ M.insert na (HealthResult s err M.empty) details)++healthEndpoint :: MonadIO m => IO HealthResult -> Bool -> AppT cxt m HealthResult+healthEndpoint a True = liftIO a+healthEndpoint a _ = liftIO a
+ src/Yam/Server/Refresh.hs view
@@ -0,0 +1,21 @@+module Yam.Server.Refresh where++import Data.Text (Text, pack)+import Salak+import Servant+import Yam.App+import Yam.Logger+import Yam.Prelude++type RefreshEndpoint = "refresh" :> Post '[PlainText] Text++refreshEndpoint :: (HasLogger cxt, MonadIO m) => IO ReloadResult -> Bool -> AppT cxt m Text+refreshEndpoint io True = do+ ReloadResult{..} <- liftIO $ io+ if isError+ then throwS err400 $ showMsg msg+ else return $ showMsg msg+refreshEndpoint _ _ = throwS err401 "Refresh not allowed"++showMsg :: [String] -> Text+showMsg = pack . unlines
src/Yam/Swagger.hs view
@@ -3,14 +3,17 @@ SwaggerConfig(..) , serveWithContextAndSwagger , baseInfo+ , SwaggerTag ) where import Control.Lens hiding (Context) import Data.Reflection import Data.Swagger import Data.Version (showVersion)+import GHC.TypeLits import Salak import Servant+import Servant.Client import Servant.Swagger import Servant.Swagger.UI import Yam.Prelude@@ -59,5 +62,27 @@ & info . version .~ pack (showVersion v) & host ?~ Host hostName (Just $ fromIntegral p) +data SwaggerTag (name :: Symbol) (desp :: Symbol) +instance HasServer api ctx+ => HasServer (SwaggerTag name desp :> api) ctx where+ type ServerT (SwaggerTag name desp :> api) m = ServerT api m+ route _ = route (Proxy @api)+ hoistServerWithContext _ = hoistServerWithContext (Proxy @api)++instance HasClient m api+ => HasClient m (SwaggerTag name desp :> api) where+ type Client m (SwaggerTag name desp :> api) = Client m api+ clientWithRoute _ _ = clientWithRoute (Proxy @m) (Proxy @api)+ hoistClientMonad pm _ = hoistClientMonad pm (Proxy @api)++instance (HasSwagger api, KnownSymbol name, KnownSymbol desp)+ => HasSwagger (SwaggerTag name desp :> api) where+ toSwagger _ = toSwagger (Proxy @api) & applyTags [tag]+ where+ tag = Tag (go (Proxy @name)) (g2 $ go (Proxy @desp)) Nothing+ go :: forall a. KnownSymbol a => Proxy a -> Text+ go = pack . symbolVal+ g2 "" = Nothing+ g2 a = Just a
yam.cabal view
@@ -1,6 +1,6 @@ cabal-version: 1.12 name: yam-version: 0.6.0+version: 0.6.1 license: BSD3 license-file: LICENSE copyright: (c) 2018 Daniel YU@@ -19,6 +19,7 @@ library exposed-modules: Yam+ Yam.Middleware.Client hs-source-dirs: src other-modules: Yam.Swagger@@ -26,8 +27,12 @@ Yam.App Yam.Prelude Yam.Config+ Yam.Middleware Yam.Middleware.Trace Yam.Middleware.Error+ Yam.Server+ Yam.Server.Refresh+ Yam.Server.Health Data.Opentracing Data.Opentracing.Types Data.Opentracing.Tracer@@ -53,7 +58,9 @@ binary >=0.8.6.0 && <0.9, bytestring >=0.10.8.2 && <0.11, data-default >=0.7.1.1 && <0.8,+ exceptions >=0.10.1 && <0.11, fast-logger >=2.4.15 && <2.5,+ http-client >=0.6.4 && <0.7, http-types >=0.12.3 && <0.13, lens ==4.17.*, menshen >=0.0.3 && <0.1,@@ -61,13 +68,15 @@ mtl >=2.2.2 && <2.3, mwc-random >=0.14.0.0 && <0.15, reflection >=2.1.4 && <2.2,- salak >=0.2.9 && <0.3,+ salak >=0.2.9.3 && <0.3, scientific >=0.3.6.2 && <0.4,+ servant-client ==0.16.*, servant-server ==0.16.*, servant-swagger >=1.1.7 && <1.2, servant-swagger-ui >=0.3.2.3.19.3 && <0.4, swagger2 >=2.3.1.1 && <2.4, text >=1.2.3.1 && <1.3,+ transformers >=0.5.6.2 && <0.6, unliftio-core >=0.1.2.0 && <0.2, unordered-containers >=0.2.10.0 && <0.3, vault >=0.3.1.2 && <0.4,@@ -88,9 +97,14 @@ Yam.App Yam.Config Yam.Logger+ Yam.Middleware+ Yam.Middleware.Client Yam.Middleware.Error Yam.Middleware.Trace Yam.Prelude+ Yam.Server+ Yam.Server.Health+ Yam.Server.Refresh Yam.Swagger Paths_yam default-language: Haskell2010@@ -115,8 +129,10 @@ binary >=0.8.6.0 && <0.9, bytestring >=0.10.8.2 && <0.11, data-default >=0.7.1.1 && <0.8,+ exceptions >=0.10.1 && <0.11, fast-logger >=2.4.15 && <2.5, hspec ==2.*,+ http-client >=0.6.4 && <0.7, http-types >=0.12.3 && <0.13, lens ==4.17.*, menshen >=0.0.3 && <0.1,@@ -124,13 +140,15 @@ mtl >=2.2.2 && <2.3, mwc-random >=0.14.0.0 && <0.15, reflection >=2.1.4 && <2.2,- salak >=0.2.9 && <0.3,+ salak >=0.2.9.3 && <0.3, scientific >=0.3.6.2 && <0.4,+ servant-client ==0.16.*, servant-server ==0.16.*, servant-swagger >=1.1.7 && <1.2, servant-swagger-ui >=0.3.2.3.19.3 && <0.4, swagger2 >=2.3.1.1 && <2.4, text >=1.2.3.1 && <1.3,+ transformers >=0.5.6.2 && <0.6, unliftio-core >=0.1.2.0 && <0.2, unordered-containers >=0.2.10.0 && <0.3, vault >=0.3.1.2 && <0.4,