Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add custom processor module to add ScaffoldedFrom relation #1591

Merged
merged 14 commits into from
May 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Catalog Backend Module for Scaffolder Relation Catalog Processor

This is an extension module to the catalog-backend plugin, providing an additional catalog entity processor that adds a new relation that depends on the `spec.scaffoldedFrom` field to link scaffolder templates and the catalog entities they generated.

## Getting Started

1. Install the scaffolder relation catalog processor module using the following command:

```console
yarn workspace backend add @janus-idp/backstage-plugin-catalog-backend-module-scaffolder-relation-processor
```

### Installing on the new backend system

To install this module into the [new backend system](https://backstage.io/docs/backend-system/), add the following into the `packages/backend/src/index.ts` file:

```ts title="packages/backend/src/index.ts
const backend = createBackend();

// highlight-add-start
backend.add(
import(
'@janus-idp/backstage-plugin-catalog-backend-module-scaffolder-relation-processor/alpha'
),
);
// highlight-add-end

backend.start();
```

### Installing on the legacy backend system

To install this module into the legacy backend system, add the following to the `packages/backend/src/plugins/catalog.ts` file:

```ts title=packages/backend/src/plugins/catalog.ts
// highlight-add-start
import { ScaffolderRelationEntityProcessor } from '@janus-idp/backstage-plugin-catalog-backend-module-scaffolder-relation-processor';

// highlight-add-end

export default async function createPlugin(
env: PluginEnvironment,
): Promise<Router> {
const builder = await CatalogBuilder.create(env);

/* ... other processors and/or providers ... */
// highlight-add-start
builder.addProcessor(new ScaffolderRelationEntityProcessor());
// highlight-add-end

const { processingEngine, router } = await builder.build();
await processingEngine.start();

return router;
}
```

### Usage

Catalog entities containing the `spec.scaffoldedFrom` field will have a relation link be formed between it and the `template` corresponding to the entity ref in the `spec.scaffoldedFrom` field.

This link can be viewed in the `relations` field of the Raw YAML view of a catalog entity when inspecting an entity. In the entity with the `spec.scaffoldedFrom` field, the relation type is `scaffoldedFrom` with a target pointing to the value of the `spec.scaffoldedFrom` field. Conversely, for the target template, it will have a relation type of `ScaffolderOf` with a target pointing to the entity with the `spec.scaffoldedFrom` field.

These relations should also appear on the `EntityCatalogGraphView` component from the `@backstage/plugin-catalog-graph` package (only if the entity corresponding to the entity ref exists in the catalog).

#### Example graph view

![scaffoldedFrom Relation Graph View](./docs/example-images/scaffoldedFromGraphView.png)
![scaffolderOf Relation Graph View](./docs/example-images/scaffolderOfGraphView.png)

#### Example Raw YAML view

![scaffoldedFrom Relation YAML View](./docs/example-images/scaffoldedFromYAMLView.png)
![scaffoldedOf Relation YAML View](./docs/example-images/scaffolderOfYAMLView.png)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export interface Config {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
{
"name": "@janus-idp/backstage-plugin-catalog-backend-module-scaffolder-relation-processor-dynamic",
"description": "The scaffolder-relation-processor backend module for the catalog plugin.",
"version": "0.1.0",
"main": "dist/index.cjs.js",
"types": "dist/index.d.ts",
"license": "Apache-2.0",
"publishConfig": {
"access": "public",
"main": "dist/index.cjs.js",
"types": "dist/index.d.ts"
},
"backstage": {
"role": "backend-plugin-module"
},
"exports": {
".": {
"require": "./dist/index.cjs.js",
"default": "./dist/index.cjs.js"
},
"./alpha": {
"require": "./dist/alpha.cjs.js",
"default": "./dist/alpha.cjs.js"
},
"./package.json": "./package.json"
},
"scripts": {},
"dependencies": {},
"devDependencies": {},
"files": [
"dist",
"config.d.ts",
"app-config.janus-idp.yaml",
"alpha"
],
"configSchema": "config.d.ts",
"repository": {
"type": "git",
"url": "https://github.com/janus-idp/backstage-plugins",
"directory": "plugins/catalog"
},
"keywords": [
"backstage",
"plugin"
],
"homepage": "https://janus-idp.io/",
"bugs": "https://github.com/janus-idp/backstage-plugins/issues",
"bundleDependencies": true,
"peerDependencies": {
"@backstage/backend-common": "^0.21.6",
"@backstage/backend-dynamic-feature-service": "^0.2.8",
"@backstage/backend-plugin-api": "^0.6.16",
"@backstage/catalog-model": "^1.4.5",
"@backstage/plugin-catalog-common": "^1.0.22",
"@backstage/plugin-catalog-node": "^1.11.1"
},
"overrides": {
"@aws-sdk/util-utf8-browser": {
"@smithy/util-utf8": "^2.0.0"
}
},
"resolutions": {
"@aws-sdk/util-utf8-browser": "npm:@smithy/util-utf8@~2"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1


"@aws-sdk/util-utf8-browser@npm:@smithy/util-utf8@~2":
version "2.3.0"
resolved "https://registry.yarnpkg.com/@smithy/util-utf8/-/util-utf8-2.3.0.tgz#dd96d7640363259924a214313c3cf16e7dd329c5"
integrity sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==
dependencies:
"@smithy/util-buffer-from" "^2.2.0"
tslib "^2.6.2"

"@smithy/is-array-buffer@^2.2.0":
version "2.2.0"
resolved "https://registry.yarnpkg.com/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz#f84f0d9f9a36601a9ca9381688bd1b726fd39111"
integrity sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==
dependencies:
tslib "^2.6.2"

"@smithy/util-buffer-from@^2.2.0":
version "2.2.0"
resolved "https://registry.yarnpkg.com/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz#6fc88585165ec73f8681d426d96de5d402021e4b"
integrity sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==
dependencies:
"@smithy/is-array-buffer" "^2.2.0"
tslib "^2.6.2"

tslib@^2.6.2:
version "2.6.2"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae"
integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
{
"name": "@janus-idp/backstage-plugin-catalog-backend-module-scaffolder-relation-processor",
"description": "The scaffolder-relation-processor backend module for the catalog plugin.",
"version": "0.1.0",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
"publishConfig": {
"access": "public",
"main": "dist/index.cjs.js",
"types": "dist/index.d.ts"
},
"backstage": {
"role": "backend-plugin-module"
},
"exports": {
".": "./src/index.ts",
"./alpha": "./src/alpha.ts",
"./package.json": "./package.json"
},
"typesVersions": {
"*": {
"alpha": [
"src/alpha.ts"
],
"package.json": [
"package.json"
]
}
},
"scripts": {
"build": "backstage-cli package build",
"clean": "backstage-cli package clean",
"export-dynamic": "janus-cli package export-dynamic-plugin",
"lint": "backstage-cli package lint",
"postpack": "backstage-cli package postpack",
"postversion": "yarn run export-dynamic",
"prepack": "backstage-cli package prepack",
"start": "backstage-cli package start",
"test": "backstage-cli package test --passWithNoTests --coverage",
"tsc": "tsc"
},
"dependencies": {
"@backstage/backend-common": "^0.21.6",
"@backstage/backend-dynamic-feature-service": "^0.2.8",
"@backstage/backend-plugin-api": "^0.6.16",
"@backstage/catalog-model": "^1.4.5",
"@backstage/plugin-catalog-common": "^1.0.22",
"@backstage/plugin-catalog-node": "^1.11.1"
},
"devDependencies": {
"@backstage/backend-test-utils": "0.3.6",
"@backstage/cli": "0.26.2",
"@janus-idp/cli": "1.8.2"
},
"files": [
"dist",
"config.d.ts",
"dist-dynamic/*.*",
"dist-dynamic/dist/**",
"dist-dynamic/alpha/*",
"app-config.janus-idp.yaml"
],
"configSchema": "config.d.ts",
"repository": {
"type": "git",
"url": "https://github.com/janus-idp/backstage-plugins",
"directory": "plugins/catalog"
},
"keywords": [
"backstage",
"plugin"
],
"homepage": "https://janus-idp.io/",
"bugs": "https://github.com/janus-idp/backstage-plugins/issues"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { Entity } from '@backstage/catalog-model';

import { ScaffolderRelationEntityProcessor } from './ScaffolderRelationEntityProcessor';

describe('ScaffolderRelationEntityProcessor', () => {
describe('postProcessEntity', () => {
const processor = new ScaffolderRelationEntityProcessor();
const location = { type: 'url', target: 'test-url' };
const emit = jest.fn();

afterEach(() => jest.resetAllMocks());

it('generates relations for any arbitrary entity', async () => {
const entity: Entity = {
apiVersion: 'backstage.io/v1alpha1',
kind: 'Component',
metadata: { name: 'test-entity' },
spec: {
scaffoldedFrom: 'test-template',
},
};

await processor.postProcessEntity(entity, location, emit);
expect(emit).toHaveBeenCalledTimes(2);
expect(emit).toHaveBeenCalledWith({
type: 'relation',
relation: {
source: {
kind: 'Template',
namespace: 'default',
name: 'test-template',
},
type: 'scaffolderOf',
target: {
kind: 'Component',
namespace: 'default',
name: 'test-entity',
},
},
});
expect(emit).toHaveBeenCalledWith({
type: 'relation',
relation: {
source: {
kind: 'Component',
namespace: 'default',
name: 'test-entity',
},
type: 'scaffoldedFrom',
target: {
kind: 'Template',
namespace: 'default',
name: 'test-template',
},
},
});
});
it('generates no relations if the `spec.scaffoldedFrom` field is empty or does not exist', async () => {
const entity: Entity = {
apiVersion: 'backstage.io/v1alpha1',
kind: 'Component',
metadata: { name: 'n' },
spec: {
type: 'service',
},
};
await processor.postProcessEntity(entity, location, emit);
expect(emit).toHaveBeenCalledTimes(0);

const entity2: Entity = {
apiVersion: 'backstage.io/v1alpha1',
kind: 'Component',
metadata: { name: 'n' },
spec: {
scaffoldedFrom: '',
},
};
await processor.postProcessEntity(entity2, location, emit);
expect(emit).toHaveBeenCalledTimes(0);
});
});
});
Loading
Loading