Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ustc #4372: allow full ISO date + end date for reconciliation report #4421

Merged
merged 20 commits into from
Apr 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
97ae0f6
ustc #4372: allow full ISO date + end date for reconciliation report
jtdevos Dec 13, 2023
0a9e21a
reconciliation report: disallow timespan > 24h
jtdevos Dec 15, 2023
f0bdfc0
Merge branch 'staging' into ustc-4372-reconciliation-report
jtdevos Dec 15, 2023
67dddb4
reconciliation report: use InvalidRequest; added 1-arg route
jtdevos Dec 15, 2023
e18536d
pulled date normalization/validation into DateHandler
jtdevos Dec 19, 2023
4c5193e
reconciliation report: use timeStart/timeEnd query parms instead of t…
jtdevos Dec 21, 2023
e5f8675
reonciliation report: updating tests
jtdevos Dec 22, 2023
2796a51
Merge branch 'staging' into ustc-4372-reconciliation-report
jtdevos Dec 27, 2023
37e0638
reconciliation report: derive args from seed data
jtdevos Dec 28, 2023
c49ab72
reconciliation report: fleshed out integration tests
jtdevos Dec 29, 2023
9baf1dd
reconciliation report: shorter parm names; cleanup
jtdevos Dec 29, 2023
012ede1
added yaml docs for reconciliation report
jtdevos Jan 2, 2024
0004912
Merge branch 'staging' into ustc-4372-reconciliation-report
jtdevos Jan 4, 2024
da7d339
4372 reconciliation report: remove timespan check (not needed)
jtdevos Jan 16, 2024
25729d0
Merge branch 'staging' into ustc-4372-reconciliation-report
jimlerza Jan 19, 2024
bc8d457
Merge branch 'staging' into ustc-4372-reconciliation-report
jimlerza Jan 29, 2024
59110cd
Merge branch 'staging' into ustc-4372-reconciliation-report
mmarcotte Apr 23, 2024
b9d5440
devex 4372: added test for start of DST
jtdevos Apr 23, 2024
5ff97a8
Merge branch 'staging' into ustc-4372-reconciliation-report
mmarcotte Apr 26, 2024
c1de057
Merge branch 'staging' into ustc-4372-reconciliation-report
mmarcotte Apr 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions docs/api/v2.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,27 @@ paths:
required: true
schema:
type: string
examples:
justDate:
summary: A specific date
value: 2022-12-25
withToday:
summary: The current date
value: today
- in: query
name: end
description: A time in HH:mm format (EST), to limit docket entries served before specified time
required: false
schema:
type: string
example: '23:00'
- in: query
name: start
description: A time in HH:mm format (EST), to limit docket entries served after specified time
required: false
schema:
type: string
example: '05:30'
responses:
'200':
description: reconciliation report of served docket entries
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ describe('getReconciliationReportInteractor', () => {
getReconciliationReportInteractor(applicationContext, {
reconciliationDate: undefined,
}),
).rejects.toThrow('must be formatted');
).rejects.toThrow('Must be valid reconciliation date');
});
it('should throw an error if date is in the future', async () => {
await expect(
Expand Down Expand Up @@ -163,4 +163,45 @@ describe('getReconciliationReportInteractor', () => {
.calls[0][0].docketNumbers,
).toEqual(['135-20']);
});

//Given date may contain ISO time component
it('should accept ISO dates + start time', async () => {
const startDate = '2020-01-01';
const timeStart = '05:00';
await expect(
getReconciliationReportInteractor(applicationContext, {
reconciliationDate: startDate,
start: timeStart,
}),
).resolves.not.toThrow();
});

//Caller may provide two date arguments
it('should accept starting date and start+end times', async () => {
const startDate = '2021-01-05';
const timeStart = '05:00';
const timeEnd = '09:00';
const docketEntries = [
{
docketEntryId: '3d27e02e-6954-4595-8b3f-0e91bbc1b51e',
docketNumber: '135-20',
documentTitle: 'Petition',
eventCode: 'P',
filedBy: 'Petr. Kaitlin Chaney',
filingDate: '2021-01-05T21:14:09.031Z',
servedAt: '2021-01-05T21:14:09.031Z',
},
] as any;

applicationContext
.getPersistenceGateway()
.getReconciliationReport.mockReturnValue(docketEntries);

const result = await getReconciliationReportInteractor(applicationContext, {
end: timeEnd,
reconciliationDate: startDate,
start: timeStart,
});
expect(result.reconciliationDate).toBe(startDate);
});
});
64 changes: 39 additions & 25 deletions shared/src/business/useCases/getReconciliationReportInteractor.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,22 @@
import { DocketEntryDynamoRecord } from '../../../../web-api/src/persistence/dynamo/dynamoTypes';
import {
FORMATS,
PATTERNS,
createEndOfDayISO,
createStartOfDayISO,
formatNow,
isValidDateString,
isValidReconciliationDate,
normalizeIsoDateRange,
} from '../../business/utilities/DateHandler';
import { InvalidRequest } from '@web-api/errors/errors';
import {
ROLE_PERMISSIONS,
isAuthorized,
} from '../../authorization/authorizationClientService';
import { ReconciliationReportEntry } from '../entities/ReconciliationReportEntry';
import { UnauthorizedError } from '@web-api/errors/errors';

