[data views] Disable scripted fields in serverless environment (#163228)

## Summary

Disables scripted fields in serverless environments. Data view defined
scripted fields are ignored and the scripted fields tab in data view
management is hidden.

scriptedFieldsEnabled is defined via conditional config that's only
available in serverless environments.

---------

Co-authored-by: Julia Rechkunova <julia.rechkunova@gmail.com>
This commit is contained in:
Matthew Kime 2023-08-22 21:46:40 -05:00 committed by GitHub
parent af450729ff
commit 2b47e4b346
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
37 changed files with 241 additions and 24 deletions

View file

@ -23,6 +23,7 @@ describe('createSearchSource', () => {
getConfig: jest.fn(),
search: jest.fn(),
onResponse: (req, res) => res,
scriptedFieldsEnabled: true,
};
indexPatternContractMock = {

View file

@ -71,4 +71,5 @@ export const createSearchSourceMock = (
)
),
onResponse: jest.fn().mockImplementation((req, res) => res),
scriptedFieldsEnabled: true,
});

View file

@ -94,6 +94,7 @@ describe('SearchSource', () => {
getConfig: getConfigMock,
search: mockSearchMethod,
onResponse: jest.fn().mockImplementation((_, res) => res),
scriptedFieldsEnabled: true,
};
searchSource = new SearchSource({}, searchSourceDependencies);
@ -651,6 +652,22 @@ describe('SearchSource', () => {
const request = searchSource.getSearchRequestBody();
expect(request.script_fields).toEqual({ hello: {}, world: {} });
});
test('ignores scripted fields when scripted fields are disabled', async () => {
searchSource.setField('index', {
...indexPattern,
getComputedFields: () => ({
storedFields: [],
scriptFields: { hello: {}, world: {} },
docvalueFields: [],
}),
} as unknown as DataView);
searchSourceDependencies.scriptedFieldsEnabled = false;
searchSource.setField('fields', ['timestamp', '*']);
const request = searchSource.getSearchRequestBody();
expect(request.script_fields).toEqual({});
});
});
describe('handling for when specific fields are provided', () => {

View file

@ -138,6 +138,7 @@ export const searchSourceRequiredUiSettings = [
export interface SearchSourceDependencies extends FetchHandlers {
aggs: AggsStart;
search: ISearchGeneric;
scriptedFieldsEnabled: boolean;
}
interface ExpressionAstOptions {
@ -798,10 +799,12 @@ export class SearchSource {
// set defaults
let fieldsFromSource = searchRequest.fieldsFromSource || [];
body.fields = body.fields || [];
body.script_fields = {
...body.script_fields,
...scriptFields,
};
body.script_fields = this.dependencies.scriptedFieldsEnabled
? {
...body.script_fields,
...scriptFields,
}
: {};
body.stored_fields = storedFields;
body.runtime_mappings = runtimeFields || {};

View file

@ -19,6 +19,7 @@ describe('SearchSource service', () => {
getConfig: jest.fn(),
search: jest.fn(),
onResponse: jest.fn(),
scriptedFieldsEnabled: true,
};
});

View file

@ -138,6 +138,7 @@ export class DataPublicPlugin
fieldFormats,
indexPatterns: dataViews,
screenshotMode,
scriptedFieldsEnabled: dataViews.scriptedFieldsEnabled,
});
setSearchService(search);

View file

@ -69,6 +69,7 @@ describe('Search service', () => {
fieldFormats: {} as FieldFormatsStart,
indexPatterns: {} as DataViewsContract,
screenshotMode: screenshotModePluginMock.createStartContract(),
scriptedFieldsEnabled: true,
});
});

View file

