Skip to content

Commit

Permalink
feat: deployments tab for sources (#765)
Browse files Browse the repository at this point in the history
* Add sourceId to the Deployment types

* Implement a Deployments tab in the Sources interface

* Refactor common git and docker elements into their own components

* Add a view button to the apps table for consistency

* Use the common git and docker widgets on the app detail page

* Update the snapshot for the app detail page
  • Loading branch information
eabruzzese authored Apr 2, 2024
1 parent 8e7f06a commit f48a4a7
Show file tree
Hide file tree
Showing 16 changed files with 487 additions and 166 deletions.
5 changes: 5 additions & 0 deletions src/app/router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ import {
SettingsPage,
SignupPage,
SourceDetailAppsPage,
SourceDetailDeploymentsPage,
SourceDetailLayout,
SourceDetailPage,
SourcesPage,
Expand Down Expand Up @@ -290,6 +291,10 @@ export const appRoutes: RouteObject[] = [
path: routes.SOURCE_DETAIL_APPS_PATH,
element: <SourceDetailAppsPage />,
},
{
path: routes.SOURCE_DETAIL_DEPLOYMENTS_PATH,
element: <SourceDetailDeploymentsPage />,
},
],
},
],
Expand Down
10 changes: 10 additions & 0 deletions src/deployment/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export interface DeploymentResponse {
operation: LinkResponse;
configuration: LinkResponse;
image: LinkResponse;
source: LinkResponse;
};
_type: "deployment";
}
Expand All @@ -48,6 +49,7 @@ export const defaultDeploymentResponse = (
operation: defaultHalHref(),
configuration: defaultHalHref(),
image: defaultHalHref(),
source: defaultHalHref(),
...p._links,
},
...p,
Expand Down Expand Up @@ -77,6 +79,7 @@ export const deserializeDeployment = (
operationId: extractIdFromLink(links.operation),
imageId: extractIdFromLink(links.image),
configurationId: extractIdFromLink(links.configuration),
sourceId: extractIdFromLink(links.source),
};
};

Expand All @@ -95,6 +98,13 @@ export const selectDeploymentsByAppId = createSelector(
return deployments.filter((d) => d.appId === appId);
},
);
export const selectDeploymentsBySourceId = createSelector(
selectDeploymentsAsList,
(_: WebState, p: { sourceId: string }) => p.sourceId,
(deployments, sourceId): Deployment[] => {
return deployments.filter((d) => d.sourceId === sourceId);
},
);

export function getRegistryParts(url: string): { name: string; tag: string } {
const [name, tag] = url.split(":");
Expand Down
3 changes: 3 additions & 0 deletions src/mocks/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,7 @@ export const testDeploymentGit = defaultDeploymentResponse({
),
configuration: defaultHalHref(),
image: defaultHalHref(),
source: defaultHalHref(),
},
});

Expand All @@ -711,6 +712,7 @@ export const testDeploymentDocker = defaultDeploymentResponse({
),
configuration: defaultHalHref(),
image: defaultHalHref(),
source: defaultHalHref(),
},
});

Expand All @@ -731,5 +733,6 @@ export const testDeploymentEmpty = defaultDeploymentResponse({
),
configuration: defaultHalHref(),
image: defaultHalHref(),
source: defaultHalHref(),
},
});
3 changes: 3 additions & 0 deletions src/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -397,3 +397,6 @@ export const SOURCE_DETAIL_PATH = "/sources/:id";
export const sourceDetailUrl = (id: string) => `/sources/${id}`;
export const SOURCE_DETAIL_APPS_PATH = "/sources/:id/apps";
export const sourceDetailAppsUrl = (id: string) => `/sources/${id}/apps`;
export const SOURCE_DETAIL_DEPLOYMENTS_PATH = "/sources/:id/deployments";
export const sourceDetailDeploymentsUrl = (id: string) =>
`/sources/${id}/deployments`;
1 change: 1 addition & 0 deletions src/schema/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -822,6 +822,7 @@ export const defaultDeployment = (a: Partial<Deployment> = {}): Deployment => {
operationId: "",
imageId: "",
configurationId: "",
sourceId: "",
...a,
};
};
Expand Down
3 changes: 3 additions & 0 deletions src/source/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ export const selectSourcesAsList = schema.sources.selectTableAsList;

export const fetchSources = api.get("/sources");
export const fetchSourceById = api.get<{ id: string }>("/sources/:id");
export const fetchDeploymentsBySourceId = api.get<{ id: string }>(
"/sources/:id/deployments",
);

