]> source.dussan.org Git - sonarqube.git/commitdiff
[NO JIRA] Rename ISO date helpers
authorWouter Admiraal <wouter.admiraal@sonarsource.com>
Thu, 4 May 2023 08:50:19 +0000 (10:50 +0200)
committersonartech <sonartech@sonarsource.com>
Fri, 5 May 2023 20:03:00 +0000 (20:03 +0000)
The "NotSoISO" part of the old names was incorrect; the return value is
definitely ISO 8601.

23 files changed:
server/sonar-web/src/main/js/api/mocks/ComputeEngineServiceMock.ts
server/sonar-web/src/main/js/app/components/StartupModal.tsx
server/sonar-web/src/main/js/app/components/__tests__/StartupModal-test.tsx
server/sonar-web/src/main/js/apps/background-tasks/__tests__/BackgroundTasks-it.tsx
server/sonar-web/src/main/js/apps/background-tasks/components/BackgroundTasksApp.tsx
server/sonar-web/src/main/js/apps/background-tasks/utils.ts
server/sonar-web/src/main/js/apps/overview/branches/BranchOverview.tsx
server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysesList.tsx
server/sonar-web/src/main/js/apps/projectBaseline/components/BranchAnalysisList.tsx
server/sonar-web/src/main/js/apps/projectBaseline/components/BranchAnalysisListRenderer.tsx
server/sonar-web/src/main/js/apps/projectBaseline/components/BranchBaselineSettingModal.tsx
server/sonar-web/src/main/js/apps/projectsManagement/BulkApplyTemplateModal.tsx
server/sonar-web/src/main/js/apps/projectsManagement/DeleteModal.tsx
server/sonar-web/src/main/js/apps/projectsManagement/ProjectManagementApp.tsx
server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangelogContainer.tsx
server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionRules.tsx
server/sonar-web/src/main/js/apps/system/components/PageHeader.tsx
server/sonar-web/src/main/js/apps/users/UsersApp.tsx
server/sonar-web/src/main/js/helpers/__tests__/dates-test.ts
server/sonar-web/src/main/js/helpers/dates.ts
server/sonar-web/src/main/js/helpers/l10nBundle.ts
server/sonar-web/src/main/js/helpers/query.ts
server/sonar-web/src/main/js/helpers/tokens.ts

index d892eda5901f9afcb8c5a9053808ae7a93f9eb1f..93d2e3493722f17db341173f7559d4c0633ee4ff 100644 (file)
@@ -22,6 +22,7 @@ import { cloneDeep, groupBy, sortBy } from 'lodash';
 import { PAGE_SIZE } from '../../apps/background-tasks/constants';
 import { parseDate } from '../../helpers/dates';
 import { mockTask } from '../../helpers/mocks/tasks';
