"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.AbstractESSource = void 0; exports.isSearchSourceAbortError = isSearchSourceAbortError; var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _i18n = require("@kbn/i18n"); var _uuid = require("uuid"); var _rxjs = require("rxjs"); var _vector_source = require("../vector_source"); var _kibana_services = require("../../../kibana_services"); var _i18n_getters = require("../../../../common/i18n_getters"); var _elasticsearch_util = require("../../../../common/elasticsearch_util"); var _copy_persistent_state = require("../../../reducers/copy_persistent_state"); var _data_request = require("../../util/data_request"); var _geo_tile_utils = require("../../util/geo_tile_utils"); var _valid_string_config = require("../../util/valid_string_config"); var _execution_context_utils = require("../execution_context_utils"); /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ function isSearchSourceAbortError(error) { return error.name === 'AbortError'; } class AbstractESSource extends _vector_source.AbstractVectorSource { static createDescriptor(descriptor) { if (!(0, _valid_string_config.isValidStringConfig)(descriptor.indexPatternId)) { throw new Error('Cannot create AbstractESSourceDescriptor when indexPatternId is not provided'); } return { ...descriptor, id: (0, _valid_string_config.isValidStringConfig)(descriptor.id) ? descriptor.id : (0, _uuid.v4)(), type: (0, _valid_string_config.isValidStringConfig)(descriptor.type) ? descriptor.type : '', indexPatternId: descriptor.indexPatternId, applyGlobalQuery: typeof descriptor.applyGlobalQuery !== 'undefined' ? descriptor.applyGlobalQuery : true, applyGlobalTime: typeof descriptor.applyGlobalTime !== 'undefined' ? descriptor.applyGlobalTime : true, applyForceRefresh: typeof descriptor.applyForceRefresh !== 'undefined' ? descriptor.applyForceRefresh : true }; } constructor(descriptor) { super(AbstractESSource.createDescriptor(descriptor)); (0, _defineProperty2.default)(this, "indexPattern", void 0); (0, _defineProperty2.default)(this, "_descriptor", void 0); (0, _defineProperty2.default)(this, "getValueSuggestions", async (field, query) => { try { const indexPattern = await this.getIndexPattern(); const indexPatternField = indexPattern.fields.getByName(field.getRootName()); return await (0, _kibana_services.getAutocompleteService)().getValueSuggestions({ indexPattern, field: indexPatternField, query }); } catch (error) { // eslint-disable-next-line no-console console.warn(`Unable to fetch suggestions for field: ${field.getRootName()}, query: ${query}, error: ${error.message}`); return []; } }); this._descriptor = descriptor; } getId() { return this._descriptor.id; } getApplyGlobalQuery() { return this._descriptor.applyGlobalQuery; } getApplyGlobalTime() { return this._descriptor.applyGlobalTime; } getApplyForceRefresh() { return this._descriptor.applyForceRefresh; } isQueryAware() { return true; } getIndexPatternIds() { return [this.getIndexPatternId()]; } getQueryableIndexPatternIds() { if (this.getApplyGlobalQuery()) { return [this.getIndexPatternId()]; } return []; } isESSource() { return true; } cloneDescriptor() { const clonedDescriptor = (0, _copy_persistent_state.copyPersistentState)(this._descriptor); // id used as uuid to track requests in inspector clonedDescriptor.id = (0, _uuid.v4)(); return clonedDescriptor; } async _runEsQuery({ registerCancelCallback, requestDescription, requestId, requestName, searchSessionId, searchSource, executionContext, requestsAdapter }) { const abortController = new AbortController(); registerCancelCallback(() => abortController.abort()); try { const { rawResponse: resp } = await (0, _rxjs.lastValueFrom)(searchSource.fetch$({ abortSignal: abortController.signal, sessionId: searchSessionId, legacyHitsTotal: false, inspector: { adapter: requestsAdapter, id: requestId, title: requestName, description: requestDescription }, executionContext })); return resp; } catch (error) { if (isSearchSourceAbortError(error)) { throw new _data_request.DataRequestAbortError(); } throw new Error(_i18n.i18n.translate('xpack.maps.source.esSource.requestFailedErrorMessage', { defaultMessage: `Elasticsearch search request failed, error: {message}`, values: { message: error.message } })); } } async makeSearchSource(requestMeta, limit, initialSearchContext) { const indexPattern = await this.getIndexPattern(); const globalFilters = requestMeta.applyGlobalQuery ? requestMeta.filters : []; const allFilters = [...globalFilters]; if (requestMeta.joinKeyFilter) { allFilters.push(requestMeta.joinKeyFilter); } if (this.isFilterByMapBounds() && 'buffer' in requestMeta && requestMeta.buffer) { // buffer can be empty const geoField = await this._getGeoField(); const buffer = 'isGeoGridPrecisionAware' in this && 'getGeoGridPrecision' in this && this.isGeoGridPrecisionAware() ? (0, _geo_tile_utils.expandToTileBoundaries)(requestMeta.buffer, this.getGeoGridPrecision(requestMeta.zoom)) : requestMeta.buffer; const extentFilter = (0, _elasticsearch_util.createExtentFilter)(buffer, [geoField.name]); allFilters.push(extentFilter); } let isFeatureEditorOpenForLayer = false; if ('isFeatureEditorOpenForLayer' in requestMeta) { isFeatureEditorOpenForLayer = requestMeta.isFeatureEditorOpenForLayer; } if (requestMeta.applyGlobalTime && (await this.isTimeAware()) && !isFeatureEditorOpenForLayer) { const timeRange = requestMeta.timeslice ? { from: new Date(requestMeta.timeslice.from).toISOString(), to: new Date(requestMeta.timeslice.to).toISOString(), mode: 'absolute' } : requestMeta.timeFilters; const filter = (0, _kibana_services.getTimeFilter)().createFilter(indexPattern, timeRange); if (filter) { allFilters.push(filter); } } const searchService = (0, _kibana_services.getSearchService)(); const searchSource = await searchService.searchSource.create(initialSearchContext); searchSource.setField('index', indexPattern); searchSource.setField('size', limit); searchSource.setField('filter', allFilters); if (requestMeta.applyGlobalQuery && !isFeatureEditorOpenForLayer) { searchSource.setField('query', requestMeta.query); } const parents = []; if (requestMeta.sourceQuery && !isFeatureEditorOpenForLayer) { const layerSearchSource = searchService.searchSource.createEmpty(); layerSearchSource.setField('index', indexPattern); layerSearchSource.setField('query', requestMeta.sourceQuery); parents.push(layerSearchSource); } if (requestMeta.embeddableSearchContext && !isFeatureEditorOpenForLayer) { const embeddableSearchSource = searchService.searchSource.createEmpty(); embeddableSearchSource.setField('index', indexPattern); embeddableSearchSource.setField('query', requestMeta.embeddableSearchContext.query); embeddableSearchSource.setField('filter', requestMeta.embeddableSearchContext.filters); parents.push(embeddableSearchSource); } if (parents.length === 1) { searchSource.setParent(parents[0]); } else if (parents.length === 2) { parents[1].setParent(parents[0]); searchSource.setParent(parents[1]); } return searchSource; } async getBoundsForFilters(boundsFilters, registerCancelCallback) { const searchSource = await this.makeSearchSource(boundsFilters, 0); searchSource.setField('trackTotalHits', false); searchSource.setField('aggs', { fitToBounds: { geo_bounds: { field: this.getGeoFieldName() } } }); let esBounds; try { const abortController = new AbortController(); registerCancelCallback(() => abortController.abort()); const { rawResponse: esResp } = await (0, _rxjs.lastValueFrom)(searchSource.fetch$({ abortSignal: abortController.signal, legacyHitsTotal: false, executionContext: (0, _execution_context_utils.mergeExecutionContext)({ description: 'es_source:bounds' }, boundsFilters.executionContext) })); if (!esResp.aggregations) { return null; } const fitToBounds = esResp.aggregations.fitToBounds; if (!fitToBounds.bounds) { // aggregations.fitToBounds is empty object when there are no matching documents return null; } esBounds = fitToBounds.bounds; } catch (error) { if (error.name === 'AbortError') { throw new _data_request.DataRequestAbortError(); } return null; } const minLon = esBounds.top_left.lon; const maxLon = esBounds.bottom_right.lon; return { minLon: minLon > maxLon ? minLon - 360 : minLon, // fixes an ES bbox to straddle dateline maxLon, minLat: esBounds.bottom_right.lat, maxLat: esBounds.top_left.lat }; } async isTimeAware() { try { const indexPattern = await this.getIndexPattern(); const timeField = indexPattern.timeFieldName; return !!timeField; } catch (error) { return false; } } getIndexPatternId() { return this._descriptor.indexPatternId; } getGeoFieldName() { if (!this._descriptor.geoField) { throw new Error(`Required field 'geoField' not provided in '_descriptor'`); } return this._descriptor.geoField; } async getIndexPattern() { // Do we need this cache? Doesn't the IndexPatternService take care of this? if (this.indexPattern) { return this.indexPattern; } try { this.indexPattern = await (0, _kibana_services.getIndexPatternService)().get(this.getIndexPatternId()); return this.indexPattern; } catch (error) { throw new Error((0, _i18n_getters.getDataViewNotFoundMessage)(this.getIndexPatternId())); } } async supportsFitToBounds() { try { const geoField = await this._getGeoField(); return !!geoField.aggregatable; } catch (error) { return false; } } async _getGeoField() { const indexPattern = await this.getIndexPattern(); const geoField = indexPattern.fields.getByName(this.getGeoFieldName()); if (!geoField) { throw new Error(_i18n.i18n.translate('xpack.maps.source.esSource.noGeoFieldErrorMessage', { defaultMessage: `Data view "{indexPatternLabel}" no longer contains the geo field "{geoField}"`, values: { indexPatternLabel: indexPattern.getName(), geoField: this.getGeoFieldName() } })); } return geoField; } async getDisplayName() { try { const indexPattern = await this.getIndexPattern(); return indexPattern.getName(); } catch (error) { // Unable to load index pattern, just return id as display name return this.getIndexPatternId(); } } isBoundsAware() { return true; } async createFieldFormatter(field) { let indexPattern; try { indexPattern = await this.getIndexPattern(); } catch (error) { return null; } const fieldFromIndexPattern = indexPattern.fields.getByName(field.getRootName()); if (!fieldFromIndexPattern) { return null; } return indexPattern.getFormatterForField(fieldFromIndexPattern).getConverterFor('text'); } async loadStylePropsMeta({ layerName, style, dynamicStyleProps, registerCancelCallback, sourceQuery, timeFilters, searchSessionId, inspectorAdapters, executionContext }) { const promises = dynamicStyleProps.map(dynamicStyleProp => { return dynamicStyleProp.getFieldMetaRequest(); }); const fieldAggRequests = await Promise.all(promises); const allAggs = fieldAggRequests.reduce((aggs, fieldAggRequest) => { if (fieldAggRequest) { Object.assign(aggs, fieldAggRequest); } return aggs; }, {}); const indexPattern = await this.getIndexPattern(); const searchService = (0, _kibana_services.getSearchService)(); const searchSource = searchService.searchSource.createEmpty(); searchSource.setField('index', indexPattern); searchSource.setField('size', 0); searchSource.setField('aggs', allAggs); if (sourceQuery) { searchSource.setField('query', sourceQuery); } if (style.isTimeAware() && (await this.isTimeAware())) { const timeFilter = (0, _kibana_services.getTimeFilter)().createFilter(indexPattern, timeFilters); if (timeFilter) { searchSource.setField('filter', [timeFilter]); } } const resp = await this._runEsQuery({ requestId: `${this.getId()}_styleMeta`, requestName: _i18n.i18n.translate('xpack.maps.source.esSource.stylePropsMetaRequestName', { defaultMessage: '{layerName} - metadata', values: { layerName } }), searchSource, registerCancelCallback, requestDescription: _i18n.i18n.translate('xpack.maps.source.esSource.stylePropsMetaRequestDescription', { defaultMessage: 'Elasticsearch request retrieving field metadata used for calculating symbolization bands.' }), searchSessionId, executionContext: (0, _execution_context_utils.mergeExecutionContext)({ description: 'es_source:style_meta' }, executionContext), requestsAdapter: inspectorAdapters.requests }); return resp.aggregations; } } exports.AbstractESSource = AbstractESSource;