From 25f6d7bddbded53fbeef4f3700d7b505ffca7bec Mon Sep 17 00:00:00 2001 From: Patrick Jungermann Date: Wed, 9 Nov 2022 12:16:09 +0100 Subject: [PATCH] feat(events/gerrit): add `GerritEventRouter` Add an event router for Gerrit which handles events from the topic `gerrit` and re-publishes events under their more specific topic based on the `$.type` payload field like e.g., `gerrit.change-merged`. Signed-off-by: Patrick Jungermann --- .changeset/light-eagles-poke.md | 14 ++++++ .github/CODEOWNERS | 1 + .../events-backend-module-gerrit/.eslintrc.js | 1 + .../events-backend-module-gerrit/README.md | 42 ++++++++++++++++ .../api-report.md | 21 ++++++++ .../events-backend-module-gerrit/package.json | 40 +++++++++++++++ .../events-backend-module-gerrit/src/index.ts | 25 ++++++++++ .../src/router/GerritEventRouter.test.ts | 50 +++++++++++++++++++ .../src/router/GerritEventRouter.ts | 42 ++++++++++++++++ .../GerritEventRouterEventsModule.test.ts | 46 +++++++++++++++++ .../service/GerritEventRouterEventsModule.ts | 44 ++++++++++++++++ .../src/setupTests.ts | 17 +++++++ yarn.lock | 14 ++++++ 13 files changed, 357 insertions(+) create mode 100644 .changeset/light-eagles-poke.md create mode 100644 plugins/events-backend-module-gerrit/.eslintrc.js create mode 100644 plugins/events-backend-module-gerrit/README.md create mode 100644 plugins/events-backend-module-gerrit/api-report.md create mode 100644 plugins/events-backend-module-gerrit/package.json create mode 100644 plugins/events-backend-module-gerrit/src/index.ts create mode 100644 plugins/events-backend-module-gerrit/src/router/GerritEventRouter.test.ts create mode 100644 plugins/events-backend-module-gerrit/src/router/GerritEventRouter.ts create mode 100644 plugins/events-backend-module-gerrit/src/service/GerritEventRouterEventsModule.test.ts create mode 100644 plugins/events-backend-module-gerrit/src/service/GerritEventRouterEventsModule.ts create mode 100644 plugins/events-backend-module-gerrit/src/setupTests.ts diff --git a/.changeset/light-eagles-poke.md b/.changeset/light-eagles-poke.md new file mode 100644 index 0000000000..8b1f5bb522 --- /dev/null +++ b/.changeset/light-eagles-poke.md @@ -0,0 +1,14 @@ +--- +'@backstage/plugin-events-backend-module-gerrit': minor +--- + +Adds a new module `gerrit` to plugin-events-backend. + +The module adds a new event router `GerritEventRouter`. + +The event router will re-publish events received at topic `gerrit` +under a more specific topic depending on their `$.type` value +(e.g., `gerrit.change-merged`). + +Please find more information at +https://github.com/backstage/backstage/tree/master/plugins/events-backend-module-gerrit/README.md. diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 85e9726578..3f17c9591f 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -40,6 +40,7 @@ yarn.lock @backstage/reviewers @backst /plugins/events-backend-module-aws-sqs @backstage/reviewers @pjungermann /plugins/events-backend-module-azure @backstage/reviewers @pjungermann /plugins/events-backend-module-bitbucket-cloud @backstage/reviewers @pjungermann +/plugins/events-backend-module-gerrit @backstage/reviewers @pjungermann /plugins/events-backend-module-github @backstage/reviewers @pjungermann /plugins/events-backend-module-gitlab @backstage/reviewers @pjungermann /plugins/events-backend-test-utils @backstage/reviewers @pjungermann diff --git a/plugins/events-backend-module-gerrit/.eslintrc.js b/plugins/events-backend-module-gerrit/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/events-backend-module-gerrit/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/events-backend-module-gerrit/README.md b/plugins/events-backend-module-gerrit/README.md new file mode 100644 index 0000000000..933b7a172f --- /dev/null +++ b/plugins/events-backend-module-gerrit/README.md @@ -0,0 +1,42 @@ +# events-backend-module-gerrit + +Welcome to the `events-backend-module-gerrit` backend plugin! + +This plugin is a module for the `events-backend` backend plugin +and extends it with an `GerritEventRouter`. + +The event router will subscribe to the topic `gerrit` +and route the events to more concrete topics based on the value +of the provided `$.type` payload field. + +Examples: + +| `$.type` | topic | +| ---------------- | ----------------------- | +| `change-created` | `gerrit.change-created` | +| `change-merged` | `gerrit.change-merged` | + +Please find all possible webhook event types at the +[official documentation](https://gerrit-review.googlesource.com/Documentation/cmd-stream-events.html#events). + +## Installation + +Install the [`events-backend` plugin](../events-backend/README.md). + +Install this module: + +```bash +# From your Backstage root directory +yarn add --cwd packages/backend @backstage/plugin-events-backend-module-gerrit +``` + +Add the event router to the `EventsBackend`: + +```diff ++const gerritEventRouter = new GerritEventRouter(); + + EventsBackend ++ .addPublishers(gerritEventRouter) ++ .addSubscribers(gerritEventRouter); +// [...] +``` diff --git a/plugins/events-backend-module-gerrit/api-report.md b/plugins/events-backend-module-gerrit/api-report.md new file mode 100644 index 0000000000..268e5970fa --- /dev/null +++ b/plugins/events-backend-module-gerrit/api-report.md @@ -0,0 +1,21 @@ +## API Report File for "@backstage/plugin-events-backend-module-gerrit" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { BackendFeature } from '@backstage/backend-plugin-api'; +import { EventParams } from '@backstage/plugin-events-node'; +import { SubTopicEventRouter } from '@backstage/plugin-events-node'; + +// @public +export class GerritEventRouter extends SubTopicEventRouter { + constructor(); + // (undocumented) + protected determineSubTopic(params: EventParams): string | undefined; +} + +// @alpha +export const gerritEventRouterEventsModule: ( + options?: undefined, +) => BackendFeature; +``` diff --git a/plugins/events-backend-module-gerrit/package.json b/plugins/events-backend-module-gerrit/package.json new file mode 100644 index 0000000000..f1808fc306 --- /dev/null +++ b/plugins/events-backend-module-gerrit/package.json @@ -0,0 +1,40 @@ +{ + "name": "@backstage/plugin-events-backend-module-gerrit", + "version": "0.0.0", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "publishConfig": { + "access": "public", + "alphaTypes": "dist/index.alpha.d.ts", + "main": "dist/index.cjs.js", + "types": "dist/index.d.ts" + }, + "backstage": { + "role": "backend-plugin-module" + }, + "scripts": { + "start": "backstage-cli package start", + "build": "backstage-cli package build --experimental-type-build", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "clean": "backstage-cli package clean", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack" + }, + "dependencies": { + "@backstage/backend-plugin-api": "workspace:^", + "@backstage/plugin-events-node": "workspace:^", + "winston": "^3.2.1" + }, + "devDependencies": { + "@backstage/backend-test-utils": "workspace:^", + "@backstage/cli": "workspace:^", + "@backstage/plugin-events-backend-test-utils": "workspace:^", + "supertest": "^6.1.3" + }, + "files": [ + "alpha", + "dist" + ] +} diff --git a/plugins/events-backend-module-gerrit/src/index.ts b/plugins/events-backend-module-gerrit/src/index.ts new file mode 100644 index 0000000000..1a9ebb03ee --- /dev/null +++ b/plugins/events-backend-module-gerrit/src/index.ts @@ -0,0 +1,25 @@ +/* + * Copyright 2020 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. + */ + +/** + * The module `gerrit` for the Backstage backend plugin "events-backend" + * adding an event router for Gerrit. + * + * @packageDocumentation + */ + +export { GerritEventRouter } from './router/GerritEventRouter'; +export { gerritEventRouterEventsModule } from './service/GerritEventRouterEventsModule'; diff --git a/plugins/events-backend-module-gerrit/src/router/GerritEventRouter.test.ts b/plugins/events-backend-module-gerrit/src/router/GerritEventRouter.test.ts new file mode 100644 index 0000000000..7302a26012 --- /dev/null +++ b/plugins/events-backend-module-gerrit/src/router/GerritEventRouter.test.ts @@ -0,0 +1,50 @@ +/* + * Copyright 2022 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. + */ + +import { TestEventBroker } from '@backstage/plugin-events-backend-test-utils'; +import { GerritEventRouter } from './GerritEventRouter'; + +describe('GerritEventRouter', () => { + const eventRouter = new GerritEventRouter(); + const topic = 'gerrit'; + const eventPayload = { type: 'test-type', test: 'payload' }; + const metadata = {}; + + it('no $.type', () => { + const eventBroker = new TestEventBroker(); + eventRouter.setEventBroker(eventBroker); + + eventRouter.onEvent({ + topic, + eventPayload: { invalid: 'payload' }, + metadata, + }); + + expect(eventBroker.published).toEqual([]); + }); + + it('with $.type', () => { + const eventBroker = new TestEventBroker(); + eventRouter.setEventBroker(eventBroker); + + eventRouter.onEvent({ topic, eventPayload, metadata }); + + expect(eventBroker.published.length).toBe(1); + expect(eventBroker.published[0].topic).toEqual('gerrit.test-type'); + expect(eventBroker.published[0].eventPayload).toEqual(eventPayload); + expect(eventBroker.published[0].metadata).toEqual(metadata); + }); +}); diff --git a/plugins/events-backend-module-gerrit/src/router/GerritEventRouter.ts b/plugins/events-backend-module-gerrit/src/router/GerritEventRouter.ts new file mode 100644 index 0000000000..3d97508b62 --- /dev/null +++ b/plugins/events-backend-module-gerrit/src/router/GerritEventRouter.ts @@ -0,0 +1,42 @@ +/* + * Copyright 2022 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. + */ + +import { + EventParams, + SubTopicEventRouter, +} from '@backstage/plugin-events-node'; + +/** + * Subscribes to the generic `gerrit` topic + * and publishes the events under the more concrete sub-topic + * depending on the `$.type` field provided. + * + * @public + */ +export class GerritEventRouter extends SubTopicEventRouter { + constructor() { + super('gerrit'); + } + + protected determineSubTopic(params: EventParams): string | undefined { + if ('type' in (params.eventPayload as object)) { + const payload = params.eventPayload as { type: string }; + return payload.type; + } + + return undefined; + } +} diff --git a/plugins/events-backend-module-gerrit/src/service/GerritEventRouterEventsModule.test.ts b/plugins/events-backend-module-gerrit/src/service/GerritEventRouterEventsModule.test.ts new file mode 100644 index 0000000000..b5fd6a80ca --- /dev/null +++ b/plugins/events-backend-module-gerrit/src/service/GerritEventRouterEventsModule.test.ts @@ -0,0 +1,46 @@ +/* + * Copyright 2022 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. + */ + +import { startTestBackend } from '@backstage/backend-test-utils'; +import { eventsExtensionPoint } from '@backstage/plugin-events-node'; +import { gerritEventRouterEventsModule } from './GerritEventRouterEventsModule'; +import { GerritEventRouter } from '../router/GerritEventRouter'; + +describe('gerritEventRouterEventsModule', () => { + it('should be correctly wired and set up', async () => { + let addedPublisher: GerritEventRouter | undefined; + let addedSubscriber: GerritEventRouter | undefined; + const extensionPoint = { + addPublishers: (publisher: any) => { + addedPublisher = publisher; + }, + addSubscribers: (subscriber: any) => { + addedSubscriber = subscriber; + }, + }; + + await startTestBackend({ + extensionPoints: [[eventsExtensionPoint, extensionPoint]], + services: [], + features: [gerritEventRouterEventsModule()], + }); + + expect(addedPublisher).not.toBeUndefined(); + expect(addedPublisher).toBeInstanceOf(GerritEventRouter); + expect(addedSubscriber).not.toBeUndefined(); + expect(addedSubscriber).toBeInstanceOf(GerritEventRouter); + }); +}); diff --git a/plugins/events-backend-module-gerrit/src/service/GerritEventRouterEventsModule.ts b/plugins/events-backend-module-gerrit/src/service/GerritEventRouterEventsModule.ts new file mode 100644 index 0000000000..734b574edd --- /dev/null +++ b/plugins/events-backend-module-gerrit/src/service/GerritEventRouterEventsModule.ts @@ -0,0 +1,44 @@ +/* + * Copyright 2022 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. + */ + +import { createBackendModule } from '@backstage/backend-plugin-api'; +import { eventsExtensionPoint } from '@backstage/plugin-events-node'; +import { GerritEventRouter } from '../router/GerritEventRouter'; + +/** + * Module for the events-backend plugin, adding an event router for Gerrit. + * + * Registers the {@link GerritEventRouter}. + * + * @alpha + */ +export const gerritEventRouterEventsModule = createBackendModule({ + pluginId: 'events', + moduleId: 'gerritEventRouter', + register(env) { + env.registerInit({ + deps: { + events: eventsExtensionPoint, + }, + async init({ events }) { + const eventRouter = new GerritEventRouter(); + + events.addPublishers(eventRouter); + events.addSubscribers(eventRouter); + }, + }); + }, +}); diff --git a/plugins/events-backend-module-gerrit/src/setupTests.ts b/plugins/events-backend-module-gerrit/src/setupTests.ts new file mode 100644 index 0000000000..d3232290a7 --- /dev/null +++ b/plugins/events-backend-module-gerrit/src/setupTests.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2020 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. + */ + +export {}; diff --git a/yarn.lock b/yarn.lock index 679664a60b..9b0f6dec5f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6369,6 +6369,20 @@ __metadata: languageName: unknown linkType: soft +"@backstage/plugin-events-backend-module-gerrit@workspace:plugins/events-backend-module-gerrit": + version: 0.0.0-use.local + resolution: "@backstage/plugin-events-backend-module-gerrit@workspace:plugins/events-backend-module-gerrit" + dependencies: + "@backstage/backend-plugin-api": "workspace:^" + "@backstage/backend-test-utils": "workspace:^" + "@backstage/cli": "workspace:^" + "@backstage/plugin-events-backend-test-utils": "workspace:^" + "@backstage/plugin-events-node": "workspace:^" + supertest: ^6.1.3 + winston: ^3.2.1 + languageName: unknown + linkType: soft + "@backstage/plugin-events-backend-module-github@workspace:plugins/events-backend-module-github": version: 0.0.0-use.local resolution: "@backstage/plugin-events-backend-module-github@workspace:plugins/events-backend-module-github"