-- Hoogle documentation, generated by Haddock
-- See Hoogle, http://www.haskell.org/hoogle/


-- | Servant combinators to facilitate writing GitHub webhooks.
--   
--   This package provides servant combinators that make writing safe
--   GitHub webhooks very simple.
--   
--   It features automatic verification of the digital signatures provided
--   by GitHub in the webhook HTTP requests as well as route dispatching
--   based on repository event type.
@package servant-github-webhook
@version 0.4.1.0


-- | The GitHub webhook machinery will attach three headers to the HTTP
--   requests that it fires: <tt>X-Github-Event</tt>,
--   <tt>X-Hub-Signature</tt>, and <tt>X-Github-Delivery</tt>. The former
--   two headers correspond with the <a>GitHubEvent</a> and
--   <a>GitHubSignedReqBody''</a> routing combinators. This library ignores
--   the <tt>X-Github-Delivery</tt> header; if you would like to access its
--   value, then use the builtin <a>Header</a> combinator from Servant.
--   
--   Usage of the library is straightforward: protect routes with the
--   <a>GitHubEvent</a> combinator to ensure that the route is only reached
--   for specific <a>RepoWebhookEvent</a>s, and replace any <a>ReqBody</a>
--   combinators you would write under that route with
--   <a>GitHubSignedReqBody</a>. It is advised to always include a
--   <a>GitHubSignedReqBody''</a>, as this is the only way you can be sure
--   that it is GitHub who is sending the request, and not a malicious
--   user. If you don't care about the request body, then simply use
--   Aeson's <a>Object</a> type as the deserialization target --
--   <tt>GitHubSignedReqBody' key '[JSON] Object</tt> -- and ignore the
--   <tt>Object</tt> in the handler.
--   
--   The <a>GitHubSignedReqBody''</a> combinator makes use of the Servant
--   <a>Context</a> in order to extract the signing key. This is the same
--   key that must be entered in the configuration of the webhook on
--   GitHub. See <a>GitHubKey'</a> for more details.
--   
--   In order to support multiple keys on a per-route basis, the basic
--   combinator <tt>GitHubSignedReqBody''</tt> takes as a type parameter as
--   a key index. To use this, create a datatype, e.g. <tt>KeyIndex</tt>
--   whose constructors identify the different keys you will be using.
--   Generally, this means one constructor per repository. Use the
--   <tt>DataKinds</tt> extension to promote this datatype to a kind, and
--   write an instance of <a>Reflect</a> for each promoted constructor of
--   your datatype. Finally, create a <a>Context</a> containing
--   <a>GitHubKey'</a> whose wrapped function's domain is the datatype
--   you've built up. Thus, your function can determine which key to
--   retrieve.
module Servant.GitHub.Webhook

-- | A clone of Servant's <a>ReqBody</a> combinator, except that it will
--   also verify the signature provided by GitHub in the
--   <tt>X-Hub-Signature</tt> header by computing the SHA1 HMAC of the
--   request body and comparing.
--   
--   The use of this combinator will require that the router context
--   contain an appropriate <a>GitHubKey'</a> entry. Specifically, the type
--   parameter of <a>GitHubKey'</a> must correspond with <tt>Demote k</tt>
--   where <tt>k</tt> is the kind of the index <tt>key</tt> used here.
--   Consequently, it will be necessary to use <a>serveWithContext</a>
--   instead of <a>serve</a>.
--   
--   Other routes are not tried upon the failure of this combinator, and a
--   401 response is generated.
--   
--   Use of this datatype directly is discouraged, since the choice of the
--   index <tt>key</tt> determines its kind <tt>k</tt> and hence
--   <tt>proxy</tt>, which is . Instead, use <a>GitHubSignedReqBody'</a>,
--   which computes the <tt>proxy</tt> argument given just <tt>key</tt>.
--   The proxy argument is necessary to avoid <tt>UndecidableInstances</tt>
--   for the implementation of the <a>HasServer</a> instance for the
--   datatype.
data GitHubSignedReqBody'' (proxy :: KProxy k) (key :: k) (list :: [*]) (result :: *)

-- | Convenient synonym for <a>GitHubSignedReqBody''</a> that computes its
--   first type argument given just the second one.
--   
--   Use this type synonym if you are creating a webhook server to handle
--   webhooks from multiple repositories, with different secret keys.
type GitHubSignedReqBody' (key :: k) = GitHubSignedReqBody'' ( 'KProxy :: KProxy k) key

-- | A convenient alias for a trivial key index.
--   
--   USe this type synonym if you are creating a webhook server to handle
--   only webhooks from a single repository, or for mutliple repositories
--   using the same secret key.
type GitHubSignedReqBody = GitHubSignedReqBody'  '()

-- | A routing combinator that succeeds only for a webhook request that
--   matches one of the given <a>RepoWebhookEvent</a> given in the
--   type-level list <tt>events</tt>.
--   
--   If the list contains <a>WebhookWildcardEvent</a>, then all events will
--   be matched.
--   
--   The combinator will require that its associated handler take a
--   <a>RepoWebhookEvent</a> parameter, and the matched event will be
--   passed to the handler. This allows the handler to determine which
--   event triggered it from the list.
--   
--   Other routes are tried if there is a mismatch.
data GitHubEvent (events :: [RepoWebhookEvent])

