Merge pull request #15004 from akundrock/scaffolder/fields/bitbucketRepoPicker

Add field selection rendering of allowedProjects for BitbucketRepoPicker
This commit is contained in:
Patrik Oldsberg
2022-12-13 17:23:01 +01:00
committed by GitHub
7 changed files with 122 additions and 10 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-scaffolder': minor
---
The `RepoUrlPicker` field extension now has an `allowedProjects` option for narrowing the selection of Bitbucket URLs.
@@ -20,7 +20,17 @@ spec:
ui:options:
allowedHosts:
- bitbucket.org
- server.bitbucket.com
# The rest of these options are optional.
# You can read more at: https://backstage.io/docs/reference/plugin-scaffolder.repourlpickerfieldextension
# allowedOwners:
# - WORKSPACE1
# - WORKSPACE2
# allowedProjects:
# - PROJECT1
# - PROJECT2
# allowedRepos:
# - REPO1
# - REPO2
- title: Fill in some steps
required:
- name
+2
View File
@@ -340,6 +340,7 @@ export const RepoUrlPickerFieldExtension: FieldExtensionComponent<
{
allowedOwners?: string[] | undefined;
allowedOrganizations?: string[] | undefined;
allowedProjects?: string[] | undefined;
allowedRepos?: string[] | undefined;
allowedHosts?: string[] | undefined;
requestUserCredentials?:
@@ -365,6 +366,7 @@ export const RepoUrlPickerFieldSchema: FieldSchema<
{
allowedOwners?: string[] | undefined;
allowedOrganizations?: string[] | undefined;
allowedProjects?: string[] | undefined;
allowedRepos?: string[] | undefined;
allowedHosts?: string[] | undefined;
requestUserCredentials?:
@@ -56,6 +56,7 @@ describe('BitbucketRepoPicker', () => {
expect(getAllByRole('textbox')).toHaveLength(1);
});
describe('workspace field', () => {
it('calls onChange when the workspace changes', () => {
const onChange = jest.fn();
@@ -92,5 +93,47 @@ describe('BitbucketRepoPicker', () => {
expect(onChange).toHaveBeenCalledWith({ project: 'test-project' });
});
it('Does not render a select if the list of allowed projects does not exist', async () => {
const { getAllByRole } = render(
<BitbucketRepoPicker
onChange={jest.fn()}
rawErrors={[]}
state={{ host: 'bitbucket.org', repoName: 'repo' }}
/>,
);
expect(getAllByRole('textbox')).toHaveLength(2);
expect(getAllByRole('textbox')[1]).toHaveValue('');
});
it('Does not render a select if the list of allowed projects is empty', async () => {
const { getAllByRole } = render(
<BitbucketRepoPicker
onChange={jest.fn()}
rawErrors={[]}
state={{ host: 'bitbucket.org', repoName: 'repo' }}
allowedProjects={[]}
/>,
);
expect(getAllByRole('textbox')).toHaveLength(2);
expect(getAllByRole('textbox')[1]).toHaveValue('');
});
it('Does render a select if there is a list of allowed projects', async () => {
const allowedProjects = ['project1', 'project2'];
const { findByText } = render(
<BitbucketRepoPicker
onChange={jest.fn()}
rawErrors={[]}
state={{ host: 'bitbucket.org', repoName: 'repo' }}
allowedProjects={allowedProjects}
/>,
);
expect(await findByText('project1')).toBeInTheDocument();
expect(await findByText('project2')).toBeInTheDocument();
});
});
});
@@ -21,17 +21,36 @@ import InputLabel from '@material-ui/core/InputLabel';
import { Select, SelectItem } from '@backstage/core-components';
import { RepoUrlPickerState } from './types';
/**
* The underlying component that is rendered in the form for the `BitbucketRepoPicker`
* field extension.
*
* @public
* @param allowedOwners - Allowed workspaces for the Bitbucket cloud repository
* @param allowedProjects - Allowed projects for the Bitbucket cloud repository
*
*/
export const BitbucketRepoPicker = (props: {
allowedOwners?: string[];
allowedProjects?: string[];
onChange: (state: RepoUrlPickerState) => void;
state: RepoUrlPickerState;
rawErrors: string[];
}) => {
const { allowedOwners = [], onChange, rawErrors, state } = props;
const {
allowedOwners = [],
allowedProjects = [],
onChange,
rawErrors,
state,
} = props;
const { host, workspace, project } = state;
const ownerItems: SelectItem[] = allowedOwners
? allowedOwners?.map(i => ({ label: i, value: i }))
: [];
const projectItems: SelectItem[] = allowedProjects
? allowedProjects?.map(i => ({ label: i, value: i }))
: [];
useEffect(() => {
if (host === 'bitbucket.org' && allowedOwners.length) {
@@ -69,7 +88,7 @@ export const BitbucketRepoPicker = (props: {
</>
)}
<FormHelperText>
The Organization that this repo will belong to
The Workspace that this repo will belong to
</FormHelperText>
</FormControl>
)}
@@ -78,12 +97,27 @@ export const BitbucketRepoPicker = (props: {
required
error={rawErrors?.length > 0 && !project}
>
<InputLabel htmlFor="projectInput">Project</InputLabel>
<Input
id="projectInput"
onChange={e => onChange({ project: e.target.value })}
value={project}
/>
{allowedProjects?.length ? (
<Select
native
label="Allowed Projects"
onChange={s =>
onChange({ project: String(Array.isArray(s) ? s[0] : s) })
}
disabled={allowedProjects.length === 1}
selected={project}
items={projectItems}
/>
) : (
<>
<InputLabel htmlFor="projectInput">Project</InputLabel>
<Input
id="projectInput"
onChange={e => onChange({ project: e.target.value })}
value={project}
/>
</>
)}
<FormHelperText>
The Project that this repo will belong to
</FormHelperText>
@@ -60,12 +60,16 @@ export const RepoUrlPicker = (props: RepoUrlPickerProps) => {
() => uiSchema?.['ui:options']?.allowedOwners ?? [],
[uiSchema],
);
const allowedProjects = useMemo(
() => uiSchema?.['ui:options']?.allowedProjects ?? [],
[uiSchema],
);
const allowedRepos = useMemo(
() => uiSchema?.['ui:options']?.allowedRepos ?? [],
[uiSchema],
);
const { owner, organization, repoName } = state;
const { owner, organization, project, repoName } = state;
useEffect(() => {
onChange(serializeRepoPickerUrl(state));
@@ -90,6 +94,15 @@ export const RepoUrlPicker = (props: RepoUrlPickerProps) => {
}
}, [setState, allowedOwners, owner]);
useEffect(() => {
if (allowedProjects.length > 0 && !project) {
setState(prevState => ({
...prevState,
project: allowedProjects[0],
}));
}
}, [setState, allowedProjects, project]);
useEffect(() => {
if (allowedRepos.length > 0 && !repoName) {
setState(prevState => ({ ...prevState, repoName: allowedRepos[0] }));
@@ -169,6 +182,7 @@ export const RepoUrlPicker = (props: RepoUrlPickerProps) => {
{hostType === 'bitbucket' && (
<BitbucketRepoPicker
allowedOwners={allowedOwners}
allowedProjects={allowedProjects}
rawErrors={rawErrors}
state={state}
onChange={updateLocalState}
@@ -34,6 +34,10 @@ export const RepoUrlPickerFieldSchema = makeFieldSchemaFromZod(
.array(z.string())
.optional()
.describe('List of allowed owners in the given SCM platform'),
allowedProjects: z
.array(z.string())
.optional()
.describe('List of allowed projects in the given SCM platform'),
allowedRepos: z
.array(z.string())
.optional()