@@ -0,0 +1,13 @@
|
||||
---
|
||||
'@backstage/plugin-tech-insights-backend': patch
|
||||
'@backstage/plugin-microsoft-calendar': patch
|
||||
'@backstage/plugin-scaffolder-backend': patch
|
||||
'@backstage/plugin-catalog-backend': patch
|
||||
'@backstage/backend-tasks': patch
|
||||
'@backstage/plugin-cloudbuild': patch
|
||||
'@backstage/plugin-gcalendar': patch
|
||||
'@backstage/plugin-xcmetrics': patch
|
||||
'@backstage/plugin-ilert': patch
|
||||
---
|
||||
|
||||
Update to handle invalid luxon values
|
||||
@@ -14,4 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export {};
|
||||
import { Settings } from 'luxon';
|
||||
|
||||
// TS still thinks that methods can return null / placeholders, but we still want to throw as soon as possible when things go wrong
|
||||
Settings.throwOnInvalid = true;
|
||||
|
||||
@@ -112,9 +112,15 @@ export function parseDuration(
|
||||
return frequency.cron;
|
||||
}
|
||||
|
||||
if (Duration.isDuration(frequency)) {
|
||||
return frequency.toISO();
|
||||
const parsed = Duration.isDuration(frequency)
|
||||
? frequency
|
||||
: Duration.fromObject(frequency);
|
||||
|
||||
if (!parsed.isValid) {
|
||||
throw new Error(
|
||||
`Invalid duration, ${parsed.invalidReason}: ${parsed.invalidExplanation}`,
|
||||
);
|
||||
}
|
||||
|
||||
return Duration.fromObject(frequency).toISO();
|
||||
return parsed.toISO()!;
|
||||
}
|
||||
|
||||
@@ -47,8 +47,8 @@ describe('TaskWorker', () => {
|
||||
const settings: TaskSettingsV2 = {
|
||||
version: 2,
|
||||
cadence: '*/2 * * * * *',
|
||||
initialDelayDuration: Duration.fromObject({ seconds: 1 }).toISO(),
|
||||
timeoutAfterDuration: Duration.fromObject({ minutes: 1 }).toISO(),
|
||||
initialDelayDuration: Duration.fromObject({ seconds: 1 }).toISO()!,
|
||||
timeoutAfterDuration: Duration.fromObject({ minutes: 1 }).toISO()!,
|
||||
};
|
||||
|
||||
const worker = new TaskWorker('task1', fn, knex, logger);
|
||||
@@ -131,7 +131,7 @@ describe('TaskWorker', () => {
|
||||
version: 2,
|
||||
initialDelayDuration: undefined,
|
||||
cadence: '* * * * * *',
|
||||
timeoutAfterDuration: Duration.fromMillis(60000).toISO(),
|
||||
timeoutAfterDuration: Duration.fromMillis(60000).toISO()!,
|
||||
};
|
||||
const checkFrequency = Duration.fromObject({ milliseconds: 100 });
|
||||
const worker = new TaskWorker('task1', fn, knex, logger, checkFrequency);
|
||||
@@ -154,7 +154,7 @@ describe('TaskWorker', () => {
|
||||
version: 2,
|
||||
initialDelayDuration: undefined,
|
||||
cadence: '* * * * * *',
|
||||
timeoutAfterDuration: Duration.fromMillis(60000).toISO(),
|
||||
timeoutAfterDuration: Duration.fromMillis(60000).toISO()!,
|
||||
};
|
||||
const checkFrequency = Duration.fromObject({ milliseconds: 100 });
|
||||
const worker = new TaskWorker('task1', fn, knex, logger, checkFrequency);
|
||||
@@ -179,7 +179,7 @@ describe('TaskWorker', () => {
|
||||
version: 2,
|
||||
initialDelayDuration: undefined,
|
||||
cadence: '* * * * * *',
|
||||
timeoutAfterDuration: Duration.fromMillis(60000).toISO(),
|
||||
timeoutAfterDuration: Duration.fromMillis(60000).toISO()!,
|
||||
};
|
||||
|
||||
const worker = new TaskWorker('task1', fn, knex, logger);
|
||||
@@ -235,7 +235,7 @@ describe('TaskWorker', () => {
|
||||
version: 2,
|
||||
initialDelayDuration: undefined,
|
||||
cadence: '* * * * * *',
|
||||
timeoutAfterDuration: Duration.fromMillis(60000).toISO(),
|
||||
timeoutAfterDuration: Duration.fromMillis(60000).toISO()!,
|
||||
};
|
||||
|
||||
const worker1 = new TaskWorker('task1', fn, knex, logger);
|
||||
|
||||
@@ -375,7 +375,7 @@ export class DefaultProcessingDatabase implements ProcessingDatabase {
|
||||
entityRef,
|
||||
newLocationKey: locationKey,
|
||||
existingLocationKey: conflictingKey,
|
||||
lastConflictAt: DateTime.now().toISO(),
|
||||
lastConflictAt: DateTime.now().toISO()!,
|
||||
},
|
||||
};
|
||||
await this.options.eventBroker?.publish(eventParams);
|
||||
|
||||
@@ -87,7 +87,7 @@ const generatedColumns: TableColumn[] = [
|
||||
title: 'Created',
|
||||
render: (row: Partial<WorkflowRun>) => (
|
||||
<Typography data-testid="cell-created" variant="body2" noWrap>
|
||||
{DateTime.fromISO(row.createTime ?? DateTime.now().toISO()).toFormat(
|
||||
{DateTime.fromISO(row.createTime ?? DateTime.now().toISO()!).toFormat(
|
||||
'dd-MM-yyyy hh:mm:ss',
|
||||
)}
|
||||
</Typography>
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { DateTime } from 'luxon';
|
||||
import { GCalendar, GCalendarEvent } from '../src/api';
|
||||
|
||||
@@ -38,11 +39,11 @@ export const eventsMock: GCalendarEvent[] = [...Array(3).keys()].map(i => ({
|
||||
start: {
|
||||
dateTime: DateTime.now()
|
||||
.minus({ hour: i + 1 })
|
||||
.toISO(),
|
||||
.toISO()!,
|
||||
timeZone: 'Europe/London',
|
||||
},
|
||||
end: {
|
||||
dateTime: DateTime.now().minus({ hour: i }).toISO(),
|
||||
dateTime: DateTime.now().minus({ hour: i }).toISO()!,
|
||||
timeZone: 'Europe/London',
|
||||
},
|
||||
description: '<h3>Dummy title</h3><p>Dummy description</p>',
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { sortBy } from 'lodash';
|
||||
import { DateTime } from 'luxon';
|
||||
import React, { useState } from 'react';
|
||||
@@ -67,9 +68,9 @@ export const CalendarCard = () => {
|
||||
calendars,
|
||||
selectedCalendars: storedCalendars,
|
||||
enabled: isSignedIn && calendars.length > 0,
|
||||
timeMin: date.startOf('day').toISO(),
|
||||
timeMax: date.endOf('day').toISO(),
|
||||
timeZone: date.zoneName,
|
||||
timeMin: date.startOf('day').toISO()!,
|
||||
timeMax: date.endOf('day').toISO()!,
|
||||
timeZone: date.zoneName ?? 'UTC', // TODO: Use browser timezone? This probably never happens anyway
|
||||
});
|
||||
|
||||
const showLoader =
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { compact, unescape } from 'lodash';
|
||||
import { useMemo } from 'react';
|
||||
import { useQueries } from '@tanstack/react-query';
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import Button from '@material-ui/core/Button';
|
||||
@@ -161,7 +162,7 @@ export const ShiftOverrideModal = ({
|
||||
value={start}
|
||||
className={classes.formControl}
|
||||
onChange={date => {
|
||||
setStart(date ? date.toISO() : '');
|
||||
setStart(date?.toISO() ?? '');
|
||||
}}
|
||||
/>
|
||||
<DateTimePicker
|
||||
@@ -173,7 +174,7 @@ export const ShiftOverrideModal = ({
|
||||
value={end}
|
||||
className={classes.formControl}
|
||||
onChange={date => {
|
||||
setEnd(date ? date.toISO() : '');
|
||||
setEnd(date?.toISO() ?? '');
|
||||
}}
|
||||
/>
|
||||
</MuiPickersUtilsProvider>
|
||||
|
||||
+4
-3
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { render } from '@testing-library/react';
|
||||
@@ -44,7 +45,7 @@ const podWithConditions = (conditions: IPodCondition[]): any => {
|
||||
|
||||
describe('PendingPodContent', () => {
|
||||
it('show startup conditions - all healthy', async () => {
|
||||
const oneDayAgo = DateTime.now().minus({ days: 1 }).toISO();
|
||||
const oneDayAgo = DateTime.now().minus({ days: 1 }).toISO()!;
|
||||
const { getByText, queryByLabelText, queryAllByLabelText } = render(
|
||||
wrapInTestApp(
|
||||
<PendingPodContent
|
||||
@@ -87,7 +88,7 @@ describe('PendingPodContent', () => {
|
||||
expect(queryByLabelText('Status error')).not.toBeInTheDocument();
|
||||
});
|
||||
it('show startup conditions - all fail', async () => {
|
||||
const oneHourAgo = DateTime.now().minus({ hours: 1 }).toISO();
|
||||
const oneHourAgo = DateTime.now().minus({ hours: 1 }).toISO()!;
|
||||
const { getByText, queryByLabelText, queryAllByLabelText } = render(
|
||||
wrapInTestApp(
|
||||
<PendingPodContent
|
||||
@@ -154,7 +155,7 @@ describe('PendingPodContent', () => {
|
||||
expect(queryAllByLabelText('Status error')).toHaveLength(4);
|
||||
});
|
||||
it('show startup conditions - show unknown', async () => {
|
||||
const oneHourAgo = DateTime.now().minus({ hours: 1 }).toISO();
|
||||
const oneHourAgo = DateTime.now().minus({ hours: 1 }).toISO()!;
|
||||
const { getByText, queryByLabelText, getByLabelText } = render(
|
||||
wrapInTestApp(
|
||||
<PendingPodContent
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { sortBy } from 'lodash';
|
||||
import { DateTime } from 'luxon';
|
||||
import React, { useState } from 'react';
|
||||
@@ -56,9 +57,9 @@ export const CalendarCard = () => {
|
||||
const { data: events, isLoading: isEventLoading } = useEventsQuery({
|
||||
calendarId: selectedCalendarId || defaultCalendarId || '',
|
||||
enabled: isSignedIn && calendars.length > 0,
|
||||
timeMin: date.startOf('day').toISO(),
|
||||
timeMax: date.endOf('day').toISO(),
|
||||
timeZone: date.zoneName,
|
||||
timeMin: date.startOf('day').toISO()!,
|
||||
timeMax: date.endOf('day').toISO()!,
|
||||
timeZone: date.zoneName ?? undefined,
|
||||
});
|
||||
|
||||
const showLoader =
|
||||
|
||||
@@ -69,7 +69,7 @@ export type DatabaseTaskStoreOptions = {
|
||||
};
|
||||
|
||||
/**
|
||||
* Typeguard to help DatabaseTaskStore understand when database is PluginDatabaseManager vs. when database is a Knex instance.
|
||||
* Type guard to help DatabaseTaskStore understand when database is PluginDatabaseManager vs. when database is a Knex instance.
|
||||
*
|
||||
* * @public
|
||||
*/
|
||||
@@ -81,7 +81,13 @@ function isPluginDatabaseManager(
|
||||
|
||||
const parseSqlDateToIsoString = <T>(input: T): T | string => {
|
||||
if (typeof input === 'string') {
|
||||
return DateTime.fromSQL(input, { zone: 'UTC' }).toISO();
|
||||
const parsed = DateTime.fromSQL(input, { zone: 'UTC' });
|
||||
if (!parsed.isValid) {
|
||||
throw new Error(
|
||||
`Failed to parse database timestamp '${input}', ${parsed.invalidReason}: ${parsed.invalidExplanation}`,
|
||||
);
|
||||
}
|
||||
return parsed.toISO()!;
|
||||
}
|
||||
|
||||
return input;
|
||||
|
||||
@@ -23,7 +23,7 @@ import { DateTime } from 'luxon';
|
||||
describe('<CreatedAtColumn />', () => {
|
||||
it('should render the column with the time', async () => {
|
||||
const props = {
|
||||
createdAt: DateTime.now().toISO(),
|
||||
createdAt: DateTime.now().toISO()!,
|
||||
};
|
||||
|
||||
const { getByText } = await renderInTestApp(<CreatedAtColumn {...props} />);
|
||||
|
||||
@@ -63,7 +63,7 @@ describe('SentryIssuesTable', () => {
|
||||
},
|
||||
count: '101',
|
||||
userCount: 202,
|
||||
lastSeen: DateTime.now().toISO(),
|
||||
lastSeen: DateTime.now().toISO()!,
|
||||
},
|
||||
];
|
||||
const table = await renderInTestApp(
|
||||
|
||||
@@ -74,13 +74,13 @@ const secondSchema = {
|
||||
}),
|
||||
};
|
||||
|
||||
const now = DateTime.now().toISO();
|
||||
const now = DateTime.now().toISO()!;
|
||||
const shortlyInTheFuture = DateTime.now()
|
||||
.plus(Duration.fromMillis(555))
|
||||
.toISO();
|
||||
.toISO()!;
|
||||
const farInTheFuture = DateTime.now()
|
||||
.plus(Duration.fromMillis(555666777))
|
||||
.toISO();
|
||||
.toISO()!;
|
||||
|
||||
const facts = [
|
||||
{
|
||||
@@ -127,13 +127,13 @@ const sameFactsDiffDateSchema = {
|
||||
}),
|
||||
};
|
||||
|
||||
const sameFactsDiffDateNow = DateTime.now().toISO();
|
||||
const sameFactsDiffDateNow = DateTime.now().toISO()!;
|
||||
const sameFactsDiffDateNearFuture = DateTime.now()
|
||||
.plus(Duration.fromMillis(555))
|
||||
.toISO();
|
||||
.toISO()!;
|
||||
const sameFactsDiffDateFuture = DateTime.now()
|
||||
.plus(Duration.fromMillis(1000))
|
||||
.toISO();
|
||||
.toISO()!;
|
||||
|
||||
const multipleSameFacts = [
|
||||
{
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Knex } from 'knex';
|
||||
import {
|
||||
FactLifecycle,
|
||||
@@ -105,12 +106,13 @@ export class TechInsightsDatabase implements TechInsightsStore {
|
||||
if (facts.length === 0) return;
|
||||
const currentSchema = await this.getLatestSchema(id);
|
||||
const factRows = facts.map(it => {
|
||||
const ts = it.timestamp?.toISO();
|
||||
return {
|
||||
id,
|
||||
version: currentSchema.version,
|
||||
entity: stringifyEntityRef(it.entity),
|
||||
facts: JSON.stringify(it.facts),
|
||||
...(it.timestamp && { timestamp: it.timestamp.toISO() }),
|
||||
...(ts && { timestamp: ts }),
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { renderInTestApp, TestApiProvider } from '@backstage/test-utils';
|
||||
import { BuildList } from './BuildList';
|
||||
|
||||
@@ -39,8 +39,8 @@ export const BuildList = () => {
|
||||
const tableRef = useRef<any>();
|
||||
|
||||
const initialFilters = {
|
||||
from: DateTime.now().minus({ years: 1 }).toISODate(),
|
||||
to: DateTime.now().toISODate(),
|
||||
from: DateTime.now().minus({ years: 1 }).toISODate()!,
|
||||
to: DateTime.now().toISODate()!,
|
||||
};
|
||||
|
||||
const [filters, setFilters] = useState<BuildFilters>(initialFilters);
|
||||
|
||||
Reference in New Issue
Block a user