-- | A wrapper for an IO strategy to obtain the signing key for the webhook
--   as configured in GitHub. The strategy is executed each time the
--   <a>GitHubSignedReqBody'</a>s routing logic is executed.
--   
--   We allow the use of <tt>IO</tt> here so that you can fetch the key
--   from a cache or a database. If the key is a constant or read only
--   once, just use <a>pure</a>.
--   
--   The type <tt>key</tt> used here must correspond with <tt><a>Demote</a>
--   k</tt> where <tt>k</tt> is the kind whose types are used as indices in
--   <a>GitHubSignedReqBody'</a>.
--   
--   If you don't care about indices and just want to write a webhook using
--   a global key, see <a>GitHubKey</a> which fixes <tt>key</tt> to
--   <tt>()</tt> and use <a>gitHubKey</a>, which fills the newtype with a
--   constant function.
newtype GitHubKey' key result
GitHubKey :: (key -> result -> IO (Maybe ByteString)) -> GitHubKey' key result
[unGitHubKey] :: GitHubKey' key result -> key -> result -> IO (Maybe ByteString)

-- | A synonym for strategies producing so-called <i>global</i> keys, in
--   which the key index is simply <tt>()</tt>.
type GitHubKey result = GitHubKey' () result

-- | Smart constructor for <a>GitHubKey</a>, for a so-called <i>global</i>
--   key.
gitHubKey :: IO ByteString -> GitHubKey result

-- | <tt>dynamicKey keyLookup keyIdLookup</tt> acquires the key identifier,
--   such as repository or user name, from the result then uses
--   <tt>keyLookup</tt> to acquire the key (or <tt>Nothing</tt>).
--   
--   Dynamic keys allow servers to specify per-user repository keys. This
--   limits the impact of compromized keys and allows the server to acquire
--   the key from external sources, such as a live configuration or
--   per-user rows in a database.
dynamicKey :: (Text -> IO (Maybe ByteString)) -> (result -> Maybe Text) -> GitHubKey result
repositoryKey :: HasRepository result => (Text -> IO (Maybe ByteString)) -> GitHubKey result

-- | The HasRepository class helps extract the full (unique) "name/repo" of
--   a repository, allowing easy lookup of a per-repository key or, using
--   <tt>takeWhile (<i>='</i>')</tt>, lookup of per user keys.
class HasRepository r

-- | For use with 'github-webhooks' package types. Routes would look like:
--   
--   <pre>
--   api = "github-webevent" :&gt; 
--       :&gt; GitHubSignedReqBody '[JSON] (EventWithHookRepo IssuesEvent)
--       :&gt; Post '[JSON] ()
--   </pre>
--   
--   And the handler would unwrap the event:
--   
--   <pre>
--   handler :: EventWithHookRepo IssuesEvent -&gt; Handler ()
--   handler (eventOf -&gt; e) = -- ... expr handling e :: IssuesEvent ...
--   </pre>
newtype EventWithHookRepo e
EventWithHookRepo :: e -> EventWithHookRepo e
[eventOf] :: EventWithHookRepo e -> e
data RepoWebhookEvent :: *
WebhookWildcardEvent :: RepoWebhookEvent
WebhookCommitCommentEvent :: RepoWebhookEvent
WebhookCreateEvent :: RepoWebhookEvent
WebhookDeleteEvent :: RepoWebhookEvent
WebhookDeploymentEvent :: RepoWebhookEvent
WebhookDeploymentStatusEvent :: RepoWebhookEvent
WebhookForkEvent :: RepoWebhookEvent
WebhookGollumEvent :: RepoWebhookEvent
WebhookIssueCommentEvent :: RepoWebhookEvent
WebhookIssuesEvent :: RepoWebhookEvent
WebhookMemberEvent :: RepoWebhookEvent
WebhookPageBuildEvent :: RepoWebhookEvent
WebhookPingEvent :: RepoWebhookEvent
WebhookPublicEvent :: RepoWebhookEvent
WebhookPullRequestReviewCommentEvent :: RepoWebhookEvent
WebhookPullRequestEvent :: RepoWebhookEvent
WebhookPushEvent :: RepoWebhookEvent
WebhookReleaseEvent :: RepoWebhookEvent
WebhookStatusEvent :: RepoWebhookEvent
WebhookTeamAddEvent :: RepoWebhookEvent
WebhookWatchEvent :: RepoWebhookEvent

-- | A concrete, promotable proxy type, for use at the kind level There are
--   no instances for this because it is intended at the kind level only
data KProxy t :: * -> *
KProxy :: KProxy t

-- | Convient alias for <a>Demote'</a> that allows us to avoid using
--   <a>KProxy</a> explicitly.
type Demote (a :: k) = Demote' ( 'KProxy :: KProxy k)

-- | Type function that reflects a kind to a type.

