core-app-api: add support for backstage.io/config tags

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2024-08-30 15:44:40 +02:00
parent 590fb2dfd7
commit ea69e4671c
3 changed files with 76 additions and 2 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/core-app-api': patch
---
The `defaultConfigLoader` now also reads configuration from scripts tags with `type="backstage.io/config"`. The tag is expected to contain a JSON-serialized array of `AppConfig` objects. If any of these script tags are present, the injected runtime configuration in the static assets will no longer be used.
@@ -14,7 +14,10 @@
* limitations under the License.
*/
import React from 'react';
import { render } from '@testing-library/react';
import { defaultConfigLoaderSync } from './defaultConfigLoader';
import { ConfigReader } from '@backstage/config';
(process as any).env = { NODE_ENV: 'test' };
const anyEnv = process.env as any;
@@ -53,6 +56,45 @@ describe('defaultConfigLoaderSync', () => {
]);
});
it('loads config from script tags and ignore static config', () => {
anyEnv.APP_CONFIG = [];
render(
<script type="backstage.io/config">
{`[{"data":{"my":"config"},"context":"a"},{"data":{"my":"override-config"},"context":"b"}]`}
</script>,
);
const configs = (defaultConfigLoaderSync as any)('{"my":"runtime-config"}');
expect(configs).toEqual([
{ data: { my: 'config' }, context: 'a' },
{ data: { my: 'override-config' }, context: 'b' },
]);
expect(ConfigReader.fromConfigs(configs).get('my')).toBe('override-config');
});
it('loads config from all script tags in order', () => {
anyEnv.APP_CONFIG = [];
render(
<>
<script type="backstage.io/config">
{`[{"data":{"my":"config"},"context":"a"}]`}
</script>
<script type="backstage.io/config">
{`[{"data":{"my":"override-config"},"context":"b"}]`}
</script>
</>,
);
const configs = (defaultConfigLoaderSync as any)('{"my":"runtime-config"}');
expect(configs).toEqual([
{ data: { my: 'config' }, context: 'a' },
{ data: { my: 'override-config' }, context: 'b' },
]);
expect(ConfigReader.fromConfigs(configs).get('my')).toBe('override-config');
});
it('fails to load invalid missing config', () => {
expect(() => defaultConfigLoaderSync()).toThrow(
'No static configuration provided',
@@ -49,9 +49,36 @@ export function defaultConfigLoaderSync(
}
const configs = appConfig.slice() as unknown as AppConfig[];
// Avoiding this string also being replaced at runtime
if (
// Check if we have any config script tags, otherwise fall back to injected config
const configScripts = document.querySelectorAll(
'script[type="backstage.io/config"]',
);
if (configScripts.length > 0) {
for (const el of configScripts) {
try {
const content = el.textContent;
if (!content) {
throw new Error('tag is empty');
}
let data;
try {
data = JSON.parse(content);
} catch (error) {
throw new Error(`failed to parse config; ${error}`);
}
if (!Array.isArray(data)) {
throw new Error('data is not an array');
}
configs.push(...data);
} catch (error) {
throw new Error(
`Failed to load config from script tag, ${error.message}`,
);
}
}
} else if (
runtimeConfigJson !==
// Avoiding this string also being replaced at runtime
'__app_injected_runtime_config__'.toLocaleUpperCase('en-US')
) {
try {