fix(DryRunContext): pass down template secrets to dryRun api

Signed-off-by: sonikro <jonathan@nagayoshi.com.br>
This commit is contained in:
sonikro
2025-02-21 21:59:46 +00:00
parent b8ef3783fb
commit 6a3fa48167
7 changed files with 145 additions and 41 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-scaffolder': patch
---
Fixes DryRunContext not forwarding the correct Scaffolder Secrets to the DryRun API
@@ -14,10 +14,23 @@
* limitations under the License.
*/
import { base64EncodeContent } from './DryRunContext';
import { TestApiProvider } from '@backstage/test-utils';
// eslint-disable-next-line no-restricted-imports
import { TextEncoder } from 'util';
import {
base64EncodeContent,
DryRunProvider,
useDryRun,
} from './DryRunContext';
import { errorApiRef } from '@backstage/core-plugin-api';
import {
scaffolderApiRef,
SecretsContextProvider,
} from '@backstage/plugin-scaffolder-react';
import { renderHook } from '@testing-library/react';
import React from 'react';
import { formDecoratorsApiRef } from '../../api';
window.TextEncoder = TextEncoder;
@@ -42,3 +55,56 @@ describe('base64EncodeContent', () => {
);
});
});
describe('DryRunProvider', () => {
describe('execute', () => {
it('passes the secrets from the SecretsContext to the dryRun call', async () => {
const scaffolderApiMock = {
dryRun: jest.fn(),
};
const formDecoratorsApiMock = {
getFormDecorators: jest.fn().mockResolvedValue([]),
};
const { result } = renderHook(
() => ({
hook: useDryRun(),
}),
{
wrapper: ({ children }: React.PropsWithChildren<{}>) => (
<TestApiProvider
apis={[
[scaffolderApiRef, scaffolderApiMock],
[formDecoratorsApiRef, formDecoratorsApiMock],
[errorApiRef, { post: jest.fn() }],
]}
>
<SecretsContextProvider initialSecrets={{ foo: 'bar' }}>
<DryRunProvider>{children}</DryRunProvider>
</SecretsContextProvider>
</TestApiProvider>
),
},
);
const {
hook: { execute },
} = result.current;
// When
await execute({
templateContent: 'content',
values: {},
files: [],
});
// Then
expect(scaffolderApiMock.dryRun).toHaveBeenCalledWith({
template: 'content',
values: {},
secrets: { foo: 'bar' },
directoryContents: [],
});
});
});
});
@@ -29,6 +29,7 @@ import React, {
import {
scaffolderApiRef,
ScaffolderDryRunResponse,
useTemplateSecrets,
} from '@backstage/plugin-scaffolder-react';
import { useFormDecorators } from '../../hooks/useFormDecorators';
@@ -84,7 +85,7 @@ export function base64EncodeContent(content: string): string {
export function DryRunProvider(props: DryRunProviderProps) {
const decorators = useFormDecorators();
const scaffolderApi = useApi(scaffolderApiRef);
const { secrets: contextSecrets } = useTemplateSecrets();
const [state, setState] = useState<
Pick<DryRun, 'results' | 'selectedResult'>
>({
@@ -134,7 +135,7 @@ export function DryRunProvider(props: DryRunProviderProps) {
const { formState: values, secrets } = await decorators.run({
formState: options.values as Record<string, JsonValue>,
secrets: {},
secrets: contextSecrets,
manifest: parsed?.spec,
});
@@ -158,7 +159,7 @@ export function DryRunProvider(props: DryRunProviderProps) {
selectedResult: prevState.selectedResult ?? result,
}));
},
[scaffolderApi, decorators],
[scaffolderApi, decorators, contextSecrets],
);
const dryRun = useMemo(
@@ -18,7 +18,10 @@ import { renderInTestApp, TestApiProvider } from '@backstage/test-utils';
import { act, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React, { useEffect } from 'react';
import { scaffolderApiRef } from '@backstage/plugin-scaffolder-react';
import {
scaffolderApiRef,
SecretsContextProvider,
} from '@backstage/plugin-scaffolder-react';
import { DryRunProvider, useDryRun } from '../DryRunContext';
import { DryRunResults } from './DryRunResults';
import { formDecoratorsApiRef } from '../../../api';
@@ -75,9 +78,11 @@ describe('DryRunResults', () => {
it('renders without exploding', async () => {
await renderInTestApp(
<TestApiProvider apis={mockApis}>
<DryRunProvider>
<DryRunResults />
</DryRunProvider>
<SecretsContextProvider>
<DryRunProvider>
<DryRunResults />
</DryRunProvider>
</SecretsContextProvider>
</TestApiProvider>,
);
expect(screen.getByText('Dry-run results')).toBeInTheDocument();
@@ -86,10 +91,12 @@ describe('DryRunResults', () => {
it('expands when dry-run result is added and toggles on click, and disappears when results are gone', async () => {
const { rerender } = await renderInTestApp(
<TestApiProvider apis={mockApis}>
<DryRunProvider>
<DryRunRemote />
<DryRunResults />
</DryRunProvider>
<SecretsContextProvider>
<DryRunProvider>
<DryRunRemote />
<DryRunResults />
</DryRunProvider>
</SecretsContextProvider>
</TestApiProvider>,
);
@@ -98,10 +105,12 @@ describe('DryRunResults', () => {
await act(async () => {
rerender(
<TestApiProvider apis={mockApis}>
<DryRunProvider>
<DryRunRemote execute />
<DryRunResults />
</DryRunProvider>
<SecretsContextProvider>
<DryRunProvider>
<DryRunRemote execute />
<DryRunResults />
</DryRunProvider>
</SecretsContextProvider>
</TestApiProvider>,
);
});
@@ -117,10 +126,12 @@ describe('DryRunResults', () => {
await act(async () => {
rerender(
<TestApiProvider apis={mockApis}>
<DryRunProvider>
<DryRunRemote remove />
<DryRunResults />
</DryRunProvider>
<SecretsContextProvider>
<DryRunProvider>
<DryRunRemote remove />
<DryRunResults />
</DryRunProvider>
</SecretsContextProvider>
</TestApiProvider>,
);
});
@@ -18,7 +18,10 @@ import { renderInTestApp, TestApiProvider } from '@backstage/test-utils';
import { act, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React, { useEffect } from 'react';
import { scaffolderApiRef } from '@backstage/plugin-scaffolder-react';
import {
scaffolderApiRef,
SecretsContextProvider,
} from '@backstage/plugin-scaffolder-react';
import { DryRunProvider, useDryRun } from '../DryRunContext';
import { DryRunResultsList } from './DryRunResultsList';
import { formDecoratorsApiRef } from '../../../api';
@@ -64,9 +67,11 @@ describe('DryRunResultsList', () => {
it('renders without exploding', async () => {
const rendered = await renderInTestApp(
<TestApiProvider apis={mockApis}>
<DryRunProvider>
<DryRunResultsList />
</DryRunProvider>
<SecretsContextProvider>
<DryRunProvider>
<DryRunResultsList />
</DryRunProvider>
</SecretsContextProvider>
</TestApiProvider>,
);
expect(rendered.baseElement.querySelector('ul')).toBeEmptyDOMElement();
@@ -75,10 +80,12 @@ describe('DryRunResultsList', () => {
it('adds new result items and deletes them', async () => {
const { rerender } = await renderInTestApp(
<TestApiProvider apis={mockApis}>
<DryRunProvider>
<DryRunRemote execute={1} />
<DryRunResultsList />
</DryRunProvider>
<SecretsContextProvider>
<DryRunProvider>
<DryRunRemote execute={1} />
<DryRunResultsList />
</DryRunProvider>
</SecretsContextProvider>
</TestApiProvider>,
);
@@ -88,10 +95,12 @@ describe('DryRunResultsList', () => {
await act(async () => {
rerender(
<TestApiProvider apis={mockApis}>
<DryRunProvider>
<DryRunRemote execute={2} />
<DryRunResultsList />
</DryRunProvider>
<SecretsContextProvider>
<DryRunProvider>
<DryRunRemote execute={2} />
<DryRunResultsList />
</DryRunProvider>
</SecretsContextProvider>
</TestApiProvider>,
);
});
@@ -19,7 +19,10 @@ import { renderInTestApp, TestApiProvider } from '@backstage/test-utils';
import { screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React, { ReactNode, useEffect } from 'react';
import { scaffolderApiRef } from '@backstage/plugin-scaffolder-react';
import {
scaffolderApiRef,
SecretsContextProvider,
} from '@backstage/plugin-scaffolder-react';
import { DryRunProvider, useDryRun } from '../DryRunContext';
import { DryRunResultsView } from './DryRunResultsView';
import { formDecoratorsApiRef } from '../../../api';
@@ -79,10 +82,12 @@ describe('DryRunResultsView', () => {
],
]}
>
<DryRunProvider>
<DryRunResultsView />
<DryRunRemote />
</DryRunProvider>
<SecretsContextProvider>
<DryRunProvider>
<DryRunResultsView />
<DryRunRemote />
</DryRunProvider>
</SecretsContextProvider>
</TestApiProvider>,
{
mountedRoutes: {
@@ -18,7 +18,10 @@ import React from 'react';
import { screen } from '@testing-library/react';
import { renderInTestApp, TestApiProvider } from '@backstage/test-utils';
import { catalogApiRef } from '@backstage/plugin-catalog-react';
import { scaffolderApiRef } from '@backstage/plugin-scaffolder-react';
import {
scaffolderApiRef,
SecretsContextProvider,
} from '@backstage/plugin-scaffolder-react';
import { TemplateEditorPage } from './TemplateEditorPage';
import { rootRouteRef } from '../../../routes';
import { formDecoratorsApiRef } from '../../api';
@@ -39,7 +42,9 @@ describe('TemplateEditorPage', () => {
[formDecoratorsApiRef, formDecoratorsApiMock],
]}
>
<TemplateEditorPage />
<SecretsContextProvider>
<TemplateEditorPage />
</SecretsContextProvider>
</TestApiProvider>,
{
mountedRoutes: {
@@ -61,7 +66,9 @@ describe('TemplateEditorPage', () => {
[formDecoratorsApiRef, formDecoratorsApiMock],
]}
>
<TemplateEditorPage />
<SecretsContextProvider>
<TemplateEditorPage />
</SecretsContextProvider>
</TestApiProvider>,
{
mountedRoutes: {