-- | Class of types that can be reflected to values.
class Reflect (a :: k)
reflect :: Reflect a => Proxy (a :: k) -> Demote a

-- | Helper that parses a header using a <a>FromHttpApiData</a> instance
--   and discards the parse error message if any.
parseHeaderMaybe :: FromHttpApiData a => ByteString -> Maybe a

-- | Determines whether a given webhook event matches a given raw
--   representation of one. The result is <a>Nothing</a> if there is no
--   match. This function accounts for the <a>WebhookWildcardEvent</a>
--   matching everything, so it returns the result of parsing the raw
--   representation when trying to match against the wildcard.
matchEvent :: RepoWebhookEvent -> ByteString -> Maybe RepoWebhookEvent
instance forall k sublayout (context :: [*]) (key :: k) result (list :: [*]). (Servant.Server.Internal.HasServer sublayout context, Servant.Server.Internal.Context.HasContextEntry context (Servant.GitHub.Webhook.GitHubKey' (Servant.GitHub.Webhook.Demote key) result), Servant.GitHub.Webhook.Reflect key, Servant.API.ContentTypes.AllCTUnrender list result) => Servant.Server.Internal.HasServer (Servant.GitHub.Webhook.GitHubSignedReqBody'' 'Data.Proxy.KProxy key list result Servant.API.Sub.:> sublayout) context
instance (Servant.GitHub.Webhook.Reflect events, Servant.Server.Internal.HasServer sublayout context) => Servant.Server.Internal.HasServer (Servant.GitHub.Webhook.GitHubEvent events Servant.API.Sub.:> sublayout) context
instance GHC.TypeLits.KnownSymbol s => Servant.GitHub.Webhook.Reflect s
instance Servant.GitHub.Webhook.Reflect '()
instance Servant.GitHub.Webhook.Reflect '[]
instance forall a (x :: a) (xs :: [a]). (Servant.GitHub.Webhook.Reflect x, Servant.GitHub.Webhook.Reflect xs) => Servant.GitHub.Webhook.Reflect (x : xs)
instance Servant.GitHub.Webhook.Reflect 'GitHub.Data.Webhooks.WebhookWildcardEvent
instance Servant.GitHub.Webhook.Reflect 'GitHub.Data.Webhooks.WebhookCommitCommentEvent
instance Servant.GitHub.Webhook.Reflect 'GitHub.Data.Webhooks.WebhookCreateEvent
instance Servant.GitHub.Webhook.Reflect 'GitHub.Data.Webhooks.WebhookDeleteEvent
instance Servant.GitHub.Webhook.Reflect 'GitHub.Data.Webhooks.WebhookDeploymentEvent
instance Servant.GitHub.Webhook.Reflect 'GitHub.Data.Webhooks.WebhookDeploymentStatusEvent
instance Servant.GitHub.Webhook.Reflect 'GitHub.Data.Webhooks.WebhookForkEvent
instance Servant.GitHub.Webhook.Reflect 'GitHub.Data.Webhooks.WebhookGollumEvent
instance Servant.GitHub.Webhook.Reflect 'GitHub.Data.Webhooks.WebhookIssueCommentEvent
instance Servant.GitHub.Webhook.Reflect 'GitHub.Data.Webhooks.WebhookIssuesEvent
instance Servant.GitHub.Webhook.Reflect 'GitHub.Data.Webhooks.WebhookMemberEvent
instance Servant.GitHub.Webhook.Reflect 'GitHub.Data.Webhooks.WebhookPageBuildEvent
instance Servant.GitHub.Webhook.Reflect 'GitHub.Data.Webhooks.WebhookPingEvent
instance Servant.GitHub.Webhook.Reflect 'GitHub.Data.Webhooks.WebhookPublicEvent
instance Servant.GitHub.Webhook.Reflect 'GitHub.Data.Webhooks.WebhookPullRequestReviewCommentEvent
instance Servant.GitHub.Webhook.Reflect 'GitHub.Data.Webhooks.WebhookPullRequestEvent
instance Servant.GitHub.Webhook.Reflect 'GitHub.Data.Webhooks.WebhookPushEvent
instance Servant.GitHub.Webhook.Reflect 'GitHub.Data.Webhooks.WebhookReleaseEvent
instance Servant.GitHub.Webhook.Reflect 'GitHub.Data.Webhooks.WebhookStatusEvent
instance Servant.GitHub.Webhook.Reflect 'GitHub.Data.Webhooks.WebhookTeamAddEvent
instance Servant.GitHub.Webhook.Reflect 'GitHub.Data.Webhooks.WebhookWatchEvent
instance Data.Aeson.Types.FromJSON.FromJSON e => Data.Aeson.Types.FromJSON.FromJSON (Servant.GitHub.Webhook.EventWithHookRepo e)
instance GitHub.Data.Webhooks.Events.EventHasRepo e => Servant.GitHub.Webhook.HasRepository (Servant.GitHub.Webhook.EventWithHookRepo e)
instance Servant.GitHub.Webhook.HasRepository Data.Aeson.Types.Internal.Value
instance Servant.GitHub.Webhook.HasRepository Data.Aeson.Types.Internal.Object
