Skip to content

Commit 2b44d1d

Browse files
flux list display (#70)
1 parent 6e30ac2 commit 2b44d1d

File tree

4 files changed

+189
-30
lines changed

4 files changed

+189
-30
lines changed

public/locales/en.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,11 @@
1212
"FluxList": {
1313
"tableNameHeader": "Name",
1414
"tableStatusHeader": "Status",
15-
"tableCreatedHeader": "Created"
15+
"tableCreatedHeader": "Created",
16+
"tableVersionHeader": "Revision",
17+
"noFluxError": "Please install flux to view this component",
18+
"gitOpsTitle": "GitOps",
19+
"kustomizationsTitle": "Kustomizations"
1620
},
1721
"ProvidersList": {
1822
"tableNameHeader": "Name",

src/components/ControlPlane/FluxList.tsx

Lines changed: 128 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,20 @@ import {
33
AnalyticalTableColumnDefinition,
44
Title,
55
} from '@ui5/webcomponents-react';
6-
import ReactTimeAgo from 'react-time-ago';
76
import IllustratedError from '../Shared/IllustratedError.tsx';
87
import useResource from '../../lib/api/useApiResource';
9-
import { FluxGitRepo } from '../../lib/api/types/flux/listGitRepo';
8+
import { FluxRequest } from '../../lib/api/types/flux/listGitRepo';
109
import { FluxKustomization } from '../../lib/api/types/flux/listKustomization';
1110
import { useTranslation } from 'react-i18next';
11+
import { timeAgo } from '../../utils/i18n/timeAgo.ts';
12+
import { ResourceStatusCell } from '../Shared/ResourceStatusCell.tsx';
1213

1314
export default function FluxList() {
1415
const {
15-
data: repoData,
16+
data: gitReposData,
1617
error: repoErr,
1718
isLoading: repoIsLoading,
18-
} = useResource(FluxGitRepo); //404 if component not enabled
19+
} = useResource(FluxRequest); //404 if component not enabled
1920
const {
2021
data: kustmizationData,
2122
error: kustomizationErr,
@@ -24,43 +25,145 @@ export default function FluxList() {
2425

2526
const { t } = useTranslation();
2627

27-
if (repoErr) {
28-
return <IllustratedError error={repoErr} />;
28+
interface CellData<T> {
29+
cell: {
30+
value: T | null; // null for grouping rows
31+
row: {
32+
original?: FluxRow; // missing for grouping rows
33+
};
34+
};
2935
}
30-
if (kustomizationErr) {
31-
return <IllustratedError error={kustomizationErr} />;
36+
37+
type FluxRow = {
38+
name: string;
39+
created: string;
40+
isReady: boolean;
41+
statusUpdateTime?: string;
42+
};
43+
44+
if (repoErr || kustomizationErr) {
45+
return (
46+
<IllustratedError
47+
error={repoErr || kustomizationErr}
48+
title={t('FluxList.noFluxError')}
49+
/>
50+
);
3251
}
3352

34-
const columns: AnalyticalTableColumnDefinition[] = [
53+
const gitReposColumns: AnalyticalTableColumnDefinition[] = [
3554
{
3655
Header: t('FluxList.tableNameHeader'),
37-
accessor: 'metadata.name',
56+
accessor: 'name',
3857
},
3958
{
4059
Header: t('FluxList.tableStatusHeader'),
41-
accessor: 'status.usages',
60+
accessor: 'status',
61+
Cell: (cellData: CellData<FluxRow['isReady']>) =>
62+
cellData.cell.row.original?.isReady != null ? (
63+
<ResourceStatusCell
64+
value={cellData.cell.row.original?.isReady}
65+
transitionTime={
66+
cellData.cell.row.original?.statusUpdateTime
67+
? cellData.cell.row.original?.statusUpdateTime
68+
: ''
69+
}
70+
/>
71+
) : null,
72+
},
73+
{
74+
Header: t('FluxList.tableVersionHeader'),
75+
accessor: 'revision',
4276
},
4377
{
4478
Header: t('FluxList.tableCreatedHeader'),
45-
accessor: 'metadata.creationTimestamp',
46-
Cell: (props: any) => <ReactTimeAgo date={new Date(props.cell.value)} />,
79+
accessor: 'created',
4780
},
4881
];
4982

83+
const kustomizationsColumns: AnalyticalTableColumnDefinition[] = [
84+
{
85+
Header: t('FluxList.tableNameHeader'),
86+
accessor: 'name',
87+
},
88+
{
89+
Header: t('FluxList.tableStatusHeader'),
90+
accessor: 'status',
91+
Cell: (cellData: CellData<FluxRow['isReady']>) =>
92+
cellData.cell.row.original?.isReady != null ? (
93+
<ResourceStatusCell
94+
value={cellData.cell.row.original?.isReady}
95+
transitionTime={
96+
cellData.cell.row.original?.statusUpdateTime
97+
? cellData.cell.row.original?.statusUpdateTime
98+
: ''
99+
}
100+
/>
101+
) : null,
102+
},
103+
{
104+
Header: t('FluxList.tableCreatedHeader'),
105+
accessor: 'created',
106+
},
107+
];
108+
109+
const gitReposRows: FluxRow[] =
110+
gitReposData?.items?.map((item) => {
111+
return {
112+
name: item.metadata.name,
113+
isReady:
114+
item.status.conditions.find((x) => x.type === 'Ready')?.status ===
115+
'True',
116+
statusUpdateTime: item.status.conditions.find((x) => x.type === 'Ready')
117+
?.lastTransitionTime,
118+
revision: shortenCommitHash(item.status.artifact.revision),
119+
created: timeAgo.format(new Date(item.metadata.creationTimestamp)),
120+
};
121+
}) ?? [];
122+
123+
const kustomizationsRows: FluxRow[] =
124+
kustmizationData?.items?.map((item) => {
125+
return {
126+
name: item.metadata.name,
127+
isReady:
128+
item.status.conditions.find((x) => x.type === 'Ready')?.status ===
129+
'True',
130+
statusUpdateTime: item.status.conditions.find((x) => x.type === 'Ready')
131+
?.lastTransitionTime,
132+
created: timeAgo.format(new Date(item.metadata.creationTimestamp)),
133+
};
134+
}) ?? [];
135+
50136
return (
51137
<>
52-
<Title level="H4">Git Repos</Title>
53-
<ConfiguredAnalyticstable
54-
columns={columns}
55-
isLoading={repoIsLoading}
56-
data={repoData ?? []}
57-
/>
58-
<Title level="H4">Kustomizations</Title>
59-
<ConfiguredAnalyticstable
60-
columns={columns}
61-
isLoading={kustomizationIsLoading}
62-
data={kustmizationData ?? []}
63-
/>
138+
{' '}
139+
<div className="crossplane-table-element">
140+
<Title level="H4">{t('FluxList.gitOpsTitle')}</Title>
141+
<ConfiguredAnalyticstable
142+
columns={gitReposColumns}
143+
isLoading={repoIsLoading}
144+
data={gitReposRows}
145+
/>
146+
</div>
147+
<div className="crossplane-table-element">
148+
<Title level="H4">{t('FluxList.kustomizationsTitle')}</Title>
149+
<ConfiguredAnalyticstable
150+
columns={kustomizationsColumns}
151+
isLoading={kustomizationIsLoading}
152+
data={kustomizationsRows}
153+
/>
154+
</div>
64155
</>
65156
);
66157
}
158+
159+
function shortenCommitHash(commitHash: string): string {
160+
//example hash: master@sha1:b3396adb98a6a0f5eeedd1a600beaf5e954a1f28
161+
const match = commitHash.match(/^([a-zA-Z0-9-_]+)@sha1:([a-f0-9]{40})/);
162+
163+
if (match && match[2]) {
164+
return `${match[1]}@${match[2].slice(0, 7)}`;
165+
}
166+
167+
//example output : master@b3396ad
168+
return commitHash;
169+
}
Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,32 @@
11
import { Resource } from '../resource';
22

3-
export const FluxGitRepo: Resource<any> = {
3+
export type GitReposResponse = {
4+
items: [
5+
{
6+
spec: {
7+
package: string;
8+
};
9+
kind: string;
10+
metadata: {
11+
name: string;
12+
creationTimestamp: string;
13+
};
14+
status: {
15+
artifact: {
16+
revision: string;
17+
};
18+
conditions: [
19+
{
20+
status: string;
21+
type: string;
22+
lastTransitionTime: string;
23+
},
24+
];
25+
};
26+
},
27+
];
28+
};
29+
30+
export const FluxRequest: Resource<GitReposResponse> = {
431
path: '/apis/source.toolkit.fluxcd.io/v1/gitrepositories',
5-
jq: '[.items[]]',
632
};
Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,32 @@
11
import { Resource } from '../resource';
22

3-
export const FluxKustomization: Resource<any> = {
3+
export type KustomizationsResponse = {
4+
items: [
5+
{
6+
spec: {
7+
package: string;
8+
};
9+
kind: string;
10+
metadata: {
11+
name: string;
12+
creationTimestamp: string;
13+
};
14+
status: {
15+
artifact: {
16+
revision: string;
17+
};
18+
conditions: [
19+
{
20+
status: string;
21+
type: string;
22+
lastTransitionTime: string;
23+
},
24+
];
25+
};
26+
},
27+
];
28+
};
29+
30+
export const FluxKustomization: Resource<KustomizationsResponse> = {
431
path: '/apis/kustomize.toolkit.fluxcd.io/v1/kustomizations',
5-
jq: '[.items[]]',
632
};

0 commit comments

Comments
 (0)