Signed-off-by: Fredrik Adelöw <freben@gmail.com>
This commit is contained in:
Fredrik Adelöw
2023-09-04 09:24:31 +02:00
parent 4912292503
commit 814feeed73
19 changed files with 75 additions and 38 deletions
+13
View File
@@ -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
+4 -1
View File
@@ -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>
+3 -2
View File
@@ -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>
@@ -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);