Skip to content

Commit

Permalink
[Backport 4.4-2.3-wzd] Centralize the plugin settings (#4501) (#4752)
Browse files Browse the repository at this point in the history
* Centralize the plugin settings (#4501)

* feat(settings): centralize the plugin settings

Create the plugin setting schema
Define the current plugin settings
Remove refactored code

* feat(settings): add setting services and replaced the references to constants

* feat(settings): refactor the content of the default configuration file

Use dynamically the definition of the plugin settings

* feat(inputs): create new inputs components

Add new hooks to manage when a input value or form has changed
Add new inputs components

* feat(configuration): refactor the form of Settings/Configuration

Refactor Header, BottomBar, Configuration components
Remove deprecated files

* feat(settings): support updating multiple setting at the same time

Changed the endpoint that updating the plugin setting to support
  multiple settings at the same time
Refactor the getConfiguration service. Split the logic to:
  - Read the file and transform to JSON
  - Obfuscate the password key of the host configuration

* clean: remove not used code

* fix: fixed category name in `Settings/Configuration`

* fix(settings): fixed error due to missing service

* fix(settings): refactor the form and inputs of `Settings/Configuration` to control the global state of the form

* fix: add value transformation for the form inputs and output of fields changed

* fix(settings): renamed properties related to transform the value of the input

* feat(settings): add description to the plugin setting definition properties

* fix(settings): fix getConfiguration service when the configuration file has no `hosts` entry

* fix(settings): Fixed error when do changes of the `useForm` hook an rename methods of this

* tests(settings): add test related to the plugin settings and its configuration from the UI

* feat(settings): rename plugin setting options of type select to match its type

* feat(settings): add plugin settings services and enhance the description of the plugin settings in default configuration file and UI

* tests(input-form): update tests of InputForm component

* test(configuration-file): add tests of the default configuration file

* feat(settings): remove `extensions.mitre` plugin setting

* feat(settings): add documentation to some setting services and test some of them

* fix: fixed documentation of setting service

* doc(settings): move the documentation of the plugin setting properties

* fix(settings): rename some plugin setting properties because of request changes

- Rename plugin setting properties:
  - `default` to `defaultValue`
  - `defaultHidden` to `defaultValueIfNotSet`
  - `configurableFile` to `isConfigurableFromFile`
  - configurableUI` to `isConfigurableFromUI`
  - `requireHealthCheck` to `requiresRunningHealthCheck`
  - `requireReload` to `requiresReloadingBrowserTab`
  - `requireRestart` to `requiresRestartingPluginPlatform`
- Fix tests

* tests: fix tests of InputForm component

* fix: response properties when saving the configuration

* fix(settings): fix displaying toast to run the healthcheck when saving the configuration

* Added category sorting + description + docs link

* Added settings sorting within their category

* Fixed constant types definition

* Checks if localCompare exists validation

* fix(settings): fixed plugin setting description doesn't display the minimum number value when it is falsy (0)

* fix(settings): fix setting type of `wazuh.monitoring.replicas` and limit the valid number for the number input

* feat(settins): add plugin settings category description

* fix(settings): fix a problem comparing the initial and current value for the plugin settings of the `number` type

* fix(settings): fix typo in setting description

* fix(tests): format tables of the tests

* Fix small typo

* fix(settings): fix a typo in a toast related to modify the plugin settings from UI

* Changed Custom Branding documentation link

* Delete unused imports

* Update constants.ts

Co-authored-by: Federico Rodriguez <[email protected]>
Co-authored-by: Álex <[email protected]>
(cherry picked from commit 430fe82)

* fix: fix some problems related to the conflict resolution and remove unused security factories

* fix(test): fix tests related to the conflict resolution

* fix(test): fix a failed test due to missing dependency

Co-authored-by: Antonio <[email protected]>
Co-authored-by: Antonio David Gutiérrez <[email protected]>
  • Loading branch information
3 people authored Oct 25, 2022
1 parent bda3064 commit 665ecc4
Show file tree
Hide file tree
Showing 64 changed files with 2,840 additions and 1,314 deletions.
1,105 changes: 1,028 additions & 77 deletions common/constants.ts

Large diffs are not rendered by default.

34 changes: 34 additions & 0 deletions common/services/settings.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import {
formatLabelValuePair,
formatSettingValueToFile
} from "./settings";

describe('[settings] Methods', () => {

describe('formatLabelValuePair: Format the label-value pairs used to display the allowed values', () => {
it.each`
label | value | expected
${'TestLabel'} | ${true} | ${'true (TestLabel)'}
${'true'} | ${true} | ${'true'}
`(`label: $label | value: $value | expected: $expected`, ({ label, expected, value }) => {
expect(formatLabelValuePair(label, value)).toBe(expected);
});
});

describe('formatSettingValueToFile: Format setting values to save in the configuration file', () => {
it.each`
input | expected
${'test'} | ${'\"test\"'}
${'test space'} | ${'\"test space\"'}
${'test\nnew line'} | ${'\"test\\nnew line\"'}
${''} | ${'\"\"'}
${1} | ${1}
${true} | ${true}
${false} | ${false}
${['test1']} | ${'[\"test1\"]'}
${['test1', 'test2']} | ${'[\"test1\",\"test2\"]'}
`(`input: $input | expected: $expected`, ({ input, expected }) => {
expect(formatSettingValueToFile(input)).toBe(expected);
});
});
});
124 changes: 124 additions & 0 deletions common/services/settings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import {
PLUGIN_SETTINGS,
PLUGIN_SETTINGS_CATEGORIES,
TPluginSetting,
TPluginSettingKey,
TPluginSettingWithKey
} from '../constants';

/**
* Look for a configuration category setting by its name
* @param categoryTitle
* @returns category settings
*/
export function getCategorySettingByTitle(categoryTitle: string): any {
return Object.entries(PLUGIN_SETTINGS_CATEGORIES).find(([key, category]) => category?.title == categoryTitle)?.[1];
}

/**
* Get the default value of the plugin setting.
* @param setting setting key
* @returns setting default value. It returns `defaultValueIfNotSet` or `defaultValue`.
*/
export function getSettingDefaultValue(settingKey: string): any {
return typeof PLUGIN_SETTINGS[settingKey].defaultValueIfNotSet !== 'undefined'
? PLUGIN_SETTINGS[settingKey].defaultValueIfNotSet
: PLUGIN_SETTINGS[settingKey].defaultValue;
};

/**
* Get the default settings configuration. key-value pair
* @returns an object with key-value pairs whose value is the default one
*/
export function getSettingsDefault() : {[key in TPluginSettingKey]: unknown} {
return Object.entries(PLUGIN_SETTINGS).reduce((accum, [pluginSettingID, pluginSettingConfiguration]) => ({
...accum,
[pluginSettingID]: pluginSettingConfiguration.defaultValue
}), {});
};

/**
* Get the settings grouped by category
* @returns an object whose keys are the categories and its value is an array of setting of that category
*/
export function getSettingsByCategories() : {[key: string]: TPluginSetting[]} {
return Object.entries(PLUGIN_SETTINGS).reduce((accum, [pluginSettingID, pluginSettingConfiguration]) => ({
...accum,
[pluginSettingConfiguration.category]: [...(accum[pluginSettingConfiguration.category] || []), { ...pluginSettingConfiguration, key: pluginSettingID }]
}), {});
};

/**
* Get the plugin settings as an array
* @returns an array of plugin setting denifitions including the key
*/
export function getSettingsDefaultList(): TPluginSettingWithKey[] {
return Object.entries(PLUGIN_SETTINGS).reduce((accum, [pluginSettingID, pluginSettingConfiguration]) => ([
...accum,
{ ...pluginSettingConfiguration, key: pluginSettingID }
]), []);
};

/**
* Format the plugin setting value received in the backend to store in the plugin configuration file (.yml).
* @param value plugin setting value sent to the endpoint
* @returns valid value to .yml
*/
export function formatSettingValueToFile(value: any) {
const formatter = formatSettingValueToFileType[typeof value] || formatSettingValueToFileType.default;
return formatter(value);
};

const formatSettingValueToFileType = {
string: (value: string): string => `"${value.replace(/"/,'\\"').replace(/\n/g,'\\n')}"`, // Escape the " character and new line
object: (value: any): string => JSON.stringify(value),
default: (value: any): any => value
};

