frontend-app-api: switch new API conflict error to be a warning

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2026-01-26 13:23:28 +01:00
parent 4ad63b8d9f
commit 17e0eb3e5f
6 changed files with 24 additions and 10 deletions
@@ -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.
@@ -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.
+1
View File
@@ -0,0 +1 @@
Rolls back the immediate breaking change of API factory conflicts in the new frontend system, making it a deprecation warning instead.
@@ -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', () => {
@@ -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 });
@@ -23,6 +23,7 @@ const DEFAULT_WARNING_CODES: Array<keyof AppErrorTypes> = [
'INVALID_EXTENSION_CONFIG_KEY',
'EXTENSION_INPUT_DATA_IGNORED',
'EXTENSION_OUTPUT_IGNORED',
'API_FACTORY_CONFLICT',
];
function AppErrorItem(props: { error: AppError }): JSX.Element {