generated from coulomb/repo-seed
docs: align v2 bootstrap api contract
This commit is contained in:
@@ -10,6 +10,7 @@ import IHP.Prelude
|
|||||||
import IHP.ControllerPrelude
|
import IHP.ControllerPrelude
|
||||||
import Data.Aeson (object, (.=), Array, toJSON)
|
import Data.Aeson (object, (.=), Array, toJSON)
|
||||||
import qualified Data.Aeson as A
|
import qualified Data.Aeson as A
|
||||||
|
import qualified Data.Aeson.Key as K
|
||||||
import qualified Data.Vector as V
|
import qualified Data.Vector as V
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
import qualified Data.Text.Encoding as TE
|
import qualified Data.Text.Encoding as TE
|
||||||
@@ -94,11 +95,19 @@ buildOpenApiSpec = do
|
|||||||
, "Hub" .= hubSchema
|
, "Hub" .= hubSchema
|
||||||
, "CreateHubRequest" .= createHubRequestSchema
|
, "CreateHubRequest" .= createHubRequestSchema
|
||||||
, "HubCapabilityManifest" .= manifestSchema
|
, "HubCapabilityManifest" .= manifestSchema
|
||||||
|
, "CreateHubCapabilityManifestRequest" .= createManifestRequestSchema
|
||||||
|
, "UpdateHubCapabilityManifestRequest" .= updateManifestRequestSchema
|
||||||
, "ApiConsumer" .= apiConsumerSchema
|
, "ApiConsumer" .= apiConsumerSchema
|
||||||
|
, "CreateApiConsumerRequest" .= createApiConsumerRequestSchema
|
||||||
, "ApiKey" .= apiKeySchema
|
, "ApiKey" .= apiKeySchema
|
||||||
|
, "CreateApiKeyRequest" .= createApiKeyRequestSchema
|
||||||
|
, "ApiKeyCreatedResponse" .= apiKeyCreatedResponseSchema
|
||||||
, "Widget" .= widgetSchema
|
, "Widget" .= widgetSchema
|
||||||
|
, "CreateWidgetRequest" .= createWidgetRequestSchema
|
||||||
, "InteractionEvent" .= interactionEventSchema
|
, "InteractionEvent" .= interactionEventSchema
|
||||||
|
, "CreateInteractionEventRequest" .= createInteractionEventRequestSchema
|
||||||
, "Annotation" .= annotationSchema
|
, "Annotation" .= annotationSchema
|
||||||
|
, "CreateAnnotationRequest" .= createAnnotationRequestSchema
|
||||||
, "RequirementCandidate" .= rcSchema
|
, "RequirementCandidate" .= rcSchema
|
||||||
, "DecisionRecord" .= drSchema
|
, "DecisionRecord" .= drSchema
|
||||||
, "DeploymentRecord" .= depSchema
|
, "DeploymentRecord" .= depSchema
|
||||||
@@ -106,6 +115,12 @@ buildOpenApiSpec = do
|
|||||||
, "OutcomeCorrelation" .= outcomeCorrelationSchema
|
, "OutcomeCorrelation" .= outcomeCorrelationSchema
|
||||||
, "PatternPerformanceRecord" .= patternPerformanceSchema
|
, "PatternPerformanceRecord" .= patternPerformanceSchema
|
||||||
, "InstitutionalKnowledgeEntry" .= institutionalKnowledgeSchema
|
, "InstitutionalKnowledgeEntry" .= institutionalKnowledgeSchema
|
||||||
|
, "HubRegistryEntry" .= hubRegistryEntrySchema
|
||||||
|
, "HubManifestSummary" .= hubManifestSummarySchema
|
||||||
|
, "WidgetPattern" .= widgetPatternSchema
|
||||||
|
, "WidgetPatternDetail" .= widgetPatternDetailSchema
|
||||||
|
, "WidgetPatternVersion" .= widgetPatternVersionSchema
|
||||||
|
, "PatternAdoptionResponse" .= patternAdoptionResponseSchema
|
||||||
]
|
]
|
||||||
, "securitySchemes" .= object
|
, "securitySchemes" .= object
|
||||||
[ "BearerAuth" .= object
|
[ "BearerAuth" .= object
|
||||||
@@ -134,10 +149,20 @@ buildPaths = object
|
|||||||
]
|
]
|
||||||
, "/hub-capability-manifests/{id}" .= object
|
, "/hub-capability-manifests/{id}" .= object
|
||||||
[ "get" .= showOp "HubCapabilityManifest"
|
[ "get" .= showOp "HubCapabilityManifest"
|
||||||
, "patch" .= writeOpWithSummary "Update HubCapabilityManifest" "HubCapabilityManifest" "UpdateHubCapabilityManifestRequest"
|
, "patch" .= writeOpWithStatusAndParams
|
||||||
|
"Update HubCapabilityManifest"
|
||||||
|
"HubCapabilityManifest"
|
||||||
|
"UpdateHubCapabilityManifestRequest"
|
||||||
|
True
|
||||||
|
"200"
|
||||||
|
[pathParam "id"]
|
||||||
]
|
]
|
||||||
, "/hub-capability-manifests/{id}/activate" .= object
|
, "/hub-capability-manifests/{id}/activate" .= object
|
||||||
[ "post" .= writeOpWithSummary "Activate HubCapabilityManifest" "HubCapabilityManifest" "ActivateHubCapabilityManifestRequest"
|
[ "post" .= postNoBodyOpWithStatusAndParams
|
||||||
|
"Activate HubCapabilityManifest"
|
||||||
|
"HubCapabilityManifest"
|
||||||
|
"200"
|
||||||
|
[pathParam "id"]
|
||||||
]
|
]
|
||||||
, "/api-consumers" .= object
|
, "/api-consumers" .= object
|
||||||
[ "get" .= listOp "ApiConsumer" []
|
[ "get" .= listOp "ApiConsumer" []
|
||||||
@@ -145,7 +170,13 @@ buildPaths = object
|
|||||||
]
|
]
|
||||||
, "/api-consumers/{id}" .= getShowPath "ApiConsumer"
|
, "/api-consumers/{id}" .= getShowPath "ApiConsumer"
|
||||||
, "/api-consumers/{id}/api-keys" .= object
|
, "/api-consumers/{id}/api-keys" .= object
|
||||||
[ "post" .= writeOpWithSummary "Create ApiKey" "ApiKey" "CreateApiKeyRequest"
|
[ "post" .= writeOpWithResponseStatusAndParams
|
||||||
|
"Create ApiKey"
|
||||||
|
"ApiKeyCreatedResponse"
|
||||||
|
"CreateApiKeyRequest"
|
||||||
|
False
|
||||||
|
"201"
|
||||||
|
[pathParam "id"]
|
||||||
]
|
]
|
||||||
, "/widgets" .= object
|
, "/widgets" .= object
|
||||||
[ "get" .= listOp "Widget" []
|
[ "get" .= listOp "Widget" []
|
||||||
@@ -181,11 +212,15 @@ buildPaths = object
|
|||||||
, "/token" .= tokenPath
|
, "/token" .= tokenPath
|
||||||
-- Phase 10 — Hub Registry and Widget Marketplace
|
-- Phase 10 — Hub Registry and Widget Marketplace
|
||||||
, "/hub-registry" .= getListPath "HubRegistryEntry"
|
, "/hub-registry" .= getListPath "HubRegistryEntry"
|
||||||
, "/hub-registry/{hubId}" .= getShowPath "HubRegistryEntry"
|
, "/hub-registry/{hubId}" .= getShowPathWithParam "HubRegistryEntry" "hubId"
|
||||||
, "/widget-patterns" .= getListPath "WidgetPattern"
|
, "/widget-patterns" .= getListPath "WidgetPattern"
|
||||||
, "/widget-patterns/{id}" .= getShowPath "WidgetPattern"
|
, "/widget-patterns/{id}" .= getShowPath "WidgetPatternDetail"
|
||||||
, "/widget-patterns/{id}/adopt" .= object
|
, "/widget-patterns/{id}/adopt" .= object
|
||||||
[ "post" .= writeOp "PatternAdoption" "AdoptPatternRequest"
|
[ "post" .= postNoBodyOpWithStatusAndParams
|
||||||
|
"Adopt WidgetPattern"
|
||||||
|
"PatternAdoptionResponse"
|
||||||
|
"200"
|
||||||
|
[pathParam "id"]
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -197,6 +232,10 @@ getShowPath :: Text -> Value
|
|||||||
getShowPath schemaName = object
|
getShowPath schemaName = object
|
||||||
[ "get" .= showOp schemaName ]
|
[ "get" .= showOp schemaName ]
|
||||||
|
|
||||||
|
getShowPathWithParam :: Text -> Text -> Value
|
||||||
|
getShowPathWithParam schemaName paramName = object
|
||||||
|
[ "get" .= showOpWithParam schemaName paramName ]
|
||||||
|
|
||||||
listOp :: Text -> [(Text, Text, Text)] -> Value
|
listOp :: Text -> [(Text, Text, Text)] -> Value
|
||||||
listOp schemaName extraParams = object
|
listOp schemaName extraParams = object
|
||||||
[ "summary" .= ("List " <> schemaName)
|
[ "summary" .= ("List " <> schemaName)
|
||||||
@@ -230,10 +269,13 @@ listOp schemaName extraParams = object
|
|||||||
]
|
]
|
||||||
|
|
||||||
showOp :: Text -> Value
|
showOp :: Text -> Value
|
||||||
showOp schemaName = object
|
showOp schemaName = showOpWithParam schemaName "id"
|
||||||
|
|
||||||
|
showOpWithParam :: Text -> Text -> Value
|
||||||
|
showOpWithParam schemaName paramName = object
|
||||||
[ "summary" .= ("Get " <> schemaName)
|
[ "summary" .= ("Get " <> schemaName)
|
||||||
, "security" .= [object ["BearerAuth" .= ([] :: [Text])]]
|
, "security" .= [object ["BearerAuth" .= ([] :: [Text])]]
|
||||||
, "parameters" .= [object ["name" .= ("id" :: Text), "in" .= ("path" :: Text), "required" .= True, "schema" .= object ["type" .= ("string" :: Text), "format" .= ("uuid" :: Text)]]]
|
, "parameters" .= [pathParam paramName]
|
||||||
, "responses" .= object
|
, "responses" .= object
|
||||||
[ "200" .= object
|
[ "200" .= object
|
||||||
[ "description" .= ("OK" :: Text)
|
[ "description" .= ("OK" :: Text)
|
||||||
@@ -251,23 +293,65 @@ writeOp :: Text -> Text -> Value
|
|||||||
writeOp schemaName reqSchema = writeOpWithSummary ("Create " <> schemaName) schemaName reqSchema
|
writeOp schemaName reqSchema = writeOpWithSummary ("Create " <> schemaName) schemaName reqSchema
|
||||||
|
|
||||||
writeOpWithSummary :: Text -> Text -> Text -> Value
|
writeOpWithSummary :: Text -> Text -> Text -> Value
|
||||||
writeOpWithSummary summaryText schemaName _reqSchema = object
|
writeOpWithSummary summaryText schemaName reqSchema =
|
||||||
|
writeOpWithStatusAndParams summaryText schemaName reqSchema True "201" []
|
||||||
|
|
||||||
|
writeOpWithStatusAndParams :: Text -> Text -> Text -> Bool -> Text -> [Value] -> Value
|
||||||
|
writeOpWithStatusAndParams = writeOpWithResponseStatusAndParams
|
||||||
|
|
||||||
|
writeOpWithResponseStatusAndParams :: Text -> Text -> Text -> Bool -> Text -> [Value] -> Value
|
||||||
|
writeOpWithResponseStatusAndParams summaryText responseSchema reqSchema bodyRequired successStatus params = object
|
||||||
[ "summary" .= summaryText
|
[ "summary" .= summaryText
|
||||||
, "security" .= [object ["BearerAuth" .= ([] :: [Text])]]
|
, "security" .= [object ["BearerAuth" .= ([] :: [Text])]]
|
||||||
|
, "parameters" .= params
|
||||||
, "requestBody" .= object
|
, "requestBody" .= object
|
||||||
[ "required" .= True
|
[ "required" .= bodyRequired
|
||||||
, "content" .= object
|
, "content" .= object
|
||||||
[ "application/json" .= object
|
[ "application/json" .= object
|
||||||
["schema" .= object ["$ref" .= ("#/components/schemas/" <> schemaName)]]
|
["schema" .= object ["$ref" .= ("#/components/schemas/" <> reqSchema)]]
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
, "responses" .= object
|
, "responses" .= object
|
||||||
[ "201" .= object ["description" .= ("Created" :: Text)]
|
[ K.fromText successStatus .= object
|
||||||
|
[ "description" .= ("OK" :: Text)
|
||||||
|
, "content" .= object
|
||||||
|
[ "application/json" .= object
|
||||||
|
["schema" .= object ["$ref" .= ("#/components/schemas/" <> responseSchema)]]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
, "400" .= object ["description" .= ("Invalid request" :: Text)]
|
||||||
, "401" .= object ["description" .= ("Unauthorized" :: Text)]
|
, "401" .= object ["description" .= ("Unauthorized" :: Text)]
|
||||||
, "422" .= object ["description" .= ("Validation error" :: Text)]
|
, "422" .= object ["description" .= ("Validation error" :: Text)]
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
postNoBodyOpWithStatusAndParams :: Text -> Text -> Text -> [Value] -> Value
|
||||||
|
postNoBodyOpWithStatusAndParams summaryText responseSchema successStatus params = object
|
||||||
|
[ "summary" .= summaryText
|
||||||
|
, "security" .= [object ["BearerAuth" .= ([] :: [Text])]]
|
||||||
|
, "parameters" .= params
|
||||||
|
, "responses" .= object
|
||||||
|
[ K.fromText successStatus .= object
|
||||||
|
[ "description" .= ("OK" :: Text)
|
||||||
|
, "content" .= object
|
||||||
|
[ "application/json" .= object
|
||||||
|
["schema" .= object ["$ref" .= ("#/components/schemas/" <> responseSchema)]]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
, "400" .= object ["description" .= ("Invalid request" :: Text)]
|
||||||
|
, "401" .= object ["description" .= ("Unauthorized" :: Text)]
|
||||||
|
, "422" .= object ["description" .= ("Validation error" :: Text)]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
pathParam :: Text -> Value
|
||||||
|
pathParam name = object
|
||||||
|
[ "name" .= name
|
||||||
|
, "in" .= ("path" :: Text)
|
||||||
|
, "required" .= True
|
||||||
|
, "schema" .= uuidProp
|
||||||
|
]
|
||||||
|
|
||||||
publicListPath :: Text -> Value
|
publicListPath :: Text -> Value
|
||||||
publicListPath schemaName = object
|
publicListPath schemaName = object
|
||||||
[ "get" .= object
|
[ "get" .= object
|
||||||
@@ -360,6 +444,22 @@ widgetSchema = object
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
createWidgetRequestSchema :: Value
|
||||||
|
createWidgetRequestSchema = object
|
||||||
|
[ "type" .= ("object" :: Text)
|
||||||
|
, "required" .= (["hubId", "name", "widgetType"] :: [Text])
|
||||||
|
, "properties" .= object
|
||||||
|
[ "hubId" .= uuidProp
|
||||||
|
, "name" .= strProp
|
||||||
|
, "widgetType" .= object ["$ref" .= ("#/components/schemas/WidgetType" :: Text)]
|
||||||
|
, "capabilityRef" .= strProp
|
||||||
|
, "viewContext" .= strProp
|
||||||
|
, "policyScope" .= object ["$ref" .= ("#/components/schemas/PolicyScope" :: Text)]
|
||||||
|
, "status" .= object ["type" .= ("string" :: Text), "enum" .= ["active" :: Text, "deprecated", "draft"]]
|
||||||
|
, "adapterSpecId" .= uuidProp
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
manifestSchema :: Value
|
manifestSchema :: Value
|
||||||
manifestSchema = object
|
manifestSchema = object
|
||||||
[ "type" .= ("object" :: Text)
|
[ "type" .= ("object" :: Text)
|
||||||
@@ -380,6 +480,32 @@ manifestSchema = object
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
createManifestRequestSchema :: Value
|
||||||
|
createManifestRequestSchema = object
|
||||||
|
[ "type" .= ("object" :: Text)
|
||||||
|
, "required" .= (["hubId"] :: [Text])
|
||||||
|
, "properties" .= manifestRequestProperties True
|
||||||
|
]
|
||||||
|
|
||||||
|
updateManifestRequestSchema :: Value
|
||||||
|
updateManifestRequestSchema = object
|
||||||
|
[ "type" .= ("object" :: Text)
|
||||||
|
, "properties" .= manifestRequestProperties False
|
||||||
|
]
|
||||||
|
|
||||||
|
manifestRequestProperties :: Bool -> Value
|
||||||
|
manifestRequestProperties includeHubId =
|
||||||
|
object $
|
||||||
|
(if includeHubId then ["hubId" .= uuidProp] else [])
|
||||||
|
++ [ "manifestVersion" .= strProp
|
||||||
|
, "declaredWidgetTypes" .= arrayOfRef "WidgetType"
|
||||||
|
, "declaredEventTypes" .= arrayOfRef "EventType"
|
||||||
|
, "declaredAnnotationCategories" .= arrayOfRef "AnnotationCategory"
|
||||||
|
, "declaredPolicyScopes" .= arrayOfRef "PolicyScope"
|
||||||
|
, "capabilityDescription" .= strProp
|
||||||
|
, "contact" .= strProp
|
||||||
|
]
|
||||||
|
|
||||||
apiConsumerSchema :: Value
|
apiConsumerSchema :: Value
|
||||||
apiConsumerSchema = object
|
apiConsumerSchema = object
|
||||||
[ "type" .= ("object" :: Text)
|
[ "type" .= ("object" :: Text)
|
||||||
@@ -397,6 +523,19 @@ apiConsumerSchema = object
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
createApiConsumerRequestSchema :: Value
|
||||||
|
createApiConsumerRequestSchema = object
|
||||||
|
[ "type" .= ("object" :: Text)
|
||||||
|
, "required" .= (["name"] :: [Text])
|
||||||
|
, "properties" .= object
|
||||||
|
[ "name" .= strProp
|
||||||
|
, "description" .= strProp
|
||||||
|
, "hubCapabilityManifestId" .= uuidProp
|
||||||
|
, "rateLimitPerMinute" .= object ["type" .= ("integer" :: Text), "minimum" .= (1 :: Int), "default" .= (60 :: Int)]
|
||||||
|
, "quotaPerDay" .= object ["type" .= ("integer" :: Text), "minimum" .= (1 :: Int), "default" .= (10000 :: Int)]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
apiKeySchema :: Value
|
apiKeySchema :: Value
|
||||||
apiKeySchema = object
|
apiKeySchema = object
|
||||||
[ "type" .= ("object" :: Text)
|
[ "type" .= ("object" :: Text)
|
||||||
@@ -413,6 +552,27 @@ apiKeySchema = object
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
createApiKeyRequestSchema :: Value
|
||||||
|
createApiKeyRequestSchema = object
|
||||||
|
[ "type" .= ("object" :: Text)
|
||||||
|
, "properties" .= object
|
||||||
|
[ "scopes" .= strProp
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
apiKeyCreatedResponseSchema :: Value
|
||||||
|
apiKeyCreatedResponseSchema = object
|
||||||
|
[ "type" .= ("object" :: Text)
|
||||||
|
, "properties" .= object
|
||||||
|
[ "apiKey" .= object ["$ref" .= ("#/components/schemas/ApiKey" :: Text)]
|
||||||
|
, "fullKey" .= object
|
||||||
|
[ "type" .= ("string" :: Text)
|
||||||
|
, "description" .= ("Static API key secret. Returned only in this creation response; it is stored hashed and cannot be recovered later." :: Text)
|
||||||
|
]
|
||||||
|
, "displayOnce" .= boolProp
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
interactionEventSchema :: Value
|
interactionEventSchema :: Value
|
||||||
interactionEventSchema = object
|
interactionEventSchema = object
|
||||||
[ "type" .= ("object" :: Text)
|
[ "type" .= ("object" :: Text)
|
||||||
@@ -428,6 +588,18 @@ interactionEventSchema = object
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
createInteractionEventRequestSchema :: Value
|
||||||
|
createInteractionEventRequestSchema = object
|
||||||
|
[ "type" .= ("object" :: Text)
|
||||||
|
, "required" .= (["widgetId", "eventType"] :: [Text])
|
||||||
|
, "properties" .= object
|
||||||
|
[ "widgetId" .= uuidProp
|
||||||
|
, "eventType" .= object ["$ref" .= ("#/components/schemas/EventType" :: Text)]
|
||||||
|
, "viewContext" .= strProp
|
||||||
|
, "metadata" .= objectProp
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
annotationSchema :: Value
|
annotationSchema :: Value
|
||||||
annotationSchema = object
|
annotationSchema = object
|
||||||
[ "type" .= ("object" :: Text)
|
[ "type" .= ("object" :: Text)
|
||||||
@@ -445,6 +617,17 @@ annotationSchema = object
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
createAnnotationRequestSchema :: Value
|
||||||
|
createAnnotationRequestSchema = object
|
||||||
|
[ "type" .= ("object" :: Text)
|
||||||
|
, "required" .= (["widgetId", "category", "body"] :: [Text])
|
||||||
|
, "properties" .= object
|
||||||
|
[ "widgetId" .= uuidProp
|
||||||
|
, "category" .= object ["$ref" .= ("#/components/schemas/AnnotationCategory" :: Text)]
|
||||||
|
, "body" .= strProp
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
rcSchema :: Value
|
rcSchema :: Value
|
||||||
rcSchema = object
|
rcSchema = object
|
||||||
[ "type" .= ("object" :: Text)
|
[ "type" .= ("object" :: Text)
|
||||||
@@ -546,12 +729,114 @@ institutionalKnowledgeSchema = object
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
hubRegistryEntrySchema :: Value
|
||||||
|
hubRegistryEntrySchema = object
|
||||||
|
[ "type" .= ("object" :: Text)
|
||||||
|
, "properties" .= object
|
||||||
|
[ "id" .= uuidProp
|
||||||
|
, "name" .= strProp
|
||||||
|
, "slug" .= strProp
|
||||||
|
, "domain" .= strProp
|
||||||
|
, "hubKind" .= strProp
|
||||||
|
, "hubFamily" .= strProp
|
||||||
|
, "vsmFunction" .= strProp
|
||||||
|
, "vsmSystem" .= strProp
|
||||||
|
, "gaafStatus" .= object ["type" .= ("string" :: Text), "enum" .= ["compliant" :: Text, "draft_only", "no_manifest"]]
|
||||||
|
, "manifest" .= object ["$ref" .= ("#/components/schemas/HubManifestSummary" :: Text)]
|
||||||
|
, "healthScore" .= intProp
|
||||||
|
, "healthAt" .= dtProp
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
hubManifestSummarySchema :: Value
|
||||||
|
hubManifestSummarySchema = object
|
||||||
|
[ "type" .= ("object" :: Text)
|
||||||
|
, "properties" .= object
|
||||||
|
[ "id" .= uuidProp
|
||||||
|
, "manifestVersion" .= strProp
|
||||||
|
, "status" .= strProp
|
||||||
|
, "declaredWidgetTypes" .= arrayOfRef "WidgetType"
|
||||||
|
, "declaredEventTypes" .= arrayOfRef "EventType"
|
||||||
|
, "declaredAnnotationCategories" .= arrayOfRef "AnnotationCategory"
|
||||||
|
, "declaredPolicyScopes" .= arrayOfRef "PolicyScope"
|
||||||
|
, "activatedAt" .= dtProp
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
widgetPatternSchema :: Value
|
||||||
|
widgetPatternSchema = object
|
||||||
|
[ "type" .= ("object" :: Text)
|
||||||
|
, "properties" .= object
|
||||||
|
[ "id" .= uuidProp
|
||||||
|
, "hubId" .= uuidProp
|
||||||
|
, "name" .= strProp
|
||||||
|
, "description" .= strProp
|
||||||
|
, "widgetType" .= object ["$ref" .= ("#/components/schemas/WidgetType" :: Text)]
|
||||||
|
, "isCrossHub" .= boolProp
|
||||||
|
, "isPublished" .= boolProp
|
||||||
|
, "createdAt" .= dtProp
|
||||||
|
, "updatedAt" .= dtProp
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
widgetPatternDetailSchema :: Value
|
||||||
|
widgetPatternDetailSchema = object
|
||||||
|
[ "type" .= ("object" :: Text)
|
||||||
|
, "properties" .= object
|
||||||
|
[ "pattern" .= object ["$ref" .= ("#/components/schemas/WidgetPattern" :: Text)]
|
||||||
|
, "versions" .= object
|
||||||
|
[ "type" .= ("array" :: Text)
|
||||||
|
, "items" .= object ["$ref" .= ("#/components/schemas/WidgetPatternVersion" :: Text)]
|
||||||
|
]
|
||||||
|
, "adopterCount" .= intProp
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
widgetPatternVersionSchema :: Value
|
||||||
|
widgetPatternVersionSchema = object
|
||||||
|
[ "type" .= ("object" :: Text)
|
||||||
|
, "properties" .= object
|
||||||
|
[ "id" .= uuidProp
|
||||||
|
, "versionNumber" .= intProp
|
||||||
|
, "definition" .= objectProp
|
||||||
|
, "changelog" .= strProp
|
||||||
|
, "publishedAt" .= dtProp
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
patternAdoptionResponseSchema :: Value
|
||||||
|
patternAdoptionResponseSchema = object
|
||||||
|
[ "type" .= ("object" :: Text)
|
||||||
|
, "properties" .= object
|
||||||
|
[ "adopted" .= boolProp
|
||||||
|
, "adoptionId" .= uuidProp
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
uuidProp :: Value
|
uuidProp :: Value
|
||||||
uuidProp = object ["type" .= ("string" :: Text), "format" .= ("uuid" :: Text)]
|
uuidProp = object ["type" .= ("string" :: Text), "format" .= ("uuid" :: Text)]
|
||||||
|
|
||||||
strProp :: Value
|
strProp :: Value
|
||||||
strProp = object ["type" .= ("string" :: Text)]
|
strProp = object ["type" .= ("string" :: Text)]
|
||||||
|
|
||||||
|
intProp :: Value
|
||||||
|
intProp = object ["type" .= ("integer" :: Text)]
|
||||||
|
|
||||||
|
boolProp :: Value
|
||||||
|
boolProp = object ["type" .= ("boolean" :: Text)]
|
||||||
|
|
||||||
|
objectProp :: Value
|
||||||
|
objectProp = object
|
||||||
|
[ "type" .= ("object" :: Text)
|
||||||
|
, "additionalProperties" .= True
|
||||||
|
]
|
||||||
|
|
||||||
|
arrayOfRef :: Text -> Value
|
||||||
|
arrayOfRef schemaName = object
|
||||||
|
[ "type" .= ("array" :: Text)
|
||||||
|
, "items" .= object ["$ref" .= ("#/components/schemas/" <> schemaName)]
|
||||||
|
]
|
||||||
|
|
||||||
dtProp :: Value
|
dtProp :: Value
|
||||||
dtProp = object ["type" .= ("string" :: Text), "format" .= ("date-time" :: Text)]
|
dtProp = object ["type" .= ("string" :: Text), "format" .= ("date-time" :: Text)]
|
||||||
|
|
||||||
|
|||||||
@@ -126,14 +126,28 @@ Domain hubs may register additional event types via `HubCapabilityManifest`.
|
|||||||
|
|
||||||
## Phase 9 Extension: `/api/v2/` (IHUB-WP-0010)
|
## Phase 9 Extension: `/api/v2/` (IHUB-WP-0010)
|
||||||
|
|
||||||
The v2 API supersedes per-hub Bearer tokens with OAuth 2.0 client credentials.
|
The v2 API supports authenticated Bearer access with static API keys and, where
|
||||||
|
configured, OAuth 2.0 client credentials.
|
||||||
|
|
||||||
**OpenAPI spec:** `/api/v2/openapi.json` (live-generated; `widget_type`,
|
**OpenAPI spec:** `/api/v2/openapi.json` (live-generated; `widget_type`,
|
||||||
`event_type`, and `category` fields carry `enum` arrays from the type registries)
|
`event_type`, and `category` fields carry `enum` arrays from the type registries)
|
||||||
|
|
||||||
**New endpoints in v2:**
|
**New endpoints in v2:**
|
||||||
- `POST /api/v2/token` — OAuth 2.0 client credentials token exchange
|
- `POST /api/v2/token` — OAuth 2.0 client credentials token exchange
|
||||||
|
- `GET /api/v2/hubs` / `POST /api/v2/hubs` — list or create hubs, including
|
||||||
|
first-class VSM hub metadata
|
||||||
|
- `GET /api/v2/hub-capability-manifests` / `POST /api/v2/hub-capability-manifests`
|
||||||
|
— list or create hub capability manifests
|
||||||
|
- `PATCH /api/v2/hub-capability-manifests/{id}` — update draft manifest
|
||||||
|
vocabulary and metadata
|
||||||
|
- `POST /api/v2/hub-capability-manifests/{id}/activate` — activate a draft
|
||||||
|
manifest and register its declared vocabulary
|
||||||
|
- `GET /api/v2/api-consumers` / `POST /api/v2/api-consumers` — list or create
|
||||||
|
API consumers
|
||||||
|
- `POST /api/v2/api-consumers/{id}/api-keys` — create a static API key; the
|
||||||
|
raw `fullKey` is returned exactly once
|
||||||
- `GET /api/v2/widgets` — paginated widget listing
|
- `GET /api/v2/widgets` — paginated widget listing
|
||||||
|
- `POST /api/v2/widgets` — create a widget
|
||||||
- `GET /api/v2/interaction-events` — paginated event listing
|
- `GET /api/v2/interaction-events` — paginated event listing
|
||||||
- `POST /api/v2/interaction-events` — submit event (registry-validated)
|
- `POST /api/v2/interaction-events` — submit event (registry-validated)
|
||||||
- `GET /api/v2/annotations` — paginated annotation listing
|
- `GET /api/v2/annotations` — paginated annotation listing
|
||||||
|
|||||||
@@ -3,7 +3,9 @@
|
|||||||
**Audience:** A developer starting a new domain hub (dev-hub, ops-hub, fin-hub, etc.)
|
**Audience:** A developer starting a new domain hub (dev-hub, ops-hub, fin-hub, etc.)
|
||||||
that will live in its own repository and use inter-hub as the governance substrate.
|
that will live in its own repository and use inter-hub as the governance substrate.
|
||||||
|
|
||||||
**Current state:** inter-hub v0.2.0-alpha.1 is running at `http://192.168.178.135:8080`.
|
**Current state:** inter-hub v0.2.0-alpha.1 exposes its supported integration
|
||||||
|
surface under `/api/v2`. The examples below use `$IHUB_BASE`; point it at the
|
||||||
|
environment you are bootstrapping against.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -30,76 +32,142 @@ or type-safe access to inter-hub's data model.
|
|||||||
|
|
||||||
## Pattern A — API Consumer Hub
|
## Pattern A — API Consumer Hub
|
||||||
|
|
||||||
### 1. Create an API consumer in inter-hub
|
### 1. Start with an operator API key
|
||||||
|
|
||||||
Go to `http://192.168.178.135:8080/ApiConsumers` → New.
|
Every write call below requires `Authorization: Bearer <key>`. Use an existing
|
||||||
|
operator/admin API key for the first bootstrap call. New hub-specific keys can
|
||||||
Fill in:
|
then be created through the API and should replace the operator key for normal
|
||||||
- **Name:** `dev-hub` (or your hub name)
|
runtime traffic.
|
||||||
- **Contact:** your team email
|
|
||||||
- **Description:** what this hub does
|
|
||||||
|
|
||||||
After creating the consumer, go to **API Keys → New** and generate a key
|
|
||||||
for this consumer. Copy the key — it is shown only once.
|
|
||||||
|
|
||||||
### 2. Register your hub
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -X POST http://192.168.178.135:8080/api/v2/hubs \
|
export IHUB_BASE="http://127.0.0.1:8000"
|
||||||
-H "Authorization: Bearer <your-api-key>" \
|
export IHUB_OPERATOR_KEY="<existing-operator-api-key>"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Register the VSM Operations hub
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -s -X POST "$IHUB_BASE/api/v2/hubs" \
|
||||||
|
-H "Authorization: Bearer $IHUB_OPERATOR_KEY" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d '{
|
-d '{
|
||||||
"name": "Dev Hub",
|
"name": "Operations Hub",
|
||||||
"slug": "dev-hub",
|
"slug": "ops-hub",
|
||||||
"domain": "dev.example.com",
|
"domain": "operations",
|
||||||
"hubKind": "domain"
|
"hubKind": "domain",
|
||||||
|
"hubFamily": "vsm",
|
||||||
|
"vsmFunction": "operations",
|
||||||
|
"vsmSystem": "1"
|
||||||
}'
|
}'
|
||||||
```
|
```
|
||||||
|
|
||||||
Save the returned `id` — this is your `hubId` for all subsequent calls.
|
Save the returned `id` — this is your `hubId` for all subsequent calls.
|
||||||
|
|
||||||
### 3. Register your type vocabulary
|
### 3. Register and activate the ops-hub manifest
|
||||||
|
|
||||||
Before creating widgets with domain-specific types, register them via the
|
|
||||||
inter-hub UI at `/HubCapabilityManifests` → New → select your hub.
|
|
||||||
|
|
||||||
In the manifest editor, declare your types:
|
|
||||||
```json
|
|
||||||
["dev-pipeline-run", "dev-pr-review", "dev-build-status"]
|
|
||||||
```
|
|
||||||
|
|
||||||
Click **Activate**. See `docs/domain-hub-extension-guide.md` for the full
|
|
||||||
naming rules and conflict-resolution workflow.
|
|
||||||
|
|
||||||
### 4. Register widgets
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -X POST http://192.168.178.135:8080/api/v2/widgets \
|
curl -s -X POST "$IHUB_BASE/api/v2/hub-capability-manifests" \
|
||||||
-H "Authorization: Bearer <your-api-key>" \
|
-H "Authorization: Bearer $IHUB_OPERATOR_KEY" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d '{
|
-d '{
|
||||||
"name": "Pipeline Status Panel",
|
"hubId": "<ops-hub-id>",
|
||||||
"widgetType": "dev-pipeline-run",
|
"manifestVersion": "1.0",
|
||||||
"hubId": "<your-hub-id>",
|
"declaredWidgetTypes": ["ops-endpoint-card"],
|
||||||
"viewContext": "pipeline-dashboard"
|
"declaredEventTypes": ["ops-endpoint-verified"],
|
||||||
|
"declaredAnnotationCategories": ["ops-risk"],
|
||||||
|
"declaredPolicyScopes": ["ops-internal"],
|
||||||
|
"capabilityDescription": "Operations inventory and endpoint verification",
|
||||||
|
"contact": "ops@example.com"
|
||||||
}'
|
}'
|
||||||
```
|
```
|
||||||
|
|
||||||
### 5. Record interaction events
|
Then activate the returned manifest:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -X POST http://192.168.178.135:8080/api/v2/interaction-events \
|
curl -s -X POST "$IHUB_BASE/api/v2/hub-capability-manifests/<manifest-id>/activate" \
|
||||||
-H "Authorization: Bearer <your-api-key>" \
|
-H "Authorization: Bearer $IHUB_OPERATOR_KEY"
|
||||||
|
```
|
||||||
|
|
||||||
|
Activation registers the declared vocabulary. Domain-owned widget types,
|
||||||
|
event types, annotation categories, and policy scopes must be declared here
|
||||||
|
before use.
|
||||||
|
|
||||||
|
### 4. Create an ops-hub API consumer and key
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -s -X POST "$IHUB_BASE/api/v2/api-consumers" \
|
||||||
|
-H "Authorization: Bearer $IHUB_OPERATOR_KEY" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"name": "ops-hub",
|
||||||
|
"description": "Operations hub runtime client",
|
||||||
|
"hubCapabilityManifestId": "<active-manifest-id>",
|
||||||
|
"rateLimitPerMinute": 120,
|
||||||
|
"quotaPerDay": 50000
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
Create the static key for the returned consumer:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -s -X POST "$IHUB_BASE/api/v2/api-consumers/<api-consumer-id>/api-keys" \
|
||||||
|
-H "Authorization: Bearer $IHUB_OPERATOR_KEY" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"scopes": "ops:write"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
The response contains `fullKey` exactly once. Store it in the hub runtime
|
||||||
|
secret store and use it for all following calls:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export OPS_HUB_KEY="<fullKey-from-create-api-key-response>"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Register widgets
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -s -X POST "$IHUB_BASE/api/v2/widgets" \
|
||||||
|
-H "Authorization: Bearer $OPS_HUB_KEY" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"name": "CoulombCore Gitea Registry",
|
||||||
|
"widgetType": "ops-endpoint-card",
|
||||||
|
"hubId": "<ops-hub-id>",
|
||||||
|
"viewContext": "operations-inventory",
|
||||||
|
"policyScope": "ops-internal"
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. Record interaction events
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -s -X POST "$IHUB_BASE/api/v2/interaction-events" \
|
||||||
|
-H "Authorization: Bearer $OPS_HUB_KEY" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d '{
|
-d '{
|
||||||
"widgetId": "<widget-id>",
|
"widgetId": "<widget-id>",
|
||||||
"eventType": "clicked",
|
"eventType": "ops-endpoint-verified",
|
||||||
"userId": "<optional-user-id>",
|
"viewContext": "registry-readiness",
|
||||||
"payload": {"button": "retry", "pipeline": "main-ci"}
|
"metadata": {
|
||||||
|
"service": "gitea",
|
||||||
|
"endpoint": "https://gitea.coulomb.social/v2/",
|
||||||
|
"result": "auth-challenge-ok"
|
||||||
|
}
|
||||||
}'
|
}'
|
||||||
```
|
```
|
||||||
|
|
||||||
### 6. What you get for free
|
### 7. Verify the bootstrap
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -s "$IHUB_BASE/api/v2/interaction-events?widgetId=<widget-id>&eventType=ops-endpoint-verified" \
|
||||||
|
-H "Authorization: Bearer $OPS_HUB_KEY"
|
||||||
|
```
|
||||||
|
|
||||||
|
The event should appear with the submitted `metadata`. If the API returns
|
||||||
|
`event_type_not_in_manifest`, check that the API consumer is bound to the
|
||||||
|
active ops-hub manifest and that the event type was declared before activation.
|
||||||
|
|
||||||
|
### 8. What you get for free
|
||||||
|
|
||||||
Once events are flowing, the inter-hub framework automatically provides:
|
Once events are flowing, the inter-hub framework automatically provides:
|
||||||
- Annotation collection on any widget
|
- Annotation collection on any widget
|
||||||
@@ -109,8 +177,8 @@ Once events are flowing, the inter-hub framework automatically provides:
|
|||||||
- Outcome signals and regression detection
|
- Outcome signals and regression detection
|
||||||
- Widget marketplace discovery
|
- Widget marketplace discovery
|
||||||
|
|
||||||
Your hub only needs to POST events. Everything downstream is managed by
|
Your hub only needs to register its vocabulary, seed meaningful widgets, and
|
||||||
inter-hub.
|
POST events. Everything downstream is managed by inter-hub.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -153,16 +221,16 @@ import IHP.Prelude
|
|||||||
import Network.HTTP.Simple
|
import Network.HTTP.Simple
|
||||||
|
|
||||||
postEvent :: Text -> Text -> Text -> Value -> IO ()
|
postEvent :: Text -> Text -> Text -> Value -> IO ()
|
||||||
postEvent apiKey widgetId eventType payload = do
|
postEvent apiKey widgetId eventType metadata = do
|
||||||
let req = setRequestMethod "POST"
|
let req = setRequestMethod "POST"
|
||||||
$ setRequestHeader "Authorization" ["Bearer " <> cs apiKey]
|
$ setRequestHeader "Authorization" ["Bearer " <> cs apiKey]
|
||||||
$ setRequestHeader "Content-Type" ["application/json"]
|
$ setRequestHeader "Content-Type" ["application/json"]
|
||||||
$ setRequestBodyJSON (object
|
$ setRequestBodyJSON (object
|
||||||
[ "widgetId" .= widgetId
|
[ "widgetId" .= widgetId
|
||||||
, "eventType" .= eventType
|
, "eventType" .= eventType
|
||||||
, "payload" .= payload
|
, "metadata" .= metadata
|
||||||
])
|
])
|
||||||
$ parseRequest_ "http://192.168.178.135:8080/api/v2/interaction-events"
|
$ parseRequest_ "http://127.0.0.1:8000/api/v2/interaction-events"
|
||||||
void $ httpLBS req
|
void $ httpLBS req
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -239,6 +307,7 @@ every domain hub:
|
|||||||
- `WidgetEnvelope` helpers — consistent widget wrapping across hubs
|
- `WidgetEnvelope` helpers — consistent widget wrapping across hubs
|
||||||
- `InterHubClient` — typed API client with retry and auth built in
|
- `InterHubClient` — typed API client with retry and auth built in
|
||||||
- `HubCapabilityManifest` bootstrap — auto-activate manifest on startup
|
- `HubCapabilityManifest` bootstrap — auto-activate manifest on startup
|
||||||
|
(planned; use the API recipe above today)
|
||||||
- Shared `defaultLayout` with inter-hub navigation integration
|
- Shared `defaultLayout` with inter-hub navigation integration
|
||||||
|
|
||||||
Until `hub-core` exists, copy the client helper above and the 3-step
|
Until `hub-core` exists, copy the client helper above and the 3-step
|
||||||
@@ -248,10 +317,11 @@ registration pattern into your new hub. It is ~50 lines of boilerplate.
|
|||||||
|
|
||||||
## Checklist for a New Hub
|
## Checklist for a New Hub
|
||||||
|
|
||||||
- [ ] Create ApiConsumer + ApiKey in inter-hub UI
|
- [ ] Start with an existing operator API key
|
||||||
|
- [ ] Create ApiConsumer + ApiKey through `/api/v2/api-consumers`
|
||||||
- [ ] Record your hub ID and API key in the new hub's `.env`
|
- [ ] Record your hub ID and API key in the new hub's `.env`
|
||||||
- [ ] Register HubCapabilityManifest with domain type vocabulary
|
- [ ] Register HubCapabilityManifest with domain type vocabulary through `/api/v2/hub-capability-manifests`
|
||||||
- [ ] Activate the manifest (validates no naming conflicts)
|
- [ ] Activate the manifest through `/api/v2/hub-capability-manifests/<id>/activate`
|
||||||
- [ ] Create at least one Widget per meaningful UI surface
|
- [ ] Create at least one Widget per meaningful UI surface
|
||||||
- [ ] Instrument interactions with POST to `/api/v2/interaction-events`
|
- [ ] Instrument interactions with POST to `/api/v2/interaction-events`
|
||||||
- [ ] Verify events appear in inter-hub at `/InteractionEvents`
|
- [ ] Verify events appear in inter-hub at `/InteractionEvents`
|
||||||
@@ -266,8 +336,9 @@ registration pattern into your new hub. It is ~50 lines of boilerplate.
|
|||||||
|
|
||||||
| Resource | Location |
|
| Resource | Location |
|
||||||
|----------|----------|
|
|----------|----------|
|
||||||
| API reference (OpenAPI) | `http://192.168.178.135:8080/api/v2/openapi.json` |
|
| API reference (OpenAPI) | `$IHUB_BASE/api/v2/openapi.json` |
|
||||||
| Type registry browser | `http://192.168.178.135:8080/TypeRegistries/WidgetTypes` |
|
| Swagger UI | `$IHUB_BASE/api/v2/docs` |
|
||||||
|
| Type registry browser | `$IHUB_BASE/TypeRegistries/WidgetTypes` |
|
||||||
| Domain hub extension guide | `docs/domain-hub-extension-guide.md` |
|
| Domain hub extension guide | `docs/domain-hub-extension-guide.md` |
|
||||||
| IHP data and queries | `docs/ihp-data-and-queries.md` |
|
| IHP data and queries | `docs/ihp-data-and-queries.md` |
|
||||||
| IHP controllers and views | `docs/ihp-controllers-views-forms.md` |
|
| IHP controllers and views | `docs/ihp-controllers-views-forms.md` |
|
||||||
|
|||||||
@@ -244,7 +244,7 @@ shell does not have `IHP_LIB`/the IHP dev environment loaded.
|
|||||||
|
|
||||||
```task
|
```task
|
||||||
id: IHUB-WP-0019-T06
|
id: IHUB-WP-0019-T06
|
||||||
status: todo
|
status: done
|
||||||
priority: medium
|
priority: medium
|
||||||
state_hub_task_id: "84c92e05-3e0f-490a-a48f-e2d9ddace764"
|
state_hub_task_id: "84c92e05-3e0f-490a-a48f-e2d9ddace764"
|
||||||
```
|
```
|
||||||
@@ -260,6 +260,19 @@ Update OpenAPI and docs to match the real API:
|
|||||||
Done when: a new hub implementer can follow docs without discovering missing
|
Done when: a new hub implementer can follow docs without discovering missing
|
||||||
API endpoints at runtime.
|
API endpoints at runtime.
|
||||||
|
|
||||||
|
Implementation note (2026-05-19): updated the generated OpenAPI contract to
|
||||||
|
use distinct request schemas for hub, manifest, API consumer/key, widget,
|
||||||
|
interaction-event, and annotation writes. The spec now represents manifest
|
||||||
|
activation and widget-pattern adoption as no-body actions, documents the
|
||||||
|
one-time `ApiKeyCreatedResponse.fullKey`, adds missing hub registry and widget
|
||||||
|
pattern response schemas, and fixes path parameter naming for `hubId`. Updated
|
||||||
|
`docs/new-hub-quickstart.md` to show the supported `ops-hub` bootstrap path
|
||||||
|
through `/api/v2`, including VSM metadata, manifest activation, consumer/key
|
||||||
|
creation, widget seeding, and `metadata` event submission. Updated the
|
||||||
|
functional contract endpoint list. `git diff --check` passed;
|
||||||
|
`scripts/compile-check` remains blocked in this shell because `IHP_LIB` is not
|
||||||
|
set.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### T07 — Add an ops-hub bootstrap smoke test
|
### T07 — Add an ops-hub bootstrap smoke test
|
||||||
|
|||||||
Reference in New Issue
Block a user