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

-- | The admin API's route types, generated from the registry.
module Arbiter.Servant.API
  ( ArbiterAPI
  , RegistryToAPI
  , SharedAPI
  , TableAPI (..)
  , JobsAPI (..)
  , DLQAPI (..)
  , ArchiveAPI (..)
  , StatsAPI (..)
  , QueuesAPI (..)
  , MaintenanceAPI (..)
  , EventsAPI
  , CronAPI (..)
  , WorkersAPI (..)
  , HealthAPI (..)
  , RateLimitsAPI (..)
  , ConcurrencyAPI (..)
  ) where

import Arbiter.Core.Job.Types (JobStatus, jobStatusToText)
import Arbiter.Core.QueueRegistry (JobPayloadRegistry, SpecName, SpecPayload, SpecResult)
import Arbiter.Core.Sql.Jobs
  ( ArchiveSortColumn
  , DLQSortColumn
  , JobSortColumn
  , SortDir
  , archiveSortColumnName
  , dlqSortColumnName
  , jobSortColumnName
  , sortDirSql
  )
import Data.Int (Int64)
import Data.Kind (Type)
import Data.Text (Text)
import Data.Text qualified as T
import Data.Time (UTCTime)
import Data.UUID.Types (UUID)
import GHC.Generics (Generic)
import Servant.API

import Arbiter.Servant.Types

-- | Case-insensitive lookup of an enum value by its canonical name.
parseEnum :: (Bounded a, Enum a) => (a -> Text) -> Text -> Either Text a
parseEnum :: forall a.
(Bounded a, Enum a) =>
(a -> Text) -> Text -> Either Text a
parseEnum a -> Text
toName Text
input =
  Either Text a -> (a -> Either Text a) -> Maybe a -> Either Text a
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (Text -> Either Text a
forall a b. a -> Either a b
Left (Text -> Either Text a) -> Text -> Either Text a
forall a b. (a -> b) -> a -> b
$ Text
"unknown value: " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
input) a -> Either Text a
forall a b. b -> Either a b
Right (Text -> [(Text, a)] -> Maybe a
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup (Text -> Text
T.toLower Text
input) [(Text, a)]
table)
  where
    table :: [(Text, a)]
table = [(Text -> Text
T.toLower (a -> Text
toName a
value), a
value) | a
value <- [a
forall a. Bounded a => a
minBound .. a
forall a. Bounded a => a
maxBound]]

instance FromHttpApiData JobSortColumn where
  parseQueryParam :: Text -> Either Text JobSortColumn
parseQueryParam = (JobSortColumn -> Text) -> Text -> Either Text JobSortColumn
forall a.
(Bounded a, Enum a) =>
(a -> Text) -> Text -> Either Text a
parseEnum JobSortColumn -> Text
jobSortColumnName

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

instance FromHttpApiData DLQSortColumn where
  parseQueryParam :: Text -> Either Text DLQSortColumn
parseQueryParam = (DLQSortColumn -> Text) -> Text -> Either Text DLQSortColumn
forall a.
(Bounded a, Enum a) =>
(a -> Text) -> Text -> Either Text a
parseEnum DLQSortColumn -> Text
dlqSortColumnName

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

instance FromHttpApiData ArchiveSortColumn where
  parseQueryParam :: Text -> Either Text ArchiveSortColumn
parseQueryParam = (ArchiveSortColumn -> Text)
-> Text -> Either Text ArchiveSortColumn
forall a.
(Bounded a, Enum a) =>
(a -> Text) -> Text -> Either Text a
parseEnum ArchiveSortColumn -> Text
archiveSortColumnName

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

instance FromHttpApiData SortDir where
  parseQueryParam :: Text -> Either Text SortDir
parseQueryParam = (SortDir -> Text) -> Text -> Either Text SortDir
forall a.
(Bounded a, Enum a) =>
(a -> Text) -> Text -> Either Text a
parseEnum SortDir -> Text
sortDirSql

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

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

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