export const entities = {
source: defaultEntity({
Expand Down
1 change: 1 addition & 0 deletions src/types/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,5 @@ export interface Deployment {
appId: string;
configurationId: string;
imageId: string;
sourceId: string;
}
60 changes: 42 additions & 18 deletions src/ui/layouts/app-detail-layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@ import {
cancelAppOpsPoll,
fetchApp,
fetchConfiguration,
fetchImageById,
fetchServicesByAppId,
pollAppOperations,
selectAppById,
selectEnvironmentById,
selectImageById,
selectLatestDeployOp,
selectUserHasPerms,
} from "@app/deploy";
import { fetchDeploymentById, getDockerImageName } from "@app/deployment";
import { fetchDeploymentById, selectDeploymentById } from "@app/deployment";
import { findLoaderComposite } from "@app/loaders";
import { selectHasBetaFeatures } from "@app/organizations";
import { useDispatch, useQuery, useSelector } from "@app/react";
Expand All @@ -25,23 +27,24 @@ import {
appServicesUrl,
appSettingsUrl,
environmentAppsUrl,
sourceDetailUrl,
} from "@app/routes";
import { schema } from "@app/schema";
import { setResourceStats } from "@app/search";
import { fetchSourceById, selectSourceById } from "@app/source";
import type { DeployApp } from "@app/types";
import { useEffect, useMemo } from "react";
import { Outlet, useParams } from "react-router-dom";
import { Link, Outlet, useParams } from "react-router-dom";
import { usePoller } from "../hooks";
import {
ActiveOperationNotice,
DeploymentGitSha,
DeploymentTagText,
DetailHeader,
DetailInfoGrid,
DetailInfoItem,
DetailPageHeaderView,
DetailTitleBar,
GitMetadata,
DockerImage,
GitCommitMessage,
GitRef,
SourceName,
TabItem,
} from "../shared";
Expand All @@ -54,11 +57,23 @@ export function AppHeader({
const lastDeployOp = useSelector((s) =>
selectLatestDeployOp(s, { appId: app.id }),
);

const hasBetaFeatures = useSelector(selectHasBetaFeatures);

useQuery(fetchDeploymentById({ id: app.currentDeploymentId }));
const deployment = useSelector((s) =>
schema.deployments.selectById(s, { id: app.currentDeploymentId }),
selectDeploymentById(s, { id: app.currentDeploymentId }),
);

useQuery(fetchImageById({ id: app.currentImageId }));
const image = useSelector((s) =>
selectImageById(s, { id: app.currentImageId }),
);

useQuery(fetchSourceById({ id: app.currentSourceId }));
const source = useSelector((s) =>
selectSourceById(s, { id: app.currentSourceId }),
);
const dockerImage = getDockerImageName(deployment);

return (
<DetailHeader>
Expand All @@ -78,25 +93,34 @@ export function AppHeader({
<DetailInfoGrid>
<DetailInfoItem title="ID">{app.id}</DetailInfoItem>
<DetailInfoItem title="Git Ref">
<DeploymentGitSha deployment={deployment} />
<GitRef
gitRef={deployment.gitRef}
commitSha={deployment.gitCommitSha}
commitUrl={deployment.gitCommitUrl}
/>
</DetailInfoItem>

<DetailInfoItem title="Source">
<SourceName app={app} deployment={deployment} />
{hasBetaFeatures && source.id ? (
<Link to={sourceDetailUrl(source.id)}>{source.displayName}</Link>
) : (
<SourceName app={app} deployment={deployment} />
)}
</DetailInfoItem>
{deployment.gitCommitMessage ? (
<DetailInfoItem title="Commit Message">
<GitMetadata deployment={deployment} />
</DetailInfoItem>
) : null}
<DetailInfoItem title="Tag">
<DeploymentTagText deployment={deployment} />
<DetailInfoItem title="Commit Message">
<GitCommitMessage message={deployment.gitCommitMessage} />
</DetailInfoItem>

<DetailInfoItem title="Created">
{prettyDateTime(app.createdAt)}
</DetailInfoItem>
<DetailInfoItem title="Docker Image">{dockerImage}</DetailInfoItem>
<DetailInfoItem title="Docker Image">
<DockerImage
image={deployment.dockerImage}
digest={image.dockerRef}
repoUrl={deployment.dockerRepositoryUrl}
/>
</DetailInfoItem>
<DetailInfoItem title="Last Deployed">
{lastDeployOp
? `${prettyDateTime(lastDeployOp.createdAt)}`
Expand Down
11 changes: 9 additions & 2 deletions src/ui/layouts/source-detail-layout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { prettyDateTime } from "@app/date";
import { useQuery, useSelector } from "@app/react";
import { sourceDetailAppsUrl, sourcesUrl } from "@app/routes";
import {
sourceDetailAppsUrl,
sourceDetailDeploymentsUrl,
sourcesUrl,
} from "@app/routes";
import { fetchSourceById, selectSourceById } from "@app/source";
import { DeploySource } from "@app/types";
import { Outlet, useParams } from "react-router";
Expand Down Expand Up @@ -57,7 +61,10 @@ function SourcePageHeader() {

const crumbs = [{ name: "Sources", to: sourcesUrl() }];

const tabs: TabItem[] = [{ name: "Apps", href: sourceDetailAppsUrl(id) }];
const tabs: TabItem[] = [
{ name: "Apps", href: sourceDetailAppsUrl(id) },
{ name: "Deployments", href: sourceDetailDeploymentsUrl(id) },
];

return (
<DetailPageHeaderView
Expand Down
Loading

0 comments on commit f48a4a7

Please sign in to comment.