mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01: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-adapter-react-16": "1.15.6",
 | 
			
		||||
    "enzyme-to-json": "3.6.2",
 | 
			
		||||
    "fast-json-patch": "3.1.0",
 | 
			
		||||
    "fetch-mock": "9.11.0",
 | 
			
		||||
    "http-proxy-middleware": "2.0.1",
 | 
			
		||||
    "immutable": "4.0.0",
 | 
			
		||||
@ -83,8 +84,7 @@
 | 
			
		||||
    "sass": "1.42.1",
 | 
			
		||||
    "swr": "1.0.1",
 | 
			
		||||
    "typescript": "4.4.4",
 | 
			
		||||
    "web-vitals": "2.1.2",
 | 
			
		||||
    "fast-json-patch": "3.1.0"
 | 
			
		||||
    "web-vitals": "2.1.2"
 | 
			
		||||
  },
 | 
			
		||||
  "jest": {
 | 
			
		||||
    "moduleNameMapper": {
 | 
			
		||||
 | 
			
		||||
@ -1,13 +1,15 @@
 | 
			
		||||
import classNames from 'classnames';
 | 
			
		||||
import PercentageCircle from '../../../../common/PercentageCircle/PercentageCircle';
 | 
			
		||||
import { useStyles } from './FeatureEnvironmentMetrics.styles';
 | 
			
		||||
import { IEnvironmentMetrics } from '../../../../../interfaces/environments';
 | 
			
		||||
import PieChartIcon from '@material-ui/icons/PieChart';
 | 
			
		||||
import { useMediaQuery } from '@material-ui/core';
 | 
			
		||||
import { IFeatureEnvironmentMetrics } from '../../../../../interfaces/featureToggle';
 | 
			
		||||
import { parseISO } from 'date-fns';
 | 
			
		||||
 | 
			
		||||
interface IFeatureEnvironmentProps {
 | 
			
		||||
    className?: string;
 | 
			
		||||
    primaryMetric?: boolean;
 | 
			
		||||
    metric: IEnvironmentMetrics;
 | 
			
		||||
    metric: IFeatureEnvironmentMetrics;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const FeatureEnvironmentMetrics = ({
 | 
			
		||||
@ -21,6 +23,11 @@ const FeatureEnvironmentMetrics = ({
 | 
			
		||||
    const containerClasses = classNames(styles.container, className, {
 | 
			
		||||
        [styles.primaryMetric]: primaryMetric,
 | 
			
		||||
    });
 | 
			
		||||
    let hour = '';
 | 
			
		||||
    if (metric?.timestamp) {
 | 
			
		||||
        const metricTime = parseISO(metric.timestamp);
 | 
			
		||||
        hour = `since ${metricTime.getHours()}:${metricTime.getMinutes()}`;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const calculatePercentage = () => {
 | 
			
		||||
        const total = metric.yes + metric.no;
 | 
			
		||||
@ -52,7 +59,7 @@ const FeatureEnvironmentMetrics = ({
 | 
			
		||||
            <div className={containerClasses}>
 | 
			
		||||
                <div className={styles.headerContainer}>
 | 
			
		||||
                    <h2 data-loading className={styles.title}>
 | 
			
		||||
                        Traffic in {metric.name}
 | 
			
		||||
                        Traffic in {metric.environment} {hour}
 | 
			
		||||
                    </h2>
 | 
			
		||||
                </div>
 | 
			
		||||
 | 
			
		||||
@ -75,7 +82,7 @@ const FeatureEnvironmentMetrics = ({
 | 
			
		||||
        <div className={containerClasses}>
 | 
			
		||||
            <div className={styles.headerContainer}>
 | 
			
		||||
                <h2 data-loading className={styles.title}>
 | 
			
		||||
                    Traffic in {metric.name}
 | 
			
		||||
                    Traffic in {metric.environment} {hour}
 | 
			
		||||
                </h2>
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,72 +1,33 @@
 | 
			
		||||
import { useParams } from 'react-router';
 | 
			
		||||
import { useState, useEffect } from 'react';
 | 
			
		||||
import useFeature from '../../../../../hooks/api/getters/useFeature/useFeature';
 | 
			
		||||
import { IFeatureViewParams } from '../../../../../interfaces/params';
 | 
			
		||||
import { IEnvironmentMetrics } from '../../../../../interfaces/environments';
 | 
			
		||||
import FeatureEnvironmentMetrics from '../FeatureEnvironmentMetrics/FeatureEnvironmentMetrics';
 | 
			
		||||
import { useStyles } from './FeatureOverviewMetrics.styles';
 | 
			
		||||
 | 
			
		||||
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'],
 | 
			
		||||
};
 | 
			
		||||
import useFeatureMetrics from '../../../../../hooks/api/getters/useFeatureMetrics/useFeatureMetrics';
 | 
			
		||||
 | 
			
		||||
const FeatureOverviewMetrics = () => {
 | 
			
		||||
    const styles = useStyles();
 | 
			
		||||
    const { projectId, featureId } = useParams<IFeatureViewParams>();
 | 
			
		||||
    const { feature } = useFeature(projectId, featureId);
 | 
			
		||||
    const [featureMetrics, setFeatureMetrics] = useState<IEnvironmentMetrics[]>(
 | 
			
		||||
        []
 | 
			
		||||
    );
 | 
			
		||||
    const { metrics } = useFeatureMetrics(projectId, featureId);
 | 
			
		||||
 | 
			
		||||
    useEffect(() => {
 | 
			
		||||
        const featureMetricList = feature?.environments.map(env => {
 | 
			
		||||
            const metrics = data.lastHourUsage.find(
 | 
			
		||||
                metric => metric.environment === env.name
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            if (!metrics) {
 | 
			
		||||
                return {
 | 
			
		||||
                    name: env.name,
 | 
			
		||||
                    yes: 0,
 | 
			
		||||
                    no: 0,
 | 
			
		||||
                    timestamp: '',
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
    const featureMetrics = feature?.environments.map(env => {
 | 
			
		||||
        const metric = metrics.lastHourUsage.find(
 | 
			
		||||
            metric => metric.environment === env.name
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        if (!metric) {
 | 
			
		||||
            return {
 | 
			
		||||
                name: env.name,
 | 
			
		||||
                yes: metrics.yes,
 | 
			
		||||
                no: metrics.no,
 | 
			
		||||
                timestamp: metrics.timestamp,
 | 
			
		||||
                environment: env.name,
 | 
			
		||||
                yes: 0,
 | 
			
		||||
                no: 0,
 | 
			
		||||
                timestamp: ''
 | 
			
		||||
            };
 | 
			
		||||
        });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return metric;
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
        setFeatureMetrics(featureMetricList);
 | 
			
		||||
        /* Update on useSWR metrics change */
 | 
			
		||||
        /* eslint-disable-next-line */
 | 
			
		||||
    }, []);
 | 
			
		||||
 | 
			
		||||
    const renderFeatureMetrics = () => {
 | 
			
		||||
        if (featureMetrics.length === 0) {
 | 
			
		||||
@ -88,14 +49,14 @@ const FeatureOverviewMetrics = () => {
 | 
			
		||||
                    return (
 | 
			
		||||
                        <FeatureEnvironmentMetrics
 | 
			
		||||
                            className={styles.firstContainer}
 | 
			
		||||
                            key={metric.name}
 | 
			
		||||
                            key={metric.environment}
 | 
			
		||||
                            metric={metric}
 | 
			
		||||
                        />
 | 
			
		||||
                    );
 | 
			
		||||
                }
 | 
			
		||||
                return (
 | 
			
		||||
                    <FeatureEnvironmentMetrics
 | 
			
		||||
                        key={metric.name}
 | 
			
		||||
                        key={metric.environment}
 | 
			
		||||
                        metric={metric}
 | 
			
		||||
                    />
 | 
			
		||||
                );
 | 
			
		||||
@ -109,7 +70,7 @@ const FeatureOverviewMetrics = () => {
 | 
			
		||||
                    return (
 | 
			
		||||
                        <FeatureEnvironmentMetrics
 | 
			
		||||
                            primaryMetric
 | 
			
		||||
                            key={metric.name}
 | 
			
		||||
                            key={metric.environment}
 | 
			
		||||
                            metric={metric}
 | 
			
		||||
                        />
 | 
			
		||||
                    );
 | 
			
		||||
@ -119,7 +80,7 @@ const FeatureOverviewMetrics = () => {
 | 
			
		||||
                    return (
 | 
			
		||||
                        <FeatureEnvironmentMetrics
 | 
			
		||||
                            className={styles.firstContainer}
 | 
			
		||||
                            key={metric.name}
 | 
			
		||||
                            key={metric.environment}
 | 
			
		||||
                            metric={metric}
 | 
			
		||||
                        />
 | 
			
		||||
                    );
 | 
			
		||||
@ -127,7 +88,7 @@ const FeatureOverviewMetrics = () => {
 | 
			
		||||
 | 
			
		||||
                return (
 | 
			
		||||
                    <FeatureEnvironmentMetrics
 | 
			
		||||
                        key={metric.name}
 | 
			
		||||
                        key={metric.environment}
 | 
			
		||||
                        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;
 | 
			
		||||
    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