const isValidDate = dateString => {
const dateInputValid = PATTERNS.YYYYMMDD.test(dateString);
const todayDate = formatNow(FORMATS.YYYYMMDD);
const dateLessthanOrEqualToToday = dateString <= todayDate;
return dateInputValid && dateLessthanOrEqualToToday;
};
function isValidTime(time: string): boolean {
return isValidDateString(time, [FORMATS.TIME_24_HOUR]);
}

/**
* getReconciliationReportInteractor
Expand All @@ -30,35 +28,50 @@ const isValidDate = dateString => {
*/
export const getReconciliationReportInteractor = async (
applicationContext: IApplicationContext,
{ reconciliationDate }: { reconciliationDate: string },
{
end: timeEnd,
reconciliationDate,
start: timeStart,
}: {
reconciliationDate: string;
end?: string;
start?: string;
},
) => {
const authorizedUser = applicationContext.getCurrentUser();

if (!isAuthorized(authorizedUser, ROLE_PERMISSIONS.SERVICE_SUMMARY_REPORT)) {
throw new UnauthorizedError('Unauthorized');
}

if (reconciliationDate === 'today') {
reconciliationDate = formatNow(FORMATS.YYYYMMDD);
} else {
const dateInputValid = isValidDate(reconciliationDate);
if (!dateInputValid) {
throw new Error(
'Date must be formatted as YYYY-MM-DD and not later than today',
);
}
const effectiveTimeStart = isValidTime(timeStart!) ? timeStart : '00:00';
const effectiveTimeEnd = isValidTime(timeEnd!)
? `${timeEnd}:59.999`
: '23:59:59.999';

const effectiveReconciliationDate =
reconciliationDate == 'today'
? formatNow(FORMATS.YYYYMMDD)
: reconciliationDate;

if (!isValidReconciliationDate(effectiveReconciliationDate)) {
throw new InvalidRequest(
'Must be valid reconciliation date and not later than today',
);
}

const [year, month, day] = reconciliationDate.split('-');
const reconciliationDateStart = createStartOfDayISO({ day, month, year });
const reconciliationDateEnd = createEndOfDayISO({ day, month, year });
//convert to full iso-8601 time stamps in utc timezone
const { end: isoEnd, start: isoStart } = normalizeIsoDateRange(
`${effectiveReconciliationDate}T${effectiveTimeStart}`,
`${effectiveReconciliationDate}T${effectiveTimeEnd}`,
);

// const reconciliationDateStart = dtReconciliationDateStart.toISO();
const docketEntries = await applicationContext
.getPersistenceGateway()
.getReconciliationReport({
applicationContext,
reconciliationDateEnd,
reconciliationDateStart,
reconciliationDateEnd: isoEnd,
reconciliationDateStart: isoStart,
});

await assignCaseCaptionFromPersistence(applicationContext, docketEntries);
Expand All @@ -69,6 +82,7 @@ export const getReconciliationReportInteractor = async (
{ applicationContext },
),
reconciliationDate,
reconciliationDateEnd: isoEnd,
reportTitle: 'Reconciliation Report',
totalDocketEntries: docketEntries.length,
};
Expand Down
74 changes: 73 additions & 1 deletion shared/src/business/utilities/DateHandler.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
/* eslint-disable max-lines */
import { DateTime, Settings } from 'luxon';
import {
FORMATS,
PATTERNS,
USTC_TZ,
calculateDifferenceInDays,
calculateDifferenceInHours,
calculateISODate,
castToISO,
checkDate,
Expand All @@ -20,11 +23,11 @@ import {
isStringISOFormatted,
isTodayWithinGivenInterval,
isValidDateString,
normalizeIsoDateRange,
prepareDateFromString,
subtractISODates,
validateDateAndCreateISO,
} from './DateHandler';
import { Settings } from 'luxon';

describe('DateHandler', () => {
const timeZones = [
Expand Down Expand Up @@ -740,4 +743,73 @@ describe('DateHandler', () => {
expect(result).toBe(true);
});
});

describe('ISO date range tests', () => {
const dcz = { zone: USTC_TZ };

//should return date range if given partial iso start date
it('should return date range if given partial iso start date', () => {
const start = '2002-11-01';
const expectedStart = DateTime.fromISO(start, dcz).toUTC().toISO();
const expectedEnd = DateTime.fromISO(start, dcz)
.endOf('day')
.toUTC()
.toISO();
const range = normalizeIsoDateRange(start, undefined);
expect(range.start).toBe(expectedStart);
expect(range.end).toBe(expectedEnd);
});

//should return date range if given two partial iso dates
it('should return date range if given two partial iso dates', () => {
const start = '2002-11-01';
const end = '2002-11-01T05:00';
const expectedStart = DateTime.fromISO(start, dcz).toUTC().toISO();
const expectedEnd = DateTime.fromISO(end, dcz).toUTC().toISO();
const range = normalizeIsoDateRange(start, end);
expect(range.start).toBe(expectedStart);
expect(range.end).toBe(expectedEnd);
});

//should throw an exception if given invalid iso start/end dates
it('should throw an error if given invalid start', () => {
expect(() => {
normalizeIsoDateRange('taco tuesday', undefined);
}).toThrow();

expect(() => {
normalizeIsoDateRange('today', 'tomorrow');
}).toThrow();
});
});

describe('calculateDifferenceInHours', () => {
it('should calculate difference in hours', () => {
const dateStart = '2000-01-01T00:00';
const dateEnd = '2000-01-01T05:00';
const diff = calculateDifferenceInHours(dateEnd, dateStart);
expect(diff).toBe(5);
});

it('should calculate 24 hours difference in a day', () => {
const dt = DateTime.fromISO('2024-11-01', { zone: USTC_TZ });
const dt2 = dt.plus({ days: 1 });
const diff = calculateDifferenceInHours(dt2.toISO()!, dt.toISO()!);
expect(diff).toBe(24);
});

it('should calculate 25 hours on last day of daylight savings time', () => {
const dt = DateTime.fromISO('2024-11-03', { zone: USTC_TZ });
const dt2 = dt.plus({ days: 1 });
const diff = calculateDifferenceInHours(dt2.toISO()!, dt.toISO()!);
expect(diff).toBe(25);
});

it('should calculate 23 hours on first day of daylight savings time', () => {
const dt = DateTime.fromISO('2024-03-10', { zone: USTC_TZ });
const dt2 = dt.plus({ days: 1 });
const diff = calculateDifferenceInHours(dt2.toISO()!, dt.toISO()!);
expect(diff).toBe(23);
});
});
});
Loading
Loading