Updating request to focus on single request, started working on reservationScheduler
This commit is contained in:
parent
743cc08887
commit
7246ecabf2
7 changed files with 127 additions and 94 deletions
|
|
@ -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,24 +72,26 @@ 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 } = dateRanges[i]
|
const { start, end } = dateRange
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
@ -99,7 +108,6 @@ const validateRequestDateRanges = (dateRanges: DateRange[]): void => {
|
||||||
throw new ValidationError('Invalid request', ValidationErrorCode.INVALID_START_OR_END_DATE)
|
throw new ValidationError('Invalid request', ValidationErrorCode.INVALID_START_OR_END_DATE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const validateRequestOpponent = (opponent?: Opponent): void => {
|
const validateRequestOpponent = (opponent?: Opponent): void => {
|
||||||
if (!opponent) return
|
if (!opponent) return
|
||||||
|
|
|
||||||
|
|
@ -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 })
|
|
||||||
}
|
|
||||||
|
|
@ -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 })
|
|
||||||
}
|
|
||||||
7
src/lambdas/reservationHandler.ts
Normal file
7
src/lambdas/reservationHandler.ts
Normal 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> => {
|
||||||
|
}
|
||||||
22
src/lambdas/reservationScheduler.ts
Normal file
22
src/lambdas/reservationScheduler.ts
Normal 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')
|
||||||
|
}
|
||||||
9
src/stepFunctions/event.ts
Normal file
9
src/stepFunctions/event.ts
Normal 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
|
||||||
|
}
|
||||||
|
|
@ -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,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue