module LaunchDarkly.Server.Network.Polling (pollingThread) where import GHC.Generics (Generic) import Data.Text (Text) import qualified Data.Text as T import Network.HTTP.Client (Manager, Request(..), Response(..), httpLbs) 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.Network.Common (checkAuthorization, tryHTTP, handleUnauthorized) import LaunchDarkly.Server.Features (Flag, Segment) import LaunchDarkly.AesonCompat (KeyMap) import GHC.Natural (Natural) import LaunchDarkly.Server.DataSource.Internal (DataSourceUpdates(..)) import LaunchDarkly.Server.Config.ClientContext import LaunchDarkly.Server.Client.Internal (Status(..)) import LaunchDarkly.Server.Config.HttpConfiguration (HttpConfiguration(..), prepareRequest) data PollingResponse = PollingResponse { PollingResponse -> KeyMap Flag flags :: !(KeyMap Flag) , PollingResponse -> KeyMap Segment segments :: !(KeyMap 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 -> DataSourceUpdates -> Request -> m () processPoll :: Manager -> DataSourceUpdates -> Request -> m () processPoll Manager manager DataSourceUpdates dataSourceUpdates 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 () pack :: String -> Text monadLoggerLog :: forall (m :: * -> *) msg. (MonadLogger m, ToLogStr msg) => Loc -> Text -> LogLevel -> msg -> m () . :: 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 () pack :: String -> Text monadLoggerLog :: forall (m :: * -> *) msg. (MonadLogger m, ToLogStr msg) => Loc -> Text -> LogLevel -> msg -> m () . :: 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 () pack :: String -> Text monadLoggerLog :: forall (m :: * -> *) msg. (MonadLogger m, ToLogStr msg) => Loc -> Text -> LogLevel -> msg -> m () . :: 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 (IO (Either Text ()) -> m (Either Text ())) -> IO (Either Text ()) -> m (Either Text ()) forall a b. (a -> b) -> a -> b $ DataSourceUpdates -> KeyMap Flag -> KeyMap Segment -> IO (Either Text ()) dataSourceUpdatesInit DataSourceUpdates dataSourceUpdates (PollingResponse -> KeyMap Flag forall (f :: Symbol) a s. HasField' f s a => s -> a getField @"flags" PollingResponse body) (PollingResponse -> KeyMap Segment forall (f :: Symbol) a s. HasField' f s a => s -> a getField @"segments" PollingResponse body) case Either Text () status of Right () -> IO () -> m () forall (m :: * -> *) a. MonadIO m => IO a -> m a liftIO (IO () -> m ()) -> IO () -> m () forall a b. (a -> b) -> a -> b $ DataSourceUpdates -> Status -> IO () dataSourceUpdatesSetStatus DataSourceUpdates dataSourceUpdates Status Initialized 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 () pack :: String -> Text monadLoggerLog :: forall (m :: * -> *) msg. (MonadLogger m, ToLogStr msg) => Loc -> Text -> LogLevel -> msg -> m () . :: 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) => Text -> Natural -> ClientContext -> DataSourceUpdates -> m () pollingThread :: Text -> Natural -> ClientContext -> DataSourceUpdates -> m () pollingThread Text baseURI Natural pollingIntervalSeconds ClientContext clientContext DataSourceUpdates dataSourceUpdates = do let pollingMicroseconds :: Int pollingMicroseconds = Natural -> Int forall a b. (Integral a, Num b) => a -> b fromIntegral Natural pollingIntervalSeconds Int -> Int -> Int forall a. Num a => a -> a -> a * Int 1000000 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 $ HttpConfiguration -> String -> IO Request forall (m :: * -> *). MonadThrow m => HttpConfiguration -> String -> m Request prepareRequest (ClientContext -> HttpConfiguration httpConfiguration ClientContext clientContext) (Text -> String T.unpack Text baseURI String -> ShowS forall a. [a] -> [a] -> [a] ++ String "/sdk/latest-all") DataSourceUpdates -> m () -> m () forall (m :: * -> *). (MonadIO m, MonadLogger m, MonadCatch m) => DataSourceUpdates -> m () -> m () handleUnauthorized DataSourceUpdates dataSourceUpdates (m () -> m ()) -> m () -> m () forall a b. (a -> b) -> a -> b $ m () -> m () forall (f :: * -> *) a b. Applicative f => f a -> f b forever (m () -> m ()) -> m () -> m () 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 () pack :: String -> Text monadLoggerLog :: forall (m :: * -> *) msg. (MonadLogger m, ToLogStr msg) => Loc -> Text -> LogLevel -> msg -> m () . :: forall b c a. (b -> c) -> (a -> b) -> a -> c id :: forall a. a -> a logInfo) Text "starting poll" Manager -> DataSourceUpdates -> Request -> m () forall (m :: * -> *). (MonadIO m, MonadLogger m, MonadMask m, MonadThrow m) => Manager -> DataSourceUpdates -> Request -> m () processPoll (HttpConfiguration -> Manager tlsManager (HttpConfiguration -> Manager) -> HttpConfiguration -> Manager forall a b. (a -> b) -> a -> b $ ClientContext -> HttpConfiguration httpConfiguration ClientContext clientContext) DataSourceUpdates dataSourceUpdates 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 () pack :: String -> Text monadLoggerLog :: forall (m :: * -> *) msg. (MonadLogger m, ToLogStr msg) => Loc -> Text -> LogLevel -> msg -> m () . :: 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 pollingMicroseconds