Merge pull request #15004 from akundrock/scaffolder/fields/bitbucketRepoPicker
Add field selection rendering of allowedProjects for BitbucketRepoPicker
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user