mirror of
https://github.com/Unleash/unleash.git
synced 2025-08-04 13:48:56 +02:00
Create hook for fetching featureMetrics (#414)
* Create hook for fetching featureMetrics
This commit is contained in:
parent
d821224a4b
commit
8a9dc32bad
@ -61,6 +61,7 @@
|
|||||||
"enzyme": "3.11.0",
|
"enzyme": "3.11.0",
|
||||||
"enzyme-adapter-react-16": "1.15.6",
|
"enzyme-adapter-react-16": "1.15.6",
|
||||||
"enzyme-to-json": "3.6.2",
|
"enzyme-to-json": "3.6.2",
|
||||||
|
"fast-json-patch": "3.1.0",
|
||||||
"fetch-mock": "9.11.0",
|
"fetch-mock": "9.11.0",
|
||||||
"http-proxy-middleware": "2.0.1",
|
"http-proxy-middleware": "2.0.1",
|
||||||
"immutable": "4.0.0",
|
"immutable": "4.0.0",
|
||||||
@ -83,8 +84,7 @@
|
|||||||
"sass": "1.42.1",
|
"sass": "1.42.1",
|
||||||
"swr": "1.0.1",
|
"swr": "1.0.1",
|
||||||
"typescript": "4.4.4",
|
"typescript": "4.4.4",
|
||||||
"web-vitals": "2.1.2",
|
"web-vitals": "2.1.2"
|
||||||
"fast-json-patch": "3.1.0"
|
|
||||||
},
|
},
|
||||||
"jest": {
|
"jest": {
|
||||||
"moduleNameMapper": {
|
"moduleNameMapper": {
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import PercentageCircle from '../../../../common/PercentageCircle/PercentageCircle';
|
import PercentageCircle from '../../../../common/PercentageCircle/PercentageCircle';
|
||||||
import { useStyles } from './FeatureEnvironmentMetrics.styles';
|
import { useStyles } from './FeatureEnvironmentMetrics.styles';
|
||||||
import { IEnvironmentMetrics } from '../../../../../interfaces/environments';
|
|
||||||
import PieChartIcon from '@material-ui/icons/PieChart';
|
import PieChartIcon from '@material-ui/icons/PieChart';
|
||||||
import { useMediaQuery } from '@material-ui/core';
|
import { useMediaQuery } from '@material-ui/core';
|
||||||
|
import { IFeatureEnvironmentMetrics } from '../../../../../interfaces/featureToggle';
|
||||||
|
import { parseISO } from 'date-fns';
|
||||||
|
|
||||||
interface IFeatureEnvironmentProps {
|
interface IFeatureEnvironmentProps {
|
||||||
className?: string;
|
className?: string;
|
||||||
primaryMetric?: boolean;
|
primaryMetric?: boolean;
|
||||||
metric: IEnvironmentMetrics;
|
metric: IFeatureEnvironmentMetrics;
|
||||||
}
|
}
|
||||||
|
|
||||||
const FeatureEnvironmentMetrics = ({
|
const FeatureEnvironmentMetrics = ({
|
||||||
@ -21,6 +23,11 @@ const FeatureEnvironmentMetrics = ({
|
|||||||
const containerClasses = classNames(styles.container, className, {
|
const containerClasses = classNames(styles.container, className, {
|
||||||
[styles.primaryMetric]: primaryMetric,
|
[styles.primaryMetric]: primaryMetric,
|
||||||
});
|
});
|
||||||
|
let hour = '';
|
||||||
|
if (metric?.timestamp) {
|
||||||
|
const metricTime = parseISO(metric.timestamp);
|
||||||
|
hour = `since ${metricTime.getHours()}:${metricTime.getMinutes()}`;
|
||||||
|
}
|
||||||
|
|
||||||
const calculatePercentage = () => {
|
const calculatePercentage = () => {
|
||||||
const total = metric.yes + metric.no;
|
const total = metric.yes + metric.no;
|
||||||
@ -52,7 +59,7 @@ const FeatureEnvironmentMetrics = ({
|
|||||||
<div className={containerClasses}>
|
<div className={containerClasses}>
|
||||||
<div className={styles.headerContainer}>
|
<div className={styles.headerContainer}>
|
||||||
<h2 data-loading className={styles.title}>
|
<h2 data-loading className={styles.title}>
|
||||||
Traffic in {metric.name}
|
Traffic in {metric.environment} {hour}
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -75,7 +82,7 @@ const FeatureEnvironmentMetrics = ({
|
|||||||
<div className={containerClasses}>
|
<div className={containerClasses}>
|
||||||
<div className={styles.headerContainer}>
|
<div className={styles.headerContainer}>
|
||||||
<h2 data-loading className={styles.title}>
|
<h2 data-loading className={styles.title}>
|
||||||
Traffic in {metric.name}
|
Traffic in {metric.environment} {hour}
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -1,72 +1,33 @@
|
|||||||
import { useParams } from 'react-router';
|
import { useParams } from 'react-router';
|
||||||
import { useState, useEffect } from 'react';
|
|
||||||
import useFeature from '../../../../../hooks/api/getters/useFeature/useFeature';
|
import useFeature from '../../../../../hooks/api/getters/useFeature/useFeature';
|
||||||
import { IFeatureViewParams } from '../../../../../interfaces/params';
|
import { IFeatureViewParams } from '../../../../../interfaces/params';
|
||||||
import { IEnvironmentMetrics } from '../../../../../interfaces/environments';
|
|
||||||
import FeatureEnvironmentMetrics from '../FeatureEnvironmentMetrics/FeatureEnvironmentMetrics';
|
import FeatureEnvironmentMetrics from '../FeatureEnvironmentMetrics/FeatureEnvironmentMetrics';
|
||||||
import { useStyles } from './FeatureOverviewMetrics.styles';
|
import { useStyles } from './FeatureOverviewMetrics.styles';
|
||||||
|
import useFeatureMetrics from '../../../../../hooks/api/getters/useFeatureMetrics/useFeatureMetrics';
|
||||||
const data = {
|
|
||||||
version: 1,
|
|
||||||
maturity: 'experimental',
|
|
||||||
lastHourUsage: [
|
|
||||||
{
|
|
||||||
environment: 'default',
|
|
||||||
timestamp: '2021-10-07 10:00:00',
|
|
||||||
yes: 250,
|
|
||||||
no: 60,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
environment: 'production',
|
|
||||||
timestamp: '2021-10-07 10:00:00',
|
|
||||||
yes: 200,
|
|
||||||
no: 500,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
environment: 'development',
|
|
||||||
timestamp: '2021-10-07 10:00:00',
|
|
||||||
yes: 0,
|
|
||||||
no: 0,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
seenApplications: ['web', 'backend-api', 'commerce'],
|
|
||||||
};
|
|
||||||
|
|
||||||
const FeatureOverviewMetrics = () => {
|
const FeatureOverviewMetrics = () => {
|
||||||
const styles = useStyles();
|
const styles = useStyles();
|
||||||
const { projectId, featureId } = useParams<IFeatureViewParams>();
|
const { projectId, featureId } = useParams<IFeatureViewParams>();
|
||||||
const { feature } = useFeature(projectId, featureId);
|
const { feature } = useFeature(projectId, featureId);
|
||||||
const [featureMetrics, setFeatureMetrics] = useState<IEnvironmentMetrics[]>(
|
const { metrics } = useFeatureMetrics(projectId, featureId);
|
||||||
[]
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
const featureMetrics = feature?.environments.map(env => {
|
||||||
const featureMetricList = feature?.environments.map(env => {
|
const metric = metrics.lastHourUsage.find(
|
||||||
const metrics = data.lastHourUsage.find(
|
metric => metric.environment === env.name
|
||||||
metric => metric.environment === env.name
|
);
|
||||||
);
|
|
||||||
|
|
||||||
if (!metrics) {
|
|
||||||
return {
|
|
||||||
name: env.name,
|
|
||||||
yes: 0,
|
|
||||||
no: 0,
|
|
||||||
timestamp: '',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (!metric) {
|
||||||
return {
|
return {
|
||||||
name: env.name,
|
environment: env.name,
|
||||||
yes: metrics.yes,
|
yes: 0,
|
||||||
no: metrics.no,
|
no: 0,
|
||||||
timestamp: metrics.timestamp,
|
timestamp: ''
|
||||||
};
|
};
|
||||||
});
|
}
|
||||||
|
|
||||||
|
return metric;
|
||||||
|
});
|
||||||
|
|
||||||
setFeatureMetrics(featureMetricList);
|
|
||||||
/* Update on useSWR metrics change */
|
|
||||||
/* eslint-disable-next-line */
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const renderFeatureMetrics = () => {
|
const renderFeatureMetrics = () => {
|
||||||
if (featureMetrics.length === 0) {
|
if (featureMetrics.length === 0) {
|
||||||
@ -88,14 +49,14 @@ const FeatureOverviewMetrics = () => {
|
|||||||
return (
|
return (
|
||||||
<FeatureEnvironmentMetrics
|
<FeatureEnvironmentMetrics
|
||||||
className={styles.firstContainer}
|
className={styles.firstContainer}
|
||||||
key={metric.name}
|
key={metric.environment}
|
||||||
metric={metric}
|
metric={metric}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<FeatureEnvironmentMetrics
|
<FeatureEnvironmentMetrics
|
||||||
key={metric.name}
|
key={metric.environment}
|
||||||
metric={metric}
|
metric={metric}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@ -109,7 +70,7 @@ const FeatureOverviewMetrics = () => {
|
|||||||
return (
|
return (
|
||||||
<FeatureEnvironmentMetrics
|
<FeatureEnvironmentMetrics
|
||||||
primaryMetric
|
primaryMetric
|
||||||
key={metric.name}
|
key={metric.environment}
|
||||||
metric={metric}
|
metric={metric}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@ -119,7 +80,7 @@ const FeatureOverviewMetrics = () => {
|
|||||||
return (
|
return (
|
||||||
<FeatureEnvironmentMetrics
|
<FeatureEnvironmentMetrics
|
||||||
className={styles.firstContainer}
|
className={styles.firstContainer}
|
||||||
key={metric.name}
|
key={metric.environment}
|
||||||
metric={metric}
|
metric={metric}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@ -127,7 +88,7 @@ const FeatureOverviewMetrics = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<FeatureEnvironmentMetrics
|
<FeatureEnvironmentMetrics
|
||||||
key={metric.name}
|
key={metric.environment}
|
||||||
metric={metric}
|
metric={metric}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -0,0 +1,57 @@
|
|||||||
|
import { formatApiPath } from '../../../../utils/format-path';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import useSWR, { mutate } from 'swr';
|
||||||
|
import { IFeatureMetrics } from '../../../../interfaces/featureToggle';
|
||||||
|
|
||||||
|
interface IUseFeatureMetricsOptions {
|
||||||
|
refreshInterval?: number;
|
||||||
|
revalidateOnFocus?: boolean;
|
||||||
|
revalidateOnReconnect?: boolean;
|
||||||
|
revalidateIfStale?: boolean;
|
||||||
|
revalidateOnMount?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const emptyMetrics = { lastHourUsage: [], seenApplications: [] };
|
||||||
|
|
||||||
|
const useFeatureMetrics = (projectId: string, featureId: string, options: IUseFeatureMetricsOptions = {}) => {
|
||||||
|
const fetcher = async () => {
|
||||||
|
const path = formatApiPath(`api/admin/client-metrics/features/${featureId}`);
|
||||||
|
const res = await fetch(path, {
|
||||||
|
method: 'GET'
|
||||||
|
});
|
||||||
|
if (res.ok) {
|
||||||
|
return res.json();
|
||||||
|
} else {
|
||||||
|
return emptyMetrics;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const FEATURE_METRICS_CACHE_KEY = `${projectId}_${featureId}_metrics`;
|
||||||
|
const { data, error } = useSWR<IFeatureMetrics>(
|
||||||
|
FEATURE_METRICS_CACHE_KEY,
|
||||||
|
fetcher,
|
||||||
|
{
|
||||||
|
...options
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const [loading, setLoading] = useState(!error && !data);
|
||||||
|
|
||||||
|
const refetch = () => {
|
||||||
|
mutate(FEATURE_METRICS_CACHE_KEY);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setLoading(!error && !data);
|
||||||
|
}, [data, error]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
metrics: data || emptyMetrics,
|
||||||
|
error,
|
||||||
|
loading,
|
||||||
|
refetch,
|
||||||
|
FEATURE_METRICS_CACHE_KEY
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useFeatureMetrics;
|
@ -48,3 +48,17 @@ export interface IPayload {
|
|||||||
name: string;
|
name: string;
|
||||||
value: string;
|
value: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IFeatureEnvironmentMetrics {
|
||||||
|
environment: string;
|
||||||
|
timestamp: string;
|
||||||
|
yes: number;
|
||||||
|
no: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IFeatureMetrics {
|
||||||
|
version: number;
|
||||||
|
maturity: string;
|
||||||
|
lastHourUsage: IFeatureEnvironmentMetrics[],
|
||||||
|
seenApplication: string[]
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user