From 3b9fb1aa60d060d7cfd7634e532327086f0ef5f1 Mon Sep 17 00:00:00 2001 From: Profpatsch Date: Sun, 17 Mar 2024 02:26:47 +0100 Subject: feat(users/Profpatsch/whatcd-resolver): add autorefresh Adds a little polling mechanism that compares against an ID that is generated anew every time the server is restarted. Works well together with shortcuttable. Change-Id: Icc6745b599e43881c14349794feaf5794cfe6777 Reviewed-on: https://cl.tvl.fyi/c/depot/+/11172 Autosubmit: Profpatsch Reviewed-by: Profpatsch Tested-by: BuildkiteCI --- users/Profpatsch/.hlint.yaml | 4 +- .../my-prelude/src/Postgres/MonadPostgres.hs | 15 ++++++ users/Profpatsch/whatcd-resolver/src/AppT.hs | 16 +++++++ .../whatcd-resolver/src/WhatcdResolver.hs | 56 +++++++++++++++++++--- 4 files changed, 82 insertions(+), 9 deletions(-) (limited to 'users/Profpatsch') diff --git a/users/Profpatsch/.hlint.yaml b/users/Profpatsch/.hlint.yaml index 1e7e083bba93..f00f78c5259d 100644 --- a/users/Profpatsch/.hlint.yaml +++ b/users/Profpatsch/.hlint.yaml @@ -125,11 +125,11 @@ message: "`void` leads to bugs. Use an explicit `_ <- …` instead" - name: Data.Foldable.length - within: [] + within: ["MyPrelude"] message: "`Data.Foldable.length` is dangerous to use, because it also works on types you wouldn’t expect, like `length (3,4) == 1` and `length (Just 2) == 1`. Use the `length` function for your specific type instead, for example `List.length` or `Map.length`." - name: Prelude.length - within: [MyPrelude] + within: ["MyPrelude"] message: "`Prelude.length` is dangerous to use, because it also works on types you wouldn’t expect, like `length (3,4) == 1` and `length (Just 2) == 1`. Use the `length` function for your specific type instead, for example `List.length` or `Map.length`." # Using an explicit lambda with its argument “underscored” diff --git a/users/Profpatsch/my-prelude/src/Postgres/MonadPostgres.hs b/users/Profpatsch/my-prelude/src/Postgres/MonadPostgres.hs index bd8ddd12f775..2585f6637c41 100644 --- a/users/Profpatsch/my-prelude/src/Postgres/MonadPostgres.hs +++ b/users/Profpatsch/my-prelude/src/Postgres/MonadPostgres.hs @@ -164,6 +164,21 @@ querySingleRow :: querySingleRow qry params = do query qry params >>= ensureSingleRow +-- TODO: implement via fold, so that the result doesn’t have to be realized in memory +querySingleRowWith :: + ( MonadPostgres m, + ToRow qParams, + Typeable qParams, + Typeable a, + MonadThrow m + ) => + Query -> + qParams -> + Decoder a -> + Transaction m a +querySingleRowWith qry params decoder = do + queryWith qry params decoder >>= ensureSingleRow + -- TODO: implement via fold, so that the result doesn’t have to be realized in memory querySingleRowMaybe :: ( MonadPostgres m, diff --git a/users/Profpatsch/whatcd-resolver/src/AppT.hs b/users/Profpatsch/whatcd-resolver/src/AppT.hs index 7bd38a733e53..7afd430745f6 100644 --- a/users/Profpatsch/whatcd-resolver/src/AppT.hs +++ b/users/Profpatsch/whatcd-resolver/src/AppT.hs @@ -66,6 +66,17 @@ addAttribute span key a = Otel.addAttribute span ("_." <> key) a addAttributes :: (MonadIO m) => Otel.Span -> HashMap Text Otel.Attribute -> m () addAttributes span attrs = Otel.addAttributes span $ attrs & HashMap.mapKeys ("_." <>) +appThrowTreeNewSpan :: (MonadThrow m, MonadOtel m) => Text -> ErrorTree -> m a +appThrowTreeNewSpan spanName exc = inSpan' spanName $ \span -> do + let msg = prettyErrorTree exc + recordException + span + ( T2 + (label @"type_" "AppException") + (label @"message" msg) + ) + throwM $ AppException msg + appThrowTree :: (MonadThrow m, MonadIO m) => Otel.Span -> ErrorTree -> m a appThrowTree span exc = do let msg = prettyErrorTree exc @@ -87,6 +98,11 @@ assertM span f v = case f v of Right a -> pure a Left err -> appThrowTree span err +assertMNewSpan :: (MonadThrow f, MonadOtel f) => Text -> (t -> Either ErrorTree a) -> t -> f a +assertMNewSpan spanName f v = case f v of + Right a -> pure a + Left err -> appThrowTreeNewSpan spanName err + -- | A specialized variant of @addEvent@ that records attributes conforming to -- the OpenTelemetry specification's -- diff --git a/users/Profpatsch/whatcd-resolver/src/WhatcdResolver.hs b/users/Profpatsch/whatcd-resolver/src/WhatcdResolver.hs index fe0952a5ffab..7f2c4ec7e83c 100644 --- a/users/Profpatsch/whatcd-resolver/src/WhatcdResolver.hs +++ b/users/Profpatsch/whatcd-resolver/src/WhatcdResolver.hs @@ -82,7 +82,16 @@ main = htmlUi :: AppT IO () htmlUi = do let debug = True - withRunInIO $ \runInIO -> Warp.run 9092 $ \req respond -> do + uniqueRunId <- + runTransaction $ + querySingleRowWith + [sql| + SELECT gen_random_uuid()::text + |] + () + (Dec.fromField @Text) + + withRunInIO $ \runInIO -> Warp.run 9093 $ \req respond -> do let catchAppException act = try act >>= \case Right a -> pure a @@ -95,7 +104,7 @@ htmlUi = do if debug then Html.Pretty.renderHtml >>> stringToText >>> textToBytesUtf8 >>> toLazyBytes else Html.renderHtml - let h route act = + let hh route act = runInIO $ Otel.inSpan' [fmt|Route {route }|] @@ -108,9 +117,9 @@ htmlUi = do ) ( \span -> withRunInIO $ \runInIO' -> do res <- runInIO' $ act span - respond . Wai.responseLBS Http.ok200 [("Content-Type", "text/html")] . renderHtml $ res + respond . Wai.responseLBS Http.ok200 ([("Content-Type", "text/html")] <> res.extraHeaders) . renderHtml $ res.html ) - + let h route act = hh route (\span -> act span <&> (\html -> T2 (label @"html" html) (label @"extraHeaders" []))) let mp span parser = Multipart.parseMultipartOrThrow (appThrowTree span) @@ -127,8 +136,12 @@ htmlUi = do Parse.runParse "Unable to find the right request query arguments" (lmap Wai.queryString parser) req & assertM span id + let parseQueryArgsNewSpan spanName parser = + Parse.runParse "Unable to find the right request query arguments" (lmap Wai.queryString parser) req + & assertMNewSpan spanName id + case req & Wai.pathInfo & Text.intercalate "/" of - "" -> h "/" mainHtml + "" -> h "/" (mainHtml uniqueRunId) "snips/redacted/search" -> do h "/snips/redacted/search" $ \span -> do dat <- @@ -209,12 +222,30 @@ htmlUi = do ) jsonld <- httpGetJsonLd (qry.target) pure $ renderJsonld jsonld - otherRoute -> h [fmt|/{otherRoute}|] mainHtml + "autorefresh" -> do + qry <- + runInIO $ + parseQueryArgsNewSpan + "Autorefresh Query Parse" + ( label @"hasItBeenRestarted" + <$> singleQueryArgument "hasItBeenRestarted" Field.utf8 + ) + respond $ + Wai.responseLBS + Http.ok200 + ( [("Content-Type", "text/html")] + <> if uniqueRunId /= qry.hasItBeenRestarted + then -- cause the client side to refresh + [("HX-Refresh", "true")] + else [] + ) + "" + otherRoute -> h [fmt|/{otherRoute}|] (mainHtml uniqueRunId) where everySecond :: Text -> Enc -> Html -> Html everySecond call extraData innerHtml = [hsx|
{innerHtml}
|] - mainHtml _span = runTransaction $ do + mainHtml uniqueRunId _span = runTransaction $ do jsonld <- httpGetJsonLd ( URI.parseURI "https://musicbrainz.org/work/92000fd4-d304-406d-aeb4-6bdbeed318ec" & annotate "not an URI" & unwrapError, @@ -257,6 +288,17 @@ htmlUi = do
{bestTorrentsTable}
+ + |] -- cgit 1.4.1