@@ -32,7 +32,7 @@ import { InfoCardPlus } from './components/InfoCardPlus';
|
||||
import { isProjectValid } from './helpers/isProjectValid';
|
||||
import { PluginApiClientContext } from './contexts/PluginApiClientContext';
|
||||
import { ProjectContext, Project } from './contexts/ProjectContext';
|
||||
import { RepoDetailsForm } from './cards/projectForm/RepoDetailsForm';
|
||||
import { RepoDetailsForm } from './cards/RepoDetailsForm/RepoDetailsForm';
|
||||
import { useQueryHandler } from './hooks/useQueryHandler';
|
||||
import { useStyles } from './styles/styles';
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ import { readGitHubIntegrationConfigs } from '@backstage/integration';
|
||||
|
||||
import { CalverTagParts } from '../helpers/tagParts/getCalverTagParts';
|
||||
import { DISABLE_CACHE } from '../constants/constants';
|
||||
import { getRcGitHubInfo } from '../cards/createRc/getRcGitHubInfo';
|
||||
import { getRcGitHubInfo } from '../cards/CreateRc/helpers/getRcGitHubInfo';
|
||||
import { Project } from '../contexts/ProjectContext';
|
||||
import { SemverTagParts } from '../helpers/tagParts/getSemverTagParts';
|
||||
|
||||
|
||||
@@ -15,18 +15,17 @@
|
||||
*/
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { useAsync } from 'react-use';
|
||||
import { ErrorBoundary } from '@backstage/core';
|
||||
import { Alert } from '@material-ui/lab';
|
||||
|
||||
import { CenteredCircularProgress } from '../components/CenteredCircularProgress';
|
||||
import { CreateRc } from './createRc/CreateRc';
|
||||
import { getGitHubBatchInfo } from '../sideEffects/getGitHubBatchInfo';
|
||||
import { CreateRc } from './CreateRc/CreateRc';
|
||||
import { GitHubReleaseManagerProps } from '../GitHubReleaseManager';
|
||||
import { Info } from './info/Info';
|
||||
import { Patch } from './patchRc/Patch';
|
||||
import { PromoteRc } from './promoteRc/PromoteRc';
|
||||
import { Info } from './Info/Info';
|
||||
import { Patch } from './Patch/Patch';
|
||||
import { PromoteReleaseCandidate } from './PromoteReleaseCandidate/PromoteRc';
|
||||
import { RefetchContext } from '../contexts/RefetchContext';
|
||||
import { useGetGitHubBatchInfo } from '../hooks/useGetGitHubBatchInfo';
|
||||
import { usePluginApiClientContext } from '../contexts/PluginApiClientContext';
|
||||
import { useProjectContext } from '../contexts/ProjectContext';
|
||||
import { useVersioningStrategyMatchesRepoTags } from '../hooks/useVersioningStrategyMatchesRepoTags';
|
||||
@@ -39,10 +38,11 @@ export function Cards({
|
||||
const pluginApiClient = usePluginApiClientContext();
|
||||
const project = useProjectContext();
|
||||
const [refetchTrigger, setRefetchTrigger] = useState(0);
|
||||
const gitHubBatchInfo = useAsync(
|
||||
getGitHubBatchInfo({ project, pluginApiClient }),
|
||||
[project, refetchTrigger],
|
||||
);
|
||||
const { gitHubBatchInfo } = useGetGitHubBatchInfo({
|
||||
pluginApiClient,
|
||||
project,
|
||||
refetchTrigger,
|
||||
});
|
||||
|
||||
const { versioningStrategyMatches } = useVersioningStrategyMatchesRepoTags({
|
||||
latestReleaseTagName: gitHubBatchInfo.value?.latestRelease?.tagName,
|
||||
@@ -111,7 +111,7 @@ export function Cards({
|
||||
)}
|
||||
|
||||
{!components?.promoteRc?.omit && (
|
||||
<PromoteRc
|
||||
<PromoteReleaseCandidate
|
||||
latestRelease={gitHubBatchInfo.value.latestRelease}
|
||||
successCb={components?.promoteRc?.successCb}
|
||||
/>
|
||||
|
||||
+2
-2
@@ -21,8 +21,8 @@ import {
|
||||
mockApiClient,
|
||||
mockBumpedTag,
|
||||
mockCalverProject,
|
||||
mockReleaseCandidateCalver,
|
||||
mockReleaseBranch,
|
||||
mockReleaseCandidateCalver,
|
||||
mockReleaseVersionCalver,
|
||||
mockTagParts,
|
||||
} from '../../test-helpers/test-helpers';
|
||||
@@ -33,7 +33,7 @@ jest.mock('../../contexts/PluginApiClientContext', () => ({
|
||||
jest.mock('../../contexts/ProjectContext', () => ({
|
||||
useProjectContext: jest.fn(() => mockCalverProject),
|
||||
}));
|
||||
jest.mock('./sideEffects/usePatch', () => ({
|
||||
jest.mock('./hooks/usePatch', () => ({
|
||||
usePatch: () => ({
|
||||
run: jest.fn(),
|
||||
responseSteps: [],
|
||||
+10
-6
@@ -40,12 +40,12 @@ import {
|
||||
import { CalverTagParts } from '../../helpers/tagParts/getCalverTagParts';
|
||||
import { CenteredCircularProgress } from '../../components/CenteredCircularProgress';
|
||||
import { ComponentConfigPatch } from '../../types/types';
|
||||
import { Dialog } from '../../components/Dialog';
|
||||
import { Differ } from '../../components/Differ';
|
||||
import { GitHubReleaseManagerError } from '../../errors/GitHubReleaseManagerError';
|
||||
import { ResponseStepDialog } from '../../components/ResponseStepDialog/ResponseStepDialog';
|
||||
import { SemverTagParts } from '../../helpers/tagParts/getSemverTagParts';
|
||||
import { TEST_IDS } from '../../test-helpers/test-ids';
|
||||
import { usePatch } from './sideEffects/usePatch';
|
||||
import { usePatch } from './hooks/usePatch';
|
||||
import { usePluginApiClientContext } from '../../contexts/PluginApiClientContext';
|
||||
import { useProjectContext } from '../../contexts/ProjectContext';
|
||||
import { useStyles } from '../../styles/styles';
|
||||
@@ -91,7 +91,7 @@ export const PatchBody = ({
|
||||
};
|
||||
});
|
||||
|
||||
const { run, responseSteps, progress } = usePatch({
|
||||
const { progress, responseSteps, run, runInvoked } = usePatch({
|
||||
bumpedTag,
|
||||
latestRelease,
|
||||
pluginApiClient,
|
||||
@@ -101,7 +101,7 @@ export const PatchBody = ({
|
||||
});
|
||||
if (responseSteps.length > 0) {
|
||||
return (
|
||||
<Dialog
|
||||
<ResponseStepDialog
|
||||
progress={progress}
|
||||
responseSteps={responseSteps}
|
||||
title="Patch Release Candidate"
|
||||
@@ -190,7 +190,7 @@ export const PatchBody = ({
|
||||
|
||||
<ListItem
|
||||
disabled={
|
||||
progress > 0 || commitExistsOnReleaseBranch || hasNoParent
|
||||
runInvoked || commitExistsOnReleaseBranch || hasNoParent
|
||||
}
|
||||
role={undefined}
|
||||
dense
|
||||
@@ -238,7 +238,11 @@ export const PatchBody = ({
|
||||
<ListItemSecondaryAction>
|
||||
<IconButton
|
||||
aria-label="commit"
|
||||
disabled={commitExistsOnReleaseBranch || !releaseBranch}
|
||||
disabled={
|
||||
runInvoked ||
|
||||
commitExistsOnReleaseBranch ||
|
||||
!releaseBranch
|
||||
}
|
||||
onClick={() => {
|
||||
const repoPath = pluginApiClient.getRepoPath({
|
||||
owner: project.owner,
|
||||
+10
-5
@@ -17,13 +17,13 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useAsync, useAsyncFn } from 'react-use';
|
||||
|
||||
import { ComponentConfigPatch } from '../../../types/types';
|
||||
import { CalverTagParts } from '../../../helpers/tagParts/getCalverTagParts';
|
||||
import {
|
||||
GetLatestReleaseResult,
|
||||
GetRecentCommitsResultSingle,
|
||||
IPluginApiClient,
|
||||
} from '../../../api/PluginApiClient';
|
||||
import { CalverTagParts } from '../../../helpers/tagParts/getCalverTagParts';
|
||||
import { ComponentConfigPatch, CardHook } from '../../../types/types';
|
||||
import { Project } from '../../../contexts/ProjectContext';
|
||||
import { SemverTagParts } from '../../../helpers/tagParts/getSemverTagParts';
|
||||
import { useResponseSteps } from '../../../hooks/useResponseSteps';
|
||||
@@ -45,7 +45,7 @@ export function usePatch({
|
||||
project,
|
||||
tagParts,
|
||||
successCb,
|
||||
}: Patch) {
|
||||
}: Patch): CardHook<GetRecentCommitsResultSingle> {
|
||||
const {
|
||||
responseSteps,
|
||||
addStepToResponseSteps,
|
||||
@@ -345,8 +345,13 @@ export function usePatch({
|
||||
}, [TOTAL_STEPS, responseSteps.length]);
|
||||
|
||||
return {
|
||||
run,
|
||||
responseSteps,
|
||||
progress,
|
||||
responseSteps,
|
||||
run,
|
||||
runInvoked: Boolean(
|
||||
releaseBranchRes.loading ||
|
||||
releaseBranchRes.value ||
|
||||
releaseBranchRes.error,
|
||||
),
|
||||
};
|
||||
}
|
||||
+6
-4
@@ -29,11 +29,13 @@ jest.mock('./PromoteRcBody', () => ({
|
||||
),
|
||||
}));
|
||||
|
||||
import { PromoteRc } from './PromoteRc';
|
||||
import { PromoteReleaseCandidate } from './PromoteRc';
|
||||
|
||||
describe('PromoteRc', () => {
|
||||
it('return early if no latest release present', () => {
|
||||
const { getByTestId } = render(<PromoteRc latestRelease={null} />);
|
||||
const { getByTestId } = render(
|
||||
<PromoteReleaseCandidate latestRelease={null} />,
|
||||
);
|
||||
|
||||
expect(
|
||||
getByTestId(TEST_IDS.components.noLatestRelease),
|
||||
@@ -42,7 +44,7 @@ describe('PromoteRc', () => {
|
||||
|
||||
it('should display not-rc warning', () => {
|
||||
const { getByTestId } = render(
|
||||
<PromoteRc latestRelease={mockReleaseVersionCalver} />,
|
||||
<PromoteReleaseCandidate latestRelease={mockReleaseVersionCalver} />,
|
||||
);
|
||||
|
||||
expect(getByTestId(TEST_IDS.promoteRc.notRcWarning)).toBeInTheDocument();
|
||||
@@ -50,7 +52,7 @@ describe('PromoteRc', () => {
|
||||
|
||||
it('should display PromoteRcBody', () => {
|
||||
const { getByTestId } = render(
|
||||
<PromoteRc latestRelease={mockReleaseCandidateCalver} />,
|
||||
<PromoteReleaseCandidate latestRelease={mockReleaseCandidateCalver} />,
|
||||
);
|
||||
|
||||
expect(
|
||||
+8
-5
@@ -18,20 +18,23 @@ import React from 'react';
|
||||
import { Alert, AlertTitle } from '@material-ui/lab';
|
||||
import { Typography } from '@material-ui/core';
|
||||
|
||||
import { ComponentConfigPromoteRc } from '../../types/types';
|
||||
import { GetLatestReleaseResult } from '../../api/PluginApiClient';
|
||||
import { InfoCardPlus } from '../../components/InfoCardPlus';
|
||||
import { NoLatestRelease } from '../../components/NoLatestRelease';
|
||||
import { ComponentConfigPromoteRc } from '../../types/types';
|
||||
import { PromoteRcBody } from './PromoteRcBody';
|
||||
import { useStyles } from '../../styles/styles';
|
||||
import { TEST_IDS } from '../../test-helpers/test-ids';
|
||||
import { GetLatestReleaseResult } from '../../api/PluginApiClient';
|
||||
import { useStyles } from '../../styles/styles';
|
||||
|
||||
interface PromoteRcProps {
|
||||
interface PromoteReleaseCandidateProps {
|
||||
latestRelease: GetLatestReleaseResult;
|
||||
successCb?: ComponentConfigPromoteRc['successCb'];
|
||||
}
|
||||
|
||||
export const PromoteRc = ({ latestRelease, successCb }: PromoteRcProps) => {
|
||||
export const PromoteReleaseCandidate = ({
|
||||
latestRelease,
|
||||
successCb,
|
||||
}: PromoteReleaseCandidateProps) => {
|
||||
const classes = useStyles();
|
||||
|
||||
function Body() {
|
||||
+1
-1
@@ -30,7 +30,7 @@ jest.mock('../../contexts/PluginApiClientContext', () => ({
|
||||
jest.mock('../../contexts/ProjectContext', () => ({
|
||||
useProjectContext: jest.fn(() => mockCalverProject),
|
||||
}));
|
||||
jest.mock('./sideEffects/usePromoteRc', () => ({
|
||||
jest.mock('./hooks/usePromoteRc', () => ({
|
||||
usePromoteRc: () => ({
|
||||
run: jest.fn(),
|
||||
responseSteps: [],
|
||||
+5
-4
@@ -18,13 +18,13 @@ import React from 'react';
|
||||
import { Button, Typography } from '@material-ui/core';
|
||||
|
||||
import { ComponentConfigPromoteRc } from '../../types/types';
|
||||
import { Dialog } from '../../components/Dialog';
|
||||
import { Differ } from '../../components/Differ';
|
||||
import { GetLatestReleaseResult } from '../../api/PluginApiClient';
|
||||
import { ResponseStepDialog } from '../../components/ResponseStepDialog/ResponseStepDialog';
|
||||
import { TEST_IDS } from '../../test-helpers/test-ids';
|
||||
import { usePluginApiClientContext } from '../../contexts/PluginApiClientContext';
|
||||
import { useProjectContext } from '../../contexts/ProjectContext';
|
||||
import { usePromoteRc } from './sideEffects/usePromoteRc';
|
||||
import { usePromoteRc } from './hooks/usePromoteRc';
|
||||
import { useStyles } from '../../styles/styles';
|
||||
|
||||
interface PromoteRcBodyProps {
|
||||
@@ -38,7 +38,7 @@ export const PromoteRcBody = ({ rcRelease, successCb }: PromoteRcBodyProps) => {
|
||||
const classes = useStyles();
|
||||
const releaseVersion = rcRelease.tagName.replace('rc-', 'version-');
|
||||
|
||||
const { run, responseSteps, progress } = usePromoteRc({
|
||||
const { progress, responseSteps, run, runInvoked } = usePromoteRc({
|
||||
pluginApiClient,
|
||||
project,
|
||||
rcRelease,
|
||||
@@ -48,7 +48,7 @@ export const PromoteRcBody = ({ rcRelease, successCb }: PromoteRcBodyProps) => {
|
||||
|
||||
if (responseSteps.length > 0) {
|
||||
return (
|
||||
<Dialog
|
||||
<ResponseStepDialog
|
||||
progress={progress}
|
||||
responseSteps={responseSteps}
|
||||
title="Promote Release Candidate"
|
||||
@@ -70,6 +70,7 @@ export const PromoteRcBody = ({ rcRelease, successCb }: PromoteRcBodyProps) => {
|
||||
data-testid={TEST_IDS.promoteRc.cta}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
disabled={runInvoked}
|
||||
onClick={() => run()}
|
||||
>
|
||||
Promote Release Candidate
|
||||
+9
-4
@@ -17,11 +17,11 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useAsync, useAsyncFn } from 'react-use';
|
||||
|
||||
import { ComponentConfigPromoteRc } from '../../../types/types';
|
||||
import {
|
||||
GetLatestReleaseResult,
|
||||
IPluginApiClient,
|
||||
} from '../../../api/PluginApiClient';
|
||||
import { CardHook, ComponentConfigPromoteRc } from '../../../types/types';
|
||||
import { Project } from '../../../contexts/ProjectContext';
|
||||
import { useResponseSteps } from '../../../hooks/useResponseSteps';
|
||||
|
||||
@@ -39,7 +39,7 @@ export function usePromoteRc({
|
||||
rcRelease,
|
||||
releaseVersion,
|
||||
successCb,
|
||||
}: PromoteRc) {
|
||||
}: PromoteRc): CardHook<void> {
|
||||
const {
|
||||
responseSteps,
|
||||
addStepToResponseSteps,
|
||||
@@ -105,8 +105,13 @@ export function usePromoteRc({
|
||||
}, [TOTAL_STEPS, responseSteps.length]);
|
||||
|
||||
return {
|
||||
run,
|
||||
responseSteps,
|
||||
progress,
|
||||
responseSteps,
|
||||
run,
|
||||
runInvoked: Boolean(
|
||||
promotedReleaseRes.loading ||
|
||||
promotedReleaseRes.value ||
|
||||
promotedReleaseRes.error,
|
||||
),
|
||||
};
|
||||
}
|
||||
+1
-1
@@ -26,11 +26,11 @@ import {
|
||||
} from '@material-ui/core';
|
||||
|
||||
import { CenteredCircularProgress } from '../../components/CenteredCircularProgress';
|
||||
import { TEST_IDS } from '../../test-helpers/test-ids';
|
||||
import { useFormClasses } from './styles';
|
||||
import { usePluginApiClientContext } from '../../contexts/PluginApiClientContext';
|
||||
import { useProjectContext } from '../../contexts/ProjectContext';
|
||||
import { useQueryHandler } from '../../hooks/useQueryHandler';
|
||||
import { TEST_IDS } from '../../test-helpers/test-ids';
|
||||
|
||||
export function Owner({ username }: { username: string }) {
|
||||
const project = useProjectContext();
|
||||
+6
-6
@@ -19,18 +19,18 @@ import { useAsync } from 'react-use';
|
||||
import { useNavigate } from 'react-router';
|
||||
import {
|
||||
FormControl,
|
||||
InputLabel,
|
||||
Select,
|
||||
MenuItem,
|
||||
FormHelperText,
|
||||
InputLabel,
|
||||
MenuItem,
|
||||
Select,
|
||||
} from '@material-ui/core';
|
||||
|
||||
import { usePluginApiClientContext } from '../../contexts/PluginApiClientContext';
|
||||
import { useFormClasses } from './styles';
|
||||
import { CenteredCircularProgress } from '../../components/CenteredCircularProgress';
|
||||
import { TEST_IDS } from '../../test-helpers/test-ids';
|
||||
import { useFormClasses } from './styles';
|
||||
import { usePluginApiClientContext } from '../../contexts/PluginApiClientContext';
|
||||
import { useProjectContext } from '../../contexts/ProjectContext';
|
||||
import { useQueryHandler } from '../../hooks/useQueryHandler';
|
||||
import { TEST_IDS } from '../../test-helpers/test-ids';
|
||||
|
||||
export function Repo() {
|
||||
const pluginApiClient = usePluginApiClientContext();
|
||||
+1
-1
@@ -24,9 +24,9 @@ import {
|
||||
RadioGroup,
|
||||
} from '@material-ui/core';
|
||||
|
||||
import { TEST_IDS } from '../../test-helpers/test-ids';
|
||||
import { useProjectContext } from '../../contexts/ProjectContext';
|
||||
import { useQueryHandler } from '../../hooks/useQueryHandler';
|
||||
import { TEST_IDS } from '../../test-helpers/test-ids';
|
||||
|
||||
export function VersioningStrategy() {
|
||||
const navigate = useNavigate();
|
||||
@@ -21,28 +21,31 @@ import {
|
||||
mockApiClient,
|
||||
mockCalverProject,
|
||||
mockNextGitHubInfo,
|
||||
mockReleaseCandidateCalver,
|
||||
mockReleaseBranch,
|
||||
mockReleaseCandidateCalver,
|
||||
mockReleaseVersionCalver,
|
||||
mockSemverProject,
|
||||
} from '../../test-helpers/test-helpers';
|
||||
import { TEST_IDS } from '../../test-helpers/test-ids';
|
||||
import { useCreateRc } from './hooks/useCreateRc';
|
||||
|
||||
jest.mock('../../contexts/PluginApiClientContext', () => ({
|
||||
usePluginApiClientContext: jest.fn(() => mockApiClient),
|
||||
usePluginApiClientContext: () => mockApiClient,
|
||||
}));
|
||||
jest.mock('../../contexts/ProjectContext', () => ({
|
||||
useProjectContext: jest.fn(() => mockCalverProject),
|
||||
}));
|
||||
jest.mock('./getRcGitHubInfo', () => ({
|
||||
jest.mock('./helpers/getRcGitHubInfo', () => ({
|
||||
getRcGitHubInfo: () => mockNextGitHubInfo,
|
||||
}));
|
||||
jest.mock('./sideEffects/useCreateRc', () => ({
|
||||
useCreateRc: () => ({
|
||||
run: jest.fn(),
|
||||
responseSteps: [],
|
||||
progress: 0,
|
||||
}),
|
||||
jest.mock('./hooks/useCreateRc', () => ({
|
||||
useCreateRc: () =>
|
||||
({
|
||||
run: jest.fn(),
|
||||
responseSteps: [],
|
||||
progress: 0,
|
||||
runLoading: false,
|
||||
} as ReturnType<typeof useCreateRc>),
|
||||
}));
|
||||
|
||||
import { useProjectContext } from '../../contexts/ProjectContext';
|
||||
|
||||
@@ -31,13 +31,13 @@ import {
|
||||
GetRepositoryResult,
|
||||
} from '../../api/PluginApiClient';
|
||||
import { ComponentConfigCreateRc } from '../../types/types';
|
||||
import { Dialog } from '../../components/Dialog';
|
||||
import { Differ } from '../../components/Differ';
|
||||
import { getRcGitHubInfo } from './getRcGitHubInfo';
|
||||
import { getRcGitHubInfo } from './helpers/getRcGitHubInfo';
|
||||
import { InfoCardPlus } from '../../components/InfoCardPlus';
|
||||
import { ResponseStepDialog } from '../../components/ResponseStepDialog/ResponseStepDialog';
|
||||
import { SEMVER_PARTS } from '../../constants/constants';
|
||||
import { TEST_IDS } from '../../test-helpers/test-ids';
|
||||
import { useCreateRc } from './sideEffects/useCreateRc';
|
||||
import { useCreateRc } from './hooks/useCreateRc';
|
||||
import { usePluginApiClientContext } from '../../contexts/PluginApiClientContext';
|
||||
import { useProjectContext } from '../../contexts/ProjectContext';
|
||||
import { useStyles } from '../../styles/styles';
|
||||
@@ -72,7 +72,7 @@ export const CreateRc = ({
|
||||
);
|
||||
}, [semverBumpLevel, setNextGitHubInfo, latestRelease, project]);
|
||||
|
||||
const { run, responseSteps, progress } = useCreateRc({
|
||||
const { progress, responseSteps, run, runInvoked } = useCreateRc({
|
||||
defaultBranch,
|
||||
latestRelease,
|
||||
nextGitHubInfo,
|
||||
@@ -82,7 +82,7 @@ export const CreateRc = ({
|
||||
});
|
||||
if (responseSteps.length > 0) {
|
||||
return (
|
||||
<Dialog
|
||||
<ResponseStepDialog
|
||||
progress={progress}
|
||||
responseSteps={responseSteps}
|
||||
title="Create Release Candidate"
|
||||
@@ -139,14 +139,12 @@ export const CreateRc = ({
|
||||
return (
|
||||
<Button
|
||||
data-testid={TEST_IDS.createRc.cta}
|
||||
disabled={conflictingPreRelease || tagAlreadyExists}
|
||||
disabled={conflictingPreRelease || tagAlreadyExists || runInvoked}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={async () => {
|
||||
await run();
|
||||
}}
|
||||
onClick={() => run()}
|
||||
>
|
||||
Create RC
|
||||
Create Release Candidate
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
+2
-2
@@ -17,11 +17,11 @@
|
||||
import { DateTime } from 'luxon';
|
||||
|
||||
import {
|
||||
mockSemverProject,
|
||||
mockCalverProject,
|
||||
mockReleaseVersionCalver,
|
||||
mockReleaseVersionSemver,
|
||||
} from '../../test-helpers/test-helpers';
|
||||
mockSemverProject,
|
||||
} from '../../../test-helpers/test-helpers';
|
||||
import { getRcGitHubInfo } from './getRcGitHubInfo';
|
||||
|
||||
describe('getRcGitHubInfo', () => {
|
||||
+5
-5
@@ -16,11 +16,11 @@
|
||||
|
||||
import { DateTime } from 'luxon';
|
||||
|
||||
import { getBumpedSemverTagParts } from '../../helpers/getBumpedTag';
|
||||
import { GetLatestReleaseResult } from '../../api/PluginApiClient';
|
||||
import { getSemverTagParts } from '../../helpers/tagParts/getSemverTagParts';
|
||||
import { Project } from '../../contexts/ProjectContext';
|
||||
import { SEMVER_PARTS } from '../../constants/constants';
|
||||
import { getBumpedSemverTagParts } from '../../../helpers/getBumpedTag';
|
||||
import { GetLatestReleaseResult } from '../../../api/PluginApiClient';
|
||||
import { getSemverTagParts } from '../../../helpers/tagParts/getSemverTagParts';
|
||||
import { Project } from '../../../contexts/ProjectContext';
|
||||
import { SEMVER_PARTS } from '../../../constants/constants';
|
||||
|
||||
export const getRcGitHubInfo = ({
|
||||
project,
|
||||
+1
@@ -94,6 +94,7 @@ describe('useCreateRc', () => {
|
||||
},
|
||||
],
|
||||
"run": [Function],
|
||||
"runLoading": false,
|
||||
}
|
||||
`);
|
||||
});
|
||||
+9
-6
@@ -17,15 +17,15 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useAsync, useAsyncFn } from 'react-use';
|
||||
|
||||
import { getRcGitHubInfo } from '../getRcGitHubInfo';
|
||||
import { ComponentConfigCreateRc } from '../../../types/types';
|
||||
import {
|
||||
GetLatestReleaseResult,
|
||||
GetRepositoryResult,
|
||||
IPluginApiClient,
|
||||
} from '../../../api/PluginApiClient';
|
||||
import { Project } from '../../../contexts/ProjectContext';
|
||||
import { CardHook, ComponentConfigCreateRc } from '../../../types/types';
|
||||
import { getRcGitHubInfo } from '../helpers/getRcGitHubInfo';
|
||||
import { GitHubReleaseManagerError } from '../../../errors/GitHubReleaseManagerError';
|
||||
import { Project } from '../../../contexts/ProjectContext';
|
||||
import { useResponseSteps } from '../../../hooks/useResponseSteps';
|
||||
|
||||
interface CreateRC {
|
||||
@@ -44,7 +44,7 @@ export function useCreateRc({
|
||||
pluginApiClient,
|
||||
project,
|
||||
successCb,
|
||||
}: CreateRC) {
|
||||
}: CreateRC): CardHook<void> {
|
||||
const {
|
||||
responseSteps,
|
||||
addStepToResponseSteps,
|
||||
@@ -211,8 +211,11 @@ export function useCreateRc({
|
||||
}, [TOTAL_STEPS, responseSteps.length]);
|
||||
|
||||
return {
|
||||
run,
|
||||
responseSteps,
|
||||
progress,
|
||||
responseSteps,
|
||||
run,
|
||||
runInvoked: Boolean(
|
||||
latestCommitRes.loading || latestCommitRes.value || latestCommitRes.error,
|
||||
),
|
||||
};
|
||||
}
|
||||
@@ -1,40 +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 from 'react';
|
||||
import {
|
||||
Box,
|
||||
LinearProgress,
|
||||
LinearProgressProps,
|
||||
Typography,
|
||||
} from '@material-ui/core';
|
||||
|
||||
export function LinearProgressWithLabel(
|
||||
props: LinearProgressProps & { value: number },
|
||||
) {
|
||||
return (
|
||||
<Box display="flex" alignItems="center" width="90%" alignSelf="center">
|
||||
<Box width="100%" mr={1}>
|
||||
<LinearProgress variant="determinate" {...props} />
|
||||
</Box>
|
||||
<Box minWidth={35}>
|
||||
<Typography variant="body2" color="textSecondary">{`${Math.round(
|
||||
props.value,
|
||||
)}%`}</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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 { Box, LinearProgress, Typography } from '@material-ui/core';
|
||||
|
||||
import { ResponseStep } from '../../types/types';
|
||||
|
||||
const STATUSES = {
|
||||
FAILURE: 'FAILURE',
|
||||
ONGOING: 'ONGOING',
|
||||
SUCCESS: 'SUCCESS',
|
||||
} as const;
|
||||
|
||||
export function LinearProgressWithLabel(props: {
|
||||
progress: number;
|
||||
responseSteps: ResponseStep[];
|
||||
}) {
|
||||
const roundedValue = Math.ceil(props.progress);
|
||||
const progress = roundedValue < 100 ? roundedValue : 100;
|
||||
|
||||
const failure = props.responseSteps.some(
|
||||
responseStep => responseStep.icon === 'failure',
|
||||
);
|
||||
|
||||
let status: keyof typeof STATUSES = STATUSES.ONGOING;
|
||||
if (!failure && progress === 100) status = STATUSES.SUCCESS;
|
||||
if (failure) status = STATUSES.FAILURE;
|
||||
|
||||
const CompletionEmoji = () => {
|
||||
if (status === STATUSES.ONGOING) return null;
|
||||
if (status === STATUSES.FAILURE) return <span>{' 🔥 '}</span>;
|
||||
return <span>{' 🚀 '}</span>;
|
||||
};
|
||||
|
||||
return (
|
||||
<Box
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
width="90%"
|
||||
alignSelf="center"
|
||||
flexDirection="column"
|
||||
>
|
||||
<Box width="100%">
|
||||
<LinearProgress variant="determinate" value={progress} />
|
||||
</Box>
|
||||
|
||||
<Box>
|
||||
<Typography
|
||||
variant="body2"
|
||||
style={{
|
||||
marginTop: 8,
|
||||
minWidth: 35,
|
||||
color: failure ? '#ff0033' : '#1DB954',
|
||||
fontWeight: 'bold',
|
||||
fontSize: `${125 + Math.ceil(progress / 3)}%`,
|
||||
}}
|
||||
>
|
||||
<CompletionEmoji />
|
||||
{`${progress}%`}
|
||||
<CompletionEmoji />
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
+5
-5
@@ -17,19 +17,19 @@
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
|
||||
import { Dialog } from './Dialog';
|
||||
import { ResponseStepDialog } from './ResponseStepDialog';
|
||||
|
||||
jest.mock('../contexts/RefetchContext', () => ({
|
||||
jest.mock('../../contexts/RefetchContext', () => ({
|
||||
useRefetchContext: () => jest.fn(),
|
||||
}));
|
||||
|
||||
describe('Dialog', () => {
|
||||
it('should render Dialog', () => {
|
||||
describe('ResponseStepDialog', () => {
|
||||
it('should render ResponseStepDialog', () => {
|
||||
const mockTitle = 'mock_dialog_title';
|
||||
const mockResponseStepMessage = 'banana';
|
||||
|
||||
const { baseElement } = render(
|
||||
<Dialog
|
||||
<ResponseStepDialog
|
||||
progress={1}
|
||||
responseSteps={[{ message: mockResponseStepMessage }]}
|
||||
title={mockTitle}
|
||||
+32
-9
@@ -14,18 +14,21 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React, { forwardRef, Ref } from 'react';
|
||||
import {
|
||||
Button,
|
||||
Dialog as MaterialDialog,
|
||||
DialogActions,
|
||||
DialogTitle,
|
||||
Slide,
|
||||
} from '@material-ui/core';
|
||||
import { TransitionProps } from '@material-ui/core/transitions';
|
||||
import RefreshIcon from '@material-ui/icons/Refresh';
|
||||
|
||||
import { LinearProgressWithLabel } from './LinearProgressWithLabel';
|
||||
import { ResponseStep } from '../types/types';
|
||||
import { ResponseStepList } from './ResponseStepList/ResponseStepList';
|
||||
import { useRefetchContext } from '../contexts/RefetchContext';
|
||||
import { ResponseStep } from '../../types/types';
|
||||
import { ResponseStepList } from './ResponseStepList';
|
||||
import { useRefetchContext } from '../../contexts/RefetchContext';
|
||||
|
||||
interface DialogProps {
|
||||
progress: number;
|
||||
@@ -33,23 +36,43 @@ interface DialogProps {
|
||||
title: string;
|
||||
}
|
||||
|
||||
export const Dialog = ({ progress, responseSteps, title }: DialogProps) => {
|
||||
const Transition = forwardRef(function Transition(
|
||||
props: { children?: React.ReactElement<any, any> } & TransitionProps,
|
||||
ref: Ref<unknown>,
|
||||
) {
|
||||
return <Slide direction="up" ref={ref} {...props} />;
|
||||
});
|
||||
|
||||
export const ResponseStepDialog = ({
|
||||
progress,
|
||||
responseSteps,
|
||||
title,
|
||||
}: DialogProps) => {
|
||||
const { setRefetchTrigger } = useRefetchContext();
|
||||
|
||||
return (
|
||||
<MaterialDialog open maxWidth="md" fullWidth>
|
||||
<MaterialDialog
|
||||
open
|
||||
maxWidth="md"
|
||||
fullWidth
|
||||
TransitionComponent={Transition}
|
||||
>
|
||||
<DialogTitle>{title}</DialogTitle>
|
||||
|
||||
<ResponseStepList responseSteps={responseSteps} />
|
||||
|
||||
<LinearProgressWithLabel value={progress} />
|
||||
<LinearProgressWithLabel
|
||||
progress={progress}
|
||||
responseSteps={responseSteps}
|
||||
/>
|
||||
|
||||
<DialogActions>
|
||||
<DialogActions style={{ padding: 20 }}>
|
||||
<Button
|
||||
onClick={() => setRefetchTrigger(Date.now())}
|
||||
color="primary"
|
||||
variant="contained"
|
||||
size="large"
|
||||
color="primary"
|
||||
startIcon={<RefreshIcon />}
|
||||
>
|
||||
Ok
|
||||
</Button>
|
||||
+1
-1
@@ -17,8 +17,8 @@
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
|
||||
import { TEST_IDS } from '../../test-helpers/test-ids';
|
||||
import { ResponseStepListItem } from './ResponseStepListItem';
|
||||
import { TEST_IDS } from '../../test-helpers/test-ids';
|
||||
|
||||
describe('ResponseStepListItem', () => {
|
||||
it('should render', () => {
|
||||
+1
-1
@@ -28,8 +28,8 @@ import ErrorOutlineIcon from '@material-ui/icons/ErrorOutline';
|
||||
import FiberManualRecordIcon from '@material-ui/icons/FiberManualRecord';
|
||||
import OpenInNewIcon from '@material-ui/icons/OpenInNew';
|
||||
|
||||
import { TEST_IDS } from '../../test-helpers/test-ids';
|
||||
import { ResponseStep } from '../../types/types';
|
||||
import { TEST_IDS } from '../../test-helpers/test-ids';
|
||||
|
||||
interface ResponseStepListItemProps {
|
||||
responseStep: ResponseStep;
|
||||
+27
-17
@@ -14,39 +14,49 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { useAsync } from 'react-use';
|
||||
|
||||
import { IPluginApiClient } from '../api/PluginApiClient';
|
||||
import { Project } from '../contexts/ProjectContext';
|
||||
|
||||
interface GetGitHubBatchInfo {
|
||||
project: Project;
|
||||
pluginApiClient: IPluginApiClient;
|
||||
refetchTrigger: number;
|
||||
}
|
||||
|
||||
export const getGitHubBatchInfo = ({
|
||||
export const useGetGitHubBatchInfo = ({
|
||||
project,
|
||||
pluginApiClient,
|
||||
}: GetGitHubBatchInfo) => async () => {
|
||||
const [repository, latestRelease] = await Promise.all([
|
||||
pluginApiClient.getRepository({ ...project }),
|
||||
pluginApiClient.getLatestRelease({ ...project }),
|
||||
]);
|
||||
refetchTrigger,
|
||||
}: GetGitHubBatchInfo) => {
|
||||
const gitHubBatchInfo = useAsync(async () => {
|
||||
const [repository, latestRelease] = await Promise.all([
|
||||
pluginApiClient.getRepository({ ...project }),
|
||||
pluginApiClient.getLatestRelease({ ...project }),
|
||||
]);
|
||||
|
||||
if (latestRelease === null) {
|
||||
return {
|
||||
latestRelease,
|
||||
releaseBranch: null,
|
||||
repository,
|
||||
};
|
||||
}
|
||||
|
||||
const releaseBranch = await pluginApiClient.getBranch({
|
||||
...project,
|
||||
branchName: latestRelease.targetCommitish,
|
||||
});
|
||||
|
||||
if (latestRelease === null) {
|
||||
return {
|
||||
latestRelease,
|
||||
releaseBranch: null,
|
||||
releaseBranch,
|
||||
repository,
|
||||
};
|
||||
}
|
||||
|
||||
const releaseBranch = await pluginApiClient.getBranch({
|
||||
...project,
|
||||
branchName: latestRelease.targetCommitish,
|
||||
});
|
||||
}, [project, refetchTrigger]);
|
||||
|
||||
return {
|
||||
latestRelease,
|
||||
releaseBranch,
|
||||
repository,
|
||||
gitHubBatchInfo,
|
||||
};
|
||||
};
|
||||
@@ -18,37 +18,37 @@ import { useState } from 'react';
|
||||
|
||||
import { ResponseStep } from '../types/types';
|
||||
|
||||
const RESPONSE_STEP_FAILURE_ABORT: ResponseStep = {
|
||||
message: 'Skipped due to error in previous step',
|
||||
icon: 'failure',
|
||||
};
|
||||
|
||||
export function useResponseSteps() {
|
||||
const [responseSteps, setResponseSteps] = useState<ResponseStep[]>([]);
|
||||
|
||||
function abortIfError(error?: Error) {
|
||||
const RESPONSE_STEP_SKIP = {
|
||||
responseStep: {
|
||||
message: 'Skipped due to error in previous step',
|
||||
icon: 'failure',
|
||||
} as ResponseStep,
|
||||
};
|
||||
const addStepToResponseSteps = (responseStep: ResponseStep) => {
|
||||
setResponseSteps([...responseSteps, responseStep]);
|
||||
};
|
||||
|
||||
const abortIfError = (error?: Error) => {
|
||||
if (error) {
|
||||
setResponseSteps([...responseSteps, RESPONSE_STEP_SKIP.responseStep]);
|
||||
addStepToResponseSteps(RESPONSE_STEP_FAILURE_ABORT);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function asyncCatcher(error: Error): never {
|
||||
const asyncCatcher = (error?: Error): never => {
|
||||
const responseStepError: ResponseStep = {
|
||||
message: 'Something went wrong ❌',
|
||||
secondaryMessage: `Error message: ${error.message}`,
|
||||
message: 'Something went wrong 🔥',
|
||||
secondaryMessage: `Error message: ${
|
||||
error?.message ? error.message : 'unknown'
|
||||
}`,
|
||||
icon: 'failure',
|
||||
};
|
||||
|
||||
setResponseSteps([...responseSteps, responseStepError]);
|
||||
addStepToResponseSteps(responseStepError);
|
||||
throw error;
|
||||
}
|
||||
|
||||
function addStepToResponseSteps(responseStep: ResponseStep) {
|
||||
setResponseSteps([...responseSteps, responseStep]);
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
responseSteps,
|
||||
|
||||
@@ -14,15 +14,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { CalverTagParts } from '../helpers/tagParts/getCalverTagParts';
|
||||
import { getRcGitHubInfo } from '../cards/createRc/getRcGitHubInfo';
|
||||
import { Project } from '../contexts/ProjectContext';
|
||||
import {
|
||||
GetBranchResult,
|
||||
GetLatestReleaseResult,
|
||||
GetRecentCommitsResultSingle,
|
||||
IPluginApiClient,
|
||||
} from '../api/PluginApiClient';
|
||||
import { CalverTagParts } from '../helpers/tagParts/getCalverTagParts';
|
||||
import { getRcGitHubInfo } from '../cards/CreateRc/helpers/getRcGitHubInfo';
|
||||
import { Project } from '../contexts/ProjectContext';
|
||||
|
||||
const mockOwner = 'mock_owner';
|
||||
const mockRepo = 'mock_repo';
|
||||
|
||||
@@ -54,3 +54,10 @@ export interface ResponseStep {
|
||||
link?: string;
|
||||
icon?: 'success' | 'failure';
|
||||
}
|
||||
|
||||
export interface CardHook<RunArgs> {
|
||||
progress: number;
|
||||
responseSteps: ResponseStep[];
|
||||
run: (args: RunArgs) => Promise<any>;
|
||||
runInvoked: boolean;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user