feat: support codeowners in catalog-import

This commit is contained in:
Andrew Thauer
2021-02-20 13:48:57 -05:00
parent cea4609677
commit 968b588f79
6 changed files with 157 additions and 67 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog-import': patch
---
Allows the CodeOwnersProcessor to set the owner automatically within the catalog-import plugin. This adds an additional checkbox that overrides the group selector and will omit the owner option in the generated catalog file yaml.
@@ -22,6 +22,7 @@ import { StepFinishImportLocation } from '../StepFinishImportLocation';
import { StepInitAnalyzeUrl } from '../StepInitAnalyzeUrl';
import {
AutocompleteTextField,
CheckboxField,
StepPrepareCreatePullRequest,
} from '../StepPrepareCreatePullRequest';
import { StepPrepareSelectLocations } from '../StepPrepareSelectLocations';
@@ -169,74 +170,97 @@ export function defaultGenerateStepper(
renderFormFields={({
control,
errors,
watch,
groupsLoading,
groups,
register,
}) => (
<>
<Box marginTop={2}>
<Typography variant="h6">Pull Request Details</Typography>
</Box>
}) => {
const watchUseCodeowners = watch('useCodeowners', false);
<TextField
name="title"
label="Pull Request Title"
placeholder="Add Backstage catalog entity descriptor files"
margin="normal"
variant="outlined"
fullWidth
inputRef={register({ required: true })}
error={Boolean(errors.title)}
required
/>
return (
<>
<Box marginTop={2}>
<Typography variant="h6">
Pull Request Details
</Typography>
</Box>
<TextField
name="body"
label="Pull Request Body"
placeholder="A describing text with Markdown support"
margin="normal"
variant="outlined"
fullWidth
inputRef={register({ required: true })}
error={Boolean(errors.body)}
multiline
required
/>
<TextField
name="title"
label="Pull Request Title"
placeholder="Add Backstage catalog entity descriptor files"
margin="normal"
variant="outlined"
fullWidth
inputRef={register({ required: true })}
error={Boolean(errors.title)}
required
/>
<Box marginTop={2}>
<Typography variant="h6">Entity Configuration</Typography>
</Box>
<TextField
name="body"
label="Pull Request Body"
placeholder="A describing text with Markdown support"
margin="normal"
variant="outlined"
fullWidth
inputRef={register({ required: true })}
error={Boolean(errors.body)}
multiline
required
/>
<TextField
name="componentName"
label="Name of the created component"
placeholder="my-component"
margin="normal"
variant="outlined"
fullWidth
inputRef={register({ required: true })}
error={Boolean(errors.componentName)}
required
/>
<Box marginTop={2}>
<Typography variant="h6">
Entity Configuration
</Typography>
</Box>
<AutocompleteTextField
name="owner"
control={control}
errors={errors}
options={groups || []}
loading={groupsLoading}
loadingText="Loading groups…"
helperText="Select an owner from the list or enter a reference to a Group or a User"
errorHelperText="required value"
textFieldProps={{
label: 'Entity Owner',
placeholder: 'my-group',
}}
rules={{ required: true }}
required
/>
</>
)}
<TextField
name="componentName"
label="Name of the created component"
placeholder="my-component"
margin="normal"
variant="outlined"
fullWidth
inputRef={register({ required: true })}
error={Boolean(errors.componentName)}
required
/>
{!watchUseCodeowners && (
<AutocompleteTextField
name="owner"
control={control}
errors={errors}
options={groups || []}
loading={groupsLoading}
loadingText="Loading groups…"
helperText="Select an owner from the list or enter a reference to a Group or a User"
errorHelperText="required value"
textFieldProps={{
label: 'Entity Owner',
placeholder: 'my-group',
}}
rules={{ required: true }}
required
/>
)}
<CheckboxField
name="useCodeowners"
inputRef={register}
label="Use CODEOWNERS File"
helperText="WARNING: This may fail is no CODEOWNERS file is found at the target location."
onChange={(_, value) => {
if (value) {
control.setValue('owner', '');
}
}}
/>
</>
);
}}
/>
),
};
@@ -0,0 +1,53 @@
/*
* Copyright 2021 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Checkbox, FormControlLabel, FormHelperText } from '@material-ui/core';
import React from 'react';
type Props<TFieldValue extends string> = {
name: TFieldValue;
label: React.ReactNode;
inputRef:
| ((instance: HTMLInputElement | null) => void)
| React.RefObject<HTMLInputElement>
| null
| undefined;
onChange?: (
event: React.ChangeEvent<HTMLInputElement>,
checked: boolean,
) => void;
helperText?: React.ReactNode | string;
};
export const CheckboxField = <TFieldValue extends string>({
name,
label,
inputRef,
onChange,
helperText,
}: Props<TFieldValue>) => {
return (
<>
<FormControlLabel
control={
<Checkbox name={name} inputRef={inputRef} onChange={onChange} />
}
label={label}
/>
{helperText && <FormHelperText>{helperText}</FormHelperText>}
</>
);
};
@@ -32,7 +32,7 @@ type Props<TFieldValues extends Record<string, any>> = Pick<
render: (
props: Pick<
UseFormMethods<TFieldValues>,
'errors' | 'register' | 'control'
'errors' | 'register' | 'control' | 'formState' | 'watch'
> & {
values: UnpackNestedValue<TFieldValues>;
},
@@ -55,13 +55,13 @@ export const PreparePullRequestForm = <
onSubmit,
render,
}: Props<TFieldValues>) => {
const { handleSubmit, watch, control, register, errors } = useForm<
const { handleSubmit, control, register, errors, formState, watch } = useForm<
TFieldValues
>({ mode: 'onTouched', defaultValues });
return (
<form onSubmit={handleSubmit(onSubmit)}>
{render({ values: watch(), errors, register, control })}
{render({ values: watch(), errors, register, control, formState, watch })}
</form>
);
};
@@ -48,6 +48,7 @@ type FormData = {
body: string;
componentName: string;
owner: string;
useCodeowners: boolean;
};
type Props = {
@@ -62,7 +63,10 @@ type Props = {
defaultBody: string;
renderFormFields: (
props: Pick<UseFormMethods<FormData>, 'errors' | 'register' | 'control'> & {
props: Pick<
UseFormMethods<FormData>,
'errors' | 'register' | 'control' | 'formState' | 'watch'
> & {
groups: string[];
groupsLoading: boolean;
},
@@ -72,7 +76,7 @@ type Props = {
export function generateEntities(
entities: PartialEntity[],
componentName: string,
owner: string,
owner?: string,
): Entity[] {
return entities.map(e => ({
...e,
@@ -84,7 +88,7 @@ export function generateEntities(
},
spec: {
...e.spec,
owner,
...(owner ? { owner } : {}),
},
}));
}
@@ -189,13 +193,16 @@ export const StepPrepareCreatePullRequest = ({
(analyzeResult.generatedEntities[0]?.spec?.owner as string) || '',
componentName:
analyzeResult.generatedEntities[0]?.metadata?.name || '',
useCodeowners: false,
}}
render={({ values, errors, control, register }) => (
render={({ values, errors, control, register, formState, watch }) => (
<>
{renderFormFields({
errors,
register,
control,
formState,
watch,
groups: groups ?? [],
groupsLoading,
})}
@@ -15,6 +15,7 @@
*/
export { AutocompleteTextField } from './AutocompleteTextField';
export { CheckboxField } from './CheckboxField';
export { PreparePullRequestForm } from './PreparePullRequestForm';
export { PreviewCatalogInfoComponent } from './PreviewCatalogInfoComponent';
export { PreviewPullRequestComponent } from './PreviewPullRequestComponent';