diff --git a/pkg/tsdb/elasticsearch/elasticsearch.go b/pkg/tsdb/elasticsearch/elasticsearch.go index b60f392c6c6..31a907cc2fb 100644 --- a/pkg/tsdb/elasticsearch/elasticsearch.go +++ b/pkg/tsdb/elasticsearch/elasticsearch.go @@ -188,9 +188,10 @@ func (s *Service) CallResource(ctx context.Context, req *backend.CallResourceReq logger := eslog.FromContext(ctx) // allowed paths for resource calls: // - empty string for fetching db version - // - ?/_mapping for fetching index mapping + // - /_mapping for fetching index mapping, e.g. requests going to `index/_mapping` // - _msearch for executing getTerms queries - if req.Path != "" && !strings.HasSuffix(req.Path, "/_mapping") && req.Path != "_msearch" { + // - _mapping for fetching "root" index mappings + if req.Path != "" && !strings.HasSuffix(req.Path, "/_mapping") && req.Path != "_msearch" && req.Path != "_mapping" { logger.Error("Invalid resource path", "path", req.Path) return fmt.Errorf("invalid resource URL: %s", req.Path) } diff --git a/public/app/plugins/datasource/elasticsearch/datasource.test.ts b/public/app/plugins/datasource/elasticsearch/datasource.test.ts index d59640549da..4dfeefc2fae 100644 --- a/public/app/plugins/datasource/elasticsearch/datasource.test.ts +++ b/public/app/plugins/datasource/elasticsearch/datasource.test.ts @@ -151,6 +151,15 @@ describe('ElasticDatasource', () => { const lastCall = fetchMock.mock.calls[fetchMock.mock.calls.length - 1]; expect(lastCall[0].url).toBe(`${ELASTICSEARCH_MOCK_URL}/test-${today}/_mapping`); }); + + it('should call `/_mapping` with an empty index', async () => { + const { ds, fetchMock } = getTestContext({ jsonData: { index: '' } }); + + await ds.testDatasource(); + + const lastCall = fetchMock.mock.calls[fetchMock.mock.calls.length - 1]; + expect(lastCall[0].url).toBe(`${ELASTICSEARCH_MOCK_URL}/_mapping`); + }); }); describe('When issuing metric query with interval pattern', () => { diff --git a/public/app/plugins/datasource/elasticsearch/datasource.ts b/public/app/plugins/datasource/elasticsearch/datasource.ts index d40bb41bd3a..7aa1ece56a4 100644 --- a/public/app/plugins/datasource/elasticsearch/datasource.ts +++ b/public/app/plugins/datasource/elasticsearch/datasource.ts @@ -195,17 +195,24 @@ export class ElasticDatasource * * When multiple indices span the provided time range, the request is sent starting from the newest index, * and then going backwards until an index is found. - * - * @param url the url to query the index on, for example `/_mapping`. */ - - private requestAllIndices(url: string, range = getDefaultTimeRange()) { + private requestAllIndices(range = getDefaultTimeRange()) { let indexList = this.indexPattern.getIndexList(range.from, range.to); if (!Array.isArray(indexList)) { indexList = [this.indexPattern.getIndexForToday()]; } - const indexUrlList = indexList.map((index) => index + url); + const url = '_mapping'; + + const indexUrlList = indexList.map((index) => { + // make sure `index` does not end with a slash + index = index.replace(/\/$/, ''); + if (index === '') { + return url; + } + + return `${index}/${url}`; + }); const maxTraversals = 7; // do not go beyond one week (for a daily pattern) const listLen = indexUrlList.length; @@ -708,7 +715,7 @@ export class ElasticDatasource nested: 'nested', histogram: 'number', }; - return this.requestAllIndices('/_mapping', range).pipe( + return this.requestAllIndices(range).pipe( map((result) => { const shouldAddField = (obj: any, key: string) => { if (this.isMetadataField(key)) { diff --git a/public/app/plugins/datasource/elasticsearch/mocks.ts b/public/app/plugins/datasource/elasticsearch/mocks.ts index a33e258c449..09470fff165 100644 --- a/public/app/plugins/datasource/elasticsearch/mocks.ts +++ b/public/app/plugins/datasource/elasticsearch/mocks.ts @@ -39,9 +39,9 @@ export function createElasticDatasource(settings: Partial