+import { isDefined } from '../../helpers/types';
 import { ActivityRequestParameters, Task, TaskStatuses, TaskTypes } from '../../types/tasks';
 import {
   cancelAllTasks,
@@ -63,7 +64,7 @@ export default class ComputeEngineServiceMock {
   constructor() {
     (cancelAllTasks as jest.Mock).mockImplementation(this.handleCancelAllTasks);
     (cancelTask as jest.Mock).mockImplementation(this.handleCancelTask);
-    (getActivity as jest.Mock).mockImplementation(this.handleGetActivity);
+    jest.mocked(getActivity).mockImplementation(this.handleGetActivity);
     (getStatus as jest.Mock).mockImplementation(this.handleGetStatus);
     (getTypes as jest.Mock).mockImplementation(this.handleGetTypes);
     (getTask as jest.Mock).mockImplementation(this.handleGetTask);
@@ -96,7 +97,6 @@ export default class ComputeEngineServiceMock {
 
   handleGetActivity = (data: ActivityRequestParameters) => {
     let results = cloneDeep(this.tasks);
-
     results = results.filter((task) => {
       return !(
         (data.component && task.componentKey !== data.component) ||
@@ -115,12 +115,10 @@ export default class ComputeEngineServiceMock {
     });
 
     if (data.onlyCurrents) {
-      /*
-       *  This is more complex in real life, but it's a good enough approximation to suit tests
-       */
-      results = Object.values(groupBy(results, (t) => t.componentKey)).map(
-        (tasks) => sortBy(tasks, (t) => t.executedAt).pop()!
-      );
+      // This is more complex in real life, but it's a good enough approximation to suit tests.
+      results = Object.values(groupBy(results, (t) => t.componentKey))
+        .map((tasks) => sortBy(tasks, (t) => t.executedAt).pop())
+        .filter(isDefined);
     }
 
     const page = data.p ?? 1;
index 127b7f6cead5ce867f6e80d97705cb55905a8847..50b97a2df1367a60226a22139ccdd29126229adc 100644 (file)
@@ -22,7 +22,7 @@ import * as React from 'react';
 import { showLicense } from '../../api/editions';
 import LicensePromptModal from '../../apps/marketplace/components/LicensePromptModal';
 import { Location, Router, withRouter } from '../../components/hoc/withRouter';
-import { parseDate, toShortNotSoISOString } from '../../helpers/dates';
+import { parseDate, toShortISO8601String } from '../../helpers/dates';
 import { hasMessage } from '../../helpers/l10n';
 import { get, save } from '../../helpers/storage';
 import { AppState } from '../../types/appstate';
@@ -71,7 +71,7 @@ export class StartupModal extends React.PureComponent<Props & StateProps, State>
         showLicense()
           .then((license) => {
             if (!license || !license.isValidEdition) {
-              save(LICENSE_PROMPT, toShortNotSoISOString(new Date()), currentUser.login);
+              save(LICENSE_PROMPT, toShortISO8601String(new Date()), currentUser.login);
               this.setState({ open: true });
             }
           })
index 5c36e7721448d2cefe9c9fcbc5b92244860e16f7..5908e9d4460269d002e2e16da8b6fa106d637364 100644 (file)
@@ -21,8 +21,9 @@ import { differenceInDays } from 'date-fns';
 import { shallow, ShallowWrapper } from 'enzyme';
 import * as React from 'react';
 import { showLicense } from '../../../api/editions';
-import { toShortNotSoISOString } from '../../../helpers/dates';
+import { toShortISO8601String } from '../../../helpers/dates';
 import { hasMessage } from '../../../helpers/l10n';
+import { mockLicense } from '../../../helpers/mocks/editions';
 import { get, save } from '../../../helpers/storage';
 import { mockAppState, mockLocation, mockRouter } from '../../../helpers/testMocks';
 import { waitAndUpdate } from '../../../helpers/testUtils';
@@ -45,7 +46,7 @@ jest.mock('../../../helpers/l10n', () => ({
 
 jest.mock('../../../helpers/dates', () => ({
   parseDate: jest.fn().mockReturnValue('parsed-date'),
-  toShortNotSoISOString: jest.fn().mockReturnValue('short-not-iso-date'),
+  toShortISO8601String: jest.fn().mockReturnValue('short-not-iso-date'),
 }));
 
 jest.mock('date-fns', () => ({ differenceInDays: jest.fn().mockReturnValue(1) }));
@@ -60,12 +61,12 @@ const LOGGED_IN_USER: LoggedInUser = {
 };
 
 beforeEach(() => {
-  (differenceInDays as jest.Mock<any>).mockClear();
-  (hasMessage as jest.Mock<any>).mockClear();
-  (get as jest.Mock<any>).mockClear();
-  (save as jest.Mock<any>).mockClear();
-  (showLicense as jest.Mock<any>).mockClear();
-  (toShortNotSoISOString as jest.Mock<any>).mockClear();
+  jest.mocked(differenceInDays).mockClear();
+  jest.mocked(hasMessage).mockClear();
+  jest.mocked(get).mockClear();
+  jest.mocked(save).mockClear();
+  jest.mocked(showLicense).mockClear();
+  jest.mocked(toShortISO8601String).mockClear();
 });
 
 it('should render only the children', async () => {
@@ -76,14 +77,14 @@ it('should render only the children', async () => {
 
   await shouldNotHaveModals(getWrapper({ appState: mockAppState({ canAdmin: false }) }));
 
-  (hasMessage as jest.Mock<any>).mockReturnValueOnce(false);
+  jest.mocked(hasMessage).mockReturnValueOnce(false);
   await shouldNotHaveModals(getWrapper());
 
-  (showLicense as jest.Mock<any>).mockResolvedValueOnce({ isValidEdition: true });
+  jest.mocked(showLicense).mockResolvedValueOnce(mockLicense({ isValidEdition: true }));
   await shouldNotHaveModals(getWrapper());
 
-  (get as jest.Mock<any>).mockReturnValueOnce('date');
-  (differenceInDays as jest.Mock<any>).mockReturnValueOnce(0);
+  jest.mocked(get).mockReturnValueOnce('date');
+  jest.mocked(differenceInDays).mockReturnValueOnce(0);
   await shouldNotHaveModals(getWrapper());
 
   await shouldNotHaveModals(
@@ -99,11 +100,11 @@ it('should render license prompt', async () => {
   await shouldDisplayLicense(getWrapper());
   expect(save).toHaveBeenCalledWith('sonarqube.license.prompt', 'short-not-iso-date', 'luke');
 
-  (get as jest.Mock<any>).mockReturnValueOnce('date');
-  (differenceInDays as jest.Mock<any>).mockReturnValueOnce(1);
+  jest.mocked(get).mockReturnValueOnce('date');
+  jest.mocked(differenceInDays).mockReturnValueOnce(1);
   await shouldDisplayLicense(getWrapper());
 
-  (showLicense as jest.Mock<any>).mockResolvedValueOnce({ isValidEdition: false });
+  jest.mocked(showLicense).mockResolvedValueOnce(mockLicense({ isValidEdition: false }));
   await shouldDisplayLicense(getWrapper());
 });
 
index 2db1eb701ce9e0f1dcf9e508f19f8dba716fb02f..e49c2ef8e129e7665bd7eaeff5b81cb019d7ea95 100644 (file)
@@ -21,8 +21,13 @@ import { screen, waitFor, within } from '@testing-library/react';
 import userEvent from '@testing-library/user-event';
 import { byLabelText, byPlaceholderText, byRole, byText } from 'testing-library-selector';
 import ComputeEngineServiceMock from '../../../api/mocks/ComputeEngineServiceMock';
+import { parseDate } from '../../../helpers/dates';
 import { mockAppState } from '../../../helpers/testMocks';
-import { RenderContext, renderAppWithAdminContext } from '../../../helpers/testReactTestingUtils';
+import {
+  RenderContext,
+  dateInputEvent,
+  renderAppWithAdminContext,
+} from '../../../helpers/testReactTestingUtils';
 import { EditionKey } from '../../../types/editions';
 import { TaskStatuses, TaskTypes } from '../../../types/tasks';
 import routes from '../routes';
@@ -87,6 +92,8 @@ describe('The Global background task page', () => {
     });
     computeEngineServiceMock.addTask({ status: TaskStatuses.Pending, type: TaskTypes.IssueSync });
     computeEngineServiceMock.addTask({
+      executedAt: '2022-02-04T11:45:36+0200',
+      submittedAt: '2022-02-04T11:45:35+0200',
       componentKey: 'otherComponent',
       status: TaskStatuses.Success,
       type: TaskTypes.AppRefresh,
@@ -99,25 +106,36 @@ describe('The Global background task page', () => {
 
     expect(ui.getAllRows()).toHaveLength(4);
 
+    // Status filter.
     await ui.changeTaskFilter('status', 'background_task.status.IN_PROGRESS');
     expect(ui.getAllRows()).toHaveLength(1);
 
     await ui.changeTaskFilter('status', 'background_task.status.ALL');
     expect(ui.getAllRows()).toHaveLength(5);
 
+    // Type filter.
     await ui.changeTaskFilter('type', `background_task.type.${TaskTypes.AppRefresh}`);
     expect(ui.getAllRows()).toHaveLength(3);
 
+    // Latest analysis.
     await user.click(ui.onlyLatestAnalysis.get());
     expect(ui.getAllRows()).toHaveLength(2);
-    await user.click(ui.onlyLatestAnalysis.get());
 
-    /*
-     * Must test date range filters, but it requires refactoring the DateRange component
-     */
+    // Reset filters.
+    await user.click(ui.resetFilters.get());
+    expect(ui.getAllRows()).toHaveLength(4);
+
+    // Date filters.
+    await ui.setDateRange('2022-02-01', '2022-02-04');
+    expect(ui.getAllRows()).toHaveLength(1);
+
+    // Reset filters.
+    await user.click(ui.resetFilters.get());
+    expect(ui.getAllRows()).toHaveLength(4);
+
+    // Search.
     await user.click(ui.search.get());
     await user.keyboard('other');
-
     expect(ui.getAllRows()).toHaveLength(1);
 
     // Reset filters
@@ -298,6 +316,8 @@ function getPageObject() {
     numberOfWorkers: byText('background_tasks.number_of_workers'),
     onlyLatestAnalysis: byRole('checkbox', { name: 'yes' }),
     search: byPlaceholderText('background_tasks.search_by_task_or_component'),
+    fromDateInput: byRole('textbox', { name: 'start_date' }),
+    toDateInput: byRole('textbox', { name: 'end_date' }),
     resetFilters: byRole('button', { name: 'reset_verb' }),
     showMoreButton: byRole('button', { name: 'show_more' }),
     reloadButton: byRole('button', { name: 'reload' }),
@@ -309,20 +329,33 @@ function getPageObject() {
   const ui = {
     ...selectors,
 
-    appLoaded: async () => {
+    async appLoaded() {
       await waitFor(() => {
         expect(selectors.loading.query()).not.toBeInTheDocument();
       });
     },
 
-    getAllRows: () => screen.getAllByRole('row').slice(1), // Excludes header
+    getAllRows() {
+      return screen.getAllByRole('row').slice(1); // Excludes header
+    },
 
-    changeTaskFilter: async (fieldLabel: string, value: string) => {
+    async changeTaskFilter(fieldLabel: string, value: string) {
       await user.click(screen.getByLabelText(fieldLabel, { selector: 'input' }));
       await user.click(screen.getByText(value));
     },
 
-    clickOnTaskAction: async (rowIndex: number, label: string) => {
+    async setDateRange(from?: string, to?: string) {
+      const dateInput = dateInputEvent(user);
+      if (from) {
+        await dateInput.pickDate(ui.fromDateInput.get(), parseDate(from));
+      }
+
+      if (to) {
+        await dateInput.pickDate(ui.toDateInput.get(), parseDate(to));
+      }
+    },
+
+    async clickOnTaskAction(rowIndex: number, label: string) {
       const row = ui.getAllRows()[rowIndex];
       expect(row).toBeVisible();
       await user.click(within(row).getByRole('button', { name: 'background_tasks.show_actions' }));
index 7d8d47d5f7ccc26e1e59f2e0e1f0f46efd5d46d2..bc27414d2e9d1632a0ccaf4a0036cac7df4db0be 100644 (file)
@@ -32,7 +32,7 @@ import ListFooter from '../../../components/controls/ListFooter';
 import Suggestions from '../../../components/embed-docs-modal/Suggestions';
 import { Location, Router, withRouter } from '../../../components/hoc/withRouter';
 import DeferredSpinner from '../../../components/ui/DeferredSpinner';
-import { toShortNotSoISOString } from '../../../helpers/dates';
+import { toShortISO8601String } from '../../../helpers/dates';
 import { translate } from '../../../helpers/l10n';
 import { parseAsDate } from '../../../helpers/query';
 import { Task, TaskStatuses } from '../../../types/tasks';
@@ -162,11 +162,11 @@ export class BackgroundTasksApp extends React.PureComponent<Props, State> {
     });
 
     if (nextQuery.minSubmittedAt) {
-      nextQuery.minSubmittedAt = toShortNotSoISOString(nextQuery.minSubmittedAt);
+      nextQuery.minSubmittedAt = toShortISO8601String(nextQuery.minSubmittedAt);
     }
 
     if (nextQuery.maxExecutedAt) {
-      nextQuery.maxExecutedAt = toShortNotSoISOString(nextQuery.maxExecutedAt);
+      nextQuery.maxExecutedAt = toShortISO8601String(nextQuery.maxExecutedAt);
     }
 
     this.props.router.push({
index 314b8cbf8ef0032eb920697c5669667f4b983f80..9007eafbb57963422554819f7ec7c750ff8d2cf5 100644 (file)
@@ -17,7 +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.
  */
-import { toShortNotSoISOString } from '../../helpers/dates';
+import { toShortISO8601String } from '../../helpers/dates';
 import { ActivityRequestParameters, Task, TaskStatuses } from '../../types/tasks';
 import { ALL_TYPES, CURRENTS, STATUSES } from './constants';
 
@@ -65,11 +65,11 @@ export function mapFiltersToParameters(filters: Partial<Query> = {}) {
   }
 
   if (filters.minSubmittedAt) {
-    parameters.minSubmittedAt = toShortNotSoISOString(filters.minSubmittedAt);
+    parameters.minSubmittedAt = toShortISO8601String(filters.minSubmittedAt);
   }
 
   if (filters.maxExecutedAt) {
-    parameters.maxExecutedAt = toShortNotSoISOString(filters.maxExecutedAt);
+    parameters.maxExecutedAt = toShortISO8601String(filters.maxExecutedAt);
   }
 
   if (filters.query) {
index db2be496150038eee2c65529eca390e0ad181c9d..020ae38d128e7b538e3f34fae129b7bb4ce622bc 100644 (file)
@@ -34,7 +34,7 @@ import {
   getBranchLikeQuery,
   isMainBranch,
 } from '../../../helpers/branch-like';
-import { parseDate, toNotSoISOString } from '../../../helpers/dates';
+import { parseDate, toISO8601WithOffsetString } from '../../../helpers/dates';
 import { enhanceConditionWithMeasure, enhanceMeasuresWithMetrics } from '../../../helpers/measures';
 import {
   extractStatusConditionsFromApplicationStatusChildProject,
@@ -78,7 +78,7 @@ export const BRANCH_OVERVIEW_ACTIVITY_GRAPH = 'sonar_branch_overview.graph';
 export const NO_CI_DETECTED = 'undetected';
 
 // Get all history data over the past year.
-const FROM_DATE = toNotSoISOString(new Date().setFullYear(new Date().getFullYear() - 1));
+const FROM_DATE = toISO8601WithOffsetString(new Date().setFullYear(new Date().getFullYear() - 1));
 
 export default class BranchOverview extends React.PureComponent<Props, State> {
   mounted = false;
index 130b24c6cfb69d3affbcdfd935156600fd8f9313..3e67f481a9a86ec039b327b3714f56bd74f22f40 100644 (file)
@@ -22,7 +22,7 @@ import { isEqual } from 'date-fns';
 import * as React from 'react';
 import Tooltip from '../../../components/controls/Tooltip';
 import DateFormatter from '../../../components/intl/DateFormatter';
-import { toShortNotSoISOString } from '../../../helpers/dates';
+import { toShortISO8601String } from '../../../helpers/dates';
 import { translate } from '../../../helpers/l10n';
 import { ComponentQualifier } from '../../../types/component';
 import { ParsedAnalysis } from '../../../types/project-activity';
@@ -143,7 +143,7 @@ export default class ProjectActivityAnalysesList extends React.PureComponent<Pro
                 {days.map((day) => (
                   <li
                     className="project-activity-day"
-                    data-day={toShortNotSoISOString(Number(day))}
+                    data-day={toShortISO8601String(Number(day))}
                     key={day}
                   >
                     <h3>
index 5ed0f7692b7b04d9befde9ba0369e787bfa03884..11012335e5695b19cd0d145eeb07fe7a5e15e27f 100644 (file)
@@ -21,7 +21,7 @@ import { subDays } from 'date-fns';
 import { throttle } from 'lodash';
 import * as React from 'react';
 import { getProjectActivity } from '../../../api/projectActivity';
-import { parseDate, toShortNotSoISOString } from '../../../helpers/dates';
+import { parseDate, toShortISO8601String } from '../../../helpers/dates';
 import { Analysis, ParsedAnalysis } from '../../../types/project-activity';
 import { Dict } from '../../../types/types';
 import BranchAnalysisListRenderer from './BranchAnalysisListRenderer';
@@ -81,7 +81,7 @@ export default class BranchAnalysisList extends React.PureComponent<Props, State
     return getProjectActivity({
       branch,
       project: component,
-      from: range ? toShortNotSoISOString(subDays(new Date(), range)) : undefined,
+      from: range ? toShortISO8601String(subDays(new Date(), range)) : undefined,
     }).then((result: { analyses: Analysis[] }) => {
       // If the selected analysis wasn't found in the default 30 days range, redo the search
       if (initial && analysis && !result.analyses.find((a) => a.key === analysis)) {
index 8bb4bf17c76b52a647e16de39bc7c76fff0bbdae..94aa4bcf90222f8e2d2894b0cf4112a1d0df2072 100644 (file)
@@ -26,7 +26,7 @@ import Tooltip from '../../../components/controls/Tooltip';
 import DateFormatter, { longFormatterOption } from '../../../components/intl/DateFormatter';
 import TimeFormatter from '../../../components/intl/TimeFormatter';
 import DeferredSpinner from '../../../components/ui/DeferredSpinner';
-import { parseDate, toShortNotSoISOString } from '../../../helpers/dates';
+import { parseDate, toShortISO8601String } from '../../../helpers/dates';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
 import { ParsedAnalysis } from '../../../types/project-activity';
 import Events from '../../projectActivity/components/Events';
@@ -173,7 +173,7 @@ function BranchAnalysisListRenderer(
                       {days.map((day) => (
                         <li
                           className="branch-analysis-day"
-                          data-day={toShortNotSoISOString(Number(day))}
+                          data-day={toShortISO8601String(Number(day))}
                           key={day}
                         >
                           <div className="branch-analysis-date">
index 6b94e209a34de287f8f6ad8c1f6f9305fb9791cb..1c1d8822ce61e83c9b589a14d631d98646d490f7 100644 (file)
@@ -22,7 +22,7 @@ import { setNewCodePeriod } from '../../../api/newCodePeriod';
 import Modal from '../../../components/controls/Modal';
 import { ResetButtonLink, SubmitButton } from '../../../components/controls/buttons';
 import DeferredSpinner from '../../../components/ui/DeferredSpinner';
-import { toNotSoISOString } from '../../../helpers/dates';
+import { toISO8601WithOffsetString } from '../../../helpers/dates';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
 import { Branch, BranchWithNewCodePeriod } from '../../../types/branch-like';
 import { ParsedAnalysis } from '../../../types/project-activity';
@@ -113,7 +113,7 @@ export default class BranchBaselineSettingModal extends React.PureComponent<Prop
           this.props.onClose(branch.name, {
             type,
             value,
-            effectiveValue: analysisDate && toNotSoISOString(analysisDate),
+            effectiveValue: analysisDate && toISO8601WithOffsetString(analysisDate),
           });
         },
         () => {
index 337f9dedef3cb9ab0b901ab62dd37c16704ec092..c8b0a57ac34b5d3a3e4bfea4d01df7b04095e597 100644 (file)
  */
 import * as React from 'react';
 import { bulkApplyTemplate, getPermissionTemplates } from '../../api/permissions';
-import { ResetButtonLink, SubmitButton } from '../../components/controls/buttons';
 import Modal from '../../components/controls/Modal';
 import Select from '../../components/controls/Select';
+import { ResetButtonLink, SubmitButton } from '../../components/controls/buttons';
 import { Alert } from '../../components/ui/Alert';
 import MandatoryFieldMarker from '../../components/ui/MandatoryFieldMarker';
 import MandatoryFieldsExplanation from '../../components/ui/MandatoryFieldsExplanation';
-import { toNotSoISOString } from '../../helpers/dates';
+import { toISO8601WithOffsetString } from '../../helpers/dates';
 import { addGlobalErrorMessageFromAPI } from '../../helpers/globalMessages';
 import { translate, translateWithParameters } from '../../helpers/l10n';
 import { PermissionTemplate } from '../../types/types';
@@ -94,7 +94,7 @@ export default class BulkApplyTemplateModal extends React.PureComponent<Props, S
             templateId: permissionTemplate,
           }
         : {
-            analyzedBefore: analyzedBefore && toNotSoISOString(analyzedBefore),
+            analyzedBefore: analyzedBefore && toISO8601WithOffsetString(analyzedBefore),
             onProvisionedOnly: this.props.provisioned || undefined,
             qualifiers: this.props.qualifier,
             q: this.props.query || undefined,
index 08074d91bce920d4c16b5c55d4fd285a082cbb9e..343747b4906b19dc9c04f257802b90fe79e2fe7a 100644 (file)
  */
 import * as React from 'react';
 import { bulkDeleteProjects } from '../../api/components';
-import { ResetButtonLink, SubmitButton } from '../../components/controls/buttons';
 import Modal from '../../components/controls/Modal';
+import { ResetButtonLink, SubmitButton } from '../../components/controls/buttons';
 import { Alert } from '../../components/ui/Alert';
-import { toNotSoISOString } from '../../helpers/dates';
+import { toISO8601WithOffsetString } from '../../helpers/dates';
 import { translate, translateWithParameters } from '../../helpers/l10n';
 
 export interface Props {
@@ -60,7 +60,7 @@ export default class DeleteModal extends React.PureComponent<Props, State> {
           projects: this.props.selection.join(),
         }
       : {
-          analyzedBefore: analyzedBefore && toNotSoISOString(analyzedBefore),
+          analyzedBefore: analyzedBefore && toISO8601WithOffsetString(analyzedBefore),
           onProvisionedOnly: this.props.provisioned || undefined,
           qualifiers: this.props.qualifier,
           q: this.props.query || undefined,
index 2f4a02b75b56ec2f1e314775b431a554c95ec882..730d75391341416fff8e513e60f6118862d1705b 100644 (file)
 import { debounce, uniq, without } from 'lodash';
 import * as React from 'react';
 import { Helmet } from 'react-helmet-async';
-import { getComponents, Project } from '../../api/components';
+import { Project, getComponents } from '../../api/components';
 import { changeProjectDefaultVisibility } from '../../api/permissions';
 import { getValue } from '../../api/settings';
 import withCurrentUserContext from '../../app/components/current-user/withCurrentUserContext';
 import ListFooter from '../../components/controls/ListFooter';
 import Suggestions from '../../components/embed-docs-modal/Suggestions';
-import { toShortNotSoISOString } from '../../helpers/dates';
+import { toShortISO8601String } from '../../helpers/dates';
 import { throwGlobalError } from '../../helpers/error';
 import { translate } from '../../helpers/l10n';
 import { hasGlobalPermission } from '../../helpers/users';
@@ -109,7 +109,7 @@ export class ProjectManagementApp extends React.PureComponent<Props, State> {
   requestProjects = () => {
     const { analyzedBefore } = this.state;
     const parameters = {
-      analyzedBefore: analyzedBefore && toShortNotSoISOString(analyzedBefore),
+      analyzedBefore: analyzedBefore && toShortISO8601String(analyzedBefore),
       onProvisionedOnly: this.state.provisioned || undefined,
       p: this.state.page !== 1 ? this.state.page : undefined,
       ps: PAGE_SIZE,
index 63863e966c97bb5d974661874682184dc3de44c2..2d99c5cb61a5cfe1cc1bb6628105a8dce369e443 100644 (file)
@@ -21,7 +21,7 @@ import * as React from 'react';
 import { getProfileChangelog } from '../../../api/quality-profiles';
 import { Location, Router, withRouter } from '../../../components/hoc/withRouter';
 import DeferredSpinner from '../../../components/ui/DeferredSpinner';
-import { parseDate, toShortNotSoISOString } from '../../../helpers/dates';
+import { parseDate, toShortISO8601String } from '../../../helpers/dates';
 import { translate } from '../../../helpers/l10n';
 import { withQualityProfilesContext } from '../qualityProfilesContext';
 import { Profile, ProfileChangelogEvent } from '../types';
@@ -117,8 +117,8 @@ export class ChangelogContainer extends React.PureComponent<Props, State> {
 
   handleDateRangeChange = ({ from, to }: { from?: Date; to?: Date }) => {
     const path = getProfileChangelogPath(this.props.profile.name, this.props.profile.language, {
-      since: from && toShortNotSoISOString(from),
-      to: to && toShortNotSoISOString(to),
+      since: from && toShortISO8601String(from),
+      to: to && toShortISO8601String(to),
     });
     this.props.router.push(path);
   };
index 369672147cc4d410229f6fc0282ad45436ee45d7..a087612cbe6a09e741ef3d8afb78c614f76beb06 100644 (file)
@@ -21,7 +21,7 @@ import { sortBy } from 'lodash';
 import * as React from 'react';
 import { searchRules } from '../../../api/rules';
 import Link from '../../../components/common/Link';
-import { toShortNotSoISOString } from '../../../helpers/dates';
+import { toShortISO8601String } from '../../../helpers/dates';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
 import { formatMeasure } from '../../../helpers/measures';
 import { getRulesUrl } from '../../../helpers/urls';
@@ -54,7 +54,7 @@ export default class EvolutionRules extends React.PureComponent<{}, State> {
     this.state = {};
     const startDate = new Date();
     startDate.setFullYear(startDate.getFullYear() - 1);
-    this.periodStartDate = toShortNotSoISOString(startDate);
+    this.periodStartDate = toShortISO8601String(startDate);
   }
 
   componentDidMount() {
index 19d64ecdc80bf23a0cf0508628f85f8d13f503fc..3b09b5839515f752a5cdfc3ab49fd0751356914b 100644 (file)
@@ -21,7 +21,7 @@ import * as React from 'react';
 import withAppStateContext from '../../../app/components/app-state/withAppStateContext';
 import { ClipboardButton } from '../../../components/controls/clipboard';
 import { Alert } from '../../../components/ui/Alert';
-import { toShortNotSoISOString } from '../../../helpers/dates';
+import { toShortISO8601String } from '../../../helpers/dates';
 import { translate } from '../../../helpers/l10n';
 import { AppState } from '../../../types/appstate';
 import PageActions from './PageActions';
@@ -86,7 +86,7 @@ function PageHeader(props: Props) {
               copyValue={`SonarQube ID information
 Server ID: ${serverId}
 Version: ${version}
-Date: ${toShortNotSoISOString(Date.now())}
+Date: ${toShortISO8601String(Date.now())}
 `}
             >
               {translate('system.copy_id_info')}
index 053cfe7003ff2e48d71fc04bd254750f2b73a7a8..c17382ae7fd04ad565aa09544e7bbe413d8e46ea 100644 (file)
@@ -29,7 +29,7 @@ import Select, { LabelValueSelectOption } from '../../components/controls/Select
 import Suggestions from '../../components/embed-docs-modal/Suggestions';
 import { useManageProvider } from '../../components/hooks/useManageProvider';
 import DeferredSpinner from '../../components/ui/DeferredSpinner';
-import { now, toNotSoISOString } from '../../helpers/dates';
+import { now, toISO8601WithOffsetString } from '../../helpers/dates';
 import { translate } from '../../helpers/l10n';
 import { IdentityProvider, Paging } from '../../types/types';
 import { User } from '../../types/users';
@@ -59,16 +59,16 @@ export default function UsersApp() {
     switch (usersActivity) {
       case UserActivity.ActiveSonarLintUser:
         return {
-          slLastConnectedAfter: toNotSoISOString(nowDateMinus30Days),
+          slLastConnectedAfter: toISO8601WithOffsetString(nowDateMinus30Days),
         };
       case UserActivity.ActiveSonarQubeUser:
         return {
-          lastConnectedAfter: toNotSoISOString(nowDateMinus30Days),
-          slLastConnectedBefore: toNotSoISOString(nowDateMinus30DaysAnd1Second),
+          lastConnectedAfter: toISO8601WithOffsetString(nowDateMinus30Days),
+          slLastConnectedBefore: toISO8601WithOffsetString(nowDateMinus30DaysAnd1Second),
         };
       case UserActivity.InactiveUser:
         return {
-          lastConnectedBefore: toNotSoISOString(nowDateMinus30DaysAnd1Second),
+          lastConnectedBefore: toISO8601WithOffsetString(nowDateMinus30DaysAnd1Second),
         };
       default:
         return {};
index 0652223990f1d7bcf561e906b13296f69ff6bf1d..fa8bec73b9cc7052f4dfb6fdf1793ffe2efdbef0 100644 (file)
@@ -22,12 +22,12 @@ import * as dates from '../dates';
 const { parseDate } = dates;
 const recentDate = parseDate('2017-08-16T12:00:00.000Z');
 
-it('toShortNotSoISOString', () => {
-  expect(dates.toShortNotSoISOString(recentDate)).toBe('2017-08-16');
+it('toShortISO8601String', () => {
+  expect(dates.toShortISO8601String(recentDate)).toBe('2017-08-16');
 });
 
-it('toNotSoISOString', () => {
-  expect(dates.toNotSoISOString(recentDate)).toBe('2017-08-16T12:00:00+0000');
+it('toISO8601WithOffsetString', () => {
+  expect(dates.toISO8601WithOffsetString(recentDate)).toBe('2017-08-16T12:00:00+0000');
 });
 
 it('isValidDate', () => {
index 9b1ea096353e71f6c6599b33f9c9313e15a4e274..01785173308fdb1fc178a4a7673e1e78e98c2a60 100644 (file)
@@ -30,15 +30,18 @@ export function parseDate(rawDate: ParsableDate): Date {
   return new Date(rawDate);
 }
 
-export function toShortNotSoISOString(rawDate: ParsableDate): string {
+export function toShortISO8601String(rawDate: ParsableDate): string {
   const date = parseDate(rawDate);
   return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
 }
 
-export function toNotSoISOString(rawDate: ParsableDate): string {
+export function toISO8601WithOffsetString(rawDate: ParsableDate): string {
   const date = parseDate(rawDate);
-  const dateWithoutZone = date.toISOString().split('.')[0];
-  return dateWithoutZone + '+0000';
+  // JS ISO Date implementation returns a datetime in UTC time (suffixed by "Z"). But the backend
+  // expects a datetime with a timeoffset (e.g., +0200). UTC time is actually "+0000", so we convert
+  // the string to this other format for the backend. The backend also doesn't expect milliseconds, so
+  // we truncate that part, too.
+  return date.toISOString().split('.')[0] + '+0000';
 }
 
 export function isValidDate(date: Date): boolean {
index 91a999f92681a040dcd239002fe103232212eafc..13a42a9e2faf62fa16b758917a75a2431df731c3 100644 (file)
@@ -20,7 +20,7 @@
 import { fetchL10nBundle } from '../api/l10n';
 import { L10nBundle, L10nBundleRequestParams } from '../types/l10nBundle';
 import { Dict } from '../types/types';
-import { toNotSoISOString } from './dates';
+import { toISO8601WithOffsetString } from './dates';
 
 const DEFAULT_LOCALE = 'en';
 const DEFAULT_MESSAGES: Dict<string> = {
@@ -70,7 +70,7 @@ export async function loadL10nBundle() {
   });
 
   const bundle = {
-    timestamp: toNotSoISOString(new Date()),
+    timestamp: toISO8601WithOffsetString(new Date()),
     locale: effectiveLocale,
     messages,
   };
index f2a00dc17ef0dcaa8af35e8f3fad678ddf463bea..f332c3117a767095df740b20ea25634e6d793832 100644 (file)
@@ -19,7 +19,7 @@
  */
 import { isEqual, isNil, omitBy } from 'lodash';
 import { RawQuery } from '../types/types';
-import { isValidDate, parseDate, toNotSoISOString, toShortNotSoISOString } from './dates';
+import { isValidDate, parseDate, toISO8601WithOffsetString, toShortISO8601String } from './dates';
 
 export function queriesEqual(a: RawQuery, b: RawQuery): boolean {
   const keysA = Object.keys(a);
@@ -91,7 +91,10 @@ export function parseAsOptionalArray<T>(
   return value ? parseAsArray(value, itemParser) : undefined;
 }
 
-export function serializeDate(value?: Date, serializer = toNotSoISOString): string | undefined {
+export function serializeDate(
+  value?: Date,
+  serializer = toISO8601WithOffsetString
+): string | undefined {
   if (value != null) {
     return serializer(value);
   }
@@ -99,7 +102,7 @@ export function serializeDate(value?: Date, serializer = toNotSoISOString): stri
 }
 
 export function serializeDateShort(value: Date | undefined): string | undefined {
-  return serializeDate(value, toShortNotSoISOString);
+  return serializeDate(value, toShortISO8601String);
 }
 
 export function serializeString(value: string | undefined): string | undefined {
index 811716765f9a78e1c87145e091f32b3e23f3c54f..5852cfa8928bc3ee6d4e93ad3e11fa868b354c46 100644 (file)
@@ -20,7 +20,7 @@
 import { getAllValues } from '../api/settings';
 import { SettingsKey } from '../types/settings';
 import { TokenExpiration, UserToken } from '../types/token';
-import { now, toShortNotSoISOString } from './dates';
+import { now, toShortISO8601String } from './dates';
 import { translate } from './l10n';
 
 export const EXPIRATION_OPTIONS = [
@@ -67,7 +67,7 @@ export async function getAvailableExpirationOptions() {
 export function computeTokenExpirationDate(days: number) {
   const expirationDate = now();
   expirationDate.setDate(expirationDate.getDate() + days);
-  return toShortNotSoISOString(expirationDate);
+  return toShortISO8601String(expirationDate);
 }
 
 export function getNextTokenName(tokenNameBase: string, tokens: UserToken[]) {