mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-17 12:31:52 +03:00
5b54f9d766
* add expiry time to webhook user info This also adds an optional message to webhook errors: if we fail to parse an expiry time, we will log a warning with the parse error. * refactored Auth This change had one main goal: put in common all expiry time extraction code between the JWT and WebHook parts of the code. Furthermore, this change also moves all WebHook specific code to its own module, similarly to what is done for JWT. * Remove dependency on string-conversions in favor of text-conversions string-conversions silently uses UTF8 instead of being explicit about it, and it uses lenientDecode when decoding ByteStrings when it’s usually better to reject invalid UTF8 input outright. text-conversions solves both those problems. Co-authored-by: Alexis King <lexi.lambda@gmail.com>
148 lines
6.0 KiB
Haskell
148 lines
6.0 KiB
Haskell
{-# OPTIONS_GHC -fno-warn-orphans #-}
|
|
module Hasura.Prelude
|
|
( module M
|
|
, alphaNumerics
|
|
, onNothing
|
|
, onJust
|
|
, onLeft
|
|
, choice
|
|
, afold
|
|
, bsToTxt
|
|
, txtToBs
|
|
, spanMaybeM
|
|
, findWithIndex
|
|
, mapFromL
|
|
-- * Measuring and working with moments and durations
|
|
, withElapsedTime
|
|
, startTimer
|
|
, module Data.Time.Clock.Units
|
|
) where
|
|
|
|
import Control.Applicative as M (Alternative (..))
|
|
import Control.Arrow as M (first, second, (&&&), (***), (<<<), (>>>))
|
|
import Control.DeepSeq as M (NFData, deepseq, force)
|
|
import Control.Monad as M (void, when)
|
|
import Control.Monad.Base as M
|
|
import Control.Monad.Except as M
|
|
import Control.Monad.Fail as M (MonadFail)
|
|
import Control.Monad.Identity as M
|
|
import Control.Monad.Reader as M
|
|
import Control.Monad.State.Strict as M
|
|
import Control.Monad.Writer.Strict as M (MonadWriter (..), WriterT (..))
|
|
import Data.Align as M (Align (align, alignWith))
|
|
import Data.Align.Key as M (AlignWithKey (..))
|
|
import Data.Bool as M (bool)
|
|
import Data.Data as M (Data (..))
|
|
import Data.Either as M (lefts, partitionEithers, rights)
|
|
import Data.Foldable as M (asum, foldrM, for_, toList, traverse_)
|
|
import Data.Function as M (on, (&))
|
|
import Data.Functor as M (($>), (<&>))
|
|
import Data.Hashable as M (Hashable)
|
|
import Data.HashMap.Strict as M (HashMap)
|
|
import Data.HashSet as M (HashSet)
|
|
import Data.List as M (find, findIndex, foldl', group,
|
|
intercalate, intersect, lookup, sort,
|
|
sortBy, sortOn, union, unionBy, (\\))
|
|
import Data.List.NonEmpty as M (NonEmpty (..))
|
|
import Data.Maybe as M (catMaybes, fromMaybe, isJust, isNothing,
|
|
listToMaybe, mapMaybe, maybeToList)
|
|
import Data.Monoid as M (getAlt)
|
|
import Data.Ord as M (comparing)
|
|
import Data.Semigroup as M (Semigroup (..))
|
|
import Data.Sequence as M (Seq)
|
|
import Data.String as M (IsString)
|
|
import Data.Text as M (Text)
|
|
import Data.These as M (These (..), fromThese, mergeThese,
|
|
mergeTheseWith, these)
|
|
import Data.Time.Clock.Units
|
|
import Data.Traversable as M (for)
|
|
import Data.Word as M (Word64)
|
|
import GHC.Generics as M (Generic)
|
|
import Prelude as M hiding (fail, init, lookup)
|
|
import Test.QuickCheck.Arbitrary.Generic as M
|
|
import Text.Read as M (readEither, readMaybe)
|
|
|
|
import qualified Data.ByteString as B
|
|
import qualified Data.HashMap.Strict as Map
|
|
import qualified Data.Text as T
|
|
import qualified Data.Text.Encoding as TE
|
|
import qualified Data.Text.Encoding.Error as TE
|
|
import qualified GHC.Clock as Clock
|
|
import qualified Test.QuickCheck as QC
|
|
|
|
alphaNumerics :: String
|
|
alphaNumerics = ['a'..'z'] ++ ['A'..'Z'] ++ "0123456789 "
|
|
|
|
instance Arbitrary Text where
|
|
arbitrary = T.pack <$> QC.listOf (QC.elements alphaNumerics)
|
|
|
|
onNothing :: (Monad m) => Maybe a -> m a -> m a
|
|
onNothing m act = maybe act return m
|
|
|
|
onJust :: (Monad m) => Maybe a -> (a -> m ()) -> m ()
|
|
onJust m action = maybe (return ()) action m
|
|
|
|
onLeft :: (Monad m) => Either e a -> (e -> m a) -> m a
|
|
onLeft e f = either f return e
|
|
|
|
choice :: (Alternative f) => [f a] -> f a
|
|
choice = asum
|
|
|
|
afold :: (Foldable t, Alternative f) => t a -> f a
|
|
afold = getAlt . foldMap pure
|
|
|
|
bsToTxt :: B.ByteString -> Text
|
|
bsToTxt = TE.decodeUtf8With TE.lenientDecode
|
|
|
|
txtToBs :: Text -> B.ByteString
|
|
txtToBs = TE.encodeUtf8
|
|
|
|
-- Like 'span', but monadic and with a function that produces 'Maybe' instead of 'Bool'
|
|
spanMaybeM
|
|
:: (Foldable f, Monad m)
|
|
=> (a -> m (Maybe b)) -> f a -> m ([b], [a])
|
|
spanMaybeM f = go . toList
|
|
where
|
|
go [] = pure ([], [])
|
|
go l@(x:xs) = f x >>= \case
|
|
Just y -> first (y:) <$> go xs
|
|
Nothing -> pure ([], l)
|
|
|
|
findWithIndex :: (a -> Bool) -> [a] -> Maybe (a, Int)
|
|
findWithIndex p l = do
|
|
v <- find p l
|
|
i <- findIndex p l
|
|
pure (v, i)
|
|
|
|
-- TODO: Move to Data.HashMap.Strict.Extended; rename to fromListWith?
|
|
mapFromL :: (Eq k, Hashable k) => (a -> k) -> [a] -> Map.HashMap k a
|
|
mapFromL f = Map.fromList . map (\v -> (f v, v))
|
|
|
|
-- | Time an IO action, returning the time with microsecond precision. The
|
|
-- result of the input action will be evaluated to WHNF.
|
|
--
|
|
-- The result 'DiffTime' is guarenteed to be >= 0.
|
|
withElapsedTime :: MonadIO m=> m a -> m (DiffTime, a)
|
|
withElapsedTime ma = do
|
|
bef <- liftIO Clock.getMonotonicTimeNSec
|
|
!a <- ma
|
|
aft <- liftIO Clock.getMonotonicTimeNSec
|
|
let !dur = nanoseconds $ fromIntegral (aft - bef)
|
|
return (dur, a)
|
|
|
|
-- | Start timing and return an action to return the elapsed time since 'startTimer' was called.
|
|
--
|
|
-- @
|
|
-- timer <- startTimer
|
|
-- someStuffToTime
|
|
-- elapsed <- timer
|
|
-- moreStuff
|
|
-- elapsedBoth <- timer
|
|
-- @
|
|
startTimer :: (MonadIO m, MonadIO n)=> m (n DiffTime)
|
|
startTimer = do
|
|
!bef <- liftIO Clock.getMonotonicTimeNSec
|
|
return $ do
|
|
aft <- liftIO Clock.getMonotonicTimeNSec
|
|
return $ nanoseconds $ fromIntegral (aft - bef)
|