module LaunchDarkly.Server.Network.Polling (pollingThread) where import GHC.Generics (Generic) import Data.HashMap.Strict (HashMap) import Data.Text (Text) import qualified Data.Text as T import Network.HTTP.Client (Manager, Request(..), Response(..), httpLbs, parseRequest) import Data.Generics.Product (getField) import Control.Monad (forever) import Control.Concurrent (threadDelay) import Data.Aeson (eitherDecode, FromJSON(..)) import Control.Monad.Logger (MonadLogger, logInfo, logError) import Control.Monad.IO.Class (MonadIO, liftIO) import Control.Monad.Catch (MonadMask, MonadThrow) import Network.HTTP.Types.Status (ok200) import LaunchDarkly.Server.Client.Internal (ClientI) import LaunchDarkly.Server.Network.Common (tryAuthorized, checkAuthorization, prepareRequest, tryHTTP) import LaunchDarkly.Server.Features (Flag, Segment) import LaunchDarkly.Server.Store.Internal (StoreHandle, initializeStore) data PollingResponse = PollingResponse { PollingResponse -> HashMap Text Flag flags :: !(HashMap Text Flag) , PollingResponse -> HashMap Text Segment segments :: !(HashMap Text Segment) } deriving ((forall x. PollingResponse -> Rep PollingResponse x) -> (forall x. Rep PollingResponse x -> PollingResponse) -> Generic PollingResponse forall x. Rep PollingResponse x -> PollingResponse forall x. PollingResponse -> Rep PollingResponse x forall a. (forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a $cto :: forall x. Rep PollingResponse x -> PollingResponse $cfrom :: forall x. PollingResponse -> Rep PollingResponse x Generic, Value -> Parser [PollingResponse] Value -> Parser PollingResponse (Value -> Parser PollingResponse) -> (Value -> Parser [PollingResponse]) -> FromJSON PollingResponse forall a. (Value -> Parser a) -> (Value -> Parser [a]) -> FromJSON a parseJSONList :: Value -> Parser [PollingResponse] $cparseJSONList :: Value -> Parser [PollingResponse] parseJSON :: Value -> Parser PollingResponse $cparseJSON :: Value -> Parser PollingResponse FromJSON, Int -> PollingResponse -> ShowS [PollingResponse] -> ShowS PollingResponse -> String (Int -> PollingResponse -> ShowS) -> (PollingResponse -> String) -> ([PollingResponse] -> ShowS) -> Show PollingResponse forall a. (Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a showList :: [PollingResponse] -> ShowS $cshowList :: [PollingResponse] -> ShowS show :: PollingResponse -> String $cshow :: PollingResponse -> String showsPrec :: Int -> PollingResponse -> ShowS $cshowsPrec :: Int -> PollingResponse -> ShowS Show) processPoll :: (MonadIO m, MonadLogger m, MonadMask m, MonadThrow m) => Manager -> StoreHandle IO -> Request -> m () processPoll :: Manager -> StoreHandle IO -> Request -> m () processPoll Manager manager StoreHandle IO store Request request = IO (Either HttpException (Response ByteString)) -> m (Either HttpException (Response ByteString)) forall (m :: * -> *) a. MonadIO m => IO a -> m a liftIO (IO (Response ByteString) -> IO (Either HttpException (Response ByteString)) forall (m :: * -> *) a. MonadCatch m => m a -> m (Either HttpException a) tryHTTP (IO (Response ByteString) -> IO (Either HttpException (Response ByteString))) -> IO (Response ByteString) -> IO (Either HttpException (Response ByteString)) forall a b. (a -> b) -> a -> b $ Request -> Manager -> IO (Response ByteString) httpLbs Request request Manager manager) m (Either HttpException (Response ByteString)) -> (Either HttpException (Response ByteString) -> m ()) -> m () forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b >>= \case (Left HttpException err) -> $(Int String LogLevel String -> Text String -> String -> String -> CharPos -> CharPos -> Loc Text -> Text Loc -> Text -> LogLevel -> Text -> m () (Text -> m ()) -> (Text -> Text) -> Text -> m () forall a. a -> a forall b c a. (b -> c) -> (a -> b) -> a -> c forall (m :: * -> *) msg. (MonadLogger m, ToLogStr msg) => Loc -> Text -> LogLevel -> msg -> m () monadLoggerLog :: forall (m :: * -> *) msg. (MonadLogger m, ToLogStr msg) => Loc -> Text -> LogLevel -> msg -> m () pack :: String -> Text . :: forall b c a. (b -> c) -> (a -> b) -> a -> c id :: forall a. a -> a logError) (String -> Text T.pack (String -> Text) -> String -> Text forall a b. (a -> b) -> a -> b $ HttpException -> String forall a. Show a => a -> String show HttpException err) (Right Response ByteString response) -> Response ByteString -> m () forall (m :: * -> *) body. MonadThrow m => Response body -> m () checkAuthorization Response ByteString response m () -> m () -> m () forall (m :: * -> *) a b. Monad m => m a -> m b -> m b >> if Response ByteString -> Status forall body. Response body -> Status responseStatus Response ByteString response Status -> Status -> Bool forall a. Eq a => a -> a -> Bool /= Status ok200 then $(Int String LogLevel String -> Text String -> String -> String -> CharPos -> CharPos -> Loc Text -> Text Loc -> Text -> LogLevel -> Text -> m () (Text -> m ()) -> (Text -> Text) -> Text -> m () forall a. a -> a forall b c a. (b -> c) -> (a -> b) -> a -> c forall (m :: * -> *) msg. (MonadLogger m, ToLogStr msg) => Loc -> Text -> LogLevel -> msg -> m () monadLoggerLog :: forall (m :: * -> *) msg. (MonadLogger m, ToLogStr msg) => Loc -> Text -> LogLevel -> msg -> m () pack :: String -> Text . :: forall b c a. (b -> c) -> (a -> b) -> a -> c id :: forall a. a -> a logError) Text "unexpected polling status code" else case (ByteString -> Either String PollingResponse forall a. FromJSON a => ByteString -> Either String a eitherDecode (Response ByteString -> ByteString forall body. Response body -> body responseBody Response ByteString response) :: Either String PollingResponse) of (Left String err) -> $(Int String LogLevel String -> Text String -> String -> String -> CharPos -> CharPos -> Loc Text -> Text Loc -> Text -> LogLevel -> Text -> m () (Text -> m ()) -> (Text -> Text) -> Text -> m () forall a. a -> a forall b c a. (b -> c) -> (a -> b) -> a -> c forall (m :: * -> *) msg. (MonadLogger m, ToLogStr msg) => Loc -> Text -> LogLevel -> msg -> m () monadLoggerLog :: forall (m :: * -> *) msg. (MonadLogger m, ToLogStr msg) => Loc -> Text -> LogLevel -> msg -> m () pack :: String -> Text . :: forall b c a. (b -> c) -> (a -> b) -> a -> c id :: forall a. a -> a logError) (String -> Text T.pack (String -> Text) -> String -> Text forall a b. (a -> b) -> a -> b $ ShowS forall a. Show a => a -> String show String err) (Right PollingResponse body) -> do Either Text () status <- IO (Either Text ()) -> m (Either Text ()) forall (m :: * -> *) a. MonadIO m => IO a -> m a liftIO (StoreHandle IO -> HashMap Text Flag -> HashMap Text Segment -> IO (Either Text ()) forall store (m :: * -> *). (LaunchDarklyStoreWrite store m, Monad m) => store -> HashMap Text Flag -> HashMap Text Segment -> StoreResultM m () initializeStore StoreHandle IO store (PollingResponse -> HashMap Text Flag forall (f :: Symbol) a s. HasField' f s a => s -> a getField @"flags" PollingResponse body) (PollingResponse -> HashMap Text Segment forall (f :: Symbol) a s. HasField' f s a => s -> a getField @"segments" PollingResponse body)) case Either Text () status of Right () -> () -> m () forall (f :: * -> *) a. Applicative f => a -> f a pure () Left Text err -> $(Int String LogLevel String -> Text String -> String -> String -> CharPos -> CharPos -> Loc Text -> Text Loc -> Text -> LogLevel -> Text -> m () (Text -> m ()) -> (Text -> Text) -> Text -> m () forall a. a -> a forall b c a. (b -> c) -> (a -> b) -> a -> c forall (m :: * -> *) msg. (MonadLogger m, ToLogStr msg) => Loc -> Text -> LogLevel -> msg -> m () monadLoggerLog :: forall (m :: * -> *) msg. (MonadLogger m, ToLogStr msg) => Loc -> Text -> LogLevel -> msg -> m () pack :: String -> Text . :: forall b c a. (b -> c) -> (a -> b) -> a -> c id :: forall a. a -> a logError) (Text -> m ()) -> Text -> m () forall a b. (a -> b) -> a -> b $ Text -> Text -> Text T.append Text "store failed put: " Text err pollingThread :: (MonadIO m, MonadLogger m, MonadMask m) => Manager -> ClientI -> m () pollingThread :: Manager -> ClientI -> m () pollingThread Manager manager ClientI client = do let config :: ConfigI config = ClientI -> ConfigI forall (f :: Symbol) a s. HasField' f s a => s -> a getField @"config" ClientI client; store :: StoreHandle IO store = ClientI -> StoreHandle IO forall (f :: Symbol) a s. HasField' f s a => s -> a getField @"store" ClientI client; Request req <- (IO Request -> m Request forall (m :: * -> *) a. MonadIO m => IO a -> m a liftIO (IO Request -> m Request) -> IO Request -> m Request forall a b. (a -> b) -> a -> b $ String -> IO Request forall (m :: * -> *). MonadThrow m => String -> m Request parseRequest (String -> IO Request) -> String -> IO Request forall a b. (a -> b) -> a -> b $ (Text -> String T.unpack (Text -> String) -> Text -> String forall a b. (a -> b) -> a -> b $ ConfigI -> Text forall (f :: Symbol) a s. HasField' f s a => s -> a getField @"baseURI" ConfigI config) String -> ShowS forall a. [a] -> [a] -> [a] ++ String "/sdk/latest-all") m Request -> (Request -> m Request) -> m Request forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b >>= Request -> m Request forall (f :: * -> *) a. Applicative f => a -> f a pure (Request -> m Request) -> (Request -> Request) -> Request -> m Request forall b c a. (b -> c) -> (a -> b) -> a -> c . ConfigI -> Request -> Request prepareRequest ConfigI config ClientI -> m Any -> m () forall (m :: * -> *) a. (MonadIO m, MonadLogger m, MonadCatch m) => ClientI -> m a -> m () tryAuthorized ClientI client (m Any -> m ()) -> m Any -> m () forall a b. (a -> b) -> a -> b $ m () -> m Any forall (f :: * -> *) a b. Applicative f => f a -> f b forever (m () -> m Any) -> m () -> m Any forall a b. (a -> b) -> a -> b $ do $(Int String LogLevel String -> Text String -> String -> String -> CharPos -> CharPos -> Loc Text -> Text Loc -> Text -> LogLevel -> Text -> m () (Text -> m ()) -> (Text -> Text) -> Text -> m () forall a. a -> a forall b c a. (b -> c) -> (a -> b) -> a -> c forall (m :: * -> *) msg. (MonadLogger m, ToLogStr msg) => Loc -> Text -> LogLevel -> msg -> m () monadLoggerLog :: forall (m :: * -> *) msg. (MonadLogger m, ToLogStr msg) => Loc -> Text -> LogLevel -> msg -> m () pack :: String -> Text . :: forall b c a. (b -> c) -> (a -> b) -> a -> c id :: forall a. a -> a logInfo) Text "starting poll" Manager -> StoreHandle IO -> Request -> m () forall (m :: * -> *). (MonadIO m, MonadLogger m, MonadMask m, MonadThrow m) => Manager -> StoreHandle IO -> Request -> m () processPoll Manager manager StoreHandle IO store Request req $(Int String LogLevel String -> Text String -> String -> String -> CharPos -> CharPos -> Loc Text -> Text Loc -> Text -> LogLevel -> Text -> m () (Text -> m ()) -> (Text -> Text) -> Text -> m () forall a. a -> a forall b c a. (b -> c) -> (a -> b) -> a -> c forall (m :: * -> *) msg. (MonadLogger m, ToLogStr msg) => Loc -> Text -> LogLevel -> msg -> m () monadLoggerLog :: forall (m :: * -> *) msg. (MonadLogger m, ToLogStr msg) => Loc -> Text -> LogLevel -> msg -> m () pack :: String -> Text . :: forall b c a. (b -> c) -> (a -> b) -> a -> c id :: forall a. a -> a logInfo) Text "finished poll" IO () -> m () forall (m :: * -> *) a. MonadIO m => IO a -> m a liftIO (IO () -> m ()) -> IO () -> m () forall a b. (a -> b) -> a -> b $ Int -> IO () threadDelay (Int -> IO ()) -> Int -> IO () forall a b. (a -> b) -> a -> b $ Int -> Int -> Int forall a. Num a => a -> a -> a (*) Int 1000000 (Int -> Int) -> Int -> Int forall a b. (a -> b) -> a -> b $ Natural -> Int forall a b. (Integral a, Num b) => a -> b fromIntegral (Natural -> Int) -> Natural -> Int forall a b. (a -> b) -> a -> b $ ConfigI -> Natural forall (f :: Symbol) a s. HasField' f s a => s -> a getField @"pollIntervalSeconds" ConfigI config