app-defaults: use FrontendHostDiscovery by default
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
---
|
||||
'@backstage/app-defaults': minor
|
||||
---
|
||||
|
||||
**BREAKING**: The default `DiscoveryApi` implementation has been switched to use `FrontendHostDiscovery`, which adds support for the `discovery.endpoints` configuration.
|
||||
|
||||
This is marked as a breaking change because it will cause any existing `discovery.endpoints` configuration to be picked up and used, which may break existing setups.
|
||||
|
||||
For example, consider the following configuration:
|
||||
|
||||
```yaml
|
||||
app:
|
||||
baseUrl: https://backstage.acme.org
|
||||
|
||||
backend:
|
||||
baseUrl: https://backstage.internal.acme.org
|
||||
|
||||
discovery:
|
||||
endpoints:
|
||||
- target: https://catalog.internal.acme.org/api/{{pluginId}}
|
||||
plugins: [catalog]
|
||||
```
|
||||
|
||||
This will now cause requests from the frontend towards the `catalog` plugin to be routed to `https://internal-catalog.acme.org/api/catalog`, but this might not be reachable from the frontend. To fix this, you should update the `discovery.endpoints` configuration to only override the internal URL of the plugin:
|
||||
|
||||
```yaml
|
||||
discovery:
|
||||
endpoints:
|
||||
- target:
|
||||
internal: https://catalog.internal.acme.org/api/{{pluginId}}
|
||||
plugins: [catalog]
|
||||
```
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/core-app-api': minor
|
||||
'@backstage/backend-defaults': patch
|
||||
---
|
||||
|
||||
The `discovery.endpoints` configuration no longer requires both `internal` and `external` target when using the object form, instead falling back to the default.
|
||||
@@ -28,13 +28,13 @@ import {
|
||||
BitbucketServerAuth,
|
||||
OAuthRequestManager,
|
||||
WebStorage,
|
||||
UrlPatternDiscovery,
|
||||
OneLoginAuth,
|
||||
UnhandledErrorForwarder,
|
||||
AtlassianAuth,
|
||||
createFetchApi,
|
||||
FetchMiddlewares,
|
||||
VMwareCloudAuth,
|
||||
FrontendHostDiscovery,
|
||||
} from '@backstage/core-app-api';
|
||||
|
||||
import {
|
||||
@@ -68,10 +68,7 @@ export const apis = [
|
||||
createApiFactory({
|
||||
api: discoveryApiRef,
|
||||
deps: { configApi: configApiRef },
|
||||
factory: ({ configApi }) =>
|
||||
UrlPatternDiscovery.compile(
|
||||
`${configApi.getString('backend.baseUrl')}/api/{{ pluginId }}`,
|
||||
),
|
||||
factory: ({ configApi }) => FrontendHostDiscovery.fromConfig(configApi),
|
||||
}),
|
||||
createApiFactory({
|
||||
api: alertApiRef,
|
||||
|
||||
+1
-1
@@ -652,7 +652,7 @@ export interface Config {
|
||||
* Can be either a string or an object with internal and external keys.
|
||||
* Targets with `{{pluginId}}` or `{{ pluginId }} in the URL will be replaced with the plugin ID.
|
||||
*/
|
||||
target: string | { internal: string; external: string };
|
||||
target: string | { internal?: string; external?: string };
|
||||
/**
|
||||
* Array of plugins which use the target base URL.
|
||||
*/
|
||||
|
||||
@@ -162,6 +162,42 @@ describe('HostDiscovery', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('allows plugin overrides to only override either internal or external targets', async () => {
|
||||
const discovery = HostDiscovery.fromConfig(
|
||||
new ConfigReader({
|
||||
backend: {
|
||||
baseUrl: 'http://localhost:40',
|
||||
listen: { port: 80, host: 'localhost' },
|
||||
},
|
||||
discovery: {
|
||||
endpoints: [
|
||||
{
|
||||
target: { internal: 'http://catalog-backend:8080/api/catalog' },
|
||||
plugins: ['catalog'],
|
||||
},
|
||||
{
|
||||
target: { external: 'http://frontend/api/scaffolder' },
|
||||
plugins: ['scaffolder'],
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
await expect(discovery.getBaseUrl('catalog')).resolves.toBe(
|
||||
'http://catalog-backend:8080/api/catalog',
|
||||
);
|
||||
await expect(discovery.getExternalBaseUrl('catalog')).resolves.toBe(
|
||||
'http://localhost:40/api/catalog',
|
||||
);
|
||||
await expect(discovery.getBaseUrl('scaffolder')).resolves.toBe(
|
||||
'http://localhost:80/api/scaffolder',
|
||||
);
|
||||
await expect(discovery.getExternalBaseUrl('scaffolder')).resolves.toBe(
|
||||
'http://frontend/api/scaffolder',
|
||||
);
|
||||
});
|
||||
|
||||
it('replaces {{pluginId}} or {{ pluginId }} in the target', async () => {
|
||||
const discovery = HostDiscovery.fromConfig(
|
||||
new ConfigReader({
|
||||
|
||||
@@ -102,10 +102,13 @@ export class HostDiscovery implements DiscoveryService {
|
||||
private getTargetFromConfig(pluginId: string, type: 'internal' | 'external') {
|
||||
const endpoints = this.discoveryConfig?.getOptionalConfigArray('endpoints');
|
||||
|
||||
const target = endpoints
|
||||
const targetOrObj = endpoints
|
||||
?.find(endpoint => endpoint.getStringArray('plugins').includes(pluginId))
|
||||
?.get<Target>('target');
|
||||
|
||||
const target =
|
||||
typeof targetOrObj === 'string' ? targetOrObj : targetOrObj?.[type];
|
||||
|
||||
if (!target) {
|
||||
const baseUrl =
|
||||
type === 'external' ? this.externalBaseUrl : this.internalBaseUrl;
|
||||
@@ -113,14 +116,7 @@ export class HostDiscovery implements DiscoveryService {
|
||||
return `${baseUrl}/${encodeURIComponent(pluginId)}`;
|
||||
}
|
||||
|
||||
if (typeof target === 'string') {
|
||||
return target.replace(
|
||||
/\{\{\s*pluginId\s*\}\}/g,
|
||||
encodeURIComponent(pluginId),
|
||||
);
|
||||
}
|
||||
|
||||
return target[type].replace(
|
||||
return target.replace(
|
||||
/\{\{\s*pluginId\s*\}\}/g,
|
||||
encodeURIComponent(pluginId),
|
||||
);
|
||||
|
||||
Vendored
+1
-1
@@ -163,7 +163,7 @@ export interface Config {
|
||||
/**
|
||||
* @visibility frontend
|
||||
*/
|
||||
external: string;
|
||||
external?: string;
|
||||
};
|
||||
/**
|
||||
* Array of plugins which use the target baseUrl.
|
||||
|
||||
+24
@@ -72,6 +72,30 @@ describe('FrontendHostDiscovery', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should not use internal plugin overrides', async () => {
|
||||
const discovery = FrontendHostDiscovery.fromConfig(
|
||||
new ConfigReader({
|
||||
backend: {
|
||||
baseUrl: 'http://localhost:40',
|
||||
},
|
||||
discovery: {
|
||||
endpoints: [
|
||||
{
|
||||
target: {
|
||||
internal: 'http://catalog-backend-internal:8080/api/catalog',
|
||||
},
|
||||
plugins: ['catalog'],
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
await expect(discovery.getBaseUrl('catalog')).resolves.toBe(
|
||||
'http://localhost:40/api/catalog',
|
||||
);
|
||||
});
|
||||
|
||||
it('uses a single target for internal and external for a plugin', async () => {
|
||||
const discovery = FrontendHostDiscovery.fromConfig(
|
||||
new ConfigReader({
|
||||
|
||||
+4
-1
@@ -54,8 +54,11 @@ export class FrontendHostDiscovery implements DiscoveryApi {
|
||||
?.flatMap(e => {
|
||||
const target =
|
||||
typeof e.get('target') === 'object'
|
||||
? e.getString('target.external')
|
||||
? e.getOptionalString('target.external')
|
||||
: e.getString('target');
|
||||
if (!target) {
|
||||
return [];
|
||||
}
|
||||
const discovery = UrlPatternDiscovery.compile(target);
|
||||
return e
|
||||
.getStringArray('plugins')
|
||||
|
||||
Reference in New Issue
Block a user