]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-11938 Remove Server ID and Version from system information
authorWouter Admiraal <wouter.admiraal@sonarsource.com>
Tue, 7 May 2019 11:01:01 +0000 (13:01 +0200)
committerSonarTech <sonartech@sonarsource.com>
Thu, 9 May 2019 18:21:09 +0000 (20:21 +0200)
32 files changed:
server/sonar-web/src/main/js/api/system.ts
server/sonar-web/src/main/js/app/types.d.ts
server/sonar-web/src/main/js/apps/system/__tests__/utils-test.ts
server/sonar-web/src/main/js/apps/system/components/App.tsx
server/sonar-web/src/main/js/apps/system/components/ClusterSysInfos.tsx
server/sonar-web/src/main/js/apps/system/components/PageHeader.tsx
server/sonar-web/src/main/js/apps/system/components/StandaloneSysInfos.tsx
server/sonar-web/src/main/js/apps/system/components/__tests__/ClusterSysInfos-test.tsx
server/sonar-web/src/main/js/apps/system/components/__tests__/PageHeader-test.tsx
server/sonar-web/src/main/js/apps/system/components/__tests__/StandaloneSysInfos-test.tsx
server/sonar-web/src/main/js/apps/system/components/__tests__/__snapshots__/ClusterSysInfos-test.tsx.snap
server/sonar-web/src/main/js/apps/system/components/__tests__/__snapshots__/PageHeader-test.tsx.snap
server/sonar-web/src/main/js/apps/system/components/__tests__/__snapshots__/StandaloneSysInfos-test.tsx.snap
server/sonar-web/src/main/js/apps/system/components/info-items/HealthCard.tsx
server/sonar-web/src/main/js/apps/system/components/info-items/HealthCauseItem.tsx
server/sonar-web/src/main/js/apps/system/components/info-items/HealthItem.tsx
server/sonar-web/src/main/js/apps/system/components/info-items/Section.tsx
server/sonar-web/src/main/js/apps/system/components/info-items/SysInfoItem.tsx
server/sonar-web/src/main/js/apps/system/components/info-items/__tests__/HealthCard-test.tsx
server/sonar-web/src/main/js/apps/system/components/info-items/__tests__/HealthCauseItem-test.tsx
server/sonar-web/src/main/js/apps/system/components/info-items/__tests__/HealthItem-test.tsx
server/sonar-web/src/main/js/apps/system/components/system-upgrade/SystemUpgradeForm.tsx
server/sonar-web/src/main/js/apps/system/components/system-upgrade/SystemUpgradeIntermediate.tsx
server/sonar-web/src/main/js/apps/system/components/system-upgrade/SystemUpgradeItem.tsx
server/sonar-web/src/main/js/apps/system/components/system-upgrade/SystemUpgradeNotif.tsx
server/sonar-web/src/main/js/apps/system/styles.css
server/sonar-web/src/main/js/apps/system/utils.ts
server/sonar-web/src/main/js/components/controls/ClipboardButton.tsx
server/sonar-web/src/main/js/components/controls/__tests__/ClipboardButton-test.tsx
server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/ClipboardButton-test.tsx.snap
server/sonar-web/src/main/js/helpers/testMocks.ts
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index 05542d799391c0ce6a939581f5bcfbc24954d605..83b78eab9938e81fee4673d298312504022f67da 100644 (file)
 import { getJSON, post, postJSON } from '../helpers/request';
 import throwGlobalError from '../app/utils/throwGlobalError';
 
-export type SysValue = boolean | string | number | HealthType | SysValueObject | SysValueArray;
-export interface SysValueObject {
-  [key: string]: SysValue;
-}
-export interface SysValueArray extends Array<SysValue> {}
-
-export interface SysInfoSection {
-  [sectionName: string]: SysValueObject;
-}
-
-export enum HealthType {
-  RED = 'RED',
-  YELLOW = 'YELLOW',
-  GREEN = 'GREEN'
-}
-
-export interface NodeInfo extends SysValueObject {
-  'Compute Engine Logging': { 'Logs Level': string };
-  Health: HealthType;
-  'Health Causes': string[];
-  Name: string;
-  'Web Logging': { 'Logs Level': string };
-}
-
-export interface SysInfo extends SysValueObject {
-  Health: HealthType;
-  'Health Causes': string[];
-  System: {
-    'High Availability': boolean;
-    'Logs Level': string;
-    'Server ID': string;
-  };
-}
-
-export interface ClusterSysInfo extends SysInfo {
-  'Application Nodes': NodeInfo[];
-  'Search Nodes': NodeInfo[];
-}
-
-export interface SystemUpgrade {
-  version: string;
-  description: string;
-  releaseDate: string;
-  changeLogUrl: string;
-  downloadUrl: string;
-  plugins: any;
-}
-
 export function setLogLevel(level: string): Promise<void | Response> {
   return post('/api/system/change_log_level', { level }).catch(throwGlobalError);
 }
 
-export function getSystemInfo(): Promise<SysInfo> {
+export function getSystemInfo(): Promise<T.SysInfoCluster | T.SysInfoStandalone> {
   return getJSON('/api/system/info').catch(throwGlobalError);
 }
 
@@ -81,7 +33,7 @@ export function getSystemStatus(): Promise<{ id: string; version: string; status
 }
 
 export function getSystemUpgrades(): Promise<{
-  upgrades: SystemUpgrade[];
+  upgrades: T.SystemUpgrade[];
   updateCenterRefresh: string;
 }> {
   return getJSON('/api/system/upgrades');
index 27fde4e253897368119ae45b4274e508f7cbb8c0..7ee7ed04cdd64b1a794dafe165f6774315a56eb7 100644 (file)
@@ -301,6 +301,8 @@ declare namespace T {
     name: string;
   }
 
+  export type HealthType = 'RED' | 'YELLOW' | 'GREEN';
+
   export type HomePage =
     | { type: 'APPLICATION'; branch: string | undefined; component: string }
     | { type: 'ISSUES' }
@@ -743,8 +745,6 @@ declare namespace T {
 
   export type RuleType = 'BUG' | 'VULNERABILITY' | 'CODE_SMELL' | 'SECURITY_HOTSPOT' | 'UNKNOWN';
 
-  export type Status = 'ERROR' | 'OK';
-
   export type Setting = SettingValue & { definition: SettingDefinition };
 
   export type SettingType =
@@ -850,6 +850,8 @@ declare namespace T {
 
   export type StandardType = 'owaspTop10' | 'sansTop25' | 'cwe' | 'sonarsourceSecurity';
 
+  export type Status = 'ERROR' | 'OK';
+
   export interface SubscriptionPlan {
     maxNcloc: number;
     price: number;
@@ -861,6 +863,80 @@ declare namespace T {
     text: string;
   }
 
+  export interface SysInfoAppNode extends SysInfoBase {
+    'Compute Engine Logging': SysInfoLogging;
+    Name: string;
+    'Web Logging': SysInfoLogging;
+  }
+
+  export interface SysInfoBase extends SysInfoValueObject {
+    Health: HealthType;
+    'Health Causes': string[];
+    Plugins?: Dict<string>;
+    System: {
+      Version: string;
+    };
+  }
+
+  export interface SysInfoCluster extends SysInfoBase {
+    'Application Nodes': SysInfoAppNode[];
+    'Search Nodes': SysInfoSearchNode[];
+    Settings: Dict<string>;
+    Statistics?: {
+      ncloc: number;
+    };
+    System: {
+      'High Availability': true;
+      'Server ID': string;
+      Version: string;
+    };
+  }
+
+  export interface SysInfoLogging extends Dict<string> {
+    'Logs Level': string;
+  }
+
+  export interface SysInfoSearchNode extends SysInfoValueObject {
+    Name: string;
+  }
+
+  export interface SysInfoSection extends Dict<SysInfoValueObject> {}
+
+  export interface SysInfoStandalone extends SysInfoBase {
+    'Compute Engine Logging': SysInfoLogging;
+    Settings: Dict<string>;
+    Statistics?: {
+      ncloc: number;
+    } & Dict<string | number>;
+    System: {
+      'High Availability': false;
+      'Server ID': string;
+      Version: string;
+    };
+    'Web Logging': SysInfoLogging;
+  }
+
+  export type SysInfoValue =
+    | boolean
+    | string
+    | number
+    | undefined
+    | HealthType
+    | SysInfoValueObject
+    | SysInfoValueArray;
+
+  export interface SysInfoValueArray extends Array<SysInfoValue> {}
+
+  export interface SysInfoValueObject extends Dict<SysInfoValue> {}
+
+  export interface SystemUpgrade {
+    version: string;
+    description: string;
+    releaseDate: string;
+    changeLogUrl: string;
+    downloadUrl: string;
+  }
+
   export interface Task {
     analysisId?: string;
     branch?: string;
index d94d51599ec5116d2fa9a83d870447bbcad92bcf..22247758f54e71d0611c4f25b64ce2b01107f444 100644 (file)
@@ -17,8 +17,9 @@
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
+/* eslint-disable sonarjs/no-duplicate-string */
 import * as u from '../utils';
-import { ClusterSysInfo, SysInfo, SystemUpgrade } from '../../../api/system';
+import { mockClusterSysInfo, mockStandaloneSysInfo } from '../../../helpers/testMocks';
 
 describe('parseQuery', () => {
   it('should correctly parse the expand array', () => {
@@ -44,44 +45,30 @@ describe('groupSections', () => {
 });
 
 describe('getSystemLogsLevel', () => {
-  it('should correctly return log level for standalone mode', () => {
-    expect(u.getSystemLogsLevel({ System: { 'Logs Level': 'FOO' } } as SysInfo)).toBe('FOO');
-    expect(u.getSystemLogsLevel({} as SysInfo)).toBe('INFO');
-    expect(u.getSystemLogsLevel()).toBe('INFO');
+  it('should correctly return the worst log level for standalone mode', () => {
+    expect(u.getSystemLogsLevel(mockStandaloneSysInfo())).toBe('DEBUG');
   });
 
   it('should return the worst log level for cluster mode', () => {
-    expect(
-      u.getSystemLogsLevel({
-        System: { 'High Availability': true },
-        'Application Nodes': [
-          {
-            'Compute Engine Logging': { 'Logs Level': 'DEBUG' },
-            'Web Logging': { 'Logs Level': 'INFO' }
-          },
-          {
-            'Compute Engine Logging': { 'Logs Level': 'INFO' },
-            'Web Logging': { 'Logs Level': 'INFO' }
-          }
-        ]
-      } as ClusterSysInfo)
-    ).toBe('DEBUG');
+    expect(u.getSystemLogsLevel(mockClusterSysInfo())).toBe('DEBUG');
   });
 
   it('should not fail if the log informations are not there yet', () => {
     expect(
-      u.getSystemLogsLevel({
-        System: { 'High Availability': true },
-        'Application Nodes': [{ Name: 'App 1' }, { Name: 'App 2' }]
-      } as ClusterSysInfo)
+      u.getSystemLogsLevel(
+        mockClusterSysInfo({
+          'Application Nodes': [{ Name: 'App 1' }, { Name: 'App 2' }]
+        })
+      )
     ).toBe('INFO');
     expect(
-      u.getSystemLogsLevel({
-        System: { 'High Availability': true },
-        'Application Nodes': [{ 'Compute Engine Logging': {} }, { Name: 'App 2' }]
-      } as any)
+      u.getSystemLogsLevel(
+        mockClusterSysInfo({
+          'Application Nodes': [{ 'Compute Engine Logging': {} }, { Name: 'App 2' }]
+        })
+      )
     ).toBe('INFO');
-    expect(u.getSystemLogsLevel({ System: {} } as SysInfo)).toBe('INFO');
+    expect(u.getSystemLogsLevel({} as T.SysInfoStandalone)).toBe('INFO');
   });
 });
 
@@ -93,7 +80,7 @@ describe('sortUpgrades', () => {
         { version: '5.10' },
         { version: '5.1' },
         { version: '5.4' }
-      ] as SystemUpgrade[])
+      ] as T.SystemUpgrade[])
     ).toEqual([{ version: '5.10' }, { version: '5.4.2' }, { version: '5.4' }, { version: '5.1' }]);
     expect(
       u.sortUpgrades([
@@ -101,7 +88,7 @@ describe('sortUpgrades', () => {
         { version: '5.1.2' },
         { version: '6.0' },
         { version: '6.9' }
-      ] as SystemUpgrade[])
+      ] as T.SystemUpgrade[])
     ).toEqual([{ version: '6.9' }, { version: '6.0' }, { version: '5.10' }, { version: '5.1.2' }]);
   });
 });
@@ -114,7 +101,7 @@ describe('groupUpgrades', () => {
         { version: '5.4.2' },
         { version: '5.4' },
         { version: '5.1' }
-      ] as SystemUpgrade[])
+      ] as T.SystemUpgrade[])
     ).toEqual([
       [{ version: '5.10' }, { version: '5.4.2' }, { version: '5.4' }, { version: '5.1' }]
     ]);
@@ -125,10 +112,186 @@ describe('groupUpgrades', () => {
         { version: '6.0' },
         { version: '5.10' },
         { version: '5.4.2' }
-      ] as SystemUpgrade[])
+      ] as T.SystemUpgrade[])
     ).toEqual([
       [{ version: '6.9' }, { version: '6.7' }, { version: '6.0' }],
       [{ version: '5.10' }, { version: '5.4.2' }]
     ]);
   });
 });
