D7net
Home
Console
Upload
information
Create File
Create Folder
About
Tools
:
/
usr
/
share
/
grafana
/
public
/
app
/
plugins
/
datasource
/
jaeger
/
components
/
Filename :
SearchForm.tsx
back
Copy
import { css } from '@emotion/css'; import React, { useCallback, useEffect, useState } from 'react'; import { SelectableValue, toOption } from '@grafana/data'; import { getTemplateSrv } from '@grafana/runtime'; import { fuzzyMatch, InlineField, InlineFieldRow, Input, Select } from '@grafana/ui'; import { notifyApp } from 'app/core/actions'; import { createErrorNotification } from 'app/core/copy/appNotification'; import { dispatch } from 'app/store/store'; import { JaegerDatasource } from '../datasource'; import { JaegerQuery } from '../types'; import { transformToLogfmt } from '../util'; const durationPlaceholder = 'e.g. 1.2s, 100ms, 500us'; type Props = { datasource: JaegerDatasource; query: JaegerQuery; onChange: (value: JaegerQuery) => void; }; export const ALL_OPERATIONS_KEY = 'All'; const allOperationsOption: SelectableValue<string> = { label: ALL_OPERATIONS_KEY, value: undefined, }; export function SearchForm({ datasource, query, onChange }: Props) { const [serviceOptions, setServiceOptions] = useState<Array<SelectableValue<string>>>(); const [operationOptions, setOperationOptions] = useState<Array<SelectableValue<string>>>(); const [isLoading, setIsLoading] = useState<{ services: boolean; operations: boolean; }>({ services: false, operations: false, }); const loadOptions = useCallback( async (url: string, loaderOfType: string, query = ''): Promise<Array<SelectableValue<string>>> => { setIsLoading((prevValue) => ({ ...prevValue, [loaderOfType]: true })); try { const values: string[] | null = await datasource.metadataRequest(url); if (!values) { return [{ label: `No ${loaderOfType} found`, value: `No ${loaderOfType} found` }]; } const options: SelectableValue[] = values.sort().map((option) => ({ label: option, value: option, })); const filteredOptions = options.filter((item) => (item.value ? fuzzyMatch(item.value, query).found : false)); return filteredOptions; } catch (error) { if (error instanceof Error) { dispatch(notifyApp(createErrorNotification('Error', error))); } return []; } finally { setIsLoading((prevValue) => ({ ...prevValue, [loaderOfType]: false })); } }, [datasource] ); useEffect(() => { const getServices = async () => { const services = await loadOptions('/api/services', 'services'); if (query.service && getTemplateSrv().containsTemplate(query.service)) { services.push(toOption(query.service)); } setServiceOptions(services); }; getServices(); }, [datasource, loadOptions, query.service]); useEffect(() => { const getOperations = async () => { const operations = await loadOptions( `/api/services/${encodeURIComponent(getTemplateSrv().replace(query.service!))}/operations`, 'operations' ); if (query.operation && getTemplateSrv().containsTemplate(query.operation)) { operations.push(toOption(query.operation)); } setOperationOptions([allOperationsOption, ...operations]); }; if (query.service) { getOperations(); } }, [datasource, query.service, loadOptions, query.operation]); return ( <div className={css({ maxWidth: '500px' })}> <InlineFieldRow> <InlineField label="Service Name" labelWidth={14} grow> <Select inputId="service" options={serviceOptions} onOpenMenu={() => loadOptions('/api/services', 'services')} isLoading={isLoading.services} value={serviceOptions?.find((v) => v?.value === query.service) || undefined} placeholder="Select a service" onChange={(v) => onChange({ ...query, service: v?.value!, operation: query.service !== v?.value ? undefined : query.operation, }) } menuPlacement="bottom" isClearable aria-label={'select-service-name'} allowCustomValue={true} /> </InlineField> </InlineFieldRow> <InlineFieldRow> <InlineField label="Operation Name" labelWidth={14} grow disabled={!query.service}> <Select inputId="operation" options={operationOptions} onOpenMenu={() => loadOptions( `/api/services/${encodeURIComponent(getTemplateSrv().replace(query.service!))}/operations`, 'operations' ) } isLoading={isLoading.operations} value={operationOptions?.find((v) => v.value === query.operation) || null} placeholder="Select an operation" onChange={(v) => onChange({ ...query, operation: v?.value! || undefined, }) } menuPlacement="bottom" isClearable aria-label={'select-operation-name'} allowCustomValue={true} /> </InlineField> </InlineFieldRow> <InlineFieldRow> <InlineField label="Tags" labelWidth={14} grow tooltip="Values should be in logfmt."> <Input id="tags" value={transformToLogfmt(query.tags)} placeholder="http.status_code=200 error=true" onChange={(v) => onChange({ ...query, tags: v.currentTarget.value, }) } /> </InlineField> </InlineFieldRow> <InlineFieldRow> <InlineField label="Min Duration" labelWidth={14} grow> <Input id="minDuration" name="minDuration" value={query.minDuration || ''} placeholder={durationPlaceholder} onChange={(v) => onChange({ ...query, minDuration: v.currentTarget.value, }) } /> </InlineField> </InlineFieldRow> <InlineFieldRow> <InlineField label="Max Duration" labelWidth={14} grow> <Input id="maxDuration" name="maxDuration" value={query.maxDuration || ''} placeholder={durationPlaceholder} onChange={(v) => onChange({ ...query, maxDuration: v.currentTarget.value, }) } /> </InlineField> </InlineFieldRow> <InlineFieldRow> <InlineField label="Limit" labelWidth={14} grow tooltip="Maximum number of returned results"> <Input id="limit" name="limit" value={query.limit || ''} type="number" onChange={(v) => onChange({ ...query, limit: v.currentTarget.value ? parseInt(v.currentTarget.value, 10) : undefined, }) } /> </InlineField> </InlineFieldRow> </div> ); } export default SearchForm;