Copyright | (c) 2023 Brendan Hay |
---|---|
License | Mozilla Public License, v. 2.0. |
Maintainer | Brendan Hay <brendan.g.hay+amazonka@gmail.com> |
Stability | highly experimental |
Portability | non-portable (GHC extensions) |
Safe Haskell | Safe-Inferred |
Language | Haskell2010 |
Hooks carried within an Env
, allowing ad-hoc injection of
different behaviour during Amazonka's request/response cycle.
Hooks are currently experimental, but Amazonka uses the Hooks
API
to implement its default logging, and you can add your own
behaviour here as well. Some examples of things hooks can do:
- Log all requests Amazonka makes to a separate log, in order to
audit which IAM permissions your program actually needs
(see
requestHook
);
{-# LANGAUGE OverloadedLabels #-} import Amazonka import Amazonka.Env.Hooks import Data.Generics.Labels () main :: IO () main = do env <- newEnv discover <&> #hooks %~requestHook
(addAWSRequestHook
$ \_env req -> req <$ logRequest req) ... logRequest :: AWSRequest a => a -> IO () logRequest = ...
- Inject a Trace ID for AWS X-Ray,
into each request before it is signed and sent
(see
configuredRequestHook
); and
{-# LANGAUGE OverloadedLabels #-} import Amazonka import Amazonka.Env.Hooks import Data.Generics.Labels () main :: IO () main = do env <- newEnv discover <&> #hooks %~configuredRequestHook
(addHook
$ \_env req -> req & #headers %~ addXRayIdHeader) ... -- The actual header would normally come from whatever calls into your program, -- or you would randomly generate one yourself (hooks run inIO
). addXRayIdHeader :: [Header
] -> [Header
] addXRayIdHeader = ...
- Selectively silence certain expected errors, such as DynamoDB's
ConditionalCheckFailedException
(errorHook
andsilenceError
)
{-# LANGAUGE OverloadedLabels #-} import Amazonka import Amazonka.Env.Hooks import qualified Amazonka.DynamoDB as DynamoDB import Data.Generics.Labels () main :: IO () main = do env <- newEnv discover putItemResponse <- runResourceT $ send (env & #hooks %~errorHook
(silenceError
DynamoDB._ConditionalCheckFailedException)) (DynamoDB.newPutItem ...) ...
Most functions with names ending in Hook
(requestHook
, etc.)
are intended for use with lenses: partially apply them to get a
function
that can go on the RHS of Hook
a -> Hook
a(%~)
(the lens modify function). You then use functions like
addHookFor
to selectively extend the hooks used at any particular
time.
Names ending in _
(Hook_
, addHookFor_
, etc.) concern hooks
that return ()
instead of the hook's input type. These hooks
respond to some event but lack the ability to change Amazonka's
behaviour; either because it is unsafe to do so, or because it is
difficult to do anything meaningful with the updated value.
The request/response flow for a standard send
looks like
this:
send (req ::AWSRequest
a => a) | V Run Hook: request | V Amazonka: configure req into "Request a" (Amazonka-specific HTTP request type) | V Run Hook: configuredRequest | V Amazonka: sign request, turn into standard Network.HTTP.Client.Request
| +-<---------------------------------. V | Run Hook: signedRequest | | | V | Run Hook: clientRequest | | | V | Amazonka: send request to AWS Run Hook: requestRetry | ^ V | Run Hook: clientResponse | | | V | Run Hook: rawResponseBody | | | V | Amazonka: was error? ------------------. | | Yes | | | V | | No Run Hook: error | | (NotFinal
) | | | | +-<-----------------------' | V | Amazonka: should retry? -------------------------' | Yes | No V Amazonka: was error? ------------------. | Yes | | V | No | | | Run Hook: response Run Hook: error | (Final
) | | V | Amazonka: parse response | | | +-<-----------------------' V Amazonka: return result
Synopsis
- type Hook a = forall withAuth. Env' withAuth -> a -> IO a
- type Hook_ a = forall withAuth. Env' withAuth -> a -> IO ()
- data Hooks = Hooks {
- request :: forall a. (AWSRequest a, Typeable a) => Hook a
- configuredRequest :: forall a. (AWSRequest a, Typeable a) => Hook (Request a)
- wait :: forall a. (AWSRequest a, Typeable a) => Hook (Wait a)
- signedRequest :: forall a. (AWSRequest a, Typeable a) => Hook_ (Signed a)
- clientRequest :: Hook ClientRequest
- clientResponse :: forall a. (AWSRequest a, Typeable a) => Hook_ (Request a, ClientResponse ())
- rawResponseBody :: Hook ByteStringLazy
- requestRetry :: forall a. (AWSRequest a, Typeable a) => Hook_ (Request a, Text, RetryStatus)
- awaitRetry :: forall a. (AWSRequest a, Typeable a) => Hook_ (Request a, Wait a, Accept, RetryStatus)
- response :: forall a. (AWSRequest a, Typeable a) => Hook_ (Request a, ClientResponse (AWSResponse a))
- error :: forall a. (AWSRequest a, Typeable a) => Hook_ (Finality, Request a, Error)
- data Finality
- requestHook :: (forall a. (AWSRequest a, Typeable a) => Hook a -> Hook a) -> Hooks -> Hooks
- waitHook :: (forall a. (AWSRequest a, Typeable a) => Hook (Wait a) -> Hook (Wait a)) -> Hooks -> Hooks
- configuredRequestHook :: (forall a. (AWSRequest a, Typeable a) => Hook (Request a) -> Hook (Request a)) -> Hooks -> Hooks
- signedRequestHook :: (forall a. (AWSRequest a, Typeable a) => Hook_ (Signed a) -> Hook_ (Signed a)) -> Hooks -> Hooks
- clientRequestHook :: (Hook ClientRequest -> Hook ClientRequest) -> Hooks -> Hooks
- clientResponseHook :: (forall a. (AWSRequest a, Typeable a) => Hook_ (Request a, ClientResponse ()) -> Hook_ (Request a, ClientResponse ())) -> Hooks -> Hooks
- rawResponseBodyHook :: (Hook ByteStringLazy -> Hook ByteStringLazy) -> Hooks -> Hooks
- requestRetryHook :: (forall a. (AWSRequest a, Typeable a) => Hook_ (Request a, Text, RetryStatus) -> Hook_ (Request a, Text, RetryStatus)) -> Hooks -> Hooks
- awaitRetryHook :: (forall a. (AWSRequest a, Typeable a) => Hook_ (Request a, Wait a, Accept, RetryStatus) -> Hook_ (Request a, Wait a, Accept, RetryStatus)) -> Hooks -> Hooks
- responseHook :: (forall a. (AWSRequest a, Typeable a) => Hook_ (Request a, ClientResponse (AWSResponse a)) -> Hook_ (Request a, ClientResponse (AWSResponse a))) -> Hooks -> Hooks
- errorHook :: (forall a. (AWSRequest a, Typeable a) => Hook_ (Finality, Request a, Error) -> Hook_ (Finality, Request a, Error)) -> Hooks -> Hooks
- noHook :: Hook a -> Hook a
- noHook_ :: Hook_ a -> Hook_ a
- addHook :: Typeable a => Hook a -> Hook a -> Hook a
- addHook_ :: Typeable a => Hook_ a -> Hook_ a -> Hook_ a
- addAWSRequestHook :: (AWSRequest a, Typeable a) => Hook a -> Hook a -> Hook a
- addAWSRequestHook_ :: (AWSRequest a, Typeable a) => Hook_ a -> Hook_ a -> Hook_ a
- addHookFor :: forall a b. (Typeable a, Typeable b) => Hook a -> Hook b -> Hook b
- addHookFor_ :: forall a b. (Typeable a, Typeable b) => Hook_ a -> Hook_ b -> Hook_ b
- removeHooksFor :: forall a b. (Typeable a, Typeable b) => Hook b -> Hook b
- removeHooksFor_ :: forall a b. (Typeable a, Typeable b) => Hook_ b -> Hook_ b
- silenceError :: Getting Any Error e -> Hook_ (Finality, Request a, Error) -> Hook_ (Finality, Request a, Error)
- addLoggingHooks :: Hooks -> Hooks
- noHooks :: Hooks
Documentation
type Hook a = forall withAuth. Env' withAuth -> a -> IO a Source #
A hook that returns an updated version of its arguments.
type Hook_ a = forall withAuth. Env' withAuth -> a -> IO () Source #
A hook that cannot return an updated version of its argument.
Hooks | |
|
Indicates whether an error hook is potentially going to be retried.
See: $sel:error:Hooks
Instances
Bounded Finality Source # | |
Enum Finality Source # | |
Generic Finality Source # | |
Show Finality Source # | |
Eq Finality Source # | |
Ord Finality Source # | |
Defined in Amazonka.Env.Hooks | |
type Rep Finality Source # | |
Updating members of Hooks
requestHook :: (forall a. (AWSRequest a, Typeable a) => Hook a -> Hook a) -> Hooks -> Hooks Source #
waitHook :: (forall a. (AWSRequest a, Typeable a) => Hook (Wait a) -> Hook (Wait a)) -> Hooks -> Hooks Source #
configuredRequestHook :: (forall a. (AWSRequest a, Typeable a) => Hook (Request a) -> Hook (Request a)) -> Hooks -> Hooks Source #
signedRequestHook :: (forall a. (AWSRequest a, Typeable a) => Hook_ (Signed a) -> Hook_ (Signed a)) -> Hooks -> Hooks Source #
clientRequestHook :: (Hook ClientRequest -> Hook ClientRequest) -> Hooks -> Hooks Source #
clientResponseHook :: (forall a. (AWSRequest a, Typeable a) => Hook_ (Request a, ClientResponse ()) -> Hook_ (Request a, ClientResponse ())) -> Hooks -> Hooks Source #
rawResponseBodyHook :: (Hook ByteStringLazy -> Hook ByteStringLazy) -> Hooks -> Hooks Source #
requestRetryHook :: (forall a. (AWSRequest a, Typeable a) => Hook_ (Request a, Text, RetryStatus) -> Hook_ (Request a, Text, RetryStatus)) -> Hooks -> Hooks Source #
awaitRetryHook :: (forall a. (AWSRequest a, Typeable a) => Hook_ (Request a, Wait a, Accept, RetryStatus) -> Hook_ (Request a, Wait a, Accept, RetryStatus)) -> Hooks -> Hooks Source #
responseHook :: (forall a. (AWSRequest a, Typeable a) => Hook_ (Request a, ClientResponse (AWSResponse a)) -> Hook_ (Request a, ClientResponse (AWSResponse a))) -> Hooks -> Hooks Source #
errorHook :: (forall a. (AWSRequest a, Typeable a) => Hook_ (Finality, Request a, Error) -> Hook_ (Finality, Request a, Error)) -> Hooks -> Hooks Source #
Functions to use with the ones above
addHook :: Typeable a => Hook a -> Hook a -> Hook a Source #
Unconditionally add a
to the chain of hooks. If you
need to do something with specific request types, you want
Hook
aaddHookFor
, instead.
addHook_ :: Typeable a => Hook_ a -> Hook_ a -> Hook_ a Source #
Unconditionally add a
to the chain of hooks. If you
need to do something with specific request types, you want
Hook_
aaddHookFor_
, instead.
addAWSRequestHook :: (AWSRequest a, Typeable a) => Hook a -> Hook a -> Hook a Source #
Like addHook
, adds an unconditional hook, but it also captures
the
constraint. Useful for handling every AWS
request type in a generic way.AWSRequest
a
addAWSRequestHook_ :: (AWSRequest a, Typeable a) => Hook_ a -> Hook_ a -> Hook_ a Source #
addAWSRequestHook_
is addAWSRequestHook
but for Hook_
s.
addHookFor :: forall a b. (Typeable a, Typeable b) => Hook a -> Hook b -> Hook b Source #
addHookFor @a newHook oldHook
When a
and b
are the same
type, run the given 'Hook a' after all others, otherwise only run
the existing hooks.
-- Example: RungetObjectRequestHook
on anything that is aGetObjectRequest
: requestHook (addHookFor @GetObjectRequest getObjectRequestHook) :: Hooks -> Hooks
addHookFor_ :: forall a b. (Typeable a, Typeable b) => Hook_ a -> Hook_ b -> Hook_ b Source #
When a
and b
are the same type, run the given 'Hook_ a' after
all other hooks have run.
-- Example: RunaSignedRequestHook
on anything that is aSigned GetObjectRequest
: requestHook (addHookFor_ @(Signed GetObjectRequest) aSignedRequestHook) :: Hooks -> Hooks
removeHooksFor :: forall a b. (Typeable a, Typeable b) => Hook b -> Hook b Source #
When a
and b
are the same type, do not call any more hooks.
-- Example: Prevent any request hooks from running against a PutObjectRequest
:
requestHook (removeHooksFor @PutObjectRequest) :: Hooks -> Hooks
removeHooksFor_ :: forall a b. (Typeable a, Typeable b) => Hook_ b -> Hook_ b Source #
When a
and b
are the same type, do not call any more hooks.
-- Example: Prevent any error hooks from running against errors caused by a PutObjectRequest
:
errorHook (removeHooksFor @(Finality, Request PutObjectRequest, Error)) :: Hooks -> Hooks
Specialty combinators
silenceError :: Getting Any Error e -> Hook_ (Finality, Request a, Error) -> Hook_ (Finality, Request a, Error) Source #
Run the wrapped hook unless the given Fold
or Traversal
matches the error. You will probably want to use this with the
error matchers defined by each service binding, allowing you to
selectively silence specific errors:
-- Assuming `env :: Amazonka.Env` and `putRequest :: DynamoDB.PutRequest`, -- this silences a single type of error for a single call: send (env & #hooks %~ errorHook (silenceError DynamoDB._ConditionalCheckFailedException))
silenceError
:: Getter Error e ->Hook_
(Finality
, Request a, Error) ->Hook_
(Finality
, Request a, Error)silenceError
:: Fold Error e ->Hook_
(Finality
, Request a, Error) ->Hook_
(Finality
, Request a, Error)silenceError
:: Iso' Error e ->Hook_
(Finality
, Request a, Error) ->Hook_
(Finality
, Request a, Error)silenceError
:: Lens' Error e ->Hook_
(Finality
, Request a, Error) ->Hook_
(Finality
, Request a, Error)silenceError
:: Traversal' Error e ->Hook_
(Finality
, Request a, Error) ->Hook_
(Finality
, Request a, Error)
Building Hooks
addLoggingHooks :: Hooks -> Hooks Source #