Files
inter-hub/Web/Controller/Api/V2/HubRegistry.hs
tegwick 5d5e810886
Some checks failed
Build and Deploy / build-push-deploy (push) Has been cancelled
feat: add vsm hub metadata
2026-05-19 02:16:39 +02:00

84 lines
3.3 KiB
Haskell

module Web.Controller.Api.V2.HubRegistry where
-- GET /api/v2/hub-registry — list hubs with active manifest summary + GAAF indicator
-- GET /api/v2/hub-registry/:hubId — single hub detail
import Web.Types
import Generated.Types
import IHP.Prelude
import IHP.ControllerPrelude
import Data.Aeson (object, (.=), Value)
import Web.Controller.Api.V2.Auth (requireApiConsumer)
import Application.Helper.ApiRateLimit (checkRateLimitAndLog)
instance Controller ApiV2HubRegistryController where
action ApiV2IndexHubRegistryAction = do
consumer <- requireApiConsumer
checkRateLimitAndLog consumer "GET" "/api/v2/hub-registry"
hubs <- query @Hub |> orderByAsc #name |> fetch
rows <- mapM buildHubJson hubs
renderJson rows
action ApiV2ShowHubRegistryAction { hubId } = do
consumer <- requireApiConsumer
checkRateLimitAndLog consumer "GET" ("/api/v2/hub-registry/" <> tshow hubId)
hub <- fetch hubId
mManifest <- query @HubCapabilityManifest
|> filterWhere (#hubId, hubId)
|> filterWhere (#status, "active")
|> fetchOneOrNothing
mSnapshot <- query @HubHealthSnapshot
|> filterWhere (#hubId, hubId)
|> orderByDesc #computedAt
|> limit 1
|> fetchOneOrNothing
renderJson (hubDetailJson hub mManifest mSnapshot)
-- | Build a summary JSON object for a hub including manifest and health data.
buildHubJson :: (?modelContext :: ModelContext) => Hub -> IO Value
buildHubJson hub = do
mManifest <- query @HubCapabilityManifest
|> filterWhere (#hubId, hub.id)
|> filterWhere (#status, "active")
|> fetchOneOrNothing
mSnapshot <- query @HubHealthSnapshot
|> filterWhere (#hubId, hub.id)
|> orderByDesc #computedAt
|> limit 1
|> fetchOneOrNothing
pure $ hubDetailJson hub mManifest mSnapshot
hubDetailJson :: Hub -> Maybe HubCapabilityManifest -> Maybe HubHealthSnapshot -> Value
hubDetailJson hub mManifest mSnapshot =
let gaafIndicator = case mManifest of
Nothing -> "no_manifest" :: Text
Just m | m.status == "active" -> "compliant"
| otherwise -> "draft_only"
in object
[ "id" .= hub.id
, "name" .= hub.name
, "slug" .= hub.slug
, "domain" .= hub.domain
, "hubKind" .= hub.hubKind
, "hubFamily" .= hub.hubFamily
, "vsmFunction" .= hub.vsmFunction
, "vsmSystem" .= hub.vsmSystem
, "gaafStatus" .= gaafIndicator
, "manifest" .= fmap manifestSummary mManifest
, "healthScore" .= fmap (.healthScore) mSnapshot
, "healthAt" .= fmap (.computedAt) mSnapshot
]
manifestSummary :: HubCapabilityManifest -> Value
manifestSummary m = object
[ "id" .= m.id
, "manifestVersion" .= m.manifestVersion
, "status" .= m.status
, "declaredWidgetTypes" .= m.declaredWidgetTypes
, "declaredEventTypes" .= m.declaredEventTypes
, "declaredAnnotationCategories" .= m.declaredAnnotationCategories
, "declaredPolicyScopes" .= m.declaredPolicyScopes
, "activatedAt" .= m.activatedAt
]