api-ref: move opaque helper to frontend-internal
Share the internal ApiRef opaque helper through frontend-internal and fail fast when ApiRef-shaped values have an unsupported opaque version. Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com> Made-with: Cursor
This commit is contained in:
@@ -423,7 +423,7 @@ describe('createSpecializedApp', () => {
|
||||
expect(app.apis.get(testApiRef)).toEqual({ value: 'owner' });
|
||||
});
|
||||
|
||||
it('should ignore plugin ownership metadata from unsupported opaque ApiRefs', () => {
|
||||
it('should reject unsupported opaque ApiRef versions', () => {
|
||||
const testApiRef = {
|
||||
$$type: '@backstage/ApiRef',
|
||||
version: 'v0',
|
||||
@@ -439,46 +439,39 @@ describe('createSpecializedApp', () => {
|
||||
readonly pluginId: 'owner';
|
||||
};
|
||||
|
||||
const app = createSpecializedApp({
|
||||
features: [
|
||||
makeAppPlugin(),
|
||||
createFrontendPlugin({
|
||||
pluginId: 'other-before',
|
||||
extensions: [
|
||||
ApiBlueprint.make({
|
||||
params: defineParams =>
|
||||
defineParams({
|
||||
api: testApiRef,
|
||||
deps: {},
|
||||
factory: () => ({ value: 'other' }),
|
||||
}),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
createFrontendPlugin({
|
||||
pluginId: 'owner',
|
||||
extensions: [
|
||||
ApiBlueprint.make({
|
||||
params: defineParams =>
|
||||
defineParams({
|
||||
api: testApiRef,
|
||||
deps: {},
|
||||
factory: () => ({ value: 'owner' }),
|
||||
}),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
expect(app.errors).toEqual([
|
||||
expect.objectContaining({
|
||||
code: 'API_FACTORY_CONFLICT',
|
||||
message: expect.stringContaining("API 'shared.api'"),
|
||||
expect(() =>
|
||||
createSpecializedApp({
|
||||
features: [
|
||||
makeAppPlugin(),
|
||||
createFrontendPlugin({
|
||||
pluginId: 'other-before',
|
||||
extensions: [
|
||||
ApiBlueprint.make({
|
||||
params: defineParams =>
|
||||
defineParams({
|
||||
api: testApiRef,
|
||||
deps: {},
|
||||
factory: () => ({ value: 'other' }),
|
||||
}),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
createFrontendPlugin({
|
||||
pluginId: 'owner',
|
||||
extensions: [
|
||||
ApiBlueprint.make({
|
||||
params: defineParams =>
|
||||
defineParams({
|
||||
api: testApiRef,
|
||||
deps: {},
|
||||
factory: () => ({ value: 'owner' }),
|
||||
}),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
]);
|
||||
|
||||
expect(app.apis.get(testApiRef)).toEqual({ value: 'other' });
|
||||
).toThrow("Invalid opaque type instance, got version 'v0', expected 'v1'");
|
||||
});
|
||||
|
||||
it('should not infer app ownership from core-prefixed API ids', () => {
|
||||
|
||||
@@ -43,6 +43,7 @@ import {
|
||||
import { ApiFactoryRegistry, ApiResolver } from '@backstage/core-app-api';
|
||||
import {
|
||||
createExtensionDataContainer,
|
||||
OpaqueApiRef,
|
||||
OpaqueFrontendPlugin,
|
||||
} from '@internal/frontend';
|
||||
|
||||
@@ -51,8 +52,6 @@ import {
|
||||
resolveExtensionDefinition,
|
||||
toInternalExtension,
|
||||
} from '../../../frontend-plugin-api/src/wiring/resolveExtensionDefinition';
|
||||
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
|
||||
import { OpaqueApiRef } from '../../../frontend-plugin-api/src/apis/system/ApiRef';
|
||||
|
||||
import {
|
||||
extractRouteInfoFromAppNode,
|
||||
@@ -459,13 +458,9 @@ function createApiFactories(options: {
|
||||
// might need to wait for some future update for API factories.
|
||||
function getApiOwnerId(apiRef: { id: string }): string {
|
||||
if (OpaqueApiRef.isType(apiRef)) {
|
||||
try {
|
||||
const { pluginId } = OpaqueApiRef.toInternal(apiRef);
|
||||
if (pluginId) {
|
||||
return pluginId;
|
||||
}
|
||||
} catch {
|
||||
// Fall back to legacy ID inference for unsupported opaque ApiRef versions.
|
||||
const { pluginId } = OpaqueApiRef.toInternal(apiRef);
|
||||
if (pluginId) {
|
||||
return pluginId;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { ApiRef } from '@backstage/frontend-plugin-api';
|
||||
import { OpaqueType } from '@internal/opaque';
|
||||
|
||||
export const OpaqueApiRef = OpaqueType.create<{
|
||||
public: ApiRef<unknown> & {
|
||||
readonly $$type: '@backstage/ApiRef';
|
||||
};
|
||||
versions: {
|
||||
readonly version: 'v1';
|
||||
readonly pluginId?: string;
|
||||
};
|
||||
}>({
|
||||
type: '@backstage/ApiRef',
|
||||
versions: ['v1'],
|
||||
});
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export * from './OpaqueApiRef';
|
||||
@@ -15,4 +15,5 @@
|
||||
*/
|
||||
|
||||
export * from './routing';
|
||||
export * from './apis';
|
||||
export * from './wiring';
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { OpaqueType } from '@internal/opaque';
|
||||
import { OpaqueApiRef } from '@internal/frontend';
|
||||
import type { ApiRef } from './types';
|
||||
|
||||
/**
|
||||
@@ -31,20 +31,6 @@ type ApiRefBuilderConfig<TId extends string> = {
|
||||
pluginId?: string;
|
||||
};
|
||||
|
||||
/** @internal */
|
||||
export const OpaqueApiRef = OpaqueType.create<{
|
||||
public: ApiRef<unknown> & {
|
||||
readonly $$type: '@backstage/ApiRef';
|
||||
};
|
||||
versions: {
|
||||
readonly version: 'v1';
|
||||
readonly pluginId?: string;
|
||||
};
|
||||
}>({
|
||||
type: '@backstage/ApiRef',
|
||||
versions: ['v1'],
|
||||
});
|
||||
|
||||
function validateId(id: string): void {
|
||||
const valid = id
|
||||
.split('.')
|
||||
|
||||
Reference in New Issue
Block a user