2021-11-28 13:06:52 +01:00
|
|
|
import dayjs from 'dayjs'
|
2021-11-24 00:00:22 +01:00
|
|
|
import isSameOrBefore from 'dayjs/plugin/isSameOrBefore'
|
|
|
|
|
dayjs.extend(isSameOrBefore)
|
|
|
|
|
|
|
|
|
|
import { DateRange, Opponent } from './reservation'
|
|
|
|
|
|
2021-11-28 19:40:16 +01:00
|
|
|
export interface ReservationRequest extends Record<string, unknown> {
|
2021-11-24 00:00:22 +01:00
|
|
|
username: string
|
|
|
|
|
password: string
|
2021-11-28 13:06:52 +01:00
|
|
|
dateRange: DateRange
|
2021-11-24 00:00:22 +01:00
|
|
|
opponent: Opponent
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export enum ValidationErrorCode {
|
2021-11-24 15:38:58 +01:00
|
|
|
UNDEFINED_REQUEST_BODY,
|
|
|
|
|
INVALID_JSON,
|
|
|
|
|
INVALID_REQUEST_BODY,
|
|
|
|
|
INVALID_DATE_RANGE,
|
|
|
|
|
INVALID_START_OR_END_DATE,
|
|
|
|
|
INVALID_OPPONENT,
|
2021-11-24 00:00:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export class ValidationError extends Error {
|
|
|
|
|
public readonly code: ValidationErrorCode
|
|
|
|
|
|
|
|
|
|
constructor(message: string, code: ValidationErrorCode) {
|
|
|
|
|
super(message)
|
|
|
|
|
this.code = code
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-11-24 15:38:58 +01:00
|
|
|
/**
|
|
|
|
|
* Validates an incoming request body and converts to ReservationRequest
|
|
|
|
|
* @param body String of request body
|
|
|
|
|
* @returns ReservationRequest
|
|
|
|
|
*/
|
2021-11-28 19:40:16 +01:00
|
|
|
export const validateStringRequest = (body: string): ReservationRequest => {
|
|
|
|
|
const request = validateRequestBody(body)
|
|
|
|
|
validateRequestDateRange(request.dateRange)
|
|
|
|
|
validateRequestOpponent(request.opponent)
|
|
|
|
|
return request
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const validateJSONRequest = (
|
|
|
|
|
body: Record<string, unknown>
|
|
|
|
|
): ReservationRequest => {
|
2021-11-24 00:00:22 +01:00
|
|
|
const request = validateRequestBody(body)
|
2021-11-24 15:38:58 +01:00
|
|
|
validateRequestDateRange(request.dateRange)
|
2021-11-24 00:00:22 +01:00
|
|
|
validateRequestOpponent(request.opponent)
|
|
|
|
|
return request
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-28 19:40:16 +01:00
|
|
|
const validateRequestBody = (
|
|
|
|
|
body?: string | Record<string, unknown>
|
|
|
|
|
): ReservationRequest => {
|
2021-11-24 00:00:22 +01:00
|
|
|
if (body === undefined) {
|
2021-11-28 13:06:52 +01:00
|
|
|
throw new ValidationError(
|
|
|
|
|
'Invalid request',
|
|
|
|
|
ValidationErrorCode.UNDEFINED_REQUEST_BODY
|
|
|
|
|
)
|
2021-11-24 00:00:22 +01:00
|
|
|
}
|
|
|
|
|
|
2021-11-28 19:40:16 +01:00
|
|
|
let jsonBody: ReservationRequest
|
|
|
|
|
if (typeof body === 'string') {
|
|
|
|
|
jsonBody = transformRequestBody(body)
|
|
|
|
|
} else {
|
|
|
|
|
const { username, password, dateRange, opponent } = body
|
|
|
|
|
jsonBody = {
|
|
|
|
|
username: username as string,
|
|
|
|
|
password: password as string,
|
|
|
|
|
dateRange: convertDateRangeStringToObject(
|
|
|
|
|
dateRange as { start: string; end: string }
|
|
|
|
|
),
|
|
|
|
|
opponent: opponent as Opponent,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-24 15:38:58 +01:00
|
|
|
const { username, password, opponent, dateRange } = jsonBody
|
2021-11-24 00:00:22 +01:00
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
!username ||
|
|
|
|
|
username.length < 1 ||
|
|
|
|
|
!password ||
|
|
|
|
|
password.length < 1 ||
|
2021-11-24 15:38:58 +01:00
|
|
|
!dateRange ||
|
|
|
|
|
!dateRange.start ||
|
|
|
|
|
!dateRange.end ||
|
2021-11-24 00:00:22 +01:00
|
|
|
(opponent && opponent.id && opponent.id.length < 1) ||
|
|
|
|
|
(opponent && opponent.name && opponent.name.length < 1)
|
|
|
|
|
) {
|
2021-11-28 13:06:52 +01:00
|
|
|
throw new ValidationError(
|
|
|
|
|
'Invalid request',
|
|
|
|
|
ValidationErrorCode.INVALID_REQUEST_BODY
|
|
|
|
|
)
|
2021-11-24 00:00:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return jsonBody
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const transformRequestBody = (body: string): ReservationRequest => {
|
2021-11-24 15:38:58 +01:00
|
|
|
let json
|
|
|
|
|
try {
|
|
|
|
|
json = JSON.parse(body)
|
|
|
|
|
} catch (err) {
|
2021-11-28 19:40:16 +01:00
|
|
|
console.error(err)
|
2021-11-28 13:06:52 +01:00
|
|
|
throw new ValidationError(
|
|
|
|
|
'Invalid request',
|
|
|
|
|
ValidationErrorCode.INVALID_JSON
|
|
|
|
|
)
|
2021-11-24 15:38:58 +01:00
|
|
|
}
|
2021-11-28 19:40:16 +01:00
|
|
|
const start = json.dateRange?.start ?? 'invalid'
|
|
|
|
|
const end = json.dateRange?.end ?? 'invalid'
|
|
|
|
|
const dateRange: DateRange = convertDateRangeStringToObject({ start, end })
|
2021-11-24 00:00:22 +01:00
|
|
|
return {
|
|
|
|
|
username: json.username,
|
|
|
|
|
password: json.password,
|
2021-11-24 15:38:58 +01:00
|
|
|
dateRange,
|
2021-11-28 13:06:52 +01:00
|
|
|
opponent: json.opponent,
|
2021-11-24 00:00:22 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-28 19:40:16 +01:00
|
|
|
const convertDateRangeStringToObject = ({
|
|
|
|
|
start,
|
|
|
|
|
end,
|
|
|
|
|
}: {
|
|
|
|
|
start: string
|
|
|
|
|
end: string
|
|
|
|
|
}): DateRange => ({ start: dayjs(start), end: dayjs(end) })
|
|
|
|
|
|
2021-11-24 15:38:58 +01:00
|
|
|
const validateRequestDateRange = (dateRange: DateRange): void => {
|
|
|
|
|
// checking that both dates are valid
|
|
|
|
|
const { start, end } = dateRange
|
|
|
|
|
if (!start.isValid() || !end.isValid()) {
|
2021-11-28 13:06:52 +01:00
|
|
|
throw new ValidationError(
|
|
|
|
|
'Invalid request',
|
|
|
|
|
ValidationErrorCode.INVALID_DATE_RANGE
|
|
|
|
|
)
|
2021-11-24 15:38:58 +01:00
|
|
|
}
|
2021-11-24 00:00:22 +01:00
|
|
|
|
2021-11-24 15:38:58 +01:00
|
|
|
// checking that:
|
|
|
|
|
// 1. start occurs after now
|
|
|
|
|
// 2. start occurs before or same as end
|
|
|
|
|
// 3. start and end fall on same YYYY/MM/DD
|
|
|
|
|
if (
|
|
|
|
|
!start.isAfter(dayjs()) ||
|
|
|
|
|
!start.isSameOrBefore(end) ||
|
|
|
|
|
start.format('YYYY MM DD') !== end.format('YYYY MM DD')
|
|
|
|
|
) {
|
2021-11-28 13:06:52 +01:00
|
|
|
throw new ValidationError(
|
|
|
|
|
'Invalid request',
|
|
|
|
|
ValidationErrorCode.INVALID_START_OR_END_DATE
|
|
|
|
|
)
|
2021-11-24 00:00:22 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const validateRequestOpponent = (opponent?: Opponent): void => {
|
|
|
|
|
if (!opponent) return
|
|
|
|
|
|
|
|
|
|
const { id, name } = opponent
|
|
|
|
|
if (
|
|
|
|
|
typeof id !== 'string' ||
|
|
|
|
|
typeof name !== 'string' ||
|
|
|
|
|
id.length < 1 ||
|
|
|
|
|
name.length < 1
|
|
|
|
|
) {
|
2021-11-28 13:06:52 +01:00
|
|
|
throw new ValidationError(
|
|
|
|
|
'Invalid request',
|
|
|
|
|
ValidationErrorCode.INVALID_OPPONENT
|
|
|
|
|
)
|
2021-11-24 00:00:22 +01:00
|
|
|
}
|
|
|
|
|
}
|