+
+describe('isCluster', () => {
+  it('should return the correct information', () => {
+    expect(u.isCluster(mockClusterSysInfo())).toBe(true);
+    expect(u.isCluster(mockStandaloneSysInfo())).toBe(false);
+  });
+});
+
+describe('isLogInfoBlock', () => {
+  it('should return the correct information', () => {
+    expect(u.isLogInfoBlock(mockStandaloneSysInfo().System)).toBe(false);
+    expect(u.isLogInfoBlock(mockStandaloneSysInfo()['Web Logging'])).toBe(true);
+  });
+});
+
+describe('hasLoggingInfo', () => {
+  it('should return the correct information', () => {
+    expect(u.hasLoggingInfo(mockStandaloneSysInfo())).toBe(true);
+    expect(u.hasLoggingInfo(mockClusterSysInfo()['Application Nodes'][0])).toBe(true);
+    expect(u.hasLoggingInfo(mockClusterSysInfo())).toBe(false);
+  });
+});
+
+describe('getStandaloneSecondarySections', () => {
+  it('should return the correct information', () => {
+    expect(Object.keys(u.getStandaloneSecondarySections(mockStandaloneSysInfo()))).toEqual(
+      expect.arrayContaining(['Compute Engine', 'Search Engine', 'Web'])
+    );
+    expect(Object.keys(u.getStandaloneSecondarySections(mockClusterSysInfo()))).toEqual(
+      expect.arrayContaining(['Compute Engine', 'Search Engine', 'Web'])
+    );
+  });
+});
+
+describe('getStandaloneMainSections', () => {
+  it('should return the correct information', () => {
+    expect(Object.keys(u.getStandaloneMainSections(mockStandaloneSysInfo()))).toEqual(
+      expect.arrayContaining([
+        'Server ID',
+        'High Availability',
+        'Health',
+        'Health Causes',
+        'Database'
+      ])
+    );
+  });
+});
+
+describe('getClusterMainCardSection', () => {
+  it('should return the correct information', () => {
+    expect(Object.keys(u.getClusterMainCardSection(mockClusterSysInfo()))).toEqual(
+      expect.arrayContaining([
+        'Server ID',
+        'High Availability',
+        'Lines of Code',
+        'Health',
+        'Health Causes',
+        'Database',
+        'Compute Engine Tasks',
+        'Search State',
+        'Search Indexes'
+      ])
+    );
+  });
+});
+
+describe('getSearchNodes', () => {
+  it('should return the correct information', () => {
+    expect(
+      u.getSearchNodes(
+        mockClusterSysInfo({
+          'Search Nodes': [{ Name: 'searchnode1' }]
+        })
+      )
+    ).toEqual([{ Name: 'searchnode1' }]);
+  });
+});
+
+describe('getAppNodes', () => {
+  it('should return the correct information', () => {
+    expect(
+      u.getAppNodes(
+        mockClusterSysInfo({
+          'Application Nodes': [{ Name: 'appnode1' }]
+        })
+      )
+    ).toEqual([{ Name: 'appnode1' }]);
+  });
+});
+
+describe('getNodeName', () => {
+  it('should return the correct information', () => {
+    expect(u.getNodeName({ Name: 'Foo' })).toEqual('Foo');
+  });
+});
+
+describe('getHealthCauses', () => {
+  it('should return the correct information', () => {
+    expect(u.getHealthCauses({ 'Health Causes': ['Foo'] } as T.SysInfoBase)).toEqual(['Foo']);
+  });
+});
+
+describe('getHealth', () => {
+  it('should return the correct information', () => {
+    expect(u.getHealth({ Health: 'GREEN' } as T.SysInfoBase)).toEqual('GREEN');
+  });
+});
+
+describe('getLogsLevel', () => {
+  it('should return the correct information, if available', () => {
+    expect(u.getLogsLevel({ 'Compute Engine Logging': { 'Logs Level': 'TRACE' } })).toEqual(
+      'TRACE'
+    );
+  });
+
+  it('should return the worst level', () => {
+    expect(
+      u.getLogsLevel({
+        'Web Logging': { 'Logs Level': 'DEBUG' },
+        'Compute Engine Logging': { 'Logs Level': 'TRACE' }
+      })
+    ).toEqual('TRACE');
+  });
+
+  it('should return the default level if no information is provided', () => {
+    expect(u.getLogsLevel()).toEqual('INFO');
+  });
+});
+
+describe('getServerId', () => {
+  it('should return the correct information, if available', () => {
+    expect(u.getServerId(mockStandaloneSysInfo({ System: { 'Server ID': 'foo-bar' } }))).toEqual(
+      'foo-bar'
+    );
+  });
+
+  it('should return undefined if no information is available', () => {
+    expect(u.getServerId(mockStandaloneSysInfo({ System: {} }))).toBeUndefined();
+  });
+});
+
+describe('getVersion', () => {
+  it('should return the correct information, if available', () => {
+    expect(u.getVersion(mockStandaloneSysInfo({ System: { Version: '1.0' } }))).toEqual('1.0');
+  });
+
+  it('should return undefined if no information is available', () => {
+    expect(u.getVersion(mockStandaloneSysInfo({ System: {} }))).toBeUndefined();
+  });
+});
+
+describe('getClusterVersion', () => {
+  it('should return the correct information, if available', () => {
+    expect(
+      u.getClusterVersion(
+        mockClusterSysInfo({
+          'Application Nodes': [{ System: { Version: '1.0' } }]
+        })
+      )
+    ).toEqual('1.0');
+  });
+
+  it('should return undefined if no information is available', () => {
+    expect(
+      u.getClusterVersion(mockClusterSysInfo({ 'Application Nodes': [{ System: {} }] }))
+    ).toBeUndefined();
+    expect(
+      u.getClusterVersion(
+        mockClusterSysInfo({
+          'Application Nodes': [],
+          System: { Version: '1.0' }
+        })
+      )
+    ).toBeUndefined();
+  });
+});
index b90b28493f3e40e16ef8e495bce36004bec90f75..07cec002e46a3f40d95cf06822b9bae27a01643a 100644 (file)
@@ -26,14 +26,16 @@ import StandaloneSysInfos from './StandaloneSysInfos';
 import SystemUpgradeNotif from './system-upgrade/SystemUpgradeNotif';
 import Suggestions from '../../../app/components/embed-docs-modal/Suggestions';
 import { translate } from '../../../helpers/l10n';
