Giter Club home page Giter Club logo

backstage-backend-module's Introduction

This project has been created to demo: createBackendModule and scaffolderTemplatingExtensionPoint. See ticket here.

Note: Documentation about how to create a Scaffold backend module: https://github.com/backstage/backstage/blob/master/docs/backend-system/building-backends/08-migrating.md#the-scaffolder-plugin

HowTo guide: Backend module and Template filter

The following section describe how to register, using the new backend system, a backend module able to add Template filters that we will use within a Template scaffolded to perform operations like this : ${{ parameters.name | filter1 | filter2 | etc }}

  • Create a backstage backend using backstage 1.25
npx @backstage/[email protected] --skip-install
  • Move to the project and add the needed dependencies
cd my-demo

yarn add --cwd packages/backend @backstage/backend-plugin-api
yarn add --cwd packages/backend @backstage/plugin-scaffolder-node
yarn add --cwd packages/backend @backstage/types
  • Create a backend module which uses as dependency: scaffolderTemplatingExtensionPoint and declare some filters using the function init function scaffolderTemplating.addTemplateFilters. For this demo project, we will create this basic base64 filter: { base64: (...args: JsonValue[]) => btoa(args.join(""))}
mkdir -p packages/backend/src/modules

cat <<EOF > packages/backend/src/modules/scaffoldTemplateFilters.ts
import {coreServices, createBackendModule} from '@backstage/backend-plugin-api';
import {scaffolderTemplatingExtensionPoint} from '@backstage/plugin-scaffolder-node/alpha';
import {JsonValue} from '@backstage/types';

export const myModule = createBackendModule({
    pluginId: 'scaffolder',
    moduleId: 'my-custom-filter',
    register(env) {
      env.registerInit({
        deps: {
          config: coreServices.rootConfig,
          scaffolderTemplating: scaffolderTemplatingExtensionPoint,
        },
        async init({scaffolderTemplating}) {
          scaffolderTemplating.addTemplateFilters(
            { base64: (...args: JsonValue[]) => btoa(args.join(""))}
          );
        },
      });
    },
  });
  • Edit the file my-demo/packages/backend/src/index.ts to import the new backend module
// Add the module: scaffoldTemplateFilters
backend.add(import('./modules/scaffoldTemplateFilters'));
...
backend.start()
  • Create a dummy template within the examples folder
cat <<EOF > ./examples/template/dummy.yaml
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
  name: dummy
  title: Dummy template
  description: Dummy template to test fields
spec:
  owner: guests
  type: service

  # These parameters are used to generate the input form in the frontend, and are
  # used to gather input data for the execution of the template.
  parameters:
    - title: input
      required:
        - myInput
      properties:
        myInput:
          title: Input
          type: string
          description: Input
  steps:
    - id: debug
      name: debug
      action: debug:log
      input:
        message: \${{ parameters.myInput | base64 }}
EOF
  • Add it to the app-config.yaml file
...
    # Local dummy template
    - type: file
      target: ../../examples/template/dummy.yaml
      rules:
        - allow: [Template]
  • Launch backstage locally
yarn dev
  • Open the dummy template, add your name using the inputName field, click on review and create button
  • Check the log of the backstage backend application
type=incomingRequest
2024-04-05T12:14:34.973Z rootHttpRouter info ::1 - - [05/Apr/2024:12:14:34 +0000] "POST /api/scaffolder/v2/tasks HTTP/1.1" 201 45 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36" type=incomingRequest
info: {
  "message": "Q2hhcmxlcw=="
}
info: Q2hhcmxlcw== 

Use an external module

  • Create a backend module within the plugins folder of your backstage app project using the client: npx @backstage/cli@latest new --select backend-module
npx @backstage/cli@latest new --select backend-module
? Enter the ID of the plugin [required] scaffolder
? Enter the ID of the module [required] my-custom-filter
cd 
  • Rename the name of the new plugin package created from "name": "backstage-plugin-scaffolder-backend-module-my-custom-filter", to "name": "@internal/backend-module-scaffolder-my-custom-filter",
  • Update the dependency name within the package.json of the backend application too
  • Add the needed dependency to the plugin created:
    yarn --cwd plugins/scaffolder-backend-module-my-custom-filter add @backstage/plugin-scaffolder-node
    yarn --cwd plugins/scaffolder-backend-module-my-custom-filter add @backstage/types
  • Override the createBackendModule function code within your filter definition:
