]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-14498 Support JSON field type in Settings
authorJay <jeremy.davis@sonarsource.com>
Wed, 24 Feb 2021 13:19:10 +0000 (14:19 +0100)
committersonartech <sonartech@sonarsource.com>
Fri, 26 Feb 2021 20:07:40 +0000 (20:07 +0000)
19 files changed:
server/sonar-web/src/main/js/api/settings.ts
server/sonar-web/src/main/js/apps/settings/__tests__/utils-test.ts
server/sonar-web/src/main/js/apps/settings/components/Definition.tsx
server/sonar-web/src/main/js/apps/settings/components/DefinitionActions.tsx
server/sonar-web/src/main/js/apps/settings/components/DefinitionsList.tsx
server/sonar-web/src/main/js/apps/settings/components/SubCategoryDefinitionsList.tsx
server/sonar-web/src/main/js/apps/settings/components/__tests__/Definition-test.tsx
server/sonar-web/src/main/js/apps/settings/components/__tests__/DefinitionActions-test.tsx
server/sonar-web/src/main/js/apps/settings/components/inputs/InputForSingleSelectList.tsx
server/sonar-web/src/main/js/apps/settings/components/inputs/PrimitiveInput.tsx
server/sonar-web/src/main/js/apps/settings/components/inputs/__tests__/Input-test.tsx
server/sonar-web/src/main/js/apps/settings/components/inputs/__tests__/MultiValueInput-test.tsx
server/sonar-web/src/main/js/apps/settings/store/__tests__/actions-test.ts
server/sonar-web/src/main/js/apps/settings/store/actions.ts
server/sonar-web/src/main/js/apps/settings/store/definitions.ts
server/sonar-web/src/main/js/apps/settings/store/values.ts
server/sonar-web/src/main/js/apps/settings/utils.ts
server/sonar-web/src/main/js/types/settings.ts
server/sonar-web/src/main/js/types/types.d.ts

index 2e8019d730a3d21c73041461fe04a32d6e0f2dc9..03946b15832a9abb956ff7c0f7cbe7f7a7ed64be 100644 (file)
@@ -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<T.SettingCategoryDefinition[]> {
+export function getDefinitions(component?: string): Promise<SettingCategoryDefinition[]> {
   return getJSON('/api/settings/list_definitions', { component }).then(
     r => r.definitions,
     throwGlobalError
@@ -32,12 +33,12 @@ export function getDefinitions(component?: string): Promise<T.SettingCategoryDef
 
 export function getValues(
   data: { keys: string; component?: string } & BranchParameters
-): Promise<T.SettingValue[]> {
+): Promise<SettingValue[]> {
   return getJSON('/api/settings/values', data).then(r => r.settings);
 }
 
 export function setSettingValue(
-  definition: T.SettingDefinition,
+  definition: SettingDefinition,
   value: any,
   component?: string
 ): Promise<void> {
index 8705c57668cf03d70afc9a4a8a7cc617a50bc437..d37c7753bc44fdf75b3556825f43491c87534c08 100644 (file)
  * 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', () => {
index 9b1036ce68e105196bd044da11f0af4a6c3ad921..66a0d15c5f99ac1bb570b1b1f44ac1194da1e130 100644 (file)
@@ -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<void>;
   saveValue: (key: string, component?: string) => Promise<void>;
-  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<Props, State> {
   timeout?: number;
   mounted = false;
@@ -91,7 +94,10 @@ export class Definition extends React.PureComponent<Props, State> {
     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<Props, State> {
       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 */
+        }
       );
     }
   };
index cfc9eb5e71ba6088e89461061d4c023e6821cdd3..70eb44829c5d59a286c967b9dde783059aea2ac0 100644 (file)
@@ -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 };
index 6687de632cfd090a530e9c5bfbcf113725ea4125..653cf225f8dab367190aa47ae9f443fcb42ea79f 100644 (file)
  * 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) {
index 1cc41a49a1dab7fdc64e5895a9e3b651aeacc93a..22493ed1046cd9dd6722d040c4e21775164fc481 100644 (file)
@@ -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<T.Setting & { definition: T.SettingCategoryDefinition }>;
+  settings: Array<Setting & { definition: SettingCategoryDefinition }>;
   subCategory?: string;
 }
 
index 8707c5f3e10c720e6495d99ef75cc88f108c0e22..764be721a3832a2a526b27a6a705f33b7b228a64 100644 (file)
 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<Function>('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<Definition['props']> = {}) {
-  return shallow(
+  return shallow<Definition>(
     <Definition
       cancelChange={jest.fn()}
       changeValue={jest.fn()}
index 7472ad0bcb7ccb7aebbc4d066858d6af850d1619..76efb7f237d82abe7e7b2adbe00f0b6bade8d074 100644 (file)
  */
 import { shallow } from 'enzyme';
 import * as React from 'react';
+import { SettingCategoryDefinition } from '../../../../types/settings';
 import DefinitionActions from '../DefinitionActions';
 
-const definition: T.SettingCategoryDefinition = {
+const definition: SettingCategoryDefinition = {
   category: 'baz',
   description: 'lorem',
   fields: [],
index 774426fdbb5ae5713d5b47c86519d05170d3c2ea..117b391671592139e50b38255e4dfffe6712d9e5 100644 (file)
  */
 import * as React from 'react';
 import Select from 'sonar-ui-common/components/controls/Select';
+import { SettingCategoryDefinition } from '../../../../types/settings';
 import { DefaultSpecializedInputProps } from '../../utils';
 
-type Props = DefaultSpecializedInputProps & Pick<T.SettingCategoryDefinition, 'options'>;
+type Props = DefaultSpecializedInputProps & Pick<SettingCategoryDefinition, 'options'>;
 
 export default class InputForSingleSelectList extends React.PureComponent<Props> {
   handleInputChange = ({ value }: { value: string }) => {
index 44b6131c21d7552dc782e44271bf41f653c4361f..ce91fc76318f17fbe7bfc303a6b573c3774bdba9 100644 (file)
@@ -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<DefaultSpecializedInputProps>;
+  [type in SettingType]?: React.ComponentType<DefaultSpecializedInputProps>;
 } = {
   STRING: InputForString,
   TEXT: InputForText,
+  JSON: InputForText,
   PASSWORD: InputForPassword,
   BOOLEAN: InputForBoolean,
   INTEGER: InputForNumber,
index 8623863c719185f4effb10c6ebee916c01aec59b..c800f03dee07346a29aa2939211410c1f4066fdb 100644 (file)
@@ -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' }
   };
index cd90e9b0d0ff0918eaca8504abcef96e2c5c7fef..c47d92eadb27bc9d49a204691f60041d983c20d2 100644 (file)
@@ -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',
index 68cf95eb1c4c146f85840a449b83f59ec55cc2c6..924aa9190a1d97c61f6063bee4c269710bbe50a3 100644 (file)
  * 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
+    });
+  });
+});
index 23b969a94842ce34b60fd1cf32fa5ad1731722b9..7201065e6ab0cdbeac11fb08dfc1fa371291e2f7 100644 (file)
@@ -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;
   };
