Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: release #1099

Merged
merged 7 commits into from
Dec 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/terraform.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ jobs:
API_URL=https://gcp9dahm4c.execute-api.ca-central-1.amazonaws.com/test/app
API_AUTH_SECRET=${{ secrets.API_AUTH_SECRET }}
APP_ENV=development
GRAFANA_API_URL=https://sso-grafana-sandbox.apps.gold.devops.gov.bc.ca/api
GRAFANA_API_TOKEN=${{ secrets.DEV_GRAFANA_API_TOKEN }}
TF_STATE_BUCKET=xgr00q-dev-sso-requests
TF_STATE_BUCKET_KEY=css-tf-dev-state
TF_STATE_DYNAMODB_TABLE=xgr00q-dev-sso-requests-state-locking
Expand Down Expand Up @@ -69,6 +71,8 @@ jobs:
API_URL=https://kgodz1zmk2.execute-api.ca-central-1.amazonaws.com/test/app
API_AUTH_SECRET=${{ secrets.API_AUTH_SECRET }}
APP_ENV=production
GRAFANA_API_URL=https://sso-grafana.apps.gold.devops.gov.bc.ca/api
GRAFANA_API_TOKEN=${{ secrets.PROD_GRAFANA_API_TOKEN }}
TF_STATE_BUCKET=xgr00q-prod-sso-requests
TF_STATE_BUCKET_KEY=css-tf-prod-state
TF_STATE_DYNAMODB_TABLE=xgr00q-prod-sso-requests-state-locking
Expand Down Expand Up @@ -209,6 +213,8 @@ jobs:
custom_domain_name="${{ env.CUSTOM_DOMAIN_NAME }}"
uptime_status_domain_name="${{ env.UPTIME_STATUS_DOMAIN_NAME }}"
aws_ecr_uri="${{ env.AWS_ECR_URI }}"
grafana_api_token="${{ env.GRAFANA_API_TOKEN }}"
grafana_api_url="${{ env.GRAFANA_API_URL }}"
include_digital_credential="${{ env.INCLUDE_DIGITAL_CREDENTIAL }}"
EOF

Expand Down
36 changes: 36 additions & 0 deletions app/components/DateTimePicker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import DatePicker, { ReactDatePickerProps } from 'react-datepicker';
import 'react-datepicker/dist/react-datepicker.css';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faCalendar } from '@fortawesome/free-solid-svg-icons';
import { FormLabel } from 'react-bootstrap';

export default function DateTimePicker(props: ReactDatePickerProps & { label: string }) {
return (
<>
<DatePicker
dateFormat="yyyy-MM-dd"
customInput={<DateTimeInput label={props.label} />}
enableTabLoop={false}
popperPlacement="bottom-start"
maxDate={new Date()}
{...props}
/>
</>
);
}

function DateTimeInput({ value, onClick, label }: any) {
return (
<>
<FormLabel style={{ fontWeight: 'bold' }}>{label}</FormLabel>
<div className="input-group">
<input type="text" className="form-control" value={value} onClick={onClick} readOnly />
<div className="input-group-append">
<span className="input-group-text" style={{ height: '100%' }}>
<FontAwesomeIcon icon={faCalendar} />
</span>
</div>
</div>
</>
);
}
5 changes: 5 additions & 0 deletions app/interfaces/Request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,8 @@ export interface ClientRole {
name: string;
composites: string[];
}

