123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249 |
- import * as React from 'react';
- import isEqual from 'lodash/isEqual';
- import omitBy from 'lodash/omitBy';
- import {addErrorMessage} from 'sentry/actionCreators/indicator';
- import {doMetricsRequest, DoMetricsRequestOptions} from 'sentry/actionCreators/metrics';
- import {Client, ResponseMeta} from 'sentry/api';
- import {shouldFetchPreviousPeriod} from 'sentry/components/charts/utils';
- import {DEFAULT_STATS_PERIOD} from 'sentry/constants';
- import {t} from 'sentry/locale';
- import {MetricsApiResponse} from 'sentry/types';
- import {Series} from 'sentry/types/echarts';
- import {getPeriod} from 'sentry/utils/getPeriod';
- import {TableData} from '../discover/discoverQuery';
- import {deprecatedTransformMetricsResponseToTable} from './deprecatedTransformMetricsResponseToTable';
- import {transformMetricsResponseToSeries} from './transformMetricsResponseToSeries';
- const propNamesToIgnore = ['api', 'children'];
- const omitIgnoredProps = (props: Props) =>
- omitBy(props, (_value, key) => propNamesToIgnore.includes(key));
- export type MetricsRequestRenderProps = {
- error: string | null;
- errored: boolean;
- isLoading: boolean;
- loading: boolean;
- pageLinks: string | null;
- reloading: boolean;
- response: MetricsApiResponse | null;
- responsePrevious: MetricsApiResponse | null;
- seriesData?: Series[];
- seriesDataPrevious?: Series[];
- tableData?: TableData;
- };
- type DefaultProps = {
- /**
- * @deprecated
- * Transform the response data to be something ingestible by GridEditable tables and rename fields for performance
- */
- includeDeprecatedTabularData: boolean;
- /**
- * Include data for previous period
- */
- includePrevious: boolean;
- /**
- * Transform the response data to be something ingestible by charts
- */
- includeSeriesData: boolean;
- /**
- * If true, no request will be made
- */
- isDisabled?: boolean;
- };
- type Props = DefaultProps &
- Omit<
- DoMetricsRequestOptions,
- 'includeAllArgs' | 'statsPeriodStart' | 'statsPeriodEnd'
- > & {
- api: Client;
- children?: (renderProps: MetricsRequestRenderProps) => React.ReactNode;
- };
- type State = {
- error: string | null;
- errored: boolean;
- pageLinks: string | null;
- reloading: boolean;
- response: MetricsApiResponse | null;
- responsePrevious: MetricsApiResponse | null;
- };
- class MetricsRequest extends React.Component<Props, State> {
- static defaultProps: DefaultProps = {
- includePrevious: false,
- includeSeriesData: false,
- includeDeprecatedTabularData: false,
- isDisabled: false,
- };
- state: State = {
- reloading: false,
- errored: false,
- error: null,
- response: null,
- responsePrevious: null,
- pageLinks: null,
- };
- componentDidMount() {
- this.fetchData();
- }
- componentDidUpdate(prevProps: Props) {
- if (isEqual(omitIgnoredProps(prevProps), omitIgnoredProps(this.props))) {
- return;
- }
- this.fetchData();
- }
- componentWillUnmount() {
- this.unmounting = true;
- }
- private unmounting: boolean = false;
- getQueryParams({previousPeriod = false} = {}) {
- const {
- project,
- environment,
- field,
- query,
- groupBy,
- orderBy,
- limit,
- interval,
- cursor,
- statsPeriod,
- start,
- end,
- orgSlug,
- } = this.props;
- const commonQuery = {
- field,
- cursor,
- environment,
- groupBy,
- interval,
- query,
- limit,
- project,
- orderBy,
- orgSlug,
- };
- if (!previousPeriod) {
- return {
- ...commonQuery,
- statsPeriod,
- start,
- end,
- };
- }
- const doubledStatsPeriod = getPeriod(
- {period: statsPeriod, start: undefined, end: undefined},
- {shouldDoublePeriod: true}
- ).statsPeriod;
- return {
- ...commonQuery,
- statsPeriodStart: doubledStatsPeriod,
- statsPeriodEnd: statsPeriod ?? DEFAULT_STATS_PERIOD,
- };
- }
- fetchData = async () => {
- const {api, isDisabled, start, end, statsPeriod, includePrevious} = this.props;
- if (isDisabled) {
- return;
- }
- this.setState(state => ({
- reloading: state.response !== null,
- errored: false,
- error: null,
- pageLinks: null,
- }));
- const promises = [
- doMetricsRequest(api, {includeAllArgs: true, ...this.getQueryParams()}),
- ];
- // TODO(metrics): this could be merged into one request by doubling the statsPeriod and then splitting the response in half
- if (shouldFetchPreviousPeriod({start, end, period: statsPeriod, includePrevious})) {
- promises.push(doMetricsRequest(api, this.getQueryParams({previousPeriod: true})));
- }
- try {
- const [[response, _, responseMeta], responsePrevious] = (await Promise.all(
- promises
- )) as [
- [MetricsApiResponse, string | undefined, ResponseMeta | undefined],
- MetricsApiResponse | undefined
- ];
- if (this.unmounting) {
- return;
- }
- this.setState({
- reloading: false,
- response,
- responsePrevious: responsePrevious ?? null,
- pageLinks: responseMeta?.getResponseHeader('Link') ?? null,
- });
- } catch (error) {
- addErrorMessage(error.responseJSON?.detail ?? t('Error loading metrics data'));
- this.setState({
- reloading: false,
- errored: true,
- error: error.responseJSON?.detail ?? null,
- pageLinks: null,
- });
- }
- };
- render() {
- const {reloading, errored, error, response, responsePrevious, pageLinks} = this.state;
- const {
- children,
- isDisabled,
- includeDeprecatedTabularData,
- includeSeriesData,
- includePrevious,
- } = this.props;
- const loading = response === null && !isDisabled && !error;
- return children?.({
- loading,
- reloading,
- isLoading: loading || reloading, // some components downstream are used to loading/reloading or isLoading that combines both (EventsRequest vs DiscoverQuery)
- errored,
- error,
- response,
- responsePrevious,
- pageLinks,
- tableData: includeDeprecatedTabularData
- ? deprecatedTransformMetricsResponseToTable({response})
- : undefined,
- seriesData: includeSeriesData
- ? transformMetricsResponseToSeries(response)
- : undefined,
- seriesDataPrevious:
- includeSeriesData && includePrevious
- ? transformMetricsResponseToSeries(responsePrevious)
- : undefined,
- });
- }
- }
- export default MetricsRequest;
|