Replace root component props with input fields
Signed-off-by: Erik Engervall <erik.engervall@gmail.com>
This commit is contained in:
@@ -15,10 +15,11 @@
|
||||
*/
|
||||
|
||||
import { Alert } from '@material-ui/lab';
|
||||
import { CircularProgress, makeStyles } from '@material-ui/core';
|
||||
import { makeStyles } from '@material-ui/core';
|
||||
import { useAsync } from 'react-use';
|
||||
import React, { useState } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useApi, ContentHeader, ErrorBoundary } from '@backstage/core';
|
||||
import { useForm } from 'react-hook-form';
|
||||
|
||||
import { CreateRc } from './cards/createRc/CreateRc';
|
||||
import { getGitHubBatchInfo } from './sideEffects/getGitHubBatchInfo';
|
||||
@@ -35,11 +36,11 @@ import {
|
||||
PluginApiClientContext,
|
||||
usePluginApiClientContext,
|
||||
} from './contexts/PluginApiClientContext';
|
||||
import {
|
||||
ProjectContext,
|
||||
useProjectContext,
|
||||
Project,
|
||||
} from './contexts/ProjectContext';
|
||||
import { ProjectContext, Project } from './contexts/ProjectContext';
|
||||
import { isProjectValid } from './cards/projectForm/isProjectValid';
|
||||
import { InfoCardPlus } from './components/InfoCardPlus';
|
||||
import { RepoDetailsForm } from './cards/projectForm/RepoDetailsForm';
|
||||
import { CenteredCircularProgress } from './components/CenteredCircularProgress';
|
||||
|
||||
interface GitHubReleaseManagerProps {
|
||||
components?: {
|
||||
@@ -62,30 +63,54 @@ export function GitHubReleaseManager({
|
||||
}: GitHubReleaseManagerProps) {
|
||||
const pluginApiClient = useApi(githubReleaseManagerApiRef);
|
||||
const classes = useStyles();
|
||||
const usernameResponse = useAsync(() => pluginApiClient.getUsername());
|
||||
const { control, watch } = useForm();
|
||||
const project: Project = watch('repo-details-form');
|
||||
|
||||
const project: Project = {
|
||||
owner: 'erikengervall',
|
||||
repo: 'playground',
|
||||
versioningStrategy: 'semver',
|
||||
};
|
||||
if (usernameResponse.error) {
|
||||
return <Alert severity="error">{usernameResponse.error.message}</Alert>;
|
||||
}
|
||||
|
||||
if (usernameResponse.loading) {
|
||||
return <CenteredCircularProgress />;
|
||||
}
|
||||
|
||||
if (!usernameResponse.value?.username) {
|
||||
return <Alert severity="error">Unable to retrieve username</Alert>;
|
||||
}
|
||||
|
||||
return (
|
||||
<ProjectContext.Provider value={project}>
|
||||
{/* @ts-ignore-error TODO: Update interface for PluginApiClient */}
|
||||
<PluginApiClientContext.Provider value={pluginApiClient}>
|
||||
<div className={classes.root}>
|
||||
<ContentHeader title="GitHub Release Manager" />
|
||||
<PluginApiClientContext.Provider
|
||||
value={
|
||||
pluginApiClient as any // TODO: Fix type errors
|
||||
}
|
||||
>
|
||||
<div className={classes.root}>
|
||||
<ContentHeader title="GitHub Release Manager" />
|
||||
|
||||
<Cards components={components} />
|
||||
</div>
|
||||
</PluginApiClientContext.Provider>
|
||||
</ProjectContext.Provider>
|
||||
<InfoCardPlus>
|
||||
<RepoDetailsForm
|
||||
control={control}
|
||||
username={usernameResponse.value.username}
|
||||
/>
|
||||
</InfoCardPlus>
|
||||
|
||||
{isProjectValid(project) && (
|
||||
<Cards components={components} project={project} />
|
||||
)}
|
||||
</div>
|
||||
</PluginApiClientContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
function Cards({ components }: GitHubReleaseManagerProps) {
|
||||
function Cards({
|
||||
components,
|
||||
project,
|
||||
}: {
|
||||
components: GitHubReleaseManagerProps['components'];
|
||||
project: Project;
|
||||
}) {
|
||||
const pluginApiClient = usePluginApiClientContext();
|
||||
const project = useProjectContext();
|
||||
const [refetch, setRefetch] = useState(0);
|
||||
const gitHubBatchInfo = useAsync(
|
||||
getGitHubBatchInfo({ project, pluginApiClient }),
|
||||
@@ -97,11 +122,7 @@ function Cards({ components }: GitHubReleaseManagerProps) {
|
||||
}
|
||||
|
||||
if (gitHubBatchInfo.loading) {
|
||||
return (
|
||||
<div style={{ display: 'flex', justifyContent: 'center' }}>
|
||||
<CircularProgress />
|
||||
</div>
|
||||
);
|
||||
return <CenteredCircularProgress />;
|
||||
}
|
||||
|
||||
if (gitHubBatchInfo.value === undefined) {
|
||||
@@ -120,38 +141,40 @@ function Cards({ components }: GitHubReleaseManagerProps) {
|
||||
}
|
||||
|
||||
return (
|
||||
<ErrorBoundary>
|
||||
<Info
|
||||
latestRelease={gitHubBatchInfo.value.latestRelease}
|
||||
releaseBranch={gitHubBatchInfo.value.releaseBranch}
|
||||
/>
|
||||
|
||||
{components?.default?.createRc?.omit !== true && (
|
||||
<CreateRc
|
||||
<ProjectContext.Provider value={project}>
|
||||
<ErrorBoundary>
|
||||
<Info
|
||||
latestRelease={gitHubBatchInfo.value.latestRelease}
|
||||
releaseBranch={gitHubBatchInfo.value.releaseBranch}
|
||||
defaultBranch={gitHubBatchInfo.value.repository.defaultBranch}
|
||||
setRefetch={setRefetch}
|
||||
successCb={components?.default?.createRc?.successCb}
|
||||
/>
|
||||
)}
|
||||
|
||||
{components?.default?.promoteRc?.omit !== true && (
|
||||
<PromoteRc
|
||||
latestRelease={gitHubBatchInfo.value.latestRelease}
|
||||
setRefetch={setRefetch}
|
||||
successCb={components?.default?.promoteRc?.successCb}
|
||||
/>
|
||||
)}
|
||||
{components?.default?.createRc?.omit !== true && (
|
||||
<CreateRc
|
||||
latestRelease={gitHubBatchInfo.value.latestRelease}
|
||||
releaseBranch={gitHubBatchInfo.value.releaseBranch}
|
||||
defaultBranch={gitHubBatchInfo.value.repository.defaultBranch}
|
||||
setRefetch={setRefetch}
|
||||
successCb={components?.default?.createRc?.successCb}
|
||||
/>
|
||||
)}
|
||||
|
||||
{components?.default?.patch?.omit !== true && (
|
||||
<Patch
|
||||
latestRelease={gitHubBatchInfo.value.latestRelease}
|
||||
releaseBranch={gitHubBatchInfo.value.releaseBranch}
|
||||
setRefetch={setRefetch}
|
||||
successCb={components?.default?.patch?.successCb}
|
||||
/>
|
||||
)}
|
||||
</ErrorBoundary>
|
||||
{components?.default?.promoteRc?.omit !== true && (
|
||||
<PromoteRc
|
||||
latestRelease={gitHubBatchInfo.value.latestRelease}
|
||||
setRefetch={setRefetch}
|
||||
successCb={components?.default?.promoteRc?.successCb}
|
||||
/>
|
||||
)}
|
||||
|
||||
{components?.default?.patch?.omit !== true && (
|
||||
<Patch
|
||||
latestRelease={gitHubBatchInfo.value.latestRelease}
|
||||
releaseBranch={gitHubBatchInfo.value.releaseBranch}
|
||||
setRefetch={setRefetch}
|
||||
successCb={components?.default?.patch?.successCb}
|
||||
/>
|
||||
)}
|
||||
</ErrorBoundary>
|
||||
</ProjectContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -157,10 +157,13 @@ export interface IPluginApiClient {
|
||||
} & PartialProject,
|
||||
) => Promise<Todo>;
|
||||
};
|
||||
|
||||
getOrganizations: (args: { ownerIsUser: boolean }) => Promise<Todo>;
|
||||
getUsername: () => Promise<{ username: string }>;
|
||||
getRepositories: (args: { owner: string; username: string }) => Promise<Todo>;
|
||||
}
|
||||
|
||||
export class PluginApiClient implements IPluginApiClient {
|
||||
// private readonly getAccessToken: any;
|
||||
private readonly githubAuthApi: OAuthApi;
|
||||
private readonly baseUrl: string;
|
||||
readonly host: string;
|
||||
@@ -174,8 +177,6 @@ export class PluginApiClient implements IPluginApiClient {
|
||||
}) {
|
||||
this.githubAuthApi = githubAuthApi;
|
||||
|
||||
// this.getAccessToken = () => this.githubAuthApi.getAccessToken();
|
||||
|
||||
const githubIntegrationConfig = this.getGithubIntegrationConfig({
|
||||
configApi,
|
||||
});
|
||||
@@ -190,11 +191,13 @@ export class PluginApiClient implements IPluginApiClient {
|
||||
configApi.getOptionalConfigArray('integrations.github') ?? [],
|
||||
);
|
||||
|
||||
const githubIntegrationConfig = configs.find(
|
||||
v => v.host === 'github.com' || v.host.startsWith('ghe.'),
|
||||
const githubIntegrationEnterpriseConfig = configs.find(v =>
|
||||
v.host.startsWith('ghe.'),
|
||||
);
|
||||
const githubIntegrationConfig = configs.find(v => v.host === 'github.com');
|
||||
|
||||
return githubIntegrationConfig;
|
||||
// Prioritize enterprise configs if available
|
||||
return githubIntegrationEnterpriseConfig ?? githubIntegrationConfig;
|
||||
}
|
||||
|
||||
private async getOctokit() {
|
||||
@@ -216,6 +219,40 @@ export class PluginApiClient implements IPluginApiClient {
|
||||
return `${owner}/${repo}`;
|
||||
}
|
||||
|
||||
async getOrganizations() {
|
||||
const { octokit } = await this.getOctokit();
|
||||
const { data: orgs } = await octokit.orgs.listForAuthenticatedUser();
|
||||
|
||||
return { orgs };
|
||||
}
|
||||
|
||||
async getRepositories({
|
||||
owner,
|
||||
username,
|
||||
}: {
|
||||
owner: string;
|
||||
username: string;
|
||||
}) {
|
||||
const { octokit } = await this.getOctokit();
|
||||
|
||||
if (owner === username) {
|
||||
const { data: repos } = await octokit.repos.listForUser({ username });
|
||||
|
||||
return { repos };
|
||||
}
|
||||
|
||||
const { data: repos } = await octokit.repos.listForOrg({ org: owner });
|
||||
|
||||
return { repos };
|
||||
}
|
||||
|
||||
async getUsername() {
|
||||
const { octokit } = await this.getOctokit();
|
||||
const { data: user } = await octokit.users.getAuthenticated();
|
||||
|
||||
return { username: user.login };
|
||||
}
|
||||
|
||||
async getRecentCommits({
|
||||
owner,
|
||||
repo,
|
||||
|
||||
@@ -20,7 +20,6 @@ import { useAsync, useAsyncFn } from 'react-use';
|
||||
import {
|
||||
Button,
|
||||
Checkbox,
|
||||
CircularProgress,
|
||||
IconButton,
|
||||
Link,
|
||||
List,
|
||||
@@ -50,6 +49,7 @@ import { useStyles } from '../../styles/styles';
|
||||
import { TEST_IDS } from '../../test-helpers/test-ids';
|
||||
import { patch } from './sideEffects/patch';
|
||||
import { useProjectContext } from '../../contexts/ProjectContext';
|
||||
import { CenteredCircularProgress } from '../../components/CenteredCircularProgress';
|
||||
|
||||
interface PatchBodyProps {
|
||||
bumpedTag: string;
|
||||
@@ -124,7 +124,7 @@ export const PatchBody = ({
|
||||
return <Alert severity="error">{patchReleaseResponse.error.message}</Alert>;
|
||||
}
|
||||
if (githubDataResponse.loading) {
|
||||
return <CircularProgress data-testid={TEST_IDS.patch.loading} />;
|
||||
return <CenteredCircularProgress data-testid={TEST_IDS.patch.loading} />;
|
||||
}
|
||||
|
||||
function Description() {
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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 from 'react';
|
||||
import { useAsync } from 'react-use';
|
||||
import { ControllerRenderProps } from 'react-hook-form';
|
||||
import { Alert } from '@material-ui/lab';
|
||||
import { FormControl, InputLabel, MenuItem, Select } from '@material-ui/core';
|
||||
|
||||
import { usePluginApiClientContext } from '../../contexts/PluginApiClientContext';
|
||||
import { useFormClasses } from './styles';
|
||||
import { CenteredCircularProgress } from '../../components/CenteredCircularProgress';
|
||||
import { Project } from '../../contexts/ProjectContext';
|
||||
|
||||
export function Owner({
|
||||
controllerRenderProps,
|
||||
username,
|
||||
}: {
|
||||
controllerRenderProps: ControllerRenderProps;
|
||||
username: string;
|
||||
}) {
|
||||
const pluginApiClient = usePluginApiClientContext();
|
||||
const formClasses = useFormClasses();
|
||||
const project: Project = controllerRenderProps.value;
|
||||
|
||||
const { loading, error, value } = useAsync(() =>
|
||||
pluginApiClient.getOrganizations(),
|
||||
);
|
||||
|
||||
if (error) {
|
||||
return <Alert severity="error">{error.message}</Alert>;
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
return <CenteredCircularProgress />;
|
||||
}
|
||||
|
||||
if (!value?.orgs) {
|
||||
return <Alert severity="error">Could not fetch organizations</Alert>;
|
||||
}
|
||||
|
||||
return (
|
||||
<FormControl className={formClasses.formControl}>
|
||||
<InputLabel id="owner-select-label">Organizations</InputLabel>
|
||||
<Select
|
||||
labelId="owner-select-label"
|
||||
id="owner-select"
|
||||
value={project.owner}
|
||||
onChange={event => {
|
||||
controllerRenderProps.onChange({
|
||||
...project,
|
||||
owner: event.target.value,
|
||||
repo: '',
|
||||
} as Project);
|
||||
}}
|
||||
className={formClasses.selectEmpty}
|
||||
>
|
||||
<MenuItem value="">
|
||||
<em>None</em>
|
||||
</MenuItem>
|
||||
<MenuItem value={username}>
|
||||
<strong>{username}</strong>
|
||||
</MenuItem>
|
||||
{value.orgs.map((org, index) => (
|
||||
<MenuItem key={`organization-${index}`} value={org.login}>
|
||||
{org.login}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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 from 'react';
|
||||
import { useAsync } from 'react-use';
|
||||
import { FormControl, InputLabel, Select, MenuItem } from '@material-ui/core';
|
||||
import { Alert } from '@material-ui/lab';
|
||||
import { ControllerRenderProps, useForm } from 'react-hook-form';
|
||||
|
||||
import { usePluginApiClientContext } from '../../contexts/PluginApiClientContext';
|
||||
import { useFormClasses } from './styles';
|
||||
import { CenteredCircularProgress } from '../../components/CenteredCircularProgress';
|
||||
import { Project } from '../../contexts/ProjectContext';
|
||||
|
||||
export function Repo({
|
||||
username,
|
||||
controllerRenderProps,
|
||||
}: {
|
||||
username: string;
|
||||
controllerRenderProps: ControllerRenderProps;
|
||||
}) {
|
||||
const pluginApiClient = usePluginApiClientContext();
|
||||
const formClasses = useFormClasses();
|
||||
const project: Project = controllerRenderProps.value;
|
||||
|
||||
const { loading, error, value } = useAsync(
|
||||
() =>
|
||||
pluginApiClient.getRepositories({
|
||||
owner: project.owner,
|
||||
username,
|
||||
}),
|
||||
[project.owner],
|
||||
);
|
||||
|
||||
if (error) {
|
||||
return <Alert severity="error">{error.message}</Alert>;
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
return <CenteredCircularProgress />;
|
||||
}
|
||||
|
||||
if (!value?.repos) {
|
||||
return (
|
||||
<Alert severity="error">
|
||||
Could not fetch repositories for "{project.owner}"
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<FormControl className={formClasses.formControl}>
|
||||
<InputLabel id="repo-select-label">Repositories</InputLabel>
|
||||
<Select
|
||||
labelId="repo-select-label"
|
||||
id="repo-select"
|
||||
value={project.repo}
|
||||
onChange={event => {
|
||||
controllerRenderProps.onChange({
|
||||
...project,
|
||||
repo: event.target.value,
|
||||
} as Project);
|
||||
}}
|
||||
className={formClasses.selectEmpty}
|
||||
>
|
||||
<MenuItem value="">
|
||||
<em>None</em>
|
||||
</MenuItem>
|
||||
{value.repos.map((repository, index) => (
|
||||
<MenuItem key={`repository-${index}`} value={repository.name}>
|
||||
{repository.name}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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 from 'react';
|
||||
import { Controller, useForm } from 'react-hook-form';
|
||||
import { Project } from '../../contexts/ProjectContext';
|
||||
|
||||
import { VersioningStrategy } from './VersioningStrategy';
|
||||
import { Owner } from './Owner';
|
||||
import { Repo } from './Repo';
|
||||
|
||||
export function RepoDetailsForm({
|
||||
control,
|
||||
username,
|
||||
}: {
|
||||
control: ReturnType<typeof useForm>['control'];
|
||||
username: string;
|
||||
}) {
|
||||
return (
|
||||
<Controller
|
||||
render={controllerRenderProps => {
|
||||
const project: Project = controllerRenderProps.value;
|
||||
|
||||
return (
|
||||
<>
|
||||
<VersioningStrategy controllerRenderProps={controllerRenderProps} />
|
||||
|
||||
<Owner
|
||||
controllerRenderProps={controllerRenderProps}
|
||||
username={username}
|
||||
/>
|
||||
|
||||
{project.owner.length > 0 && (
|
||||
<Repo
|
||||
controllerRenderProps={controllerRenderProps}
|
||||
username={username}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}}
|
||||
control={control}
|
||||
name="repo-details-form"
|
||||
defaultValue={
|
||||
{
|
||||
owner: '',
|
||||
repo: '',
|
||||
versioningStrategy: 'semver',
|
||||
} as Project
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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 {
|
||||
FormControl,
|
||||
FormControlLabel,
|
||||
FormLabel,
|
||||
Radio,
|
||||
RadioGroup,
|
||||
} from '@material-ui/core';
|
||||
import React from 'react';
|
||||
import { ControllerRenderProps } from 'react-hook-form';
|
||||
import { Project } from '../../contexts/ProjectContext';
|
||||
|
||||
export function VersioningStrategy({
|
||||
controllerRenderProps,
|
||||
}: {
|
||||
controllerRenderProps: ControllerRenderProps;
|
||||
}) {
|
||||
const project: Project = controllerRenderProps.value;
|
||||
|
||||
return (
|
||||
<FormControl component="fieldset">
|
||||
<FormLabel component="legend">Calendar strategy</FormLabel>
|
||||
<RadioGroup
|
||||
aria-label="calendar-strategy"
|
||||
name="calendar-strategy"
|
||||
value={project.versioningStrategy}
|
||||
onChange={event => {
|
||||
controllerRenderProps.onChange({
|
||||
...project,
|
||||
versioningStrategy: event.target.value,
|
||||
} as Project);
|
||||
}}
|
||||
>
|
||||
<FormControlLabel
|
||||
value="semver"
|
||||
control={<Radio />}
|
||||
label="Semantic versioning"
|
||||
/>
|
||||
<FormControlLabel
|
||||
value="calver"
|
||||
control={<Radio />}
|
||||
label="Calendar versioning"
|
||||
/>
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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 { Project } from '../../contexts/ProjectContext';
|
||||
|
||||
export function isProjectValid(project: any): project is Project {
|
||||
return (
|
||||
project?.owner?.length > 0 &&
|
||||
project?.repo?.length > 0 &&
|
||||
project?.versioningStrategy?.length > 0
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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 { createStyles, makeStyles, Theme } from '@material-ui/core';
|
||||
|
||||
export const useFormClasses = makeStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
formControl: {
|
||||
margin: theme.spacing(1),
|
||||
minWidth: 120,
|
||||
},
|
||||
selectEmpty: {
|
||||
marginTop: theme.spacing(2),
|
||||
},
|
||||
}),
|
||||
);
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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 from 'react';
|
||||
import { CircularProgress } from '@material-ui/core';
|
||||
|
||||
export const CenteredCircularProgress = () => {
|
||||
return (
|
||||
<div style={{ display: 'flex', justifyContent: 'center' }}>
|
||||
<CircularProgress />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -17,7 +17,6 @@
|
||||
import React, { PropsWithChildren } from 'react';
|
||||
import {
|
||||
List,
|
||||
CircularProgress,
|
||||
Button,
|
||||
Dialog,
|
||||
DialogActions,
|
||||
@@ -28,6 +27,7 @@ import {
|
||||
import { ResponseStep, SetRefetch } from '../../types/types';
|
||||
import { TEST_IDS } from '../../test-helpers/test-ids';
|
||||
import { ResponseStepListItem } from './ResponseStepListItem';
|
||||
import { CenteredCircularProgress } from '../CenteredCircularProgress';
|
||||
|
||||
interface ResponseStepListProps {
|
||||
responseSteps?: ResponseStep[];
|
||||
@@ -68,7 +68,7 @@ export const ResponseStepList = ({
|
||||
|
||||
{loading || !responseSteps ? (
|
||||
<div style={{ margin: 10, textAlign: 'center' }}>
|
||||
<CircularProgress
|
||||
<CenteredCircularProgress
|
||||
data-testid={TEST_IDS.components.circularProgress}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -22,10 +22,10 @@ export type CalverTagParts = {
|
||||
patch: number;
|
||||
};
|
||||
|
||||
export const calverRegexp = /(rc|version)-([0-9]{4}\.[0-9]{2}\.[0-9]{2})_([0-9]+)/;
|
||||
|
||||
export function getCalverTagParts(tag: string) {
|
||||
const result = tag.match(
|
||||
/(rc|version)-([0-9]{4}\.[0-9]{2}\.[0-9]{2})_([0-9]+)/,
|
||||
);
|
||||
const result = tag.match(calverRegexp);
|
||||
|
||||
if (result === null || result.length < 4) {
|
||||
throw new GitHubReleaseManagerError('Invalid calver tag');
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
import { GitHubReleaseManagerError } from '../../errors/GitHubReleaseManagerError';
|
||||
import { calverRegexp } from './getCalverTagParts';
|
||||
|
||||
export type SemverTagParts = {
|
||||
prefix: string;
|
||||
@@ -30,6 +31,10 @@ export function getSemverTagParts(tag: string) {
|
||||
throw new GitHubReleaseManagerError('Invalid semver tag');
|
||||
}
|
||||
|
||||
if (tag.match(calverRegexp)) {
|
||||
throw new GitHubReleaseManagerError('Invalid semver tag, found calver');
|
||||
}
|
||||
|
||||
const tagParts: SemverTagParts = {
|
||||
prefix: result[1],
|
||||
major: parseInt(result[2], 10),
|
||||
|
||||
@@ -109,5 +109,13 @@ describe('getTagParts', () => {
|
||||
getTagParts({ project: mockSemverProject, tag: 'rc-1.2' }),
|
||||
).toThrowErrorMatchingInlineSnapshot(`"Invalid semver tag"`);
|
||||
});
|
||||
|
||||
it('should throw for invalid semver (founds calver)', () => {
|
||||
expect(() =>
|
||||
getTagParts({ project: mockSemverProject, tag: 'rc-1337.01.01_1' }),
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`"Invalid semver tag, found calver"`,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user