export interface EventCountMetric {
event: string;
count: number;
}
4 changes: 4 additions & 0 deletions app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,12 @@
"lodash.tolower": "^4.1.2",
"lodash.trim": "^4.5.1",
"lodash.uniq": "^4.5.0",
"moment": "^2.29.4",
"next": "13.2.1",
"re-resizable": "6.9.9",
"react": "18.2.0",
"react-bootstrap": "2.5.0",
"react-datepicker": "^4.24.0",
"react-dom": "18.2.0",
"react-idle-timer": "^5.7.2",
"react-infinite-scroller": "1.2.6",
Expand All @@ -63,6 +65,7 @@
"react-select": "^5.7.2",
"react-table": "^7.8.0",
"react-typography": "0.16.20",
"recharts": "^2.10.3",
"styled-components": "5.3.5",
"validator": "13.7.0",
"xlsx": "^0.18.5"
Expand Down Expand Up @@ -99,6 +102,7 @@
"@types/lodash.trim": "^4.5.7",
"@types/lodash.uniq": "^4.5.7",
"@types/react": "18.0.21",
"@types/react-datepicker": "^4.19.4",
"@types/react-infinite-scroller": "1.2.3",
"@types/react-jsonschema-form": "1.7.8",
"@types/react-select": "^5.0.1",
Expand Down
184 changes: 184 additions & 0 deletions app/page-partials/my-dashboard/IntegrationInfoTabs/MetricsPanel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
import React, { useCallback, useEffect, useState } from 'react';
import styled from 'styled-components';
import { EventCountMetric, Integration } from 'interfaces/Request';
import { withTopAlert } from 'layout/TopAlert';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Tabs, Tab } from '@bcgov-sso/common-react-components';
import startCase from 'lodash.startcase';
import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, Text, Legend } from 'recharts';
import { getMetrics } from '@app/services/grafana';
import throttle from 'lodash.throttle';
import moment from 'moment';
import DateTimePicker from '@app/components/DateTimePicker';
import { InfoMessage } from '@app/components/MessageBox';
import { Link } from '@button-inc/bcgov-theme';
import { subtractDaysFromDate } from '@app/utils/helpers';

export const DatePickerContainer = styled.div`
height: 100%;
display: flex;
align-items: center;
padding-right: 15px;
& > * {
margin-left: 15px;
}
`;

const Label = styled.label`
margin-bottom: 2px;
`;

const TopMargin = styled.div`
height: var(--field-top-spacing);
`;

const LeftTitle = styled.span`
color: #000;
font-size: 1.1rem;
font-weight: bold;
`;

const PaddedIcon = styled(FontAwesomeIcon)`
margin-right: 20px;
`;

const StyledP = styled.div`
margin-bottom: 5px;
display: flex;
align-items: center;
`;

const StyledHr = styled.hr`
background-color: black;
`;

interface Props {
integration: Integration;
}

const getFormattedDateString = (d: Date) => {
return `${d.getFullYear()}-${d.getMonth() + 1}-${d.getDate()}`;
};

const metricsStartDate = 'December 01, 2023';
const MetricsPanel = ({ integration }: Props) => {
const [environment, setEnvironment] = useState('dev');
const environments = integration?.environments || [];
const [metrics, setMetrics] = useState<EventCountMetric[]>([]);
const [loading, setLoading] = useState(false);
const [fromDate, setFromDate] = useState<Date>(subtractDaysFromDate(14));
const [toDate, setToDate] = useState<Date>(new Date());
let selectedFromDate: Date = new Date();

const handleTabSelect = (key: string) => {
setEnvironment(key);
};

const handleFromDateChange = (val: Date) => {
setFromDate(val);
};

const handleToDateChange = (val: Date) => {
setToDate(val);
};

const fetchMetrics = useCallback(
throttle(async (fromDate: string, toDate: string, environment: string) => {
const [metricsData, err] = await getMetrics(integration?.id as number, environment, fromDate, toDate);

if (err || metricsData.length === 0) {
setMetrics([]);
} else {
setMetrics(metricsData);
}
}),
[integration?.clientId, environment, fromDate, toDate],
);

useEffect(() => {
fetchMetrics(getFormattedDateString(fromDate), getFormattedDateString(toDate), environment);
}, [integration?.clientId, environment, fromDate, toDate]);

return (
<>
<TopMargin />

<div>
<DatePickerContainer>
<DateTimePicker
placeholderText="Start Date"
selected={new Date(fromDate)}
onChange={(date: Date) => handleFromDateChange(date)}
minDate={new Date(metricsStartDate)}
maxDate={toDate}
label="Start Date"
/>
<DateTimePicker
placeholderText="End Date"
selected={new Date(toDate)}
onChange={(date: Date) => handleToDateChange(date)}
minDate={fromDate}
label="End Date"
/>
</DatePickerContainer>
</div>

<Tabs onChange={handleTabSelect} activeKey={environment} tabBarGutter={30} destroyInactiveTabPane={true}>
<br />

{environments.map((env) => (
<Tab key={env} tab={startCase(env)}>
<div style={{ width: '100%', height: 300 }}>
{metrics?.length > 0 ? (
<ResponsiveContainer>
<BarChart
data={metrics}
margin={{
top: 20,
right: 20,
bottom: 30,
left: 20,
}}
>
<CartesianGrid strokeDasharray="3 3" />
<XAxis
dataKey="event"
tick={{ fontSize: 10 }}
label={{ value: 'Events', position: 'insideBottomRight' }}
/>
<YAxis dataKey="count" label={{ value: 'Count', angle: -90, position: 'insideLeft' }} />
<Tooltip />
<Legend />
<Bar
dataKey="count"
fill="#0d6efd"
barSize={30}
label={{ fill: '#0d6efd', fontSize: 20, position: 'top' }}
background={{ fill: '#eee' }}
/>
</BarChart>
</ResponsiveContainer>
) : (
<div style={{ textAlign: 'center' }}>
<Text>No data available yet!</Text>
</div>
)}
</div>
</Tab>
))}
</Tabs>
<InfoMessage>
This tab was released {metricsStartDate}. Please refer to{' '}
<Link
href="https://access.redhat.com/documentation/en-us/red_hat_single_sign-on/7.4/html/server_administration_guide/auditing_and_events#event_types"
external
>
here
</Link>{' '}
for event type details.
</InfoMessage>
</>
);
};

