diff --git a/.changeset/wet-tips-yawn.md b/.changeset/wet-tips-yawn.md
new file mode 100644
index 0000000000..63f3c9dd2e
--- /dev/null
+++ b/.changeset/wet-tips-yawn.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-scaffolder': minor
+---
+
+The `RepoUrlPicker` field extension now has an `allowedProjects` option for narrowing the selection of Bitbucket URLs.
diff --git a/plugins/scaffolder-backend/sample-templates/bitbucket-demo/template.yaml b/plugins/scaffolder-backend/sample-templates/bitbucket-demo/template.yaml
index ee1bc634b4..58955582d2 100644
--- a/plugins/scaffolder-backend/sample-templates/bitbucket-demo/template.yaml
+++ b/plugins/scaffolder-backend/sample-templates/bitbucket-demo/template.yaml
@@ -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
diff --git a/plugins/scaffolder/api-report.md b/plugins/scaffolder/api-report.md
index 39e58a7a42..a8dcdd76ed 100644
--- a/plugins/scaffolder/api-report.md
+++ b/plugins/scaffolder/api-report.md
@@ -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?:
diff --git a/plugins/scaffolder/src/components/fields/RepoUrlPicker/BitbucketRepoPicker.test.tsx b/plugins/scaffolder/src/components/fields/RepoUrlPicker/BitbucketRepoPicker.test.tsx
index fc643e7360..f799d68c2d 100644
--- a/plugins/scaffolder/src/components/fields/RepoUrlPicker/BitbucketRepoPicker.test.tsx
+++ b/plugins/scaffolder/src/components/fields/RepoUrlPicker/BitbucketRepoPicker.test.tsx
@@ -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(
+ ,
+ );
+
+ 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(
+ ,
+ );
+
+ 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(
+ ,
+ );
+
+ expect(await findByText('project1')).toBeInTheDocument();
+ expect(await findByText('project2')).toBeInTheDocument();
+ });
});
});
diff --git a/plugins/scaffolder/src/components/fields/RepoUrlPicker/BitbucketRepoPicker.tsx b/plugins/scaffolder/src/components/fields/RepoUrlPicker/BitbucketRepoPicker.tsx
index 5bc1380553..d070e3fee5 100644
--- a/plugins/scaffolder/src/components/fields/RepoUrlPicker/BitbucketRepoPicker.tsx
+++ b/plugins/scaffolder/src/components/fields/RepoUrlPicker/BitbucketRepoPicker.tsx
@@ -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: {
>
)}
- The Organization that this repo will belong to
+ The Workspace that this repo will belong to
)}
@@ -78,12 +97,27 @@ export const BitbucketRepoPicker = (props: {
required
error={rawErrors?.length > 0 && !project}
>
- Project
- onChange({ project: e.target.value })}
- value={project}
- />
+ {allowedProjects?.length ? (
+