]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-14128 display node name in background tasks for DCE
authorAurelien Poscia <aurelien.poscia@sonarsource.com>
Thu, 22 Dec 2022 09:01:18 +0000 (10:01 +0100)
committersonartech <sonartech@sonarsource.com>
Fri, 23 Dec 2022 20:02:51 +0000 (20:02 +0000)
server/sonar-web/src/main/js/apps/background-tasks/__tests__/BackgroundTasks-it.tsx
server/sonar-web/src/main/js/apps/background-tasks/components/Task.tsx
server/sonar-web/src/main/js/apps/background-tasks/components/TaskNodeName.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/background-tasks/components/Tasks.tsx
server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/BackgroundTasksApp-test.tsx
server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/BackgroundTasksApp-test.tsx.snap
server/sonar-web/src/main/js/types/tasks.ts
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index 266237abc2d624bd727859d45ae8d031a95c4538..d4c410b9f3a6f61ce06fc918664681d0ce1098d5 100644 (file)
@@ -22,7 +22,9 @@ import { screen, waitFor, within } from '@testing-library/react';
 import userEvent from '@testing-library/user-event';
 import { UserEvent } from '@testing-library/user-event/dist/types/setup/setup';
 import ComputeEngineServiceMock from '../../../api/mocks/ComputeEngineServiceMock';
-import { renderAppWithAdminContext } from '../../../helpers/testReactTestingUtils';
+import { mockAppState } from '../../../helpers/testMocks';
+import { renderAppWithAdminContext, RenderContext } from '../../../helpers/testReactTestingUtils';
+import { EditionKey } from '../../../types/editions';
 import { TaskStatuses, TaskTypes } from '../../../types/tasks';
 import routes from '../routes';
 
@@ -141,6 +143,36 @@ describe('The Global background task page', () => {
     expect(await screen.findAllByRole('row')).toHaveLength(2);
   });
 