export default withTopAlert(MetricsPanel);
15 changes: 15 additions & 0 deletions app/page-partials/my-dashboard/IntegrationInfoTabs/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import BceidStatusPanel from './BceidStatusPanel';
import GithubStatusPanel from './GithubStatusPanel';
import ServiceAccountRoles from 'page-partials/my-dashboard/ServiceAccountRoles';
import DigitalCredentialPanel from './DigitalCredentialPanel';
import MetricsPanel from './MetricsPanel';

const TabWrapper = styled.div<{ short?: boolean }>`
padding-left: 1rem;
Expand All @@ -44,6 +45,7 @@ const TAB_USER_ROLE_MANAGEMENT = 'user-role-management';
const TAB_SERVICE_ACCOUNT_ROLE_MANAGEMENT = 'service-account-role-management';
const TAB_SECRET = 'secret';
const TAB_HISTORY = 'history';
const TAB_METRICS = 'metrics';

const joinEnvs = (integration: Integration) => {
if (!integration?.environments) return '';
Expand Down Expand Up @@ -210,6 +212,16 @@ const getSecretsTab = ({ integration }: { integration: Integration }) => {
);
};

const getMetricsTab = ({ integration }: { integration: Integration }) => {
return (
<Tab key={TAB_METRICS} tab="Metrics">
<TabWrapper short={false}>
<MetricsPanel integration={integration} />
</TabWrapper>
</Tab>
);
};

const getHistoryTab = ({ integration }: { integration: Integration }) => {
return (
<Tab key={TAB_HISTORY} tab="Change History">
Expand Down Expand Up @@ -327,6 +339,9 @@ function IntegrationInfoTabs({ integration }: Props) {

tabs.push(getHistoryTab({ integration }));
allowedTabs.push(TAB_HISTORY);

tabs.push(getMetricsTab({ integration }));
allowedTabs.push(TAB_METRICS);
}

let activeKey = activeTab;
Expand Down
14 changes: 14 additions & 0 deletions app/services/grafana.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { instance } from './axios';

export const getMetrics = async (id: number, env: string, fromDate?: string, toDate?: string) => {
try {
const result = await instance
.get(`requests/${id}/metrics?env=${env}&fromDate=${fromDate}&toDate=${toDate}`)
.then((res: any) => res?.data);

return [result, null];
} catch (err) {
console.error(err);
return [null, err];
}
};
6 changes: 6 additions & 0 deletions app/utils/helpers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -321,3 +321,9 @@ export const checkIfDigitalCredentialProdApplying = (integration: Integration) =
const prodApplying = checkIfProdApplying(integration, 'digitalCredentialApproved');
return prodApplying;
};

export const subtractDaysFromDate = (days: number) => {
const d = new Date();
d.setDate(d.getDate() - days);
return d;
};
Loading
Loading