From 9f7b6ac9b1b6be5a08493bba271b34f38ee2c620 Mon Sep 17 00:00:00 2001 From: Benjamin Janssens Date: Mon, 3 Feb 2025 13:51:07 +0100 Subject: [PATCH] feat(scaffolder): add support for autocompleting GitHub repositories to scaffolder Signed-off-by: Benjamin Janssens --- .../src/autocomplete/autocomplete.ts | 10 ++--- .../fields/RepoUrlPicker/GithubRepoPicker.tsx | 45 +++++++++++++++---- 2 files changed, 40 insertions(+), 15 deletions(-) diff --git a/plugins/scaffolder-backend-module-github/src/autocomplete/autocomplete.ts b/plugins/scaffolder-backend-module-github/src/autocomplete/autocomplete.ts index a015067f52..08041640d9 100644 --- a/plugins/scaffolder-backend-module-github/src/autocomplete/autocomplete.ts +++ b/plugins/scaffolder-backend-module-github/src/autocomplete/autocomplete.ts @@ -40,14 +40,12 @@ export function createHandleAutocompleteRequest(options: { const client = new Octokit(octokitOptions); switch (resource) { - case 'owners': { - const organizations = await client.paginate( - client.rest.orgs.listForAuthenticatedUser, + case 'repositoriesWithOwner': { + const repositoriesWithOwner = await client.paginate( + client.rest.repos.listForAuthenticatedUser, ); - const results = organizations.map(organization => ({ - id: organization.login, - })); + const results = repositoriesWithOwner.map(r => ({ id: r.full_name })); return { results }; } diff --git a/plugins/scaffolder/src/components/fields/RepoUrlPicker/GithubRepoPicker.tsx b/plugins/scaffolder/src/components/fields/RepoUrlPicker/GithubRepoPicker.tsx index 09850d85c0..4aa9cc057b 100644 --- a/plugins/scaffolder/src/components/fields/RepoUrlPicker/GithubRepoPicker.tsx +++ b/plugins/scaffolder/src/components/fields/RepoUrlPicker/GithubRepoPicker.tsx @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import React, { useCallback, useState } from 'react'; +import React, { useCallback, useMemo, useState } from 'react'; import FormControl from '@material-ui/core/FormControl'; import FormHelperText from '@material-ui/core/FormHelperText'; import TextField from '@material-ui/core/TextField'; @@ -25,6 +25,8 @@ import { useApi } from '@backstage/core-plugin-api'; import { scaffolderApiRef } from '@backstage/plugin-scaffolder-react'; import useDebounce from 'react-use/esm/useDebounce'; import Autocomplete from '@material-ui/lab/Autocomplete'; +import uniq from 'lodash/uniq'; +import map from 'lodash/map'; export const GithubRepoPicker = ( props: BaseRepoUrlPickerProps<{ @@ -42,30 +44,55 @@ export const GithubRepoPicker = ( const scaffolderApi = useApi(scaffolderApiRef); - const [availableOwners, setAvailableOwners] = useState([]); + const [availableRepositoriesWithOwner, setAvailableRepositoriesWithOwner] = + useState<{ owner: string; name: string }[]>([]); - // Update available owners when client is available - const updateAvailableOwners = useCallback(() => { + // Update available repositories with owner when client is available + const updateAvailableRepositoriesWithOwner = useCallback(() => { if (!scaffolderApi.autocomplete || !accessToken) { - setAvailableOwners([]); + setAvailableRepositoriesWithOwner([]); return; } scaffolderApi .autocomplete({ token: accessToken, - resource: 'owners', + resource: 'repositoriesWithOwner', provider: 'github', }) .then(({ results }) => { - setAvailableOwners(results.map(r => r.id)); + setAvailableRepositoriesWithOwner( + results.map(r => { + const [rOwner, rName] = r.id.split('/'); + return { owner: rOwner, name: rName }; + }), + ); }) .catch(() => { - setAvailableOwners([]); + setAvailableRepositoriesWithOwner([]); }); }, [scaffolderApi, accessToken]); - useDebounce(updateAvailableOwners, 500, [updateAvailableOwners]); + useDebounce(updateAvailableRepositoriesWithOwner, 500, [ + updateAvailableRepositoriesWithOwner, + ]); + + // Update available owners when available repositories with owner change + const availableOwners = useMemo( + () => uniq(map(availableRepositoriesWithOwner, 'owner')), + [availableRepositoriesWithOwner], + ); + + // Update available repositories when available repositories with owner change or when owner changes + const updateAvailableRepositories = useCallback(() => { + const availableRepos = availableRepositoriesWithOwner.flatMap(r => + r.owner === owner ? [{ name: r.name }] : [], + ); + + onChange({ availableRepos }); + }, [availableRepositoriesWithOwner, owner, onChange]); + + useDebounce(updateAvailableRepositories, 500, [updateAvailableRepositories]); return ( <>