[data view mgmt] change urls from indexPatterns to dataViews (#114912)

* index pattern management to data view url changes
This commit is contained in:
Matthew Kime 2021-10-21 05:42:13 -05:00 committed by GitHub
parent d831c5035d
commit d2dea6816c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
35 changed files with 145 additions and 79 deletions

View file

@ -423,11 +423,7 @@ export class DataViewsService {
);
if (!savedObject.version) {
throw new SavedObjectNotFound(
DATA_VIEW_SAVED_OBJECT_TYPE,
id,
'management/kibana/indexPatterns'
);
throw new SavedObjectNotFound(DATA_VIEW_SAVED_OBJECT_TYPE, id, 'management/kibana/dataViews');
}
return this.initFromSavedObject(savedObject);

View file

@ -22,7 +22,7 @@ export const onRedirectNoIndexPattern =
) =>
() => {
const canManageIndexPatterns = capabilities.management.kibana.indexPatterns;
const redirectTarget = canManageIndexPatterns ? '/management/kibana/indexPatterns' : '/home';
const redirectTarget = canManageIndexPatterns ? '/management/kibana/dataViews' : '/home';
let timeoutId: NodeJS.Timeout | undefined;
if (timeoutId) {

View file

@ -23,11 +23,11 @@ export const dataViewSavedObjectType: SavedObjectsType = {
return obj.attributes.title;
},
getEditUrl(obj) {
return `/management/kibana/indexPatterns/patterns/${encodeURIComponent(obj.id)}`;
return `/management/kibana/dataViews/dataView/${encodeURIComponent(obj.id)}`;
},
getInAppUrl(obj) {
return {
path: `/app/management/kibana/indexPatterns/patterns/${encodeURIComponent(obj.id)}`,
path: `/app/management/kibana/dataViews/dataView/${encodeURIComponent(obj.id)}`,
uiCapabilitiesPath: 'management.kibana.indexPatterns',
};
},

View file

@ -45,7 +45,7 @@ export const CreateEditField = withRouter(
name: undefined,
} as unknown as IndexPatternField);
const url = `/patterns/${indexPattern.id}`;
const url = `/dataView/${indexPattern.id}`;
if (mode === 'edit' && !spec) {
const message = i18n.translate('indexPatternManagement.editDataView.scripted.noFieldLabel', {

View file

@ -15,5 +15,5 @@ test('getPath() should encode "fieldName"', () => {
{ name: 'Memory: Allocated Bytes/sec' } as unknown as IndexPatternField,
{ id: 'id' } as unknown as IndexPattern
)
).toMatchInlineSnapshot(`"/patterns/id/field/Memory%3A%20Allocated%20Bytes%2Fsec"`);
).toMatchInlineSnapshot(`"/dataView/id/field/Memory%3A%20Allocated%20Bytes%2Fsec"`);
});

View file

@ -102,7 +102,7 @@ export function getTabs(indexPattern: IndexPattern, fieldFilter: string) {
}
export function getPath(field: IndexPatternField, indexPattern: IndexPattern) {
return `/patterns/${indexPattern?.id}/field/${encodeURIComponent(field.name)}`;
return `/dataView/${indexPattern?.id}/field/${encodeURIComponent(field.name)}`;
}
const allTypesDropDown = i18n.translate(

View file

@ -8,7 +8,7 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { Router, Switch, Route } from 'react-router-dom';
import { Router, Switch, Route, Redirect } from 'react-router-dom';
import { i18n } from '@kbn/i18n';
import { I18nProvider } from '@kbn/i18n/react';
@ -73,12 +73,13 @@ export async function mountManagementSection(
<Route path={['/create']}>
<IndexPatternTableWithRouter canSave={canSave} showCreateDialog={true} />
</Route>
<Route path={['/patterns/:id/field/:fieldName', '/patterns/:id/create-field/']}>
<Route path={['/dataView/:id/field/:fieldName', '/dataView/:id/create-field/']}>
<CreateEditFieldContainer />
</Route>
<Route path={['/patterns/:id']}>
<Route path={['/dataView/:id']}>
<EditIndexPatternContainer />
</Route>
<Redirect path={'/patterns*'} to={'dataView*'} />
<Route path={['/']}>
<IndexPatternTableWithRouter canSave={canSave} />
</Route>

View file

@ -36,7 +36,7 @@ const sectionsHeader = i18n.translate('indexPatternManagement.dataView.sectionsH
defaultMessage: 'Data Views',
});
const IPM_APP_ID = 'indexPatterns';
const IPM_APP_ID = 'dataViews';
export class IndexPatternManagementPlugin
implements
@ -72,6 +72,8 @@ export class IndexPatternManagementPlugin
id: IPM_APP_ID,
title: sectionsHeader,
order: 0,
capabilitiesId: 'indexPatterns',
redirectFrom: 'kibana/indexPatterns',
mount: async (params) => {
const { mountManagementSection } = await import('./management_app');

View file

@ -7,7 +7,7 @@
*/
import React, { memo } from 'react';
import { Route, Router, Switch } from 'react-router-dom';
import { Route, Router, Switch, Redirect } from 'react-router-dom';
import { AppMountParameters, ChromeBreadcrumb, ScopedHistory } from 'kibana/public';
import { ManagementAppWrapper } from '../management_app_wrapper';
import { ManagementLandingPage } from '../landing';
@ -43,6 +43,12 @@ export const ManagementRouter = memo(
/>
))
)}
{sections.map((section) =>
section
.getAppsEnabled()
.filter((app) => app.redirectFrom)
.map((app) => <Redirect path={`/${app.redirectFrom}*`} to={`${app.basePath}*`} />)
)}
<Route
path={'/'}
component={() => (

View file

@ -74,7 +74,11 @@ export class ManagementSectionsService {
if (capabilities.management.hasOwnProperty(section.id)) {
const sectionCapabilities = capabilities.management[section.id];
section.apps.forEach((app) => {
if (sectionCapabilities.hasOwnProperty(app.id) && sectionCapabilities[app.id] !== true) {
const capabilitiesId = app.capabilitiesId || app.id;
if (
sectionCapabilities.hasOwnProperty(capabilitiesId) &&
sectionCapabilities[capabilitiesId] !== true
) {
app.disable();
}
});

View file

@ -72,4 +72,6 @@ export interface CreateManagementItemArgs {
order?: number;
euiIconType?: string; // takes precedence over `icon` property.
icon?: string; // URL to image file; fallback if no `euiIconType`
capabilitiesId?: string; // overrides app id
redirectFrom?: string; // redirects from an old app id to the current app id
}

View file

@ -15,16 +15,29 @@ export class ManagementItem {
public readonly order: number;
public readonly euiIconType?: string;
public readonly icon?: string;
public readonly capabilitiesId?: string;
public readonly redirectFrom?: string;
public enabled: boolean = true;
constructor({ id, title, tip, order = 100, euiIconType, icon }: CreateManagementItemArgs) {
constructor({
id,
title,
tip,
order = 100,
euiIconType,
icon,
capabilitiesId,
redirectFrom,
}: CreateManagementItemArgs) {
this.id = id;
this.title = title;
this.tip = tip;
this.order = order;
this.euiIconType = euiIconType;
this.icon = icon;
this.capabilitiesId = capabilitiesId;
this.redirectFrom = redirectFrom;
}
disable() {

View file

@ -30,9 +30,9 @@ describe('Inspect component', () => {
meta: {
title: `MyIndexPattern*`,
icon: 'indexPatternApp',
editUrl: '#/management/kibana/indexPatterns/patterns/1',
editUrl: '#/management/kibana/dataViews/dataView/1',
inAppUrl: {
path: '/management/kibana/indexPatterns/patterns/1',
path: '/management/kibana/dataViews/dataView/1',
uiCapabilitiesPath: 'management.kibana.indexPatterns',
},
},

View file

@ -158,9 +158,9 @@ describe('SavedObjectEdition', () => {
meta: {
title: `MyIndexPattern*`,
icon: 'indexPatternApp',
editUrl: '#/management/kibana/indexPatterns/patterns/1',
editUrl: '#/management/kibana/dataViews/dataView/1',
inAppUrl: {
path: '/management/kibana/indexPatterns/patterns/1',
path: '/management/kibana/dataViews/dataView/1',
uiCapabilitiesPath: 'management.kibana.indexPatterns',
},
hiddenType: false,
@ -187,7 +187,7 @@ describe('SavedObjectEdition', () => {
const headerComponent = component.find('Header');
expect(headerComponent.prop('canViewInApp')).toBe(true);
expect(headerComponent.prop('canDelete')).toBe(true);
expect(headerComponent.prop('viewUrl')).toEqual('/management/kibana/indexPatterns/patterns/1');
expect(headerComponent.prop('viewUrl')).toEqual('/management/kibana/dataViews/dataView/1');
const inspectComponent = component.find('Inspect');
expect(inspectComponent.prop('object')).toEqual(savedObjectItem);
});
@ -225,9 +225,9 @@ describe('SavedObjectEdition', () => {
meta: {
title: `MyIndexPattern*`,
icon: 'indexPatternApp',
editUrl: '#/management/kibana/indexPatterns/patterns/1',
editUrl: '#/management/kibana/dataViews/dataView/1',
inAppUrl: {
path: '/management/kibana/indexPatterns/patterns/1',
path: '/management/kibana/dataViews/dataView/1',
uiCapabilitiesPath: 'management.kibana.indexPatterns',
},
hiddenType: false,

View file

@ -235,10 +235,10 @@ exports[`SavedObjectsTable should render normally 1`] = `
Object {
"id": "1",
"meta": Object {
"editUrl": "#/management/kibana/indexPatterns/patterns/1",
"editUrl": "#/management/kibana/dataViews/dataView/1",
"icon": "indexPatternApp",
"inAppUrl": Object {
"path": "/management/kibana/indexPatterns/patterns/1",
"path": "/management/kibana/dataViews/dataView/1",
"uiCapabilitiesPath": "management.kibana.indexPatterns",
},
"title": "MyIndexPattern*",

View file

@ -627,10 +627,10 @@ exports[`Relationships should render searches normally 1`] = `
Object {
"id": "1",
"meta": Object {
"editUrl": "/management/kibana/indexPatterns/patterns/1",
"editUrl": "/management/kibana/dataViews/dataView/1",
"icon": "indexPatternApp",
"inAppUrl": Object {
"path": "/app/management/kibana/indexPatterns/patterns/1",
"path": "/app/management/kibana/dataViews/dataView/1",
"uiCapabilitiesPath": "management.kibana.indexPatterns",
},
"title": "My Index Pattern",

View file

@ -174,10 +174,10 @@ exports[`Table prevents saved objects from being deleted 1`] = `
"attributes": Object {},
"id": "1",
"meta": Object {
"editUrl": "#/management/kibana/indexPatterns/patterns/1",
"editUrl": "#/management/kibana/dataViews/dataView/1",
"icon": "indexPatternApp",
"inAppUrl": Object {
"path": "/management/kibana/indexPatterns/patterns/1",
"path": "/management/kibana/dataViews/dataView/1",
"uiCapabilitiesPath": "management.kibana.indexPatterns",
},
"title": "MyIndexPattern*",
@ -394,10 +394,10 @@ exports[`Table should render normally 1`] = `
"attributes": Object {},
"id": "1",
"meta": Object {
"editUrl": "#/management/kibana/indexPatterns/patterns/1",
"editUrl": "#/management/kibana/dataViews/dataView/1",
"icon": "indexPatternApp",
"inAppUrl": Object {
"path": "/management/kibana/indexPatterns/patterns/1",
"path": "/management/kibana/dataViews/dataView/1",
"uiCapabilitiesPath": "management.kibana.indexPatterns",
},
"title": "MyIndexPattern*",

View file

@ -74,9 +74,9 @@ describe('Relationships', () => {
meta: {
title: 'MyIndexPattern*',
icon: 'indexPatternApp',
editUrl: '#/management/kibana/indexPatterns/patterns/1',
editUrl: '#/management/kibana/dataViews/dataView/1',
inAppUrl: {
path: '/management/kibana/indexPatterns/patterns/1',
path: '/management/kibana/dataViews/dataView/1',
uiCapabilitiesPath: 'management.kibana.indexPatterns',
},
},
@ -111,10 +111,10 @@ describe('Relationships', () => {
id: '1',
relationship: 'child',
meta: {
editUrl: '/management/kibana/indexPatterns/patterns/1',
editUrl: '/management/kibana/dataViews/dataView/1',
icon: 'indexPatternApp',
inAppUrl: {
path: '/app/management/kibana/indexPatterns/patterns/1',
path: '/app/management/kibana/dataViews/dataView/1',
uiCapabilitiesPath: 'management.kibana.indexPatterns',
},
title: 'My Index Pattern',
@ -365,9 +365,9 @@ describe('Relationships', () => {
meta: {
title: 'MyIndexPattern*',
icon: 'indexPatternApp',
editUrl: '#/management/kibana/indexPatterns/patterns/1',
editUrl: '#/management/kibana/dataViews/dataView/1',
inAppUrl: {
path: '/management/kibana/indexPatterns/patterns/1',
path: '/management/kibana/dataViews/dataView/1',
uiCapabilitiesPath: 'management.kibana.indexPatterns',
},
},

View file

@ -28,9 +28,9 @@ const defaultProps: TableProps = {
meta: {
title: `MyIndexPattern*`,
icon: 'indexPatternApp',
editUrl: '#/management/kibana/indexPatterns/patterns/1',
editUrl: '#/management/kibana/dataViews/dataView/1',
inAppUrl: {
path: '/management/kibana/indexPatterns/patterns/1',
path: '/management/kibana/dataViews/dataView/1',
uiCapabilitiesPath: 'management.kibana.indexPatterns',
},
},
@ -59,9 +59,9 @@ const defaultProps: TableProps = {
meta: {
title: `MyIndexPattern*`,
icon: 'indexPatternApp',
editUrl: '#/management/kibana/indexPatterns/patterns/1',
editUrl: '#/management/kibana/dataViews/dataView/1',
inAppUrl: {
path: '/management/kibana/indexPatterns/patterns/1',
path: '/management/kibana/dataViews/dataView/1',
uiCapabilitiesPath: 'management.kibana.indexPatterns',
},
},

View file

@ -152,9 +152,9 @@ describe('SavedObjectsTable', () => {
meta: {
title: `MyIndexPattern*`,
icon: 'indexPatternApp',
editUrl: '#/management/kibana/indexPatterns/patterns/1',
editUrl: '#/management/kibana/dataViews/dataView/1',
inAppUrl: {
path: '/management/kibana/indexPatterns/patterns/1',
path: '/management/kibana/dataViews/dataView/1',
uiCapabilitiesPath: 'management.kibana.indexPatterns',
},
},

View file

@ -244,10 +244,9 @@ export default function ({ getService }: FtrProviderContext) {
icon: 'indexPatternApp',
title: 'saved_objects*',
hiddenType: false,
editUrl:
'/management/kibana/indexPatterns/patterns/8963ca30-3224-11e8-a572-ffca06da1357',
editUrl: '/management/kibana/dataViews/dataView/8963ca30-3224-11e8-a572-ffca06da1357',
inAppUrl: {
path: '/app/management/kibana/indexPatterns/patterns/8963ca30-3224-11e8-a572-ffca06da1357',
path: '/app/management/kibana/dataViews/dataView/8963ca30-3224-11e8-a572-ffca06da1357',
uiCapabilitiesPath: 'management.kibana.indexPatterns',
},
namespaceType: 'single',

View file

@ -86,10 +86,9 @@ export default function ({ getService }: FtrProviderContext) {
meta: {
title: 'saved_objects*',
icon: 'indexPatternApp',
editUrl:
'/management/kibana/indexPatterns/patterns/8963ca30-3224-11e8-a572-ffca06da1357',
editUrl: '/management/kibana/dataViews/dataView/8963ca30-3224-11e8-a572-ffca06da1357',
inAppUrl: {
path: '/app/management/kibana/indexPatterns/patterns/8963ca30-3224-11e8-a572-ffca06da1357',
path: '/app/management/kibana/dataViews/dataView/8963ca30-3224-11e8-a572-ffca06da1357',
uiCapabilitiesPath: 'management.kibana.indexPatterns',
},
namespaceType: 'single',
@ -128,10 +127,9 @@ export default function ({ getService }: FtrProviderContext) {
meta: {
icon: 'indexPatternApp',
title: 'saved_objects*',
editUrl:
'/management/kibana/indexPatterns/patterns/8963ca30-3224-11e8-a572-ffca06da1357',
editUrl: '/management/kibana/dataViews/dataView/8963ca30-3224-11e8-a572-ffca06da1357',
inAppUrl: {
path: '/app/management/kibana/indexPatterns/patterns/8963ca30-3224-11e8-a572-ffca06da1357',
path: '/app/management/kibana/dataViews/dataView/8963ca30-3224-11e8-a572-ffca06da1357',
uiCapabilitiesPath: 'management.kibana.indexPatterns',
},
namespaceType: 'single',

View file

@ -133,7 +133,7 @@ export default function ({ getService, getPageObjects }) {
return retry.try(function tryingForTime() {
return browser.getCurrentUrl().then(function (currentUrl) {
log.debug('currentUrl = ' + currentUrl);
expect(currentUrl).to.contain('management/kibana/indexPatterns');
expect(currentUrl).to.contain('management/kibana/dataViews');
});
});
});

View file

@ -0,0 +1,53 @@
/*
* 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.
*/
import expect from '@kbn/expect';
import { FtrProviderContext } from '../../ftr_provider_context';
export default function ({ getPageObjects, getService }: FtrProviderContext) {
const browser = getService('browser');
const PageObjects = getPageObjects(['settings', 'common', 'header']);
const kibanaServer = getService('kibanaServer');
describe('legacy urls redirect correctly', () => {
before(async function () {
await browser.setWindowSize(1200, 800);
await kibanaServer.importExport.load('test/functional/fixtures/kbn_archiver/discover');
await kibanaServer.uiSettings.replace({});
});
after(async function afterAll() {
await kibanaServer.importExport.unload('test/functional/fixtures/kbn_archiver/discover');
});
it('redirects correctly to index pattern management', async () => {
await PageObjects.settings.navigateTo();
await PageObjects.settings.clickKibanaIndexPatterns();
await PageObjects.settings.clickIndexPatternLogstash();
const url = await (await browser.getCurrentUrl()).split('#')[0];
const modifiedUrl = url.replace('indexPatterns', 'dataViews');
await browser.navigateTo(modifiedUrl);
await PageObjects.header.waitUntilLoadingHasFinished();
const newUrl = (await browser.getCurrentUrl()).split('#')[0];
expect(newUrl).to.equal(url);
});
it('redirects correctly to specific index pattern', async () => {
await PageObjects.settings.clickKibanaIndexPatterns();
await PageObjects.settings.clickIndexPatternLogstash();
const url = await (await browser.getCurrentUrl()).split('#')[0];
const modifiedUrl = url.replace('patterns', 'dataView').replace('indexPatterns', 'dataViews');
await browser.navigateTo(modifiedUrl);
await PageObjects.header.waitUntilLoadingHasFinished();
const newUrl = (await browser.getCurrentUrl()).split('#')[0];
expect(newUrl).to.equal(url);
});
});
}

View file

@ -35,6 +35,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) {
loadTestFile(require.resolve('./_scripted_fields'));
loadTestFile(require.resolve('./_runtime_fields'));
loadTestFile(require.resolve('./_field_formatter'));
loadTestFile(require.resolve('./_legacy_url_redirect'));
});
describe('', function () {

View file

@ -43,10 +43,10 @@ export class SettingsPageObject extends FtrService {
}
async clickKibanaIndexPatterns() {
this.log.debug('clickKibanaIndexPatterns link');
this.log.debug('clickKibanaDataViews link');
const currentUrl = await this.browser.getCurrentUrl();
if (!currentUrl.endsWith('indexPatterns')) {
await this.testSubjects.click('indexPatterns');
if (!currentUrl.endsWith('dataViews')) {
await this.testSubjects.click('dataViews');
}
await this.header.waitUntilLoadingHasFinished();
@ -384,10 +384,10 @@ export class SettingsPageObject extends FtrService {
await this.retry.try(async () => {
const currentUrl = await this.browser.getCurrentUrl();
this.log.info('currentUrl', currentUrl);
if (!currentUrl.match(/indexPatterns\/.+\?/)) {
throw new Error('Index pattern not created');
if (!currentUrl.match(/dataViews\/.+\?/)) {
throw new Error('Data view not created');
} else {
this.log.debug('Index pattern created: ' + currentUrl);
this.log.debug('Data view created: ' + currentUrl);
}
});

View file

@ -183,7 +183,7 @@ export class ImportCompleteView extends Component<Props, {}> {
<a
data-test-subj="indexManagementNewIndexLink"
target="_blank"
href={getHttp().basePath.prepend('/app/management/kibana/indexPatterns')}
href={getHttp().basePath.prepend('/app/management/kibana/dataViews')}
>
<FormattedMessage
id="xpack.fileUpload.importComplete.indexMgmtLink"

View file

@ -39,7 +39,7 @@ exports[`should render no index pattern warning when there are no matching index
values={Object {}}
/>
<EuiLink
href="abc//app/management/kibana/indexPatterns"
href="abc//app/management/kibana/dataViews"
>
<FormattedMessage
defaultMessage="Create a data view."

View file

@ -93,7 +93,7 @@ export class GeoIndexPatternSelect extends Component<Props, State> {
id="xpack.maps.noIndexPattern.doThisPrefixDescription"
defaultMessage="You'll need to "
/>
<EuiLink href={getHttp().basePath.prepend(`/app/management/kibana/indexPatterns`)}>
<EuiLink href={getHttp().basePath.prepend(`/app/management/kibana/dataViews`)}>
<FormattedMessage
id="xpack.maps.noIndexPattern.doThisLinkTextDescription"
defaultMessage="Create a data view."

View file

@ -31,7 +31,7 @@ export const IndexPatternPrompt: FC<Props> = ({ destIndex }) => {
destIndex,
linkToDataViewManagement: (
<EuiLink
href={`${basePath.get()}/app/management/kibana/indexPatterns/create`}
href={`${basePath.get()}/app/management/kibana/dataViews/create`}
target="_blank"
>
<FormattedMessage

View file

@ -6,7 +6,7 @@ exports[`IndexPatternsMissingPrompt renders correctly against snapshot 1`] = `
<EuiButton
color="primary"
fill={true}
href="/test/base/path/app/management/kibana/indexPatterns"
href="/test/base/path/app/management/kibana/dataViews"
target="_blank"
>
Configure index patterns

View file

@ -62,7 +62,7 @@ export const IndexPatternsMissingPromptComponent = () => {
}
actions={
<EuiButton
href={`${kibanaBasePath}/management/kibana/indexPatterns`}
href={`${kibanaBasePath}/management/kibana/dataViews`}
color="primary"
target="_blank"
fill

View file

@ -100,9 +100,7 @@ export class GeoIndexPatternSelect extends Component<Props, State> {
id="xpack.stackAlerts.geoContainment.noIndexPattern.doThisPrefixDescription"
defaultMessage="You'll need to "
/>
<EuiLink
href={this.props.http.basePath.prepend(`/app/management/kibana/indexPatterns`)}
>
<EuiLink href={this.props.http.basePath.prepend(`/app/management/kibana/dataViews`)}>
<FormattedMessage
id="xpack.stackAlerts.geoContainment.noIndexPattern.doThisLinkTextDescription"
defaultMessage="Create a data view."

View file

@ -43,7 +43,7 @@ quantities of data the frequency listed below (20000ms = 20s) or higher:
There are 3 separate tabs you'll need for a combination of loading and viewing the
data. Since you'll be jumping between them, it might be easiest to just open them
upfront. Each is preceded by `https://localhost:5601/<your dev env prefix>/app/`:
- Stack Management > Data Views: `management/kibana/indexPatterns`
- Stack Management > Data Views: `management/kibana/dataViews`
- Stack Management > Alerts & Actions: `management/insightsAndAlerting/triggersActions/alerts`
- Maps: `maps`

View file

@ -68,14 +68,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
});
expect(sections[1]).to.eql({
sectionId: 'kibana',
sectionLinks: [
'indexPatterns',
'objects',
'tags',
'search_sessions',
'spaces',
'settings',
],
sectionLinks: ['dataViews', 'objects', 'tags', 'search_sessions', 'spaces', 'settings'],
});
});
});