-import { ClusterSysInfo, getSystemInfo, SysInfo } from '../../../api/system';
+import { getSystemInfo } from '../../../api/system';
 import {
   getServerId,
   getSystemLogsLevel,
   isCluster,
   parseQuery,
   Query,
-  serializeQuery
+  serializeQuery,
+  getVersion,
+  getClusterVersion
 } from '../utils';
 import '../styles.css';
 
@@ -41,7 +43,7 @@ type Props = WithRouterProps;
 
 interface State {
   loading: boolean;
-  sysInfoData?: SysInfo;
+  sysInfoData?: T.SysInfoCluster | T.SysInfoStandalone;
 }
 
 class App extends React.PureComponent<Props, State> {
@@ -60,7 +62,7 @@ class App extends React.PureComponent<Props, State> {
   fetchSysInfo = () => {
     this.setState({ loading: true });
     getSystemInfo().then(
-      (sysInfoData: SysInfo) => {
+      sysInfoData => {
         if (this.mounted) {
           this.setState({ loading: false, sysInfoData });
         }
@@ -100,7 +102,7 @@ class App extends React.PureComponent<Props, State> {
       return (
         <ClusterSysInfos
           expandedCards={query.expandedCards}
-          sysInfoData={sysInfoData as ClusterSysInfo}
+          sysInfoData={sysInfoData}
           toggleCard={this.toggleSysInfoCards}
         />
       );
@@ -121,14 +123,19 @@ class App extends React.PureComponent<Props, State> {
         <Suggestions suggestions="system_info" />
         <Helmet title={translate('system_info.page')} />
         <SystemUpgradeNotif />
-        <PageHeader
-          isCluster={isCluster(sysInfoData)}
-          loading={loading}
-          logLevel={getSystemLogsLevel(sysInfoData)}
-          onLogLevelChange={this.fetchSysInfo}
-          serverId={getServerId(sysInfoData)}
-          showActions={sysInfoData !== undefined}
-        />
+        {sysInfoData && (
+          <PageHeader
+            isCluster={isCluster(sysInfoData)}
+            loading={loading}
+            logLevel={getSystemLogsLevel(sysInfoData)}
+            onLogLevelChange={this.fetchSysInfo}
+            serverId={getServerId(sysInfoData)}
+            showActions={sysInfoData !== undefined}
+            version={
+              isCluster(sysInfoData) ? getClusterVersion(sysInfoData) : getVersion(sysInfoData)
+            }
+          />
+        )}
         {this.renderSysInfo()}
       </div>
     );
index 7231c753b252d1bc94aa2281bd4ed0bdfe220f76..2a86c77b1c3436bd8f5faaa81e5a5b333599f3fe 100644 (file)
@@ -21,7 +21,6 @@ import * as React from 'react';
 import { sortBy } from 'lodash';
 import HealthCard from './info-items/HealthCard';
 import { translate } from '../../../helpers/l10n';
-import { ClusterSysInfo } from '../../../api/system';
 import {
   getAppNodes,
   getHealth,
@@ -34,7 +33,7 @@ import {
 
 interface Props {
   expandedCards: string[];
-  sysInfoData: ClusterSysInfo;
+  sysInfoData: T.SysInfoCluster;
   toggleCard: (toggledCard: string) => void;
 }
 
@@ -54,7 +53,7 @@ export default function ClusterSysInfos({ expandedCards, sysInfoData, toggleCard
       <li className="note system-info-health-title">
         {translate('system.application_nodes_title')}
       </li>
-      {sortBy(getAppNodes(sysInfoData), getNodeName).map(node => (
+      {sortBy(getAppNodes(sysInfoData), getNodeName).map((node: T.SysInfoAppNode) => (
         <HealthCard
           health={getHealth(node)}
           healthCauses={getHealthCauses(node)}
@@ -66,10 +65,8 @@ export default function ClusterSysInfos({ expandedCards, sysInfoData, toggleCard
         />
       ))}
       <li className="note system-info-health-title">{translate('system.search_nodes_title')}</li>
-      {sortBy(getSearchNodes(sysInfoData), getNodeName).map(node => (
+      {sortBy(getSearchNodes(sysInfoData), getNodeName).map((node: T.SysInfoSearchNode) => (
         <HealthCard
-          health={getHealth(node)}
-          healthCauses={getHealthCauses(node)}
           key={getNodeName(node)}
           name={getNodeName(node)}
           onClick={toggleCard}
index bc55969fc2af8d2771d762477f56ad04786a6dc4..3816eb2cb028857008607dc1c820c084ca22c21e 100644 (file)
  */
 import * as React from 'react';
 import PageActions from './PageActions';
+import ClipboardButton from '../../../components/controls/ClipboardButton';
 import { translate } from '../../../helpers/l10n';
+import { toShortNotSoISOString } from '../../../helpers/dates';
 
-interface Props {
+export interface Props {
   isCluster: boolean;
   loading: boolean;
   logLevel: string;
   onLogLevelChange: () => void;
   serverId?: string;
   showActions: boolean;
+  version?: string;
 }
 
 export default function PageHeader(props: Props) {
+  const { isCluster, loading, logLevel, serverId, showActions, version } = props;
   return (
     <header className="page-header">
       <h1 className="page-title">{translate('system_info.page')}</h1>
-      {props.showActions && (
+      {showActions && (
         <PageActions
-          canDownloadLogs={!props.isCluster}
-          canRestart={!props.isCluster}
-          cluster={props.isCluster}
-          logLevel={props.logLevel}
+          canDownloadLogs={!isCluster}
+          canRestart={!isCluster}
+          cluster={isCluster}
+          logLevel={logLevel}
           onLogLevelChange={props.onLogLevelChange}
-          serverId={props.serverId}
+          serverId={serverId}
         />
       )}
-      {props.loading && (
+      {loading && (
         <div className="page-actions">
           <i className="spinner" />
         </div>
       )}
+      {serverId && version && (
+        <div className="system-info-copy-paste-id-info boxed-group display-flex-center">
+          <div className="flex-1">
+            <table className="width-100">
+              <tbody>
+                <tr>
+                  <th>
+                    <strong>{translate('system.server_id')}</strong>
+                  </th>
+                  <td>{serverId}</td>
+                </tr>
+                <tr>
+                  <th>
+                    <strong>{translate('system.version')}</strong>
+                  </th>
+                  <td>{version}</td>
+                </tr>
+              </tbody>
+            </table>
+          </div>
+          <ClipboardButton
+            className="flex-0"
+            copyValue={`SonarQube ID information
+Server ID: ${serverId}
+Version: ${version}
+Date: ${toShortNotSoISOString(Date.now())}
+`}
+            label={translate('system.copy_id_info')}
+          />
+        </div>
+      )}
     </header>
   );
 }
index e0ba16fe7548a8f46cc0759b71d43eb06bf9f312..adb9db52ec5ee3550da8638bd13acc20502fd228 100644 (file)
@@ -20,7 +20,6 @@
 import * as React from 'react';
 import { map } from 'lodash';
 import HealthCard from './info-items/HealthCard';
-import { SysInfo } from '../../../api/system';
 import {
   getHealth,
   getHealthCauses,
@@ -31,7 +30,7 @@ import {
 
 interface Props {
   expandedCards: string[];
-  sysInfoData: SysInfo;
+  sysInfoData: T.SysInfoStandalone;
   toggleCard: (toggledCard: string) => void;
 }
 
index ed0b62b7dc33269d55c9b89b9d5162d4137c441a..395f5bf30ac90cede71597adcf4cfb93cb6a6ec0 100644 (file)
 import * as React from 'react';
 import { shallow } from 'enzyme';
 import ClusterSysInfos from '../ClusterSysInfos';
-import { ClusterSysInfo, HealthType } from '../../../../api/system';
-
-const sysInfoData: ClusterSysInfo = {
-  Health: HealthType.RED,
-  'Health Causes': ['Database down'],
-  'Application Nodes': [
-    {
-      Name: 'Bar',
-      Health: HealthType.GREEN,
-      'Health Causes': [],
-      'Compute Engine Logging': { 'Logs Level': 'INFO' },
-      'Web Logging': { 'Logs Level': 'INFO' }
-    }
-  ],
-  'Search Nodes': [
-    {
-      Name: 'Baz',
-      Health: HealthType.YELLOW,
-      'Health Causes': [],
-      'Compute Engine Logging': { 'Logs Level': 'INFO' },
-      'Web Logging': { 'Logs Level': 'INFO' }
-    }
-  ],
-  System: {
-    'High Availability': true,
-    'Logs Level': 'INFO',
-    'Server ID': 'MyServerId'
-  }
-};
+import { mockClusterSysInfo } from '../../../../helpers/testMocks';
 
 it('should render correctly', () => {
   expect(
-    getWrapper({
-      sysInfoData: {
-        ...sysInfoData,
+    shallowRender(
+      mockClusterSysInfo({
+        sysInfoData: {
+          Health: 'RED',
+          'Health Causes': ['Database down'],
+          'Application Nodes': [
+            {
+              Name: 'Foo',
+              Health: 'GREEN',
+              'Health Causes': [],
+              'Compute Engine Logging': { 'Logs Level': 'INFO' },
+              'Web Logging': { 'Logs Level': 'INFO' }
+            },
+            {
+              Name: 'Bar',
+              Health: 'RED',
+              'Health Causes': [],
+              'Compute Engine Logging': { 'Logs Level': 'INFO' },
+              'Web Logging': { 'Logs Level': 'DEBUG' }
+            },
+            {
+              Name: 'Baz',
+              Health: 'YELLOW',
+              'Health Causes': [],
+              'Compute Engine Logging': { 'Logs Level': 'TRACE' },
+              'Web Logging': { 'Logs Level': 'DEBUG' }
+            }
+          ]
+        }
+      })
+    ).find('HealthCard')
+  ).toHaveLength(4);
+});
+
+it('should support more than two nodes', () => {
+  expect(shallowRender()).toMatchSnapshot();
+});
+
+function shallowRender(props = {}) {
+  return shallow(
+    <ClusterSysInfos
+      expandedCards={['System', 'Foo']}
+      sysInfoData={mockClusterSysInfo({
+        Health: 'RED',
+        'Health Causes': ['Database down'],
         'Application Nodes': [
-          {
-            Name: 'Foo',
-            Health: HealthType.GREEN,
-            'Health Causes': [],
-            'Compute Engine Logging': { 'Logs Level': 'INFO' },
-            'Web Logging': { 'Logs Level': 'INFO' }
-          },
           {
             Name: 'Bar',
-            Health: HealthType.RED,
+            Health: 'GREEN',
             'Health Causes': [],
             'Compute Engine Logging': { 'Logs Level': 'INFO' },
-            'Web Logging': { 'Logs Level': 'DEBUG' }
-          },
+            'Web Logging': { 'Logs Level': 'INFO' }
+          }
+        ],
+        'Search Nodes': [
           {
             Name: 'Baz',
-            Health: HealthType.YELLOW,
+            Health: 'YELLOW',
             'Health Causes': [],
-            'Compute Engine Logging': { 'Logs Level': 'TRACE' },
-            'Web Logging': { 'Logs Level': 'DEBUG' }
+            'Compute Engine Logging': { 'Logs Level': 'INFO' },
+            'Web Logging': { 'Logs Level': 'INFO' }
           }
         ]
-      }
-    }).find('HealthCard')
-  ).toHaveLength(5);
-});
-
-it('should support more than two nodes', () => {
-  expect(getWrapper()).toMatchSnapshot();
-});
-
-function getWrapper(props = {}) {
-  return shallow(
-    <ClusterSysInfos
-      expandedCards={['System', 'Foo']}
-      sysInfoData={sysInfoData}
+      })}
       toggleCard={() => {}}
       {...props}
     />
index 0164cc044ebc1a37e3958ffc1d2e9b5fc30bacef..382169ee3b9f0a7e292a9d8194c94042cae6f958 100644 (file)
  */
 import * as React from 'react';
 import { shallow } from 'enzyme';
-import PageHeader from '../PageHeader';
+import PageHeader, { Props } from '../PageHeader';
+
+jest.mock('../../../../helpers/dates', () => ({
+  toShortNotSoISOString: () => '2019-01-01'
+}));
 
 it('should render correctly', () => {
-  expect(
-    shallow(
-      <PageHeader
-        isCluster={true}
-        loading={false}
-        logLevel="INFO"
-        onLogLevelChange={() => {}}
-        showActions={true}
-      />
-    )
-  ).toMatchSnapshot();
+  expect(shallowRender()).toMatchSnapshot();
+  expect(shallowRender({ loading: true, showActions: false })).toMatchSnapshot();
+  expect(shallowRender({ serverId: 'foo-bar', version: '7.7.0.1234' })).toMatchSnapshot();
 });
 
-it('should show a loading spinner and no actions', () => {
-  expect(
-    shallow(
-      <PageHeader
-        isCluster={true}
-        loading={true}
-        logLevel="INFO"
-        onLogLevelChange={() => {}}
-        showActions={false}
-      />
-    )
-  ).toMatchSnapshot();
-});
+function shallowRender(props: Partial<Props> = {}) {
+  return shallow(
+    <PageHeader
+      isCluster={true}
+      loading={false}
+      logLevel="INFO"
+      onLogLevelChange={jest.fn()}
+      showActions={true}
+      {...props}
+    />
+  );
+}
index 69a40917cb5f3fad1a14d865a1f497271368542b..e7bec64929e5bf2b16ba8fcaf0dabc36ba1f41e7 100644 (file)
 import * as React from 'react';
 import { shallow } from 'enzyme';
 import StandaloneSysInfos from '../StandaloneSysInfos';
-import { HealthType, SysInfo } from '../../../../api/system';
-
-const sysInfoData: SysInfo = {
-  Health: HealthType.RED,
-  'Health Causes': ['Database down'],
-  'Web JVM': { 'Max Memory': '2Gb' },
-  'Compute Engine': { Pending: 4 },
-  Search: { 'Number of Nodes': 1 },
-  System: {
-    'High Availability': true,
-    'Logs Level': 'DEBUG',
-    'Server ID': 'MyServerId'
-  }
-};
+import { mockStandaloneSysInfo } from '../../../../helpers/testMocks';
 
 it('should render correctly', () => {
   expect(getWrapper()).toMatchSnapshot();
@@ -43,7 +30,7 @@ function getWrapper(props = {}) {
   return shallow(
     <StandaloneSysInfos
       expandedCards={['Compute Engine', 'Foo']}
-      sysInfoData={sysInfoData}
+      sysInfoData={mockStandaloneSysInfo({ Health: 'RED', 'Health Causes': ['Database down'] })}
       toggleCard={() => {}}
       {...props}
     />
index 580f697953430fb22dcb9609a3249579fd448acb..a186f4110d3c0c17b0de8c74d7d4ade4467a0693 100644 (file)
@@ -15,9 +15,28 @@ exports[`should support more than two nodes 1`] = `
     open={true}
     sysInfoData={
       Object {
+        "Compute Engine Tasks": Object {
+          "Total In Progress": 0,
+          "Total Pending": 0,
+        },
+        "Database": Object {
+          "Database": "PostgreSQL",
+          "Database Version": "10.3",
+          "Driver": "PostgreSQL JDBC Driver",
+          "Driver Version": "42.2.5",
+          "URL": "jdbc:postgresql://localhost/sonar",
+          "Username": "sonar",
+        },
         "High Availability": true,
-        "Logs Level": "INFO",
-        "Server ID": "MyServerId",
+        "Lines of Code": "989,880",
+        "Search Indexes": Object {
+          "Index components - Docs": 30445,
+          "Index components - Shards": 10,
+        },
+        "Search State": Object {
+          "Nodes": 3,
+          "State": "GREEN",
+        },
       }
     }
   />
@@ -50,8 +69,6 @@ exports[`should support more than two nodes 1`] = `
     system.search_nodes_title
   </li>
   <HealthCard
-    health="YELLOW"
-    healthCauses={Array []}
     key="Baz"
     name="Baz"
     onClick={[Function]}
index 39f93da43227c2a872fb0569d0ed89df02e96ad0..84f459faba77caef50bd1ebae351efe31975801d 100644 (file)
@@ -14,12 +14,12 @@ exports[`should render correctly 1`] = `
     canRestart={false}
     cluster={true}
     logLevel="INFO"
-    onLogLevelChange={[Function]}
+    onLogLevelChange={[MockFunction]}
   />
 </header>
 `;
 
-exports[`should show a loading spinner and no actions 1`] = `
+exports[`should render correctly 2`] = `
 <header
   className="page-header"
 >
@@ -37,3 +37,66 @@ exports[`should show a loading spinner and no actions 1`] = `
   </div>
 </header>
 `;
+
+exports[`should render correctly 3`] = `
+<header
+  className="page-header"
+>
+  <h1
+    className="page-title"
+  >
+    system_info.page
+  </h1>
+  <PageActions
+    canDownloadLogs={false}
+    canRestart={false}
+    cluster={true}
+    logLevel="INFO"
+    onLogLevelChange={[MockFunction]}
+    serverId="foo-bar"
+  />
+  <div
+    className="system-info-copy-paste-id-info boxed-group display-flex-center"
+  >
+    <div
+      className="flex-1"
+    >
+      <table
+        className="width-100"
+      >
+        <tbody>
+          <tr>
+            <th>
+              <strong>
+                system.server_id
+              </strong>
+            </th>
+            <td>
+              foo-bar
+            </td>
+          </tr>
+          <tr>
+            <th>
+              <strong>
+                system.version
+              </strong>
+            </th>
+            <td>
+              7.7.0.1234
+            </td>
+          </tr>
+        </tbody>
+      </table>
+    </div>
+    <ClipboardButton
+      className="flex-0"
+      copyValue="SonarQube ID information
+Server ID: foo-bar
+Version: 7.7.0.1234
+Date: 2019-01-01
+"
+      label="system.copy_id_info"
+    />
+  </div>
+</header>
+`;
index b71406e8fba10a3a9ad1d6c141bdc78c29b263e3..0d5a69d87e1b9f9fe726d5485ece6555cc9deb54 100644 (file)
@@ -15,9 +15,15 @@ exports[`should render correctly 1`] = `
     open={false}
     sysInfoData={
       Object {
-        "High Availability": true,
-        "Logs Level": "DEBUG",
-        "Server ID": "MyServerId",
+        "Database": Object {
+          "Database": "PostgreSQL",
+          "Database Version": "10.3",
+          "Driver": "PostgreSQL JDBC Driver",
+          "Driver Version": "42.2.5",
+          "URL": "jdbc:postgresql://localhost/sonar",
+          "Username": "sonar",
+        },
+        "High Availability": false,
       }
     }
   />
@@ -28,8 +34,21 @@ exports[`should render correctly 1`] = `
     open={false}
     sysInfoData={
       Object {
-        "Web JVM": Object {
-          "Max Memory": "2Gb",
+        "Web Database Connection": Object {
+          "Pool Active Connections": 0,
+          "Pool Max Connections": 60,
+        },
+        "Web JVM Properties": Object {
+          "file.encoding": "UTF-8",
+          "file.separator": "/",
+        },
+        "Web JVM State": Object {
+          "Free Memory (MB)": 111,
+          "Max Memory (MB)": 1024,
+        },
+        "Web Logging": Object {
+          "Logs Dir": "/logs",
+          "Logs Level": "INFO",
         },
       }
     }
@@ -41,8 +60,25 @@ exports[`should render correctly 1`] = `
     open={true}
     sysInfoData={
       Object {
-        "Compute Engine": Object {
-          "Pending": 4,
+        "Compute Engine Database Connection": Object {
+          "Pool Active Connections": 0,
+          "Pool Initial Size": 0,
+        },
+        "Compute Engine JVM Properties": Object {
+          "file.encoding": "UTF-8",
+          "file.separator": "/",
+        },
+        "Compute Engine JVM State": Object {
+          "Free Memory (MB)": 89,
+          "Max Memory (MB)": 1024,
+        },
+        "Compute Engine Logging": Object {
+          "Logs Dir": "/logs",
+          "Logs Level": "DEBUG",
+        },
+        "Compute Engine Tasks": Object {
+          "In Progress": 0,
+          "Pending": 0,
         },
       }
     }
@@ -54,8 +90,13 @@ exports[`should render correctly 1`] = `
     open={false}
     sysInfoData={
       Object {
-        "Search": Object {
-          "Number of Nodes": 1,
+        "Search Indexes": Object {
+          "Index components - Docs": 30445,
+          "Index components - Shards": 10,
+        },
+        "Search State": Object {
+          "Nodes": 3,
+          "State": "GREEN",
         },
       }
     }
index f6e21f1b1894f30c2d3719d6ee16e452739d1954..95e3d72aef65e91030a8ad320a67a3927937d5e0 100644 (file)
@@ -21,20 +21,19 @@ import * as React from 'react';
 import { map } from 'lodash';
 import HealthItem from './HealthItem';
 import Section from './Section';
+import { Alert } from '../../../../components/ui/Alert';
 import BoxedGroupAccordion from '../../../../components/controls/BoxedGroupAccordion';
-import { HealthType, SysValueObject } from '../../../../api/system';
 import { LOGS_LEVELS, groupSections, getLogsLevel } from '../../utils';
 import { translate } from '../../../../helpers/l10n';
-import { Alert } from '../../../../components/ui/Alert';
 
 interface Props {
   biggerHealth?: boolean;
-  health?: HealthType;
+  health?: T.HealthType;
   healthCauses?: string[];
   onClick: (toggledCard: string) => void;
   open: boolean;
   name: string;
-  sysInfoData: SysValueObject;
+  sysInfoData: T.SysInfoValueObject;
 }
 
 export default function HealthCard({
index 04c416e13384a7a89bb15677d9cd9703ce0fee8d..79ebc2e1ca4a2f2acf41a23351ff09f541031c3b 100644 (file)
  */
 import * as React from 'react';
 import * as classNames from 'classnames';
-import { HealthType } from '../../../../api/system';
 import { Alert } from '../../../../components/ui/Alert';
 
 interface Props {
   className?: string;
-  health: HealthType;
+  health: T.HealthType;
   healthCause: string;
 }
 
@@ -33,7 +32,7 @@ export default function HealthCauseItem({ className, health, healthCause }: Prop
     <Alert
       className={classNames('boxed-group-accordion-alert', className)}
       display="inline"
-      variant={health === HealthType.RED ? 'error' : 'warning'}>
+      variant={health === 'RED' ? 'error' : 'warning'}>
       {healthCause}
     </Alert>
   );
index 1b71f37fce32502421784546538f3c8fcc8b9ac5..e5291e8c1be2facb028d54fad3f9f2301820463d 100644 (file)
@@ -22,19 +22,18 @@ import * as classNames from 'classnames';
 import HealthCauseItem from './HealthCauseItem';
 import StatusIndicator from '../../../../components/common/StatusIndicator';
 import Tooltip from '../../../../components/controls/Tooltip';
-import { HealthType } from '../../../../api/system';
 import { translateWithParameters } from '../../../../helpers/l10n';
 
 interface Props {
   biggerHealth?: boolean;
   name?: string;
   className?: string;
-  health: HealthType;
+  health: T.HealthType;
   healthCauses?: string[];
 }
 
 export default function HealthItem({ biggerHealth, className, name, health, healthCauses }: Props) {
-  const hasHealthCauses = healthCauses && healthCauses.length > 0 && health !== HealthType.GREEN;
+  const hasHealthCauses = healthCauses && healthCauses.length > 0 && health !== 'GREEN';
   const statusIndicator = (
     <StatusIndicator color={health.toLowerCase()} size={biggerHealth ? 'big' : undefined} />
   );
index a40845a12a3d9b9bec5b7200ce2f14ae95ddefa8..b548e94afa9362467d7d85f0cfb31fea260ba6a5 100644 (file)
 import * as React from 'react';
 import { map } from 'lodash';
 import SysInfoItem from './SysInfoItem';
-import { SysValueObject } from '../../../../api/system';
 
 interface Props {
   name?: string;
-  items: SysValueObject;
+  items: T.SysInfoValueObject;
 }
 
 export default function Section({ name, items }: Props) {
index 3389df8bb618ad2aa1e88bf2cc8114ad8351c2c3..fc004f88d99b98fc9787cbcf292f889fe282915f 100644 (file)
@@ -22,17 +22,16 @@ import { map } from 'lodash';
 import HealthItem from './HealthItem';
 import AlertErrorIcon from '../../../../components/icons-components/AlertErrorIcon';
 import AlertSuccessIcon from '../../../../components/icons-components/AlertSuccessIcon';
-import { HealthType, SysValue, SysValueObject } from '../../../../api/system';
-import { HEALTH_FIELD } from '../../utils';
+import { HEALTH_FIELD, STATE_FIELD } from '../../utils';
 
 export interface Props {
   name: string;
-  value: SysValue;
+  value: T.SysInfoValue;
 }
 
 export default function SysInfoItem({ name, value }: Props): JSX.Element {
-  if (name === HEALTH_FIELD || name === 'State') {
-    return <HealthItem className="no-margin" health={value as HealthType} />;
+  if (name === HEALTH_FIELD || name === STATE_FIELD) {
+    return <HealthItem className="no-margin" health={value as T.HealthType} />;
   }
   if (value instanceof Array) {
     return <code>{JSON.stringify(value)}</code>;
@@ -55,7 +54,7 @@ function BooleanItem({ value }: { value: boolean }) {
   }
 }
 
-function ObjectItem({ value }: { value: SysValueObject }) {
+function ObjectItem({ value }: { value: T.SysInfoValueObject }) {
   return (
     <table className="data">
       <tbody>
index c8eaa8d45efb844c10433e326774d98832625d21..f96d0245c37a20547c7c3bb9364530d6a4b06472 100644 (file)
@@ -20,7 +20,6 @@
 import * as React from 'react';
 import { shallow } from 'enzyme';
 import HealthCard from '../HealthCard';
-import { HealthType } from '../../../../../api/system';
 
 it('should render correctly', () => {
   expect(getWrapper()).toMatchSnapshot();
@@ -48,7 +47,7 @@ function getWrapper(props = {}) {
   return shallow(
     <HealthCard
       biggerHealth={false}
-      health={HealthType.RED}
+      health="RED"
       healthCauses={['foo']}
       name="Foobar"
       onClick={() => {}}
index 1c415360938c821bee09a2a88978a615f6db30ce..52ca0760f4c9800498a5d7f26e49a9dc8b4d1c58 100644 (file)
 import * as React from 'react';
 import { shallow } from 'enzyme';
 import HealthCauseItem from '../HealthCauseItem';
-import { HealthType } from '../../../../../api/system';
 
 it('should render correctly', () => {
-  expect(shallow(<HealthCauseItem health={HealthType.RED} healthCause="foo" />)).toMatchSnapshot();
-  expect(
-    shallow(<HealthCauseItem health={HealthType.YELLOW} healthCause="foo" />)
-  ).toMatchSnapshot();
+  expect(shallow(<HealthCauseItem health="RED" healthCause="foo" />)).toMatchSnapshot();
+  expect(shallow(<HealthCauseItem health="YELLOW" healthCause="foo" />)).toMatchSnapshot();
 });
index 9857ee9b8f9b5b52bd458af2cc383d6b01602594..a17f1140b98fc5db37efc019a0cff30030fb4172 100644 (file)
 import * as React from 'react';
 import { shallow } from 'enzyme';
 import HealthItem from '../HealthItem';
-import { HealthType } from '../../../../../api/system';
 
 it('should render correctly', () => {
   expect(
-    shallow(
-      <HealthItem biggerHealth={true} health={HealthType.RED} healthCauses={['foo']} name="Foo" />
-    )
+    shallow(<HealthItem biggerHealth={true} health="RED" healthCauses={['foo']} name="Foo" />)
   ).toMatchSnapshot();
 });
 
 it('should not render health causes', () => {
-  expect(
-    shallow(<HealthItem health={HealthType.GREEN} healthCauses={['foo']} />)
-  ).toMatchSnapshot();
-  expect(shallow(<HealthItem health={HealthType.YELLOW} healthCauses={[]} />)).toMatchSnapshot();
+  expect(shallow(<HealthItem health="GREEN" healthCauses={['foo']} />)).toMatchSnapshot();
+  expect(shallow(<HealthItem health="YELLOW" healthCauses={[]} />)).toMatchSnapshot();
 });
 
 it('should render multiple health causes', () => {
-  expect(
-    shallow(<HealthItem health={HealthType.YELLOW} healthCauses={['foo', 'bar']} />)
-  ).toMatchSnapshot();
+  expect(shallow(<HealthItem health="YELLOW" healthCauses={['foo', 'bar']} />)).toMatchSnapshot();
 });
index 65712b193e73141f6244895755ffa4501c36a94c..9bc6c42f0384c1e27d55566a5c61c94d76f7e193 100644 (file)
  */
 import * as React from 'react';
 import SystemUpgradeItem from './SystemUpgradeItem';
-import { SystemUpgrade } from '../../../../api/system';
 import Modal from '../../../../components/controls/Modal';
 import { translate } from '../../../../helpers/l10n';
 import { ResetButtonLink } from '../../../../components/ui/buttons';
 
 interface Props {
-  systemUpgrades: SystemUpgrade[][];
+  systemUpgrades: T.SystemUpgrade[][];
   onClose: () => void;
 }
 
index cd49bba50d111df0272aad2befe6f0aded11c751..14c65114dbd41de61f5d42862bd693de37f8390f 100644 (file)
@@ -21,12 +21,11 @@ import * as React from 'react';
 import DateFormatter from '../../../../components/intl/DateFormatter';
 import DropdownIcon from '../../../../components/icons-components/DropdownIcon';
 import { ButtonLink } from '../../../../components/ui/buttons';
-import { SystemUpgrade } from '../../../../api/system';
 import { translate } from '../../../../helpers/l10n';
 
 interface Props {
   className?: string;
-  upgrades: SystemUpgrade[];
+  upgrades: T.SystemUpgrade[];
 }
 
 interface State {
index 93e9e7b48e6c30da5313ca3043ac3bf02e963ec8..5881085f99d937b4d3ddbb7219924b5b4965dde9 100644 (file)
@@ -21,12 +21,11 @@ import * as React from 'react';
 import { FormattedMessage } from 'react-intl';
 import SystemUpgradeIntermediate from './SystemUpgradeIntermediate';
 import DateFormatter from '../../../../components/intl/DateFormatter';
-import { SystemUpgrade } from '../../../../api/system';
 import { translate, translateWithParameters } from '../../../../helpers/l10n';
 
 interface Props {
   type?: string;
-  systemUpgrades: SystemUpgrade[];
+  systemUpgrades: T.SystemUpgrade[];
 }
 
 export default function SystemUpgradeItem({ type, systemUpgrades }: Props) {
index 87677c1b39a4ad839f74b34e9f6e4b9d1246af02..c4800cccafa173543b23c1eb7f6ac5264359ccb6 100644 (file)
 import * as React from 'react';
 import SystemUpgradeForm from './SystemUpgradeForm';
 import { sortUpgrades, groupUpgrades } from '../../utils';
-import { getSystemUpgrades, SystemUpgrade } from '../../../../api/system';
+import { getSystemUpgrades } from '../../../../api/system';
 import { Button } from '../../../../components/ui/buttons';
 import { translate } from '../../../../helpers/l10n';
 import { Alert } from '../../../../components/ui/Alert';
 
 interface State {
-  systemUpgrades: SystemUpgrade[][];
+  systemUpgrades: T.SystemUpgrade[][];
   openSystemUpgradeForm: boolean;
 }
 
index ec695d0194fa9375fb97a8ef38afe49dffe96c89..33f60e68f48f628a81d76e3fe732e294d5fed4ee 100644 (file)
   margin-top: -12px;
 }
 
+.system-info-copy-paste-id-info {
+  max-width: 550px;
+  padding: var(--gridSize) calc(2 * var(--gridSize));
+  clear: both;
+  line-height: 1.8;
+}
+
 .system-info-health-info .status-indicator {
   position: relative;
   top: 8px;
index e2d26559342b8d5d35df9248dc645dd9985da925..85f34adb82b69e9a96df9257ffb54f7849c16249 100644 (file)
@@ -25,15 +25,6 @@ import {
   RawQuery,
   serializeStringArray
 } from '../../helpers/query';
-import {
-  ClusterSysInfo,
-  HealthType,
-  NodeInfo,
-  SysInfo,
-  SysInfoSection,
-  SysValueObject,
-  SystemUpgrade
-} from '../../api/system';
 import { formatMeasure } from '../../helpers/measures';
 
 export interface Query {
@@ -41,132 +32,167 @@ export interface Query {
 }
 
 export const LOGS_LEVELS = ['INFO', 'DEBUG', 'TRACE'];
+const DEFAULT_LOG_LEVEL = LOGS_LEVELS[0];
+
+export const APP_NODES_FIELD = 'Application Nodes';
+export const CE_FIELD_PREFIX = 'Compute Engine';
+export const CE_LOGGING_FIELD = 'Compute Engine Logging';
 export const HA_FIELD = 'High Availability';
+export const HEALTH_CAUSES_FIELD = 'Health Causes';
 export const HEALTH_FIELD = 'Health';
-export const HEALTHCAUSES_FIELD = 'Health Causes';
+export const LOGS_LEVEL_FIELD = 'Logs Level';
+export const NAME_FIELD = 'Name';
+export const NCLOC_FIELD = 'ncloc';
 export const PLUGINS_FIELD = 'Plugins';
+export const SEARCH_NODES_FIELD = 'Search Nodes';
+export const SEARCH_PREFIX = 'Search';
+export const SERVER_ID_FIELD = 'Server ID';
 export const SETTINGS_FIELD = 'Settings';
-
-export function ignoreInfoFields(sysInfoObject: SysValueObject): SysValueObject {
+export const STATE_FIELD = 'State';
+export const STATS_FIELD = 'Statistics';
+export const SYSTEM_FIELD = 'System';
+export const VERSION_FIELD = 'Version';
+export const WEB_LOGGING_FIELD = 'Web Logging';
+export const WEB_PREFIX = 'Web';
+
+export function ignoreInfoFields(sysInfoObject: T.SysInfoValueObject) {
   return omit(sysInfoObject, [
     HEALTH_FIELD,
-    HEALTHCAUSES_FIELD,
-    'Name',
+    HEALTH_CAUSES_FIELD,
+    NAME_FIELD,
     PLUGINS_FIELD,
-    SETTINGS_FIELD
-  ]) as SysValueObject;
+    SETTINGS_FIELD,
+    SERVER_ID_FIELD,
+    VERSION_FIELD
+  ]);
 }
 
-export function getHealth(sysInfoObject: SysValueObject): HealthType {
-  return sysInfoObject[HEALTH_FIELD] as HealthType;
+export function getHealth(sysInfoObject: T.SysInfoBase) {
+  return sysInfoObject[HEALTH_FIELD];
 }
 
-export function getHealthCauses(sysInfoObject: SysValueObject): string[] {
-  return sysInfoObject[HEALTHCAUSES_FIELD] as string[];
+export function getHealthCauses(sysInfoObject: T.SysInfoBase) {
+  return sysInfoObject[HEALTH_CAUSES_FIELD];
 }
 
-export function getLogsLevel(sysInfoObject?: SysValueObject): string {
-  if (!sysInfoObject) {
-    return LOGS_LEVELS[0];
-  }
-  if (sysInfoObject['Web Logging'] || sysInfoObject['Compute Engine Logging']) {
-    return sortBy(
-      [
-        getLogsLevel((sysInfoObject as NodeInfo)['Web Logging']),
-        getLogsLevel((sysInfoObject as NodeInfo)['Compute Engine Logging'])
-      ],
-      logLevel => LOGS_LEVELS.indexOf(logLevel)
-    )[1];
-  }
-  if (sysInfoObject['System']) {
-    return getLogsLevel((sysInfoObject as SysInfo)['System']);
+export function getLogsLevel(sysInfoObject?: T.SysInfoValueObject): string {
+  if (sysInfoObject !== undefined) {
+    if (isLogInfoBlock(sysInfoObject)) {
+      return sysInfoObject[LOGS_LEVEL_FIELD];
+    } else if (hasLoggingInfo(sysInfoObject)) {
+      return sortBy(
+        [
+          getLogsLevel(sysInfoObject[WEB_LOGGING_FIELD]),
+          getLogsLevel(sysInfoObject[CE_LOGGING_FIELD])
+        ],
+        logLevel => LOGS_LEVELS.indexOf(logLevel)
+      )[1];
+    }
   }
-  return (sysInfoObject['Logs Level'] || LOGS_LEVELS[0]) as string;
+  return DEFAULT_LOG_LEVEL;
 }
 
-export function getAppNodes(sysInfoData: ClusterSysInfo): NodeInfo[] {
-  return sysInfoData['Application Nodes'];
+export function getAppNodes(sysInfoData: T.SysInfoCluster): T.SysInfoAppNode[] {
+  return sysInfoData[APP_NODES_FIELD];
 }
 
-export function getSearchNodes(sysInfoData: ClusterSysInfo): NodeInfo[] {
-  return sysInfoData['Search Nodes'];
+export function getSearchNodes(sysInfoData: T.SysInfoCluster): T.SysInfoSearchNode[] {
+  return sysInfoData[SEARCH_NODES_FIELD];
 }
 
-export function isCluster(sysInfoData?: SysInfo): boolean {
-  return (
-    sysInfoData !== undefined && sysInfoData['System'] && sysInfoData['System'][HA_FIELD] === true
-  );
+export function isCluster(
+  sysInfoData: T.SysInfoCluster | T.SysInfoStandalone
+): sysInfoData is T.SysInfoCluster {
+  return sysInfoData[SYSTEM_FIELD] && sysInfoData[SYSTEM_FIELD][HA_FIELD] === true;
 }
 
-export function getServerId(sysInfoData?: SysInfo): string | undefined {
-  return sysInfoData && sysInfoData['System']['Server ID'];
+export function isLogInfoBlock(
+  sysInfoObject: T.SysInfoValueObject
+): sysInfoObject is T.SysInfoLogging {
+  return sysInfoObject[LOGS_LEVEL_FIELD] !== undefined;
 }
 
-export function getSystemLogsLevel(sysInfoData?: SysInfo): string {
-  const defaultLevel = LOGS_LEVELS[0];
-  if (!sysInfoData) {
-    return defaultLevel;
-  }
+export function hasLoggingInfo(
+  sysInfoObject: T.SysInfoValueObject
+): sysInfoObject is T.SysInfoStandalone | T.SysInfoAppNode {
+  return Boolean(sysInfoObject[WEB_LOGGING_FIELD] || sysInfoObject[CE_LOGGING_FIELD]);
+}
+
+export function getServerId(sysInfoData: T.SysInfoCluster | T.SysInfoStandalone): string {
+  return sysInfoData && sysInfoData[SYSTEM_FIELD][SERVER_ID_FIELD];
+}
+
+export function getVersion(sysInfoData: T.SysInfoStandalone): string | undefined {
+  return sysInfoData && sysInfoData[SYSTEM_FIELD][VERSION_FIELD];
+}
+
+export function getClusterVersion(sysInfoData: T.SysInfoCluster): string | undefined {
+  const appNodes = getAppNodes(sysInfoData);
+  return appNodes.length > 0 ? appNodes[0][SYSTEM_FIELD][VERSION_FIELD] : undefined;
+}
+
+export function getSystemLogsLevel(sysInfoData: T.SysInfoCluster | T.SysInfoStandalone): string {
   if (isCluster(sysInfoData)) {
-    const logLevels = sortBy(
-      getAppNodes(sysInfoData as ClusterSysInfo).map(getLogsLevel),
-      logLevel => LOGS_LEVELS.indexOf(logLevel)
+    const logLevels = sortBy(getAppNodes(sysInfoData).map(getLogsLevel), logLevel =>
+      LOGS_LEVELS.indexOf(logLevel)
     );
-    return logLevels.length > 0 ? logLevels[logLevels.length - 1] : defaultLevel;
+    return logLevels.length > 0 ? logLevels[logLevels.length - 1] : DEFAULT_LOG_LEVEL;
   } else {
     return getLogsLevel(sysInfoData);
   }
 }
 
-export function getNodeName(nodeInfo: NodeInfo): string {
-  return nodeInfo['Name'];
+export function getNodeName(nodeInfo: T.SysInfoAppNode | T.SysInfoSearchNode): string {
+  return nodeInfo[NAME_FIELD];
 }
 
-function getSystemData(sysInfoData: SysInfo): SysValueObject {
-  const statData: SysValueObject = {};
-  const statistics = sysInfoData['Statistics'] as SysValueObject;
+function getSystemData(sysInfoData: T.SysInfoBase): T.SysInfoValueObject {
+  const statData: T.SysInfoValueObject = {};
+  const statistics = sysInfoData[STATS_FIELD] as T.SysInfoValueObject; // TODO
   if (statistics) {
-    statData['Lines of Code'] = formatMeasure(statistics['ncloc'] as number, 'INT');
+    statData['Lines of Code'] = formatMeasure(statistics[NCLOC_FIELD] as number, 'INT');
   }
-  return { ...sysInfoData['System'], ...statData };
+  return { ...sysInfoData[SYSTEM_FIELD], ...statData };
 }
 
-export function getClusterMainCardSection(sysInfoData: ClusterSysInfo): SysValueObject {
+export function getClusterMainCardSection(sysInfoData: T.SysInfoCluster): T.SysInfoValueObject {
   return {
     ...getSystemData(sysInfoData),
-    ...(omit(sysInfoData, [
-      'Application Nodes',
+    ...omit(sysInfoData, [
+      APP_NODES_FIELD,
       PLUGINS_FIELD,
-      'Search Nodes',
+      SEARCH_NODES_FIELD,
       SETTINGS_FIELD,
-      'Statistics',
-      'System'
-    ]) as SysValueObject)
+      STATS_FIELD,
+      SYSTEM_FIELD
+    ])
   };
 }
 
-export function getStandaloneMainSections(sysInfoData: SysInfo): SysValueObject {
+export function getStandaloneMainSections(sysInfoData: T.SysInfoBase): T.SysInfoValueObject {
   return {
     ...getSystemData(sysInfoData),
     ...(omitBy(
       sysInfoData,
       (value, key) =>
         value == null ||
-        [PLUGINS_FIELD, SETTINGS_FIELD, 'Statistics', 'System'].includes(key) ||
-        key.startsWith('Compute Engine') ||
-        key.startsWith('Search') ||
-        key.startsWith('Web')
-    ) as SysValueObject)
+        [PLUGINS_FIELD, SETTINGS_FIELD, STATS_FIELD, SYSTEM_FIELD].includes(key) ||
+        key.startsWith(CE_FIELD_PREFIX) ||
+        key.startsWith(SEARCH_PREFIX) ||
+        key.startsWith(WEB_PREFIX)
+    ) as T.SysInfoValueObject)
   };
 }
 
-export function getStandaloneSecondarySections(sysInfoData: SysInfo): SysInfoSection {
+export function getStandaloneSecondarySections(sysInfoData: T.SysInfoBase): T.SysInfoSection {
   return {
-    Web: pickBy(sysInfoData, (_, key) => key.startsWith('Web')) as SysValueObject,
+    Web: pickBy(sysInfoData, (_, key) => key.startsWith(WEB_PREFIX)) as T.SysInfoValueObject,
     'Compute Engine': pickBy(sysInfoData, (_, key) =>
-      key.startsWith('Compute Engine')
-    ) as SysValueObject,
-    'Search Engine': pickBy(sysInfoData, (_, key) => key.startsWith('Search')) as SysValueObject
+      key.startsWith(CE_FIELD_PREFIX)
+    ) as T.SysInfoValueObject,
+    'Search Engine': pickBy(sysInfoData, (_, key) =>
+      key.startsWith(SEARCH_PREFIX)
+    ) as T.SysInfoValueObject
   };
 }
 
@@ -179,9 +205,9 @@ export function getFileNameSuffix(suffix?: string) {
   );
 }
 
-export function groupSections(sysInfoData: SysValueObject) {
-  const mainSection: SysValueObject = {};
-  const sections: SysInfoSection = {};
+export function groupSections(sysInfoData: T.SysInfoValueObject) {
+  const mainSection: T.SysInfoValueObject = {};
+  const sections: T.SysInfoSection = {};
   each(sysInfoData, (item, key) => {
     if (typeof item !== 'object' || item instanceof Array) {
       mainSection[key] = item;
@@ -205,15 +231,15 @@ export const serializeQuery = memoize(
     })
 );
 
-export function sortUpgrades(upgrades: SystemUpgrade[]): SystemUpgrade[] {
+export function sortUpgrades(upgrades: T.SystemUpgrade[]): T.SystemUpgrade[] {
   return sortBy(upgrades, [
-    (upgrade: SystemUpgrade) => -Number(upgrade.version.split('.')[0]),
-    (upgrade: SystemUpgrade) => -Number(upgrade.version.split('.')[1] || 0),
-    (upgrade: SystemUpgrade) => -Number(upgrade.version.split('.')[2] || 0)
+    (upgrade: T.SystemUpgrade) => -Number(upgrade.version.split('.')[0]),
+    (upgrade: T.SystemUpgrade) => -Number(upgrade.version.split('.')[1] || 0),
+    (upgrade: T.SystemUpgrade) => -Number(upgrade.version.split('.')[2] || 0)
   ]);
 }
 
-export function groupUpgrades(upgrades: SystemUpgrade[]): SystemUpgrade[][] {
+export function groupUpgrades(upgrades: T.SystemUpgrade[]): T.SystemUpgrade[][] {
   const groupedVersions = groupBy(upgrades, upgrade => upgrade.version.split('.')[0]);
   const sortedMajor = sortBy(Object.keys(groupedVersions), key => -Number(key));
   return sortedMajor.map(key => groupedVersions[key]);
index e29f25b6289a6c61a5346cd4dcf9d331a5f0125c..94d9f2e017b890e85cf7654ea44632f1e7dbf251 100644 (file)
@@ -27,6 +27,7 @@ import { translate } from '../../helpers/l10n';
 interface Props {
   className?: string;
   copyValue: string;
+  label?: string;
 }
 
 interface State {
@@ -82,7 +83,7 @@ export default class ClipboardButton extends React.PureComponent<Props, State> {
           className={classNames('js-copy-to-clipboard no-select', this.props.className)}
           data-clipboard-text={this.props.copyValue}
           innerRef={node => (this.copyButton = node)}>
-          {translate('copy')}
+          {this.props.label ? this.props.label : translate('copy')}
         </Button>
       </Tooltip>
     );
index a5e98cc79fbcb5b6964b2684017ad2cafbe7dc87..b9bd71c1fca3921e0eb1661d121b04b76a89364d 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
-import { shallow } from 'enzyme';
+import { shallow, mount } from 'enzyme';
 import ClipboardButton from '../ClipboardButton';
 
+const constructor = jest.fn();
+const destroy = jest.fn();
+const on = jest.fn();
+
+jest.mock(
+  'clipboard',
+  () =>
+    function(...args: any) {
+      constructor(...args);
+      return {
+        destroy,
+        on
+      };
+    }
+);
+
 jest.useFakeTimers();
 
 it('should display correctly', () => {
-  const wrapper = shallow(<ClipboardButton copyValue="foo" />);
+  const wrapper = shallowRender();
   expect(wrapper).toMatchSnapshot();
-  (wrapper.instance() as ClipboardButton).showTooltip();
+  wrapper.instance().showTooltip();
   wrapper.update();
   expect(wrapper).toMatchSnapshot();
   jest.runAllTimers();
   wrapper.update();
   expect(wrapper).toMatchSnapshot();
 });
+
+it('should render a custom label if provided', () => {
+  expect(shallowRender({ label: 'Foo Bar' })).toMatchSnapshot();
+});
+
+it('should allow its content to be copied', () => {
+  const wrapper = mountRender();
+  const button = wrapper.find('button').getDOMNode();
+  const instance = wrapper.instance();
+
+  expect(constructor).toBeCalledWith(button);
+  expect(on).toBeCalledWith('success', instance.showTooltip);
+
+  jest.clearAllMocks();
+
+  wrapper.setProps({ label: 'Some new label' });
+  expect(destroy).toBeCalled();
+  expect(constructor).toBeCalledWith(button);
+  expect(on).toBeCalledWith('success', instance.showTooltip);
+
+  jest.clearAllMocks();
+
+  wrapper.unmount();
+  expect(destroy).toBeCalled();
+});
+
+function shallowRender(props: Partial<ClipboardButton['props']> = {}) {
+  return shallow<ClipboardButton>(createComponent(props));
+}
+
+function mountRender(props: Partial<ClipboardButton['props']> = {}) {
+  return mount<ClipboardButton>(createComponent(props));
+}
+
+function createComponent(props: Partial<ClipboardButton['props']> = {}) {
+  return <ClipboardButton copyValue="foo" {...props} />;
+}
index d25ba1092378dd13f6bf1258d91cbbdb48c17aef..5b8c90e9169513e07bdce560474418e58e200a16 100644 (file)
@@ -44,3 +44,18 @@ exports[`should display correctly 3`] = `
   </Button>
 </Tooltip>
 `;
+
+exports[`should render a custom label if provided 1`] = `
+<Tooltip
+  overlay="copied_action"
+  visible={false}
+>
+  <Button
+    className="js-copy-to-clipboard no-select"
+    data-clipboard-text="foo"
+    innerRef={[Function]}
+  >
+    Foo Bar
+  </Button>
+</Tooltip>
+`;
index 7ae22739944992ae348e3b1c027ea282015cf8b4..66c6a639e6fdbc5c20705353b6431283273d77e0 100644 (file)
@@ -17,6 +17,7 @@
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
+/* eslint-disable sonarjs/no-duplicate-string */
 import { InjectedRouter } from 'react-router';
 import { Store, createStore } from 'redux';
 import { Location } from 'history';
@@ -98,6 +99,169 @@ export function mockAppState(overrides: Partial<T.AppState> = {}): T.AppState {
   };
 }
 
+export function mockBaseSysInfo(overrides: Partial<any> = {}): T.SysInfoBase {
+  return {
+    Health: 'GREEN' as T.HealthType,
+    'Health Causes': [],
+    System: {
+      Version: '7.8'
+    },
+    Database: {
+      Database: 'PostgreSQL',
+      'Database Version': '10.3',
+      Username: 'sonar',
+      URL: 'jdbc:postgresql://localhost/sonar',
+      Driver: 'PostgreSQL JDBC Driver',
+      'Driver Version': '42.2.5'
+    },
+    'Compute Engine Tasks': {
+      'Total Pending': 0,
+      'Total In Progress': 0
+    },
+    'Search State': { State: 'GREEN', Nodes: 3 },
+    'Search Indexes': {
+      'Index components - Docs': 30445,
+      'Index components - Shards': 10
+    },
+    ...overrides
+  };
+}
+
+export function mockClusterSysInfo(overrides: Partial<any> = {}): T.SysInfoCluster {
+  const baseInfo = mockBaseSysInfo(overrides);
+  return {
+    ...baseInfo,
+    System: {
+      ...baseInfo.System,
+      'High Availability': true,
+      'Server ID': 'asd564-asd54a-5dsfg45'
+    },
+    Settings: {
+      'sonar.cluster.enabled': 'true',
+      'sonar.cluster.node.name': 'server9.example.com'
+    },
+    'Application Nodes': [
+      {
+        Name: 'server9.example.com',
+        Host: '10.0.0.0',
+        Health: 'GREEN' as T.HealthType,
+        'Health Causes': [],
+        System: {
+          Version: '7.8'
+        },
+        Plugins: {
+          java: '5.13.0.17924 [SonarJava]'
+        },
+        'Web JVM State': {
+          'Max Memory (MB)': 1024,
+          'Free Memory (MB)': 122
+        },
+        'Web Database Connection': {
+          'Pool Active Connections': 1
+        },
+        'Web Logging': { 'Logs Level': 'DEBUG' },
+        'Web JVM Properties': {
+          'file.encoding': 'UTF-8',
+          'file.separator': '/'
+        },
+        'Compute Engine Tasks': {
+          Pending: 0,
+          'In Progress': 0
+        },
+        'Compute Engine JVM State': {
+          'Max Memory (MB)': 1024,
+          'Free Memory (MB)': 78
+        },
+        'Compute Engine Database Connection': {
+          'Pool Initial Size': 0,
+          'Pool Active Connections': 0
+        },
+        'Compute Engine Logging': {
+          'Logs Level': 'INFO'
+        },
+        'Compute Engine JVM Properties': {
+          'file.encoding': 'UTF-8',
+          'file.separator': '/'
+        }
+      },
+      {
+        Name: 'server9.example.com',
+        Host: '10.0.0.0',
+        Health: 'GREEN' as T.HealthType,
+        'Health Causes': [],
+        System: {
+          Version: '7.8'
+        },
+        Plugins: {
+          java: '5.13.0.17924 [SonarJava]'
+        },
+        'Web JVM State': {
+          'Max Memory (MB)': 1024,
+          'Free Memory (MB)': 111
+        },
+        'Web Database Connection': {
+          'Pool Active Connections': 0,
+          'Pool Max Connections': 60
+        },
+        'Web Logging': { 'Logs Level': 'INFO' },
+        'Web JVM Properties': {
+          'file.encoding': 'UTF-8',
+          'file.separator': '/'
+        },
+        'Compute Engine Tasks': {
+          Pending: 0,
+          'In Progress': 0
+        },
+        'Compute Engine JVM State': {
+          'Max Memory (MB)': 1024,
+          'Free Memory (MB)': 89
+        },
+        'Compute Engine Database Connection': {
+          'Pool Initial Size': 0,
+          'Pool Active Connections': 0
+        },
+        'Compute Engine Logging': {
+          'Logs Level': 'INFO'
+        },
+        'Compute Engine JVM Properties': {
+          'file.encoding': 'UTF-8',
+          'file.separator': '/'
+        }
+      }
+    ],
+    'Search Nodes': [
+      {
+        Name: 'server.example.com',
+        Host: '10.0.0.0',
+        'Search State': {
+          'CPU Usage (%)': 0,
+          'Disk Available': '93 GB'
+        }
+      },
+      {
+        Name: 'server.example.com',
+        Host: '10.0.0.0',
+        'Search State': {
+          'CPU Usage (%)': 0,
+          'Disk Available': '93 GB'
+        }
+      },
+      {
+        Name: 'server.example.com',
+        Host: '10.0.0.0',
+        'Search State': {
+          'CPU Usage (%)': 0,
+          'Disk Available': '93 GB'
+        }
+      }
+    ],
+    Statistics: {
+      ncloc: 989880
+    },
+    ...overrides
+  };
+}
+
 export function mockComponent(overrides: Partial<T.Component> = {}): T.Component {
   return {
     breadcrumbs: [],
@@ -488,6 +652,56 @@ export function mockSourceViewerFile(
   };
 }
 
+export function mockStandaloneSysInfo(overrides: Partial<any> = {}): T.SysInfoStandalone {
+  const baseInfo = mockBaseSysInfo(overrides);
+  return {
+    ...baseInfo,
+    System: {
+      ...baseInfo.System,
+      'High Availability': false,
+      'Server ID': 'asd564-asd54a-5dsfg45'
+    },
+    Settings: {
+      'sonar.cluster.enabled': 'true',
+      'sonar.cluster.node.name': 'server9.example.com'
+    },
+    'Web JVM State': {
+      'Max Memory (MB)': 1024,
+      'Free Memory (MB)': 111
+    },
+    'Web Database Connection': {
+      'Pool Active Connections': 0,
+      'Pool Max Connections': 60
+    },
+    'Web Logging': { 'Logs Level': 'INFO', 'Logs Dir': '/logs' },
+    'Web JVM Properties': {
+      'file.encoding': 'UTF-8',
+      'file.separator': '/'
+    },
+    'Compute Engine Tasks': {
+      Pending: 0,
+      'In Progress': 0
+    },
+    'Compute Engine JVM State': {
+      'Max Memory (MB)': 1024,
+      'Free Memory (MB)': 89
+    },
+    'Compute Engine Database Connection': {
+      'Pool Initial Size': 0,
+      'Pool Active Connections': 0
+    },
+    'Compute Engine Logging': {
+      'Logs Level': 'DEBUG',
+      'Logs Dir': '/logs'
+    },
+    'Compute Engine JVM Properties': {
+      'file.encoding': 'UTF-8',
+      'file.separator': '/'
+    },
+    ...overrides
+  };
+}
+
 export function mockLongLivingBranch(
   overrides: Partial<T.LongLivingBranch> = {}
 ): T.LongLivingBranch {
index 391a892c458c2fba68384abfd8a774c15ed27a98..7c99cfb09774e3e89d0816139006c7304fd972a8 100644 (file)
@@ -2378,6 +2378,7 @@ background_tasks.failing_count=Count of projects where processing of most recent
 system.application_nodes_title=Application Nodes
 system.are_you_sure_to_restart=Are you sure you want to restart the server?
 system.cluster_log_level.info=Your selection affect all Application nodes but not the Search nodes.
+system.copy_id_info=Copy ID information
 system.current_health_of_x=Current health status of {0}
 system.download_logs=Download Logs
 system.download_system_info=Download System Info
@@ -2397,9 +2398,11 @@ system.released_x=Released {0}
 system.restart_server=Restart Server
 system.search_nodes_title=Search Nodes
 system.see_sonarqube_downloads=See All SonarQube Downloads
+system.server_id=Server ID
 system.set_log_level=Set logs level
 system.show_intermediate_versions=Show intermediate versions
 system.system_upgrade=System Upgrade
+system.version=Version
 system.version_is_availble={version} is available