Skip to content

Commit

Permalink
Add route and header for aws oidc dash
Browse files Browse the repository at this point in the history
  • Loading branch information
michellescripts committed Nov 19, 2024
1 parent 31df14d commit 913540d
Show file tree
Hide file tree
Showing 13 changed files with 550 additions and 34 deletions.
7 changes: 6 additions & 1 deletion web/packages/design/src/Label/Label.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,12 @@ const kind = ({ kind, theme }: { kind?: LabelKind; theme: Theme }) => {
};
};

type LabelKind = 'primary' | 'secondary' | 'warning' | 'danger' | 'success';
export type LabelKind =
| 'primary'
| 'secondary'
| 'warning'
| 'danger'
| 'success';

interface LabelProps extends SpaceProps {
kind?: LabelKind;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import Label, { Primary, Secondary, Warning, Danger } from './Label';
import Label, { Danger, Primary, Secondary, Warning } from './Label';

export default Label;
export { Primary, Secondary, Warning, Danger };
export type { LabelKind } from './Label';
41 changes: 9 additions & 32 deletions web/packages/teleport/src/Integrations/IntegrationList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,18 @@ import useStickyClusterId from 'teleport/useStickyClusterId';
import api from 'teleport/services/api';

import {
ExternalAuditStorageIntegration,
getStatusCodeDescription,
getStatusCodeTitle,
Integration,
IntegrationStatusCode,
IntegrationKind,
IntegrationStatusCode,
Plugin,
ExternalAuditStorageIntegration,
} from 'teleport/services/integrations';
import cfg from 'teleport/config';

import { getStatus } from 'teleport/Integrations/helpers';

import { ExternalAuditStorageOpType } from './Operations/useIntegrationOperation';

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

type IntegrationLike = Integration | Plugin | ExternalAuditStorageIntegration;
export type IntegrationLike =
| Integration
| Plugin
| ExternalAuditStorageIntegration;

export function IntegrationList(props: Props<IntegrationLike>) {
const history = useHistory();
Expand Down Expand Up @@ -254,40 +259,12 @@ const StatusCell = ({ item }: { item: IntegrationLike }) => {
);
};

enum Status {
export enum Status {
Success,
Warning,
Error,
}

function getStatus(item: IntegrationLike): Status | null {
if (item.resourceType === 'integration') {
return Status.Success;
}

if (item.resourceType === 'external-audit-storage') {
switch (item.statusCode) {
case IntegrationStatusCode.Draft:
return Status.Warning;
default:
return Status.Success;
}
}

switch (item.statusCode) {
case IntegrationStatusCode.Unknown:
return null;
case IntegrationStatusCode.Running:
return Status.Success;
case IntegrationStatusCode.SlackNotInChannel:
return Status.Warning;
case IntegrationStatusCode.Draft:
return Status.Warning;
default:
return Status.Error;
}
}

const StatusLight = styled(Box)<{ status: Status }>`
border-radius: 50%;
margin-right: 4px;
Expand Down
5 changes: 5 additions & 0 deletions web/packages/teleport/src/Integrations/IntegrationStatus.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,16 @@ import React from 'react';
import { useParams } from 'react-router';

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

export function IntegrationStatus() {
const { type: integrationType } = useParams<{
type: PluginKind | IntegrationKind;
}>();

if (integrationType === 'aws-oidc') {
return <AwsOidcRoutes />;
}

return <>Status for integration type {integrationType} is not supported</>;
}
93 changes: 93 additions & 0 deletions web/packages/teleport/src/Integrations/helpers.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/**
* Teleport
* Copyright (C) 2024 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import {
Integration,
IntegrationStatusCode,
} from 'teleport/services/integrations';
import { getStatus, getStatusAndLabel } from 'teleport/Integrations/helpers';
import { IntegrationLike, Status } from 'teleport/Integrations/IntegrationList';

test.each`
type | code | expected
${'integration'} | ${IntegrationStatusCode.Draft} | ${Status.Success}
${'integration'} | ${IntegrationStatusCode.Running} | ${Status.Success}
${'integration'} | ${IntegrationStatusCode.Unauthorized} | ${Status.Success}
${'integration'} | ${IntegrationStatusCode.SlackNotInChannel} | ${Status.Success}
${'integration'} | ${IntegrationStatusCode.Unknown} | ${Status.Success}
${'integration'} | ${IntegrationStatusCode.OtherError} | ${Status.Success}
${'external-audit-storage'} | ${IntegrationStatusCode.Draft} | ${Status.Warning}
${'external-audit-storage'} | ${IntegrationStatusCode.Running} | ${Status.Success}
${'external-audit-storage'} | ${IntegrationStatusCode.Unauthorized} | ${Status.Success}
${'external-audit-storage'} | ${IntegrationStatusCode.SlackNotInChannel} | ${Status.Success}
${'external-audit-storage'} | ${IntegrationStatusCode.Unknown} | ${Status.Success}
${'external-audit-storage'} | ${IntegrationStatusCode.OtherError} | ${Status.Success}
${'any'} | ${IntegrationStatusCode.Draft} | ${Status.Warning}
${'any'} | ${IntegrationStatusCode.Running} | ${Status.Success}
${'any'} | ${IntegrationStatusCode.Unauthorized} | ${Status.Error}
${'any'} | ${IntegrationStatusCode.SlackNotInChannel} | ${Status.Warning}
${'any'} | ${IntegrationStatusCode.Unknown} | ${null}
${'any'} | ${IntegrationStatusCode.OtherError} | ${Status.Error}
`(
'getStatus type $type with code $code returns $expected',
async ({ type, code, expected }) => {
const item: IntegrationLike = {
name: '',
kind: undefined,
resourceType: type,
statusCode: code,
};
const status = getStatus(item);
expect(status).toBe(expected);
}
);

test.each`
type | code | expectedLabelKind | expectedTitle
${'integration'} | ${IntegrationStatusCode.Draft} | ${'success'} | ${'Draft'}
${'integration'} | ${IntegrationStatusCode.Running} | ${'success'} | ${'Running'}
${'integration'} | ${IntegrationStatusCode.Unauthorized} | ${'success'} | ${'Unauthorized'}
${'integration'} | ${IntegrationStatusCode.SlackNotInChannel} | ${'success'} | ${'Bot not invited to channel'}
${'integration'} | ${IntegrationStatusCode.Unknown} | ${'success'} | ${'Unknown'}
${'integration'} | ${IntegrationStatusCode.OtherError} | ${'success'} | ${'Unknown error'}
${'external-audit-storage'} | ${IntegrationStatusCode.Draft} | ${'warning'} | ${'Draft'}
${'external-audit-storage'} | ${IntegrationStatusCode.Running} | ${'success'} | ${'Running'}
${'external-audit-storage'} | ${IntegrationStatusCode.Unauthorized} | ${'success'} | ${'Unauthorized'}
${'external-audit-storage'} | ${IntegrationStatusCode.SlackNotInChannel} | ${'success'} | ${'Bot not invited to channel'}
${'external-audit-storage'} | ${IntegrationStatusCode.Unknown} | ${'success'} | ${'Unknown'}
${'external-audit-storage'} | ${IntegrationStatusCode.OtherError} | ${'success'} | ${'Unknown error'}
${'any'} | ${IntegrationStatusCode.Draft} | ${'warning'} | ${'Draft'}
${'any'} | ${IntegrationStatusCode.Running} | ${'success'} | ${'Running'}
${'any'} | ${IntegrationStatusCode.Unauthorized} | ${'danger'} | ${'Unauthorized'}
${'any'} | ${IntegrationStatusCode.SlackNotInChannel} | ${'warning'} | ${'Bot not invited to channel'}
${'any'} | ${IntegrationStatusCode.Unknown} | ${'secondary'} | ${'Unknown'}
${'any'} | ${IntegrationStatusCode.OtherError} | ${'danger'} | ${'Unknown error'}
`(
'getStatusAndLabel type $type with code $code returns expected',
async ({ type, code, expectedLabelKind, expectedTitle }) => {
const item: Integration = {
name: '',
kind: undefined,
resourceType: type,
statusCode: code,
};
const status = getStatusAndLabel(item);
expect(status.status).toBe(expectedTitle);
expect(status.labelKind).toBe(expectedLabelKind);
}
);
74 changes: 74 additions & 0 deletions web/packages/teleport/src/Integrations/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/**
* Teleport
* Copyright (C) 2024 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import { LabelKind } from 'design/Label';

import {
getStatusCodeTitle,
Integration,
IntegrationStatusCode,
} from 'teleport/services/integrations';
import { IntegrationLike, Status } from 'teleport/Integrations/IntegrationList';

export function getStatus(item: IntegrationLike): Status {
if (item.resourceType === 'integration') {
return Status.Success;
}

if (item.resourceType === 'external-audit-storage') {
switch (item.statusCode) {
case IntegrationStatusCode.Draft:
return Status.Warning;
default:
return Status.Success;
}
}

switch (item.statusCode) {
case IntegrationStatusCode.Unknown:
return null;
case IntegrationStatusCode.Running:
return Status.Success;
case IntegrationStatusCode.SlackNotInChannel:
return Status.Warning;
case IntegrationStatusCode.Draft:
return Status.Warning;
default:
return Status.Error;
}
}

export function getStatusAndLabel(integration: Integration): {
labelKind: LabelKind;
status: string;
} {
const modifiedStatus = getStatus(integration);
const statusCode = integration.statusCode;
const title = getStatusCodeTitle(statusCode);

switch (modifiedStatus) {
case Status.Success:
return { labelKind: 'success', status: title };
case Status.Error:
return { labelKind: 'danger', status: title };
case Status.Warning:
return { labelKind: 'warning', status: title };
default:
return { labelKind: 'secondary', status: title };
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/**
* Teleport
* Copyright (C) 2024 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import React from 'react';

import { AwsOidcDashboard } from 'teleport/Integrations/status/AwsOidc/AwsOidcDashboard';
import { MockAwsOidcStatusProvider } from 'teleport/Integrations/status/AwsOidc/testHelpers/mockAwsOidcStatusProvider';

export default {
title: 'Teleport/Integrations/AwsOidc',
};

export function Dashboard() {
return (
<MockAwsOidcStatusProvider
value={{
attempt: {
status: '',
data: {
resourceType: 'integration',
name: 'integration-one',
kind: 'aws-oidc',
spec: {
roleArn: 'arn:aws:iam::111456789011:role/bar',
},
statusCode: 1,
},
statusText: '',
},
}}
>
<AwsOidcDashboard />
</MockAwsOidcStatusProvider>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/**
* Teleport
* Copyright (C) 2024 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import React from 'react';
import { render, screen } from 'design/utils/testing';

import { AwsOidcDashboard } from 'teleport/Integrations/status/AwsOidc/AwsOidcDashboard';
import { MockAwsOidcStatusProvider } from 'teleport/Integrations/status/AwsOidc/testHelpers/mockAwsOidcStatusProvider';

test('renders header', () => {
render(
<MockAwsOidcStatusProvider
value={{
attempt: {
status: '',
data: {
resourceType: 'integration',
name: 'integration-one',
kind: 'aws-oidc',
spec: {
roleArn: 'arn:aws:iam::111456789011:role/bar',
},
statusCode: 1,
},
statusText: '',
},
}}
>
<AwsOidcDashboard />
</MockAwsOidcStatusProvider>
);

expect(screen.getByRole('link', { name: 'back' })).toHaveAttribute(
'href',
'/web/integrations'
);
expect(screen.getByText('integration-one')).toBeInTheDocument();
expect(screen.getByLabelText('status')).toHaveAttribute('kind', 'success');
expect(screen.getByLabelText('status')).toHaveTextContent('Running');
});
Loading

0 comments on commit 913540d

Please sign in to comment.