+  it.each([[EditionKey.community], [EditionKey.developer], [EditionKey.enterprise]])(
+    'Editions %s should not display node name',
+    async (editionKey: EditionKey) => {
+      givenOneTaskWithoutNodeNameAndOneWithNodeName();
+
+      renderGlobalBackgroundTasksApp({ appState: mockAppState({ edition: editionKey }) });
+
+      await waitFor(() => {
+        expect(screen.getAllByRole('row')).toHaveLength(3); // including header
+      });
+
+      expect(screen.queryByText('background_tasks.table.nodeName')).not.toBeInTheDocument();
+      expect(screen.queryByText('best_node_ever')).not.toBeInTheDocument();
+    }
+  );
+
+  it('Node name should be shown in DCE edition', async () => {
+    givenOneTaskWithoutNodeNameAndOneWithNodeName();
+
+    renderGlobalBackgroundTasksApp({ appState: mockAppState({ edition: EditionKey.datacenter }) });
+
+    await waitFor(async () => {
+      expect(await screen.findByText('background_tasks.table.nodeName')).toBeInTheDocument();
+    });
+
+    expect(
+      within(await screen.getAllByRole('row')[1]).getByText('best_node_ever')
+    ).toBeInTheDocument();
+  });
+
   it('should handle task pagination', async () => {
     const user = userEvent.setup();
 
@@ -167,11 +199,30 @@ describe('The Global background task page', () => {
    */
 });
 
+function givenOneTaskWithoutNodeNameAndOneWithNodeName() {
+  computeEngineServiceMock.clearTasks();
+  computeEngineServiceMock.addTask({
+    executedAt: '2022-02-03T11:45:36+0200',
+    submittedAt: '2022-02-03T11:45:35+0200',
+    executionTimeMs: 167,
+    status: TaskStatuses.InProgress,
+    type: TaskTypes.IssueSync,
+    nodeName: 'best_node_ever',
+  });
+  computeEngineServiceMock.addTask({
+    executedAt: '2022-02-03T11:45:35+0200',
+    submittedAt: '2022-02-03T11:45:34+0200',
+    executionTimeMs: 167,
+    status: TaskStatuses.InProgress,
+    type: TaskTypes.IssueSync,
+  });
+}
+
 async function changeTaskFilter(user: UserEvent, fieldLabel: string, value: string) {
   await user.click(screen.getByLabelText(fieldLabel, { selector: 'input' }));
   await user.click(screen.getByText(value));
 }
 
-function renderGlobalBackgroundTasksApp() {
-  renderAppWithAdminContext('admin/background_tasks', routes, {});
+function renderGlobalBackgroundTasksApp(context: RenderContext = {}) {
+  renderAppWithAdminContext('admin/background_tasks', routes, context);
 }
index cf18ecb12d584aa452cc39a62c3960107e210d8a..a1c1baead8d50c8f9c41b50869740067e8d8890f 100644 (file)
@@ -18,6 +18,8 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
+import { AppState } from '../../../types/appstate';
+import { EditionKey } from '../../../types/editions';
 import { Task as ITask } from '../../../types/tasks';
 import TaskActions from './TaskActions';
 import TaskComponent from './TaskComponent';
@@ -25,6 +27,7 @@ import TaskDate from './TaskDate';
 import TaskDay from './TaskDay';
 import TaskExecutionTime from './TaskExecutionTime';
 import TaskId from './TaskId';
+import TaskNodeName from './TaskNodeName';
 import TaskStatus from './TaskStatus';
 import TaskSubmitter from './TaskSubmitter';
 
@@ -34,10 +37,11 @@ interface Props {
   onFilterTask: (task: ITask) => void;
   task: ITask;
   previousTask?: ITask;
+  appState: AppState;
 }
 
 export default function Task(props: Props) {
-  const { task, component, onCancelTask, onFilterTask, previousTask } = props;
+  const { task, component, onCancelTask, onFilterTask, previousTask, appState } = props;
 
   return (
     <tr>
@@ -45,6 +49,7 @@ export default function Task(props: Props) {
       <TaskComponent task={task} />
       <TaskId id={task.id} />
       <TaskSubmitter submitter={task.submitterLogin} />
+      {appState?.edition === EditionKey.datacenter && <TaskNodeName nodeName={task.nodeName} />}
       <TaskDay
         prevSubmittedAt={previousTask && previousTask.submittedAt}
         submittedAt={task.submittedAt}
diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/TaskNodeName.tsx b/server/sonar-web/src/main/js/apps/background-tasks/components/TaskNodeName.tsx
new file mode 100644 (file)
index 0000000..93224ac
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import * as React from 'react';
+
+interface Props {
+  nodeName?: string;
+}
+
+export default function TaskNodeName({ nodeName }: Props) {
+  return (
+    <td className="thin">
+      <div className="note">{nodeName}</div>
+    </td>
+  );
+}
index 987bcff59ddd86952cb5a9d78f0ebfbd1411dd18..1947785f0c86ee072d2723ddb820d552118d9426 100644 (file)
  */
 import classNames from 'classnames';
 import * as React from 'react';
+import withAppStateContext from '../../../app/components/app-state/withAppStateContext';
 import { translate } from '../../../helpers/l10n';
+import { AppState } from '../../../types/appstate';
+import { EditionKey } from '../../../types/editions';
 import { Task as ITask } from '../../../types/tasks';
 import Task from './Task';
 
@@ -29,9 +32,10 @@ interface Props {
   loading: boolean;
   onCancelTask: (task: ITask) => Promise<void>;
   onFilterTask: (task: ITask) => void;
+  appState: AppState;
 }
 
-export default function Tasks({ tasks, component, loading, onCancelTask, onFilterTask }: Props) {
+export function Tasks({ tasks, component, loading, onCancelTask, onFilterTask, appState }: Props) {
   const className = classNames('data zebra zebra-hover background-tasks', {
     'new-loading': loading,
   });
@@ -45,6 +49,9 @@ export default function Tasks({ tasks, component, loading, onCancelTask, onFilte
             <th>{translate('background_tasks.table.task')}</th>
             <th>{translate('background_tasks.table.id')}</th>
             <th>{translate('background_tasks.table.submitter')}</th>
+            {appState?.edition === EditionKey.datacenter && (
+              <th>{translate('background_tasks.table.nodeName')}</th>
+            )}
             <th>&nbsp;</th>
             <th className="text-right">{translate('background_tasks.table.submitted')}</th>
             <th className="text-right">{translate('background_tasks.table.started')}</th>
@@ -62,6 +69,7 @@ export default function Tasks({ tasks, component, loading, onCancelTask, onFilte
               onFilterTask={onFilterTask}
               previousTask={index > 0 ? tasks[index - 1] : undefined}
               task={task}
+              appState={appState}
             />
           ))}
         </tbody>
@@ -69,3 +77,5 @@ export default function Tasks({ tasks, component, loading, onCancelTask, onFilte
     </div>
   );
 }
+
+export default withAppStateContext(Tasks);
index bce1ed12fe00f3f1af1a412b75d5ede9d090f294..04fd4c55d47b4f25651db7cc68bd1bd08b55a64e 100644 (file)
@@ -33,6 +33,7 @@ jest.mock('../../../../api/ce', () => ({
       {
         id: 'AWkGcOThOiAPiP5AE-kM',
         type: 'VIEW_REFRESH',
+        nodeName: 'node_CE_server_1',
         componentId: 'AWBLZYhGOUrjxRA-u6ex',
         componentKey: 'sonar-csharp',
         componentName: 'SonarC#',
index 2789a48d4415b2970af345afaed04638a3b8707e..afb200f89f2bc876d04ce53105eee675928f9891 100644 (file)
@@ -116,7 +116,7 @@ exports[`should render correctly: loaded 1`] = `
       }
     }
   />
-  <Tasks
+  <withAppStateContext(Tasks)
     component={
       {
         "breadcrumbs": [],
@@ -156,6 +156,7 @@ exports[`should render correctly: loaded 1`] = `
           "hasScannerContext": false,
           "id": "AWkGcOThOiAPiP5AE-kM",
           "logs": false,
+          "nodeName": "node_CE_server_1",
           "startedAt": "2019-02-19T16:47:36+0100",
           "status": "FAILED",
           "submittedAt": "2019-02-19T16:47:35+0100",
index be6a834c83971ff4f47f4f49e2094c11fc4a0b52..c71eba6ac8ef25cfc42761c573a09dcdc6c5bc66 100644 (file)
@@ -39,6 +39,7 @@ export enum TaskStatuses {
 export interface Task {
   analysisId?: string;
   branch?: string;
+  nodeName?: string;
   componentKey?: string;
   componentName?: string;
   componentQualifier?: string;
index 7625bd7b15e08115bb1e7b7c55e3d32fa4b7aecb..257dd0798c0ce88a5e358307d31979c0dc3c897d 100644 (file)
@@ -3109,6 +3109,7 @@ background_tasks.table.submitter=Submitter
 background_tasks.table.started=Started
 background_tasks.table.finished=Finished
 background_tasks.table.duration=Duration
+background_tasks.table.nodeName=Node
 
 background_tasks.filter_by_component_x=Filter by Component "{0}"
 background_tasks.cancel_task=Cancel Task