{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeFamilies #-}
{-# OPTIONS_GHC -Wno-orphans #-}

-- | The API's route types for queue operations and remote job consumers, generated from the registry.
module Arbiter.Servant.API
  ( -- * Route tree
    ArbiterAPI
  , RegistryToAPI
  , SharedAPI

    -- * Route descriptions
  , PayloadEditNote
  , LeaseRefusal
  , Throws'
  , Throws
  , ThrowsBody

    -- * Per-queue routes
  , TableAPI (..)
  , JobsAPI (..)
  , DLQAPI (..)
  , ArchiveAPI (..)
  , StatsAPI (..)

    -- * Shared routes
  , QueuesAPI (..)
  , MaintenanceAPI (..)
  , EventsAPI
  , CronAPI (..)
  , WorkersAPI (..)
  , HealthAPI (..)
  , RateLimitsAPI (..)
  , ConcurrencyAPI (..)

    -- * Sort parameters
  , JobSortColumn (..)
  , DLQSortColumn (..)
  , ArchiveSortColumn (..)
  , SortDir (..)
  ) where

import Arbiter.Core.Enum (enumFromTextCI)
import Arbiter.Core.Job.Types (JobRead, JobStatus, Stored, jobStatusToText)
import Arbiter.Core.QueueRegistry (JobPayloadRegistry, SpecName, SpecPayload, SpecResult)
import Arbiter.Core.Sql.Jobs
  ( ArchiveSortColumn (..)
  , DLQSortColumn (..)
  , JobSortColumn (..)
  , SortDir (..)
  , archiveSortColumnName
  , dlqSortColumnName
  , jobSortColumnName
  , sortDirName
  )
import Data.Int (Int64)
import Data.Kind (Type)
import Data.Proxy (Proxy (..))
import Data.Text (Text)
import Data.Time (UTCTime)
import Data.UUID.Types (UUID)
import GHC.Generics (Generic)
import GHC.TypeLits (Nat, Symbol)
import Servant.API
import Servant.Client.Core (HasClient (..))
import Servant.Server (HasServer (..))

import Arbiter.Servant.Types

instance FromHttpApiData JobSortColumn where
  parseQueryParam :: Text -> Either Text JobSortColumn
parseQueryParam = Text
-> (JobSortColumn -> Text) -> Text -> Either Text JobSortColumn
forall a.
(Bounded a, Enum a) =>
Text -> (a -> Text) -> Text -> Either Text a
enumFromTextCI Text
"job sort column" JobSortColumn -> Text
jobSortColumnName

instance ToHttpApiData JobSortColumn where
  toUrlPiece :: JobSortColumn -> Text
toUrlPiece = JobSortColumn -> Text
jobSortColumnName

instance FromHttpApiData DLQSortColumn where
  parseQueryParam :: Text -> Either Text DLQSortColumn
parseQueryParam = Text
-> (DLQSortColumn -> Text) -> Text -> Either Text DLQSortColumn
forall a.
(Bounded a, Enum a) =>
Text -> (a -> Text) -> Text -> Either Text a
enumFromTextCI Text
"DLQ sort column" DLQSortColumn -> Text
dlqSortColumnName

instance ToHttpApiData DLQSortColumn where
  toUrlPiece :: DLQSortColumn -> Text
toUrlPiece = DLQSortColumn -> Text
dlqSortColumnName

instance FromHttpApiData ArchiveSortColumn where
  parseQueryParam :: Text -> Either Text ArchiveSortColumn
parseQueryParam = Text
-> (ArchiveSortColumn -> Text)
-> Text
-> Either Text ArchiveSortColumn
forall a.
(Bounded a, Enum a) =>
Text -> (a -> Text) -> Text -> Either Text a
enumFromTextCI Text
"archive sort column" ArchiveSortColumn -> Text
archiveSortColumnName

instance ToHttpApiData ArchiveSortColumn where
  toUrlPiece :: ArchiveSortColumn -> Text
toUrlPiece = ArchiveSortColumn -> Text
archiveSortColumnName

instance FromHttpApiData SortDir where
  parseQueryParam :: Text -> Either Text SortDir
parseQueryParam = Text -> (SortDir -> Text) -> Text -> Either Text SortDir
forall a.
(Bounded a, Enum a) =>
Text -> (a -> Text) -> Text -> Either Text a
enumFromTextCI Text
"sort direction" SortDir -> Text
sortDirName

instance ToHttpApiData SortDir where
  toUrlPiece :: SortDir -> Text
toUrlPiece = SortDir -> Text
sortDirName

instance FromHttpApiData JobStatus where
  parseQueryParam :: Text -> Either Text JobStatus
parseQueryParam = Text -> (JobStatus -> Text) -> Text -> Either Text JobStatus
forall a.
(Bounded a, Enum a) =>
Text -> (a -> Text) -> Text -> Either Text a
enumFromTextCI Text
"job status" JobStatus -> Text
jobStatusToText

instance ToHttpApiData JobStatus where
  toUrlPiece :: JobStatus -> Text
toUrlPiece = JobStatus -> Text
jobStatusToText

-- | How the optional retry and re-enqueue body reads.
type PayloadEditNote =
  "Optional. A payload replaces the stored payload, and the kind, rate-limit and concurrency columns come from it again. An empty body keeps the stored payload."

-- | Why a lease route refuses the lease it was sent.
type LeaseRefusal =
  "This lease does not hold the job, a registered worker holds it, the job is suspended, or the job was force-cancelled."

-- | A documented error response with an optional JSON body. The server, client and links ignore it.
data Throws' (status :: Nat) (reason :: Symbol) (body :: Maybe Type)

-- | An error response with no declared content type.
type Throws status reason = Throws' status reason 'Nothing

-- | An error response with a JSON body of type @body@.
type ThrowsBody status reason body = Throws' status reason ('Just body)

instance (HasServer api context) => HasServer (Throws' status reason body :> api) context where
  type ServerT (Throws' status reason body :> api) m = ServerT api m
  route :: forall env.
Proxy (Throws' status reason body :> api)
-> Context context
-> Delayed env (Server (Throws' status reason body :> api))
-> Router env
route Proxy (Throws' status reason body :> api)
_ = Proxy api
-> Context context
-> Delayed env (Server api)
-> Router' env RoutingApplication
forall env.
Proxy api
-> Context context -> Delayed env (Server api) -> Router env
forall {k} (api :: k) (context :: [*]) env.
HasServer api context =>
Proxy api
-> Context context -> Delayed env (Server api) -> Router env
route (forall t. Proxy t
forall {k} (t :: k). Proxy t
Proxy @api)
  hoistServerWithContext :: forall (m :: * -> *) (n :: * -> *).
Proxy (Throws' status reason body :> api)
-> Proxy context
-> (forall x. m x -> n x)
-> ServerT (Throws' status reason body :> api) m
-> ServerT (Throws' status reason body :> api) n
hoistServerWithContext Proxy (Throws' status reason body :> api)
_ = Proxy api
-> Proxy context
-> (forall x. m x -> n x)
-> ServerT api m
-> ServerT api n
forall {k} (api :: k) (context :: [*]) (m :: * -> *) (n :: * -> *).
HasServer api context =>
Proxy api
-> Proxy context
-> (forall x. m x -> n x)
-> ServerT api m
-> ServerT api n
forall (m :: * -> *) (n :: * -> *).
Proxy api
-> Proxy context
-> (forall x. m x -> n x)
-> ServerT api m
-> ServerT api n
hoistServerWithContext (forall t. Proxy t
forall {k} (t :: k). Proxy t
Proxy @api)

instance (HasClient m api) => HasClient m (Throws' status reason body :> api) where
  type Client m (Throws' status reason body :> api) = Client m api
  clientWithRoute :: Proxy m
-> Proxy (Throws' status reason body :> api)
-> Request
-> Client m (Throws' status reason body :> api)
clientWithRoute Proxy m
pm Proxy (Throws' status reason body :> api)
_ = Proxy m -> Proxy api -> Request -> Client m api
forall (m :: * -> *) api.
HasClient m api =>
Proxy m -> Proxy api -> Request -> Client m api
clientWithRoute Proxy m
pm (forall t. Proxy t
forall {k} (t :: k). Proxy t
Proxy @api)
  hoistClientMonad :: forall (mon :: * -> *) (mon' :: * -> *).
Proxy m
-> Proxy (Throws' status reason body :> api)
-> (forall x. mon x -> mon' x)
-> Client mon (Throws' status reason body :> api)
-> Client mon' (Throws' status reason body :> api)
hoistClientMonad Proxy m
pm Proxy (Throws' status reason body :> api)
_ = Proxy m
-> Proxy api
-> (forall x. mon x -> mon' x)
-> Client mon api
-> Client mon' api
forall (m :: * -> *) api (mon :: * -> *) (mon' :: * -> *).
HasClient m api =>
Proxy m
-> Proxy api
-> (forall x. mon x -> mon' x)
-> Client mon api
-> Client mon' api
forall (mon :: * -> *) (mon' :: * -> *).
Proxy m
-> Proxy api
-> (forall x. mon x -> mon' x)
-> Client mon api
-> Client mon' api
hoistClientMonad Proxy m
pm (forall t. Proxy t
forall {k} (t :: k). Proxy t
Proxy @api)

instance (HasLink api) => HasLink (Throws' status reason body :> api) where
  type MkLink (Throws' status reason body :> api) link = MkLink api link
  toLink :: forall a.
(Link -> a)
-> Proxy (Throws' status reason body :> api)
-> Link
-> MkLink (Throws' status reason body :> api) a
toLink Link -> a
toA Proxy (Throws' status reason body :> api)
_ = (Link -> a) -> Proxy api -> Link -> MkLink api a
forall a. (Link -> a) -> Proxy api -> Link -> MkLink api a
forall {k} (endpoint :: k) a.
HasLink endpoint =>
(Link -> a) -> Proxy endpoint -> Link -> MkLink endpoint a
toLink Link -> a
toA (forall t. Proxy t
forall {k} (t :: k). Proxy t
Proxy @api)

-- | One queue's job routes.
data JobsAPI payload result mode = JobsAPI
  { forall {k} payload result (mode :: k).
JobsAPI payload result mode
-> mode
   :- (Summary "List jobs"
       :> (Description
             "Filters combine. The payload filter searches the payload text."
           :> (QueryParam "limit" PageLimit
               :> (QueryParam "offset" Int
                   :> (QueryParam "group_key" Text
                       :> (QueryParam "parent_id" Int64
                           :> (QueryParam "job_id" Int64
                               :> (QueryFlag "roots_only"
                                   :> (QueryParam "status" JobStatus
                                       :> (QueryParam "claimed_by" UUID
                                           :> (QueryParam "kind" Text
                                               :> (QueryParam "payload" Text
                                                   :> (QueryParam "rate_limit_prefix" Text
                                                       :> (QueryParam "concurrency_prefix" Text
                                                           :> (QueryParam "sort_by" JobSortColumn
                                                               :> (QueryParam "sort_dir" SortDir
                                                                   :> Get
                                                                        '[JSON]
                                                                        (JobsResponse
                                                                           payload)))))))))))))))))
listJobs
      :: mode
        :- Summary "List jobs"
          :> Description
               "Filters combine. The payload filter searches the payload text."
          :> QueryParam "limit" PageLimit
          :> QueryParam "offset" Int
          :> QueryParam "group_key" Text
          :> QueryParam "parent_id" Int64
          :> QueryParam "job_id" Int64
          :> QueryFlag "roots_only"
          :> QueryParam "status" JobStatus
          :> QueryParam "claimed_by" UUID
          :> QueryParam "kind" Text
          :> QueryParam "payload" Text
          :> QueryParam "rate_limit_prefix" Text
          :> QueryParam "concurrency_prefix" Text
          :> QueryParam "sort_by" JobSortColumn
          :> QueryParam "sort_dir" SortDir
          :> Get '[JSON] (JobsResponse payload)
  -- ^ @GET \/queues\/:queue\/jobs@
  , forall {k} payload result (mode :: k).
JobsAPI payload result mode
-> mode
   :- (Summary "Enqueue a job"
       :> (Description
             "A duplicate of an ignore dedup key returns the job that exists. A duplicate of a replace dedup key replaces the job in the queue."
           :> (Throws
                 409
                 "A replace dedup key matched a job that is in flight, flagged for cancel, or has children."
               :> (Throws
                     409
                     "An ignore dedup key matched a job that was deleted before the server could read it."
                   :> (ReqBody '[JSON] (ApiJobWrite payload)
                       :> Post '[JSON] (JobResponse (JobRead payload)))))))
insertJob
      :: mode
        :- Summary "Enqueue a job"
          :> Description
               "A duplicate of an ignore dedup key returns the job that exists. A duplicate of a replace dedup key replaces the job in the queue."
          :> Throws 409 "A replace dedup key matched a job that is in flight, flagged for cancel, or has children."
          :> Throws 409 "An ignore dedup key matched a job that was deleted before the server could read it."
          :> ReqBody '[JSON] (ApiJobWrite payload)
          :> Post '[JSON] (JobResponse (JobRead payload))
  -- ^ @POST \/queues\/:queue\/jobs@
  , forall {k} payload result (mode :: k).
JobsAPI payload result mode
-> mode
   :- (Summary "Enqueue many jobs"
       :> (Description
             "Returns the jobs inserted or replaced. The response omits a job that an ignore dedup key skips, and a job that a replace dedup key cannot replace."
           :> ("batch"
               :> (ReqBody '[JSON] (BatchInsertRequest payload)
                   :> Post '[JSON] (BatchInsertResponse payload)))))
insertJobsBatch
      :: mode
        :- Summary "Enqueue many jobs"
          :> Description
               "Returns the jobs inserted or replaced. The response omits a job that an ignore dedup key skips, and a job that a replace dedup key cannot replace."
          :> "batch"
          :> ReqBody '[JSON] (BatchInsertRequest payload)
          :> Post '[JSON] (BatchInsertResponse payload)
  -- ^ @POST \/queues\/:queue\/jobs\/batch@
  , forall {k} payload result (mode :: k).
JobsAPI payload result mode
-> mode
   :- (Summary "Show a job"
       :> (Description "The response includes the derived status."
           :> (Capture "id" Int64
               :> Get '[JSON] (JobResponse (ApiJobWithStatus (Stored payload))))))
getJob
      :: mode
        :- Summary "Show a job"
          :> Description "The response includes the derived status."
          :> Capture "id" Int64
          :> Get '[JSON] (JobResponse (ApiJobWithStatus (Stored payload)))
  -- ^ @GET \/queues\/:queue\/jobs\/:id@
  , forall {k} payload result (mode :: k).
JobsAPI payload result mode
-> mode
   :- (Summary "Cancel a job and its descendants"
       :> (Description
             "Deletes the job and every descendant, also the jobs in flight. The handler of a job in flight continues, and its ack finds no job. Use force-cancel to stop the handler."
           :> (Capture "id" Int64 :> DeleteNoContent)))
cancelJob
      :: mode
        :- Summary "Cancel a job and its descendants"
          :> Description
               "Deletes the job and every descendant, also the jobs in flight. The handler of a job in flight continues, and its ack finds no job. Use force-cancel to stop the handler."
          :> Capture "id" Int64
          :> DeleteNoContent
  -- ^ @DELETE \/queues\/:queue\/jobs\/:id@
  , forall {k} payload result (mode :: k).
JobsAPI payload result mode
-> mode
   :- (Summary "Cancel a job and interrupt its handlers"
       :> (Description
             "Deletes the jobs in the tree that are not in flight. A job in flight receives a cancel flag and cannot be claimed again. A worker pool stops its handler. An HTTP claimant with a live lease receives 409 on its next ack, nack or extend, until the reaper deletes the job. Then it receives 404. A claimant whose lease expired before the force-cancel receives 404."
           :> (Capture "id" Int64 :> ("force-cancel" :> PostNoContent))))
forceCancelJob
      :: mode
        :- Summary "Cancel a job and interrupt its handlers"
          :> Description
               "Deletes the jobs in the tree that are not in flight. A job in flight receives a cancel flag and cannot be claimed again. A worker pool stops its handler. An HTTP claimant with a live lease receives 409 on its next ack, nack or extend, until the reaper deletes the job. Then it receives 404. A claimant whose lease expired before the force-cancel receives 404."
          :> Capture "id" Int64
          :> "force-cancel"
          :> PostNoContent
  -- ^ @POST \/queues\/:queue\/jobs\/:id\/force-cancel@
  , forall {k} payload result (mode :: k).
JobsAPI payload result mode
-> mode
   :- (Summary "Complete a held job"
       :> (Description
             "Send the claimSeq and claimedBy from the claim. The server stores the optional result for the parent rollup or the archive."
           :> (Throws 409 LeaseRefusal
               :> (Capture "id" Int64
                   :> ("ack"
                       :> (ReqBody '[JSON] (AckRequest result) :> PostNoContent))))))
ackClaimedJob
      :: mode
        :- Summary "Complete a held job"
          :> Description
               "Send the claimSeq and claimedBy from the claim. The server stores the optional result for the parent rollup or the archive."
          :> Throws 409 LeaseRefusal
          :> Capture "id" Int64
          :> "ack"
          :> ReqBody '[JSON] (AckRequest result)
          :> PostNoContent
  -- ^ @POST \/queues\/:queue\/jobs\/:id\/ack@
  , forall {k} payload result (mode :: k).
JobsAPI payload result mode
-> mode
   :- (Summary "Return a held job"
       :> (Description
             "Restores the attempt that the claim used. The job becomes claimable when its lease expires."
           :> (Throws 409 LeaseRefusal
               :> (Capture "id" Int64
                   :> ("nack" :> (ReqBody '[JSON] JobLease :> PostNoContent))))))
nackClaimedJob
      :: mode
        :- Summary "Return a held job"
          :> Description "Restores the attempt that the claim used. The job becomes claimable when its lease expires."
          :> Throws 409 LeaseRefusal
          :> Capture "id" Int64
          :> "nack"
          :> ReqBody '[JSON] JobLease
          :> PostNoContent
  -- ^ @POST \/queues\/:queue\/jobs\/:id\/nack@
  , forall {k} payload result (mode :: k).
JobsAPI payload result mode
-> mode
   :- (Summary "Extend a held lease"
       :> (Description "Sets the lease to expire leaseSeconds from now."
           :> (Throws 409 LeaseRefusal
               :> (Capture "id" Int64
                   :> ("extend"
                       :> (ReqBody '[JSON] ExtendRequest :> PostNoContent))))))
extendClaimedJob
      :: mode
        :- Summary "Extend a held lease"
          :> Description "Sets the lease to expire leaseSeconds from now."
          :> Throws 409 LeaseRefusal
          :> Capture "id" Int64
          :> "extend"
          :> ReqBody '[JSON] ExtendRequest
          :> PostNoContent
  -- ^ @POST \/queues\/:queue\/jobs\/:id\/extend@
  , forall {k} payload result (mode :: k).
JobsAPI payload result mode
-> mode
   :- (Summary "Make a job visible now"
       :> (Throws
             409
             "The job is already visible, suspended, in flight, or flagged for cancel."
           :> (Capture "id" Int64 :> ("promote" :> PostNoContent))))
promoteJob
      :: mode
        :- Summary "Make a job visible now"
          :> Throws 409 "The job is already visible, suspended, in flight, or flagged for cancel."
          :> Capture "id" Int64
          :> "promote"
          :> PostNoContent
  -- ^ @POST \/queues\/:queue\/jobs\/:id\/promote@
  , forall {k} payload result (mode :: k).
JobsAPI payload result mode
-> mode
   :- (Summary "Set when a job becomes visible"
       :> (Throws
             409
             "The job is in flight, suspended, flagged for cancel, or out of attempts, or another operation changed it."
           :> (Capture "id" Int64
               :> ("reschedule"
                   :> (ReqBody '[JSON] RescheduleRequest :> PostNoContent)))))
rescheduleJob
      :: mode
        :- Summary "Set when a job becomes visible"
          :> Throws 409 "The job is in flight, suspended, flagged for cancel, or out of attempts, or another operation changed it."
          :> Capture "id" Int64
          :> "reschedule"
          :> ReqBody '[JSON] RescheduleRequest
          :> PostNoContent
  -- ^ @POST \/queues\/:queue\/jobs\/:id\/reschedule@
  , forall {k} payload result (mode :: k).
JobsAPI payload result mode
-> mode
   :- (Summary "Move a job to the DLQ"
       :> (Description
             "A rollup takes its descendants with it. A job in flight is moved too. Its handler continues, and its ack finds no job."
           :> (Throws
                 409
                 "Another operation changed or deleted the job between the read and the move."
               :> (Capture "id" Int64 :> ("move-to-dlq" :> PostNoContent)))))
moveToDLQ
      :: mode
        :- Summary "Move a job to the DLQ"
          :> Description
               "A rollup takes its descendants with it. A job in flight is moved too. Its handler continues, and its ack finds no job."
          :> Throws 409 "Another operation changed or deleted the job between the read and the move."
          :> Capture "id" Int64
          :> "move-to-dlq"
          :> PostNoContent
  -- ^ @POST \/queues\/:queue\/jobs\/:id\/move-to-dlq@
  , forall {k} payload result (mode :: k).
JobsAPI payload result mode
-> mode
   :- (Summary "Suspend the visible descendants of a job"
       :> (Description
             "The operation skips descendants that are in flight, delayed or throttled. Succeeds when nothing is suspended. 404 when the job is gone."
           :> (Capture "id" Int64 :> ("pause-children" :> PostNoContent))))
pauseChildren
      :: mode
        :- Summary "Suspend the visible descendants of a job"
          :> Description
               "The operation skips descendants that are in flight, delayed or throttled. Succeeds when nothing is suspended. 404 when the job is gone."
          :> Capture "id" Int64
          :> "pause-children"
          :> PostNoContent
  -- ^ @POST \/queues\/:queue\/jobs\/:id\/pause-children@
  , forall {k} payload result (mode :: k).
JobsAPI payload result mode
-> mode
   :- (Summary "Resume the suspended descendants of a job"
       :> (Description
             "A finalizer with children in the queue stays suspended. Succeeds when nothing is resumed. 404 when the job is gone."
           :> (Capture "id" Int64 :> ("resume-children" :> PostNoContent))))
resumeChildren
      :: mode
        :- Summary "Resume the suspended descendants of a job"
          :> Description
               "A finalizer with children in the queue stays suspended. Succeeds when nothing is resumed. 404 when the job is gone."
          :> Capture "id" Int64
          :> "resume-children"
          :> PostNoContent
  -- ^ @POST \/queues\/:queue\/jobs\/:id\/resume-children@
  , forall {k} payload result (mode :: k).
JobsAPI payload result mode
-> mode
   :- (Summary "Suspend a job"
       :> (Description "A suspended job cannot be claimed."
           :> (Throws 409 "The job is already suspended, or it is in flight."
               :> (Capture "id" Int64 :> ("suspend" :> PostNoContent)))))
suspendJob
      :: mode
        :- Summary "Suspend a job"
          :> Description "A suspended job cannot be claimed."
          :> Throws 409 "The job is already suspended, or it is in flight."
          :> Capture "id" Int64
          :> "suspend"
          :> PostNoContent
  -- ^ @POST \/queues\/:queue\/jobs\/:id\/suspend@
  , forall {k} payload result (mode :: k).
JobsAPI payload result mode
-> mode
   :- (Summary "Resume a suspended job"
       :> (Throws
             409
             "The job is not suspended, it is a finalizer with children in the queue, or another operation changed it."
           :> (Capture "id" Int64 :> ("resume" :> PostNoContent))))
resumeJob
      :: mode
        :- Summary "Resume a suspended job"
          :> Throws 409 "The job is not suspended, it is a finalizer with children in the queue, or another operation changed it."
          :> Capture "id" Int64
          :> "resume"
          :> PostNoContent
  -- ^ @POST \/queues\/:queue\/jobs\/:id\/resume@
  }
  deriving stock ((forall x.
 JobsAPI payload result mode -> Rep (JobsAPI payload result mode) x)
-> (forall x.
    Rep (JobsAPI payload result mode) x -> JobsAPI payload result mode)
-> Generic (JobsAPI payload result mode)
forall x.
Rep (JobsAPI payload result mode) x -> JobsAPI payload result mode
forall x.
JobsAPI payload result mode -> Rep (JobsAPI payload result mode) x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
forall payload result k (mode :: k) x.
Rep (JobsAPI payload result mode) x -> JobsAPI payload result mode
forall payload result k (mode :: k) x.
JobsAPI payload result mode -> Rep (JobsAPI payload result mode) x
$cfrom :: forall payload result k (mode :: k) x.
JobsAPI payload result mode -> Rep (JobsAPI payload result mode) x
from :: forall x.
JobsAPI payload result mode -> Rep (JobsAPI payload result mode) x
$cto :: forall payload result k (mode :: k) x.
Rep (JobsAPI payload result mode) x -> JobsAPI payload result mode
to :: forall x.
Rep (JobsAPI payload result mode) x -> JobsAPI payload result mode
Generic)

-- | One queue's DLQ routes.
data DLQAPI payload mode = DLQAPI
  { forall {k} payload (mode :: k).
DLQAPI payload mode
-> mode
   :- (Summary "List dead-lettered jobs"
       :> (Description
             "The payload and error filters search the payload and the last error."
           :> (QueryParam "limit" PageLimit
               :> (QueryParam "offset" Int
                   :> (QueryParam "parent_id" Int64
                       :> (QueryParam "job_id" Int64
                           :> (QueryParam "group_key" Text
                               :> (QueryParam "kind" Text
                                   :> (QueryParam "payload" Text
                                       :> (QueryParam "error" Text
                                           :> (QueryParam "sort_by" DLQSortColumn
                                               :> (QueryParam "sort_dir" SortDir
                                                   :> Get '[JSON] (DLQResponse payload)))))))))))))
listDLQ
      :: mode
        :- Summary "List dead-lettered jobs"
          :> Description
               "The payload and error filters search the payload and the last error."
          :> QueryParam "limit" PageLimit
          :> QueryParam "offset" Int
          :> QueryParam "parent_id" Int64
          :> QueryParam "job_id" Int64
          :> QueryParam "group_key" Text
          :> QueryParam "kind" Text
          :> QueryParam "payload" Text
          :> QueryParam "error" Text
          :> QueryParam "sort_by" DLQSortColumn
          :> QueryParam "sort_dir" SortDir
          :> Get '[JSON] (DLQResponse payload)
  -- ^ @GET \/queues\/:queue\/dlq@
  , forall {k} payload (mode :: k).
DLQAPI payload mode
-> mode
   :- (Summary "Retry a dead-lettered job"
       :> (Description "Moves the job back to the queue."
           :> (Throws
                 409 "The parent of the job is gone from the queue and the DLQ."
               :> (Capture "id" Int64
                   :> ("retry"
                       :> (ReqBody'
                             '[Description PayloadEditNote]
                             '[OptionalJSON]
                             (Maybe (PayloadEdit payload))
                           :> PostNoContent))))))
retryFromDLQ
      :: mode
        :- Summary "Retry a dead-lettered job"
          :> Description "Moves the job back to the queue."
          :> Throws 409 "The parent of the job is gone from the queue and the DLQ."
          :> Capture "id" Int64
          :> "retry"
          :> ReqBody' '[Description PayloadEditNote] '[OptionalJSON] (Maybe (PayloadEdit payload))
          :> PostNoContent
  -- ^ @POST \/queues\/:queue\/dlq\/:id\/retry@
  , forall {k} payload (mode :: k).
DLQAPI payload mode
-> mode
   :- (Summary "Delete a dead-lettered job"
       :> (Description
             "Resumes its parent when no child of the parent is left in the queue."
           :> (Capture "id" Int64 :> DeleteNoContent)))
deleteDLQ
      :: mode
        :- Summary "Delete a dead-lettered job"
          :> Description "Resumes its parent when no child of the parent is left in the queue."
          :> Capture "id" Int64
          :> DeleteNoContent
  -- ^ @DELETE \/queues\/:queue\/dlq\/:id@
  , forall {k} payload (mode :: k).
DLQAPI payload mode
-> mode
   :- (Summary "Delete dead-lettered jobs"
       :> (Description
             "Returns the number deleted. The server skips unknown ids."
           :> ("batch-delete"
               :> (ReqBody '[JSON] BatchDeleteRequest
                   :> Post '[JSON] BatchDeleteResponse))))
deleteDLQBatch
      :: mode
        :- Summary "Delete dead-lettered jobs"
          :> Description "Returns the number deleted. The server skips unknown ids."
          :> "batch-delete"
          :> ReqBody '[JSON] BatchDeleteRequest
          :> Post '[JSON] BatchDeleteResponse
  -- ^ @POST \/queues\/:queue\/dlq\/batch-delete@
  , forall {k} payload (mode :: k).
DLQAPI payload mode
-> mode
   :- (Summary "Retry dead-lettered jobs"
       :> (Description
             "Retries each job's tree in its own transaction. Returns the number of ids whose retry restored a tree. The server skips unknown ids and ids restored with an earlier id's tree."
           :> ("batch-retry"
               :> (ReqBody '[JSON] BatchRetryRequest
                   :> Post '[JSON] BatchRetryResponse))))
retryDLQBatch
      :: mode
        :- Summary "Retry dead-lettered jobs"
          :> Description
               "Retries each job's tree in its own transaction. Returns the number of ids whose retry restored a tree. The server skips unknown ids and ids restored with an earlier id's tree."
          :> "batch-retry"
          :> ReqBody '[JSON] BatchRetryRequest
          :> Post '[JSON] BatchRetryResponse
  -- ^ @POST \/queues\/:queue\/dlq\/batch-retry@
  , forall {k} payload (mode :: k).
DLQAPI payload mode
-> mode
   :- (Summary "Retry every matching dead-lettered job"
       :> (Description
             "Retries every job the filters match, oldest first, as batch-retry does. With no filters it retries the whole DLQ. The payload and error filters search the payload and the last error."
           :> ("retry-matching"
               :> (QueryParam "parent_id" Int64
                   :> (QueryParam "job_id" Int64
                       :> (QueryParam "group_key" Text
                           :> (QueryParam "kind" Text
                               :> (QueryParam "payload" Text
                                   :> (QueryParam "error" Text
                                       :> Post '[JSON] BatchRetryResponse)))))))))
retryDLQMatching
      :: mode
        :- Summary "Retry every matching dead-lettered job"
          :> Description
               "Retries every job the filters match, oldest first, as batch-retry does. With no filters it retries the whole DLQ. The payload and error filters search the payload and the last error."
          :> "retry-matching"
          :> QueryParam "parent_id" Int64
          :> QueryParam "job_id" Int64
          :> QueryParam "group_key" Text
          :> QueryParam "kind" Text
          :> QueryParam "payload" Text
          :> QueryParam "error" Text
          :> Post '[JSON] BatchRetryResponse
  -- ^ @POST \/queues\/:queue\/dlq\/retry-matching@
  }
  deriving stock ((forall x. DLQAPI payload mode -> Rep (DLQAPI payload mode) x)
-> (forall x. Rep (DLQAPI payload mode) x -> DLQAPI payload mode)
-> Generic (DLQAPI payload mode)
forall x. Rep (DLQAPI payload mode) x -> DLQAPI payload mode
forall x. DLQAPI payload mode -> Rep (DLQAPI payload mode) x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
forall payload k (mode :: k) x.
Rep (DLQAPI payload mode) x -> DLQAPI payload mode
forall payload k (mode :: k) x.
DLQAPI payload mode -> Rep (DLQAPI payload mode) x
$cfrom :: forall payload k (mode :: k) x.
DLQAPI payload mode -> Rep (DLQAPI payload mode) x
from :: forall x. DLQAPI payload mode -> Rep (DLQAPI payload mode) x
$cto :: forall payload k (mode :: k) x.
Rep (DLQAPI payload mode) x -> DLQAPI payload mode
to :: forall x. Rep (DLQAPI payload mode) x -> DLQAPI payload mode
Generic)

-- | One queue's archive routes.
data ArchiveAPI payload mode = ArchiveAPI
  { forall {k} payload (mode :: k).
ArchiveAPI payload mode
-> mode
   :- (Summary "List archived jobs"
       :> (Description "The payload filter searches the payload text."
           :> (QueryParam "limit" PageLimit
               :> (QueryParam "offset" Int
                   :> (QueryParam "parent_id" Int64
                       :> (QueryParam "job_id" Int64
                           :> (QueryParam "group_key" Text
                               :> (QueryParam "kind" Text
                                   :> (QueryParam "payload" Text
                                       :> (QueryParam "completed_after" UTCTime
                                           :> (QueryParam "completed_before" UTCTime
                                               :> (QueryParam "sort_by" ArchiveSortColumn
                                                   :> (QueryParam "sort_dir" SortDir
                                                       :> Get
                                                            '[JSON]
                                                            (ArchiveResponse payload))))))))))))))
listArchive
      :: mode
        :- Summary "List archived jobs"
          :> Description "The payload filter searches the payload text."
          :> QueryParam "limit" PageLimit
          :> QueryParam "offset" Int
          :> QueryParam "parent_id" Int64
          :> QueryParam "job_id" Int64
          :> QueryParam "group_key" Text
          :> QueryParam "kind" Text
          :> QueryParam "payload" Text
          :> QueryParam "completed_after" UTCTime
          :> QueryParam "completed_before" UTCTime
          :> QueryParam "sort_by" ArchiveSortColumn
          :> QueryParam "sort_dir" SortDir
          :> Get '[JSON] (ArchiveResponse payload)
  -- ^ @GET \/queues\/:queue\/archive@
  , forall {k} payload (mode :: k).
ArchiveAPI payload mode
-> mode
   :- (Summary "Run an archived job again"
       :> (Description "Inserts a new job. The archive entry stays."
           :> (Capture "id" Int64
               :> ("reenqueue"
                   :> (ReqBody'
                         '[Description PayloadEditNote]
                         '[OptionalJSON]
                         (Maybe (PayloadEdit payload))
                       :> PostNoContent)))))
reEnqueueArchive
      :: mode
        :- Summary "Run an archived job again"
          :> Description "Inserts a new job. The archive entry stays."
          :> Capture "id" Int64
          :> "reenqueue"
          :> ReqBody' '[Description PayloadEditNote] '[OptionalJSON] (Maybe (PayloadEdit payload))
          :> PostNoContent
  -- ^ @POST \/queues\/:queue\/archive\/:id\/reenqueue@
  , forall {k} payload (mode :: k).
ArchiveAPI payload mode
-> mode
   :- (Summary "Delete an archived job"
       :> (Capture "id" Int64 :> DeleteNoContent))
deleteArchive
      :: mode
        :- Summary "Delete an archived job"
          :> Capture "id" Int64
          :> DeleteNoContent
  -- ^ @DELETE \/queues\/:queue\/archive\/:id@
  , forall {k} payload (mode :: k).
ArchiveAPI payload mode
-> mode
   :- (Summary "Delete archived jobs"
       :> (Description
             "Returns the number deleted. The server skips unknown ids."
           :> ("batch-delete"
               :> (ReqBody '[JSON] BatchDeleteRequest
                   :> Post '[JSON] BatchDeleteResponse))))
deleteArchiveBatch
      :: mode
        :- Summary "Delete archived jobs"
          :> Description "Returns the number deleted. The server skips unknown ids."
          :> "batch-delete"
          :> ReqBody '[JSON] BatchDeleteRequest
          :> Post '[JSON] BatchDeleteResponse
  -- ^ @POST \/queues\/:queue\/archive\/batch-delete@
  }
  deriving stock ((forall x.
 ArchiveAPI payload mode -> Rep (ArchiveAPI payload mode) x)
-> (forall x.
    Rep (ArchiveAPI payload mode) x -> ArchiveAPI payload mode)
-> Generic (ArchiveAPI payload mode)
forall x.
Rep (ArchiveAPI payload mode) x -> ArchiveAPI payload mode
forall x.
ArchiveAPI payload mode -> Rep (ArchiveAPI payload mode) x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
forall payload k (mode :: k) x.
Rep (ArchiveAPI payload mode) x -> ArchiveAPI payload mode
forall payload k (mode :: k) x.
ArchiveAPI payload mode -> Rep (ArchiveAPI payload mode) x
$cfrom :: forall payload k (mode :: k) x.
ArchiveAPI payload mode -> Rep (ArchiveAPI payload mode) x
from :: forall x.
ArchiveAPI payload mode -> Rep (ArchiveAPI payload mode) x
$cto :: forall payload k (mode :: k) x.
Rep (ArchiveAPI payload mode) x -> ArchiveAPI payload mode
to :: forall x.
Rep (ArchiveAPI payload mode) x -> ArchiveAPI payload mode
Generic)

-- | One queue's stats route.
data StatsAPI mode = StatsAPI
  { forall {k} (mode :: k).
StatsAPI mode
-> mode
   :- (Summary "Show queue stats" :> Get '[JSON] StatsResponse)
getStats
      :: mode
        :- Summary "Show queue stats"
          :> Get '[JSON] StatsResponse
  -- ^ @GET \/queues\/:queue\/stats@
  }
  deriving stock ((forall x. StatsAPI mode -> Rep (StatsAPI mode) x)
-> (forall x. Rep (StatsAPI mode) x -> StatsAPI mode)
-> Generic (StatsAPI mode)
forall x. Rep (StatsAPI mode) x -> StatsAPI mode
forall x. StatsAPI mode -> Rep (StatsAPI mode) x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
forall k (mode :: k) x. Rep (StatsAPI mode) x -> StatsAPI mode
forall k (mode :: k) x. StatsAPI mode -> Rep (StatsAPI mode) x
$cfrom :: forall k (mode :: k) x. StatsAPI mode -> Rep (StatsAPI mode) x
from :: forall x. StatsAPI mode -> Rep (StatsAPI mode) x
$cto :: forall k (mode :: k) x. Rep (StatsAPI mode) x -> StatsAPI mode
to :: forall x. Rep (StatsAPI mode) x -> StatsAPI mode
Generic)

-- | Schema-wide maintenance. A running worker pool does the same work.
newtype MaintenanceAPI mode = MaintenanceAPI
  { forall {k} (mode :: k).
MaintenanceAPI mode
-> mode
   :- (Summary "Run a maintenance pass"
       :> (Description
             "Runs the work of the reaper once. An operation that another caller runs at the same time is skipped and is not in the response."
           :> Post '[JSON] MaintenanceResponse))
runMaintenance
      :: mode
        :- Summary "Run a maintenance pass"
          :> Description
               "Runs the work of the reaper once. An operation that another caller runs at the same time is skipped and is not in the response."
          :> Post '[JSON] MaintenanceResponse
  -- ^ @POST \/maintenance@
  }
  deriving stock ((forall x. MaintenanceAPI mode -> Rep (MaintenanceAPI mode) x)
-> (forall x. Rep (MaintenanceAPI mode) x -> MaintenanceAPI mode)
-> Generic (MaintenanceAPI mode)
forall x. Rep (MaintenanceAPI mode) x -> MaintenanceAPI mode
forall x. MaintenanceAPI mode -> Rep (MaintenanceAPI mode) x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
forall k (mode :: k) x.
Rep (MaintenanceAPI mode) x -> MaintenanceAPI mode
forall k (mode :: k) x.
MaintenanceAPI mode -> Rep (MaintenanceAPI mode) x
$cfrom :: forall k (mode :: k) x.
MaintenanceAPI mode -> Rep (MaintenanceAPI mode) x
from :: forall x. MaintenanceAPI mode -> Rep (MaintenanceAPI mode) x
$cto :: forall k (mode :: k) x.
Rep (MaintenanceAPI mode) x -> MaintenanceAPI mode
to :: forall x. Rep (MaintenanceAPI mode) x -> MaintenanceAPI mode
Generic)

-- | One queue's routes.
data TableAPI payload result mode = TableAPI
  { forall {k} payload result (mode :: k).
TableAPI payload result mode
-> mode :- ("jobs" :> NamedRoutes (JobsAPI payload result))
jobs :: mode :- "jobs" :> NamedRoutes (JobsAPI payload result)
  -- ^ @\/queues\/:queue\/jobs@ The job routes.
  , forall {k} payload result (mode :: k).
TableAPI payload result mode
-> mode
   :- (Summary "Claim jobs"
       :> (Description
             "Leases up to maxJobs visible jobs for leaseSeconds. Each job carries the claimSeq and claimedBy that ack, nack and extend need. The server does not renew the lease. A paused queue returns no jobs."
           :> ("claim"
               :> (ReqBody '[JSON] ClaimRequest
                   :> Post '[JSON] (ClaimResponse payload)))))
claimJobs
      :: mode
        :- Summary "Claim jobs"
          :> Description
               "Leases up to maxJobs visible jobs for leaseSeconds. Each job carries the claimSeq and claimedBy that ack, nack and extend need. The server does not renew the lease. A paused queue returns no jobs."
          :> "claim"
          :> ReqBody '[JSON] ClaimRequest
          :> Post '[JSON] (ClaimResponse payload)
  -- ^ @POST \/queues\/:queue\/claim@
  , forall {k} payload result (mode :: k).
TableAPI payload result mode
-> mode :- ("dlq" :> NamedRoutes (DLQAPI payload))
dlq :: mode :- "dlq" :> NamedRoutes (DLQAPI payload)
  -- ^ @\/queues\/:queue\/dlq@ The DLQ routes.
  , forall {k} payload result (mode :: k).
TableAPI payload result mode
-> mode :- ("archive" :> NamedRoutes (ArchiveAPI payload))
archive :: mode :- "archive" :> NamedRoutes (ArchiveAPI payload)
  -- ^ @\/queues\/:queue\/archive@ The archive routes.
  , forall {k} payload result (mode :: k).
TableAPI payload result mode
-> mode :- ("stats" :> NamedRoutes StatsAPI)
stats :: mode :- "stats" :> NamedRoutes StatsAPI
  -- ^ @\/queues\/:queue\/stats@ The stats route.
  , forall {k} payload result (mode :: k).
TableAPI payload result mode
-> mode
   :- (Summary "List payload kinds"
       :> ("kinds" :> Get '[JSON] [Text]))
listKinds
      :: mode
        :- Summary "List payload kinds"
          :> "kinds"
          :> Get '[JSON] [Text]
  -- ^ @GET \/queues\/:queue\/kinds@
  , forall {k} payload result (mode :: k).
TableAPI payload result mode
-> mode
   :- (Summary "List open groups"
       :> (Description "The largest groups come first."
           :> ("groups"
               :> (QueryParam "limit" PageLimit
                   :> (QueryParam "offset" Int
                       :> (QueryParam "group_key" Text :> Get '[JSON] GroupsResponse))))))
listGroups
      :: mode
        :- Summary "List open groups"
          :> Description "The largest groups come first."
          :> "groups"
          :> QueryParam "limit" PageLimit
          :> QueryParam "offset" Int
          :> QueryParam "group_key" Text
          :> Get '[JSON] GroupsResponse
  -- ^ @GET \/queues\/:queue\/groups@
  , forall {k} payload result (mode :: k).
TableAPI payload result mode
-> mode
   :- (Summary "Show a queue's pause state and DLQ retention"
       :> (Description
             "Null when the queue has no row. Migrations create one for each queue."
           :> ("details" :> Get '[JSON] (Maybe QueueRow))))
getDetails
      :: mode
        :- Summary "Show a queue's pause state and DLQ retention"
          :> Description "Null when the queue has no row. Migrations create one for each queue."
          :> "details"
          :> Get '[JSON] (Maybe QueueRow)
  -- ^ @GET \/queues\/:queue\/details@
  , forall {k} payload result (mode :: k).
TableAPI payload result mode
-> mode
   :- (Summary "Pause a queue"
       :> (Description
             "Workers do not claim from the queue. Claims through this API return no jobs."
           :> ("pause" :> PostNoContent)))
pauseQueue
      :: mode
        :- Summary "Pause a queue"
          :> Description "Workers do not claim from the queue. Claims through this API return no jobs."
          :> "pause"
          :> PostNoContent
  -- ^ @POST \/queues\/:queue\/pause@
  , forall {k} payload result (mode :: k).
TableAPI payload result mode
-> mode
   :- (Summary "Resume a queue" :> ("resume" :> PostNoContent))
resumeQueue
      :: mode
        :- Summary "Resume a queue"
          :> "resume"
          :> PostNoContent
  -- ^ @POST \/queues\/:queue\/resume@
  , forall {k} payload result (mode :: k).
TableAPI payload result mode
-> mode
   :- (Summary "Set a queue's DLQ retention"
       :> (Description
             "The reaper deletes the queue's DLQ jobs that failed more than retentionSeconds ago. A null retentionSeconds keeps them. Every worker pool sharing the schema applies it."
           :> (Throws 400 "retentionSeconds is not positive."
               :> ("dlq-retention"
                   :> (ReqBody '[JSON] DLQRetentionRequest :> PutNoContent)))))
setDLQRetention
      :: mode
        :- Summary "Set a queue's DLQ retention"
          :> Description
               "The reaper deletes the queue's DLQ jobs that failed more than retentionSeconds ago. A null retentionSeconds keeps them. Every worker pool sharing the schema applies it."
          :> Throws 400 "retentionSeconds is not positive."
          :> "dlq-retention"
          :> ReqBody '[JSON] DLQRetentionRequest
          :> PutNoContent
  -- ^ @PUT \/queues\/:queue\/dlq-retention@
  }
  deriving stock ((forall x.
 TableAPI payload result mode
 -> Rep (TableAPI payload result mode) x)
-> (forall x.
    Rep (TableAPI payload result mode) x
    -> TableAPI payload result mode)
-> Generic (TableAPI payload result mode)
forall x.
Rep (TableAPI payload result mode) x
-> TableAPI payload result mode
forall x.
TableAPI payload result mode
-> Rep (TableAPI payload result mode) x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
forall payload result k (mode :: k) x.
Rep (TableAPI payload result mode) x
-> TableAPI payload result mode
forall payload result k (mode :: k) x.
TableAPI payload result mode
-> Rep (TableAPI payload result mode) x
$cfrom :: forall payload result k (mode :: k) x.
TableAPI payload result mode
-> Rep (TableAPI payload result mode) x
from :: forall x.
TableAPI payload result mode
-> Rep (TableAPI payload result mode) x
$cto :: forall payload result k (mode :: k) x.
Rep (TableAPI payload result mode) x
-> TableAPI payload result mode
to :: forall x.
Rep (TableAPI payload result mode) x
-> TableAPI payload result mode
Generic)

-- | The queue registry routes.
data QueuesAPI mode = QueuesAPI
  { forall {k} (mode :: k).
QueuesAPI mode
-> mode :- (Summary "List queues" :> Get '[JSON] QueuesResponse)
listQueues
      :: mode
        :- Summary "List queues"
          :> Get '[JSON] QueuesResponse
  -- ^ @GET \/queues@
  , forall {k} (mode :: k).
QueuesAPI mode
-> mode
   :- (Summary "Show stats for every queue"
       :> ("stats" :> Get '[JSON] AllStatsResponse))
getAllStats
      :: mode
        :- Summary "Show stats for every queue"
          :> "stats"
          :> Get '[JSON] AllStatsResponse
  -- ^ @GET \/queues\/stats@
  }
  deriving stock ((forall x. QueuesAPI mode -> Rep (QueuesAPI mode) x)
-> (forall x. Rep (QueuesAPI mode) x -> QueuesAPI mode)
-> Generic (QueuesAPI mode)
forall x. Rep (QueuesAPI mode) x -> QueuesAPI mode
forall x. QueuesAPI mode -> Rep (QueuesAPI mode) x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
forall k (mode :: k) x. Rep (QueuesAPI mode) x -> QueuesAPI mode
forall k (mode :: k) x. QueuesAPI mode -> Rep (QueuesAPI mode) x
$cfrom :: forall k (mode :: k) x. QueuesAPI mode -> Rep (QueuesAPI mode) x
from :: forall x. QueuesAPI mode -> Rep (QueuesAPI mode) x
$cto :: forall k (mode :: k) x. Rep (QueuesAPI mode) x -> QueuesAPI mode
to :: forall x. Rep (QueuesAPI mode) x -> QueuesAPI mode
Generic)

-- | The SSE stream, served by a raw WAI handler.
type EventsAPI = "stream" :> Raw

-- | The cron schedule routes.
data CronAPI mode = CronAPI
  { forall {k} (mode :: k).
CronAPI mode
-> mode
   :- (Summary "List cron schedules"
       :> ("schedules"
           :> (QueryParam "queue" Text :> Get '[JSON] CronSchedulesResponse)))
listSchedules
      :: mode
        :- Summary "List cron schedules"
          :> "schedules"
          :> QueryParam "queue" Text
          :> Get '[JSON] CronSchedulesResponse
  -- ^ @GET \/cron\/schedules@
  --
  -- Optional @?queue=name@ scopes the result to a single queue.
  , forall {k} (mode :: k).
CronAPI mode
-> mode
   :- (Summary "Override a cron schedule"
       :> (Description
             "A null field clears its override. An absent field keeps it."
           :> (Throws 400 "An override is not valid."
               :> ("schedules"
                   :> (Capture "name" Text
                       :> (ReqBody '[JSON] CronScheduleUpdate
                           :> Patch '[JSON] CronScheduleView))))))
updateSchedule
      :: mode
        :- Summary "Override a cron schedule"
          :> Description "A null field clears its override. An absent field keeps it."
          :> Throws 400 "An override is not valid."
          :> "schedules"
          :> Capture "name" Text
          :> ReqBody '[JSON] CronScheduleUpdate
          :> Patch '[JSON] CronScheduleView
  -- ^ @PATCH \/cron\/schedules\/:name@
  , forall {k} (mode :: k).
CronAPI mode
-> mode
   :- (Summary "Run a cron schedule now"
       :> (Throws
             409
             "The schedule is disabled, or it already has a run that waits to start."
           :> ("schedules"
               :> (Capture "name" Text :> ("run" :> PostNoContent)))))
runSchedule
      :: mode
        :- Summary "Run a cron schedule now"
          :> Throws 409 "The schedule is disabled, or it already has a run that waits to start."
          :> "schedules"
          :> Capture "name" Text
          :> "run"
          :> PostNoContent
  -- ^ @POST \/cron\/schedules\/:name\/run@
  }
  deriving stock ((forall x. CronAPI mode -> Rep (CronAPI mode) x)
-> (forall x. Rep (CronAPI mode) x -> CronAPI mode)
-> Generic (CronAPI mode)
forall x. Rep (CronAPI mode) x -> CronAPI mode
forall x. CronAPI mode -> Rep (CronAPI mode) x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
forall k (mode :: k) x. Rep (CronAPI mode) x -> CronAPI mode
forall k (mode :: k) x. CronAPI mode -> Rep (CronAPI mode) x
$cfrom :: forall k (mode :: k) x. CronAPI mode -> Rep (CronAPI mode) x
from :: forall x. CronAPI mode -> Rep (CronAPI mode) x
$cto :: forall k (mode :: k) x. Rep (CronAPI mode) x -> CronAPI mode
to :: forall x. Rep (CronAPI mode) x -> CronAPI mode
Generic)

-- | The worker registry routes.
data WorkersAPI mode = WorkersAPI
  { forall {k} (mode :: k).
WorkersAPI mode
-> mode
   :- (Summary "List workers"
       :> (QueryParam "queue" Text
           :> (QueryParam "live" Double :> Get '[JSON] WorkersResponse)))
listWorkers
      :: mode
        :- Summary "List workers"
          :> QueryParam "queue" Text
          :> QueryParam "live" Double
          :> Get '[JSON] WorkersResponse
  -- ^ @GET \/workers@
  --
  -- Optional @?queue=name@ scopes the result to a single queue.
  -- Optional @?live=seconds@ filters to workers with a heartbeat inside the
  -- threshold. Without it all rows are returned.
  , forall {k} (mode :: k).
WorkersAPI mode
-> mode
   :- (Summary "Pause a worker"
       :> (Description "The worker does not claim new jobs."
           :> (Capture "id" UUID :> ("pause" :> PostNoContent))))
pauseWorker
      :: mode
        :- Summary "Pause a worker"
          :> Description "The worker does not claim new jobs."
          :> Capture "id" UUID
          :> "pause"
          :> PostNoContent
  -- ^ @POST \/workers\/:id\/pause@
  --
  -- Sets the worker's @paused@ flag.
  , forall {k} (mode :: k).
WorkersAPI mode
-> mode
   :- (Summary "Resume a worker"
       :> (Capture "id" UUID :> ("resume" :> PostNoContent)))
resumeWorker
      :: mode
        :- Summary "Resume a worker"
          :> Capture "id" UUID
          :> "resume"
          :> PostNoContent
  -- ^ @POST \/workers\/:id\/resume@
  }
  deriving stock ((forall x. WorkersAPI mode -> Rep (WorkersAPI mode) x)
-> (forall x. Rep (WorkersAPI mode) x -> WorkersAPI mode)
-> Generic (WorkersAPI mode)
forall x. Rep (WorkersAPI mode) x -> WorkersAPI mode
forall x. WorkersAPI mode -> Rep (WorkersAPI mode) x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
forall k (mode :: k) x. Rep (WorkersAPI mode) x -> WorkersAPI mode
forall k (mode :: k) x. WorkersAPI mode -> Rep (WorkersAPI mode) x
$cfrom :: forall k (mode :: k) x. WorkersAPI mode -> Rep (WorkersAPI mode) x
from :: forall x. WorkersAPI mode -> Rep (WorkersAPI mode) x
$cto :: forall k (mode :: k) x. Rep (WorkersAPI mode) x -> WorkersAPI mode
to :: forall x. Rep (WorkersAPI mode) x -> WorkersAPI mode
Generic)

-- | Rate limits API routes, schema-wide.
data RateLimitsAPI mode = RateLimitsAPI
  { forall {k} (mode :: k).
RateLimitsAPI mode
-> mode
   :- (Summary "List rate-limit policies"
       :> Get '[JSON] RateLimitPoliciesResponse)
listRateLimits
      :: mode
        :- Summary "List rate-limit policies"
          :> Get '[JSON] RateLimitPoliciesResponse
  -- ^ @GET \/rate-limits@
  , forall {k} (mode :: k).
RateLimitsAPI mode
-> mode
   :- (Summary "List the buckets of a rate-limit policy"
       :> (Capture "prefix" Text
           :> ("buckets"
               :> (QueryParam "limit" KeyPageLimit
                   :> (QueryParam "offset" Int
                       :> Get '[JSON] RateLimitBucketsResponse)))))
listRateLimitBuckets
      :: mode
        :- Summary "List the buckets of a rate-limit policy"
          :> Capture "prefix" Text
          :> "buckets"
          :> QueryParam "limit" KeyPageLimit
          :> QueryParam "offset" Int
          :> Get '[JSON] RateLimitBucketsResponse
  -- ^ @GET \/rate-limits\/:prefix\/buckets@
  , forall {k} (mode :: k).
RateLimitsAPI mode
-> mode
   :- (Summary "Override a rate-limit policy"
       :> (Description
             "A null field clears its override. An absent field keeps it."
           :> (Throws 400 "An override value is not valid."
               :> (Capture "prefix" Text
                   :> (ReqBody '[JSON] RateLimitPolicyUpdate
                       :> Patch '[JSON] RateLimitPolicyView)))))
updateRateLimitPolicy
      :: mode
        :- Summary "Override a rate-limit policy"
          :> Description "A null field clears its override. An absent field keeps it."
          :> Throws 400 "An override value is not valid."
          :> Capture "prefix" Text
          :> ReqBody '[JSON] RateLimitPolicyUpdate
          :> Patch '[JSON] RateLimitPolicyView
  -- ^ @PATCH \/rate-limits\/:prefix@
  , forall {k} (mode :: k).
RateLimitsAPI mode
-> mode
   :- (Summary "Refill the buckets of a rate-limit policy"
       :> (Description
             "Refills every bucket under the prefix to full. Returns the number refilled."
           :> (Capture "prefix" Text
               :> ("reset" :> Post '[JSON] RateLimitResetResponse))))
resetRateLimitBuckets
      :: mode
        :- Summary "Refill the buckets of a rate-limit policy"
          :> Description "Refills every bucket under the prefix to full. Returns the number refilled."
          :> Capture "prefix" Text
          :> "reset"
          :> Post '[JSON] RateLimitResetResponse
  -- ^ @POST \/rate-limits\/:prefix\/reset@
  , forall {k} (mode :: k).
RateLimitsAPI mode
-> mode
   :- (Summary "Add tokens to a rate-limit bucket"
       :> (Description "Wakes the throttled jobs of the key."
           :> (Throws
                 400
                 "The token count is 0 or less, or the key is not under the prefix."
               :> (Capture "prefix" Text
                   :> ("buckets"
                       :> (Capture "key" Text
                           :> ("tokens"
                               :> (ReqBody '[JSON] AddTokensRequest
                                   :> Post '[JSON] AddTokensResponse))))))))
addRateLimitTokens
      :: mode
        :- Summary "Add tokens to a rate-limit bucket"
          :> Description "Wakes the throttled jobs of the key."
          :> Throws 400 "The token count is 0 or less, or the key is not under the prefix."
          :> Capture "prefix" Text
          :> "buckets"
          :> Capture "key" Text
          :> "tokens"
          :> ReqBody '[JSON] AddTokensRequest
          :> Post '[JSON] AddTokensResponse
  -- ^ @POST \/rate-limits\/:prefix\/buckets\/:key\/tokens@
  --
  -- @:key@ is the full bucket key with its prefix, as the bucket listing shows it.
  , forall {k} (mode :: k).
RateLimitsAPI mode
-> mode
   :- (Summary "Prune idle rate-limit buckets"
       :> (Throws 400 "The idle value is negative."
           :> ("prune"
               :> (QueryParam "idle" Double :> Post '[JSON] PruneResponse))))
pruneRateLimitBuckets
      :: mode
        :- Summary "Prune idle rate-limit buckets"
          :> Throws 400 "The idle value is negative."
          :> "prune"
          :> QueryParam "idle" Double
          :> Post '[JSON] PruneResponse
  -- ^ @POST \/rate-limits\/prune@
  --
  -- The @idle@ value is in seconds. The default is the server's maintenance bucket idle age.
  }
  deriving stock ((forall x. RateLimitsAPI mode -> Rep (RateLimitsAPI mode) x)
-> (forall x. Rep (RateLimitsAPI mode) x -> RateLimitsAPI mode)
-> Generic (RateLimitsAPI mode)
forall x. Rep (RateLimitsAPI mode) x -> RateLimitsAPI mode
forall x. RateLimitsAPI mode -> Rep (RateLimitsAPI mode) x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
forall k (mode :: k) x.
Rep (RateLimitsAPI mode) x -> RateLimitsAPI mode
forall k (mode :: k) x.
RateLimitsAPI mode -> Rep (RateLimitsAPI mode) x
$cfrom :: forall k (mode :: k) x.
RateLimitsAPI mode -> Rep (RateLimitsAPI mode) x
from :: forall x. RateLimitsAPI mode -> Rep (RateLimitsAPI mode) x
$cto :: forall k (mode :: k) x.
Rep (RateLimitsAPI mode) x -> RateLimitsAPI mode
to :: forall x. Rep (RateLimitsAPI mode) x -> RateLimitsAPI mode
Generic)

-- | Concurrency API routes, schema-wide.
data ConcurrencyAPI mode = ConcurrencyAPI
  { forall {k} (mode :: k).
ConcurrencyAPI mode
-> mode
   :- (Summary "List concurrency policies"
       :> Get '[JSON] ConcurrencyPoliciesResponse)
listConcurrency
      :: mode
        :- Summary "List concurrency policies"
          :> Get '[JSON] ConcurrencyPoliciesResponse
  -- ^ @GET \/concurrency@
  , forall {k} (mode :: k).
ConcurrencyAPI mode
-> mode
   :- (Summary "List the keys of a concurrency policy"
       :> (Capture "prefix" Text
           :> ("keys"
               :> (QueryParam "limit" KeyPageLimit
                   :> (QueryParam "offset" Int
                       :> Get '[JSON] ConcurrencyKeysResponse)))))
listConcurrencyKeys
      :: mode
        :- Summary "List the keys of a concurrency policy"
          :> Capture "prefix" Text
          :> "keys"
          :> QueryParam "limit" KeyPageLimit
          :> QueryParam "offset" Int
          :> Get '[JSON] ConcurrencyKeysResponse
  -- ^ @GET \/concurrency\/:prefix\/keys@
  , forall {k} (mode :: k).
ConcurrencyAPI mode
-> mode
   :- (Summary "Override a concurrency limit"
       :> (Description
             "A null overrideLimit clears the override. An absent one keeps it."
           :> (Throws 400 "The override limit is negative."
               :> (Capture "prefix" Text
                   :> (ReqBody '[JSON] ConcurrencyPolicyUpdate
                       :> Patch '[JSON] ConcurrencyPolicyView)))))
updateConcurrencyPolicy
      :: mode
        :- Summary "Override a concurrency limit"
          :> Description "A null overrideLimit clears the override. An absent one keeps it."
          :> Throws 400 "The override limit is negative."
          :> Capture "prefix" Text
          :> ReqBody '[JSON] ConcurrencyPolicyUpdate
          :> Patch '[JSON] ConcurrencyPolicyView
  -- ^ @PATCH \/concurrency\/:prefix@
  , forall {k} (mode :: k).
ConcurrencyAPI mode
-> mode
   :- (Summary "Recount in-flight jobs per concurrency key"
       :> (Description
             "Repairs each in-flight count from the live jobs. Returns the rows repaired."
           :> ("reconcile" :> Post '[JSON] ConcurrencyReconcileResponse)))
reconcileConcurrency
      :: mode
        :- Summary "Recount in-flight jobs per concurrency key"
          :> Description "Repairs each in-flight count from the live jobs. Returns the rows repaired."
          :> "reconcile"
          :> Post '[JSON] ConcurrencyReconcileResponse
  -- ^ @POST \/concurrency\/reconcile@
  , forall {k} (mode :: k).
ConcurrencyAPI mode
-> mode
   :- (Summary "Prune drained concurrency keys"
       :> (Description "Deletes keys with no live job."
           :> ("prune" :> Post '[JSON] PruneResponse)))
pruneConcurrencyKeys
      :: mode
        :- Summary "Prune drained concurrency keys"
          :> Description "Deletes keys with no live job."
          :> "prune"
          :> Post '[JSON] PruneResponse
  -- ^ @POST \/concurrency\/prune@
  }
  deriving stock ((forall x. ConcurrencyAPI mode -> Rep (ConcurrencyAPI mode) x)
-> (forall x. Rep (ConcurrencyAPI mode) x -> ConcurrencyAPI mode)
-> Generic (ConcurrencyAPI mode)
forall x. Rep (ConcurrencyAPI mode) x -> ConcurrencyAPI mode
forall x. ConcurrencyAPI mode -> Rep (ConcurrencyAPI mode) x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
forall k (mode :: k) x.
Rep (ConcurrencyAPI mode) x -> ConcurrencyAPI mode
forall k (mode :: k) x.
ConcurrencyAPI mode -> Rep (ConcurrencyAPI mode) x
$cfrom :: forall k (mode :: k) x.
ConcurrencyAPI mode -> Rep (ConcurrencyAPI mode) x
from :: forall x. ConcurrencyAPI mode -> Rep (ConcurrencyAPI mode) x
$cto :: forall k (mode :: k) x.
Rep (ConcurrencyAPI mode) x -> ConcurrencyAPI mode
to :: forall x. Rep (ConcurrencyAPI mode) x -> ConcurrencyAPI mode
Generic)

-- | Liveness and readiness.
data HealthAPI mode = HealthAPI
  { forall {k} (mode :: k).
HealthAPI mode
-> mode
   :- (Summary "Check readiness"
       :> (ThrowsBody 503 "The database is not reachable." HealthResponse
           :> Get '[JSON] HealthResponse))
getHealth
      :: mode
        :- Summary "Check readiness"
          :> ThrowsBody 503 "The database is not reachable." HealthResponse
          :> Get '[JSON] HealthResponse
  -- ^ @GET \/health@
  --
  -- Readiness. Reaches the database and reports its connection counters.
  , forall {k} (mode :: k).
HealthAPI mode
-> mode
   :- (Summary "Check liveness"
       :> ("live" :> Get '[JSON] LivenessResponse))
getLiveness
      :: mode
        :- Summary "Check liveness"
          :> "live"
          :> Get '[JSON] LivenessResponse
  -- ^ @GET \/health\/live@
  --
  -- Liveness. Does not touch the database.
  }
  deriving stock ((forall x. HealthAPI mode -> Rep (HealthAPI mode) x)
-> (forall x. Rep (HealthAPI mode) x -> HealthAPI mode)
-> Generic (HealthAPI mode)
forall x. Rep (HealthAPI mode) x -> HealthAPI mode
forall x. HealthAPI mode -> Rep (HealthAPI mode) x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
forall k (mode :: k) x. Rep (HealthAPI mode) x -> HealthAPI mode
forall k (mode :: k) x. HealthAPI mode -> Rep (HealthAPI mode) x
$cfrom :: forall k (mode :: k) x. HealthAPI mode -> Rep (HealthAPI mode) x
from :: forall x. HealthAPI mode -> Rep (HealthAPI mode) x
$cto :: forall k (mode :: k) x. Rep (HealthAPI mode) x -> HealthAPI mode
to :: forall x. Rep (HealthAPI mode) x -> HealthAPI mode
Generic)

-- | Shared top-level routes appended after the per-table routes.
type SharedAPI =
  "queues" :> NamedRoutes QueuesAPI
    :<|> "maintenance" :> NamedRoutes MaintenanceAPI
    :<|> "events" :> EventsAPI
    :<|> "cron" :> NamedRoutes CronAPI
    :<|> "workers" :> NamedRoutes WorkersAPI
    :<|> "rate-limits" :> NamedRoutes RateLimitsAPI
    :<|> "concurrency" :> NamedRoutes ConcurrencyAPI
    :<|> "health" :> NamedRoutes HealthAPI

-- | A 'TableAPI' route per registry entry, followed by the shared
-- top-level routes.
type family RegistryToAPI (registry :: JobPayloadRegistry) :: Type where
  RegistryToAPI '[] = SharedAPI
  RegistryToAPI (spec ': rest) =
    ("queues" :> SpecName spec :> NamedRoutes (TableAPI (SpecPayload spec) (SpecResult spec)))
      :<|> RegistryToAPI rest

-- | Top-level Arbiter API, mounted at @\/api@. The route tree under that
-- prefix is generated from the registry. See 'RegistryToAPI' for the shape.
type ArbiterAPI :: JobPayloadRegistry -> Type
type ArbiterAPI registry = "api" :> RegistryToAPI registry