@ -86,6 +86,7 @@ export interface SearchServiceStartDependencies {
fieldFormats: FieldFormatsStart;
indexPatterns: DataViewsContract;
screenshotMode: ScreenshotModePluginStart;
scriptedFieldsEnabled: boolean;
}
export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
@ -216,7 +217,12 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
public start(
{ http, theme, uiSettings, chrome, application }: CoreStart,
{ fieldFormats, indexPatterns, screenshotMode }: SearchServiceStartDependencies
{
fieldFormats,
indexPatterns,
screenshotMode,
scriptedFieldsEnabled,
}: SearchServiceStartDependencies
): ISearchStart {
const search = ((request, options = {}) => {
return this.searchInterceptor.search(request, options);
@ -245,6 +251,7 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
}
return response;
},
scriptedFieldsEnabled,
};
const config = this.initializerContext.config.get();

View file

@ -302,6 +302,7 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
getConfig: <T = any>(key: string): T => uiSettingsCache[key],
search: this.asScoped(request).search,
onResponse: (req, res) => res,
scriptedFieldsEnabled: true,
};
return this.searchSourceService.start(scopedIndexPatterns, searchSourceDependencies);

View file

@ -569,7 +569,7 @@ export function Tabs({
const euiTabs: EuiTabbedContentTab[] = useMemo(
() =>
getTabs(indexPattern, fieldFilter, relationships.length).map(
getTabs(indexPattern, fieldFilter, relationships.length, dataViews.scriptedFieldsEnabled).map(
(tab: Pick<EuiTabbedContentTab, 'name' | 'id'>) => {
return {
...tab,
@ -577,7 +577,7 @@ export function Tabs({
};
}
),
[fieldFilter, getContent, indexPattern, relationships]
[fieldFilter, getContent, indexPattern, relationships, dataViews.scriptedFieldsEnabled]
);
const [selectedTabId, setSelectedTabId] = useState(euiTabs[0].id);

View file

@ -15,7 +15,7 @@ import {
TAB_SOURCE_FILTERS,
TAB_RELATIONSHIPS,
} from '../constants';
import { areScriptedFieldsEnabled } from '../../utils';
import { isRollup } from '../../utils';
function filterByName(items: DataViewField[], filter: string) {
const lowercaseFilter = (filter || '').toLowerCase();
@ -73,7 +73,12 @@ function getTitle(type: string, filteredCount: Dictionary<number>, totalCount: D
return title + count;
}
export function getTabs(indexPattern: DataView, fieldFilter: string, relationshipCount = 0) {
export function getTabs(
indexPattern: DataView,
fieldFilter: string,
relationshipCount = 0,
scriptedFieldsEnabled: boolean
) {
const totalCount = getCounts(indexPattern.fields.getAll(), indexPattern.getSourceFiltering());
const filteredCount = getCounts(
indexPattern.fields.getAll(),
@ -89,7 +94,7 @@ export function getTabs(indexPattern: DataView, fieldFilter: string, relationshi
'data-test-subj': 'tab-indexedFields',
});
if (areScriptedFieldsEnabled(indexPattern)) {
if (!isRollup(indexPattern.type) && scriptedFieldsEnabled) {
tabs.push({
name: getTitle('scripted', filteredCount, totalCount),
id: TAB_SCRIPTED_FIELDS,

View file

@ -213,7 +213,10 @@ export const IndexPatternTable = ({
width: '70%',
render: (name: string, dataView: IndexPatternTableItem) => (
<div>
<EuiLink {...reactRouterNavigate(history, `patterns/${dataView.id}`)}>
<EuiLink
{...reactRouterNavigate(history, `patterns/${dataView.id}`)}
data-test-subj={`detail-link-${dataView.name}`}
>
{dataView.getName()}
{dataView.name ? (
<>

View file

@ -28,7 +28,7 @@ const rollupIndexPatternListName = i18n.translate(
}
);
const isRollup = (indexPatternType: string = '') => {
export const isRollup = (indexPatternType: string = '') => {
return indexPatternType === 'rollup';
};
@ -85,10 +85,6 @@ export const getTags = (indexPattern: DataViewListItem | DataView, isDefault: bo
return tags;
};
export const areScriptedFieldsEnabled = (indexPattern: DataViewListItem | DataView) => {
return !isRollup(indexPattern.type);
};
export const getFieldInfo = (indexPattern: DataViewListItem | DataView, field: DataViewField) => {
if (!isRollup(indexPattern.type)) {
return [];

View file

@ -92,6 +92,10 @@ export async function mountManagementSection(
savedObjectsManagement,
};
const editPath = '/dataView/:id/field/:fieldName';
const createPath = '/dataView/:id/create-field/';
const createEditPath = dataViews.scriptedFieldsEnabled ? [editPath, createPath] : [editPath];
ReactDOM.render(
<KibanaRenderContextProvider theme={theme} i18n={coreI18n}>
<KibanaContextProvider services={deps}>
@ -100,7 +104,7 @@ export async function mountManagementSection(
<Route path={['/create']}>
<IndexPatternTableWithRouter canSave={canSave} showCreateDialog={true} />
</Route>
<Route path={['/dataView/:id/field/:fieldName', '/dataView/:id/create-field/']}>
<Route path={createEditPath}>
<CreateEditFieldContainer />
</Route>
<Route path={['/dataView/:id']}>

View file

@ -115,6 +115,7 @@ describe('IndexPatterns', () => {
onRedirectNoIndexPattern: () => {},
getCanSave: () => Promise.resolve(true),
getCanSaveAdvancedSettings: () => Promise.resolve(true),
scriptedFieldsEnabled: true,
});
indexPatternsNoAccess = new DataViewsService({
@ -127,6 +128,7 @@ describe('IndexPatterns', () => {
onRedirectNoIndexPattern: () => {},
getCanSave: () => Promise.resolve(false),
getCanSaveAdvancedSettings: () => Promise.resolve(false),
scriptedFieldsEnabled: true,
});
});

View file

@ -120,6 +120,8 @@ export interface DataViewsServiceDeps {
* Determines whether the user can save advancedSettings (used for defaultIndex)
*/
getCanSaveAdvancedSettings: () => Promise<boolean>;
scriptedFieldsEnabled: boolean;
}
/**
@ -325,6 +327,8 @@ export class DataViewsService {
* Can the user save data views?
*/
public getCanSave: () => Promise<boolean>;
public readonly scriptedFieldsEnabled: boolean;
/**
* DataViewsService constructor
* @param deps Service dependencies
@ -339,6 +343,7 @@ export class DataViewsService {
onError,
getCanSave = () => Promise.resolve(false),
getCanSaveAdvancedSettings,
scriptedFieldsEnabled,
} = deps;
this.apiClient = apiClient;
this.config = uiSettings;
@ -350,6 +355,7 @@ export class DataViewsService {
this.getCanSaveAdvancedSettings = getCanSaveAdvancedSettings;
this.dataViewCache = createDataViewCache();
this.scriptedFieldsEnabled = scriptedFieldsEnabled;
}
/**
@ -591,7 +597,9 @@ export class DataViewsService {
private refreshFieldsFn = async (indexPattern: DataView) => {
const { fields, indices } = await this.getFieldsAndIndicesForDataView(indexPattern);
fields.forEach((field) => (field.isMapped = true));
const scripted = indexPattern.getScriptedFields().map((field) => field.spec);
const scripted = this.scriptedFieldsEnabled
? indexPattern.getScriptedFields().map((field) => field.spec)
: [];
const fieldAttrs = indexPattern.getFieldAttrs();
const fieldsWithSavedAttrs = Object.values(
this.fieldArrayToMap([...fields, ...scripted], fieldAttrs)
@ -655,7 +663,9 @@ export class DataViewsService {
displayErrors: boolean = true
) => {
const fieldsAsArr = Object.values(fields);
const scriptedFields = fieldsAsArr.filter((field) => field.scripted);
const scriptedFields = this.scriptedFieldsEnabled
? fieldsAsArr.filter((field) => field.scripted)
: [];
try {
let updatedFieldList: FieldSpec[];
const { fields: newFields, indices } = await this.getFieldsAndIndicesForWildcard(options);

View file

@ -525,3 +525,7 @@ export interface HasDataService {
hasUserDataView: () => Promise<boolean>;
hasDataView: () => Promise<boolean>;
}
export interface ClientConfigType {
scriptedFieldsEnabled?: boolean;
}

View file

@ -29,6 +29,7 @@ export interface DataViewsServicePublicDeps extends DataViewsServiceDeps {
showAllIndices?: boolean;
isRollupIndex: (indexName: string) => boolean;
}) => Promise<MatchedItem[]>;
scriptedFieldsEnabled: boolean;
}
/**
@ -44,6 +45,7 @@ export class DataViewsServicePublic extends DataViewsService {
isRollupIndex: (indexName: string) => boolean;
}) => Promise<MatchedItem[]>;
public hasData: HasDataService;
public readonly scriptedFieldsEnabled: boolean;
/**
* Constructor
@ -55,5 +57,6 @@ export class DataViewsServicePublic extends DataViewsService {
this.getCanSaveSync = deps.getCanSaveSync;
this.hasData = deps.hasData;
this.getIndices = deps.getIndices;
this.scriptedFieldsEnabled = deps.scriptedFieldsEnabled;
}
}

View file

@ -6,6 +6,8 @@
* Side Public License, v 1.
*/
import { PluginInitializerContext } from '@kbn/core/public';
export {
ILLEGAL_CHARACTERS_KEY,
CONTAINS_SPACES_KEY,
@ -55,8 +57,8 @@ export { UiSettingsPublicToCommon } from './ui_settings_wrapper';
import { DataViewsPublicPlugin } from './plugin';
export function plugin() {
return new DataViewsPublicPlugin();
export function plugin(initializerContext: PluginInitializerContext) {
return new DataViewsPublicPlugin(initializerContext);
}
export type {

View file

@ -6,9 +6,10 @@
* Side Public License, v 1.
*/
import { CoreSetup, CoreStart, Plugin } from '@kbn/core/public';
import { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from '@kbn/core/public';
import { i18n } from '@kbn/i18n';
import { getIndexPatternLoad } from './expressions';
import type { ClientConfigType } from '../common/types';
import {
DataViewsPublicPluginSetup,
DataViewsPublicPluginStart,
@ -40,6 +41,8 @@ export class DataViewsPublicPlugin
{
private readonly hasData = new HasData();
constructor(private readonly initializerContext: PluginInitializerContext) {}
public setup(
core: CoreSetup<DataViewsPublicStartDependencies, DataViewsPublicPluginStart>,
{ expressions, contentManagement }: DataViewsPublicSetupDependencies
@ -74,6 +77,8 @@ export class DataViewsPublicPlugin
10000
);
const config = this.initializerContext.config.get<ClientConfigType>();
return new DataViewsServicePublic({
hasData: this.hasData.start(core),
uiSettings: new UiSettingsPublicToCommon(uiSettings),
@ -91,6 +96,7 @@ export class DataViewsPublicPlugin
getCanSaveAdvancedSettings: () =>
Promise.resolve(application.capabilities.advancedSettings.save === true),
getIndices: (props) => getIndices({ ...props, http: core.http }),
scriptedFieldsEnabled: config.scriptedFieldsEnabled === false ? false : true, // accounting for null value
});
}

View file

@ -120,6 +120,7 @@ export interface DataViewsServicePublic extends DataViewsServicePublicMethods {
showAllIndices?: boolean;
isRollupIndex: (indexName: string) => boolean;
}) => Promise<MatchedItem[]>;
scriptedFieldsEnabled: boolean;
}
export type DataViewsContract = DataViewsServicePublic;

View file

@ -25,6 +25,7 @@ interface DataViewsServiceFactoryDeps {
uiSettings: UiSettingsServiceStart;
fieldFormats: FieldFormatsStart;
capabilities: CoreStart['capabilities'];
scriptedFieldsEnabled: boolean;
rollupsEnabled: boolean;
}
@ -70,5 +71,6 @@ export const dataViewsServiceFactory = (deps: DataViewsServiceFactoryDeps) =>
: request
? (await capabilities.resolveCapabilities(request)).advancedSettings.save === true
: false,
scriptedFieldsEnabled: deps.scriptedFieldsEnabled,
});
};

View file

@ -6,6 +6,8 @@
* Side Public License, v 1.
*/
import { schema, TypeOf } from '@kbn/config-schema';
import type { PluginConfigDescriptor } from '@kbn/core/server';
export { getFieldByName, findIndexPatternById } from './utils';
export type { FieldDescriptor, RollupIndexCapability } from './fetcher';
export { IndexPatternsFetcher, getCapabilitiesForRollupIndices } from './fetcher';
@ -36,6 +38,24 @@ export type {
};
export { DataViewsServerPlugin as Plugin };
const configSchema = schema.object({
scriptedFieldsEnabled: schema.conditional(
schema.contextRef('serverless'),
true,
schema.boolean({ defaultValue: false }),
schema.never()
),
});
type ConfigType = TypeOf<typeof configSchema>;
export const config: PluginConfigDescriptor<ConfigType> = {
schema: configSchema,
exposeToBrowser: {
scriptedFieldsEnabled: true,
},
};
export {
SERVICE_PATH,
SERVICE_PATH_LEGACY,

View file

@ -12,6 +12,7 @@ export function createIndexPatternsStartMock() {
const dataViewsServiceFactory = jest.fn().mockResolvedValue({ get: jest.fn() });
return {
dataViewsServiceFactory,
getScriptedFieldsEnabled: jest.fn().mockReturnValue(true),
};
}

View file

@ -15,6 +15,7 @@ import { getIndexPatternLoad } from './expressions';
import { registerIndexPatternsUsageCollector } from './register_index_pattern_usage_collection';
import { createScriptedFieldsDeprecationsConfig } from './deprecations';
import { DATA_VIEW_SAVED_OBJECT_TYPE, LATEST_VERSION } from '../common';
import type { ClientConfigType } from '../common/types';
import {
DataViewsServerPluginSetup,
DataViewsServerPluginStart,
@ -35,7 +36,7 @@ export class DataViewsServerPlugin
private readonly logger: Logger;
private rollupsEnabled: boolean = false;
constructor(initializerContext: PluginInitializerContext) {
constructor(private readonly initializerContext: PluginInitializerContext) {
this.logger = initializerContext.logger.get('dataView');
}
@ -75,16 +76,21 @@ export class DataViewsServerPlugin
{ uiSettings, capabilities }: CoreStart,
{ fieldFormats }: DataViewsServerPluginStartDependencies
) {
const config = this.initializerContext.config.get<ClientConfigType>();
const scriptedFieldsEnabled = config.scriptedFieldsEnabled === false ? false : true; // accounting for null value
const serviceFactory = dataViewsServiceFactory({
logger: this.logger.get('indexPatterns'),
uiSettings,
fieldFormats,
capabilities,
scriptedFieldsEnabled,
rollupsEnabled: this.rollupsEnabled,
});
return {
dataViewsServiceFactory: serviceFactory,
getScriptedFieldsEnabled: () => scriptedFieldsEnabled,
};
}

View file

@ -48,6 +48,10 @@ export interface DataViewsServerPluginStart {
* Returns a DataViews service instance
*/
dataViewsServiceFactory: ServiceFactory;
/**
*
*/
getScriptedFieldsEnabled: () => boolean;
}
/**

View file

@ -60,6 +60,7 @@ describe('saved_searches_utils', () => {
},
"getConfig": [MockFunction],
"onResponse": [MockFunction],
"scriptedFieldsEnabled": true,
"search": [MockFunction],
},
"fields": Object {},

View file

@ -23,7 +23,7 @@ declare global {
}
const EXPOSED_CONFIG_SETTINGS_ERROR =
'Actual config settings exposed to the browser do not match what is expected; this assertion fails if extra settings are present and/or expected settings are missing';
'Actual config settings exposed to the browser do not match list defined in test; this assertion fails if extra settings are present and/or expected settings are missing';
export default function ({ getService }: PluginFunctionalProviderContext) {
const appsMenu = getService('appsMenu');
@ -104,6 +104,7 @@ export default function ({ getService }: PluginFunctionalProviderContext) {
'data.search.sessions.management.refreshTimeout (duration)',
'data.search.sessions.maxUpdateRetries (number)',
'data.search.sessions.notTouchedTimeout (duration)',
'data_views.scriptedFieldsEnabled (any)', // It's a boolean (any because schema.conditional)
'dev_tools.deeplinks.navLinkStatus (string)',
'enterpriseSearch.canDeployEntSearch (boolean)',
'enterpriseSearch.host (string)',

View file

@ -270,6 +270,7 @@ describe('Alerting Plugin', () => {
dataViewsServiceFactory: jest
.fn()
.mockResolvedValue(dataViewPluginMocks.createStartContract()),
getScriptedFieldsEnabled: jest.fn().mockReturnValue(true),
} as DataViewsServerPluginStart,
});
@ -318,6 +319,7 @@ describe('Alerting Plugin', () => {
dataViewsServiceFactory: jest
.fn()
.mockResolvedValue(dataViewPluginMocks.createStartContract()),
getScriptedFieldsEnabled: jest.fn().mockReturnValue(true),
} as DataViewsServerPluginStart,
});
@ -377,6 +379,7 @@ describe('Alerting Plugin', () => {
dataViewsServiceFactory: jest
.fn()
.mockResolvedValue(dataViewPluginMocks.createStartContract()),
getScriptedFieldsEnabled: jest.fn().mockReturnValue(true),
} as DataViewsServerPluginStart,
});

View file

@ -133,6 +133,7 @@ describe('Task Runner', () => {
const inMemoryMetrics = inMemoryMetricsMock.create();
const dataViewsMock = {
dataViewsServiceFactory: jest.fn().mockResolvedValue(dataViewPluginMocks.createStartContract()),
getScriptedFieldsEnabled: jest.fn().mockReturnValue(true),
} as DataViewsServerPluginStart;
const alertsService = alertsServiceMock.create();
const maintenanceWindowClient = maintenanceWindowClientMock.create();

View file

@ -129,6 +129,7 @@ describe('Task Runner', () => {
const inMemoryMetrics = inMemoryMetricsMock.create();
const dataViewsMock = {
dataViewsServiceFactory: jest.fn().mockResolvedValue(dataViewPluginMocks.createStartContract()),
getScriptedFieldsEnabled: jest.fn().mockReturnValue(true),
} as DataViewsServerPluginStart;
const mockAlertsService = alertsServiceMock.create();
const mockAlertsClient = alertsClientMock.create();

View file

@ -76,6 +76,7 @@ const alertingEventLogger = alertingEventLoggerMock.create();
const logger: ReturnType<typeof loggingSystemMock.createLogger> = loggingSystemMock.createLogger();
const dataViewsMock = {
dataViewsServiceFactory: jest.fn().mockResolvedValue(dataViewPluginMocks.createStartContract()),
getScriptedFieldsEnabled: jest.fn().mockReturnValue(true),
} as DataViewsServerPluginStart;
const alertsService = alertsServiceMock.create();

View file

@ -45,6 +45,7 @@ const elasticsearchService = elasticsearchServiceMock.createInternalStart();
const dataPlugin = dataPluginMock.createStartContract();
const dataViewsMock = {
dataViewsServiceFactory: jest.fn().mockResolvedValue(dataViewPluginMocks.createStartContract()),
getScriptedFieldsEnabled: jest.fn().mockReturnValue(true),
} as DataViewsServerPluginStart;
const ruleType: UntypedNormalizedRuleType = {
id: 'test',

View file

@ -13,6 +13,7 @@ export default function ({ loadTestFile }: FtrProviderContext) {
loadTestFile(require.resolve('./spaces'));
loadTestFile(require.resolve('./security_response_headers'));
loadTestFile(require.resolve('./rollups'));
loadTestFile(require.resolve('./scripted_fields'));
loadTestFile(require.resolve('./index_management'));
loadTestFile(require.resolve('./alerting'));
loadTestFile(require.resolve('./ingest_pipelines'));

View file

@ -0,0 +1,48 @@
/*
* 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.
*/
import expect from 'expect';
import { DATA_VIEW_PATH } from '@kbn/data-views-plugin/server';
import { FtrProviderContext } from '../../ftr_provider_context';
export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const esArchiver = getService('esArchiver');
describe('scripted fields disabled', function () {
before(async () => {
await esArchiver.load('test/api_integration/fixtures/es_archiver/index_patterns/basic_index');
});
after(async () => {
await esArchiver.unload(
'test/api_integration/fixtures/es_archiver/index_patterns/basic_index'
);
});
it('scripted fields are ignored when disabled', async () => {
const response = await supertest
.post(DATA_VIEW_PATH)
.set('kbn-xsrf', 'some-xsrf-token')
.send({
data_view: {
title: 'basic_index',
fields: {
foo_scripted: {
name: 'foo_scripted',
type: 'string',
scripted: true,
script: "doc['field_name'].value",
},
},
},
});
expect(response.status).toBe(200);
expect(response.body.data_view.fields.foo_scripted).toBeUndefined();
});
});
}

View file

@ -0,0 +1,54 @@
/*
* 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.
*/
import expect from 'expect';
import { DATA_VIEW_PATH } from '@kbn/data-views-plugin/server';
import { FtrProviderContext } from '../../ftr_provider_context';
const archivePath = 'test/api_integration/fixtures/es_archiver/index_patterns/basic_index';
export default function ({ getService, getPageObjects }: FtrProviderContext) {
const PageObjects = getPageObjects(['settings', 'common', 'header']);
const esArchiver = getService('esArchiver');
const supertest = getService('supertest');
const testSubjects = getService('testSubjects');
describe('Data View Management', function () {
let dataViewId = '';
before(async () => {
await esArchiver.load(archivePath);
const response = await supertest
.post(DATA_VIEW_PATH)
.set('kbn-xsrf', 'some-xsrf-token')
.send({
data_view: {
title: 'basic_index',
},
override: true,
});
expect(response.status).toBe(200);
dataViewId = response.body.data_view.id;
});
after(async () => {
await esArchiver.unload(archivePath);
await supertest.delete(`${DATA_VIEW_PATH}/${dataViewId}`).set('kbn-xsrf', 'some-xsrf-token');
});
it('Scripted fields tab is missing', async () => {
await PageObjects.common.navigateToUrl('management', 'kibana/dataViews', {
shouldUseHashForSubUrl: false,
});
await testSubjects.click('detail-link-basic_index');
await testSubjects.exists('tab-indexedFields');
await testSubjects.missingOrFail('tab-scriptedFields');
});
});
}

View file

@ -17,5 +17,8 @@ export default function ({ loadTestFile }: FtrProviderContext) {
// Management
loadTestFile(require.resolve('./index_management'));
// Data View Management
loadTestFile(require.resolve('./data_view_mgmt'));
});
}