Updating request to focus on single request, started working on reservationScheduler

This commit is contained in:
Collin Duncan 2021-11-24 15:38:58 +01:00
parent 743cc08887
commit 7246ecabf2
No known key found for this signature in database
7 changed files with 127 additions and 94 deletions

View file

@ -7,19 +7,20 @@ import { DateRange, Opponent } from './reservation'
export interface ReservationRequest { export interface ReservationRequest {
username: string username: string
password: string password: string
dateRanges: { dateRange: {
start: Dayjs start: Dayjs
end: Dayjs end: Dayjs
}[] }
opponent: Opponent opponent: Opponent
} }
export enum ValidationErrorCode { export enum ValidationErrorCode {
UNDEFINED_REQUEST_BODY = 1, UNDEFINED_REQUEST_BODY,
INVALID_REQUEST_BODY = 2, INVALID_JSON,
INVALID_DATE_RANGE = 3, INVALID_REQUEST_BODY,
INVALID_START_OR_END_DATE = 4, INVALID_DATE_RANGE,
INVALID_OPPONENT = 5, INVALID_START_OR_END_DATE,
INVALID_OPPONENT,
} }
export class ValidationError extends Error { export class ValidationError extends Error {
@ -31,11 +32,16 @@ export class ValidationError extends Error {
} }
} }
/**
* Validates an incoming request body and converts to ReservationRequest
* @param body String of request body
* @returns ReservationRequest
*/
export const validateRequest = ( export const validateRequest = (
body: string body: string
): ReservationRequest => { ): ReservationRequest => {
const request = validateRequestBody(body) const request = validateRequestBody(body)
validateRequestDateRanges(request.dateRanges) validateRequestDateRange(request.dateRange)
validateRequestOpponent(request.opponent) validateRequestOpponent(request.opponent)
return request return request
} }
@ -46,15 +52,16 @@ const validateRequestBody = (body?: string): ReservationRequest => {
} }
const jsonBody = transformRequestBody(body) const jsonBody = transformRequestBody(body)
const { username, password, opponent, dateRanges } = jsonBody const { username, password, opponent, dateRange } = jsonBody
if ( if (
!username || !username ||
username.length < 1 || username.length < 1 ||
!password || !password ||
password.length < 1 || password.length < 1 ||
!dateRanges || !dateRange ||
dateRanges.length < 1 || !dateRange.start ||
!dateRange.end ||
(opponent && opponent.id && opponent.id.length < 1) || (opponent && opponent.id && opponent.id.length < 1) ||
(opponent && opponent.name && opponent.name.length < 1) (opponent && opponent.name && opponent.name.length < 1)
) { ) {
@ -65,39 +72,40 @@ const validateRequestBody = (body?: string): ReservationRequest => {
} }
const transformRequestBody = (body: string): ReservationRequest => { const transformRequestBody = (body: string): ReservationRequest => {
const json = JSON.parse(body) let json
const dateRanges: DateRange[] = json.dateRanges?.map( try {
({ start, end }: { start: string; end: string }): DateRange => { json = JSON.parse(body)
return { start: dayjs(start), end: dayjs(end) } } catch (err) {
} throw new ValidationError('Invalid request', ValidationErrorCode.INVALID_JSON)
) }
const startTime = json.dateRange?.start ?? 'invalid'
const endTime = json.dateRange?.end ?? 'invalid'
const dateRange: DateRange = { start: dayjs(startTime), end: dayjs(endTime) }
return { return {
username: json.username, username: json.username,
password: json.password, password: json.password,
opponent: json.opponent, opponent: json.opponent,
dateRanges, dateRange,
} }
} }
const validateRequestDateRanges = (dateRanges: DateRange[]): void => { const validateRequestDateRange = (dateRange: DateRange): void => {
for (let i = 0; i < dateRanges.length; i++) { // checking that both dates are valid
// checking that both dates are valid const { start, end } = dateRange
const { start, end } = dateRanges[i] if (!start.isValid() || !end.isValid()) {
if (!start.isValid() || !end.isValid()) { throw new ValidationError('Invalid request', ValidationErrorCode.INVALID_DATE_RANGE)
throw new ValidationError('Invalid request', ValidationErrorCode.INVALID_DATE_RANGE) }
}
// checking that: // checking that:
// 1. start occurs after now // 1. start occurs after now
// 2. start occurs before or same as end // 2. start occurs before or same as end
// 3. start and end fall on same YYYY/MM/DD // 3. start and end fall on same YYYY/MM/DD
if ( if (
!start.isAfter(dayjs()) || !start.isAfter(dayjs()) ||
!start.isSameOrBefore(end) || !start.isSameOrBefore(end) ||
start.format('YYYY MM DD') !== end.format('YYYY MM DD') start.format('YYYY MM DD') !== end.format('YYYY MM DD')
) { ) {
throw new ValidationError('Invalid request', ValidationErrorCode.INVALID_START_OR_END_DATE) throw new ValidationError('Invalid request', ValidationErrorCode.INVALID_START_OR_END_DATE)
}
} }
} }

View file

@ -1,18 +0,0 @@
import { SQSEvent, SQSHandler } from 'aws-lambda'
import { validateRequest } from '../common/request'
import { Reservation } from '../common/reservation'
import { Runner } from '../common/runner'
export const run: SQSHandler = async (event: SQSEvent): Promise<void> => {
const { request, error } = validateRequest(event.Records[0].body)
if (error || !request) {
throw new Error(error?.message)
}
const { username, password, dateRanges, opponent } = request
const reservations = dateRanges.map((dr) => new Reservation(dr, opponent))
const runner = new Runner(username, password, reservations)
await runner.run({ headless: false })
}

View file

@ -1,18 +0,0 @@
import { SQSEvent, SQSHandler } from 'aws-lambda'
import { validateRequestEvent } from '../common/request'
import { Reservation } from '../common/reservation'
import { Runner } from '../common/runner'
export const run: SQSHandler = async (event: SQSEvent): Promise<void> => {
const { request, error } = validateRequestEvent(event)
if (error || !request) {
throw new Error(error?.message)
}
const { username, password, dateTimes, opponent } = request
const reservations = dateTimes.map((dt) => new Reservation(dt, opponent))
const runner = new Runner(username, password, reservations)
await runner.run({ headless: false })
}

View file

@ -0,0 +1,7 @@
import { Handler } from 'aws-lambda'
import { Reservation } from '../common/reservation'
import { Runner } from '../common/runner'
export const run: Handler = async (payload: string): Promise<void> => {
}

View file

@ -0,0 +1,22 @@
import { Handler } from 'aws-lambda'
import dayjs from 'dayjs'
import { InputEvent } from '../stepFunctions/event'
import { Reservation } from '../common/reservation'
import { validateRequest } from '../common/request'
import { Runner } from '../common/runner'
export const run: Handler<InputEvent, void> = async (input: InputEvent): Promise<void> => {
console.log(`Handling event: ${input}`)
const { username, password, dateRange, opponent } = validateRequest(JSON.stringify(input.reservationRequest))
console.log('Successfully validated request')
console.log('Creating reservation')
const reservation = new Reservation(dateRange, opponent)
console.log('Created reservation')
console.log('Runner starting')
const runner = new Runner(username, password, [reservation])
await runner.run()
console.log('Runner finished')
}

View file

@ -0,0 +1,9 @@
import { ReservationRequest } from "../common/request";
export interface RawReservationRequest extends Omit<ReservationRequest, 'dateRanges'> {
dateRanges: { start: string, end: string }[]
}
export interface InputEvent {
reservationRequest: RawReservationRequest
}

View file

@ -12,9 +12,10 @@ describe('request', () => {
const body = JSON.stringify({ const body = JSON.stringify({
username: 'collin', username: 'collin',
password: '123abc', password: '123abc',
dateRanges: [ dateRange: {
{ start: '2021-12-25T12:34:56Z', end: '2021-12-25T12:45:56Z' } start: '2021-12-25T12:34:56Z',
], end: '2021-12-25T12:45:56Z'
},
opponent: { opponent: {
id: '123', id: '123',
name: 'collin', name: 'collin',
@ -28,17 +29,36 @@ describe('request', () => {
expect(() => validateRequest(undefined)).toThrowError(new ValidationError('Invalid request', ValidationErrorCode.UNDEFINED_REQUEST_BODY)) expect(() => validateRequest(undefined)).toThrowError(new ValidationError('Invalid request', ValidationErrorCode.UNDEFINED_REQUEST_BODY))
}) })
test('should fail for invalid json', () => {
const body = `A{
username: 'collin',
password: '123abc',
dateRange: {
start: '2021-12-25T12:34:56Z',
end: '2021-12-25T12:45:56Z'
},
opponent: {
id: '123',
name: 'collin',
}
}`
expect(() => validateRequest(body)).toThrowError(new ValidationError('Invalid request', ValidationErrorCode.INVALID_JSON))
})
test.each([ test.each([
{ username: '', password: '1qaz2wsx', dateRanges: [{ start: '1', end: '1' }], opponent: { id: '123', name: 'abc' } }, { username: '', password: '1qaz2wsx', dateRange: { start: '1', end: '1' }, opponent: { id: '123', name: 'abc' } },
{ password: '1qaz2wsx', dateRanges: [{ start: '1', end: '1' }], opponent: { id: '123', name: 'abc' } }, { password: '1qaz2wsx', dateRange: { start: '1', end: '1' }, opponent: { id: '123', name: 'abc' } },
{ username: 'collin', password: '', dateRanges: [{ start: '1', end: '1' }], opponent: { id: '123', name: 'abc' } }, { username: 'collin', password: '', dateRange: { start: '1', end: '1' }, opponent: { id: '123', name: 'abc' } },
{ username: 'collin', dateRanges: [{ start: '1', end: '1' }], opponent: { id: '123', name: 'abc' } }, { username: 'collin', dateRange: { start: '1', end: '1' }, opponent: { id: '123', name: 'abc' } },
{ username: 'collin', password: '1qaz2wsx', dateRanges: [], opponent: { id: '123', name: 'abc' } }, { username: 'collin', password: '1qaz2wsx', dateRange: {}, opponent: { id: '123', name: 'abc' } },
{ username: 'collin', password: '1qaz2wsx', dateRange: { start: '1' }, opponent: { id: '123', name: 'abc' } },
{ username: 'collin', password: '1qaz2wsx', dateRange: { end: '1' }, opponent: { id: '123', name: 'abc' } },
{ username: 'collin', password: '1qaz2wsx', opponent: { id: '123', name: 'abc' } }, { username: 'collin', password: '1qaz2wsx', opponent: { id: '123', name: 'abc' } },
{ username: 'collin', password: '1qaz2wsx', dateRanges: [{ start: '1', end: '1' }], opponent: { id: '', name: 'abc' } }, { username: 'collin', password: '1qaz2wsx', dateRange: { start: '1', end: '1' }, opponent: { id: '', name: 'abc' } },
{ username: 'collin', password: '1qaz2wsx', dateRanges: [{ start: '1', end: '1' }], opponent: { name: 'abc' } }, { username: 'collin', password: '1qaz2wsx', dateRange: { start: '1', end: '1' }, opponent: { name: 'abc' } },
{ username: 'collin', password: '1qaz2wsx', dateRanges: [{ start: '1', end: '1' }], opponent: { id: '123', name: '' } }, { username: 'collin', password: '1qaz2wsx', dateRange: { start: '1', end: '1' }, opponent: { id: '123', name: '' } },
{ username: 'collin', password: '1qaz2wsx', dateRanges: [{ start: '1', end: '1' }], opponent: { id: '123' } }, { username: 'collin', password: '1qaz2wsx', dateRange: { start: '1', end: '1' }, opponent: { id: '123' } },
])('should fail for body missing required values', (body) => { ])('should fail for body missing required values', (body) => {
expect(() => validateRequest(JSON.stringify(body))).toThrowError(new ValidationError('Invalid request', ValidationErrorCode.INVALID_REQUEST_BODY)) expect(() => validateRequest(JSON.stringify(body))).toThrowError(new ValidationError('Invalid request', ValidationErrorCode.INVALID_REQUEST_BODY))
}) })
@ -47,9 +67,10 @@ describe('request', () => {
const body = JSON.stringify({ const body = JSON.stringify({
username: 'collin', username: 'collin',
password: '123abc', password: '123abc',
dateRanges: [ dateRange: {
{ start: 'monkey', end: '2021-12-25T12:45:56Z' } start: 'monkey',
], end: '2021-12-25T12:45:56Z'
},
opponent: { opponent: {
id: '123', id: '123',
name: 'collin', name: 'collin',
@ -67,7 +88,7 @@ describe('request', () => {
const body = JSON.stringify({ const body = JSON.stringify({
username: 'collin', username: 'collin',
password: '123abc', password: '123abc',
dateRanges: [ dateRange: [
dateRange dateRange
], ],
opponent: { opponent: {
@ -83,9 +104,10 @@ describe('request', () => {
const body = JSON.stringify({ const body = JSON.stringify({
username: 'collin', username: 'collin',
password: '123abc', password: '123abc',
dateRanges: [ dateRange: {
{ start: '2021-12-25T12:34:56Z', end: '2021-12-25T12:45:56Z' } start: '2021-12-25T12:34:56Z',
], end: '2021-12-25T12:45:56Z'
},
}) })
expect(() => validateRequest(body)).not.toThrow() expect(() => validateRequest(body)).not.toThrow()
@ -100,9 +122,10 @@ describe('request', () => {
const body = JSON.stringify({ const body = JSON.stringify({
username: 'collin', username: 'collin',
password: '123abc', password: '123abc',
dateRanges: [ dateRange: {
{ start: '2021-12-25T12:34:56Z', end: '2021-12-25T12:45:56Z' } start: '2021-12-25T12:34:56Z',
], end: '2021-12-25T12:45:56Z'
},
opponent, opponent,
}) })