amazonka-2.0: Comprehensive Amazon Web Services SDK.
Copyright(c) 2023 Brendan Hay
LicenseMozilla Public License, v. 2.0.
MaintainerBrendan Hay <brendan.g.hay+amazonka@gmail.com>
Stabilityhighly experimental
Portabilitynon-portable (GHC extensions)
Safe HaskellNone
LanguageHaskell2010

Amazonka.Env.Hooks

Description

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, ScopedTypeVariables, TypeApplications #-}
  import Amazonka
  import Amazonka.Env.Hooks
  import Data.Generics.Labels ()
  import Data.Typeable (typeRep)

  main :: IO ()
  main = do
    -- Use typeRep to capture a TypeRep of the request type,
    -- so we know the request type to log. Note the `(req :: req)`
    -- argument to the lambda, which captures the type of req as
    -- a type variable req (needs `-XScopedTypeVariables`).
    env <- newEnv discover
      <&> #hooks %~ requestHook
        (addRequestHook $ \_env (req :: req) -> req <$ logRequest (typeRep (Proxy @req)))
    ...

  logRequest :: AWSRequest a => a -> IO ()
  logRequest = ...
  
  {-# LANGAUGE OverloadedLabels #-}
  import Amazonka
  import Amazonka.Env.Hooks
  import Data.Generics.Labels ()

  main :: IO ()
  main = do
    env <- newEnv discover
      <&> #hooks %~ configuredRequestHook
        (addConfiguredRequestHook $ \_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 in IO).
  addXRayIdHeader :: [Header] -> [Header]
  addXRayIdHeader = ...
  
  • Selectively silence certain expected errors, such as DynamoDB's ConditionalCheckFailedException (errorHook and silenceError)
  {-# 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 ...)
    ...
  

Function Conventions

  • Functions named ...Hook (requestHook, etc.) are intended for use with lens operators: partially apply them to get a function Hooks -> Hooks that can go on the RHS of (%~) (the lens modify function). Use functions like addRequestHookFor to selectively adjust the hook at a particular field.

Request/Response Flow

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

Documentation

type Hook a = forall (withAuth :: Type -> Type). Env' withAuth -> a -> IO a Source #

A hook that returns an updated version of its arguments.

Since: 2.0

type Hook_ a = forall (withAuth :: Type -> Type). Env' withAuth -> a -> IO () Source #

A hook that cannot return an updated version of its argument.

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.

Since: 2.0

data Hooks Source #

The collection of lifecycle hooks stored in an Env.

Since: 2.0

Constructors

Hooks 

Fields

data Finality Source #

Indicates whether an error hook is potentially going to be retried.

See: error

Since: 2.0

Constructors

NotFinal 
Final 

Instances

Instances details
Bounded Finality Source # 
Instance details

Defined in Amazonka.Env.Hooks

Enum Finality Source # 
Instance details

Defined in Amazonka.Env.Hooks

Generic Finality Source # 
Instance details

Defined in Amazonka.Env.Hooks

Associated Types

type Rep Finality 
Instance details

Defined in Amazonka.Env.Hooks

type Rep Finality = D1 ('MetaData "Finality" "Amazonka.Env.Hooks" "amazonka-2.0-2bCDfcageaLEnU0WAFn9CF" 'False) (C1 ('MetaCons "NotFinal" 'PrefixI 'False) (U1 :: Type -> Type) :+: C1 ('MetaCons "Final" 'PrefixI 'False) (U1 :: Type -> Type))

Methods

from :: Finality -> Rep Finality x #

to :: Rep Finality x -> Finality #

Show Finality Source # 
Instance details

Defined in Amazonka.Env.Hooks

Eq Finality Source # 
Instance details

Defined in Amazonka.Env.Hooks

Ord Finality Source # 
Instance details

Defined in Amazonka.Env.Hooks

type Rep Finality Source # 
Instance details

Defined in Amazonka.Env.Hooks

type Rep Finality = D1 ('MetaData "Finality" "Amazonka.Env.Hooks" "amazonka-2.0-2bCDfcageaLEnU0WAFn9CF" 'False) (C1 ('MetaCons "NotFinal" 'PrefixI 'False) (U1 :: Type -> Type) :+: C1 ('MetaCons "Final" 'PrefixI 'False) (U1 :: Type -> Type))

Updating members of Hooks

requestHook :: (forall a. AWSRequest a => Hook a -> Hook a) -> Hooks -> Hooks Source #

Since: 2.0

waitHook :: (forall a. AWSRequest a => Hook (Wait a) -> Hook (Wait a)) -> Hooks -> Hooks Source #

Since: 2.0

configuredRequestHook :: (forall a. AWSRequest a => Hook (Request a) -> Hook (Request a)) -> Hooks -> Hooks Source #

Since: 2.0

signedRequestHook :: (forall a. AWSRequest a => Hook_ (Signed a) -> Hook_ (Signed a)) -> Hooks -> Hooks Source #

Since: 2.0

clientResponseHook :: (forall a. AWSRequest a => Hook_ (Request a, ClientResponse ()) -> Hook_ (Request a, ClientResponse ())) -> Hooks -> Hooks Source #

Since: 2.0

awaitRetryHook :: (forall a. AWSRequest a => Hook_ (Request a, Wait a, Accept, RetryStatus) -> Hook_ (Request a, Wait a, Accept, RetryStatus)) -> Hooks -> Hooks Source #

Since: 2.0

errorHook :: (forall a. AWSRequest a => Hook_ (Finality, Request a, Error) -> Hook_ (Finality, Request a, Error)) -> Hooks -> Hooks Source #

Since: 2.0

Composing hooks

Functions for specific field hooks

requestHook

addRequestHook :: AWSRequest a => Hook a -> Hook a -> Hook a Source #

Add a hook for every AWS request type. Designed to be used with requestHook.

-- Example: A hook modification that logs every AWS request,
-- where `logRequest :: AWSRequest a => Hook a`.
requestHook (addRequestHook logRequest) :: Hooks -> Hooks

Since: 2.1

addRequestHookFor :: (AWSRequest a, AWSRequest b) => Hook a -> Hook b -> Hook b Source #

Add a hook for one specific AWS request type. Designed to be used with requestHook.

-- Example: Run getObjectRequestHook on anything that is a GetObjectRequest
-- Assumes getObjectRequestHook :: Hook GetObjectRequest
requestHook (addRequestHookFor getObjectRequestHook) :: Hooks -> Hooks

Since: 2.1

removeRequestHooksFor :: (AWSRequest a, AWSRequest b) => Hook b -> Hook b Source #

Remove all hooks for one AWS request type. Designed to be used with requestHook.

-- Example: Don't run request hooks on a GetObjectRequest:
requestHook (removeRequestHooksFor @GetObjectRequest) :: Hooks -> Hooks

Since: 2.1

configuredRequestHook

addConfiguredRequestHook :: AWSRequest a => Hook (Request a) -> Hook (Request a) -> Hook (Request a) Source #

Add a hook for every configured AWS request type. Designed to be used with configuredRequestHook.

Since: 2.1

addConfiguredRequestHookFor :: (AWSRequest a, AWSRequest b) => Hook (Request a) -> Hook (Request b) -> Hook (Request b) Source #

Add a hook for one specific configured AWS request type. Designed to be used with configuredRequestHook.

Since: 2.1

removeConfiguredRequestHooksFor :: (AWSRequest a, AWSRequest b) => Hook (Request b) -> Hook (Request b) Source #

Remove all hooks for one specific type of configured request. Designed to be used with configuredRequestHook.

Since: 2.1

waitHook

addWaitHook :: AWSRequest a => Hook (Wait a) -> Hook (Wait a) -> Hook (Wait a) Source #

Add a hook for every Wait a configuration. Designed to be used with waitHook.

Since: 2.1

addWaitHookFor :: (AWSRequest a, AWSRequest b) => Hook (Wait a) -> Hook (Wait b) -> Hook (Wait b) Source #

Add a hook for one specific Wait a configuration. Designed to be used with waitHook.

Since: 2.1

removeWaitHooksFor :: (AWSRequest a, AWSRequest b) => Hook (Wait b) -> Hook (Wait b) Source #

Remove all hooks for one specific type of Wait a. Designed to be used with waitHook.

Since: 2.1

signedRequestHook

addSignedRequestHook :: AWSRequest a => Hook_ (Signed a) -> Hook_ (Signed a) -> Hook_ (Signed a) Source #

Add a hook for every Signed a request. Designed to be used with signedRequestHook.

Since: 2.1

addSignedRequestHookFor :: (AWSRequest a, AWSRequest b) => Hook_ (Signed a) -> Hook_ (Signed b) -> Hook_ (Signed b) Source #

Add a hook for one specific Signed a request type. Designed to be used with signedRequestHook.

Since: 2.1

removeSignedRequestHooksFor :: (AWSRequest a, AWSRequest b) => Hook_ (Signed b) -> Hook_ (Signed b) Source #

Remove all hooks for one specific type of Signed a request. Designed to be used with signedRequestHook.

Since: 2.1

clientRequestHook

addClientRequestHook :: Hook ClientRequest -> Hook ClientRequest -> Hook ClientRequest Source #

Add a hook for ClientRequest. This is an alias for addHook, provided for consistency and designed to be used with clientRequestHook.

Since: 2.1

removeClientRequestHooks :: Hook ClientRequest -> Hook ClientRequest Source #

Remove all hooks for ClientRequest. This is an alias for noHook, provided for consistency and designed to be used with clientRequestHook.

Since: 2.1

clientResponseHook

addClientResponseHook :: AWSRequest a => Hook_ (Request a, ClientResponse ()) -> Hook_ (Request a, ClientResponse ()) -> Hook_ (Request a, ClientResponse ()) Source #

Add a hook for every type of client response. Designed to be used with clientResponseHook.

Since: 2.1

addClientResponseHookFor :: (AWSRequest a, AWSRequest b) => Hook_ (Request a, ClientResponse ()) -> Hook_ (Request b, ClientResponse ()) -> Hook_ (Request b, ClientResponse ()) Source #

Add a hook for one specific type of client response. Designed to be used with clientResponseHook.

Since: 2.1

removeClientResponseHooksFor :: (AWSRequest a, AWSRequest b) => Hook_ (Request b, ClientResponse ()) -> Hook_ (Request b, ClientResponse ()) Source #

Remove all hooks for one specific type of client response. Designed to be used with clientResponseHook.

Since: 2.1

rawResponseBodyHook

addRawResponseBodyHook :: Hook ByteStringLazy -> Hook ByteStringLazy -> Hook ByteStringLazy Source #

Add a hook for the raw response body. This is an alias for addHook, provided for consistency and designed to be used with rawResponseBodyHook.

Since: 2.1

removeRawResponseBodyHooks :: Hook ByteStringLazy -> Hook ByteStringLazy Source #

Remove all hooks for the raw response body. This is an alias for noHook, provided for consistency and designed to be used with rawResponseBodyHook.

Since: 2.1

requestRetryHook

addRequestRetryHook :: AWSRequest a => Hook_ (Request a, Text, RetryStatus) -> Hook_ (Request a, Text, RetryStatus) -> Hook_ (Request a, Text, RetryStatus) Source #

Add a request retry hook for every AWS request type. Designed to be used with requestRetryHook.

Since: 2.1

addRequestRetryHookFor :: (AWSRequest a, AWSRequest b) => Hook_ (Request a, Text, RetryStatus) -> Hook_ (Request b, Text, RetryStatus) -> Hook_ (Request b, Text, RetryStatus) Source #

Add a request retry hook for one specific AWS request type. Designed to be used with requestRetryHook.

Since: 2.1

removeRequestRetryHooksFor :: (AWSRequest a, AWSRequest b) => Hook_ (Request b, Text, RetryStatus) -> Hook_ (Request b, Text, RetryStatus) Source #

Remove all request retry hooks for one specific AWS request type. Designed to be used with requestRetryHook.

Since: 2.1

awaitRetryHook

addAwaitRetryHook :: AWSRequest a => Hook_ (Request a, Wait a, Accept, RetryStatus) -> Hook_ (Request a, Wait a, Accept, RetryStatus) -> Hook_ (Request a, Wait a, Accept, RetryStatus) Source #

Add an await retry hook for every AWS request type. Designed to be used with awaitRetryHook.

Since: 2.1

addAwaitRetryHookFor :: (AWSRequest a, AWSRequest b) => Hook_ (Request a, Wait a, Accept, RetryStatus) -> Hook_ (Request b, Wait b, Accept, RetryStatus) -> Hook_ (Request b, Wait b, Accept, RetryStatus) Source #

Add an await retry hook for a specific AWS request type. Designed to be used with awaitRetryHook.

Since: 2.1

removeAwaitRetryHooksFor :: (AWSRequest a, AWSRequest b) => Hook_ (Request b, Wait b, Accept, RetryStatus) -> Hook_ (Request b, Wait b, Accept, RetryStatus) Source #

Remove all await retry hooks for one specific AWS request type. Designed to be used with awaitRetryHook.

Since: 2.1

responseHook

addResponseHook :: AWSRequest a => Hook_ (Request a, ClientResponse (AWSResponse a)) -> Hook_ (Request a, ClientResponse (AWSResponse a)) -> Hook_ (Request a, ClientResponse (AWSResponse a)) Source #

Add a repsonse hook for every AWS request type. Designed to be used with responseHook.

Since: 2.1

addResponseHookFor :: (AWSRequest a, AWSRequest b) => Hook_ (Request a, ClientResponse (AWSResponse a)) -> Hook_ (Request b, ClientResponse (AWSResponse b)) -> Hook_ (Request b, ClientResponse (AWSResponse b)) Source #

Add a repsonse hook for one specific AWS request type. Designed to be used with responseHook.

Since: 2.1

removeResponseHooksFor :: (AWSRequest a, AWSRequest b) => Hook_ (Request b, ClientResponse (AWSResponse b)) -> Hook_ (Request b, ClientResponse (AWSResponse b)) Source #

Remove all response hooks for one specific AWS request type. Designed to be used with responseHook.

Since: 2.1

errorHook

addErrorHook :: AWSRequest a => Hook_ (Finality, Request a, Error) -> Hook_ (Finality, Request a, Error) -> Hook_ (Finality, Request a, Error) Source #

Add an error hook for every AWS request type. Designed to be used with errorHook.

Since: 2.1

addErrorHookFor :: (AWSRequest a, AWSRequest b) => Hook_ (Finality, Request a, Error) -> Hook_ (Finality, Request b, Error) -> Hook_ (Finality, Request b, Error) Source #

Add an error hook for one specific AWS request type. Designed to be used with errorHook.

Since: 2.1

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)

Since: 2.0

removeErrorHooksFor :: (AWSRequest a, AWSRequest b) => Hook_ (Finality, Request b, Error) -> Hook_ (Finality, Request b, Error) Source #

Remove all error hooks for one specific AWS request type. Designed to be used with errorHook.

Since: 2.1

Functions for any hook

noHook :: Hook a -> Hook a Source #

Turn a Hook a into another Hook a that does nothing.

-- Example: remove all request hooks:
requestHook noHook :: Hooks -> Hooks

Since: 2.0

noHook_ :: Hook_ a -> Hook_ a Source #

Turn a Hook_ a into another Hook_ a that does nothing.

-- Example: Remove all response hooks:
responseHook noHook_ :: Hooks -> Hooks

Since: 2.0

Building Hooks

addLoggingHooks :: Hooks -> Hooks Source #

Add default logging hooks. The default Env' from newEnv already has logging hooks installed, so you probably only want this if you are building your own Hooks from scratch.

Since: 2.0

noHooks :: Hooks Source #

Empty Hooks structure which returns everything unmodified.

Since: 2.0

Deprecated functions

addHook :: Hook a -> Hook a -> Hook a Source #

Deprecated: this function will be internal in Amazonka 2.2

Unconditionally add a Hook a to the chain of hooks. If you need to do something with specific request types, you want addHookFor, instead.

NOTE: This function has been deprecated because it's almost impossible to do anything useful with. Instead, you should use one of the hook-specific functions in this module.

Since: 2.0

addHook_ :: Hook_ a -> Hook_ a -> Hook_ a Source #

Deprecated: this function will be internal in Amazonka 2.2

Unconditionally add a Hook_ a to the chain of hooks. If you need to do something with specific request types, you want addHookFor_, instead.

NOTE: This function has been deprecated because it's almost impossible to do anything useful with. Instead, you should use one of the hook-specific functions in this module.

Since: 2.0

addAWSRequestHook :: AWSRequest a => Hook a -> Hook a -> Hook a Source #

Deprecated: this function will be removed in Amazonka 2.2

Like addHook, adds an unconditional hook, but it also captures the AWSRequest a constraint.

NOTE: This function has been deprecated because it's very easy to accidentally write code which typechecks but never fires the new hook. Instead, you should use one of the hook-specific functions in this module.

Since: 2.0

addAWSRequestHook_ :: AWSRequest a => Hook_ a -> Hook_ a -> Hook_ a Source #

Deprecated: this function will be removed in Amazonka 2.2

addAWSRequestHook_ is addAWSRequestHook but for Hook_s.

NOTE: This function has been deprecated because it's very easy to accidentally write code which typechecks but never fires the new hook. Instead, you should use one of the hook-specific functions in this module.

Since: 2.0

addHookFor :: (Typeable a, Typeable b) => Hook a -> Hook b -> Hook b Source #

Deprecated: this function will be internal in Amazonka 2.2

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: Run getObjectRequestHook on anything that is a GetObjectRequest:
requestHook (addHookFor @GetObjectRequest getObjectRequestHook) :: Hooks -> Hooks

NOTE: This function has been deprecated because it's very easy to accidentally write code which typechecks but never fires the new hook. Instead, you should use one of the hook-specific functions in this module.

Since: 2.0

addHookFor_ :: (Typeable a, Typeable b) => Hook_ a -> Hook_ b -> Hook_ b Source #

Deprecated: this function will be internal in Amazonka 2.2

When a and b are the same type, run the given 'Hook_ a' after all other hooks have run.

-- Example: Run aSignedRequestHook on anything that is a Signed GetObjectRequest:
requestHook (addHookFor_ @(Signed GetObjectRequest) aSignedRequestHook) :: Hooks -> Hooks

NOTE: This function has been deprecated because it's very easy to accidentally write code which typechecks but never fires the new hook. Instead, you should use one of the hook-specific functions in this module.

Since: 2.0

removeHooksFor :: (Typeable a, Typeable b) => Hook b -> Hook b Source #

Deprecated: this function will be internal in Amazonka 2.2

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

NOTE: This function has been deprecated because it's very easy to accidentally write code which typechecks but never suppresses the hook. Instead, you should use one of the hook-specific functions in this module.

Since: 2.0

removeHooksFor_ :: (Typeable a, Typeable b) => Hook_ b -> Hook_ b Source #

Deprecated: this function will be internal in Amazonka 2.2

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

NOTE: This function has been deprecated because it's very easy to accidentally write code which typechecks but never suppresses the hook. Instead, you should use one of the hook-specific functions in this module.

Since: 2.0