mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[uiSettings] support overriding uiSettings from the config file (#21628)
This PR implements the `uiSettings.overrides` setting which [when stored in kibana.yml or passed as config args when starting Kibana] allows forcing some uiSettings to always have a specific value. This setting accepts a map of uiSetting keys to values that will always be used to override whatever is stored in the config saved object.  When users view the settings in the advanced settings UI they are disabled and describe why they can't be changed.  Attempting to change these values from the uiSettings client/service/api is also prevented, causing a 400 error to be thrown and/or sent as the response.
This commit is contained in:
parent
53a69f6a29
commit
e49d5f3b10
25 changed files with 1506 additions and 62 deletions
|
@ -42,8 +42,7 @@ export async function rebuildCache(settings, logger) {
|
|||
'--env.name=production',
|
||||
'--optimize.useBundleCache=false',
|
||||
'--server.autoListen=false',
|
||||
'--plugins.initialize=false',
|
||||
'--uiSettings.enabled=false'
|
||||
'--plugins.initialize=false'
|
||||
];
|
||||
|
||||
const proc = execa(process.execPath, kibanaArgs, {
|
||||
|
|
|
@ -76,7 +76,7 @@ exports[`AdvancedSettings should render normally 1`] = `
|
|||
categoryCounts={
|
||||
Object {
|
||||
"elasticsearch": 2,
|
||||
"general": 7,
|
||||
"general": 11,
|
||||
}
|
||||
}
|
||||
clear={[Function]}
|
||||
|
@ -96,6 +96,7 @@ exports[`AdvancedSettings should render normally 1`] = `
|
|||
"description": "Description for Test array setting",
|
||||
"displayName": "Test array setting",
|
||||
"isCustom": undefined,
|
||||
"isOverridden": false,
|
||||
"name": "test:array:setting",
|
||||
"options": undefined,
|
||||
"readonly": false,
|
||||
|
@ -111,6 +112,7 @@ exports[`AdvancedSettings should render normally 1`] = `
|
|||
"description": "Description for Test boolean setting",
|
||||
"displayName": "Test boolean setting",
|
||||
"isCustom": undefined,
|
||||
"isOverridden": false,
|
||||
"name": "test:boolean:setting",
|
||||
"options": undefined,
|
||||
"readonly": false,
|
||||
|
@ -128,6 +130,7 @@ exports[`AdvancedSettings should render normally 1`] = `
|
|||
"description": "Description for Test custom string setting",
|
||||
"displayName": "Test custom string setting",
|
||||
"isCustom": undefined,
|
||||
"isOverridden": false,
|
||||
"name": "test:customstring:setting",
|
||||
"options": undefined,
|
||||
"readonly": false,
|
||||
|
@ -143,12 +146,83 @@ exports[`AdvancedSettings should render normally 1`] = `
|
|||
"description": "Description for Test image setting",
|
||||
"displayName": "Test image setting",
|
||||
"isCustom": undefined,
|
||||
"isOverridden": false,
|
||||
"name": "test:image:setting",
|
||||
"options": undefined,
|
||||
"readonly": false,
|
||||
"type": "image",
|
||||
"value": undefined,
|
||||
},
|
||||
Object {
|
||||
"ariaName": "test is overridden json",
|
||||
"category": Array [
|
||||
"general",
|
||||
],
|
||||
"defVal": "{
|
||||
\\"foo\\": \\"bar\\"
|
||||
}",
|
||||
"description": "Description for overridden json",
|
||||
"displayName": "An overridden json",
|
||||
"isCustom": undefined,
|
||||
"isOverridden": true,
|
||||
"name": "test:isOverridden:json",
|
||||
"options": undefined,
|
||||
"readonly": false,
|
||||
"type": "json",
|
||||
"value": undefined,
|
||||
},
|
||||
Object {
|
||||
"ariaName": "test is overridden number",
|
||||
"category": Array [
|
||||
"general",
|
||||
],
|
||||
"defVal": 1234,
|
||||
"description": "Description for overridden number",
|
||||
"displayName": "An overridden number",
|
||||
"isCustom": undefined,
|
||||
"isOverridden": true,
|
||||
"name": "test:isOverridden:number",
|
||||
"options": undefined,
|
||||
"readonly": false,
|
||||
"type": "number",
|
||||
"value": undefined,
|
||||
},
|
||||
Object {
|
||||
"ariaName": "test is overridden select",
|
||||
"category": Array [
|
||||
"general",
|
||||
],
|
||||
"defVal": "orange",
|
||||
"description": "Description for overridden select setting",
|
||||
"displayName": "Test overridden select setting",
|
||||
"isCustom": undefined,
|
||||
"isOverridden": true,
|
||||
"name": "test:isOverridden:select",
|
||||
"options": Array [
|
||||
"apple",
|
||||
"orange",
|
||||
"banana",
|
||||
],
|
||||
"readonly": false,
|
||||
"type": "select",
|
||||
"value": undefined,
|
||||
},
|
||||
Object {
|
||||
"ariaName": "test is overridden string",
|
||||
"category": Array [
|
||||
"general",
|
||||
],
|
||||
"defVal": "foo",
|
||||
"description": "Description for overridden string",
|
||||
"displayName": "An overridden string",
|
||||
"isCustom": undefined,
|
||||
"isOverridden": true,
|
||||
"name": "test:isOverridden:string",
|
||||
"options": undefined,
|
||||
"readonly": false,
|
||||
"type": "string",
|
||||
"value": undefined,
|
||||
},
|
||||
Object {
|
||||
"ariaName": "test json setting",
|
||||
"category": Array [
|
||||
|
@ -158,6 +232,7 @@ exports[`AdvancedSettings should render normally 1`] = `
|
|||
"description": "Description for Test json setting",
|
||||
"displayName": "Test json setting",
|
||||
"isCustom": undefined,
|
||||
"isOverridden": false,
|
||||
"name": "test:json:setting",
|
||||
"options": undefined,
|
||||
"readonly": false,
|
||||
|
@ -173,6 +248,7 @@ exports[`AdvancedSettings should render normally 1`] = `
|
|||
"description": "Description for Test markdown setting",
|
||||
"displayName": "Test markdown setting",
|
||||
"isCustom": undefined,
|
||||
"isOverridden": false,
|
||||
"name": "test:markdown:setting",
|
||||
"options": undefined,
|
||||
"readonly": false,
|
||||
|
@ -188,6 +264,7 @@ exports[`AdvancedSettings should render normally 1`] = `
|
|||
"description": "Description for Test number setting",
|
||||
"displayName": "Test number setting",
|
||||
"isCustom": undefined,
|
||||
"isOverridden": false,
|
||||
"name": "test:number:setting",
|
||||
"options": undefined,
|
||||
"readonly": false,
|
||||
|
@ -203,6 +280,7 @@ exports[`AdvancedSettings should render normally 1`] = `
|
|||
"description": "Description for Test select setting",
|
||||
"displayName": "Test select setting",
|
||||
"isCustom": undefined,
|
||||
"isOverridden": false,
|
||||
"name": "test:select:setting",
|
||||
"options": Array [
|
||||
"apple",
|
||||
|
@ -222,6 +300,7 @@ exports[`AdvancedSettings should render normally 1`] = `
|
|||
"description": "Description for Test string setting",
|
||||
"displayName": "Test string setting",
|
||||
"isCustom": undefined,
|
||||
"isOverridden": false,
|
||||
"name": "test:string:setting",
|
||||
"options": undefined,
|
||||
"readonly": false,
|
||||
|
@ -329,7 +408,7 @@ exports[`AdvancedSettings should render specific setting if given setting key 1`
|
|||
categoryCounts={
|
||||
Object {
|
||||
"elasticsearch": 2,
|
||||
"general": 7,
|
||||
"general": 11,
|
||||
}
|
||||
}
|
||||
clear={[Function]}
|
||||
|
@ -347,6 +426,7 @@ exports[`AdvancedSettings should render specific setting if given setting key 1`
|
|||
"description": "Description for Test string setting",
|
||||
"displayName": "Test string setting",
|
||||
"isCustom": undefined,
|
||||
"isOverridden": false,
|
||||
"name": "test:string:setting",
|
||||
"options": undefined,
|
||||
"readonly": false,
|
||||
|
|
|
@ -90,6 +90,7 @@ export class AdvancedSettings extends Component {
|
|||
name: setting[0],
|
||||
value: setting[1].userValue,
|
||||
isCustom: config.isCustom(setting[0]),
|
||||
isOverridden: config.isOverridden(setting[0]),
|
||||
});
|
||||
})
|
||||
.filter((c) => !c.readonly)
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import dedent from 'dedent';
|
||||
|
||||
import { AdvancedSettings } from './advanced_settings';
|
||||
|
||||
|
@ -44,6 +45,7 @@ const config = {
|
|||
set: () => {},
|
||||
remove: () => {},
|
||||
isCustom: (setting) => setting.isCustom,
|
||||
isOverridden: (key) => Boolean(config.getAll()[key].isOverridden),
|
||||
getAll: () => {
|
||||
return {
|
||||
'test:array:setting': {
|
||||
|
@ -109,6 +111,39 @@ const config = {
|
|||
type: 'string',
|
||||
isCustom: true,
|
||||
},
|
||||
'test:isOverridden:string': {
|
||||
isOverridden: true,
|
||||
value: 'foo',
|
||||
name: 'An overridden string',
|
||||
description: 'Description for overridden string',
|
||||
type: 'string',
|
||||
},
|
||||
'test:isOverridden:number': {
|
||||
isOverridden: true,
|
||||
value: 1234,
|
||||
name: 'An overridden number',
|
||||
description: 'Description for overridden number',
|
||||
type: 'number',
|
||||
},
|
||||
'test:isOverridden:json': {
|
||||
isOverridden: true,
|
||||
value: dedent`
|
||||
{
|
||||
"foo": "bar"
|
||||
}
|
||||
`,
|
||||
name: 'An overridden json',
|
||||
description: 'Description for overridden json',
|
||||
type: 'json',
|
||||
},
|
||||
'test:isOverridden:select': {
|
||||
isOverridden: true,
|
||||
value: 'orange',
|
||||
name: 'Test overridden select setting',
|
||||
description: 'Description for overridden select setting',
|
||||
type: 'select',
|
||||
options: ['apple', 'orange', 'banana'],
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,5 +1,106 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Field for array setting should render as read only with help text if overridden 1`] = `
|
||||
<EuiFlexGroup
|
||||
alignItems="stretch"
|
||||
className="advancedSettings__field"
|
||||
component="div"
|
||||
direction="row"
|
||||
gutterSize="l"
|
||||
justifyContent="flexStart"
|
||||
responsive={true}
|
||||
wrap={false}
|
||||
>
|
||||
<EuiFlexItem
|
||||
component="div"
|
||||
grow={false}
|
||||
>
|
||||
<EuiDescribedFormGroup
|
||||
className="advancedSettings__field__wrapper"
|
||||
description={
|
||||
<UNDEFINED>
|
||||
<div
|
||||
dangerouslySetInnerHTML={
|
||||
Object {
|
||||
"__html": "Description for Array test setting",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<UNDEFINED>
|
||||
<EuiSpacer
|
||||
size="s"
|
||||
/>
|
||||
<EuiText
|
||||
grow={true}
|
||||
size="xs"
|
||||
>
|
||||
<UNDEFINED>
|
||||
Default:
|
||||
<EuiCode>
|
||||
default_value
|
||||
</EuiCode>
|
||||
</UNDEFINED>
|
||||
</EuiText>
|
||||
</UNDEFINED>
|
||||
</UNDEFINED>
|
||||
}
|
||||
fullWidth={false}
|
||||
gutterSize="l"
|
||||
idAria="array:test:setting-aria"
|
||||
title={
|
||||
<h3>
|
||||
Array test setting
|
||||
|
||||
</h3>
|
||||
}
|
||||
titleSize="xs"
|
||||
>
|
||||
<EuiFormRow
|
||||
describedByIds={
|
||||
Array [
|
||||
"array:test:setting-aria",
|
||||
]
|
||||
}
|
||||
error={null}
|
||||
fullWidth={false}
|
||||
hasEmptyLabelSpace={false}
|
||||
helpText={
|
||||
<EuiText
|
||||
grow={true}
|
||||
size="xs"
|
||||
>
|
||||
This setting is overriden by the Kibana server and can not be changed.
|
||||
</EuiText>
|
||||
}
|
||||
isInvalid={false}
|
||||
label={
|
||||
<span
|
||||
aria-label="array test setting"
|
||||
>
|
||||
array:test:setting
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<EuiFieldText
|
||||
compressed={false}
|
||||
data-test-subj="advancedSetting-editField-array:test:setting"
|
||||
disabled={true}
|
||||
fullWidth={false}
|
||||
isLoading={false}
|
||||
onChange={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
value="user, value"
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiDescribedFormGroup>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
component="div"
|
||||
grow={false}
|
||||
/>
|
||||
</EuiFlexGroup>
|
||||
`;
|
||||
|
||||
exports[`Field for array setting should render custom setting icon if it is custom 1`] = `
|
||||
<EuiFlexGroup
|
||||
alignItems="stretch"
|
||||
|
@ -270,6 +371,105 @@ exports[`Field for array setting should render user value if there is user value
|
|||
</EuiFlexGroup>
|
||||
`;
|
||||
|
||||
exports[`Field for boolean setting should render as read only with help text if overridden 1`] = `
|
||||
<EuiFlexGroup
|
||||
alignItems="stretch"
|
||||
className="advancedSettings__field"
|
||||
component="div"
|
||||
direction="row"
|
||||
gutterSize="l"
|
||||
justifyContent="flexStart"
|
||||
responsive={true}
|
||||
wrap={false}
|
||||
>
|
||||
<EuiFlexItem
|
||||
component="div"
|
||||
grow={false}
|
||||
>
|
||||
<EuiDescribedFormGroup
|
||||
className="advancedSettings__field__wrapper"
|
||||
description={
|
||||
<UNDEFINED>
|
||||
<div
|
||||
dangerouslySetInnerHTML={
|
||||
Object {
|
||||
"__html": "Description for Boolean test setting",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<UNDEFINED>
|
||||
<EuiSpacer
|
||||
size="s"
|
||||
/>
|
||||
<EuiText
|
||||
grow={true}
|
||||
size="xs"
|
||||
>
|
||||
<UNDEFINED>
|
||||
Default:
|
||||
<EuiCode>
|
||||
true
|
||||
</EuiCode>
|
||||
</UNDEFINED>
|
||||
</EuiText>
|
||||
</UNDEFINED>
|
||||
</UNDEFINED>
|
||||
}
|
||||
fullWidth={false}
|
||||
gutterSize="l"
|
||||
idAria="boolean:test:setting-aria"
|
||||
title={
|
||||
<h3>
|
||||
Boolean test setting
|
||||
|
||||
</h3>
|
||||
}
|
||||
titleSize="xs"
|
||||
>
|
||||
<EuiFormRow
|
||||
describedByIds={
|
||||
Array [
|
||||
"boolean:test:setting-aria",
|
||||
]
|
||||
}
|
||||
error={null}
|
||||
fullWidth={false}
|
||||
hasEmptyLabelSpace={false}
|
||||
helpText={
|
||||
<EuiText
|
||||
grow={true}
|
||||
size="xs"
|
||||
>
|
||||
This setting is overriden by the Kibana server and can not be changed.
|
||||
</EuiText>
|
||||
}
|
||||
isInvalid={false}
|
||||
label={
|
||||
<span
|
||||
aria-label="boolean test setting"
|
||||
>
|
||||
boolean:test:setting
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<EuiSwitch
|
||||
checked={false}
|
||||
data-test-subj="advancedSetting-editField-boolean:test:setting"
|
||||
disabled={true}
|
||||
label="Off"
|
||||
onChange={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiDescribedFormGroup>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
component="div"
|
||||
grow={false}
|
||||
/>
|
||||
</EuiFlexGroup>
|
||||
`;
|
||||
|
||||
exports[`Field for boolean setting should render custom setting icon if it is custom 1`] = `
|
||||
<EuiFlexGroup
|
||||
alignItems="stretch"
|
||||
|
@ -534,6 +734,104 @@ exports[`Field for boolean setting should render user value if there is user val
|
|||
</EuiFlexGroup>
|
||||
`;
|
||||
|
||||
exports[`Field for image setting should render as read only with help text if overridden 1`] = `
|
||||
<EuiFlexGroup
|
||||
alignItems="stretch"
|
||||
className="advancedSettings__field"
|
||||
component="div"
|
||||
direction="row"
|
||||
gutterSize="l"
|
||||
justifyContent="flexStart"
|
||||
responsive={true}
|
||||
wrap={false}
|
||||
>
|
||||
<EuiFlexItem
|
||||
component="div"
|
||||
grow={false}
|
||||
>
|
||||
<EuiDescribedFormGroup
|
||||
className="advancedSettings__field__wrapper"
|
||||
description={
|
||||
<UNDEFINED>
|
||||
<div
|
||||
dangerouslySetInnerHTML={
|
||||
Object {
|
||||
"__html": "Description for Image test setting",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<UNDEFINED>
|
||||
<EuiSpacer
|
||||
size="s"
|
||||
/>
|
||||
<EuiText
|
||||
grow={true}
|
||||
size="xs"
|
||||
>
|
||||
<UNDEFINED>
|
||||
Default:
|
||||
<EuiCode>
|
||||
null
|
||||
</EuiCode>
|
||||
</UNDEFINED>
|
||||
</EuiText>
|
||||
</UNDEFINED>
|
||||
</UNDEFINED>
|
||||
}
|
||||
fullWidth={false}
|
||||
gutterSize="l"
|
||||
idAria="image:test:setting-aria"
|
||||
title={
|
||||
<h3>
|
||||
Image test setting
|
||||
|
||||
</h3>
|
||||
}
|
||||
titleSize="xs"
|
||||
>
|
||||
<EuiFormRow
|
||||
describedByIds={
|
||||
Array [
|
||||
"image:test:setting-aria",
|
||||
]
|
||||
}
|
||||
error={null}
|
||||
fullWidth={false}
|
||||
hasEmptyLabelSpace={false}
|
||||
helpText={
|
||||
<EuiText
|
||||
grow={true}
|
||||
size="xs"
|
||||
>
|
||||
This setting is overriden by the Kibana server and can not be changed.
|
||||
</EuiText>
|
||||
}
|
||||
isInvalid={false}
|
||||
label={
|
||||
<span
|
||||
aria-label="image test setting"
|
||||
>
|
||||
image:test:setting
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<EuiImage
|
||||
allowFullScreen={true}
|
||||
alt="image:test:setting"
|
||||
fullScreenIconColor="light"
|
||||
size="original"
|
||||
url="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=="
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiDescribedFormGroup>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
component="div"
|
||||
grow={false}
|
||||
/>
|
||||
</EuiFlexGroup>
|
||||
`;
|
||||
|
||||
exports[`Field for image setting should render custom setting icon if it is custom 1`] = `
|
||||
<EuiFlexGroup
|
||||
alignItems="stretch"
|
||||
|
@ -810,6 +1108,127 @@ exports[`Field for image setting should render user value if there is user value
|
|||
</EuiFlexGroup>
|
||||
`;
|
||||
|
||||
exports[`Field for json setting should render as read only with help text if overridden 1`] = `
|
||||
<EuiFlexGroup
|
||||
alignItems="stretch"
|
||||
className="advancedSettings__field"
|
||||
component="div"
|
||||
direction="row"
|
||||
gutterSize="l"
|
||||
justifyContent="flexStart"
|
||||
responsive={true}
|
||||
wrap={false}
|
||||
>
|
||||
<EuiFlexItem
|
||||
component="div"
|
||||
grow={false}
|
||||
>
|
||||
<EuiDescribedFormGroup
|
||||
className="advancedSettings__field__wrapper"
|
||||
description={
|
||||
<UNDEFINED>
|
||||
<div
|
||||
dangerouslySetInnerHTML={
|
||||
Object {
|
||||
"__html": "Description for Json test setting",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<UNDEFINED>
|
||||
<EuiSpacer
|
||||
size="s"
|
||||
/>
|
||||
<EuiText
|
||||
grow={true}
|
||||
size="xs"
|
||||
>
|
||||
<UNDEFINED>
|
||||
Default:
|
||||
<EuiCodeBlock
|
||||
language="json"
|
||||
overflowHeight={null}
|
||||
paddingSize="s"
|
||||
>
|
||||
{}
|
||||
</EuiCodeBlock>
|
||||
</UNDEFINED>
|
||||
</EuiText>
|
||||
</UNDEFINED>
|
||||
</UNDEFINED>
|
||||
}
|
||||
fullWidth={false}
|
||||
gutterSize="l"
|
||||
idAria="json:test:setting-aria"
|
||||
title={
|
||||
<h3>
|
||||
Json test setting
|
||||
|
||||
</h3>
|
||||
}
|
||||
titleSize="xs"
|
||||
>
|
||||
<EuiFormRow
|
||||
describedByIds={
|
||||
Array [
|
||||
"json:test:setting-aria",
|
||||
]
|
||||
}
|
||||
error={null}
|
||||
fullWidth={false}
|
||||
hasEmptyLabelSpace={false}
|
||||
helpText={
|
||||
<EuiText
|
||||
grow={true}
|
||||
size="xs"
|
||||
>
|
||||
This setting is overriden by the Kibana server and can not be changed.
|
||||
</EuiText>
|
||||
}
|
||||
isInvalid={false}
|
||||
label={
|
||||
<span
|
||||
aria-label="json test setting"
|
||||
>
|
||||
json:test:setting
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<div
|
||||
data-test-subj="advancedSetting-editField-json:test:setting"
|
||||
>
|
||||
<EuiCodeEditor
|
||||
editorProps={
|
||||
Object {
|
||||
"$blockScrolling": Infinity,
|
||||
}
|
||||
}
|
||||
height="auto"
|
||||
isReadOnly={true}
|
||||
maxLines={30}
|
||||
minLines={6}
|
||||
mode="json"
|
||||
onChange={[Function]}
|
||||
setOptions={
|
||||
Object {
|
||||
"showLineNumbers": false,
|
||||
"tabSize": 2,
|
||||
}
|
||||
}
|
||||
theme="textmate"
|
||||
value="{\\"hello\\": \\"world\\"}"
|
||||
width="100%"
|
||||
/>
|
||||
</div>
|
||||
</EuiFormRow>
|
||||
</EuiDescribedFormGroup>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
component="div"
|
||||
grow={false}
|
||||
/>
|
||||
</EuiFlexGroup>
|
||||
`;
|
||||
|
||||
exports[`Field for json setting should render custom setting icon if it is custom 1`] = `
|
||||
<EuiFlexGroup
|
||||
alignItems="stretch"
|
||||
|
@ -883,6 +1302,7 @@ exports[`Field for json setting should render custom setting icon if it is custo
|
|||
}
|
||||
}
|
||||
height="auto"
|
||||
isReadOnly={false}
|
||||
maxLines={30}
|
||||
minLines={6}
|
||||
mode="json"
|
||||
|
@ -1011,6 +1431,7 @@ exports[`Field for json setting should render default value if there is no user
|
|||
}
|
||||
}
|
||||
height="auto"
|
||||
isReadOnly={false}
|
||||
maxLines={30}
|
||||
minLines={6}
|
||||
mode="json"
|
||||
|
@ -1139,6 +1560,7 @@ exports[`Field for json setting should render user value if there is user value
|
|||
}
|
||||
}
|
||||
height="auto"
|
||||
isReadOnly={false}
|
||||
maxLines={30}
|
||||
minLines={6}
|
||||
mode="json"
|
||||
|
@ -1164,6 +1586,123 @@ exports[`Field for json setting should render user value if there is user value
|
|||
</EuiFlexGroup>
|
||||
`;
|
||||
|
||||
exports[`Field for markdown setting should render as read only with help text if overridden 1`] = `
|
||||
<EuiFlexGroup
|
||||
alignItems="stretch"
|
||||
className="advancedSettings__field"
|
||||
component="div"
|
||||
direction="row"
|
||||
gutterSize="l"
|
||||
justifyContent="flexStart"
|
||||
responsive={true}
|
||||
wrap={false}
|
||||
>
|
||||
<EuiFlexItem
|
||||
component="div"
|
||||
grow={false}
|
||||
>
|
||||
<EuiDescribedFormGroup
|
||||
className="advancedSettings__field__wrapper"
|
||||
description={
|
||||
<UNDEFINED>
|
||||
<div
|
||||
dangerouslySetInnerHTML={
|
||||
Object {
|
||||
"__html": "Description for Markdown test setting",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<UNDEFINED>
|
||||
<EuiSpacer
|
||||
size="s"
|
||||
/>
|
||||
<EuiText
|
||||
grow={true}
|
||||
size="xs"
|
||||
>
|
||||
<UNDEFINED>
|
||||
Default:
|
||||
<EuiCode>
|
||||
null
|
||||
</EuiCode>
|
||||
</UNDEFINED>
|
||||
</EuiText>
|
||||
</UNDEFINED>
|
||||
</UNDEFINED>
|
||||
}
|
||||
fullWidth={false}
|
||||
gutterSize="l"
|
||||
idAria="markdown:test:setting-aria"
|
||||
title={
|
||||
<h3>
|
||||
Markdown test setting
|
||||
|
||||
</h3>
|
||||
}
|
||||
titleSize="xs"
|
||||
>
|
||||
<EuiFormRow
|
||||
describedByIds={
|
||||
Array [
|
||||
"markdown:test:setting-aria",
|
||||
]
|
||||
}
|
||||
error={null}
|
||||
fullWidth={false}
|
||||
hasEmptyLabelSpace={false}
|
||||
helpText={
|
||||
<EuiText
|
||||
grow={true}
|
||||
size="xs"
|
||||
>
|
||||
This setting is overriden by the Kibana server and can not be changed.
|
||||
</EuiText>
|
||||
}
|
||||
isInvalid={false}
|
||||
label={
|
||||
<span
|
||||
aria-label="markdown test setting"
|
||||
>
|
||||
markdown:test:setting
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<div
|
||||
data-test-subj="advancedSetting-editField-markdown:test:setting"
|
||||
>
|
||||
<EuiCodeEditor
|
||||
editorProps={
|
||||
Object {
|
||||
"$blockScrolling": Infinity,
|
||||
}
|
||||
}
|
||||
height="auto"
|
||||
isReadOnly={true}
|
||||
maxLines={30}
|
||||
minLines={6}
|
||||
mode="markdown"
|
||||
onChange={[Function]}
|
||||
setOptions={
|
||||
Object {
|
||||
"showLineNumbers": false,
|
||||
"tabSize": 2,
|
||||
}
|
||||
}
|
||||
theme="textmate"
|
||||
value="**bold**"
|
||||
width="100%"
|
||||
/>
|
||||
</div>
|
||||
</EuiFormRow>
|
||||
</EuiDescribedFormGroup>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
component="div"
|
||||
grow={false}
|
||||
/>
|
||||
</EuiFlexGroup>
|
||||
`;
|
||||
|
||||
exports[`Field for markdown setting should render custom setting icon if it is custom 1`] = `
|
||||
<EuiFlexGroup
|
||||
alignItems="stretch"
|
||||
|
@ -1237,6 +1776,7 @@ exports[`Field for markdown setting should render custom setting icon if it is c
|
|||
}
|
||||
}
|
||||
height="auto"
|
||||
isReadOnly={false}
|
||||
maxLines={30}
|
||||
minLines={6}
|
||||
mode="markdown"
|
||||
|
@ -1330,6 +1870,7 @@ exports[`Field for markdown setting should render default value if there is no u
|
|||
}
|
||||
}
|
||||
height="auto"
|
||||
isReadOnly={false}
|
||||
maxLines={30}
|
||||
minLines={6}
|
||||
mode="markdown"
|
||||
|
@ -1454,6 +1995,7 @@ exports[`Field for markdown setting should render user value if there is user va
|
|||
}
|
||||
}
|
||||
height="auto"
|
||||
isReadOnly={false}
|
||||
maxLines={30}
|
||||
minLines={6}
|
||||
mode="markdown"
|
||||
|
@ -1479,6 +2021,107 @@ exports[`Field for markdown setting should render user value if there is user va
|
|||
</EuiFlexGroup>
|
||||
`;
|
||||
|
||||
exports[`Field for number setting should render as read only with help text if overridden 1`] = `
|
||||
<EuiFlexGroup
|
||||
alignItems="stretch"
|
||||
className="advancedSettings__field"
|
||||
component="div"
|
||||
direction="row"
|
||||
gutterSize="l"
|
||||
justifyContent="flexStart"
|
||||
responsive={true}
|
||||
wrap={false}
|
||||
>
|
||||
<EuiFlexItem
|
||||
component="div"
|
||||
grow={false}
|
||||
>
|
||||
<EuiDescribedFormGroup
|
||||
className="advancedSettings__field__wrapper"
|
||||
description={
|
||||
<UNDEFINED>
|
||||
<div
|
||||
dangerouslySetInnerHTML={
|
||||
Object {
|
||||
"__html": "Description for Number test setting",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<UNDEFINED>
|
||||
<EuiSpacer
|
||||
size="s"
|
||||
/>
|
||||
<EuiText
|
||||
grow={true}
|
||||
size="xs"
|
||||
>
|
||||
<UNDEFINED>
|
||||
Default:
|
||||
<EuiCode>
|
||||
5
|
||||
</EuiCode>
|
||||
</UNDEFINED>
|
||||
</EuiText>
|
||||
</UNDEFINED>
|
||||
</UNDEFINED>
|
||||
}
|
||||
fullWidth={false}
|
||||
gutterSize="l"
|
||||
idAria="number:test:setting-aria"
|
||||
title={
|
||||
<h3>
|
||||
Number test setting
|
||||
|
||||
</h3>
|
||||
}
|
||||
titleSize="xs"
|
||||
>
|
||||
<EuiFormRow
|
||||
describedByIds={
|
||||
Array [
|
||||
"number:test:setting-aria",
|
||||
]
|
||||
}
|
||||
error={null}
|
||||
fullWidth={false}
|
||||
hasEmptyLabelSpace={false}
|
||||
helpText={
|
||||
<EuiText
|
||||
grow={true}
|
||||
size="xs"
|
||||
>
|
||||
This setting is overriden by the Kibana server and can not be changed.
|
||||
</EuiText>
|
||||
}
|
||||
isInvalid={false}
|
||||
label={
|
||||
<span
|
||||
aria-label="number test setting"
|
||||
>
|
||||
number:test:setting
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<EuiFieldNumber
|
||||
compressed={false}
|
||||
data-test-subj="advancedSetting-editField-number:test:setting"
|
||||
disabled={true}
|
||||
fullWidth={false}
|
||||
isLoading={false}
|
||||
onChange={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
value={10}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiDescribedFormGroup>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
component="div"
|
||||
grow={false}
|
||||
/>
|
||||
</EuiFlexGroup>
|
||||
`;
|
||||
|
||||
exports[`Field for number setting should render custom setting icon if it is custom 1`] = `
|
||||
<EuiFlexGroup
|
||||
alignItems="stretch"
|
||||
|
@ -1749,6 +2392,124 @@ exports[`Field for number setting should render user value if there is user valu
|
|||
</EuiFlexGroup>
|
||||
`;
|
||||
|
||||
exports[`Field for select setting should render as read only with help text if overridden 1`] = `
|
||||
<EuiFlexGroup
|
||||
alignItems="stretch"
|
||||
className="advancedSettings__field"
|
||||
component="div"
|
||||
direction="row"
|
||||
gutterSize="l"
|
||||
justifyContent="flexStart"
|
||||
responsive={true}
|
||||
wrap={false}
|
||||
>
|
||||
<EuiFlexItem
|
||||
component="div"
|
||||
grow={false}
|
||||
>
|
||||
<EuiDescribedFormGroup
|
||||
className="advancedSettings__field__wrapper"
|
||||
description={
|
||||
<UNDEFINED>
|
||||
<div
|
||||
dangerouslySetInnerHTML={
|
||||
Object {
|
||||
"__html": "Description for Select test setting",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<UNDEFINED>
|
||||
<EuiSpacer
|
||||
size="s"
|
||||
/>
|
||||
<EuiText
|
||||
grow={true}
|
||||
size="xs"
|
||||
>
|
||||
<UNDEFINED>
|
||||
Default:
|
||||
<EuiCode>
|
||||
orange
|
||||
</EuiCode>
|
||||
</UNDEFINED>
|
||||
</EuiText>
|
||||
</UNDEFINED>
|
||||
</UNDEFINED>
|
||||
}
|
||||
fullWidth={false}
|
||||
gutterSize="l"
|
||||
idAria="select:test:setting-aria"
|
||||
title={
|
||||
<h3>
|
||||
Select test setting
|
||||
|
||||
</h3>
|
||||
}
|
||||
titleSize="xs"
|
||||
>
|
||||
<EuiFormRow
|
||||
describedByIds={
|
||||
Array [
|
||||
"select:test:setting-aria",
|
||||
]
|
||||
}
|
||||
error={null}
|
||||
fullWidth={false}
|
||||
hasEmptyLabelSpace={false}
|
||||
helpText={
|
||||
<EuiText
|
||||
grow={true}
|
||||
size="xs"
|
||||
>
|
||||
This setting is overriden by the Kibana server and can not be changed.
|
||||
</EuiText>
|
||||
}
|
||||
isInvalid={false}
|
||||
label={
|
||||
<span
|
||||
aria-label="select test setting"
|
||||
>
|
||||
select:test:setting
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<EuiSelect
|
||||
compressed={false}
|
||||
data-test-subj="advancedSetting-editField-select:test:setting"
|
||||
disabled={true}
|
||||
fullWidth={false}
|
||||
hasNoInitialSelection={false}
|
||||
isLoading={false}
|
||||
onChange={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
options={
|
||||
Array [
|
||||
Object {
|
||||
"text": "apple",
|
||||
"value": "apple",
|
||||
},
|
||||
Object {
|
||||
"text": "orange",
|
||||
"value": "orange",
|
||||
},
|
||||
Object {
|
||||
"text": "banana",
|
||||
"value": "banana",
|
||||
},
|
||||
]
|
||||
}
|
||||
value="banana"
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiDescribedFormGroup>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
component="div"
|
||||
grow={false}
|
||||
/>
|
||||
</EuiFlexGroup>
|
||||
`;
|
||||
|
||||
exports[`Field for select setting should render custom setting icon if it is custom 1`] = `
|
||||
<EuiFlexGroup
|
||||
alignItems="stretch"
|
||||
|
@ -2070,6 +2831,107 @@ exports[`Field for select setting should render user value if there is user valu
|
|||
</EuiFlexGroup>
|
||||
`;
|
||||
|
||||
exports[`Field for string setting should render as read only with help text if overridden 1`] = `
|
||||
<EuiFlexGroup
|
||||
alignItems="stretch"
|
||||
className="advancedSettings__field"
|
||||
component="div"
|
||||
direction="row"
|
||||
gutterSize="l"
|
||||
justifyContent="flexStart"
|
||||
responsive={true}
|
||||
wrap={false}
|
||||
>
|
||||
<EuiFlexItem
|
||||
component="div"
|
||||
grow={false}
|
||||
>
|
||||
<EuiDescribedFormGroup
|
||||
className="advancedSettings__field__wrapper"
|
||||
description={
|
||||
<UNDEFINED>
|
||||
<div
|
||||
dangerouslySetInnerHTML={
|
||||
Object {
|
||||
"__html": "Description for String test setting",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<UNDEFINED>
|
||||
<EuiSpacer
|
||||
size="s"
|
||||
/>
|
||||
<EuiText
|
||||
grow={true}
|
||||
size="xs"
|
||||
>
|
||||
<UNDEFINED>
|
||||
Default:
|
||||
<EuiCode>
|
||||
null
|
||||
</EuiCode>
|
||||
</UNDEFINED>
|
||||
</EuiText>
|
||||
</UNDEFINED>
|
||||
</UNDEFINED>
|
||||
}
|
||||
fullWidth={false}
|
||||
gutterSize="l"
|
||||
idAria="string:test:setting-aria"
|
||||
title={
|
||||
<h3>
|
||||
String test setting
|
||||
|
||||
</h3>
|
||||
}
|
||||
titleSize="xs"
|
||||
>
|
||||
<EuiFormRow
|
||||
describedByIds={
|
||||
Array [
|
||||
"string:test:setting-aria",
|
||||
]
|
||||
}
|
||||
error={null}
|
||||
fullWidth={false}
|
||||
hasEmptyLabelSpace={false}
|
||||
helpText={
|
||||
<EuiText
|
||||
grow={true}
|
||||
size="xs"
|
||||
>
|
||||
This setting is overriden by the Kibana server and can not be changed.
|
||||
</EuiText>
|
||||
}
|
||||
isInvalid={false}
|
||||
label={
|
||||
<span
|
||||
aria-label="string test setting"
|
||||
>
|
||||
string:test:setting
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<EuiFieldText
|
||||
compressed={false}
|
||||
data-test-subj="advancedSetting-editField-string:test:setting"
|
||||
disabled={true}
|
||||
fullWidth={false}
|
||||
isLoading={false}
|
||||
onChange={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
value="foo"
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiDescribedFormGroup>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
component="div"
|
||||
grow={false}
|
||||
/>
|
||||
</EuiFlexGroup>
|
||||
`;
|
||||
|
||||
exports[`Field for string setting should render custom setting icon if it is custom 1`] = `
|
||||
<EuiFlexGroup
|
||||
alignItems="stretch"
|
||||
|
|
|
@ -319,7 +319,7 @@ export class Field extends PureComponent {
|
|||
|
||||
renderField(setting) {
|
||||
const { loading, changeImage, unsavedValue } = this.state;
|
||||
const { name, value, type, options } = setting;
|
||||
const { name, value, type, options, isOverridden } = setting;
|
||||
|
||||
switch(type) {
|
||||
case 'boolean':
|
||||
|
@ -328,7 +328,7 @@ export class Field extends PureComponent {
|
|||
label={!!unsavedValue ? 'On' : 'Off'}
|
||||
checked={!!unsavedValue}
|
||||
onChange={this.onFieldChange}
|
||||
disabled={loading}
|
||||
disabled={loading || isOverridden}
|
||||
onKeyDown={this.onFieldKeyDown}
|
||||
data-test-subj={`advancedSetting-editField-${name}`}
|
||||
/>
|
||||
|
@ -346,6 +346,7 @@ export class Field extends PureComponent {
|
|||
height="auto"
|
||||
minLines={6}
|
||||
maxLines={30}
|
||||
isReadOnly={isOverridden}
|
||||
setOptions={{
|
||||
showLineNumbers: false,
|
||||
tabSize: 2,
|
||||
|
@ -369,7 +370,7 @@ export class Field extends PureComponent {
|
|||
} else {
|
||||
return (
|
||||
<EuiFilePicker
|
||||
disabled={loading}
|
||||
disabled={loading || isOverridden}
|
||||
onChange={this.onImageChange}
|
||||
accept=".jpg,.jpeg,.png"
|
||||
ref={(input) => { this.changeImageForm = input; }}
|
||||
|
@ -387,7 +388,7 @@ export class Field extends PureComponent {
|
|||
})}
|
||||
onChange={this.onFieldChange}
|
||||
isLoading={loading}
|
||||
disabled={loading}
|
||||
disabled={loading || isOverridden}
|
||||
onKeyDown={this.onFieldKeyDown}
|
||||
data-test-subj={`advancedSetting-editField-${name}`}
|
||||
/>
|
||||
|
@ -398,7 +399,7 @@ export class Field extends PureComponent {
|
|||
value={unsavedValue}
|
||||
onChange={this.onFieldChange}
|
||||
isLoading={loading}
|
||||
disabled={loading}
|
||||
disabled={loading || isOverridden}
|
||||
onKeyDown={this.onFieldKeyDown}
|
||||
data-test-subj={`advancedSetting-editField-${name}`}
|
||||
/>
|
||||
|
@ -409,7 +410,7 @@ export class Field extends PureComponent {
|
|||
value={unsavedValue}
|
||||
onChange={this.onFieldChange}
|
||||
isLoading={loading}
|
||||
disabled={loading}
|
||||
disabled={loading || isOverridden}
|
||||
onKeyDown={this.onFieldKeyDown}
|
||||
data-test-subj={`advancedSetting-editField-${name}`}
|
||||
/>
|
||||
|
@ -426,6 +427,14 @@ export class Field extends PureComponent {
|
|||
}
|
||||
|
||||
renderHelpText(setting) {
|
||||
if (setting.isOverridden) {
|
||||
return (
|
||||
<EuiText size="xs">
|
||||
This setting is overriden by the Kibana server and can not be changed.
|
||||
</EuiText>
|
||||
);
|
||||
}
|
||||
|
||||
const defaultLink = this.renderResetToDefaultLink(setting);
|
||||
const imageLink = this.renderChangeImageLink(setting);
|
||||
|
||||
|
@ -538,9 +547,12 @@ export class Field extends PureComponent {
|
|||
renderActions(setting) {
|
||||
const { ariaName, name } = setting;
|
||||
const { loading, isInvalid, changeImage, savedValue, unsavedValue } = this.state;
|
||||
if(savedValue === unsavedValue && !changeImage) {
|
||||
const isDisabled = loading || setting.isOverridden;
|
||||
|
||||
if (savedValue === unsavedValue && !changeImage) {
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<EuiFormRow className="advancedSettings__field__actions" hasEmptyLabelSpace>
|
||||
<EuiFlexGroup>
|
||||
|
@ -549,7 +561,7 @@ export class Field extends PureComponent {
|
|||
fill
|
||||
aria-label={`Save ${ariaName}`}
|
||||
onClick={this.saveEdit}
|
||||
disabled={loading || isInvalid}
|
||||
disabled={isDisabled || isInvalid}
|
||||
data-test-subj={`advancedSetting-saveEditField-${name}`}
|
||||
>
|
||||
Save
|
||||
|
@ -559,7 +571,7 @@ export class Field extends PureComponent {
|
|||
<EuiButtonEmpty
|
||||
aria-label={`Cancel editing ${ariaName}`}
|
||||
onClick={() => changeImage ? this.cancelChangeImage() : this.cancelEdit()}
|
||||
disabled={loading}
|
||||
disabled={isDisabled}
|
||||
data-test-subj={`advancedSetting-cancelEditField-${name}`}
|
||||
>
|
||||
Cancel
|
||||
|
|
|
@ -42,6 +42,7 @@ const settings = {
|
|||
value: undefined,
|
||||
defVal: ['default_value'],
|
||||
isCustom: false,
|
||||
isOverridden: false,
|
||||
options: null,
|
||||
},
|
||||
boolean: {
|
||||
|
@ -53,6 +54,7 @@ const settings = {
|
|||
value: undefined,
|
||||
defVal: true,
|
||||
isCustom: false,
|
||||
isOverridden: false,
|
||||
options: null,
|
||||
},
|
||||
image: {
|
||||
|
@ -64,6 +66,7 @@ const settings = {
|
|||
value: undefined,
|
||||
defVal: null,
|
||||
isCustom: false,
|
||||
isOverridden: false,
|
||||
options: {
|
||||
maxSize: {
|
||||
length: 1000,
|
||||
|
@ -81,6 +84,7 @@ const settings = {
|
|||
value: '{"foo": "bar"}',
|
||||
defVal: '{}',
|
||||
isCustom: false,
|
||||
isOverridden: false,
|
||||
options: null,
|
||||
},
|
||||
markdown: {
|
||||
|
@ -92,6 +96,7 @@ const settings = {
|
|||
value: undefined,
|
||||
defVal: '',
|
||||
isCustom: false,
|
||||
isOverridden: false,
|
||||
options: null,
|
||||
},
|
||||
number: {
|
||||
|
@ -103,6 +108,7 @@ const settings = {
|
|||
value: undefined,
|
||||
defVal: 5,
|
||||
isCustom: false,
|
||||
isOverridden: false,
|
||||
options: null,
|
||||
},
|
||||
select: {
|
||||
|
@ -114,6 +120,7 @@ const settings = {
|
|||
value: undefined,
|
||||
defVal: 'orange',
|
||||
isCustom: false,
|
||||
isOverridden: false,
|
||||
options: ['apple', 'orange', 'banana'],
|
||||
},
|
||||
string: {
|
||||
|
@ -125,6 +132,7 @@ const settings = {
|
|||
value: undefined,
|
||||
defVal: null,
|
||||
isCustom: false,
|
||||
isOverridden: false,
|
||||
options: null,
|
||||
},
|
||||
};
|
||||
|
@ -158,6 +166,22 @@ describe('Field', () => {
|
|||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render as read only with help text if overridden', async () => {
|
||||
const component = shallow(
|
||||
<Field
|
||||
setting={{
|
||||
...setting,
|
||||
value: userValues[type],
|
||||
isOverridden: true,
|
||||
}}
|
||||
save={save}
|
||||
clear={clear}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render user value if there is user value is set', async () => {
|
||||
const component = shallow(
|
||||
<Field
|
||||
|
|
|
@ -27,7 +27,7 @@ import { DEFAULT_CATEGORY } from './default_category';
|
|||
* @param {object} current value of setting
|
||||
* @returns {object} the editable config object
|
||||
*/
|
||||
export function toEditableConfig({ def, name, value, isCustom }) {
|
||||
export function toEditableConfig({ def, name, value, isCustom, isOverridden }) {
|
||||
if (!def) {
|
||||
def = {};
|
||||
}
|
||||
|
@ -38,6 +38,7 @@ export function toEditableConfig({ def, name, value, isCustom }) {
|
|||
value,
|
||||
category: def.category && def.category.length ? def.category : [DEFAULT_CATEGORY],
|
||||
isCustom,
|
||||
isOverridden,
|
||||
readonly: !!def.readonly,
|
||||
defVal: def.value,
|
||||
type: getValType(def, value),
|
||||
|
|
|
@ -110,6 +110,10 @@ export default async () => Joi.object({
|
|||
}).default(),
|
||||
}).default(),
|
||||
|
||||
uiSettings: Joi.object().keys({
|
||||
overrides: Joi.object().unknown(true).default()
|
||||
}).default(),
|
||||
|
||||
logging: Joi.object().keys({
|
||||
silent: Joi.boolean().default(false),
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@ module.service(`config`, function ($rootScope, Promise) {
|
|||
this.isDeclared = (...args) => uiSettings.isDeclared(...args);
|
||||
this.isDefault = (...args) => uiSettings.isDefault(...args);
|
||||
this.isCustom = (...args) => uiSettings.isCustom(...args);
|
||||
this.isOverridden = (...args) => uiSettings.isOverridden(...args);
|
||||
|
||||
// modify remove() to use angular Promises
|
||||
this.remove = (key) => (
|
||||
|
|
|
@ -34,13 +34,13 @@ import { uiSettingsMixin } from '../ui_settings_mixin';
|
|||
describe('uiSettingsMixin()', () => {
|
||||
const sandbox = sinon.createSandbox();
|
||||
|
||||
async function setup(options = {}) {
|
||||
const {
|
||||
enabled = true
|
||||
} = options;
|
||||
|
||||
async function setup() {
|
||||
const config = await Config.withDefaultSchema({
|
||||
uiSettings: { enabled }
|
||||
uiSettings: {
|
||||
overrides: {
|
||||
foo: 'bar'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// maps of decorations passed to `server.decorate()`
|
||||
|
@ -111,6 +111,9 @@ describe('uiSettingsMixin()', () => {
|
|||
sinon.assert.calledOnce(uiSettingsServiceFactory);
|
||||
sinon.assert.calledWithExactly(uiSettingsServiceFactory, server, {
|
||||
foo: 'bar',
|
||||
overrides: {
|
||||
foo: 'bar'
|
||||
},
|
||||
getDefaults: sinon.match.func,
|
||||
});
|
||||
});
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { isEqual } from 'lodash';
|
||||
import expect from 'expect.js';
|
||||
import { errors as esErrors } from 'elasticsearch';
|
||||
import Chance from 'chance';
|
||||
|
@ -42,6 +41,7 @@ describe('ui settings', () => {
|
|||
const {
|
||||
getDefaults,
|
||||
defaults = {},
|
||||
overrides,
|
||||
esDocSource = {},
|
||||
savedObjectsClient = createObjectsClientStub(TYPE, ID, esDocSource)
|
||||
} = options;
|
||||
|
@ -52,6 +52,7 @@ describe('ui settings', () => {
|
|||
buildNum: BUILD_NUM,
|
||||
getDefaults: getDefaults || (() => defaults),
|
||||
savedObjectsClient,
|
||||
overrides,
|
||||
});
|
||||
|
||||
const createOrUpgradeSavedConfig = sandbox.stub(createOrUpgradeSavedConfigNS, 'createOrUpgradeSavedConfig');
|
||||
|
@ -67,21 +68,6 @@ describe('ui settings', () => {
|
|||
|
||||
afterEach(() => sandbox.restore());
|
||||
|
||||
describe('overview', () => {
|
||||
it('has expected api surface', () => {
|
||||
const { uiSettings } = setup();
|
||||
expect(uiSettings).to.have.property('get').a('function');
|
||||
expect(uiSettings).to.have.property('getAll').a('function');
|
||||
expect(uiSettings).to.have.property('getDefaults').a('function');
|
||||
expect(uiSettings).to.have.property('getRaw').a('function');
|
||||
expect(uiSettings).to.have.property('getUserProvided').a('function');
|
||||
expect(uiSettings).to.have.property('remove').a('function');
|
||||
expect(uiSettings).to.have.property('removeMany').a('function');
|
||||
expect(uiSettings).to.have.property('set').a('function');
|
||||
expect(uiSettings).to.have.property('setMany').a('function');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#setMany()', () => {
|
||||
it('returns a promise', () => {
|
||||
const { uiSettings } = setup();
|
||||
|
@ -127,6 +113,23 @@ describe('ui settings', () => {
|
|||
sinon.assert.calledTwice(savedObjectsClient.update);
|
||||
sinon.assert.calledOnce(createOrUpgradeSavedConfig);
|
||||
});
|
||||
|
||||
it('throws an error if any key is overridden', async () => {
|
||||
const { uiSettings } = setup({
|
||||
overrides: {
|
||||
foo: 'bar'
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
await uiSettings.setMany({
|
||||
bar: 'box',
|
||||
foo: 'baz'
|
||||
});
|
||||
} catch (error) {
|
||||
expect(error.message).to.be('Unable to update "foo" because it is overridden');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('#set()', () => {
|
||||
|
@ -140,6 +143,20 @@ describe('ui settings', () => {
|
|||
await uiSettings.set('one', 'value');
|
||||
savedObjectsClient.assertUpdateQuery({ one: 'value' });
|
||||
});
|
||||
|
||||
it('throws an error if the key is overridden', async () => {
|
||||
const { uiSettings } = setup({
|
||||
overrides: {
|
||||
foo: 'bar'
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
await uiSettings.set('foo', 'baz');
|
||||
} catch (error) {
|
||||
expect(error.message).to.be('Unable to update "foo" because it is overridden');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('#remove()', () => {
|
||||
|
@ -153,6 +170,20 @@ describe('ui settings', () => {
|
|||
await uiSettings.remove('one');
|
||||
savedObjectsClient.assertUpdateQuery({ one: null });
|
||||
});
|
||||
|
||||
it('throws an error if the key is overridden', async () => {
|
||||
const { uiSettings } = setup({
|
||||
overrides: {
|
||||
foo: 'bar'
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
await uiSettings.remove('foo');
|
||||
} catch (error) {
|
||||
expect(error.message).to.be('Unable to update "foo" because it is overridden');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('#removeMany()', () => {
|
||||
|
@ -172,6 +203,20 @@ describe('ui settings', () => {
|
|||
await uiSettings.removeMany(['one', 'two', 'three']);
|
||||
savedObjectsClient.assertUpdateQuery({ one: null, two: null, three: null });
|
||||
});
|
||||
|
||||
it('throws an error if any key is overridden', async () => {
|
||||
const { uiSettings } = setup({
|
||||
overrides: {
|
||||
foo: 'bar'
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
await uiSettings.setMany(['bar', 'foo']);
|
||||
} catch (error) {
|
||||
expect(error.message).to.be('Unable to update "foo" because it is overridden');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getDefaults()', () => {
|
||||
|
@ -210,18 +255,25 @@ describe('ui settings', () => {
|
|||
const esDocSource = { user: 'customized' };
|
||||
const { uiSettings } = setup({ esDocSource });
|
||||
const result = await uiSettings.getUserProvided();
|
||||
expect(isEqual(result, {
|
||||
user: { userValue: 'customized' }
|
||||
})).to.equal(true);
|
||||
expect(result).to.eql({
|
||||
user: {
|
||||
userValue: 'customized',
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('ignores null user configuration (because default values)', async () => {
|
||||
const esDocSource = { user: 'customized', usingDefault: null, something: 'else' };
|
||||
const { uiSettings } = setup({ esDocSource });
|
||||
const result = await uiSettings.getUserProvided();
|
||||
expect(isEqual(result, {
|
||||
user: { userValue: 'customized' }, something: { userValue: 'else' }
|
||||
})).to.equal(true);
|
||||
expect(result).to.eql({
|
||||
user: {
|
||||
userValue: 'customized'
|
||||
},
|
||||
something: {
|
||||
userValue: 'else'
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('returns an empty object on 404 responses', async () => {
|
||||
|
@ -291,6 +343,27 @@ describe('ui settings', () => {
|
|||
expect(err).to.be(expectedUnexpectedError);
|
||||
}
|
||||
});
|
||||
|
||||
it('includes overridden values for overridden keys', async () => {
|
||||
const esDocSource = {
|
||||
user: 'customized'
|
||||
};
|
||||
|
||||
const overrides = {
|
||||
foo: 'bar'
|
||||
};
|
||||
|
||||
const { uiSettings } = setup({ esDocSource, overrides });
|
||||
expect(await uiSettings.getUserProvided()).to.eql({
|
||||
user: {
|
||||
userValue: 'customized',
|
||||
},
|
||||
foo: {
|
||||
userValue: 'bar',
|
||||
isOverridden: true,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getRaw()', () => {
|
||||
|
@ -324,6 +397,24 @@ describe('ui settings', () => {
|
|||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('includes the values for overridden keys', async () => {
|
||||
const esDocSource = { foo: 'bar' };
|
||||
const defaults = { key: { value: chance.word() } };
|
||||
const overrides = { foo: true };
|
||||
const { uiSettings } = setup({ esDocSource, defaults, overrides });
|
||||
const result = await uiSettings.getRaw();
|
||||
|
||||
expect(result).to.eql({
|
||||
foo: {
|
||||
userValue: true,
|
||||
isOverridden: true,
|
||||
},
|
||||
key: {
|
||||
value: defaults.key.value,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getAll()', () => {
|
||||
|
@ -361,6 +452,29 @@ describe('ui settings', () => {
|
|||
bar: 'user-provided',
|
||||
});
|
||||
});
|
||||
|
||||
it('includes the values for overridden keys', async () => {
|
||||
const esDocSource = {
|
||||
foo: 'user-override',
|
||||
bar: 'user-provided',
|
||||
};
|
||||
|
||||
const defaults = {
|
||||
foo: {
|
||||
value: 'default'
|
||||
},
|
||||
};
|
||||
|
||||
const overrides = {
|
||||
foo: 'bax'
|
||||
};
|
||||
|
||||
const { uiSettings } = setup({ esDocSource, defaults, overrides });
|
||||
expect(await uiSettings.getAll()).to.eql({
|
||||
foo: 'bax',
|
||||
bar: 'user-provided',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#get()', () => {
|
||||
|
@ -392,5 +506,61 @@ describe('ui settings', () => {
|
|||
const result = await uiSettings.get('dateFormat');
|
||||
expect(result).to.equal('YYYY-MM-DD');
|
||||
});
|
||||
|
||||
it('returns the overridden value for an overrided key', async () => {
|
||||
const esDocSource = { dateFormat: 'YYYY-MM-DD' };
|
||||
const overrides = { dateFormat: 'foo' };
|
||||
const { uiSettings } = setup({ esDocSource, overrides });
|
||||
expect(await uiSettings.get('dateFormat')).to.be('foo');
|
||||
});
|
||||
|
||||
it('returns the default value for an override with value null', async () => {
|
||||
const esDocSource = { dateFormat: 'YYYY-MM-DD' };
|
||||
const overrides = { dateFormat: null };
|
||||
const defaults = { dateFormat: { value: 'foo' } };
|
||||
const { uiSettings } = setup({ esDocSource, overrides, defaults });
|
||||
expect(await uiSettings.get('dateFormat')).to.be('foo');
|
||||
});
|
||||
|
||||
it('returns the overridden value if the document does not exist', async () => {
|
||||
const overrides = { dateFormat: 'foo' };
|
||||
const { uiSettings, savedObjectsClient } = setup({ overrides });
|
||||
savedObjectsClient.get.throws(savedObjectsClientErrors.createGenericNotFoundError());
|
||||
expect(await uiSettings.get('dateFormat')).to.be('foo');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#isOverridden()', () => {
|
||||
it('returns false if no overrides defined', () => {
|
||||
const { uiSettings } = setup();
|
||||
expect(uiSettings.isOverridden('foo')).to.be(false);
|
||||
});
|
||||
it('returns false if overrides defined but key is not included', () => {
|
||||
const { uiSettings } = setup({ overrides: { foo: true, bar: true } });
|
||||
expect(uiSettings.isOverridden('baz')).to.be(false);
|
||||
});
|
||||
it('returns false for object prototype properties', () => {
|
||||
const { uiSettings } = setup({ overrides: { foo: true, bar: true } });
|
||||
expect(uiSettings.isOverridden('hasOwnProperty')).to.be(false);
|
||||
});
|
||||
it('returns true if overrides defined and key is overridden', () => {
|
||||
const { uiSettings } = setup({ overrides: { foo: true, bar: true } });
|
||||
expect(uiSettings.isOverridden('bar')).to.be(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#assertUpdateAllowed()', () => {
|
||||
it('returns false if no overrides defined', () => {
|
||||
const { uiSettings } = setup();
|
||||
expect(uiSettings.assertUpdateAllowed('foo')).to.be(undefined);
|
||||
});
|
||||
it('throws 400 Boom error when keys is overridden', () => {
|
||||
const { uiSettings } = setup({ overrides: { foo: true } });
|
||||
expect(() => uiSettings.assertUpdateAllowed('foo')).to.throwError(error => {
|
||||
expect(error).to.have.property('message', 'Unable to update "foo" because it is overridden');
|
||||
expect(error).to.have.property('isBoom', true);
|
||||
expect(error.output).to.have.property('statusCode', 400);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -20,6 +20,8 @@ You can use \`config.get(\\"throwableProperty\\", defaultValue)\`, which will ju
|
|||
\`defaultValue\` when the key is unrecognized."
|
||||
`;
|
||||
|
||||
exports[`#overrideLocalDefault #assertUpdateAllowed() throws error when keys is overridden 1`] = `"Unable to update \\"foo\\" because its value is overridden by the Kibana server"`;
|
||||
|
||||
exports[`#overrideLocalDefault key has no user value calls subscriber with new and previous value: single subscriber call 1`] = `
|
||||
Array [
|
||||
Array [
|
||||
|
@ -95,6 +97,10 @@ Object {
|
|||
}
|
||||
`;
|
||||
|
||||
exports[`#remove throws an error if key is overridden 1`] = `"Unable to update \\"bar\\" because its value is overridden by the Kibana server"`;
|
||||
|
||||
exports[`#set throws an error if key is overridden 1`] = `"Unable to update \\"foo\\" because its value is overridden by the Kibana server"`;
|
||||
|
||||
exports[`#subscribe calls handler with { key, newValue, oldValue } when config changes 1`] = `
|
||||
Array [
|
||||
Array [
|
||||
|
|
|
@ -102,6 +102,16 @@ You can use \`config.get("${key}", defaultValue)\`, which will just return
|
|||
return this.isDeclared(key) && !('value' in this._cache[key]);
|
||||
}
|
||||
|
||||
isOverridden(key) {
|
||||
return this.isDeclared(key) && Boolean(this._cache[key].isOverridden);
|
||||
}
|
||||
|
||||
assertUpdateAllowed(key) {
|
||||
if (this.isOverridden(key)) {
|
||||
throw new Error(`Unable to update "${key}" because its value is overridden by the Kibana server`);
|
||||
}
|
||||
}
|
||||
|
||||
overrideLocalDefault(key, newDefault) {
|
||||
// capture the previous value
|
||||
const prevDefault = this._defaults[key]
|
||||
|
@ -137,6 +147,8 @@ You can use \`config.get("${key}", defaultValue)\`, which will just return
|
|||
}
|
||||
|
||||
async _update(key, value) {
|
||||
this.assertUpdateAllowed(key);
|
||||
|
||||
const declared = this.isDeclared(key);
|
||||
const defaults = this._defaults;
|
||||
|
||||
|
@ -151,7 +163,7 @@ You can use \`config.get("${key}", defaultValue)\`, which will just return
|
|||
}
|
||||
|
||||
const initialVal = declared ? this.get(key) : undefined;
|
||||
this._setLocally(key, newVal, initialVal);
|
||||
this._setLocally(key, newVal);
|
||||
|
||||
try {
|
||||
const { settings } = await this._api.batchSet(key, newVal);
|
||||
|
@ -165,6 +177,8 @@ You can use \`config.get("${key}", defaultValue)\`, which will just return
|
|||
}
|
||||
|
||||
_setLocally(key, newValue) {
|
||||
this.assertUpdateAllowed(key);
|
||||
|
||||
if (!this.isDeclared(key)) {
|
||||
this._cache[key] = {};
|
||||
}
|
||||
|
|
|
@ -123,6 +123,18 @@ describe('#set', () => {
|
|||
|
||||
await expect(config.set('foo', 'bar')).resolves.toBe(false);
|
||||
});
|
||||
|
||||
it('throws an error if key is overridden', async () => {
|
||||
const { config } = setup({
|
||||
initialSettings: {
|
||||
foo: {
|
||||
isOverridden: true,
|
||||
value: 'bar'
|
||||
}
|
||||
}
|
||||
});
|
||||
await expect(config.set('foo', true)).rejects.toThrowErrorMatchingSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('#remove', () => {
|
||||
|
@ -140,6 +152,18 @@ describe('#remove', () => {
|
|||
|
||||
await expect(config.remove('dateFormat')).resolves.toBe(false);
|
||||
});
|
||||
|
||||
it('throws an error if key is overridden', async () => {
|
||||
const { config } = setup({
|
||||
initialSettings: {
|
||||
bar: {
|
||||
isOverridden: true,
|
||||
userValue: true
|
||||
}
|
||||
}
|
||||
});
|
||||
await expect(config.remove('bar')).rejects.toThrowErrorMatchingSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('#isDeclared', () => {
|
||||
|
@ -293,4 +317,61 @@ describe('#overrideLocalDefault', () => {
|
|||
expect(config.getAll()).toMatchSnapshot('getAll after override');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#isOverridden()', () => {
|
||||
it('returns false if key is unknown', () => {
|
||||
const { config } = setup();
|
||||
expect(config.isOverridden('foo')).toBe(false);
|
||||
});
|
||||
it('returns false if key is no overridden', () => {
|
||||
const { config } = setup({
|
||||
initialSettings: {
|
||||
foo: {
|
||||
userValue: 1
|
||||
},
|
||||
bar: {
|
||||
isOverridden: true,
|
||||
userValue: 2
|
||||
}
|
||||
}
|
||||
});
|
||||
expect(config.isOverridden('foo')).toBe(false);
|
||||
});
|
||||
it('returns true when key is overridden', () => {
|
||||
const { config } = setup({
|
||||
initialSettings: {
|
||||
foo: {
|
||||
userValue: 1
|
||||
},
|
||||
bar: {
|
||||
isOverridden: true,
|
||||
userValue: 2
|
||||
},
|
||||
}
|
||||
});
|
||||
expect(config.isOverridden('bar')).toBe(true);
|
||||
});
|
||||
it('returns false for object prototype properties', () => {
|
||||
const { config } = setup();
|
||||
expect(config.isOverridden('hasOwnProperty')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#assertUpdateAllowed()', () => {
|
||||
it('returns false if no settings defined', () => {
|
||||
const { config } = setup();
|
||||
expect(config.assertUpdateAllowed('foo')).toBe(undefined);
|
||||
});
|
||||
it('throws error when keys is overridden', () => {
|
||||
const { config } = setup({
|
||||
initialSettings: {
|
||||
foo: {
|
||||
isOverridden: true,
|
||||
userValue: 'bar'
|
||||
}
|
||||
}
|
||||
});
|
||||
expect(() => config.assertUpdateAllowed('foo')).toThrowErrorMatchingSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -82,7 +82,11 @@ export function docExistsSuite() {
|
|||
},
|
||||
defaultIndex: {
|
||||
userValue: defaultIndex
|
||||
}
|
||||
},
|
||||
foo: {
|
||||
userValue: 'bar',
|
||||
isOverridden: true
|
||||
},
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -109,10 +113,33 @@ export function docExistsSuite() {
|
|||
},
|
||||
defaultIndex: {
|
||||
userValue: defaultIndex
|
||||
}
|
||||
},
|
||||
foo: {
|
||||
userValue: 'bar',
|
||||
isOverridden: true
|
||||
},
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('returns a 400 if trying to set overridden value', async () => {
|
||||
const { kbnServer } = await setup();
|
||||
|
||||
const { statusCode, result } = await kbnServer.inject({
|
||||
method: 'POST',
|
||||
url: '/api/kibana/settings/foo',
|
||||
payload: {
|
||||
value: 'baz'
|
||||
}
|
||||
});
|
||||
|
||||
expect(statusCode).to.be(400);
|
||||
assertSinonMatch(result, {
|
||||
error: 'Bad Request',
|
||||
message: 'Unable to update "foo" because it is overridden',
|
||||
statusCode: 400
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('setMany route', () => {
|
||||
|
@ -138,9 +165,34 @@ export function docExistsSuite() {
|
|||
},
|
||||
defaultIndex: {
|
||||
userValue: defaultIndex
|
||||
},
|
||||
foo: {
|
||||
userValue: 'bar',
|
||||
isOverridden: true
|
||||
},
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('returns a 400 if trying to set overridden value', async () => {
|
||||
const { kbnServer } = await setup();
|
||||
|
||||
const { statusCode, result } = await kbnServer.inject({
|
||||
method: 'POST',
|
||||
url: '/api/kibana/settings',
|
||||
payload: {
|
||||
changes: {
|
||||
foo: 'baz'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
expect(statusCode).to.be(400);
|
||||
assertSinonMatch(result, {
|
||||
error: 'Bad Request',
|
||||
message: 'Unable to update "foo" because it is overridden',
|
||||
statusCode: 400
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -164,9 +216,28 @@ export function docExistsSuite() {
|
|||
settings: {
|
||||
buildNum: {
|
||||
userValue: sinon.match.number
|
||||
}
|
||||
},
|
||||
foo: {
|
||||
userValue: 'bar',
|
||||
isOverridden: true
|
||||
},
|
||||
}
|
||||
});
|
||||
});
|
||||
it('returns a 400 if deleting overridden value', async () => {
|
||||
const { kbnServer } = await setup();
|
||||
|
||||
const { statusCode, result } = await kbnServer.inject({
|
||||
method: 'DELETE',
|
||||
url: '/api/kibana/settings/foo'
|
||||
});
|
||||
|
||||
expect(statusCode).to.be(400);
|
||||
assertSinonMatch(result, {
|
||||
error: 'Bad Request',
|
||||
message: 'Unable to update "foo" because it is overridden',
|
||||
statusCode: 400
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ export function docMissingSuite() {
|
|||
});
|
||||
|
||||
describe('get route', () => {
|
||||
it('creates doc, returns a 200 with no settings', async () => {
|
||||
it('creates doc, returns a 200 with only overridden settings', async () => {
|
||||
const { kbnServer } = getServices();
|
||||
|
||||
const { statusCode, result } = await kbnServer.inject({
|
||||
|
@ -58,7 +58,12 @@ export function docMissingSuite() {
|
|||
|
||||
expect(statusCode).to.be(200);
|
||||
assertSinonMatch(result, {
|
||||
settings: {}
|
||||
settings: {
|
||||
foo: {
|
||||
userValue: 'bar',
|
||||
isOverridden: true
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -82,6 +87,10 @@ export function docMissingSuite() {
|
|||
},
|
||||
defaultIndex: {
|
||||
userValue: defaultIndex
|
||||
},
|
||||
foo: {
|
||||
userValue: 'bar',
|
||||
isOverridden: true
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -109,6 +118,10 @@ export function docMissingSuite() {
|
|||
},
|
||||
defaultIndex: {
|
||||
userValue: defaultIndex
|
||||
},
|
||||
foo: {
|
||||
userValue: 'bar',
|
||||
isOverridden: true
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -129,6 +142,10 @@ export function docMissingSuite() {
|
|||
settings: {
|
||||
buildNum: {
|
||||
userValue: sinon.match.number
|
||||
},
|
||||
foo: {
|
||||
userValue: 'bar',
|
||||
isOverridden: true
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -27,7 +27,6 @@ import { docMissingSuite } from './doc_missing';
|
|||
import { indexMissingSuite } from './index_missing';
|
||||
|
||||
describe('uiSettings/routes', function () {
|
||||
|
||||
/**
|
||||
* The "doc missing" and "index missing" tests verify how the uiSettings
|
||||
* API behaves in between healthChecks, so they interact with the healthCheck
|
||||
|
|
|
@ -59,7 +59,7 @@ export function indexMissingSuite() {
|
|||
}
|
||||
|
||||
describe('get route', () => {
|
||||
it('returns a 200 and with empty values', async () => {
|
||||
it('returns a 200 and with just overridden values', async () => {
|
||||
const { kbnServer } = await setup();
|
||||
|
||||
const { statusCode, result } = await kbnServer.inject({
|
||||
|
@ -68,7 +68,14 @@ export function indexMissingSuite() {
|
|||
});
|
||||
|
||||
expect(statusCode).to.be(200);
|
||||
expect(result).to.eql({ settings: {} });
|
||||
expect(result).to.eql({
|
||||
settings: {
|
||||
foo: {
|
||||
userValue: 'bar',
|
||||
isOverridden: true
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -93,6 +100,10 @@ export function indexMissingSuite() {
|
|||
},
|
||||
defaultIndex: {
|
||||
userValue: defaultIndex
|
||||
},
|
||||
foo: {
|
||||
userValue: 'bar',
|
||||
isOverridden: true
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -122,6 +133,10 @@ export function indexMissingSuite() {
|
|||
},
|
||||
defaultIndex: {
|
||||
userValue: defaultIndex
|
||||
},
|
||||
foo: {
|
||||
userValue: 'bar',
|
||||
isOverridden: true
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -144,6 +159,10 @@ export function indexMissingSuite() {
|
|||
settings: {
|
||||
buildNum: {
|
||||
userValue: sinon.match.number
|
||||
},
|
||||
foo: {
|
||||
userValue: 'bar',
|
||||
isOverridden: true
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -39,7 +39,13 @@ export async function startServers() {
|
|||
log.indent(-4);
|
||||
await es.start();
|
||||
|
||||
kbnServer = kbnTestServer.createServerWithCorePlugins();
|
||||
kbnServer = kbnTestServer.createServerWithCorePlugins({
|
||||
uiSettings: {
|
||||
overrides: {
|
||||
foo: 'bar',
|
||||
}
|
||||
}
|
||||
});
|
||||
await kbnServer.ready();
|
||||
await kbnServer.server.plugins.elasticsearch.waitUntilReady();
|
||||
}
|
||||
|
|
|
@ -30,10 +30,12 @@ export function uiSettingsMixin(kbnServer, server) {
|
|||
const getDefaults = () => (
|
||||
kbnServer.uiExports.uiSettingDefaults
|
||||
);
|
||||
const overrides = kbnServer.config.get('uiSettings.overrides');
|
||||
|
||||
server.decorate('server', 'uiSettingsServiceFactory', (options = {}) => {
|
||||
return uiSettingsServiceFactory(server, {
|
||||
getDefaults,
|
||||
overrides,
|
||||
...options
|
||||
});
|
||||
});
|
||||
|
@ -41,6 +43,7 @@ export function uiSettingsMixin(kbnServer, server) {
|
|||
server.addMemoizedFactoryToRequest('getUiSettingsService', request => {
|
||||
return getUiSettingsServiceForRequest(server, request, {
|
||||
getDefaults,
|
||||
overrides,
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -18,14 +18,9 @@
|
|||
*/
|
||||
|
||||
import { defaultsDeep } from 'lodash';
|
||||
import { createOrUpgradeSavedConfig } from './create_or_upgrade_saved_config';
|
||||
import Boom from 'boom';
|
||||
|
||||
function hydrateUserSettings(userSettings) {
|
||||
return Object.keys(userSettings)
|
||||
.map(key => ({ key, userValue: userSettings[key] }))
|
||||
.filter(({ userValue }) => userValue !== null)
|
||||
.reduce((acc, { key, userValue }) => ({ ...acc, [key]: { userValue } }), {});
|
||||
}
|
||||
import { createOrUpgradeSavedConfig } from './create_or_upgrade_saved_config';
|
||||
|
||||
/**
|
||||
* Service that provides access to the UiSettings stored in elasticsearch.
|
||||
|
@ -53,6 +48,7 @@ export class UiSettingsService {
|
|||
getDefaults = () => ({}),
|
||||
// function that accepts log messages in the same format as server.log
|
||||
log = () => {},
|
||||
overrides = {},
|
||||
} = options;
|
||||
|
||||
this._type = type;
|
||||
|
@ -60,6 +56,7 @@ export class UiSettingsService {
|
|||
this._buildNum = buildNum;
|
||||
this._savedObjectsClient = savedObjectsClient;
|
||||
this._getDefaults = getDefaults;
|
||||
this._overrides = overrides;
|
||||
this._log = log;
|
||||
}
|
||||
|
||||
|
@ -91,7 +88,26 @@ export class UiSettingsService {
|
|||
}
|
||||
|
||||
async getUserProvided(options) {
|
||||
return hydrateUserSettings(await this._read(options));
|
||||
const userProvided = {};
|
||||
|
||||
// write the userValue for each key stored in the saved object that is not overridden
|
||||
for (const [key, userValue] of Object.entries(await this._read(options))) {
|
||||
if (userValue !== null && !this.isOverridden(key)) {
|
||||
userProvided[key] = {
|
||||
userValue
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// write all overridden keys, dropping the userValue is override is null and
|
||||
// adding keys for overrides that are not in saved object
|
||||
for (const [key, userValue] of Object.entries(this._overrides)) {
|
||||
userProvided[key] = userValue === null
|
||||
? { isOverridden: true }
|
||||
: { isOverridden: true, userValue };
|
||||
}
|
||||
|
||||
return userProvided;
|
||||
}
|
||||
|
||||
async setMany(changes) {
|
||||
|
@ -114,7 +130,21 @@ export class UiSettingsService {
|
|||
await this.setMany(changes);
|
||||
}
|
||||
|
||||
isOverridden(key) {
|
||||
return this._overrides.hasOwnProperty(key);
|
||||
}
|
||||
|
||||
assertUpdateAllowed(key) {
|
||||
if (this.isOverridden(key)) {
|
||||
throw Boom.badRequest(`Unable to update "${key}" because it is overridden`);
|
||||
}
|
||||
}
|
||||
|
||||
async _write({ changes, autoCreateOrUpgradeIfMissing = true }) {
|
||||
for (const key of Object.keys(changes)) {
|
||||
this.assertUpdateAllowed(key);
|
||||
}
|
||||
|
||||
try {
|
||||
await this._savedObjectsClient.update(this._type, this._id, changes);
|
||||
} catch (error) {
|
||||
|
|
|
@ -37,6 +37,7 @@ export function uiSettingsServiceFactory(server, options) {
|
|||
const {
|
||||
savedObjectsClient,
|
||||
getDefaults,
|
||||
overrides,
|
||||
} = options;
|
||||
|
||||
return new UiSettingsService({
|
||||
|
@ -45,6 +46,7 @@ export function uiSettingsServiceFactory(server, options) {
|
|||
buildNum: config.get('pkg.buildNum'),
|
||||
savedObjectsClient,
|
||||
getDefaults,
|
||||
overrides,
|
||||
log: (...args) => server.log(...args),
|
||||
});
|
||||
}
|
||||
|
|
|
@ -34,11 +34,13 @@ import { uiSettingsServiceFactory } from './ui_settings_service_factory';
|
|||
*/
|
||||
export function getUiSettingsServiceForRequest(server, request, options = {}) {
|
||||
const {
|
||||
getDefaults
|
||||
getDefaults,
|
||||
overrides,
|
||||
} = options;
|
||||
|
||||
const uiSettingsService = uiSettingsServiceFactory(server, {
|
||||
getDefaults,
|
||||
overrides,
|
||||
savedObjectsClient: request.getSavedObjectsClient()
|
||||
});
|
||||
|
||||
|
|
|
@ -66,7 +66,9 @@ export class KibanaServerUiSettings {
|
|||
const { payload } = await this._wreck.get('/api/kibana/settings');
|
||||
|
||||
for (const key of Object.keys(payload.settings)) {
|
||||
await this._wreck.delete(`/api/kibana/settings/${key}`);
|
||||
if (!payload.settings[key].isOverridden) {
|
||||
await this._wreck.delete(`/api/kibana/settings/${key}`);
|
||||
}
|
||||
}
|
||||
|
||||
this._log.debug('replacing kibana config doc: %j', doc);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue