question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[RFC] Make existing components configurable

See original GitHub issue

Status: Open for comments

Need

As a platform developer by using backstage as a foundation I would like to have an instrument to modify existing components with ease:

replacing a nested component modifying the parts of components through configuration, like for example:

  • adding additional labels/texts
  • providing custom icons
  • making buttons/icons/labels/links enabled/disabled based on custom logic.

Proposal

The proposal is to use a metadata configuration to loading and configuring the components.

The more straightforward and mostly used in React application is to provide it via properties.

As the input of configuration is going to be defined on the top level, then the properties could be provided in a hierarchical way.

For example:


{
  plugins: {
    component: {
      name: 'AboutCard',
      disableIconButton: (context: Context) => context.entity.metadata.annotations?.['backstage.io/edit-url'] && context.permissions.hasRole('ADMIN')
    }
  }
}

which will be supplied with an appropriate schema. Like for the above-mentioned example:

type Context {
  entity: Entity;
  permissions: Permissions
}

type Properties = {
  plugins: {
    component: {
      name: string;
      disableIconButton: (context: Context) => boolean
    }
  }
}

The disadvantage could be with the hassle of propagating the tree of properties across all components.

Alternatives

Other way could be the alternative way to define metadata/configuration via external configuration files like yaml/json.

Alternative #1

Same way as now app-config.yaml is used for more high-level configuration.

- plugins:
    - name: catalog
      component:
        - name: AboutCard
          disableIconButton: /path/to/file/with/defined/disableIconButtonFunction.ts
          title: /path/to/file/with/defined/renderTitleFunction.ts

Where the file contains only 1 function and get a context, based on which the logic of enabling/disabling could be decided.

For example:

type Context {
  entity: Entity;
  permissions: Permissions
}

export default function (context: Context) {
  return context.entity.metadata.annotations?.['backstage.io/edit-url'] && context.permissions.hasRole('ADMIN');
}

Same way could be done the replacement of the components.

If for example I would like to replace AboutCard to MyAboutCard cause I want to see it absolutely differently then now, I can do like:


- plugins:
    - name: catalog
      component:
        - name: AboutCard
          location: /path/to/file/with/defined/component.ts

The concept one of the ways how to make dynamic loading of components is here: https://javascript.plainenglish.io/how-to-load-a-dynamic-script-in-react-2940d30998dd

For this format also can be used type definitions supplied in typescript and validated during runtime with https://github.com/gcanti/io-ts/

And on top of that to generate API documentation with tools like http://typedoc.org/

Alternative #2

It could be also the approach in the middle of the initial proposition and alternative #1.

By supplying as a configuration not a file, but typescript object, which will be consumed with the underlying components not via properties but via Redux store (or similar).

That way each component “knows” which section of redux store to read.

This is quite similar how in backstage now works useEntity( ) and other similar methods.

Risks

The codebase will have an extra abstract layer which has to be propagated to all configurable embedded components and plugins.

Keeping straightforward implementation which can’t be extended/modified is more simple to develop and maintain.

But such approach has limitations when it is used by other vendors which would like to do certain things in an own way.

UX decisions, look and feel can have different standards from company to company.

Current approach of copy pasting the code, making a fork of certain part of backstage won’t play well in a long run, as it entails the high maintenance costs especially after each major backstage release.

P.S.

This is a quite crucial feature for us in bol.com. In case of getting to a mutual conclusion we are open to volunteer our help to implement the proposal.

Issue Analytics

  • State:closed
  • Created a year ago
  • Reactions:1
  • Comments:17 (16 by maintainers)

github_iconTop GitHub Comments

2reactions
Rugvipcommented, May 6, 2022

I’m actually leaning towards an approach where these configurations aren’t done at the app level, but rather as some form of plugin decoration managed as a plugin itself. This is from the point of view of hundreds of plugins in a large installation, each with their own separate owners from various parts of an organization. There’s a mix of both internal plugins, as well as external plugins, and the integration of these external plugins is not necessarily owned by the app integration team. If we put all of this configuration in the app I feel there is a large risk of it over time becoming mom’s spaghetti, with ownership all over the place.

Roughly the idea would be that a plugin would provide an optional API that essentially recreates itself with a new configuration. A rough idea:

// In the open-source catalog pugin
const catalogPlugin = createPlugin<CatalogPluginConfiguration>({
  ...,
});

// In an internal customization plugin, perhaps `plugins/acme-catalog`
const acmeCatalogPlugin = catalogPlugin.reconfigure({
  disableStarredEntities: true,
});

The acmeCatalogPlugin would essentially be a reconfigured clone, and extensions would follow along (somehow).

This ofc ends up playing very well with TypeScript and an open set of plugins, but it does perhaps add a bit more overhead.

1reaction
aciertocommented, May 13, 2022

I agree with you @Rugvip, reconfigure shouldn’t allow to add extra fields, but only override default.

I incorporated feedback given @grantila to keep the configuration in registry with an ability to look it up with help of React context.

I kept the naming of it as metadata, not provider, as provider in backstage codebase is appended to Api, Routing, Themes, etc.

PR: https://github.com/backstage/backstage/pull/11404

Read more comments on GitHub >

github_iconTop Results From Across the Web

RFC 3139: Requirements for Configuration Management of IP ...
4 3.0 Requirements for an IP-based Configuration Management System . ... these new technologies believed that the existing SNMP based management framework, ...
Read more >
RFC 5812 - Forwarding and Control Element Separation ...
The model represents the capabilities, state, and configuration of forwarding elements within the context of the ForCES protocol, so that control elements ......
Read more >
RFC-0127: Structured Configuration - Fuchsia
This RFC proposes a new "structured" configuration system that lets component developers easily and consistently solve a set of common component ...
Read more >
Creating an RFC - AMS Advanced User Guide
Navigate to the Create RFC page: In the left navigation pane of the AMS console click RFCs to open the RFCs list page,...
Read more >
RFC Receiver Adapter - SAP Cloud Integration
To further understand the destination configuration properties, see Creating an RFC Destination. While you configure a destination make sure to select internet ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found