From 98bd540130a8ac1a4f8bbc180f6fbf5ed71e34be Mon Sep 17 00:00:00 2001 From: Collin Duncan <3679940+cgduncan7@users.noreply.github.com> Date: Tue, 29 Mar 2022 22:41:44 +0200 Subject: [PATCH] Updating runner to change to next month if needed; Added some more logging; Cleaning up some scripts --- package.json | 9 ++++---- src/common/logger.ts | 13 +++++++++-- src/common/request.ts | 5 +--- src/common/reservation.ts | 12 ++++++++++ src/common/runner.ts | 32 ++++++++++++++++++++++---- src/local.ts | 3 +++ src/workers/requester/index.ts | 2 +- src/workers/requester/rollup.config.js | 8 ++----- src/workers/scheduler/index.ts | 11 ++++----- src/workers/scheduler/rollup.config.js | 8 ++----- src/workers/types.ts | 2 +- tests/workers/scheduler.test.ts | 14 +++++------ 12 files changed, 76 insertions(+), 43 deletions(-) diff --git a/package.json b/package.json index 99689c1..204bed0 100644 --- a/package.json +++ b/package.json @@ -7,16 +7,15 @@ "node": "14.x" }, "scripts": { - "build": "rm -r ./dist/* && rollup -c", - "build:reservationScheduler": "rm -r ./dist/reservationScheduler/ || : && rollup -c ./src/lambdas/reservationScheduler/rollup.config.js", - "package:reservationScheduler": "rm ./deploy/reservationScheduler.zip || : && mkdir ./deploy || : && zip deploy/reservationScheduler.zip -j dist/reservationScheduler/*", + "clean": "rm -r ./dist || true", + "build:requester": "rollup -c src/workers/requester/rollup.config.js", + "build:scheduler": "rollup -c src/workers/scheduler/rollup.config.js", "test": "jest", "test:clear-cache": "jest --clearCache", "test:clean": "npm run test:clear-cache && npm run test", "lint": "eslint src/ --ext ts", "prettier": "prettier src -w", - "local": "npx ts-node src/local.ts", - "zip": "mkdir deploy && zip deploy/reservation-lambda.zip -r dist" + "local": "npx ts-node src/local.ts" }, "author": "Collin Duncan ", "license": "ISC", diff --git a/src/common/logger.ts b/src/common/logger.ts index 251a335..6f8bf61 100644 --- a/src/common/logger.ts +++ b/src/common/logger.ts @@ -38,7 +38,11 @@ export class LoggerInstance { private readonly correlationId: string private readonly level: LogLevel - public constructor(tag: string, correlationId: string, level = LogLevel.ERROR) { + public constructor( + tag: string, + correlationId: string, + level = LogLevel.ERROR + ) { this.tag = tag this.correlationId = correlationId this.level = level @@ -64,7 +68,12 @@ export class LoggerInstance { } let fmtString = '<%s> [%s] %s: %s' - const params: Array = [this.tag, this.correlationId, levelString, message] + const params: Array = [ + this.tag, + this.correlationId, + levelString, + message, + ] if (details) { params.push(details) fmtString += ' - %O' diff --git a/src/common/request.ts b/src/common/request.ts index cd0fc66..583ccd4 100644 --- a/src/common/request.ts +++ b/src/common/request.ts @@ -157,10 +157,7 @@ const validateRequestOpponent = (opponent?: Opponent): void => { const idRegex = /^-1$|^[^-]\d+$/ const nameRegex = /^[A-Za-z0-9 -.'()]+$/ const { id, name } = opponent - if ( - !idRegex.test(id) || - !nameRegex.test(name) - ) { + if (!idRegex.test(id) || !nameRegex.test(name)) { throw new ValidationError( 'Invalid request', ValidationErrorCode.INVALID_OPPONENT diff --git a/src/common/reservation.ts b/src/common/reservation.ts index f3ac840..038b5b7 100644 --- a/src/common/reservation.ts +++ b/src/common/reservation.ts @@ -50,4 +50,16 @@ export class Reservation { RESERVATION_AVAILABLE_WITHIN_DAYS ) } + + public format(): unknown { + return { + opponent: this.opponent, + booked: this.booked, + possibleDates: this.possibleDates.map((date) => date.format()), + dateRange: { + start: this.dateRange.start.format(), + end: this.dateRange.end.format(), + } + } + } } diff --git a/src/common/runner.ts b/src/common/runner.ts index c61b57c..a036014 100644 --- a/src/common/runner.ts +++ b/src/common/runner.ts @@ -1,4 +1,4 @@ -import { Dayjs } from 'dayjs' +import dayjs, { Dayjs } from 'dayjs' import puppeteer, { Browser, BrowserConnectOptions, @@ -7,7 +7,7 @@ import puppeteer, { LaunchOptions, Page, } from 'puppeteer' - +import { Logger } from './logger' import { Opponent, Reservation } from './reservation' export class Runner { @@ -33,6 +33,7 @@ export class Runner { BrowserLaunchArgumentOptions & BrowserConnectOptions ): Promise { + Logger.debug('Launching browser'); this.browser = await puppeteer.launch(options) this.page = await this.browser?.newPage() await this.login() @@ -40,6 +41,7 @@ export class Runner { } private async login() { + Logger.debug('Logging in'); await this.page?.goto('https://squashcity.baanreserveren.nl/') await this.page ?.waitForSelector('input[name=username]') @@ -52,6 +54,7 @@ export class Runner { private async makeReservations(): Promise { for (let i = 0; i < this.reservations.length; i++) { + Logger.debug('Making reservation', this.reservations[i].format()); await this.makeReservation(this.reservations[i]) } @@ -66,24 +69,42 @@ export class Runner { await this.confirmReservation() reservation.booked = true } catch (err) { - console.error(err) + Logger.error('Error making reservation', reservation.format()); } } + private getLastVisibleDay(): Dayjs { + const lastDayOfMonth = dayjs().add(1, 'month').set('date', 0); + let daysToAdd = 0; + switch (lastDayOfMonth.day()) { + case 0: daysToAdd = 0; break; + default: daysToAdd = 7 - lastDayOfMonth.day(); break; + } + return lastDayOfMonth.add(daysToAdd, 'day'); + } + private async navigateToDay(date: Dayjs): Promise { + Logger.debug(`Navigating to ${date.format()}`); + + if (this.getLastVisibleDay().isBefore(date)) { + Logger.debug('Date is on different page, increase month'); + await this.page?.waitForSelector('td.month.next').then((d) => d?.click()); + } + await this.page ?.waitForSelector( - `td#cal_${date.get('year')}_${date.get('month') + 1}_${date.get('day')}` + `td#cal_${date.get('year')}_${date.get('month') + 1}_${date.get('date')}` ) .then((d) => d?.click()) await this.page?.waitForSelector( `td#cal_${date.get('year')}_${date.get('month') + 1}_${date.get( - 'day' + 'date' )}.selected` ) } private async selectAvailableTime(res: Reservation): Promise { + Logger.debug('Selecting available time', res.format()); let freeCourt: ElementHandle | null | undefined let i = 0 while (i < res.possibleDates.length && !freeCourt) { @@ -103,6 +124,7 @@ export class Runner { } private async selectOpponent(opponent: Opponent): Promise { + Logger.debug('Selecting opponent', opponent); const player2Search = await this.page?.waitForSelector( 'tr.res-make-player-2 > td > input' ) diff --git a/src/local.ts b/src/local.ts index 3d6d8a1..0a1a1de 100644 --- a/src/local.ts +++ b/src/local.ts @@ -1,9 +1,12 @@ import dayjs from 'dayjs' +import { v4 } from 'uuid' +import { Logger, LogLevel } from './common/logger' import { ReservationRequest } from './common/request' import { Reservation } from './common/reservation' import { Runner } from './common/runner' const run = async (request: ReservationRequest) => { + Logger.instantiate('local', v4(), LogLevel.DEBUG); const { username, password, dateRange, opponent } = request const reservation = new Reservation(dateRange, opponent) diff --git a/src/workers/requester/index.ts b/src/workers/requester/index.ts index 4c324e8..cf08041 100644 --- a/src/workers/requester/index.ts +++ b/src/workers/requester/index.ts @@ -1,4 +1,4 @@ -import { Worker } from "../types" +import { Worker } from '../types' export const work: Worker = async (): Promise => { return diff --git a/src/workers/requester/rollup.config.js b/src/workers/requester/rollup.config.js index 5234929..a2cd1b5 100644 --- a/src/workers/requester/rollup.config.js +++ b/src/workers/requester/rollup.config.js @@ -12,9 +12,5 @@ export default { format: 'cjs', sourcemap: true, }, - plugins: [ - typescript({ module: "esnext" }), - nodeResolve(), - commonjs(), - ] -} \ No newline at end of file + plugins: [typescript({ module: 'esnext' }), nodeResolve(), commonjs()], +} diff --git a/src/workers/scheduler/index.ts b/src/workers/scheduler/index.ts index 3180a05..3ab9563 100644 --- a/src/workers/scheduler/index.ts +++ b/src/workers/scheduler/index.ts @@ -12,18 +12,17 @@ export interface ScheduledReservationRequest { scheduledFor?: Dayjs } -export interface ReservationSchedulerResult { +export interface SchedulerResult { scheduledReservationRequest?: ScheduledReservationRequest } -export interface ReservationSchedulerInput - extends Omit { +export interface SchedulerInput extends Omit { dateRange: { start: string; end: string } } -export const work: Worker = async ( - payload: ReservationSchedulerInput, -): Promise => { +export const work: Worker = async ( + payload: SchedulerInput +): Promise => { Logger.instantiate('reservationScheduler', v4(), LogLevel.DEBUG) Logger.debug('Handling reservation', { payload }) let reservationRequest: ReservationRequest diff --git a/src/workers/scheduler/rollup.config.js b/src/workers/scheduler/rollup.config.js index 06a7f99..0dbceaa 100644 --- a/src/workers/scheduler/rollup.config.js +++ b/src/workers/scheduler/rollup.config.js @@ -12,9 +12,5 @@ export default { format: 'cjs', sourcemap: true, }, - plugins: [ - typescript({ module: "esnext" }), - nodeResolve(), - commonjs(), - ] -} \ No newline at end of file + plugins: [typescript({ module: 'esnext' }), nodeResolve(), commonjs()], +} diff --git a/src/workers/types.ts b/src/workers/types.ts index 3c35b49..dba5066 100644 --- a/src/workers/types.ts +++ b/src/workers/types.ts @@ -1 +1 @@ -export type Worker = (payload: I) => O | Promise \ No newline at end of file +export type Worker = (payload: I) => O | Promise diff --git a/tests/workers/scheduler.test.ts b/tests/workers/scheduler.test.ts index 354e83e..0ceb4fc 100644 --- a/tests/workers/scheduler.test.ts +++ b/tests/workers/scheduler.test.ts @@ -1,15 +1,15 @@ import dayjs from 'dayjs' import { ValidationError, ValidationErrorCode } from '../../src/common/request' -import { work, ReservationSchedulerInput, ReservationSchedulerResult } from '../../src/workers/scheduler' +import { work, SchedulerInput, SchedulerResult } from '../../src/workers/scheduler' jest.mock('../../src/common/logger') -describe('reservationScheduler', () => { +describe('scheduler', () => { test('should handle valid requests within reservation window', async () => { const start = dayjs().add(15, 'minutes') const end = start.add(15, 'minutes') - const payload: ReservationSchedulerInput = { + const payload: SchedulerInput = { username: "collin", password: "password", dateRange: { start: start.toISOString(), end: end.toISOString() }, @@ -17,7 +17,7 @@ describe('reservationScheduler', () => { } await expect(work(payload)).resolves - .toMatchObject({ + .toMatchObject({ scheduledReservationRequest: { reservationRequest: { username: 'collin', @@ -31,14 +31,14 @@ describe('reservationScheduler', () => { test('should handle valid requests outside of reservation window', async () => { const start = dayjs().add(15, 'days') const end = start.add(15, 'minutes') - const payload: ReservationSchedulerInput = { + const payload: SchedulerInput = { username: "collin", password: "password", dateRange: { start: start.toISOString(), end: end.toISOString() }, opponent: { id: "123", name: "collin" } } - await expect(work(payload)).resolves.toMatchObject({ + await expect(work(payload)).resolves.toMatchObject({ scheduledReservationRequest: { reservationRequest: { username: 'collin', @@ -55,7 +55,7 @@ describe('reservationScheduler', () => { const start = dayjs().add(15, 'days') const end = start.add(15, 'minutes') - const payload: ReservationSchedulerInput = { + const payload: SchedulerInput = { password: "password", dateRange: { start: start.toISOString(), end: end.toISOString() }, opponent: { id: "123", name: "collin" }