diff --git a/.changeset/api-override-deprecation-warning-defaults.md b/.changeset/api-override-deprecation-warning-defaults.md new file mode 100644 index 0000000000..8616460390 --- /dev/null +++ b/.changeset/api-override-deprecation-warning-defaults.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-defaults': patch +--- + +The `API_FACTORY_CONFLICT` error is now treated as a warning and will not prevent the app from starting. diff --git a/.changeset/api-override-deprecation-warning.md b/.changeset/api-override-deprecation-warning.md new file mode 100644 index 0000000000..91ed83efaf --- /dev/null +++ b/.changeset/api-override-deprecation-warning.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-app-api': patch +--- + +Updated the behavior of the new API override logic to log a deprecation warning instead of rejecting the override and blocking app startup, as was originally intended. diff --git a/.patches/pr-32522.txt b/.patches/pr-32522.txt new file mode 100644 index 0000000000..06f33f31b1 --- /dev/null +++ b/.patches/pr-32522.txt @@ -0,0 +1 @@ +Rolls back the immediate breaking change of API factory conflicts in the new frontend system, making it a deprecation warning instead. \ No newline at end of file diff --git a/packages/frontend-app-api/src/wiring/createSpecializedApp.test.tsx b/packages/frontend-app-api/src/wiring/createSpecializedApp.test.tsx index 6c79002ad9..fe807a1384 100644 --- a/packages/frontend-app-api/src/wiring/createSpecializedApp.test.tsx +++ b/packages/frontend-app-api/src/wiring/createSpecializedApp.test.tsx @@ -289,7 +289,7 @@ describe('createSpecializedApp', () => { expect(mockAnalyticsApi).toHaveBeenCalled(); }); - it('should select the API factory from the owning plugin on conflict', () => { + it('should warn when API overrides would be blocked by new logic', () => { const testApiRef = createApiRef<{ value: string }>({ id: 'test.api' }); const app = createSpecializedApp({ @@ -303,7 +303,7 @@ describe('createSpecializedApp', () => { defineParams({ api: testApiRef, deps: {}, - factory: () => ({ value: 'other' }), + factory: () => ({ value: 'other-before' }), }), }), ], @@ -329,7 +329,7 @@ describe('createSpecializedApp', () => { defineParams({ api: testApiRef, deps: {}, - factory: () => ({ value: 'other' }), + factory: () => ({ value: 'other-after' }), }), }), ], @@ -348,7 +348,8 @@ describe('createSpecializedApp', () => { }), ]); - expect(app.apis.get(testApiRef)).toEqual({ value: 'owner' }); + // Old behavior: last factory wins + expect(app.apis.get(testApiRef)).toEqual({ value: 'other-after' }); }); it('should allow API overrides within the same plugin', () => { diff --git a/packages/frontend-app-api/src/wiring/createSpecializedApp.tsx b/packages/frontend-app-api/src/wiring/createSpecializedApp.tsx index f902500e9f..a400d0d499 100644 --- a/packages/frontend-app-api/src/wiring/createSpecializedApp.tsx +++ b/packages/frontend-app-api/src/wiring/createSpecializedApp.tsx @@ -428,13 +428,14 @@ function createApiFactories(options: { existingPluginId: acceptedPluginId, }, }); - if (shouldReplace) { - factoriesById.set(apiRefId, { - pluginId, - factory: apiFactory, - }); + if (!shouldReplace) { + // eslint-disable-next-line no-console + console.warn( + `DEPRECATION WARNING: Plugin '${rejectedPluginId}' is overriding API '${apiRefId}' ` + + `from plugin '${acceptedPluginId}'. This will be blocked in a future release. ` + + `Please use a module for plugin '${acceptedPluginId}' instead.`, + ); } - continue; } factoriesById.set(apiRefId, { pluginId, factory: apiFactory }); diff --git a/packages/frontend-defaults/src/maybeCreateErrorPage.tsx b/packages/frontend-defaults/src/maybeCreateErrorPage.tsx index c0fb807deb..60e1dfa4f9 100644 --- a/packages/frontend-defaults/src/maybeCreateErrorPage.tsx +++ b/packages/frontend-defaults/src/maybeCreateErrorPage.tsx @@ -24,6 +24,7 @@ const DEFAULT_WARNING_CODES: Array = [ 'EXTENSION_INPUT_DATA_IGNORED', 'EXTENSION_INPUT_INTERNAL_IGNORED', 'EXTENSION_OUTPUT_IGNORED', + 'API_FACTORY_CONFLICT', ]; function AppErrorItem(props: { error: AppError }): JSX.Element {