feat: add auto-completion to workspaces and repositories
Signed-off-by: Benjamin Janssens <benji.janssens@gmail.com>
This commit is contained in:
@@ -85,6 +85,16 @@ export class BitbucketCloudClient {
|
||||
);
|
||||
}
|
||||
|
||||
listWorkspaces(
|
||||
options?: FilterAndSortOptions & PartialResponseOptions,
|
||||
): WithPagination<Models.PaginatedWorkspaces, Models.Workspace> {
|
||||
return new WithPagination(
|
||||
paginationOptions =>
|
||||
this.createUrl('/workspaces', { ...paginationOptions, ...options }),
|
||||
url => this.getTypeMapped(url),
|
||||
);
|
||||
}
|
||||
|
||||
private createUrl(endpoint: string, options?: RequestOptions): URL {
|
||||
const request = new URL(this.config.apiBaseUrl + endpoint);
|
||||
for (const key in options) {
|
||||
|
||||
@@ -264,6 +264,17 @@ export namespace Models {
|
||||
values?: Set<Project>;
|
||||
}
|
||||
|
||||
/**
|
||||
* A paginated list of workspaces.
|
||||
* @public
|
||||
*/
|
||||
export interface PaginatedWorkspaces extends Paginated<Workspace> {
|
||||
/**
|
||||
* The values of the current page.
|
||||
*/
|
||||
values?: Set<Workspace>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Object describing a user's role on resources like commits or pull requests.
|
||||
* @public
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import FormControl from '@material-ui/core/FormControl';
|
||||
import FormHelperText from '@material-ui/core/FormHelperText';
|
||||
import Input from '@material-ui/core/Input';
|
||||
import InputLabel from '@material-ui/core/InputLabel';
|
||||
import { Select, SelectItem } from '@backstage/core-components';
|
||||
import { RepoUrlPickerState } from './types';
|
||||
import { BitbucketCloudClient } from '@backstage/plugin-bitbucket-cloud-common';
|
||||
@@ -65,10 +63,8 @@ export const BitbucketRepoPicker = (props: {
|
||||
}, [allowedOwners, host, onChange]);
|
||||
|
||||
const [client, setClient] = useState<BitbucketCloudClient>();
|
||||
const [availableWorkspaces, setAvailableWorkspaces] = useState<string[]>([]);
|
||||
const [availableProjects, setAvailableProjects] = useState<string[]>([]);
|
||||
const [availableRepositories, setAvailableRepositories] = useState<string[]>(
|
||||
[],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (accessToken)
|
||||
@@ -81,39 +77,78 @@ export const BitbucketRepoPicker = (props: {
|
||||
);
|
||||
}, [accessToken]);
|
||||
|
||||
const onChangeWorkspace = async () => {
|
||||
if (client)
|
||||
if (!workspace) {
|
||||
setAvailableProjects([]);
|
||||
} else {
|
||||
try {
|
||||
for await (const page of client
|
||||
.listProjectsByWorkspace(workspace)
|
||||
.iteratePages()) {
|
||||
const keys = [...page.values!].map(p => p.key!);
|
||||
setAvailableProjects([...availableProjects, ...keys]);
|
||||
}
|
||||
} catch {
|
||||
setAvailableProjects([]);
|
||||
}
|
||||
}
|
||||
};
|
||||
// Update available workspaces when host changes
|
||||
useEffect(() => {
|
||||
const updateAvailableWorkspaces = async () => {
|
||||
if (client)
|
||||
if (!host) {
|
||||
setAvailableWorkspaces([]);
|
||||
} else {
|
||||
const result: string[] = [];
|
||||
|
||||
const onChangeProject = async () => {
|
||||
if (client && workspace)
|
||||
if (!project) {
|
||||
setAvailableRepositories([]);
|
||||
} else {
|
||||
for await (const page of client
|
||||
.listRepositoriesByWorkspace(workspace, {
|
||||
q: `project.key="${project}"`,
|
||||
})
|
||||
.iteratePages()) {
|
||||
const keys = [...page.values!].map(p => p.slug!);
|
||||
setAvailableRepositories([...availableRepositories, ...keys]);
|
||||
for await (const page of client.listWorkspaces().iteratePages()) {
|
||||
const keys = [...page.values!].map(p => p.slug!);
|
||||
result.push(...keys);
|
||||
}
|
||||
|
||||
setAvailableWorkspaces(result);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
updateAvailableWorkspaces();
|
||||
}, [client, host]);
|
||||
|
||||
// Update available repositories when workspace or project changes
|
||||
useEffect(() => {
|
||||
const updateAvailableRepositories = async () => {
|
||||
if (client && workspace)
|
||||
if (!project) {
|
||||
onChange({ availableRepos: [] });
|
||||
} else {
|
||||
const availableRepos: string[] = [];
|
||||
|
||||
for await (const page of client
|
||||
.listRepositoriesByWorkspace(workspace, {
|
||||
q: `project.key="${project}"`,
|
||||
})
|
||||
.iteratePages()) {
|
||||
const keys = [...page.values!].map(p => p.slug!);
|
||||
availableRepos.push(...keys);
|
||||
}
|
||||
|
||||
onChange({ availableRepos });
|
||||
}
|
||||
};
|
||||
|
||||
updateAvailableRepositories();
|
||||
}, [client, workspace, project, onChange]);
|
||||
|
||||
// Update available projects when workspace changes
|
||||
useEffect(() => {
|
||||
const updateAvailableProjects = async () => {
|
||||
if (client)
|
||||
if (!workspace) {
|
||||
setAvailableProjects([]);
|
||||
} else {
|
||||
try {
|
||||
const result: string[] = [];
|
||||
|
||||
for await (const page of client
|
||||
.listProjectsByWorkspace(workspace)
|
||||
.iteratePages()) {
|
||||
const keys = [...page.values!].map(p => p.key!);
|
||||
result.push(...keys);
|
||||
}
|
||||
|
||||
setAvailableProjects(result);
|
||||
} catch {
|
||||
setAvailableProjects([]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
updateAvailableProjects();
|
||||
}, [client, workspace]);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -135,15 +170,18 @@ export const BitbucketRepoPicker = (props: {
|
||||
items={ownerItems}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<InputLabel htmlFor="workspaceInput">Workspace</InputLabel>
|
||||
<Input
|
||||
id="workspaceInput"
|
||||
onChange={e => onChange({ workspace: e.target.value })}
|
||||
value={workspace}
|
||||
onBlur={() => onChangeWorkspace()}
|
||||
/>
|
||||
</>
|
||||
<Autocomplete
|
||||
value={workspace}
|
||||
onChange={(_, newValue) => {
|
||||
onChange({ workspace: newValue || '' });
|
||||
}}
|
||||
options={availableWorkspaces}
|
||||
renderInput={params => (
|
||||
<TextField {...params} label="Workspace" required />
|
||||
)}
|
||||
freeSolo
|
||||
autoSelect
|
||||
/>
|
||||
)}
|
||||
<FormHelperText>
|
||||
The Workspace that this repo will belong to
|
||||
@@ -173,9 +211,10 @@ export const BitbucketRepoPicker = (props: {
|
||||
onChange({ project: newValue || '' });
|
||||
}}
|
||||
options={availableProjects}
|
||||
renderInput={params => <TextField {...params} label="Project" />}
|
||||
renderInput={params => (
|
||||
<TextField {...params} label="Project" required />
|
||||
)}
|
||||
freeSolo
|
||||
onBlur={() => onChangeProject()}
|
||||
autoSelect
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -232,6 +232,7 @@ export const RepoUrlPicker = (props: RepoUrlPickerProps) => {
|
||||
setState(prevState => ({ ...prevState, repoName: repo }))
|
||||
}
|
||||
rawErrors={rawErrors}
|
||||
availableRepos={state.availableRepos}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -17,16 +17,17 @@ import React, { useEffect } from 'react';
|
||||
import { Select, SelectItem } from '@backstage/core-components';
|
||||
import FormControl from '@material-ui/core/FormControl';
|
||||
import FormHelperText from '@material-ui/core/FormHelperText';
|
||||
import Input from '@material-ui/core/Input';
|
||||
import InputLabel from '@material-ui/core/InputLabel';
|
||||
import Autocomplete from '@material-ui/lab/Autocomplete';
|
||||
import TextField from '@material-ui/core/TextField';
|
||||
|
||||
export const RepoUrlPickerRepoName = (props: {
|
||||
repoName?: string;
|
||||
allowedRepos?: string[];
|
||||
onChange: (host: string) => void;
|
||||
rawErrors: string[];
|
||||
availableRepos?: string[];
|
||||
}) => {
|
||||
const { repoName, allowedRepos, onChange, rawErrors } = props;
|
||||
const { repoName, allowedRepos, onChange, rawErrors, availableRepos } = props;
|
||||
|
||||
useEffect(() => {
|
||||
// If there is no repoName chosen currently
|
||||
@@ -61,14 +62,17 @@ export const RepoUrlPickerRepoName = (props: {
|
||||
items={repoItems}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<InputLabel htmlFor="repoNameInput">Repository</InputLabel>
|
||||
<Input
|
||||
id="repoNameInput"
|
||||
onChange={e => onChange(String(e.target.value))}
|
||||
value={repoName}
|
||||
/>
|
||||
</>
|
||||
<Autocomplete
|
||||
value={repoName}
|
||||
onChange={(_, newValue) => {
|
||||
onChange(newValue || '');
|
||||
}}
|
||||
options={availableRepos || []}
|
||||
renderInput={params => (
|
||||
<TextField {...params} label="Repository" required />
|
||||
)}
|
||||
freeSolo
|
||||
/>
|
||||
)}
|
||||
<FormHelperText>The name of the repository</FormHelperText>
|
||||
</FormControl>
|
||||
|
||||
@@ -20,4 +20,5 @@ export interface RepoUrlPickerState {
|
||||
organization?: string;
|
||||
workspace?: string;
|
||||
project?: string;
|
||||
availableRepos?: string[];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user