Browse Source

[NO JIRA] Rename ISO date helpers

The "NotSoISO" part of the old names was incorrect; the return value is
definitely ISO 8601.
tags/10.1.0.73491
Wouter Admiraal 1 year ago
parent
commit
563ee357c2
23 changed files with 125 additions and 87 deletions
  1. 6
    8
      server/sonar-web/src/main/js/api/mocks/ComputeEngineServiceMock.ts
  2. 2
    2
      server/sonar-web/src/main/js/app/components/StartupModal.tsx
  3. 16
    15
      server/sonar-web/src/main/js/app/components/__tests__/StartupModal-test.tsx
  4. 43
    10
      server/sonar-web/src/main/js/apps/background-tasks/__tests__/BackgroundTasks-it.tsx
  5. 3
    3
      server/sonar-web/src/main/js/apps/background-tasks/components/BackgroundTasksApp.tsx
  6. 3
    3
      server/sonar-web/src/main/js/apps/background-tasks/utils.ts
  7. 2
    2
      server/sonar-web/src/main/js/apps/overview/branches/BranchOverview.tsx
  8. 2
    2
      server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysesList.tsx
  9. 2
    2
      server/sonar-web/src/main/js/apps/projectBaseline/components/BranchAnalysisList.tsx
  10. 2
    2
      server/sonar-web/src/main/js/apps/projectBaseline/components/BranchAnalysisListRenderer.tsx
  11. 2
    2
      server/sonar-web/src/main/js/apps/projectBaseline/components/BranchBaselineSettingModal.tsx
  12. 3
    3
      server/sonar-web/src/main/js/apps/projectsManagement/BulkApplyTemplateModal.tsx
  13. 3
    3
      server/sonar-web/src/main/js/apps/projectsManagement/DeleteModal.tsx
  14. 3
    3
      server/sonar-web/src/main/js/apps/projectsManagement/ProjectManagementApp.tsx
  15. 3
    3
      server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangelogContainer.tsx
  16. 2
    2
      server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionRules.tsx
  17. 2
    2
      server/sonar-web/src/main/js/apps/system/components/PageHeader.tsx
  18. 5
    5
      server/sonar-web/src/main/js/apps/users/UsersApp.tsx
  19. 4
    4
      server/sonar-web/src/main/js/helpers/__tests__/dates-test.ts
  20. 7
    4
      server/sonar-web/src/main/js/helpers/dates.ts
  21. 2
    2
      server/sonar-web/src/main/js/helpers/l10nBundle.ts
  22. 6
    3
      server/sonar-web/src/main/js/helpers/query.ts
  23. 2
    2
      server/sonar-web/src/main/js/helpers/tokens.ts

+ 6
- 8
server/sonar-web/src/main/js/api/mocks/ComputeEngineServiceMock.ts View 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;

+ 2
- 2
server/sonar-web/src/main/js/app/components/StartupModal.tsx View 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 });
}
})

+ 16
- 15
server/sonar-web/src/main/js/app/components/__tests__/StartupModal-test.tsx View 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());
});


+ 43
- 10
server/sonar-web/src/main/js/apps/background-tasks/__tests__/BackgroundTasks-it.tsx View 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' }));

+ 3
- 3
server/sonar-web/src/main/js/apps/background-tasks/components/BackgroundTasksApp.tsx View 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({

+ 3
- 3
server/sonar-web/src/main/js/apps/background-tasks/utils.ts View 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) {

+ 2
- 2
server/sonar-web/src/main/js/apps/overview/branches/BranchOverview.tsx View 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;

+ 2
- 2
server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysesList.tsx View 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>

+ 2
- 2
server/sonar-web/src/main/js/apps/projectBaseline/components/BranchAnalysisList.tsx View 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)) {

+ 2
- 2
server/sonar-web/src/main/js/apps/projectBaseline/components/BranchAnalysisListRenderer.tsx View 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">

+ 2
- 2
server/sonar-web/src/main/js/apps/projectBaseline/components/BranchBaselineSettingModal.tsx View 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),
});
},
() => {

+ 3
- 3
server/sonar-web/src/main/js/apps/projectsManagement/BulkApplyTemplateModal.tsx View File

@@ -19,13 +19,13 @@
*/
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,

+ 3
- 3
server/sonar-web/src/main/js/apps/projectsManagement/DeleteModal.tsx View File

@@ -19,10 +19,10 @@
*/
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,

+ 3
- 3
server/sonar-web/src/main/js/apps/projectsManagement/ProjectManagementApp.tsx View File

@@ -20,13 +20,13 @@
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,

+ 3
- 3
server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangelogContainer.tsx View 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);
};

+ 2
- 2
server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionRules.tsx View 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() {

+ 2
- 2
server/sonar-web/src/main/js/apps/system/components/PageHeader.tsx View 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')}

+ 5
- 5
server/sonar-web/src/main/js/apps/users/UsersApp.tsx View 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 {};

+ 4
- 4
server/sonar-web/src/main/js/helpers/__tests__/dates-test.ts View 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', () => {

+ 7
- 4
server/sonar-web/src/main/js/helpers/dates.ts View 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 {

+ 2
- 2
server/sonar-web/src/main/js/helpers/l10nBundle.ts View 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,
};

+ 6
- 3
server/sonar-web/src/main/js/helpers/query.ts View 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 {

+ 2
- 2
server/sonar-web/src/main/js/helpers/tokens.ts View 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[]) {

Loading…
Cancel
Save