"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.stateSelectorFactory = exports.matchedIndiciesDefault = exports.DataViewEditorService = void 0; var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _rxjs = require("rxjs"); var _public = require("@kbn/data-views-plugin/public"); var _lib = require("./lib"); /* * 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 and the Server Side Public License, v 1; you may not use this file except * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ const matchedIndiciesDefault = { allIndices: [], exactMatchedIndices: [], partialMatchedIndices: [], visibleIndices: [] }; exports.matchedIndiciesDefault = matchedIndiciesDefault; const defaultDataViewEditorState = { matchedIndices: { ...matchedIndiciesDefault }, rollupIndicesCaps: {}, isLoadingSourcesInternal: false, loadingTimestampFields: false, timestampFieldOptions: [], rollupIndexName: undefined }; const stateSelectorFactory = state$ => (selector, equalityFn) => state$.pipe((0, _rxjs.map)(selector), (0, _rxjs.distinctUntilChanged)(equalityFn)); exports.stateSelectorFactory = stateSelectorFactory; class DataViewEditorService { constructor({ services: { http, dataViews }, initialValues: { type: initialType = _public.INDEX_PATTERN_TYPE.DEFAULT, indexPattern: initialIndexPattern = '', name: _initialName = '' }, requireTimestampField: _requireTimestampField = false }) { (0, _defineProperty2.default)(this, "http", void 0); (0, _defineProperty2.default)(this, "dataViews", void 0); // config (0, _defineProperty2.default)(this, "requireTimestampField", void 0); (0, _defineProperty2.default)(this, "type", _public.INDEX_PATTERN_TYPE.DEFAULT); (0, _defineProperty2.default)(this, "indexPattern", ''); (0, _defineProperty2.default)(this, "allowHidden", false); // used for data view name validation - no dupes! (0, _defineProperty2.default)(this, "dataViewNames$", void 0); (0, _defineProperty2.default)(this, "loadTimestampFieldsSub", void 0); (0, _defineProperty2.default)(this, "matchedIndicesForProviderSub", void 0); (0, _defineProperty2.default)(this, "rollupIndexForProviderSub", void 0); (0, _defineProperty2.default)(this, "state$", void 0); // used for validating rollup data views - must match one and only one data view (0, _defineProperty2.default)(this, "rollupIndicesCaps$", void 0); (0, _defineProperty2.default)(this, "isLoadingSources$", void 0); (0, _defineProperty2.default)(this, "loadingTimestampFields$", void 0); (0, _defineProperty2.default)(this, "timestampFieldOptions$", void 0); // current matched rollup index (0, _defineProperty2.default)(this, "rollupIndex$", void 0); // alernates between value and undefined so validation can treat new value as thought its a promise (0, _defineProperty2.default)(this, "rollupIndexForProvider$", new _rxjs.Subject()); (0, _defineProperty2.default)(this, "matchedIndices$", void 0); // alernates between value and undefined so validation can treat new value as thought its a promise (0, _defineProperty2.default)(this, "matchedIndicesForProvider$", new _rxjs.Subject()); (0, _defineProperty2.default)(this, "rollupCapsResponse", void 0); (0, _defineProperty2.default)(this, "currentLoadingTimestampFields", 0); (0, _defineProperty2.default)(this, "currentLoadingMatchedIndices", 0); (0, _defineProperty2.default)(this, "updateState", newState => { this.state$.next({ ...this.state$.getValue(), ...newState }); }); (0, _defineProperty2.default)(this, "getRollupIndexCaps", async () => { let rollupIndicesCaps = {}; try { rollupIndicesCaps = await this.http.get('/api/rollup/indices'); } catch (e) { // Silently swallow failure responses such as expired trials } this.updateState({ rollupIndicesCaps }); return rollupIndicesCaps; }); (0, _defineProperty2.default)(this, "getIsRollupIndex", async () => { const response = await this.rollupCapsResponse; const indices = Object.keys(response); return indexName => indices.includes(indexName); }); (0, _defineProperty2.default)(this, "loadMatchedIndices", async (query, allowHidden, allSources, type) => { const currentLoadingMatchedIndicesIdx = ++this.currentLoadingMatchedIndices; const isRollupIndex = await this.getIsRollupIndex(); const indexRequests = []; let newRollupIndexName; this.updateState({ loadingTimestampFields: true }); if (query !== null && query !== void 0 && query.endsWith('*')) { const exactMatchedQuery = this.getIndicesCached({ pattern: query, showAllIndices: allowHidden }); indexRequests.push(exactMatchedQuery); // provide default value when not making a request for the partialMatchQuery indexRequests.push(Promise.resolve([])); } else { const exactMatchQuery = this.getIndicesCached({ pattern: query, showAllIndices: allowHidden }); const partialMatchQuery = this.getIndicesCached({ pattern: `${query}*`, showAllIndices: allowHidden }); indexRequests.push(exactMatchQuery); indexRequests.push(partialMatchQuery); } const [exactMatched, partialMatched] = await (0, _lib.ensureMinimumTime)(indexRequests); const matchedIndices = (0, _lib.getMatchedIndices)(allSources, partialMatched, exactMatched, allowHidden); // verify we're looking at the current result if (currentLoadingMatchedIndicesIdx === this.currentLoadingMatchedIndices) { if (type === _public.INDEX_PATTERN_TYPE.ROLLUP) { const rollupIndices = exactMatched.filter(index => isRollupIndex(index.name)); newRollupIndexName = rollupIndices.length === 1 ? rollupIndices[0].name : null; this.updateState({ rollupIndexName: newRollupIndexName }); } else { this.updateState({ rollupIndexName: null }); } this.updateState({ matchedIndices }); } }); (0, _defineProperty2.default)(this, "setIndexPattern", indexPattern => { this.indexPattern = (0, _lib.removeSpaces)(indexPattern); this.loadIndices(); }); (0, _defineProperty2.default)(this, "setAllowHidden", allowHidden => { this.allowHidden = allowHidden; this.loadIndices(); }); (0, _defineProperty2.default)(this, "setType", type => { this.type = type; this.loadIndices(); }); (0, _defineProperty2.default)(this, "loadIndices", async () => { const allSrcs = await this.getIndicesCached({ pattern: '*', showAllIndices: this.allowHidden }); await this.loadMatchedIndices(this.indexPattern, this.allowHidden, allSrcs, this.type); this.updateState({ isLoadingSourcesInternal: false }); }); (0, _defineProperty2.default)(this, "loadDataViewNames", async initialName => { const dataViewListItems = await this.dataViews.getIdsWithTitle(true); const dataViewNames = dataViewListItems.map(item => item.name || item.title); return initialName ? dataViewNames.filter(v => v !== initialName) : dataViewNames; }); (0, _defineProperty2.default)(this, "getIndicesMemory", {}); (0, _defineProperty2.default)(this, "getIndicesCached", async props => { const key = JSON.stringify(props); this.getIndicesMemory[key] = this.getIndicesMemory[key] || this.getIsRollupIndex().then(isRollupIndex => this.dataViews.getIndices({ ...props, isRollupIndex })); this.getIndicesMemory[key].catch(() => { delete this.getIndicesMemory[key]; }); return await this.getIndicesMemory[key]; }); (0, _defineProperty2.default)(this, "timestampOptionsMemory", {}); (0, _defineProperty2.default)(this, "getTimestampOptionsForWildcard", async (getFieldsOptions, requireTimestampField) => { const fields = await (0, _lib.ensureMinimumTime)(this.dataViews.getFieldsForWildcard(getFieldsOptions)); return (0, _lib.extractTimeFields)(fields, requireTimestampField); }); (0, _defineProperty2.default)(this, "getTimestampOptionsForWildcardCached", async (getFieldsOptions, requireTimestampField) => { const key = JSON.stringify(getFieldsOptions) + requireTimestampField; const getTimestampOptionsPromise = this.getTimestampOptionsForWildcard(getFieldsOptions, requireTimestampField); this.timestampOptionsMemory[key] = this.timestampOptionsMemory[key] || getTimestampOptionsPromise; getTimestampOptionsPromise.catch(() => { delete this.timestampOptionsMemory[key]; }); return await getTimestampOptionsPromise; }); (0, _defineProperty2.default)(this, "loadTimestampFields", async () => { const currentState = this.state$.getValue(); if (currentState.matchedIndices.exactMatchedIndices.length === 0) { this.updateState({ timestampFieldOptions: [], loadingTimestampFields: false }); return; } const currentLoadingTimestampFieldsIdx = ++this.currentLoadingTimestampFields; const getFieldsOptions = { pattern: this.indexPattern }; if (this.type === _public.INDEX_PATTERN_TYPE.ROLLUP) { getFieldsOptions.type = _public.INDEX_PATTERN_TYPE.ROLLUP; getFieldsOptions.rollupIndex = currentState.rollupIndexName || ''; getFieldsOptions.allowNoIndex = true; } let timestampFieldOptions = []; try { timestampFieldOptions = await this.getTimestampOptionsForWildcardCached(getFieldsOptions, this.requireTimestampField); } finally { if (currentLoadingTimestampFieldsIdx === this.currentLoadingTimestampFields) { this.updateState({ timestampFieldOptions, loadingTimestampFields: false }); } } }); // provides info necessary for validation of index pattern in required async format (0, _defineProperty2.default)(this, "indexPatternValidationProvider", async () => { const rollupIndexPromise = (0, _rxjs.firstValueFrom)(this.rollupIndex$.pipe((0, _rxjs.first)(data => data !== undefined))); const matchedIndicesPromise = (0, _rxjs.firstValueFrom)(this.matchedIndicesForProvider$.pipe((0, _rxjs.first)(data => data !== undefined))); // necessary to get new observable value if the field hasn't changed this.loadIndices(); // Wait until we have fetched the indices. // The result will then be sent to the field validator(s) (when calling await provider();); const [rollupIndex, matchedIndices] = await Promise.all([rollupIndexPromise, matchedIndicesPromise]); return { rollupIndex, matchedIndices: matchedIndices || matchedIndiciesDefault }; }); (0, _defineProperty2.default)(this, "destroy", () => { this.loadTimestampFieldsSub.unsubscribe(); this.matchedIndicesForProviderSub.unsubscribe(); this.rollupIndexForProviderSub.unsubscribe(); }); this.http = http; this.dataViews = dataViews; this.requireTimestampField = _requireTimestampField; this.type = initialType; this.indexPattern = (0, _lib.removeSpaces)(initialIndexPattern); // fire off a couple of requests that we know we'll need this.rollupCapsResponse = this.getRollupIndexCaps(); this.dataViewNames$ = (0, _rxjs.from)(this.loadDataViewNames(_initialName)); this.state$ = new _rxjs.BehaviorSubject({ ...defaultDataViewEditorState }); const stateSelector = stateSelectorFactory(this.state$); // public observables this.matchedIndices$ = stateSelector(state => state.matchedIndices); this.rollupIndicesCaps$ = stateSelector(state => state.rollupIndicesCaps); this.isLoadingSources$ = stateSelector(state => state.isLoadingSourcesInternal); this.loadingTimestampFields$ = stateSelector(state => state.loadingTimestampFields); this.timestampFieldOptions$ = stateSelector(state => state.timestampFieldOptions); this.rollupIndex$ = stateSelector(state => state.rollupIndexName); // when list of matched indices is updated always update timestamp fields this.loadTimestampFieldsSub = this.matchedIndices$.subscribe(() => this.loadTimestampFields()); // alternate value with undefined so validation knows when its getting a fresh value this.matchedIndicesForProviderSub = this.matchedIndices$.subscribe(matchedIndices => { this.matchedIndicesForProvider$.next(matchedIndices); this.matchedIndicesForProvider$.next(undefined); }); // alternate value with undefined so validation knows when its getting a fresh value this.rollupIndexForProviderSub = this.rollupIndex$.subscribe(rollupIndex => { this.rollupIndexForProvider$.next(rollupIndex); this.rollupIndexForProvider$.next(undefined); }); } } exports.DataViewEditorService = DataViewEditorService;