Skip to content

Commit

Permalink
feat: allow packages to upgrade to latest blueprint (#54)
Browse files Browse the repository at this point in the history
This change allows packages to be upgraded to use the latest published blueprint revision. A package can be upgraded by clicking the new 'Upgrade to Latest Blueprint' button on the Package Revision Page. This button shows when the latest published revision of a package is shown, and a newer published revision exists for the package's blueprint. Additionally, the Packages Table shows a visual indicator when an upgrade is available for a package.
  • Loading branch information
ChristopherFry committed Jun 24, 2022
1 parent eca474f commit b04bc3c
Show file tree
Hide file tree
Showing 5 changed files with 182 additions and 17 deletions.
126 changes: 113 additions & 13 deletions plugins/cad/src/components/PackageRevisionPage/PackageRevisionPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import {
} from '@material-ui/core';
import Alert, { Color } from '@material-ui/lab/Alert';
import { cloneDeep } from 'lodash';
import React, { Fragment, useEffect, useState } from 'react';
import React, { Fragment, useEffect, useRef, useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import useAsync from 'react-use/lib/useAsync';
import { configAsDataApiRef } from '../../apis';
Expand Down Expand Up @@ -62,6 +62,7 @@ import {
getNextPackageRevisionResource,
getPackageRevision,
getPackageRevisionTitle,
getUpgradePackageRevisionResource,
getUpstreamPackageRevisionDetails,
isLatestPublishedRevision,
sortByPackageNameAndRevisionComparison,
Expand All @@ -76,6 +77,7 @@ import {
isDeploymentRepository,
} from '../../utils/repository';
import { getRepositorySummary } from '../../utils/repositorySummary';
import { toLowerCase } from '../../utils/string';
import { ConfirmationDialog, Select } from '../Controls';
import { PackageLink, RepositoriesLink, RepositoryLink } from '../Links';
import { AdvancedPackageRevisionOptions } from './components/AdvancedPackageRevisionOptions';
Expand Down Expand Up @@ -146,6 +148,10 @@ export const PackageRevisionPage = ({ mode }: PackageRevisionPageProps) => {

const [openRestoreDialog, setOpenRestoreDialog] = useState<boolean>(false);

const [isUpgradeAvailable, setIsUpgradeAvailable] = useState<boolean>(false);

const latestPublishedUpstream = useRef<PackageRevision>();

const loadRepositorySummary = async (): Promise<void> => {
const thisRepositorySummary = await getRepositorySummary(
api,
Expand Down Expand Up @@ -207,23 +213,41 @@ export const PackageRevisionPage = ({ mode }: PackageRevisionPageProps) => {
});
}

const upstream = getUpstreamPackageRevisionDetails(thisPackageRevision);
let upgradeAvailable = false;

if (upstream) {
const upstreamPackage = findPackageRevision(
thisPackageRevisions,
upstream.packageName,
upstream.revision,
);
if (isLatestPublishedRevision(thisPackageRevision)) {
const upstream = getUpstreamPackageRevisionDetails(thisPackageRevision);

if (upstreamPackage) {
diffItems.push({
label: `Upstream (${getPackageRevisionTitle(upstreamPackage)})`,
value: upstreamPackage.metadata.name,
});
if (upstream) {
const upstreamPackage = findPackageRevision(
thisPackageRevisions,
upstream.packageName,
upstream.revision,
);

if (upstreamPackage) {
diffItems.push({
label: `Upstream (${getPackageRevisionTitle(upstreamPackage)})`,
value: upstreamPackage.metadata.name,
});
}

const allUpstreamRevisions = filterPackageRevisions(
thisPackageRevisions,
upstream.packageName,
);
latestPublishedUpstream.current =
findLatestPublishedRevision(allUpstreamRevisions);

if (
upstream.revision !== latestPublishedUpstream.current?.spec.revision
) {
upgradeAvailable = true;
}
}
}

setIsUpgradeAvailable(upgradeAvailable);
setSelectDiffItems(diffItems);

const isPublished =
Expand Down Expand Up @@ -436,6 +460,33 @@ export const PackageRevisionPage = ({ mode }: PackageRevisionPageProps) => {
}
};

const createUpgradeRevision = async (): Promise<void> => {
setUserInitiatedApiRequest(true);

try {
if (!latestPublishedUpstream.current) {
throw new Error('The latest published upstream package is not defined');
}

const blueprintPackageRevisionName =
latestPublishedUpstream.current.metadata.name;

const requestPackageRevision = getUpgradePackageRevisionResource(
packageRevision,
blueprintPackageRevisionName,
);

const newPackageRevision = await api.createPackageRevision(
requestPackageRevision,
);
const newPackageName = newPackageRevision.metadata.name;

navigate(packageRef({ repositoryName, packageName: newPackageName }));
} finally {
setUserInitiatedApiRequest(false);
}
};

const createNewRevision = async (): Promise<void> => {
setUserInitiatedApiRequest(true);

Expand Down Expand Up @@ -671,6 +722,20 @@ export const PackageRevisionPage = ({ mode }: PackageRevisionPageProps) => {

if (isLatestPublishedPackageRevision) {
if (latestRevision === latestPublishedRevision) {
if (isUpgradeAvailable) {
options.push(
<MaterialButton
key="create-upgrade-revision"
variant="outlined"
color="primary"
onClick={createUpgradeRevision}
disabled={userInitiatedApiRequest}
>
Upgrade to Latest Blueprint
</MaterialButton>,
);
}

options.push(
<MaterialButton
key="create-new-revision"
Expand Down Expand Up @@ -781,6 +846,34 @@ export const PackageRevisionPage = ({ mode }: PackageRevisionPageProps) => {

const isViewMode = mode === PackageRevisionPageMode.VIEW;

const getUpgradeAlertText = (): string => {
const latestRevision = packageRevisions[0];

const blueprintName = `${latestPublishedUpstream.current?.spec.packageName} blueprint`;
const baseUpgradeText = `The ${blueprintName} has been upgraded.`;

const latestRevisionUpstream =
getUpstreamPackageRevisionDetails(latestRevision);

if (latestRevision !== packageRevision) {
const isLatestRevisionUpgraded =
latestRevisionUpstream?.revision ===
latestPublishedUpstream.current?.spec.revision;

const pendingRevisionName = `${latestRevision.spec.packageName} ${
latestRevision.spec.revision
} ${toLowerCase(latestRevision.spec.lifecycle)} revision`;

if (isLatestRevisionUpgraded) {
return `${baseUpgradeText} The ${pendingRevisionName} includes changes from the upgraded ${blueprintName}.`;
}

return `${baseUpgradeText} The ${pendingRevisionName} does not include changes from the upgraded ${blueprintName}. The revision must be either published or deleted first before changes from the upgraded ${blueprintName} can be pulled in.`;
}

return `${baseUpgradeText} Use the 'Upgrade to Latest Blueprint' button to create a revision that pulls in changes from the upgraded blueprint.`;
};

return (
<div>
<Breadcrumbs>
Expand Down Expand Up @@ -820,6 +913,13 @@ export const PackageRevisionPage = ({ mode }: PackageRevisionPageProps) => {
label: 'Resources',
content: (
<Fragment>
{isUpgradeAvailable && (
<Fragment>
<Alert severity="info" style={{ marginBottom: '16px' }}>
{getUpgradeAlertText()}
</Alert>
</Fragment>
)}
<PackageRevisionResourcesTable
resourcesMap={resourcesMap}
baseResourcesMap={baseResourcesMap}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,13 @@ const getSummary = (packageSummaries?: PackageSummary[]): string => {
if (draftPackages) {
summary = `${summary}, ${draftPackages} Draft`;
}
/*
const upgradePackages = packageSummaries.filter(summary => summary.isUpgradeAvailable).length;
if (upgradePackages) {
summary = `${summary}, Upgrades Avaialble`
}
*/
return summary;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { isDeploymentRepository } from '../../../utils/repository';
import { IconButton, PackageIcon } from '../../Controls';
import { PackageLink } from '../../Links';
import { SyncStatusVisual } from './SyncStatusVisual';
import ArrowUpwardIcon from '@material-ui/icons/ArrowUpward';

type PackageRevisionsTableProps = {
title: string;
Expand Down Expand Up @@ -61,6 +62,7 @@ type PackageSummaryRow = {
upstreamPackageRevision?: PackageRevision;
navigate: () => void;
unpublished?: UnpublishedPackageRevision;
isUpgradeAvailable?: boolean;
};

type NavigateToPackageRevision = (revision: PackageRevision) => void;
Expand All @@ -70,20 +72,33 @@ const renderStatusColumn = (
): JSX.Element => {
const unpublishedRevision = thisPackageRevisionRow.unpublished;

const elements: JSX.Element[] = [];

if (thisPackageRevisionRow.isUpgradeAvailable) {
elements.push(
<IconButton title="Upgrade available">
<ArrowUpwardIcon />
</IconButton>,
);
}

if (unpublishedRevision) {
return (
elements.push(
<IconButton
title={`${unpublishedRevision.lifecycle} revision`}
inTable
stopPropagation
onClick={() => unpublishedRevision.navigate()}
>
<PackageIcon lifecycle={unpublishedRevision.lifecycle} />
</IconButton>
</IconButton>,
);
}

return <Fragment />;
return (
<div style={{ position: 'absolute', transform: 'translateY(-50%)' }}>
{...elements}
</div>
);
};

const renderBlueprintColumn = (row: PackageSummaryRow): JSX.Element => {
Expand Down Expand Up @@ -189,6 +204,7 @@ const mapToPackageSummaryRow = (
upstreamPackageDisplayName: packageSummary.upstreamPackageName
? `${packageSummary.upstreamPackageName} ${packageSummary.upstreamPackageRevision}`
: undefined,
isUpgradeAvailable: packageSummary.isUpgradeAvailable,
upstreamPackageRevision: packageSummary.upstreamRevision,
unpublished: mapToUnpublishedRevision(
packageSummary,
Expand Down
31 changes: 31 additions & 0 deletions plugins/cad/src/utils/packageRevision.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,37 @@ export const getNextPackageRevisionResource = (
return resource;
};

export const getUpgradePackageRevisionResource = (
currentRevision: PackageRevision,
upgradePackageRevisionName: string,
): PackageRevision => {
const { repository, packageName, revision, tasks } = currentRevision.spec;
const nextRevision = getNextRevision(revision);

const [firstTask, ...remainderTasks] = tasks;

if (firstTask.type !== 'clone') {
throw new Error(
`First task of a package revision to be upgraded must be of type 'clone'`,
);
}

const newTasks = [
getCloneTask(upgradePackageRevisionName),
...remainderTasks,
];

const resource = getPackageRevisionResource(
repository,
packageName,
nextRevision,
PackageRevisionLifecycle.DRAFT,
newTasks,
);

return resource;
};

export const sortByPackageNameAndRevisionComparison = (
packageRevision1: PackageRevision,
packageRevision2: PackageRevision,
Expand Down
12 changes: 12 additions & 0 deletions plugins/cad/src/utils/packageSummary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { RepositorySummary } from '../types/RepositorySummary';
import { RootSync } from '../types/RootSync';
import { findRootSyncForPackage } from './configSync';
import {
filterPackageRevisions,
findLatestPublishedRevision,
findPackageRevision,
getUpstreamPackageRevisionDetails,
Expand All @@ -37,6 +38,8 @@ export type PackageSummary = {
upstreamRevision?: PackageRevision;
upstreamPackageName?: string;
upstreamPackageRevision?: string;
upstreamLatestPublishedRevision?: PackageRevision;
isUpgradeAvailable?: boolean;
sync?: RootSync;
};

Expand Down Expand Up @@ -91,6 +94,15 @@ export const getPackageSummariesForRepository = (
upstream.packageName,
upstream.revision,
);

thisPackageSummary.upstreamLatestPublishedRevision =
findLatestPublishedRevision(
filterPackageRevisions(upstreamRevisions, upstream.packageName),
);

thisPackageSummary.isUpgradeAvailable =
thisPackageSummary.upstreamLatestPublishedRevision?.spec.revision !==
upstream.revision;
}

return thisPackageSummary;
Expand Down

0 comments on commit b04bc3c

Please sign in to comment.