From 8a34c0093e84fbdc50afd2d864db7f9ced8d6e46 Mon Sep 17 00:00:00 2001 From: Jay Date: Wed, 24 Feb 2021 14:19:10 +0100 Subject: [PATCH] SONAR-14498 Support JSON field type in Settings --- server/sonar-web/src/main/js/api/settings.ts | 7 +- .../js/apps/settings/__tests__/utils-test.ts | 44 +++++---- .../apps/settings/components/Definition.tsx | 19 +++- .../settings/components/DefinitionActions.tsx | 3 +- .../settings/components/DefinitionsList.tsx | 3 +- .../components/SubCategoryDefinitionsList.tsx | 3 +- .../components/__tests__/Definition-test.tsx | 29 +++++- .../__tests__/DefinitionActions-test.tsx | 3 +- .../inputs/InputForSingleSelectList.tsx | 3 +- .../components/inputs/PrimitiveInput.tsx | 4 +- .../inputs/__tests__/Input-test.tsx | 5 +- .../inputs/__tests__/MultiValueInput-test.tsx | 3 +- .../settings/store/__tests__/actions-test.ts | 96 ++++++++++++++++++- .../main/js/apps/settings/store/actions.ts | 9 ++ .../js/apps/settings/store/definitions.ts | 5 +- .../src/main/js/apps/settings/store/values.ts | 9 +- .../src/main/js/apps/settings/utils.ts | 23 +++-- .../sonar-web/src/main/js/types/settings.ts | 49 ++++++++++ server/sonar-web/src/main/js/types/types.d.ts | 48 ---------- 19 files changed, 259 insertions(+), 106 deletions(-) diff --git a/server/sonar-web/src/main/js/api/settings.ts b/server/sonar-web/src/main/js/api/settings.ts index 2e8019d730a..03946b15832 100644 --- a/server/sonar-web/src/main/js/api/settings.ts +++ b/server/sonar-web/src/main/js/api/settings.ts @@ -22,8 +22,9 @@ import { getJSON, post, postJSON, RequestData } from 'sonar-ui-common/helpers/re import throwGlobalError from '../app/utils/throwGlobalError'; import { isCategoryDefinition } from '../apps/settings/utils'; import { BranchParameters } from '../types/branch-like'; +import { SettingCategoryDefinition, SettingDefinition, SettingValue } from '../types/settings'; -export function getDefinitions(component?: string): Promise { +export function getDefinitions(component?: string): Promise { return getJSON('/api/settings/list_definitions', { component }).then( r => r.definitions, throwGlobalError @@ -32,12 +33,12 @@ export function getDefinitions(component?: string): Promise { +): Promise { return getJSON('/api/settings/values', data).then(r => r.settings); } export function setSettingValue( - definition: T.SettingDefinition, + definition: SettingDefinition, value: any, component?: string ): Promise { diff --git a/server/sonar-web/src/main/js/apps/settings/__tests__/utils-test.ts b/server/sonar-web/src/main/js/apps/settings/__tests__/utils-test.ts index 8705c57668c..d37c7753bc4 100644 --- a/server/sonar-web/src/main/js/apps/settings/__tests__/utils-test.ts +++ b/server/sonar-web/src/main/js/apps/settings/__tests__/utils-test.ts @@ -17,14 +17,19 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import { + Setting, + SettingCategoryDefinition, + SettingFieldDefinition +} from '../../../types/settings'; import { getDefaultValue, getEmptyValue, sanitizeTranslation } from '../utils'; const fields = [ - { key: 'foo', type: 'STRING' } as T.SettingFieldDefinition, - { key: 'bar', type: 'SINGLE_SELECT_LIST' } as T.SettingFieldDefinition + { key: 'foo', type: 'STRING' } as SettingFieldDefinition, + { key: 'bar', type: 'SINGLE_SELECT_LIST' } as SettingFieldDefinition ]; -const settingDefinition: T.SettingCategoryDefinition = { +const settingDefinition: SettingCategoryDefinition = { category: 'test', fields: [], key: 'test', @@ -34,7 +39,7 @@ const settingDefinition: T.SettingCategoryDefinition = { describe('#getEmptyValue()', () => { it('should work for property sets', () => { - const setting: T.SettingCategoryDefinition = { + const setting: SettingCategoryDefinition = { ...settingDefinition, type: 'PROPERTY_SET', fields @@ -43,7 +48,7 @@ describe('#getEmptyValue()', () => { }); it('should work for multi values string', () => { - const setting: T.SettingCategoryDefinition = { + const setting: SettingCategoryDefinition = { ...settingDefinition, type: 'STRING', multiValues: true @@ -52,7 +57,7 @@ describe('#getEmptyValue()', () => { }); it('should work for multi values boolean', () => { - const setting: T.SettingCategoryDefinition = { + const setting: SettingCategoryDefinition = { ...settingDefinition, type: 'BOOLEAN', multiValues: true @@ -62,19 +67,20 @@ describe('#getEmptyValue()', () => { }); describe('#getDefaultValue()', () => { - const check = (parentValue?: string, expected?: string) => { - const setting: T.Setting = { - definition: { key: 'test', options: [], type: 'BOOLEAN' }, - parentValue, - key: 'test' - }; - expect(getDefaultValue(setting)).toEqual(expected); - }; - - it('should work for boolean field when passing "true"', () => - check('true', 'settings.boolean.true')); - it('should work for boolean field when passing "false"', () => - check('false', 'settings.boolean.false')); + it.each([ + ['true', 'settings.boolean.true'], + ['false', 'settings.boolean.false'] + ])( + 'should work for boolean field when passing "%s"', + (parentValue?: string, expected?: string) => { + const setting: Setting = { + definition: { key: 'test', options: [], type: 'BOOLEAN' }, + parentValue, + key: 'test' + }; + expect(getDefaultValue(setting)).toEqual(expected); + } + ); }); describe('sanitizeTranslation', () => { diff --git a/server/sonar-web/src/main/js/apps/settings/components/Definition.tsx b/server/sonar-web/src/main/js/apps/settings/components/Definition.tsx index 9b1036ce68e..66a0d15c5f9 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/Definition.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/Definition.tsx @@ -29,6 +29,7 @@ import { isSettingsAppLoading, Store } from '../../../store/rootReducer'; +import { Setting } from '../../../types/settings'; import { checkValue, resetValue, saveValue } from '../store/actions'; import { cancelChange, changeValue, passValidation } from '../store/settingsPage'; import { @@ -51,7 +52,7 @@ interface Props { passValidation: (key: string) => void; resetValue: (key: string, component?: string) => Promise; saveValue: (key: string, component?: string) => Promise; - setting: T.Setting; + setting: Setting; validationMessage?: string; } @@ -59,6 +60,8 @@ interface State { success: boolean; } +const SAFE_SET_STATE_DELAY = 3000; + export class Definition extends React.PureComponent { timeout?: number; mounted = false; @@ -91,7 +94,10 @@ export class Definition extends React.PureComponent { return this.props.resetValue(definition.key, componentKey).then(() => { this.props.cancelChange(definition.key); this.safeSetState({ success: true }); - this.timeout = window.setTimeout(() => this.safeSetState({ success: false }), 3000); + this.timeout = window.setTimeout( + () => this.safeSetState({ success: false }), + SAFE_SET_STATE_DELAY + ); }); }; @@ -113,9 +119,14 @@ export class Definition extends React.PureComponent { this.props.saveValue(setting.definition.key, component && component.key).then( () => { this.safeSetState({ success: true }); - this.timeout = window.setTimeout(() => this.safeSetState({ success: false }), 3000); + this.timeout = window.setTimeout( + () => this.safeSetState({ success: false }), + SAFE_SET_STATE_DELAY + ); }, - () => {} + () => { + /* Do nothing */ + } ); } }; diff --git a/server/sonar-web/src/main/js/apps/settings/components/DefinitionActions.tsx b/server/sonar-web/src/main/js/apps/settings/components/DefinitionActions.tsx index cfc9eb5e71b..70eb44829c5 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/DefinitionActions.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/DefinitionActions.tsx @@ -21,6 +21,7 @@ import * as React from 'react'; import { Button, ResetButtonLink, SubmitButton } from 'sonar-ui-common/components/controls/buttons'; import Modal from 'sonar-ui-common/components/controls/Modal'; import { translate } from 'sonar-ui-common/helpers/l10n'; +import { Setting } from '../../../types/settings'; import { getDefaultValue, getSettingValue, isEmptyValue } from '../utils'; type Props = { @@ -31,7 +32,7 @@ type Props = { onCancel: () => void; onReset: () => void; onSave: () => void; - setting: T.Setting; + setting: Setting; }; type State = { reseting: boolean }; diff --git a/server/sonar-web/src/main/js/apps/settings/components/DefinitionsList.tsx b/server/sonar-web/src/main/js/apps/settings/components/DefinitionsList.tsx index 6687de632cf..653cf225f8d 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/DefinitionsList.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/DefinitionsList.tsx @@ -18,11 +18,12 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; +import { Setting } from '../../../types/settings'; import Definition from './Definition'; interface Props { component?: T.Component; - settings: T.Setting[]; + settings: Setting[]; } export default function DefinitionsList({ component, settings }: Props) { diff --git a/server/sonar-web/src/main/js/apps/settings/components/SubCategoryDefinitionsList.tsx b/server/sonar-web/src/main/js/apps/settings/components/SubCategoryDefinitionsList.tsx index 1cc41a49a1d..22493ed1046 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/SubCategoryDefinitionsList.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/SubCategoryDefinitionsList.tsx @@ -19,6 +19,7 @@ */ import { groupBy, isEqual, sortBy } from 'lodash'; import * as React from 'react'; +import { Setting, SettingCategoryDefinition } from '../../../types/settings'; import { getSubCategoryDescription, getSubCategoryName, sanitizeTranslation } from '../utils'; import DefinitionsList from './DefinitionsList'; import EmailForm from './EmailForm'; @@ -27,7 +28,7 @@ interface Props { category: string; component?: T.Component; fetchValues: Function; - settings: Array; + settings: Array; subCategory?: string; } diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/Definition-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/__tests__/Definition-test.tsx index 8707c5f3e10..764be721a38 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/Definition-test.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/__tests__/Definition-test.tsx @@ -20,9 +20,10 @@ import { shallow } from 'enzyme'; import * as React from 'react'; import { waitAndUpdate } from 'sonar-ui-common/helpers/testUtils'; +import { Setting } from '../../../../types/settings'; import { Definition } from '../Definition'; -const setting: T.Setting = { +const setting: Setting = { key: 'foo', value: '42', inherited: true, @@ -35,6 +36,14 @@ const setting: T.Setting = { } }; +beforeAll(() => { + jest.useFakeTimers(); +}); + +afterAll(() => { + jest.useRealTimers(); +}); + it('should render correctly', () => { const wrapper = shallowRender(); expect(wrapper).toMatchSnapshot(); @@ -65,10 +74,26 @@ it('should correctly save value change', async () => { await waitAndUpdate(wrapper); expect(saveValue).toHaveBeenCalledWith(setting.definition.key, undefined); expect(wrapper.find('AlertSuccessIcon').exists()).toBe(true); + expect(wrapper.state().success).toBe(true); + jest.runAllTimers(); + expect(wrapper.state().success).toBe(false); +}); + +it('should correctly reset', async () => { + const cancelChange = jest.fn(); + const resetValue = jest.fn().mockResolvedValue({}); + const wrapper = shallowRender({ cancelChange, changedValue: 10, resetValue }); + wrapper.find('DefinitionActions').prop('onReset')(); + await waitAndUpdate(wrapper); + expect(resetValue).toHaveBeenCalledWith(setting.definition.key, undefined); + expect(cancelChange).toHaveBeenCalledWith(setting.definition.key); + expect(wrapper.state().success).toBe(true); + jest.runAllTimers(); + expect(wrapper.state().success).toBe(false); }); function shallowRender(props: Partial = {}) { - return shallow( + return shallow( ; +type Props = DefaultSpecializedInputProps & Pick; export default class InputForSingleSelectList extends React.PureComponent { handleInputChange = ({ value }: { value: string }) => { diff --git a/server/sonar-web/src/main/js/apps/settings/components/inputs/PrimitiveInput.tsx b/server/sonar-web/src/main/js/apps/settings/components/inputs/PrimitiveInput.tsx index 44b6131c21d..ce91fc76318 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/inputs/PrimitiveInput.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/inputs/PrimitiveInput.tsx @@ -18,6 +18,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; +import { SettingType } from '../../../../types/settings'; import { DefaultInputProps, DefaultSpecializedInputProps, @@ -32,10 +33,11 @@ import InputForString from './InputForString'; import InputForText from './InputForText'; const typeMapping: { - [type in T.SettingType]?: React.ComponentType; + [type in SettingType]?: React.ComponentType; } = { STRING: InputForString, TEXT: InputForText, + JSON: InputForText, PASSWORD: InputForPassword, BOOLEAN: InputForBoolean, INTEGER: InputForNumber, diff --git a/server/sonar-web/src/main/js/apps/settings/components/inputs/__tests__/Input-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/inputs/__tests__/Input-test.tsx index 8623863c719..c800f03dee0 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/inputs/__tests__/Input-test.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/inputs/__tests__/Input-test.tsx @@ -19,6 +19,7 @@ */ import { shallow } from 'enzyme'; import * as React from 'react'; +import { Setting, SettingCategoryDefinition } from '../../../../../types/settings'; import { DefaultInputProps } from '../../../utils'; import Input from '../Input'; @@ -26,7 +27,7 @@ const settingValue = { key: 'example' }; -const settingDefinition: T.SettingCategoryDefinition = { +const settingDefinition: SettingCategoryDefinition = { category: 'general', fields: [], key: 'example', @@ -57,7 +58,7 @@ it('should render MultiValueInput', () => { }); it('should render PropertySetInput', () => { - const setting: T.Setting = { + const setting: Setting = { ...settingValue, definition: { ...settingDefinition, type: 'PROPERTY_SET' } }; diff --git a/server/sonar-web/src/main/js/apps/settings/components/inputs/__tests__/MultiValueInput-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/inputs/__tests__/MultiValueInput-test.tsx index cd90e9b0d0f..c47d92eadb2 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/inputs/__tests__/MultiValueInput-test.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/inputs/__tests__/MultiValueInput-test.tsx @@ -20,6 +20,7 @@ import { shallow, ShallowWrapper } from 'enzyme'; import * as React from 'react'; import { click } from 'sonar-ui-common/helpers/testUtils'; +import { SettingCategoryDefinition } from '../../../../../types/settings'; import { DefaultInputProps } from '../../../utils'; import MultiValueInput from '../MultiValueInput'; import PrimitiveInput from '../PrimitiveInput'; @@ -28,7 +29,7 @@ const settingValue = { key: 'example' }; -const settingDefinition: T.SettingCategoryDefinition = { +const settingDefinition: SettingCategoryDefinition = { category: 'general', fields: [], key: 'example', diff --git a/server/sonar-web/src/main/js/apps/settings/store/__tests__/actions-test.ts b/server/sonar-web/src/main/js/apps/settings/store/__tests__/actions-test.ts index 68cf95eb1c4..924aa9190a1 100644 --- a/server/sonar-web/src/main/js/apps/settings/store/__tests__/actions-test.ts +++ b/server/sonar-web/src/main/js/apps/settings/store/__tests__/actions-test.ts @@ -17,7 +17,11 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { fetchSettings } from '../actions'; +import { + getSettingsAppChangedValue, + getSettingsAppDefinition +} from '../../../../store/rootReducer'; +import { checkValue, fetchSettings } from '../actions'; import { receiveDefinitions } from '../definitions'; jest.mock('../../../../api/settings', () => ({ @@ -37,6 +41,11 @@ jest.mock('../definitions', () => ({ receiveDefinitions: jest.fn() })); +jest.mock('../../../../store/rootReducer', () => ({ + getSettingsAppDefinition: jest.fn(), + getSettingsAppChangedValue: jest.fn() +})); + it('#fetchSettings should filter LICENSE type settings', async () => { const dispatch = jest.fn(); @@ -49,3 +58,88 @@ it('#fetchSettings should filter LICENSE type settings', async () => { } ]); }); + +describe('checkValue', () => { + const dispatch = jest.fn(); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should correctly identify empty strings', () => { + (getSettingsAppDefinition as jest.Mock).mockReturnValue({ + defaultValue: 'hello', + type: 'TEXT' + }); + + (getSettingsAppChangedValue as jest.Mock).mockReturnValue(undefined); + const key = 'key'; + expect(checkValue(key)(dispatch, jest.fn())).toBe(false); + expect(dispatch).toBeCalledWith({ + type: 'settingsPage/FAIL_VALIDATION', + key, + message: 'settings.state.value_cant_be_empty' + }); + }); + + it('should correctly identify empty with no default', () => { + (getSettingsAppDefinition as jest.Mock).mockReturnValue({ + type: 'TEXT' + }); + + (getSettingsAppChangedValue as jest.Mock).mockReturnValue(undefined); + + const key = 'key'; + expect(checkValue(key)(dispatch, jest.fn())).toBe(false); + expect(dispatch).toBeCalledWith({ + type: 'settingsPage/FAIL_VALIDATION', + key, + message: 'settings.state.value_cant_be_empty_no_default' + }); + }); + + it('should correctly identify non-empty strings', () => { + (getSettingsAppDefinition as jest.Mock).mockReturnValue({ + type: 'TEXT' + }); + + (getSettingsAppChangedValue as jest.Mock).mockReturnValue('not empty'); + const key = 'key'; + expect(checkValue(key)(dispatch, jest.fn())).toBe(true); + expect(dispatch).toBeCalledWith({ + type: 'settingsPage/PASS_VALIDATION', + key + }); + }); + + it('should correctly identify misformed JSON', () => { + (getSettingsAppDefinition as jest.Mock).mockReturnValue({ + type: 'JSON' + }); + + (getSettingsAppChangedValue as jest.Mock).mockReturnValue('{JSON: "asd;{'); + const key = 'key'; + expect(checkValue(key)(dispatch, jest.fn())).toBe(false); + expect(dispatch).toBeCalledWith({ + type: 'settingsPage/FAIL_VALIDATION', + key, + message: 'Unexpected token J in JSON at position 1' + }); + }); + + it('should correctly identify correct JSON', () => { + (getSettingsAppDefinition as jest.Mock).mockReturnValue({ + type: 'JSON' + }); + + (getSettingsAppChangedValue as jest.Mock).mockReturnValue( + '{"number": 42, "question": "answer"}' + ); + const key = 'key'; + expect(checkValue(key)(dispatch, jest.fn())).toBe(true); + expect(dispatch).toBeCalledWith({ + type: 'settingsPage/PASS_VALIDATION', + key + }); + }); +}); diff --git a/server/sonar-web/src/main/js/apps/settings/store/actions.ts b/server/sonar-web/src/main/js/apps/settings/store/actions.ts index 23b969a9484..7201065e6ab 100644 --- a/server/sonar-web/src/main/js/apps/settings/store/actions.ts +++ b/server/sonar-web/src/main/js/apps/settings/store/actions.ts @@ -75,6 +75,15 @@ export function checkValue(key: string) { return false; } + if (definition.type === 'JSON') { + try { + JSON.parse(value); + } catch (e) { + dispatch(failValidation(key, e.message)); + return false; + } + } + dispatch(passValidation(key)); return true; }; diff --git a/server/sonar-web/src/main/js/apps/settings/store/definitions.ts b/server/sonar-web/src/main/js/apps/settings/store/definitions.ts index 977b5ddf9f5..8e1af16c1db 100644 --- a/server/sonar-web/src/main/js/apps/settings/store/definitions.ts +++ b/server/sonar-web/src/main/js/apps/settings/store/definitions.ts @@ -19,6 +19,7 @@ */ import { keyBy, sortBy, uniqBy } from 'lodash'; import { ActionType } from '../../../store/utils/actions'; +import { SettingCategoryDefinition } from '../../../types/settings'; import { DEFAULT_CATEGORY, getCategoryName } from '../utils'; const enum Actions { @@ -27,9 +28,9 @@ const enum Actions { type Action = ActionType; -export type State = T.Dict; +export type State = T.Dict; -export function receiveDefinitions(definitions: T.SettingCategoryDefinition[]) { +export function receiveDefinitions(definitions: SettingCategoryDefinition[]) { return { type: Actions.ReceiveDefinitions, definitions }; } diff --git a/server/sonar-web/src/main/js/apps/settings/store/values.ts b/server/sonar-web/src/main/js/apps/settings/store/values.ts index 2a4256c1919..8b00453b9b6 100644 --- a/server/sonar-web/src/main/js/apps/settings/store/values.ts +++ b/server/sonar-web/src/main/js/apps/settings/store/values.ts @@ -21,6 +21,7 @@ import { keyBy } from 'lodash'; import { combineReducers } from 'redux'; import { Action as AppStateAction, Actions as AppStateActions } from '../../../store/appState'; import { ActionType } from '../../../store/utils/actions'; +import { SettingValue } from '../../../types/settings'; enum Actions { receiveValues = 'RECEIVE_VALUES' @@ -28,7 +29,7 @@ enum Actions { type Action = ActionType; -type SettingsState = T.Dict; +type SettingsState = T.Dict; export interface State { components: T.Dict; @@ -74,11 +75,7 @@ function global(state: State['components'] = {}, action: Action | AppStateAction export default combineReducers({ components, global }); -export function getValue( - state: State, - key: string, - component?: string -): T.SettingValue | undefined { +export function getValue(state: State, key: string, component?: string): SettingValue | undefined { if (component) { return state.components[component] && state.components[component][key]; } diff --git a/server/sonar-web/src/main/js/apps/settings/utils.ts b/server/sonar-web/src/main/js/apps/settings/utils.ts index 9e4860bcf5a..0fa364c5c0f 100644 --- a/server/sonar-web/src/main/js/apps/settings/utils.ts +++ b/server/sonar-web/src/main/js/apps/settings/utils.ts @@ -19,6 +19,7 @@ */ import { sanitize } from 'dompurify'; import { hasMessage, translate } from 'sonar-ui-common/helpers/l10n'; +import { Setting, SettingCategoryDefinition, SettingDefinition } from '../../types/settings'; export const DEFAULT_CATEGORY = 'general'; @@ -32,7 +33,7 @@ export interface DefaultInputProps { onCancel?: () => void; onChange: (value: any) => void; onSave?: () => void; - setting: T.Setting; + setting: Setting; value: any; } @@ -42,12 +43,12 @@ export function sanitizeTranslation(html: string) { }); } -export function getPropertyName(definition: T.SettingDefinition) { +export function getPropertyName(definition: SettingDefinition) { const key = `property.${definition.key}.name`; return hasMessage(key) ? translate(key) : definition.name; } -export function getPropertyDescription(definition: T.SettingDefinition) { +export function getPropertyDescription(definition: SettingDefinition) { const key = `property.${definition.key}.description`; return hasMessage(key) ? translate(key) : definition.description; } @@ -67,12 +68,12 @@ export function getSubCategoryDescription(category: string, subCategory: string) return hasMessage(key) ? translate(key) : null; } -export function getUniqueName(definition: T.SettingDefinition, index?: string) { +export function getUniqueName(definition: SettingDefinition, index?: string) { const indexSuffix = index ? `[${index}]` : ''; return `settings[${definition.key}]${indexSuffix}`; } -export function getSettingValue({ definition, fieldValues, value, values }: T.Setting) { +export function getSettingValue({ definition, fieldValues, value, values }: Setting) { if (isCategoryDefinition(definition) && definition.multiValues) { return values; } else if (definition.type === 'PROPERTY_SET') { @@ -82,7 +83,7 @@ export function getSettingValue({ definition, fieldValues, value, values }: T.Se } } -export function isEmptyValue(definition: T.SettingDefinition, value: any) { +export function isEmptyValue(definition: SettingDefinition, value: any) { if (value == null) { return true; } else if (definition.type === 'BOOLEAN') { @@ -92,13 +93,11 @@ export function isEmptyValue(definition: T.SettingDefinition, value: any) { } } -export function isCategoryDefinition( - item: T.SettingDefinition -): item is T.SettingCategoryDefinition { +export function isCategoryDefinition(item: SettingDefinition): item is SettingCategoryDefinition { return Boolean((item as any).fields); } -export function getEmptyValue(item: T.SettingDefinition | T.SettingCategoryDefinition): any { +export function getEmptyValue(item: SettingDefinition | SettingCategoryDefinition): any { if (isCategoryDefinition(item)) { if (item.multiValues) { return [getEmptyValue({ ...item, multiValues: false })]; @@ -117,11 +116,11 @@ export function getEmptyValue(item: T.SettingDefinition | T.SettingCategoryDefin return ''; } -export function isDefaultOrInherited(setting: T.Setting) { +export function isDefaultOrInherited(setting: Setting) { return Boolean(setting.inherited); } -export function getDefaultValue(setting: T.Setting) { +export function getDefaultValue(setting: Setting) { const { definition, parentFieldValues, parentValue, parentValues } = setting; if (definition.type === 'PASSWORD') { diff --git a/server/sonar-web/src/main/js/types/settings.ts b/server/sonar-web/src/main/js/types/settings.ts index 5626af2b709..c7ca6d250ab 100644 --- a/server/sonar-web/src/main/js/types/settings.ts +++ b/server/sonar-web/src/main/js/types/settings.ts @@ -22,3 +22,52 @@ export const enum SettingsKey { DefaultProjectVisibility = 'projects.default.visibility', ServerBaseUrl = 'sonar.core.serverBaseURL' } + +export type Setting = SettingValue & { definition: SettingDefinition }; + +export type SettingType = + | 'STRING' + | 'TEXT' + | 'JSON' + | 'PASSWORD' + | 'BOOLEAN' + | 'FLOAT' + | 'INTEGER' + | 'LICENSE' + | 'LONG' + | 'SINGLE_SELECT_LIST' + | 'PROPERTY_SET'; + +export interface SettingDefinition { + description?: string; + key: string; + multiValues?: boolean; + name?: string; + options: string[]; + type?: SettingType; +} + +export interface SettingFieldDefinition extends SettingDefinition { + description: string; + name: string; +} + +export interface SettingCategoryDefinition extends SettingDefinition { + category: string; + defaultValue?: string; + deprecatedKey?: string; + fields: SettingFieldDefinition[]; + multiValues?: boolean; + subCategory: string; +} + +export interface SettingValue { + fieldValues?: Array>; + inherited?: boolean; + key: string; + parentFieldValues?: Array>; + parentValue?: string; + parentValues?: string[]; + value?: string; + values?: string[]; +} diff --git a/server/sonar-web/src/main/js/types/types.d.ts b/server/sonar-web/src/main/js/types/types.d.ts index 9edfc2edf40..223967cd14e 100644 --- a/server/sonar-web/src/main/js/types/types.d.ts +++ b/server/sonar-web/src/main/js/types/types.d.ts @@ -672,54 +672,6 @@ declare namespace T { export type RuleType = 'BUG' | 'VULNERABILITY' | 'CODE_SMELL' | 'SECURITY_HOTSPOT' | 'UNKNOWN'; - export type Setting = SettingValue & { definition: SettingDefinition }; - - export type SettingType = - | 'STRING' - | 'TEXT' - | 'PASSWORD' - | 'BOOLEAN' - | 'FLOAT' - | 'INTEGER' - | 'LICENSE' - | 'LONG' - | 'SINGLE_SELECT_LIST' - | 'PROPERTY_SET'; - - export interface SettingDefinition { - description?: string; - key: string; - multiValues?: boolean; - name?: string; - options: string[]; - type?: SettingType; - } - - export interface SettingFieldDefinition extends SettingDefinition { - description: string; - name: string; - } - - export interface SettingCategoryDefinition extends SettingDefinition { - category: string; - defaultValue?: string; - deprecatedKey?: string; - fields: SettingFieldDefinition[]; - multiValues?: boolean; - subCategory: string; - } - - export interface SettingValue { - fieldValues?: Array>; - inherited?: boolean; - key: string; - parentFieldValues?: Array>; - parentValue?: string; - parentValues?: string[]; - value?: string; - values?: string[]; - } - export interface Snippet { start: number; end: number; -- 2.39.5