feat: added some nice validation and functionality to the repo picker
Co-authored-by: Patrik Oldsberg <poldsberg@gmail.com> Co-authored-by: Johan Haals <johan.haals@gmail.com> Signed-off-by: Johan Haals <johan.haals@gmail.com>
This commit is contained in:
@@ -82,25 +82,29 @@ spec:
|
||||
type: service
|
||||
|
||||
parameters:
|
||||
title: Fill in some BS params
|
||||
required:
|
||||
- name
|
||||
properties:
|
||||
name:
|
||||
title: Name
|
||||
type: string
|
||||
description: Unique name of the component
|
||||
ui:autofocus: true
|
||||
ui:options:
|
||||
rows: 5
|
||||
repoUrl:
|
||||
title: Repository Location
|
||||
type: string
|
||||
description: Copy https://github.com/aeothgiaetgh/aetkijhahte and hammer in some more letters
|
||||
ui:field: RepoUrlPicker
|
||||
ui:options:
|
||||
allowedHosts:
|
||||
- github.com
|
||||
- title: Fill in some parameters for the template
|
||||
required:
|
||||
- name
|
||||
properties:
|
||||
name:
|
||||
title: Name
|
||||
type: string
|
||||
description: Unique name of the component
|
||||
ui:autofocus: true
|
||||
ui:options:
|
||||
rows: 5
|
||||
- title: Choose destination
|
||||
required:
|
||||
- repoUrl
|
||||
properties:
|
||||
repoUrl:
|
||||
title: Repository Location
|
||||
type: string
|
||||
ui:field: RepoUrlPicker
|
||||
ui:options:
|
||||
allowedHosts:
|
||||
- github.com
|
||||
|
||||
|
||||
steps:
|
||||
- id: fetch-base
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* 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 React, { useState, useCallback, useEffect } from 'react';
|
||||
import { Field } from '@rjsf/core';
|
||||
import { useApi, Progress } from '@backstage/core';
|
||||
import { scaffolderApiRef } from '../../../api';
|
||||
import { useAsync } from 'react-use';
|
||||
import Select from '@material-ui/core/Select';
|
||||
import InputLabel from '@material-ui/core/InputLabel';
|
||||
import Input from '@material-ui/core/Input';
|
||||
import FormControl from '@material-ui/core/FormControl';
|
||||
import FormHelperText from '@material-ui/core/FormHelperText';
|
||||
|
||||
function splitFormData(url: string | undefined) {
|
||||
let host = undefined;
|
||||
let owner = undefined;
|
||||
let repo = undefined;
|
||||
|
||||
try {
|
||||
if (url) {
|
||||
const parsed = new URL(`https://${url}`);
|
||||
host = parsed.host;
|
||||
owner = parsed.searchParams.get('owner') || undefined;
|
||||
repo = parsed.searchParams.get('repo') || undefined;
|
||||
}
|
||||
} catch {
|
||||
/* ok */
|
||||
}
|
||||
|
||||
return { host, owner, repo };
|
||||
}
|
||||
|
||||
function serializeFormData(data: {
|
||||
host?: string;
|
||||
owner?: string;
|
||||
repo?: string;
|
||||
}) {
|
||||
if (!data.host) {
|
||||
return undefined;
|
||||
}
|
||||
const params = new URLSearchParams();
|
||||
if (data.owner) {
|
||||
params.set('owner', data.owner);
|
||||
}
|
||||
if (data.repo) {
|
||||
params.set('repo', data.repo);
|
||||
}
|
||||
|
||||
return `${data.host}?${params.toString()}`;
|
||||
}
|
||||
|
||||
export const RepoUrlPicker: Field = ({
|
||||
onChange,
|
||||
uiSchema,
|
||||
rawErrors,
|
||||
formData,
|
||||
}) => {
|
||||
const api = useApi(scaffolderApiRef);
|
||||
// const allowedHosts = uiSchema['ui:options']?.allowedHosts as string[];
|
||||
const allowedHosts = React.useMemo(
|
||||
() => ['github.com', 'gitlab.com'] as string[],
|
||||
[],
|
||||
);
|
||||
|
||||
const { value: integrations, loading } = useAsync(async () => {
|
||||
return await api.getIntegrationsList({ allowedHosts });
|
||||
});
|
||||
|
||||
const { host, owner, repo } = splitFormData(formData);
|
||||
const updateHost = useCallback(
|
||||
(evt: React.ChangeEvent<{ name?: string; value: unknown }>) =>
|
||||
onChange(
|
||||
serializeFormData({ host: evt.target.value as string, owner, repo }),
|
||||
),
|
||||
[onChange, owner, repo],
|
||||
);
|
||||
|
||||
const updateOwner = useCallback(
|
||||
(evt: React.ChangeEvent<{ name?: string; value: unknown }>) =>
|
||||
onChange(
|
||||
serializeFormData({ host, owner: evt.target.value as string, repo }),
|
||||
),
|
||||
[onChange, host, repo],
|
||||
);
|
||||
|
||||
const updateRepo = useCallback(
|
||||
(evt: React.ChangeEvent<{ name?: string; value: unknown }>) =>
|
||||
onChange(
|
||||
serializeFormData({ host, owner, repo: evt.target.value as string }),
|
||||
),
|
||||
[onChange, host, owner],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (host === undefined && integrations?.length) {
|
||||
onChange(serializeFormData({ host: integrations[0].host, owner, repo }));
|
||||
}
|
||||
}, [integrations, host]);
|
||||
|
||||
if (loading) {
|
||||
return <Progress />;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormControl
|
||||
margin="normal"
|
||||
required
|
||||
error={rawErrors?.length > 0 && !host}
|
||||
>
|
||||
<InputLabel htmlFor="hostInput">Host</InputLabel>
|
||||
<Select native id="hostInput" onChange={updateHost} value={host}>
|
||||
{integrations!
|
||||
.filter(i => allowedHosts?.includes(i.host))
|
||||
.map(({ host, title }) => (
|
||||
<option key={host} value={host}>
|
||||
{title}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
<FormHelperText>
|
||||
The host where the repository will be created
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
<FormControl
|
||||
margin="normal"
|
||||
required
|
||||
error={rawErrors?.length > 0 && !owner}
|
||||
>
|
||||
<InputLabel htmlFor="ownerInput">Owner</InputLabel>
|
||||
<Input id="ownerInput" onChange={updateOwner} value={owner} />
|
||||
<FormHelperText>
|
||||
The organization, user or project that this repo will belong to
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
<FormControl
|
||||
margin="normal"
|
||||
required
|
||||
error={rawErrors?.length > 0 && !repo}
|
||||
>
|
||||
<InputLabel htmlFor="repoInput">Repository</InputLabel>
|
||||
<Input id="repoInput" onChange={updateRepo} value={repo} />
|
||||
<FormHelperText>The name of the repository</FormHelperText>
|
||||
</FormControl>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export { RepoUrlPicker } from './RepoUrlPicker';
|
||||
@@ -1,103 +0,0 @@
|
||||
/*
|
||||
* 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 React, { useState, useCallback, useEffect } from 'react';
|
||||
import { Field } from '@rjsf/core';
|
||||
import { useApi, Progress } from '@backstage/core';
|
||||
import { scaffolderApiRef } from '../../../api';
|
||||
import { useAsync } from 'react-use';
|
||||
import Select from '@material-ui/core/Select';
|
||||
import InputLabel from '@material-ui/core/InputLabel';
|
||||
import Input from '@material-ui/core/Input';
|
||||
import FormControl from '@material-ui/core/FormControl';
|
||||
import { Typography } from '@material-ui/core';
|
||||
import { rest } from 'msw/lib/types';
|
||||
|
||||
export const RepoUrlPicker: Field = ({ onChange, uiSchema, ...rest }) => {
|
||||
const api = useApi(scaffolderApiRef);
|
||||
const allowedHosts = uiSchema['ui:options']?.allowedHosts as string[];
|
||||
|
||||
const { value: integrations, loading } = useAsync(async () => {
|
||||
return await api.getIntegrationsList({ allowedHosts });
|
||||
});
|
||||
|
||||
console.log(rest);
|
||||
|
||||
const [host, setHost] = useState('');
|
||||
const [owner, setOwner] = useState('');
|
||||
const [repo, setRepo] = useState('');
|
||||
|
||||
const updateHost = useCallback(
|
||||
(evt: React.ChangeEvent<{ name?: string; value: unknown }>) =>
|
||||
setHost(evt.target.value as string),
|
||||
[setHost],
|
||||
);
|
||||
|
||||
const updateOwner = useCallback(
|
||||
(evt: React.ChangeEvent<{ name?: string; value: unknown }>) =>
|
||||
setOwner(evt.target.value as string),
|
||||
[setOwner],
|
||||
);
|
||||
|
||||
const updateRepo = useCallback(
|
||||
(evt: React.ChangeEvent<{ name?: string; value: unknown }>) =>
|
||||
setRepo(evt.target.value as string),
|
||||
[setRepo],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (host === '' && integrations?.length) {
|
||||
setHost(integrations[0].host);
|
||||
}
|
||||
}, [integrations, host]);
|
||||
|
||||
useEffect(() => {
|
||||
const params = new URLSearchParams();
|
||||
params.set('owner', owner);
|
||||
params.set('repo', repo);
|
||||
|
||||
onChange(`${encodeURIComponent(host)}?${params.toString()}`);
|
||||
}, [host, owner, repo, onChange]);
|
||||
|
||||
if (loading) {
|
||||
return <Progress />;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Typography variant="body1">Repository Location</Typography>
|
||||
<FormControl margin="normal" required>
|
||||
<InputLabel htmlFor="hostInput">Host</InputLabel>
|
||||
<Select native id="hostInput" onChange={updateHost}>
|
||||
{integrations!
|
||||
.filter(i => allowedHosts?.includes(i.host))
|
||||
.map(({ host, title }) => (
|
||||
<option key={host} value={host}>
|
||||
{title}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
<FormControl margin="normal" required>
|
||||
<InputLabel htmlFor="ownerInput">Owner</InputLabel>
|
||||
<Input id="ownerInput" onBlur={updateOwner} />
|
||||
</FormControl>
|
||||
<FormControl margin="normal" required>
|
||||
<InputLabel htmlFor="repoInput">Repository</InputLabel>
|
||||
<Input id="repoInput" onBlur={updateRepo} />
|
||||
</FormControl>
|
||||
</>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user