... = createBackendModule({
    pluginId: 'scaffolder',
    moduleId: 'my-custom-filter',
    register(env) {
        env.registerInit({
            deps: {
                scaffolder: scaffolderTemplatingExtensionPoint,
            },
            async init({ scaffolder }) {
                scaffolder.addTemplateFilters(
                    {base64: (...args: JsonValue[]) => btoa(args.join(""))}
                );
            },
        });
    },
});
  • Update the code of the /Users/cmoullia/temp/backstage-next/backstage-backend-module/packages/backend/src/index.ts to comment the line importing the module locally // import scaffolderTemplatingExtension from './modules/scaffoldTemplateFilters';
  • and import the module backend.add(import('@internal/backend-module-scaffolder-my-custom-filter'));
  • Execute yarn install to verify that the internal module is well imported
  • Launch backstage locally

backstage-backend-module's People

Contributors

cmoulliard avatar

backstage-backend-module's Issues

Backstage fails to start with a new module: Cannot read properties of undefined (reading 'id')

Issue

Backstage fails to start with a new module: Cannot read properties of undefined (reading 'id')

Error reported is:

[1] 2024-04-05T14:30:41.840Z catalog info Performing database migration 
[1] /Users/cmoullia/temp/backstage-next/my-demo/node_modules/@backstage/backend-app-api/src/wiring/BackendInitializer.ts:248
[1]               provides: Array.from(moduleInit.consumes).map(c => c.id),
...
[1] TypeError: Cannot read properties of undefined (reading 'id')
[1]     at <anonymous> (/Users/cmoullia/temp/backstage-next/my-demo/node_modules/@backstage/backend-app-api/src/wiring/BackendInitializer.ts:248:68)
[1]     at Array.map (<anonymous>)
[1]     at <anonymous> (/Users/cmoullia/temp/backstage-next/my-demo/node_modules/@backstage/backend-app-api/src/wiring/BackendInitializer.ts:248:57)
[1]     at Array.map (<anonymous>)
[1]     at <anonymous> (/Users/cmoullia/temp/backstage-next/my-demo/node_modules/@backstage/backend-app-api/src/wiring/BackendInitializer.ts:242:33)
[1]     at Array.map (<anonymous>)
[1]     at BackendInitializer.doStart_fn (/Users/cmoullia/temp/backstage-next/my-demo/node_modules/@backstage/backend-app-api/src/wiring/BackendInitializer.ts:237:20)
[1]     at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
[1]     at BackendInitializer.start (/Users/cmoullia/temp/backstage-next/my-demo/node_modules/@backstage/backend-app-api/src/wiring/BackendInitializer.ts:149:5)
[1]     at BackstageBackend.start (/Users/cmoullia/temp/backstage-next/my-demo/node_modules/@backstage/backend-app-api/src/wiring/BackstageBackend.ts:42:11)

If I debug the code, then the error occurs around here within the BackendInitializer.ts:

// See: https://github.com/backstage/backstage/blob/23f9a92c3bf357fd5b451e01972e788fcb508fc9/packages/backend-app-api/src/wiring/BackendInitializer.ts#L276-L289

// Once all modules have been initialized, we can initialize the plugin itself
const pluginInit = pluginInits.get(pluginId);
// We allow modules to be installed without the accompanying plugin, so the plugin may not exist
if (pluginInit) {
    const pluginDeps = await this.#getInitDeps(
        pluginInit.init.deps,
        pluginId,
    );
    await pluginInit.init.func(pluginDeps).catch(error => {
        throw new ForwardedError(
            `Plugin '${pluginId}' startup failed`,
            error,
        );
    });
debug-screen-1.png

FYI: The following code works as we don't import the function ...

import { createBackend } from '@backstage/backend-defaults';
import {coreServices, createBackendModule} from '@backstage/backend-plugin-api';
import {scaffolderTemplatingExtensionPoint} from '@backstage/plugin-scaffolder-node/alpha';
import {JsonValue} from '@backstage/types';

