events-backend: add OpenAPI schema
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -44,6 +44,7 @@
|
||||
"scripts": {
|
||||
"build": "backstage-cli package build",
|
||||
"clean": "backstage-cli package clean",
|
||||
"generate": "backstage-repo-tools package schema openapi generate --server",
|
||||
"lint": "backstage-cli package lint",
|
||||
"prepack": "backstage-cli package prepack",
|
||||
"postpack": "backstage-cli package postpack",
|
||||
@@ -52,6 +53,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/backend-common": "^0.25.0",
|
||||
"@backstage/backend-openapi-utils": "workspace:^",
|
||||
"@backstage/backend-plugin-api": "workspace:^",
|
||||
"@backstage/config": "workspace:^",
|
||||
"@backstage/errors": "workspace:^",
|
||||
@@ -70,6 +72,7 @@
|
||||
"@backstage/backend-test-utils": "workspace:^",
|
||||
"@backstage/cli": "workspace:^",
|
||||
"@backstage/plugin-events-backend-test-utils": "workspace:^",
|
||||
"@backstage/repo-tools": "workspace:^",
|
||||
"supertest": "^7.0.0"
|
||||
},
|
||||
"configSchema": "config.d.ts"
|
||||
|
||||
@@ -0,0 +1,289 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
import { createValidatedOpenApiRouter } from '@backstage/backend-openapi-utils';
|
||||
|
||||
export const spec = {
|
||||
openapi: '3.0.3',
|
||||
info: {
|
||||
title: 'events',
|
||||
version: '1',
|
||||
description: 'The Backstage backend plugin that powers the events system.',
|
||||
license: {
|
||||
name: 'Apache-2.0',
|
||||
url: 'http://www.apache.org/licenses/LICENSE-2.0.html',
|
||||
},
|
||||
contact: {},
|
||||
},
|
||||
servers: [
|
||||
{
|
||||
url: '/',
|
||||
},
|
||||
],
|
||||
components: {
|
||||
examples: {},
|
||||
headers: {},
|
||||
parameters: {
|
||||
subscriptionId: {
|
||||
name: 'subscriptionId',
|
||||
in: 'path',
|
||||
required: true,
|
||||
allowReserved: true,
|
||||
schema: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
requestBodies: {},
|
||||
responses: {
|
||||
ErrorResponse: {
|
||||
description: 'An error response from the backend.',
|
||||
content: {
|
||||
'application/json; charset=utf-8': {
|
||||
schema: {
|
||||
$ref: '#/components/schemas/Error',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
schemas: {
|
||||
Event: {
|
||||
type: 'object',
|
||||
required: ['topic', 'payload'],
|
||||
properties: {
|
||||
topic: {
|
||||
type: 'string',
|
||||
description: 'The topic that the event is published on',
|
||||
},
|
||||
payload: {
|
||||
$ref: '#/components/schemas/JsonObject',
|
||||
description: 'The event payload',
|
||||
},
|
||||
},
|
||||
},
|
||||
Error: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
error: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: {
|
||||
type: 'string',
|
||||
},
|
||||
message: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
required: ['name', 'message'],
|
||||
},
|
||||
request: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
method: {
|
||||
type: 'string',
|
||||
},
|
||||
url: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
required: ['method', 'url'],
|
||||
},
|
||||
response: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
statusCode: {
|
||||
type: 'number',
|
||||
},
|
||||
},
|
||||
required: ['statusCode'],
|
||||
},
|
||||
},
|
||||
required: ['error', 'request', 'response'],
|
||||
},
|
||||
JsonObject: {
|
||||
type: 'object',
|
||||
properties: {},
|
||||
additionalProperties: {},
|
||||
},
|
||||
},
|
||||
securitySchemes: {
|
||||
JWT: {
|
||||
type: 'http',
|
||||
scheme: 'bearer',
|
||||
bearerFormat: 'JWT',
|
||||
},
|
||||
},
|
||||
},
|
||||
paths: {
|
||||
'/hub/events': {
|
||||
post: {
|
||||
operationId: 'PostEvent',
|
||||
description: 'Publish a new event',
|
||||
responses: {
|
||||
default: {
|
||||
$ref: '#/components/responses/ErrorResponse',
|
||||
},
|
||||
},
|
||||
security: [
|
||||
{},
|
||||
{
|
||||
JWT: [],
|
||||
},
|
||||
],
|
||||
requestBody: {
|
||||
required: true,
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
type: 'object',
|
||||
required: ['event'],
|
||||
properties: {
|
||||
event: {
|
||||
$ref: '#/components/schemas/Event',
|
||||
},
|
||||
subscriptionIds: {
|
||||
type: 'array',
|
||||
description:
|
||||
'The IDs of subscriptions that have already received this event',
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
examples: {
|
||||
'Publishing a simple Event': {
|
||||
value: {
|
||||
event: {
|
||||
topic: 'test-topic',
|
||||
payload: {
|
||||
myData: 'foo',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
'/hub/subscriptions/{subscriptionId}': {
|
||||
put: {
|
||||
operationId: 'PutSubscription',
|
||||
description:
|
||||
'Ensures that the subscription exists with the provided configuration',
|
||||
responses: {
|
||||
'201': {
|
||||
description: 'The subscription exists or was created successfully',
|
||||
},
|
||||
default: {
|
||||
$ref: '#/components/responses/ErrorResponse',
|
||||
},
|
||||
},
|
||||
security: [
|
||||
{},
|
||||
{
|
||||
JWT: [],
|
||||
},
|
||||
],
|
||||
parameters: [
|
||||
{
|
||||
$ref: '#/components/parameters/subscriptionId',
|
||||
},
|
||||
],
|
||||
requestBody: {
|
||||
required: true,
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
type: 'object',
|
||||
required: ['topics'],
|
||||
properties: {
|
||||
topics: {
|
||||
type: 'array',
|
||||
description: 'The topics to subscribe to',
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
examples: {
|
||||
'Subscribing to a single topic': {
|
||||
value: {
|
||||
topics: ['test-topic'],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
'/hub/subscriptions/{subscriptionId}/events': {
|
||||
get: {
|
||||
operationId: 'GetSubscriptionEvents',
|
||||
description: 'Get new events for the provided subscription',
|
||||
responses: {
|
||||
'200': {
|
||||
description: 'New events',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
events: {
|
||||
type: 'array',
|
||||
items: {
|
||||
$ref: '#/components/schemas/Event',
|
||||
},
|
||||
},
|
||||
},
|
||||
required: ['results'],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
'201': {
|
||||
description: 'Block poll response, new events are available',
|
||||
},
|
||||
default: {
|
||||
$ref: '#/components/responses/ErrorResponse',
|
||||
},
|
||||
},
|
||||
security: [
|
||||
{},
|
||||
{
|
||||
JWT: [],
|
||||
},
|
||||
],
|
||||
parameters: [
|
||||
{
|
||||
$ref: '#/components/parameters/subscriptionId',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
export const createOpenApiRouter = async (
|
||||
options?: Parameters<typeof createValidatedOpenApiRouter>['1'],
|
||||
) => createValidatedOpenApiRouter<typeof spec>(spec, options);
|
||||
@@ -0,0 +1,185 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: events
|
||||
version: '1'
|
||||
description: The Backstage backend plugin that powers the events system.
|
||||
license:
|
||||
name: Apache-2.0
|
||||
url: http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
contact: {}
|
||||
servers:
|
||||
- url: /
|
||||
components:
|
||||
examples: {}
|
||||
headers: {}
|
||||
parameters:
|
||||
subscriptionId:
|
||||
name: subscriptionId
|
||||
in: path
|
||||
required: true
|
||||
allowReserved: true
|
||||
schema:
|
||||
type: string
|
||||
requestBodies: {}
|
||||
responses:
|
||||
ErrorResponse:
|
||||
description: An error response from the backend.
|
||||
content:
|
||||
application/json; charset=utf-8:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Error'
|
||||
schemas:
|
||||
Event:
|
||||
type: object
|
||||
required:
|
||||
- topic
|
||||
- payload
|
||||
properties:
|
||||
topic:
|
||||
type: string
|
||||
description: The topic that the event is published on
|
||||
payload:
|
||||
$ref: '#/components/schemas/JsonObject'
|
||||
description: The event payload
|
||||
|
||||
Error:
|
||||
type: object
|
||||
properties:
|
||||
error:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
message:
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- message
|
||||
request:
|
||||
type: object
|
||||
properties:
|
||||
method:
|
||||
type: string
|
||||
url:
|
||||
type: string
|
||||
required:
|
||||
- method
|
||||
- url
|
||||
response:
|
||||
type: object
|
||||
properties:
|
||||
statusCode:
|
||||
type: number
|
||||
required:
|
||||
- statusCode
|
||||
required:
|
||||
- error
|
||||
- request
|
||||
- response
|
||||
|
||||
JsonObject:
|
||||
type: object
|
||||
properties: {}
|
||||
# Free form object.
|
||||
additionalProperties: {}
|
||||
securitySchemes:
|
||||
JWT:
|
||||
type: http
|
||||
scheme: bearer
|
||||
bearerFormat: JWT
|
||||
paths:
|
||||
/hub/events:
|
||||
post:
|
||||
operationId: PostEvent
|
||||
description: Publish a new event
|
||||
responses:
|
||||
default:
|
||||
$ref: '#/components/responses/ErrorResponse'
|
||||
security:
|
||||
- {}
|
||||
- JWT: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required:
|
||||
- event
|
||||
properties:
|
||||
event:
|
||||
$ref: '#/components/schemas/Event'
|
||||
subscriptionIds:
|
||||
type: array
|
||||
description: The IDs of subscriptions that have already received this event
|
||||
items:
|
||||
type: string
|
||||
examples:
|
||||
Publishing a simple Event:
|
||||
value:
|
||||
event:
|
||||
topic: test-topic
|
||||
payload:
|
||||
myData: foo
|
||||
|
||||
/hub/subscriptions/{subscriptionId}:
|
||||
put:
|
||||
operationId: PutSubscription
|
||||
description: Ensures that the subscription exists with the provided configuration
|
||||
responses:
|
||||
'201':
|
||||
description: The subscription exists or was created successfully
|
||||
default:
|
||||
$ref: '#/components/responses/ErrorResponse'
|
||||
security:
|
||||
- {}
|
||||
- JWT: []
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/subscriptionId'
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required:
|
||||
- topics
|
||||
properties:
|
||||
topics:
|
||||
type: array
|
||||
description: The topics to subscribe to
|
||||
items:
|
||||
type: string
|
||||
examples:
|
||||
Subscribing to a single topic:
|
||||
value:
|
||||
topics:
|
||||
- test-topic
|
||||
|
||||
/hub/subscriptions/{subscriptionId}/events:
|
||||
get:
|
||||
operationId: GetSubscriptionEvents
|
||||
description: Get new events for the provided subscription
|
||||
responses:
|
||||
'200':
|
||||
description: New events
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
events:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Event'
|
||||
required:
|
||||
- results
|
||||
'201':
|
||||
description: Block poll response, new events are available
|
||||
default:
|
||||
$ref: '#/components/responses/ErrorResponse'
|
||||
security:
|
||||
- {}
|
||||
- JWT: []
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/subscriptionId'
|
||||
@@ -97,7 +97,7 @@ export const eventsPlugin = createBackendPlugin({
|
||||
http.bind(eventsRouter);
|
||||
|
||||
const hub = await EventHub.create({ logger, httpAuth });
|
||||
eventsRouter.use('/hub', hub.handler());
|
||||
router.use(hub.handler());
|
||||
|
||||
router.use(eventsRouter);
|
||||
router.addAuthPolicy({
|
||||
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
HttpAuthService,
|
||||
LoggerService,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
import express, { Handler } from 'express';
|
||||
import { Handler } from 'express';
|
||||
import Router from 'express-promise-router';
|
||||
import { Socket } from 'net';
|
||||
import { STATUS_CODES } from 'http';
|
||||
@@ -30,6 +30,8 @@ import { fromZodError } from 'zod-validation-error';
|
||||
import { JsonObject, JsonValue } from '@backstage/types';
|
||||
import { serializeError } from '@backstage/errors';
|
||||
import { EventParams } from '@backstage/plugin-events-node';
|
||||
import { spec, createOpenApiRouter } from '../../schema/openapi.generated';
|
||||
import { internal } from '@backstage/backend-openapi-utils';
|
||||
|
||||
/*
|
||||
|
||||
@@ -395,15 +397,23 @@ export class EventHub {
|
||||
const hub = new EventHub(server, router, logger, httpAuth);
|
||||
|
||||
// WS
|
||||
router.get('/connect', hub.#handleGetConnect);
|
||||
router.get('/hub/connect', hub.#handleGetConnect);
|
||||
|
||||
router.use(express.json());
|
||||
const apiRouter = await createOpenApiRouter();
|
||||
|
||||
router.post('/events', hub.#handlePostEvents);
|
||||
router.use(apiRouter);
|
||||
|
||||
apiRouter.post('/hub/events', hub.#handlePostEvents);
|
||||
|
||||
// Long-polling
|
||||
router.get('/subscriptions/:id/events', hub.#handleGetSubscription);
|
||||
router.put('/subscriptions/:id', hub.#handlePutSubscription);
|
||||
apiRouter.get(
|
||||
'/hub/subscriptions/:subscriptionId/events',
|
||||
hub.#handleGetSubscription,
|
||||
);
|
||||
apiRouter.put(
|
||||
'/hub/subscriptions/:subscriptionId',
|
||||
hub.#handlePutSubscription,
|
||||
);
|
||||
|
||||
return hub;
|
||||
}
|
||||
@@ -528,14 +538,18 @@ export class EventHub {
|
||||
}
|
||||
};
|
||||
|
||||
#handlePostEvents: Handler = async (req, res) => {
|
||||
#handlePostEvents: internal.DocRequestHandler<
|
||||
typeof spec,
|
||||
'/hub/events',
|
||||
'post'
|
||||
> = async (req, res) => {
|
||||
const credentials = await this.#httpAuth.credentials(req, {
|
||||
allow: ['service'],
|
||||
});
|
||||
await this.#store.publish({
|
||||
params: {
|
||||
topic: req.body.topic,
|
||||
eventPayload: req.body.payload,
|
||||
topic: req.body.event.topic,
|
||||
eventPayload: req.body.event.payload,
|
||||
},
|
||||
subscriberIds: [],
|
||||
});
|
||||
@@ -545,11 +559,15 @@ export class EventHub {
|
||||
res.status(201).end();
|
||||
};
|
||||
|
||||
#handleGetSubscription: Handler = async (req, res) => {
|
||||
#handleGetSubscription: internal.DocRequestHandler<
|
||||
typeof spec,
|
||||
'/hub/subscriptions/{subscriptionId}/events',
|
||||
'get'
|
||||
> = async (req, res) => {
|
||||
const credentials = await this.#httpAuth.credentials(req, {
|
||||
allow: ['service'],
|
||||
});
|
||||
const id = req.params.id;
|
||||
const id = req.params.subscriptionId;
|
||||
|
||||
const { events } = await this.#store.readSubscription(id);
|
||||
|
||||
@@ -568,11 +586,15 @@ export class EventHub {
|
||||
});
|
||||
};
|
||||
|
||||
#handlePutSubscription: Handler = async (req, res) => {
|
||||
#handlePutSubscription: internal.DocRequestHandler<
|
||||
typeof spec,
|
||||
'/hub/subscriptions/{subscriptionId}',
|
||||
'put'
|
||||
> = async (req, res) => {
|
||||
const credentials = await this.#httpAuth.credentials(req, {
|
||||
allow: ['service'],
|
||||
});
|
||||
const id = req.params.id;
|
||||
const id = req.params.subscriptionId;
|
||||
|
||||
await this.#store.upsertSubscription(id, req.body.topics);
|
||||
|
||||
|
||||
@@ -6139,6 +6139,7 @@ __metadata:
|
||||
dependencies:
|
||||
"@backstage/backend-common": ^0.25.0
|
||||
"@backstage/backend-defaults": "workspace:^"
|
||||
"@backstage/backend-openapi-utils": "workspace:^"
|
||||
"@backstage/backend-plugin-api": "workspace:^"
|
||||
"@backstage/backend-test-utils": "workspace:^"
|
||||
"@backstage/cli": "workspace:^"
|
||||
@@ -6146,6 +6147,7 @@ __metadata:
|
||||
"@backstage/errors": "workspace:^"
|
||||
"@backstage/plugin-events-backend-test-utils": "workspace:^"
|
||||
"@backstage/plugin-events-node": "workspace:^"
|
||||
"@backstage/repo-tools": "workspace:^"
|
||||
"@backstage/types": "workspace:^"
|
||||
"@types/express": ^4.17.6
|
||||
express: ^4.17.1
|
||||
|
||||
Reference in New Issue
Block a user