/**
* Group the settings by category
* @param settings
* @returns
*/
export function groupSettingsByCategory(settings: TPluginSettingWithKey[]){
const settingsSortedByCategories = settings
.sort((settingA, settingB) => settingA.key?.localeCompare?.(settingB.key))
.reduce((accum, pluginSettingConfiguration) => ({
...accum,
[pluginSettingConfiguration.category]: [
...(accum[pluginSettingConfiguration.category] || []),
{ ...pluginSettingConfiguration }
]
}), {});

return Object.entries(settingsSortedByCategories)
.map(([category, settings]) => ({ category, settings }))
.filter(categoryEntry => categoryEntry.settings.length);
};

/**
* Get the plugin setting description composed.
* @param options
* @returns
*/
export function getPluginSettingDescription({description, options}: TPluginSetting): string{
return [
description,
...(options?.select ? [`Allowed values: ${options.select.map(({text, value}) => formatLabelValuePair(text, value)).join(', ')}.`] : []),
...(options?.switch ? [`Allowed values: ${['enabled', 'disabled'].map(s => formatLabelValuePair(options.switch.values[s].label, options.switch.values[s].value)).join(', ')}.`] : []),
...(options?.number && 'min' in options.number ? [`Minimum value: ${options.number.min}.`] : []),
...(options?.number && 'max' in options.number ? [`Maximum value: ${options.number.max}.`] : []),
].join(' ');
};

/**
* Format the pair value-label to display the pair. If label and the string of value are equals, only displays the value, if not, displays both.
* @param value
* @param label
* @returns
*/
export function formatLabelValuePair(label, value){
return label !== `${value}`
? `${value} (${label})`
: `${value}`
};
Loading

0 comments on commit 665ecc4

Please sign in to comment.