Skip to content

Commit ed31ef8

Browse files
Add route and header for aws oidc dash (#49163)
1 parent dc15625 commit ed31ef8

File tree

13 files changed

+552
-34
lines changed

13 files changed

+552
-34
lines changed

web/packages/design/src/Label/Label.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,12 @@ const kind = ({ kind, theme }: { kind?: LabelKind; theme: Theme }) => {
5959
};
6060
};
6161

62-
type LabelKind = 'primary' | 'secondary' | 'warning' | 'danger' | 'success';
62+
export type LabelKind =
63+
| 'primary'
64+
| 'secondary'
65+
| 'warning'
66+
| 'danger'
67+
| 'success';
6368

6469
interface LabelProps extends SpaceProps {
6570
kind?: LabelKind;

web/packages/design/src/Label/index.js renamed to web/packages/design/src/Label/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
* along with this program. If not, see <http://www.gnu.org/licenses/>.
1717
*/
1818

19-
import Label, { Primary, Secondary, Warning, Danger } from './Label';
19+
import Label, { Danger, Primary, Secondary, Warning } from './Label';
20+
2021
export default Label;
2122
export { Primary, Secondary, Warning, Danger };
23+
export type { LabelKind } from './Label';

web/packages/teleport/src/Integrations/IntegrationList.tsx

Lines changed: 9 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,18 @@ import useStickyClusterId from 'teleport/useStickyClusterId';
3333
import api from 'teleport/services/api';
3434

3535
import {
36+
ExternalAuditStorageIntegration,
3637
getStatusCodeDescription,
3738
getStatusCodeTitle,
3839
Integration,
39-
IntegrationStatusCode,
4040
IntegrationKind,
41+
IntegrationStatusCode,
4142
Plugin,
42-
ExternalAuditStorageIntegration,
4343
} from 'teleport/services/integrations';
4444
import cfg from 'teleport/config';
4545

46+
import { getStatus } from 'teleport/Integrations/helpers';
47+
4648
import { ExternalAuditStorageOpType } from './Operations/useIntegrationOperation';
4749

4850
type Props<IntegrationLike> = {
@@ -55,7 +57,10 @@ type Props<IntegrationLike> = {
5557
onDeleteExternalAuditStorage?(opType: ExternalAuditStorageOpType): void;
5658
};
5759

58-
type IntegrationLike = Integration | Plugin | ExternalAuditStorageIntegration;
60+
export type IntegrationLike =
61+
| Integration
62+
| Plugin
63+
| ExternalAuditStorageIntegration;
5964

6065
export function IntegrationList(props: Props<IntegrationLike>) {
6166
const history = useHistory();
@@ -254,40 +259,12 @@ const StatusCell = ({ item }: { item: IntegrationLike }) => {
254259
);
255260
};
256261

257-
enum Status {
262+
export enum Status {
258263
Success,
259264
Warning,
260265
Error,
261266
}
262267

263-
function getStatus(item: IntegrationLike): Status | null {
264-
if (item.resourceType === 'integration') {
265-
return Status.Success;
266-
}
267-
268-
if (item.resourceType === 'external-audit-storage') {
269-
switch (item.statusCode) {
270-
case IntegrationStatusCode.Draft:
271-
return Status.Warning;
272-
default:
273-
return Status.Success;
274-
}
275-
}
276-
277-
switch (item.statusCode) {
278-
case IntegrationStatusCode.Unknown:
279-
return null;
280-
case IntegrationStatusCode.Running:
281-
return Status.Success;
282-
case IntegrationStatusCode.SlackNotInChannel:
283-
return Status.Warning;
284-
case IntegrationStatusCode.Draft:
285-
return Status.Warning;
286-
default:
287-
return Status.Error;
288-
}
289-
}
290-
291268
const StatusLight = styled(Box)<{ status: Status }>`
292269
border-radius: 50%;
293270
margin-right: 4px;

web/packages/teleport/src/Integrations/IntegrationStatus.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,16 @@
1919
import { useParams } from 'react-router';
2020

2121
import { IntegrationKind, PluginKind } from 'teleport/services/integrations';
22+
import { AwsOidcRoutes } from 'teleport/Integrations/status/AwsOidc/AwsOidcRoutes';
2223

2324
export function IntegrationStatus() {
2425
const { type: integrationType } = useParams<{
2526
type: PluginKind | IntegrationKind;
2627
}>();
2728

29+
if (integrationType === 'aws-oidc') {
30+
return <AwsOidcRoutes />;
31+
}
32+
2833
return <>Status for integration type {integrationType} is not supported</>;
2934
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/**
2+
* Teleport
3+
* Copyright (C) 2024 Gravitational, Inc.
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU Affero General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU Affero General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Affero General Public License
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
19+
import {
20+
Integration,
21+
IntegrationStatusCode,
22+
} from 'teleport/services/integrations';
23+
import { getStatus, getStatusAndLabel } from 'teleport/Integrations/helpers';
24+
import { IntegrationLike, Status } from 'teleport/Integrations/IntegrationList';
25+
26+
test.each`
27+
type | code | expected
28+
${'integration'} | ${IntegrationStatusCode.Draft} | ${Status.Success}
29+
${'integration'} | ${IntegrationStatusCode.Running} | ${Status.Success}
30+
${'integration'} | ${IntegrationStatusCode.Unauthorized} | ${Status.Success}
31+
${'integration'} | ${IntegrationStatusCode.SlackNotInChannel} | ${Status.Success}
32+
${'integration'} | ${IntegrationStatusCode.Unknown} | ${Status.Success}
33+
${'integration'} | ${IntegrationStatusCode.OtherError} | ${Status.Success}
34+
${'external-audit-storage'} | ${IntegrationStatusCode.Draft} | ${Status.Warning}
35+
${'external-audit-storage'} | ${IntegrationStatusCode.Running} | ${Status.Success}
36+
${'external-audit-storage'} | ${IntegrationStatusCode.Unauthorized} | ${Status.Success}
37+
${'external-audit-storage'} | ${IntegrationStatusCode.SlackNotInChannel} | ${Status.Success}
38+
${'external-audit-storage'} | ${IntegrationStatusCode.Unknown} | ${Status.Success}
39+
${'external-audit-storage'} | ${IntegrationStatusCode.OtherError} | ${Status.Success}
40+
${'any'} | ${IntegrationStatusCode.Draft} | ${Status.Warning}
41+
${'any'} | ${IntegrationStatusCode.Running} | ${Status.Success}
42+
${'any'} | ${IntegrationStatusCode.Unauthorized} | ${Status.Error}
43+
${'any'} | ${IntegrationStatusCode.SlackNotInChannel} | ${Status.Warning}
44+
${'any'} | ${IntegrationStatusCode.Unknown} | ${null}
45+
${'any'} | ${IntegrationStatusCode.OtherError} | ${Status.Error}
46+
`(
47+
'getStatus type $type with code $code returns $expected',
48+
async ({ type, code, expected }) => {
49+
const item: IntegrationLike = {
50+
name: '',
51+
kind: undefined,
52+
resourceType: type,
53+
statusCode: code,
54+
};
55+
const status = getStatus(item);
56+
expect(status).toBe(expected);
57+
}
58+
);
59+
60+
test.each`
61+
type | code | expectedLabelKind | expectedTitle
62+
${'integration'} | ${IntegrationStatusCode.Draft} | ${'success'} | ${'Draft'}
63+
${'integration'} | ${IntegrationStatusCode.Running} | ${'success'} | ${'Running'}
64+
${'integration'} | ${IntegrationStatusCode.Unauthorized} | ${'success'} | ${'Unauthorized'}
65+
${'integration'} | ${IntegrationStatusCode.SlackNotInChannel} | ${'success'} | ${'Bot not invited to channel'}
66+
${'integration'} | ${IntegrationStatusCode.Unknown} | ${'success'} | ${'Unknown'}
67+
${'integration'} | ${IntegrationStatusCode.OtherError} | ${'success'} | ${'Unknown error'}
68+
${'external-audit-storage'} | ${IntegrationStatusCode.Draft} | ${'warning'} | ${'Draft'}
69+
${'external-audit-storage'} | ${IntegrationStatusCode.Running} | ${'success'} | ${'Running'}
70+
${'external-audit-storage'} | ${IntegrationStatusCode.Unauthorized} | ${'success'} | ${'Unauthorized'}
71+
${'external-audit-storage'} | ${IntegrationStatusCode.SlackNotInChannel} | ${'success'} | ${'Bot not invited to channel'}
72+
${'external-audit-storage'} | ${IntegrationStatusCode.Unknown} | ${'success'} | ${'Unknown'}
73+
${'external-audit-storage'} | ${IntegrationStatusCode.OtherError} | ${'success'} | ${'Unknown error'}
74+
${'any'} | ${IntegrationStatusCode.Draft} | ${'warning'} | ${'Draft'}
75+
${'any'} | ${IntegrationStatusCode.Running} | ${'success'} | ${'Running'}
76+
${'any'} | ${IntegrationStatusCode.Unauthorized} | ${'danger'} | ${'Unauthorized'}
77+
${'any'} | ${IntegrationStatusCode.SlackNotInChannel} | ${'warning'} | ${'Bot not invited to channel'}
78+
${'any'} | ${IntegrationStatusCode.Unknown} | ${'secondary'} | ${'Unknown'}
79+
${'any'} | ${IntegrationStatusCode.OtherError} | ${'danger'} | ${'Unknown error'}
80+
`(
81+
'getStatusAndLabel type $type with code $code returns expected',
82+
async ({ type, code, expectedLabelKind, expectedTitle }) => {
83+
const item: Integration = {
84+
name: '',
85+
kind: undefined,
86+
resourceType: type,
87+
statusCode: code,
88+
};
89+
const status = getStatusAndLabel(item);
90+
expect(status.status).toBe(expectedTitle);
91+
expect(status.labelKind).toBe(expectedLabelKind);
92+
}
93+
);
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/**
2+
* Teleport
3+
* Copyright (C) 2024 Gravitational, Inc.
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU Affero General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU Affero General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Affero General Public License
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
19+
import { LabelKind } from 'design/Label';
20+
21+
import {
22+
getStatusCodeTitle,
23+
Integration,
24+
IntegrationStatusCode,
25+
} from 'teleport/services/integrations';
26+
import { IntegrationLike, Status } from 'teleport/Integrations/IntegrationList';
27+
28+
export function getStatus(item: IntegrationLike): Status {
29+
if (item.resourceType === 'integration') {
30+
return Status.Success;
31+
}
32+
33+
if (item.resourceType === 'external-audit-storage') {
34+
switch (item.statusCode) {
35+
case IntegrationStatusCode.Draft:
36+
return Status.Warning;
37+
default:
38+
return Status.Success;
39+
}
40+
}
41+
42+
switch (item.statusCode) {
43+
case IntegrationStatusCode.Unknown:
44+
return null;
45+
case IntegrationStatusCode.Running:
46+
return Status.Success;
47+
case IntegrationStatusCode.SlackNotInChannel:
48+
return Status.Warning;
49+
case IntegrationStatusCode.Draft:
50+
return Status.Warning;
51+
default:
52+
return Status.Error;
53+
}
54+
}
55+
56+
export function getStatusAndLabel(integration: Integration): {
57+
labelKind: LabelKind;
58+
status: string;
59+
} {
60+
const modifiedStatus = getStatus(integration);
61+
const statusCode = integration.statusCode;
62+
const title = getStatusCodeTitle(statusCode);
63+
64+
switch (modifiedStatus) {
65+
case Status.Success:
66+
return { labelKind: 'success', status: title };
67+
case Status.Error:
68+
return { labelKind: 'danger', status: title };
69+
case Status.Warning:
70+
return { labelKind: 'warning', status: title };
71+
default:
72+
return { labelKind: 'secondary', status: title };
73+
}
74+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/**
2+
* Teleport
3+
* Copyright (C) 2024 Gravitational, Inc.
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU Affero General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU Affero General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Affero General Public License
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
19+
import React from 'react';
20+
21+
import { AwsOidcDashboard } from 'teleport/Integrations/status/AwsOidc/AwsOidcDashboard';
22+
import { MockAwsOidcStatusProvider } from 'teleport/Integrations/status/AwsOidc/testHelpers/mockAwsOidcStatusProvider';
23+
import { IntegrationKind } from 'teleport/services/integrations';
24+
25+
export default {
26+
title: 'Teleport/Integrations/AwsOidc',
27+
};
28+
29+
export function Dashboard() {
30+
return (
31+
<MockAwsOidcStatusProvider
32+
value={{
33+
attempt: {
34+
status: 'success',
35+
data: {
36+
resourceType: 'integration',
37+
name: 'integration-one',
38+
kind: IntegrationKind.AwsOidc,
39+
spec: {
40+
roleArn: 'arn:aws:iam::111456789011:role/bar',
41+
},
42+
statusCode: 1,
43+
},
44+
statusText: '',
45+
},
46+
}}
47+
>
48+
<AwsOidcDashboard />
49+
</MockAwsOidcStatusProvider>
50+
);
51+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/**
2+
* Teleport
3+
* Copyright (C) 2024 Gravitational, Inc.
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU Affero General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU Affero General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Affero General Public License
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
19+
import React from 'react';
20+
import { render, screen } from 'design/utils/testing';
21+
22+
import { AwsOidcDashboard } from 'teleport/Integrations/status/AwsOidc/AwsOidcDashboard';
23+
import { MockAwsOidcStatusProvider } from 'teleport/Integrations/status/AwsOidc/testHelpers/mockAwsOidcStatusProvider';
24+
import { IntegrationKind } from 'teleport/services/integrations';
25+
26+
test('renders header', () => {
27+
render(
28+
<MockAwsOidcStatusProvider
29+
value={{
30+
attempt: {
31+
status: 'success',
32+
data: {
33+
resourceType: 'integration',
34+
name: 'integration-one',
35+
kind: IntegrationKind.AwsOidc,
36+
spec: {
37+
roleArn: 'arn:aws:iam::111456789011:role/bar',
38+
},
39+
statusCode: 1,
40+
},
41+
statusText: '',
42+
},
43+
}}
44+
>
45+
<AwsOidcDashboard />
46+
</MockAwsOidcStatusProvider>
47+
);
48+
49+
expect(screen.getByRole('link', { name: 'back' })).toHaveAttribute(
50+
'href',
51+
'/web/integrations'
52+
);
53+
expect(screen.getByText('integration-one')).toBeInTheDocument();
54+
expect(screen.getByLabelText('status')).toHaveAttribute('kind', 'success');
55+
expect(screen.getByLabelText('status')).toHaveTextContent('Running');
56+
});

0 commit comments

Comments
 (0)