index 977b5ddf9f5310e6e2d3eeee48167d38b566f44c..8e1af16c1dbb0fc08d449d20fcf86b0b4b060604 100644 (file)
@@ -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<typeof receiveDefinitions, Actions.ReceiveDefinitions>;
 
-export type State = T.Dict<T.SettingCategoryDefinition>;
+export type State = T.Dict<SettingCategoryDefinition>;
 
-export function receiveDefinitions(definitions: T.SettingCategoryDefinition[]) {
+export function receiveDefinitions(definitions: SettingCategoryDefinition[]) {
   return { type: Actions.ReceiveDefinitions, definitions };
 }
 
index 2a4256c1919d31b358dd9b5e946f9b5844b9ee91..8b00453b9b6cca803f3e7c07905e027d4d04f78c 100644 (file)
@@ -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<typeof receiveValues, Actions.receiveValues>;
 
-type SettingsState = T.Dict<T.SettingValue>;
+type SettingsState = T.Dict<SettingValue>;
 
 export interface State {
   components: T.Dict<SettingsState>;
@@ -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];
   }
index 9e4860bcf5a73adb27a213ec0684d4188ec264ba..0fa364c5c0f147bfed9d4c0c5b4c53378146d2b2 100644 (file)
@@ -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') {
index 5626af2b7097c532f076469669c62f00ac12c2b6..c7ca6d250ab3eb405b25199a8068da02e88f90fb 100644 (file)
@@ -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<T.Dict<string>>;
+  inherited?: boolean;
+  key: string;
+  parentFieldValues?: Array<T.Dict<string>>;
+  parentValue?: string;
+  parentValues?: string[];
+  value?: string;
+  values?: string[];
+}
index 9edfc2edf407dc82069c0c533d4403ffd5c36bd6..223967cd14e48a57f2aadecc50e76e4bdc23e77a 100644 (file)
@@ -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<T.Dict<string>>;
-    inherited?: boolean;
-    key: string;
-    parentFieldValues?: Array<T.Dict<string>>;
-    parentValue?: string;
-    parentValues?: string[];
-    value?: string;
-    values?: string[];
-  }
-
   export interface Snippet {
     start: number;
     end: number;