Elasticsearch: remove usages of any (#69575)

* Elasticsearch: remove usages of any

* Elasticsearch: check for filter type when accessing field aggs

* Elasticsearch: use type guards instead of checking types

* Use unknown for isPrimitive function

* Add deprecation notice

* Remove unused type

* Fix bug in "isPrimitive" function

* Remove unused import

* Revert "Fix bug in "isPrimitive" function"

This reverts commit 27f9874cce.
This commit is contained in:
Matias Chomicki 2023-06-19 11:01:08 +02:00 committed by GitHub
parent cb7e18938b
commit a75752b085
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 95 additions and 76 deletions

View File

@ -3877,23 +3877,7 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "3"], [0, 0, 0, "Unexpected any. Specify a different type.", "3"],
[0, 0, 0, "Unexpected any. Specify a different type.", "4"], [0, 0, 0, "Unexpected any. Specify a different type.", "4"],
[0, 0, 0, "Unexpected any. Specify a different type.", "5"], [0, 0, 0, "Unexpected any. Specify a different type.", "5"],
[0, 0, 0, "Unexpected any. Specify a different type.", "6"], [0, 0, 0, "Unexpected any. Specify a different type.", "6"]
[0, 0, 0, "Unexpected any. Specify a different type.", "7"],
[0, 0, 0, "Unexpected any. Specify a different type.", "8"],
[0, 0, 0, "Unexpected any. Specify a different type.", "9"],
[0, 0, 0, "Unexpected any. Specify a different type.", "10"],
[0, 0, 0, "Unexpected any. Specify a different type.", "11"],
[0, 0, 0, "Unexpected any. Specify a different type.", "12"],
[0, 0, 0, "Unexpected any. Specify a different type.", "13"],
[0, 0, 0, "Unexpected any. Specify a different type.", "14"],
[0, 0, 0, "Unexpected any. Specify a different type.", "15"],
[0, 0, 0, "Unexpected any. Specify a different type.", "16"],
[0, 0, 0, "Unexpected any. Specify a different type.", "17"],
[0, 0, 0, "Unexpected any. Specify a different type.", "18"],
[0, 0, 0, "Unexpected any. Specify a different type.", "19"],
[0, 0, 0, "Unexpected any. Specify a different type.", "20"],
[0, 0, 0, "Unexpected any. Specify a different type.", "21"],
[0, 0, 0, "Unexpected any. Specify a different type.", "22"]
], ],
"public/app/plugins/datasource/elasticsearch/hooks/useStatelessReducer.ts:5381": [ "public/app/plugins/datasource/elasticsearch/hooks/useStatelessReducer.ts:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"] [0, 0, 0, "Do not use any type assertions.", "0"]

View File