-- | One queue's job routes.
data JobsAPI payload result mode = JobsAPI
  { -- GET /:table/jobs?limit=N&offset=N&group_key=X&parent_id=N&job_id=N&roots_only&status=S&claimed_by=UUID&payload=text&rate_limit_prefix=X&concurrency_prefix=X&sort_by=...&sort_dir=...
    forall {k} payload result (mode :: k).
JobsAPI payload result mode
-> mode
   :- (QueryParam "limit" Int
       :> (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
        :- QueryParam "limit" Int
          :> 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)
  , -- POST /:table/jobs (insert new job)
    forall {k} payload result (mode :: k).
JobsAPI payload result mode
-> mode
   :- (ReqBody '[JSON] (ApiJobWrite payload)
       :> Post '[JSON] (JobResponse (ApiJob payload)))
insertJob
      :: mode
        :- ReqBody '[JSON] (ApiJobWrite payload)
          :> Post '[JSON] (JobResponse (ApiJob payload))
  , -- POST /:table/jobs/batch (insert multiple jobs)
    forall {k} payload result (mode :: k).
JobsAPI payload result mode
-> mode
   :- ("batch"
       :> (ReqBody '[JSON] (BatchInsertRequest payload)
           :> Post '[JSON] (BatchInsertResponse payload)))
insertJobsBatch
      :: mode
        :- "batch"
          :> ReqBody '[JSON] (BatchInsertRequest payload)
          :> Post '[JSON] (BatchInsertResponse payload)
  , -- GET /:table/jobs/:id
    forall {k} payload result (mode :: k).
JobsAPI payload result mode
-> mode
   :- (Capture "id" Int64
       :> Get '[JSON] (JobResponse (ApiJobWithStatus payload)))
getJob
      :: mode
        :- Capture "id" Int64
          :> Get '[JSON] (JobResponse (ApiJobWithStatus payload))
  , -- DELETE /:table/jobs/:id (cancel job)
    forall {k} payload result (mode :: k).
JobsAPI payload result mode
-> mode :- (Capture "id" Int64 :> DeleteNoContent)
cancelJob
      :: mode
        :- Capture "id" Int64
          :> DeleteNoContent
  , -- POST /:table/jobs/:id/force-cancel (cascade-delete + interrupt running handler)
    forall {k} payload result (mode :: k).
JobsAPI payload result mode
-> mode
   :- (Capture "id" Int64 :> ("force-cancel" :> PostNoContent))
forceCancelJob
      :: mode
        :- Capture "id" Int64
          :> "force-cancel"
          :> PostNoContent
  , -- POST /:table/jobs/:id/ack (complete a job this caller holds)
    forall {k} payload result (mode :: k).
JobsAPI payload result mode
-> mode
   :- (Capture "id" Int64
       :> ("ack"
           :> (ReqBody '[JSON] (AckRequest result) :> PostNoContent)))
ackClaimedJob
      :: mode
        :- Capture "id" Int64
          :> "ack"
          :> ReqBody '[JSON] (AckRequest result)
          :> PostNoContent
  , -- POST /:table/jobs/:id/nack (hand back a job this caller holds)
    forall {k} payload result (mode :: k).
JobsAPI payload result mode
-> mode
   :- (Capture "id" Int64
       :> ("nack" :> (ReqBody '[JSON] JobLease :> PostNoContent)))
nackClaimedJob
      :: mode
        :- Capture "id" Int64
          :> "nack"
          :> ReqBody '[JSON] JobLease
          :> PostNoContent
  , -- POST /:table/jobs/:id/extend (push out the lease this caller holds)
    forall {k} payload result (mode :: k).
JobsAPI payload result mode
-> mode
   :- (Capture "id" Int64
       :> ("extend" :> (ReqBody '[JSON] ExtendRequest :> PostNoContent)))
extendClaimedJob
      :: mode
        :- Capture "id" Int64
          :> "extend"
          :> ReqBody '[JSON] ExtendRequest
          :> PostNoContent
  , -- POST /:table/jobs/:id/promote
    forall {k} payload result (mode :: k).
JobsAPI payload result mode
-> mode :- (Capture "id" Int64 :> ("promote" :> PostNoContent))
promoteJob
      :: mode
        :- Capture "id" Int64
          :> "promote"
          :> PostNoContent
  , -- POST /:table/jobs/:id/move-to-dlq
    forall {k} payload result (mode :: k).
JobsAPI payload result mode
-> mode :- (Capture "id" Int64 :> ("move-to-dlq" :> PostNoContent))
moveToDLQ
      :: mode
        :- Capture "id" Int64
          :> "move-to-dlq"
          :> PostNoContent
  , -- POST /:table/jobs/:id/pause-children
    forall {k} payload result (mode :: k).
JobsAPI payload result mode
-> mode
   :- (Capture "id" Int64 :> ("pause-children" :> PostNoContent))
pauseChildren
      :: mode
        :- Capture "id" Int64
          :> "pause-children"
          :> PostNoContent
  , -- POST /:table/jobs/:id/resume-children
    forall {k} payload result (mode :: k).
JobsAPI payload result mode
-> mode
   :- (Capture "id" Int64 :> ("resume-children" :> PostNoContent))
resumeChildren
      :: mode
        :- Capture "id" Int64
          :> "resume-children"
          :> PostNoContent
  , -- POST /:table/jobs/:id/suspend
    forall {k} payload result (mode :: k).
JobsAPI payload result mode
-> mode :- (Capture "id" Int64 :> ("suspend" :> PostNoContent))
suspendJob
      :: mode
        :- Capture "id" Int64
          :> "suspend"
          :> PostNoContent
  , -- POST /:table/jobs/:id/resume
    forall {k} payload result (mode :: k).
JobsAPI payload result mode
-> mode :- (Capture "id" Int64 :> ("resume" :> PostNoContent))
resumeJob
      :: mode
        :- Capture "id" Int64
          :> "resume"
          :> PostNoContent
  }
  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
  { -- GET /:table/dlq?limit=N&offset=N&parent_id=N&job_id=N&group_key=X&sort_by=...&sort_dir=...
    forall {k} payload (mode :: k).
DLQAPI payload mode
-> mode
   :- (QueryParam "limit" Int
       :> (QueryParam "offset" Int
           :> (QueryParam "parent_id" Int64
               :> (QueryParam "job_id" Int64
                   :> (QueryParam "group_key" Text
                       :> (QueryParam "kind" Text
                           :> (QueryParam "sort_by" DLQSortColumn
                               :> (QueryParam "sort_dir" SortDir
                                   :> Get '[JSON] (DLQResponse payload)))))))))
listDLQ
      :: mode
        :- QueryParam "limit" Int
          :> QueryParam "offset" Int
          :> QueryParam "parent_id" Int64
          :> QueryParam "job_id" Int64
          :> QueryParam "group_key" Text
          :> QueryParam "kind" Text
          :> QueryParam "sort_by" DLQSortColumn
          :> QueryParam "sort_dir" SortDir
          :> Get '[JSON] (DLQResponse payload)
  , -- POST /:table/dlq/:id/retry (move back to main queue)
    forall {k} payload (mode :: k).
DLQAPI payload mode
-> mode :- (Capture "id" Int64 :> ("retry" :> PostNoContent))
retryFromDLQ
      :: mode
        :- Capture "id" Int64
          :> "retry"
          :> PostNoContent
  , -- DELETE /:table/dlq/:id (permanently delete)
    forall {k} payload (mode :: k).
DLQAPI payload mode
-> mode :- (Capture "id" Int64 :> DeleteNoContent)
deleteDLQ
      :: mode
        :- Capture "id" Int64
          :> DeleteNoContent
  , -- POST /:table/dlq/batch-delete
    forall {k} payload (mode :: k).
DLQAPI payload mode
-> mode
   :- ("batch-delete"
       :> (ReqBody '[JSON] BatchDeleteRequest
           :> Post '[JSON] BatchDeleteResponse))
deleteDLQBatch
      :: mode
        :- "batch-delete"
          :> ReqBody '[JSON] BatchDeleteRequest
          :> Post '[JSON] BatchDeleteResponse
  }
  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
  { -- GET /:table/archive?limit=N&offset=N&parent_id=N&job_id=N&group_key=X&completed_after=T&completed_before=T&sort_by=...&sort_dir=...
    forall {k} payload (mode :: k).
ArchiveAPI payload mode
-> mode
   :- (QueryParam "limit" Int
       :> (QueryParam "offset" Int
           :> (QueryParam "parent_id" Int64
               :> (QueryParam "job_id" Int64
                   :> (QueryParam "group_key" Text
                       :> (QueryParam "kind" Text
                           :> (QueryParam "completed_after" UTCTime
                               :> (QueryParam "completed_before" UTCTime
                                   :> (QueryParam "sort_by" ArchiveSortColumn
                                       :> (QueryParam "sort_dir" SortDir
                                           :> Get '[JSON] (ArchiveResponse payload)))))))))))
listArchive
      :: mode
        :- QueryParam "limit" Int
          :> QueryParam "offset" Int
          :> QueryParam "parent_id" Int64
          :> QueryParam "job_id" Int64
          :> QueryParam "group_key" Text
          :> QueryParam "kind" Text
          :> QueryParam "completed_after" UTCTime
          :> QueryParam "completed_before" UTCTime
          :> QueryParam "sort_by" ArchiveSortColumn
          :> QueryParam "sort_dir" SortDir
          :> Get '[JSON] (ArchiveResponse payload)
  , -- POST /:table/archive/:id/reenqueue (re-run as a fresh job)
    forall {k} payload (mode :: k).
ArchiveAPI payload mode
-> mode :- (Capture "id" Int64 :> ("reenqueue" :> PostNoContent))
reEnqueueArchive
      :: mode
        :- Capture "id" Int64
          :> "reenqueue"
          :> PostNoContent
  , -- DELETE /:table/archive/:id (purge one)
    forall {k} payload (mode :: k).
ArchiveAPI payload mode
-> mode :- (Capture "id" Int64 :> DeleteNoContent)
deleteArchive
      :: mode
        :- Capture "id" Int64
          :> DeleteNoContent
  , -- POST /:table/archive/batch-delete (bulk purge)
    forall {k} payload (mode :: k).
ArchiveAPI payload mode
-> mode
   :- ("batch-delete"
       :> (ReqBody '[JSON] BatchDeleteRequest
           :> Post '[JSON] BatchDeleteResponse))
deleteArchiveBatch
      :: mode
        :- "batch-delete"
          :> ReqBody '[JSON] BatchDeleteRequest
          :> Post '[JSON] BatchDeleteResponse
  }
  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
  { -- GET /:table/stats
    forall {k} (mode :: k).
StatsAPI mode -> mode :- Get '[JSON] StatsResponse
getStats
      :: mode
        :- Get '[JSON] StatsResponse
  }
  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
  { -- POST /maintenance (run one gated maintenance pass)
    forall {k} (mode :: k).
MaintenanceAPI mode -> mode :- Post '[JSON] MaintenanceResponse
runMaintenance
      :: mode
        :- Post '[JSON] MaintenanceResponse
  }
  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)
  , -- POST /:table/claim (lease visible jobs)
    forall {k} payload result (mode :: k).
TableAPI payload result mode
-> mode
   :- ("claim"
       :> (ReqBody '[JSON] ClaimRequest
           :> Post '[JSON] (ClaimResponse payload)))
claimJobs
      :: mode
        :- "claim"
          :> ReqBody '[JSON] ClaimRequest
          :> Post '[JSON] (ClaimResponse payload)
  , forall {k} payload result (mode :: k).
TableAPI payload result mode
-> mode :- ("dlq" :> NamedRoutes (DLQAPI payload))
dlq :: mode :- "dlq" :> NamedRoutes (DLQAPI payload)
  , forall {k} payload result (mode :: k).
TableAPI payload result mode
-> mode :- ("archive" :> NamedRoutes (ArchiveAPI payload))
archive :: mode :- "archive" :> NamedRoutes (ArchiveAPI payload)
  , forall {k} payload result (mode :: k).
TableAPI payload result mode
-> mode :- ("stats" :> NamedRoutes StatsAPI)
stats :: mode :- "stats" :> NamedRoutes StatsAPI
  , forall {k} payload result (mode :: k).
TableAPI payload result mode
-> mode :- ("kinds" :> Get '[JSON] [Text])
listKinds :: mode :- "kinds" :> Get '[JSON] [Text]
  }
  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
  { -- GET /queues
    forall {k} (mode :: k).
QueuesAPI mode -> mode :- Get '[JSON] QueuesResponse
listQueues
      :: mode
        :- Get '[JSON] QueuesResponse
  , -- GET /queues/stats
    forall {k} (mode :: k).
QueuesAPI mode -> mode :- ("stats" :> Get '[JSON] AllStatsResponse)
getAllStats
      :: mode
        :- "stats"
          :> Get '[JSON] AllStatsResponse
  , -- GET /queues/:queue/details
    forall {k} (mode :: k).
QueuesAPI mode
-> mode
   :- (Capture "queue" Text
       :> ("details" :> Get '[JSON] (Maybe QueueRow)))
getDetails
      :: mode
        :- Capture "queue" Text
          :> "details"
          :> Get '[JSON] (Maybe QueueRow)
  , -- POST /queues/:queue/pause
    forall {k} (mode :: k).
QueuesAPI mode
-> mode :- (Capture "queue" Text :> ("pause" :> PostNoContent))
pauseQueue
      :: mode
        :- Capture "queue" Text
          :> "pause"
          :> PostNoContent
  , -- POST /queues/:queue/resume
    forall {k} (mode :: k).
QueuesAPI mode
-> mode :- (Capture "queue" Text :> ("resume" :> PostNoContent))
resumeQueue
      :: mode
        :- Capture "queue" Text
          :> "resume"
          :> PostNoContent
  }
  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
  { -- GET /cron/schedules
    --
    -- Optional @?queue=name@ scopes the result to a single queue.
    forall {k} (mode :: k).
CronAPI mode
-> mode
   :- ("schedules"
       :> (QueryParam "queue" Text :> Get '[JSON] CronSchedulesResponse))
listSchedules
      :: mode
        :- "schedules"
          :> QueryParam "queue" Text
          :> Get '[JSON] CronSchedulesResponse
  , -- PATCH /cron/schedules/:name
    forall {k} (mode :: k).
CronAPI mode
-> mode
   :- ("schedules"
       :> (Capture "name" Text
           :> (ReqBody '[JSON] CronScheduleUpdate
               :> Patch '[JSON] CronScheduleView)))
updateSchedule
      :: mode
        :- "schedules"
          :> Capture "name" Text
          :> ReqBody '[JSON] CronScheduleUpdate
          :> Patch '[JSON] CronScheduleView
  , -- POST /cron/schedules/:name/run
    forall {k} (mode :: k).
CronAPI mode
-> mode
   :- ("schedules"
       :> (Capture "name" Text :> ("run" :> PostNoContent)))
runSchedule
      :: mode
        :- "schedules"
          :> Capture "name" Text
          :> "run"
          :> PostNoContent
  }
  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
  { -- 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
   :- (QueryParam "queue" Text
       :> (QueryParam "live" Double :> Get '[JSON] WorkersResponse))
listWorkers
      :: mode
        :- QueryParam "queue" Text
          :> QueryParam "live" Double
          :> Get '[JSON] WorkersResponse
  , -- POST /workers/:id/pause
    --
    -- Sets the worker's @paused@ flag.
    forall {k} (mode :: k).
WorkersAPI mode
-> mode :- (Capture "id" UUID :> ("pause" :> PostNoContent))
pauseWorker
      :: mode
        :- Capture "id" UUID
          :> "pause"
          :> PostNoContent
  , -- POST /workers/:id/resume
    forall {k} (mode :: k).
WorkersAPI mode
-> mode :- (Capture "id" UUID :> ("resume" :> PostNoContent))
resumeWorker
      :: mode
        :- Capture "id" UUID
          :> "resume"
          :> PostNoContent
  }
  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 :- Get '[JSON] RateLimitPoliciesResponse
listRateLimits
      :: mode
        :- Get '[JSON] RateLimitPoliciesResponse
  , forall {k} (mode :: k).
RateLimitsAPI mode
-> mode
   :- (Capture "prefix" Text
       :> ("buckets"
           :> (QueryParam "limit" Int
               :> (QueryParam "offset" Int
                   :> Get '[JSON] RateLimitBucketsResponse))))
listRateLimitBuckets
      :: mode
        :- Capture "prefix" Text
          :> "buckets"
          :> QueryParam "limit" Int
          :> QueryParam "offset" Int
          :> Get '[JSON] RateLimitBucketsResponse
  , forall {k} (mode :: k).
RateLimitsAPI mode
-> mode
   :- (Capture "prefix" Text
       :> (ReqBody '[JSON] RateLimitPolicyUpdate
           :> Patch '[JSON] RateLimitPolicyView))
updateRateLimitPolicy
      :: mode
        :- Capture "prefix" Text
          :> ReqBody '[JSON] RateLimitPolicyUpdate
          :> Patch '[JSON] RateLimitPolicyView
  , forall {k} (mode :: k).
RateLimitsAPI mode
-> mode
   :- (Capture "prefix" Text
       :> ("reset" :> Post '[JSON] RateLimitResetResponse))
resetRateLimitBuckets
      :: mode
        :- Capture "prefix" Text
          :> "reset"
          :> Post '[JSON] RateLimitResetResponse
  }
  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 :- Get '[JSON] ConcurrencyPoliciesResponse
listConcurrency
      :: mode
        :- Get '[JSON] ConcurrencyPoliciesResponse
  , forall {k} (mode :: k).
ConcurrencyAPI mode
-> mode
   :- (Capture "prefix" Text
       :> ("keys"
           :> (QueryParam "limit" Int
               :> (QueryParam "offset" Int
                   :> Get '[JSON] ConcurrencyKeysResponse))))
listConcurrencyKeys
      :: mode
        :- Capture "prefix" Text
          :> "keys"
          :> QueryParam "limit" Int
          :> QueryParam "offset" Int
          :> Get '[JSON] ConcurrencyKeysResponse
  , forall {k} (mode :: k).
ConcurrencyAPI mode
-> mode
   :- (Capture "prefix" Text
       :> (ReqBody '[JSON] ConcurrencyPolicyUpdate
           :> Patch '[JSON] ConcurrencyPolicyView))
updateConcurrencyPolicy
      :: mode
        :- Capture "prefix" Text
          :> ReqBody '[JSON] ConcurrencyPolicyUpdate
          :> Patch '[JSON] ConcurrencyPolicyView
  , forall {k} (mode :: k).
ConcurrencyAPI mode
-> mode
   :- ("reconcile" :> Post '[JSON] ConcurrencyReconcileResponse)
reconcileConcurrency
      :: mode
        :- "reconcile"
          :> Post '[JSON] ConcurrencyReconcileResponse
  }
  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
  { -- GET /health
    --
    -- Readiness. Reaches the database and reports its connection counters.
    -- Returns 200 when the database is reachable and 503 when it is down.
    -- Both carry the same body.
    forall {k} (mode :: k).
HealthAPI mode -> mode :- Get '[JSON] HealthResponse
getHealth
      :: mode
        :- Get '[JSON] HealthResponse
  , -- GET /health/live
    --
    -- Liveness. Does not touch the database.
    forall {k} (mode :: k).
HealthAPI mode -> mode :- ("live" :> Get '[JSON] LivenessResponse)
getLiveness
      :: mode
        :- "live"
          :> Get '[JSON] LivenessResponse
  }
  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) =
    (SpecName spec :> NamedRoutes (TableAPI (SpecPayload spec) (SpecResult spec)))
      :<|> RegistryToAPI rest

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