|
@@ -1,21 +1,18 @@
|
|
|
-import {Fragment, memo, useCallback, useEffect, useMemo, useState} from 'react';
|
|
|
+import {Fragment, memo, useEffect, useMemo, useState} from 'react';
|
|
|
import styled from '@emotion/styled';
|
|
|
import * as Sentry from '@sentry/react';
|
|
|
-import memoize from 'lodash/memoize';
|
|
|
|
|
|
import {navigateTo} from 'sentry/actionCreators/navigation';
|
|
|
import {Button} from 'sentry/components/button';
|
|
|
import {HeaderTitle} from 'sentry/components/charts/styles';
|
|
|
import {CompactSelect} from 'sentry/components/compactSelect';
|
|
|
import PageFilterBar from 'sentry/components/organizations/pageFilterBar';
|
|
|
-import {BooleanOperator} from 'sentry/components/searchSyntax/parser';
|
|
|
-import SmartSearchBar, {SmartSearchBarProps} from 'sentry/components/smartSearchBar';
|
|
|
import Tag from 'sentry/components/tag';
|
|
|
import TextOverflow from 'sentry/components/textOverflow';
|
|
|
import {IconLightning, IconReleases, IconSettings} from 'sentry/icons';
|
|
|
import {t} from 'sentry/locale';
|
|
|
import {space} from 'sentry/styles/space';
|
|
|
-import {MetricMeta, MRI, SavedSearchType, TagCollection} from 'sentry/types';
|
|
|
+import {MetricMeta, MRI} from 'sentry/types';
|
|
|
import {
|
|
|
defaultMetricDisplayType,
|
|
|
getReadableMetricType,
|
|
@@ -29,14 +26,12 @@ import {
|
|
|
MetricWidgetQueryParams,
|
|
|
stringifyMetricWidget,
|
|
|
} from 'sentry/utils/metrics';
|
|
|
-import {formatMRI, getUseCaseFromMRI, parseMRI} from 'sentry/utils/metrics/mri';
|
|
|
+import {formatMRI, parseMRI} from 'sentry/utils/metrics/mri';
|
|
|
import {useMetricsMeta} from 'sentry/utils/metrics/useMetricsMeta';
|
|
|
import {useMetricsTags} from 'sentry/utils/metrics/useMetricsTags';
|
|
|
-import useApi from 'sentry/utils/useApi';
|
|
|
import useKeyPress from 'sentry/utils/useKeyPress';
|
|
|
-import useOrganization from 'sentry/utils/useOrganization';
|
|
|
-import usePageFilters from 'sentry/utils/usePageFilters';
|
|
|
import useRouter from 'sentry/utils/useRouter';
|
|
|
+import {MetricSearchBar} from 'sentry/views/ddm/metricSearchBar';
|
|
|
|
|
|
type QueryBuilderProps = {
|
|
|
displayType: MetricDisplayType;
|
|
@@ -302,110 +297,6 @@ export const QueryBuilder = memo(function QueryBuilder({
|
|
|
);
|
|
|
});
|
|
|
|
|
|
-interface MetricSearchBarProps extends Partial<SmartSearchBarProps> {
|
|
|
- onChange: (value: string) => void;
|
|
|
- projectIds: string[];
|
|
|
- disabled?: boolean;
|
|
|
- mri?: MRI;
|
|
|
- query?: string;
|
|
|
-}
|
|
|
-
|
|
|
-const EMPTY_ARRAY = [];
|
|
|
-const EMPTY_SET = new Set<never>();
|
|
|
-const DISSALLOWED_LOGICAL_OPERATORS = new Set([BooleanOperator.OR]);
|
|
|
-
|
|
|
-export function MetricSearchBar({
|
|
|
- mri,
|
|
|
- disabled,
|
|
|
- onChange,
|
|
|
- query,
|
|
|
- projectIds,
|
|
|
- ...props
|
|
|
-}: MetricSearchBarProps) {
|
|
|
- const org = useOrganization();
|
|
|
- const api = useApi();
|
|
|
- const {selection} = usePageFilters();
|
|
|
- const projectIdNumbers = useMemo(
|
|
|
- () => projectIds.map(id => parseInt(id, 10)),
|
|
|
- [projectIds]
|
|
|
- );
|
|
|
-
|
|
|
- const {data: tags = EMPTY_ARRAY, isLoading} = useMetricsTags(mri, projectIdNumbers);
|
|
|
-
|
|
|
- const supportedTags: TagCollection = useMemo(
|
|
|
- () => tags.reduce((acc, tag) => ({...acc, [tag.key]: tag}), {}),
|
|
|
- [tags]
|
|
|
- );
|
|
|
-
|
|
|
- const fetchTagValues = useMemo(() => {
|
|
|
- const fn = memoize((tagKey: string) => {
|
|
|
- // clear response from cache after 10 seconds
|
|
|
- setTimeout(() => {
|
|
|
- fn.cache.delete(tagKey);
|
|
|
- }, 10000);
|
|
|
- return api.requestPromise(`/organizations/${org.slug}/metrics/tags/${tagKey}/`, {
|
|
|
- query: {
|
|
|
- metric: mri,
|
|
|
- useCase: getUseCaseFromMRI(mri),
|
|
|
- project: selection.projects,
|
|
|
- },
|
|
|
- });
|
|
|
- });
|
|
|
- return fn;
|
|
|
- }, [api, mri, org.slug, selection.projects]);
|
|
|
-
|
|
|
- const getTagValues = useCallback(
|
|
|
- async (tag: any, search: string) => {
|
|
|
- const tagsValues = await fetchTagValues(tag.key);
|
|
|
-
|
|
|
- return tagsValues
|
|
|
- .filter(
|
|
|
- tv =>
|
|
|
- tv.value !== '' &&
|
|
|
- tv.value.toLocaleLowerCase().includes(search.toLocaleLowerCase())
|
|
|
- )
|
|
|
- .map(tv => tv.value);
|
|
|
- },
|
|
|
- [fetchTagValues]
|
|
|
- );
|
|
|
-
|
|
|
- const handleChange = useCallback(
|
|
|
- (value: string, {validSearch} = {validSearch: true}) => {
|
|
|
- if (validSearch) {
|
|
|
- onChange(value);
|
|
|
- }
|
|
|
- },
|
|
|
- [onChange]
|
|
|
- );
|
|
|
-
|
|
|
- return (
|
|
|
- <WideSearchBar
|
|
|
- disabled={disabled}
|
|
|
- maxMenuHeight={220}
|
|
|
- organization={org}
|
|
|
- onGetTagValues={getTagValues}
|
|
|
- supportedTags={supportedTags}
|
|
|
- // don't highlight tags while loading as we don't know yet if they are supported
|
|
|
- highlightUnsupportedTags={!isLoading}
|
|
|
- disallowedLogicalOperators={DISSALLOWED_LOGICAL_OPERATORS}
|
|
|
- disallowFreeText
|
|
|
- onClose={handleChange}
|
|
|
- onSearch={handleChange}
|
|
|
- placeholder={t('Filter by tags')}
|
|
|
- query={query}
|
|
|
- savedSearchType={SavedSearchType.METRIC}
|
|
|
- durationKeys={EMPTY_SET}
|
|
|
- percentageKeys={EMPTY_SET}
|
|
|
- numericKeys={EMPTY_SET}
|
|
|
- dateKeys={EMPTY_SET}
|
|
|
- booleanKeys={EMPTY_SET}
|
|
|
- sizeKeys={EMPTY_SET}
|
|
|
- textOperatorKeys={EMPTY_SET}
|
|
|
- {...props}
|
|
|
- />
|
|
|
- );
|
|
|
-}
|
|
|
-
|
|
|
function getWidgetDisplayType(
|
|
|
mri: MetricsQuery['mri'],
|
|
|
op: MetricsQuery['op']
|
|
@@ -427,11 +318,6 @@ const QueryBuilderRow = styled('div')`
|
|
|
padding-bottom: 0;
|
|
|
`;
|
|
|
|
|
|
-const WideSearchBar = styled(SmartSearchBar)`
|
|
|
- width: 100%;
|
|
|
- opacity: ${p => (p.disabled ? '0.6' : '1')};
|
|
|
-`;
|
|
|
-
|
|
|
const WrapPageFilterBar = styled(PageFilterBar)`
|
|
|
max-width: max-content;
|
|
|
height: auto;
|