@ -51,6 +51,7 @@ import {
isPipelineAggregationWithMultipleBucketPaths, isPipelineAggregationWithMultipleBucketPaths,
} from './components/QueryEditor/MetricAggregationsEditor/aggregations'; } from './components/QueryEditor/MetricAggregationsEditor/aggregations';
import { metricAggregationConfig } from './components/QueryEditor/MetricAggregationsEditor/utils'; import { metricAggregationConfig } from './components/QueryEditor/MetricAggregationsEditor/utils';
import { isMetricAggregationWithMeta } from './guards';
import { trackAnnotationQuery, trackQuery } from './tracking'; import { trackAnnotationQuery, trackQuery } from './tracking';
import { import {
Logs, Logs,
@ -60,6 +61,8 @@ import {
ElasticsearchQuery, ElasticsearchQuery,
TermsQuery, TermsQuery,
Interval, Interval,
ElasticsearchAnnotationQuery,
RangeMap,
} from './types'; } from './types';
import { getScriptValue, isSupportedVersion, unsupportedVersionMessage } from './utils'; import { getScriptValue, isSupportedVersion, unsupportedVersionMessage } from './utils';
@ -233,17 +236,7 @@ export class ElasticDatasource
); );
} }
private prepareAnnotationRequest(options: { private prepareAnnotationRequest(options: { annotation: ElasticsearchAnnotationQuery; range: TimeRange }) {
annotation: {
target: ElasticsearchQuery;
timeField?: string;
timeEndField?: string;
titleField?: string;
query?: string;
index?: string;
};
range: TimeRange;
}) {
const annotation = options.annotation; const annotation = options.annotation;
const timeField = annotation.timeField || '@timestamp'; const timeField = annotation.timeField || '@timestamp';
const timeEndField = annotation.timeEndField || null; const timeEndField = annotation.timeEndField || null;
@ -260,7 +253,7 @@ export class ElasticDatasource
const queryString = annotation.query ?? annotation.target?.query ?? ''; const queryString = annotation.query ?? annotation.target?.query ?? '';
const dateRanges = []; const dateRanges = [];
const rangeStart: any = {}; const rangeStart: RangeMap = {};
rangeStart[timeField] = { rangeStart[timeField] = {
from: options.range.from.valueOf(), from: options.range.from.valueOf(),
to: options.range.to.valueOf(), to: options.range.to.valueOf(),
@ -269,7 +262,7 @@ export class ElasticDatasource
dateRanges.push({ range: rangeStart }); dateRanges.push({ range: rangeStart });
if (timeEndField) { if (timeEndField) {
const rangeEnd: any = {}; const rangeEnd: RangeMap = {};
rangeEnd[timeEndField] = { rangeEnd[timeEndField] = {
from: options.range.from.valueOf(), from: options.range.from.valueOf(),
to: options.range.to.valueOf(), to: options.range.to.valueOf(),
@ -279,7 +272,9 @@ export class ElasticDatasource
} }
const queryInterpolated = this.interpolateLuceneQuery(queryString); const queryInterpolated = this.interpolateLuceneQuery(queryString);
const query: any = { const query: {
bool: { filter: Array<Record<string, Record<string, string | number | Array<{ range: RangeMap }>>>> };
} = {
bool: { bool: {
filter: [ filter: [
{ {
@ -299,12 +294,12 @@ export class ElasticDatasource
}, },
}); });
} }
const data: any = { const data = {
query, query,
size: 10000, size: 10000,
}; };
const header: any = { const header: Record<string, string | string[] | boolean> = {
search_type: 'query_then_fetch', search_type: 'query_then_fetch',
ignore_unavailable: true, ignore_unavailable: true,
}; };
@ -322,17 +317,8 @@ export class ElasticDatasource
} }
private processHitsToAnnotationEvents( private processHitsToAnnotationEvents(
annotation: { annotation: ElasticsearchAnnotationQuery,
target: ElasticsearchQuery; hits: Array<Record<string, string | number | Record<string | number, string | number>>>
timeField?: string;
titleField?: string;
timeEndField?: string;
query?: string;
tagsField?: string;
textField?: string;
index?: string;
},
hits: Array<{ [key: string]: any }>
) { ) {
const timeField = annotation.timeField || '@timestamp'; const timeField = annotation.timeField || '@timestamp';
const timeEndField = annotation.timeEndField || null; const timeEndField = annotation.timeEndField || null;
@ -340,7 +326,7 @@ export class ElasticDatasource
const tagsField = annotation.tagsField || null; const tagsField = annotation.tagsField || null;
const list: AnnotationEvent[] = []; const list: AnnotationEvent[] = [];
const getFieldFromSource = (source: any, fieldName: any) => { const getFieldFromSource = (source: any, fieldName: string | null) => {
if (!fieldName) { if (!fieldName) {
return; return;
} }
@ -363,7 +349,7 @@ export class ElasticDatasource
let time = getFieldFromSource(source, timeField); let time = getFieldFromSource(source, timeField);
if (typeof hits[i].fields !== 'undefined') { if (typeof hits[i].fields !== 'undefined') {
const fields = hits[i].fields; const fields = hits[i].fields;
if (isString(fields[timeField]) || isNumber(fields[timeField])) { if (typeof fields === 'object' && (isString(fields[timeField]) || isNumber(fields[timeField]))) {
time = fields[timeField]; time = fields[timeField];
} }
} }
@ -419,7 +405,7 @@ export class ElasticDatasource
return lastValueFrom( return lastValueFrom(
this.getFields(['date']).pipe( this.getFields(['date']).pipe(
mergeMap((dateFields) => { mergeMap((dateFields) => {
const timeField: any = find(dateFields, { text: this.timeField }); const timeField = find(dateFields, { text: this.timeField });
if (!timeField) { if (!timeField) {
return of({ return of({
status: 'error', status: 'error',
@ -437,8 +423,8 @@ export class ElasticDatasource
); );
} }
getQueryHeader(searchType: any, timeFrom?: DateTime, timeTo?: DateTime): string { getQueryHeader(searchType: string, timeFrom?: DateTime, timeTo?: DateTime): string {
const queryHeader: any = { const queryHeader = {
search_type: searchType, search_type: searchType,
ignore_unavailable: true, ignore_unavailable: true,
index: this.indexPattern.getIndexList(timeFrom, timeTo), index: this.indexPattern.getIndexList(timeFrom, timeTo),
@ -680,8 +666,8 @@ export class ElasticDatasource
}; };
// Store subfield names: [system, process, cpu, total] -> system.process.cpu.total // Store subfield names: [system, process, cpu, total] -> system.process.cpu.total
const fieldNameParts: any = []; const fieldNameParts: string[] = [];
const fields: any = {}; const fields: Record<string, { text: string; type: string }> = {};
function getFieldsRecursively(obj: any) { function getFieldsRecursively(obj: any) {
for (const key in obj) { for (const key in obj) {
@ -778,7 +764,7 @@ export class ElasticDatasource
return ('_msearch?' + searchParams.toString()).replace(/\?$/, ''); return ('_msearch?' + searchParams.toString()).replace(/\?$/, '');
} }
metricFindQuery(query: string, options?: any): Promise<MetricFindValue[]> { metricFindQuery(query: string, options?: { range: TimeRange }): Promise<MetricFindValue[]> {
const range = options?.range; const range = options?.range;
const parsedQuery = JSON.parse(query); const parsedQuery = JSON.parse(query);
if (query) { if (query) {
@ -801,36 +787,48 @@ export class ElasticDatasource
return lastValueFrom(this.getFields()); return lastValueFrom(this.getFields());
} }
getTagValues(options: any) { getTagValues(options: { key: string }) {
const range = this.timeSrv.timeRange(); const range = this.timeSrv.timeRange();
return lastValueFrom(this.getTerms({ field: options.key }, range)); return lastValueFrom(this.getTerms({ field: options.key }, range));
} }
targetContainsTemplate(target: any) { targetContainsTemplate(target: ElasticsearchQuery) {
if (this.templateSrv.containsTemplate(target.query) || this.templateSrv.containsTemplate(target.alias)) { if (this.templateSrv.containsTemplate(target.query) || this.templateSrv.containsTemplate(target.alias)) {
return true; return true;
} }
if (target.bucketAggs) {
for (const bucketAgg of target.bucketAggs) { for (const bucketAgg of target.bucketAggs) {
if (this.templateSrv.containsTemplate(bucketAgg.field) || this.objectContainsTemplate(bucketAgg.settings)) { if (isBucketAggregationWithField(bucketAgg) && this.templateSrv.containsTemplate(bucketAgg.field)) {
return true; return true;
} }
if (this.objectContainsTemplate(bucketAgg.settings)) {
return true;
}
}
} }
if (target.metrics) {
for (const metric of target.metrics) { for (const metric of target.metrics) {
if ( if (!isMetricAggregationWithField(metric)) {
this.templateSrv.containsTemplate(metric.field) || continue;
this.objectContainsTemplate(metric.settings) || }
this.objectContainsTemplate(metric.meta) if (metric.field && this.templateSrv.containsTemplate(metric.field)) {
) {
return true; return true;
} }
if (metric.settings && this.objectContainsTemplate(metric.settings)) {
return true;
}
if (isMetricAggregationWithMeta(metric) && this.objectContainsTemplate(metric.meta)) {
return true;
}
}
} }
return false; return false;
} }
private isPrimitive(obj: any) { private isPrimitive(obj: unknown) {
if (obj === null || obj === undefined) { if (obj === null || obj === undefined) {
return true; return true;
} }

View File

@ -0,0 +1,23 @@
import { Count, ExtendedStats } from './dataquery.gen';
import { isMetricAggregationWithMeta } from './guards';
describe('Type guards', () => {
test('Identifies metrics with meta attribute', () => {
const metric: ExtendedStats = {
id: 'test',
type: 'extended_stats',
meta: {
test: 'test',
},
};
expect(isMetricAggregationWithMeta(metric)).toBe(true);
});
test('Identifies metrics without meta attribute', () => {
const metric: Count = {
id: 'test',
type: 'count',
};
expect(isMetricAggregationWithMeta(metric)).toBe(false);
});
});

View File

@ -0,0 +1,8 @@
import { ExtendedStats, MetricAggregation } from './dataquery.gen';
export function isMetricAggregationWithMeta(metric: MetricAggregation): metric is ExtendedStats {
if (!metric || typeof metric !== 'object') {
return false;
}
return 'meta' in metric;
}

View File

@ -4,7 +4,7 @@ import { variableRegex } from 'app/features/variables/utils';
import { REF_ID_STARTER_LOG_VOLUME } from './datasource'; import { REF_ID_STARTER_LOG_VOLUME } from './datasource';
import pluginJson from './plugin.json'; import pluginJson from './plugin.json';
import { ElasticsearchQuery } from './types'; import { ElasticsearchAnnotationQuery, ElasticsearchQuery } from './types';
type ElasticSearchOnDashboardLoadedTrackingEvent = { type ElasticSearchOnDashboardLoadedTrackingEvent = {
grafana_version?: string; grafana_version?: string;
@ -141,16 +141,7 @@ export function trackQuery(
} }
} }
export function trackAnnotationQuery(annotation: { export function trackAnnotationQuery(annotation: ElasticsearchAnnotationQuery): void {
target: ElasticsearchQuery;
timeField?: string;
timeEndField?: string;
query?: string;
tagsField?: string;
textField?: string;
index?: string;
[key: string]: unknown;
}): void {
reportInteraction('grafana_elasticsearch_annotation_query_executed', { reportInteraction('grafana_elasticsearch_annotation_query_executed', {
grafana_version: config.buildInfo.version, grafana_version: config.buildInfo.version,
has_target_query: !!annotation.target?.query, has_target_query: !!annotation.target?.query,

View File

@ -14,6 +14,7 @@ import {
MovingAverage as SchemaMovingAverage, MovingAverage as SchemaMovingAverage,
BucketAggregation, BucketAggregation,
Logs as SchemaLogs, Logs as SchemaLogs,
Elasticsearch,
} from './dataquery.gen'; } from './dataquery.gen';
export * from './dataquery.gen'; export * from './dataquery.gen';
@ -121,3 +122,17 @@ export type DataLinkConfig = {
urlDisplayLabel?: string; urlDisplayLabel?: string;
datasourceUid?: string; datasourceUid?: string;
}; };
export interface ElasticsearchAnnotationQuery {
target: Elasticsearch;
timeField?: string;
titleField?: string;
timeEndField?: string;
query?: string;
tagsField?: string;
textField?: string;
// @deprecated index is deprecated and will be removed in the future
index?: string;
}
export type RangeMap = Record<string, { from: number; to: number; format: string }>;