Skip to content

Introduce an extension factory middleware#29068

Merged
iamEAP merged 2 commits intomasterfrom
extension/factory-middleware
Mar 7, 2025
Merged

Introduce an extension factory middleware#29068
iamEAP merged 2 commits intomasterfrom
extension/factory-middleware

Conversation

@iamEAP
Copy link
Copy Markdown
Member

@iamEAP iamEAP commented Mar 6, 2025

What / Why

I believe we'll find a variety of use-cases unlocked if we introduce a way to modify extensions en-masse as they are being instantiated. A naive example of what would be possible:

const app = createApp({
  features,
  *extensionFactoryMiddleware(originalFactory, context) {
    const output = originalFactory();
    yield* output;
    const element = output.get(coreExtensionData.reactElement);

    const owner = getOwnerForPlugin(context.node.spec.source?.id);

    if (element) {
      // Associate analytics events within this extension to a plugin "owner"
      yield coreExtensionData.reactElement(
        <AnalyticsContext attributes={{ owner }>
          {element}
        </AnalyticsContext>,
      );
    }
  },
});

Another use-case would be swapping out extensions entirely under some circumstances, e.g. for A/B testing (for which access to metadata about the extension tree would be helpful).

✔️ Checklist

  • A changeset describing the change and affected packages. (more info)
  • Tests for new functionality and regression tests for bug fixes
  • All your commits have a Signed-off-by line in the message. (more info)

@iamEAP iamEAP requested review from a team as code owners March 6, 2025 10:57
@iamEAP iamEAP requested review from freben and vinzscam March 6, 2025 10:57
@backstage-goalie
Copy link
Copy Markdown
Contributor

Changed Packages

Package Name Package Path Changeset Bump Current Version
@backstage/frontend-app-api packages/frontend-app-api patch v0.10.6-next.1
@backstage/frontend-defaults packages/frontend-defaults patch v0.1.7-next.1

iamEAP added 2 commits March 6, 2025 13:01
Signed-off-by: Eric Peterson <ericpeterson@spotify.com>
Signed-off-by: Eric Peterson <ericpeterson@spotify.com>
@iamEAP iamEAP force-pushed the extension/factory-middleware branch from 32a80b9 to 3d26073 Compare March 6, 2025 12:01
@iamEAP iamEAP requested a review from Rugvip March 7, 2025 14:12
Copy link
Copy Markdown
Member

@Rugvip Rugvip left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! 🎉

Some optional bits and followup investigation but good to merge for me

Comment on lines +44 to +46
export type ExtensionFactoryMiddleware = Parameters<
ExtensionDefinition['override']
>[0]['factory'];
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👌

Comment on lines +38 to +40
apis: ApiHolder;
createRoot(): JSX_2.Element;
tree: AppTree;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Forgot about this bit and was thinking it could make sense to wrap things up a bit more potentially. Then I had the thought that maybe we should go in the opposite direction, where we only return { apis, tree } here and grab the root from the tree instead. Or going even further, adding apis into the tree and just returning { tree }

Either way, happy to merge as is but I might tinker a bit with this in a followup.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mmm. Could be. Didn't personally feel super comfortable making backwards incompatible changes to this function, but if it makes sense, go for it.

Comment on lines +311 to +312
...context,
config: configOverride,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The type indicates that that we can override inputs as well right? i.e.

config: (innerContext?.config ?? config) as any,
inputs: resolveInputOverrides(
options.inputs,
inputs,
innerContext?.inputs,
) as any,

I can take care of getting that sorted if you want though.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That'd be great! I think it also can take params? But it wasn't immediately obvious to me how to incorporate either.

@iamEAP iamEAP merged commit c396853 into master Mar 7, 2025
@iamEAP iamEAP deleted the extension/factory-middleware branch March 7, 2025 14:41
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 7, 2025

Thank you for contributing to Backstage! The changes in this pull request will be part of the 1.37.0 release, scheduled for Tue, 18 Mar 2025.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants