From b579475ffe81879980ec0b15423b34b944174fd5 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Thu, 2 Jan 2025 14:12:23 -0800 Subject: [PATCH] also do event classes APIs --- nexus/external-api/output/nexus_tags.txt | 2 + nexus/external-api/src/lib.rs | 24 ++++ nexus/src/external_api/http_entrypoints.rs | 48 +++++++ nexus/types/src/external_api/params.rs | 19 +++ nexus/types/src/external_api/views.rs | 10 ++ openapi/nexus.json | 138 +++++++++++++++++++++ 6 files changed, 241 insertions(+) diff --git a/nexus/external-api/output/nexus_tags.txt b/nexus/external-api/output/nexus_tags.txt index 8a403fe09f..3b7fddeb96 100644 --- a/nexus/external-api/output/nexus_tags.txt +++ b/nexus/external-api/output/nexus_tags.txt @@ -237,6 +237,8 @@ webhook_create POST /experimental/v1/webhooks webhook_delete DELETE /experimental/v1/webhooks/{webhook_id} webhook_delivery_list GET /experimental/v1/webhooks/{webhook_id}/deliveries webhook_delivery_resend POST /experimental/v1/webhooks/{webhook_id}/deliveries/{event_id}/resend +webhook_event_class_list GET /experimental/v1/webhook-events/classes +webhook_event_class_view GET /experimental/v1/webhook-events/classes/{name} webhook_secrets_add POST /experimental/v1/webhooks/{webhook_id}/secrets webhook_secrets_list GET /experimental/v1/webhooks/{webhook_id}/secrets webhook_update PUT /experimental/v1/webhooks/{webhook_id} diff --git a/nexus/external-api/src/lib.rs b/nexus/external-api/src/lib.rs index e8cf0fbd90..c5e43b1a3b 100644 --- a/nexus/external-api/src/lib.rs +++ b/nexus/external-api/src/lib.rs @@ -3097,6 +3097,30 @@ pub trait NexusExternalApi { // Webhooks (experimental) + /// List webhook event classes + #[endpoint { + method = GET, + path = "/experimental/v1/webhook-events/classes", + tags = ["system/webhooks"], + }] + async fn webhook_event_class_list( + rqctx: RequestContext, + query_params: Query< + PaginationParams, + >, + ) -> Result>, HttpError>; + + /// Fetch details on an event class by name. + #[endpoint { + method = GET, + path ="/experimental/v1/webhook-events/classes/{name}", + tags = ["system/webhooks"], + }] + async fn webhook_event_class_view( + rqctx: RequestContext, + path_params: Path, + ) -> Result, HttpError>; + /// Get the configuration for a webhook receiver. #[endpoint { method = GET, diff --git a/nexus/src/external_api/http_entrypoints.rs b/nexus/src/external_api/http_entrypoints.rs index 3164c492dc..0277d1f93b 100644 --- a/nexus/src/external_api/http_entrypoints.rs +++ b/nexus/src/external_api/http_entrypoints.rs @@ -6299,6 +6299,54 @@ impl NexusExternalApi for NexusExternalApiImpl { device_auth::device_access_token(rqctx, params.into_inner()).await } + async fn webhook_event_class_list( + rqctx: RequestContext, + _query_params: Query< + PaginationParams, + >, + ) -> Result>, HttpError> { + let apictx = rqctx.context(); + let handler = async { + let nexus = &apictx.context.nexus; + + let opctx = + crate::context::op_context_for_external_api(&rqctx).await?; + + Err(nexus + .unimplemented_todo(&opctx, crate::app::Unimpl::Public) + .await + .into()) + }; + apictx + .context + .external_latencies + .instrument_dropshot_handler(&rqctx, handler) + .await + } + + async fn webhook_event_class_view( + rqctx: RequestContext, + _path_params: Path, + ) -> Result, HttpError> { + let apictx = rqctx.context(); + let handler = async { + let nexus = &apictx.context.nexus; + + let opctx = + crate::context::op_context_for_external_api(&rqctx).await?; + + Err(nexus + .unimplemented_todo(&opctx, crate::app::Unimpl::Public) + .await + .into()) + }; + apictx + .context + .external_latencies + .instrument_dropshot_handler(&rqctx, handler) + .await + } + async fn webhook_view( rqctx: RequestContext, _path_params: Path, diff --git a/nexus/types/src/external_api/params.rs b/nexus/types/src/external_api/params.rs index d3a7fc5c15..28effecf84 100644 --- a/nexus/types/src/external_api/params.rs +++ b/nexus/types/src/external_api/params.rs @@ -2282,6 +2282,25 @@ pub struct DeviceAccessTokenRequest { // Webhooks +/// Query params for listing webhook event classes. +#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)] +pub struct EventClassFilter { + /// An optional glob pattern for filtering event class names. + pub filter: Option, +} + +#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)] +pub struct EventClassPage { + /// The last webhook event class returned by a previous page. + pub last_seen: String, +} + +#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)] +pub struct EventClassSelector { + /// The name of the event class. + pub name: String, +} + #[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)] pub struct WebhookCreate { /// An identifier for this webhook receiver, which must be unique. diff --git a/nexus/types/src/external_api/views.rs b/nexus/types/src/external_api/views.rs index 1112bf0a6d..2128e1b976 100644 --- a/nexus/types/src/external_api/views.rs +++ b/nexus/types/src/external_api/views.rs @@ -1030,6 +1030,16 @@ pub struct OxqlQueryResult { // WEBHOOKS +/// A webhook event class. +#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)] +pub struct EventClass { + /// The name of the event class. + pub name: String, + + /// A description of what this event class represents. + pub description: String, +} + /// The configuration for a webhook. #[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)] pub struct Webhook { diff --git a/openapi/nexus.json b/openapi/nexus.json index ff43ed9a0e..48bb8d60ce 100644 --- a/openapi/nexus.json +++ b/openapi/nexus.json @@ -301,6 +301,105 @@ } } }, + "/experimental/v1/webhook-events/classes": { + "get": { + "tags": [ + "system/webhooks" + ], + "summary": "List webhook event classes", + "operationId": "webhook_event_class_list", + "parameters": [ + { + "in": "query", + "name": "filter", + "description": "An optional glob pattern for filtering event class names.", + "schema": { + "nullable": true, + "type": "string" + } + }, + { + "in": "query", + "name": "limit", + "description": "Maximum number of items returned by a single call", + "schema": { + "nullable": true, + "type": "integer", + "format": "uint32", + "minimum": 1 + } + }, + { + "in": "query", + "name": "page_token", + "description": "Token returned by previous call to retrieve the subsequent page", + "schema": { + "nullable": true, + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EventClassResultsPage" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + }, + "x-dropshot-pagination": { + "required": [] + } + } + }, + "/experimental/v1/webhook-events/classes/{name}": { + "get": { + "tags": [ + "system/webhooks" + ], + "summary": "Fetch details on an event class by name.", + "operationId": "webhook_event_class_view", + "parameters": [ + { + "in": "path", + "name": "name", + "description": "The name of the event class.", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EventClass" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + }, "/experimental/v1/webhooks": { "post": { "tags": [ @@ -14296,6 +14395,45 @@ "request_id" ] }, + "EventClass": { + "description": "A webhook event class.", + "type": "object", + "properties": { + "description": { + "description": "A description of what this event class represents.", + "type": "string" + }, + "name": { + "description": "The name of the event class.", + "type": "string" + } + }, + "required": [ + "description", + "name" + ] + }, + "EventClassResultsPage": { + "description": "A single page of results", + "type": "object", + "properties": { + "items": { + "description": "list of items on this page of results", + "type": "array", + "items": { + "$ref": "#/components/schemas/EventClass" + } + }, + "next_page": { + "nullable": true, + "description": "token used to fetch the next page of results (if any)", + "type": "string" + } + }, + "required": [ + "items" + ] + }, "ExternalIp": { "oneOf": [ {