export const myModule = createBackendModule({
  pluginId: 'scaffolder',
  moduleId: 'my-custom-filter',
  register(env) {
    env.registerInit({
      deps: {
        config: coreServices.rootConfig,
        scaffolderTemplating: scaffolderTemplatingExtensionPoint,
      },
      async init({scaffolderTemplating}) {
        scaffolderTemplating.addTemplateFilters(
          { base64: (...args: JsonValue[]) => btoa(args.join(""))}
        );
      },
    });
  },
});

const backend = createBackend();

backend.add(import('@backstage/plugin-auth-backend'));
backend.add(import('./authModuleGithubProvider'));
backend.add(import('@backstage/plugin-auth-backend-module-guest-provider'));

backend.add(import('@backstage/plugin-adr-backend'));
backend.add(import('@backstage/plugin-app-backend/alpha'));
backend.add(import('@backstage/plugin-azure-devops-backend'));
backend.add(import('@backstage/plugin-badges-backend'));
backend.add(import('@backstage/plugin-catalog-backend-module-unprocessed'));
backend.add(
  import('@backstage/plugin-catalog-backend-module-scaffolder-entity-model'),
);
backend.add(import('@backstage/plugin-catalog-backend/alpha'));
backend.add(import('@backstage/plugin-devtools-backend'));
backend.add(import('@backstage/plugin-entity-feedback-backend'));
backend.add(import('@backstage/plugin-jenkins-backend'));
backend.add(import('@backstage/plugin-kubernetes-backend/alpha'));
backend.add(import('@backstage/plugin-lighthouse-backend'));
backend.add(import('@backstage/plugin-linguist-backend'));
backend.add(import('@backstage/plugin-playlist-backend'));
backend.add(import('@backstage/plugin-nomad-backend'));
backend.add(
  import('@backstage/plugin-permission-backend-module-allow-all-policy'),
);
backend.add(import('@backstage/plugin-permission-backend/alpha'));
backend.add(import('@backstage/plugin-proxy-backend/alpha'));
backend.add(import('@backstage/plugin-scaffolder-backend/alpha'));
backend.add(import('@backstage/plugin-scaffolder-backend-module-github'));
backend.add(import('@backstage/plugin-search-backend-module-catalog/alpha'));
backend.add(import('@backstage/plugin-search-backend-module-explore/alpha'));
backend.add(import('@backstage/plugin-search-backend-module-techdocs/alpha'));
backend.add(
  import('@backstage/plugin-catalog-backend-module-backstage-openapi'),
);
backend.add(import('@backstage/plugin-search-backend/alpha'));
backend.add(import('@backstage/plugin-techdocs-backend/alpha'));
backend.add(import('@backstage/plugin-todo-backend'));
backend.add(import('@backstage/plugin-sonarqube-backend'));
backend.add(import('@backstage/plugin-signals-backend'));
backend.add(import('@backstage/plugin-notifications-backend'));

backend.add(myModule);

backend.start();

POST request of the debug step not logged

Issue

When we use the code of this project generated for backstage version 1.25.0, then the debug step is not logged

Normally, we should see the POST message as you can see hereafter

type=incomingRequest
2024-04-05T12:14:34.973Z rootHttpRouter info ::1 - - [05/Apr/2024:12:14:34 +0000] 
"POST /api/scaffolder/v2/tasks HTTP/1.1" 201 45 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36" type=incomingRequest
info: {
  "message": "Q2hhcmxlcw=="
}
info: Q2hhcmxlcw==

but we got only the request about the template processed:

[1] 2024-04-08T08:27:50.092Z rootHttpRouter info ::1 - - [08/Apr/2024:08:27:50 +0000] "
GET /api/catalog/entities/by-name/template/default/dummy HTTP/1.1" 200 999 "-" "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)" type=incomingRequest
[1] 2024-04-08T08:27:50.095Z rootHttpRouter info ::1 - - [08/Apr/2024:08:27:50 +0000] 
"GET /api/scaffolder/v2/templates/default/template/dummy/parameter-schema HTTP/1.1" 304 - "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36" type=incomingRequest
...

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.