Compare commits
2 Commits
d20d981190
...
196cc8783b
Author | SHA1 | Date | |
---|---|---|---|
196cc8783b | |||
c54e517700 |
25
.gitea/workflows/run_tests.yaml
Normal file
25
.gitea/workflows/run_tests.yaml
Normal file
@ -0,0 +1,25 @@
|
||||
name: Run Jest tests
|
||||
|
||||
on:
|
||||
push:
|
||||
|
||||
jobs:
|
||||
run-tests:
|
||||
runs-on: ubuntu-22.04
|
||||
container:
|
||||
image: ghcr.io/catthehacker/ubuntu:act-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup up Node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
- name: Install Dependencies
|
||||
run: npm install
|
||||
|
||||
- name: Run Tests
|
||||
run: npm test
|
6
jest.config.js
Normal file
6
jest.config.js
Normal file
@ -0,0 +1,6 @@
|
||||
export default {
|
||||
transform: {
|
||||
"^.+\\.tsx?$": ["ts-jest", { useESM: true }]
|
||||
},
|
||||
extensionsToTreatAsEsm: [".ts"],
|
||||
};
|
3833
package-lock.json
generated
3833
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -3,8 +3,9 @@
|
||||
"version": "0.0.1",
|
||||
"description": "TypeScript client for the OwlBoard API",
|
||||
"main": "index.ts",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
"test": "jest"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -13,6 +14,12 @@
|
||||
"author": "Frederick Boniface",
|
||||
"license": "GPL-3.0",
|
||||
"devDependencies": {
|
||||
"@types/jest": "^29.5.14",
|
||||
"jest": "^29.7.0",
|
||||
"ts-jest": "^29.2.6",
|
||||
"typescript": "^5.6.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"uuid": "^11.1.0"
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +0,0 @@
|
||||
import { BaseOwlBoardClient } from "./client";
|
||||
|
||||
export class LocationReferenceClientV2 {
|
||||
private client: BaseOwlBoardClient;
|
||||
|
||||
constructor(client: BaseOwlBoardClient) {
|
||||
this.client = client;
|
||||
}
|
||||
}
|
23
src/clients/ReferenceClientV2.ts
Normal file
23
src/clients/ReferenceClientV2.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { validateReasonCode } from "../inputValidation/inputValidation";
|
||||
import { ReferenceV2_LocationReferenceCodes, ReferenceV2_LocationReferenceCodeType, ReferenceV2_ReasonCode } from "../types/reference/ReferenceTypesV2";
|
||||
import { BaseOwlBoardClient } from "./client";
|
||||
|
||||
export class ReferenceClientV2 {
|
||||
private client: BaseOwlBoardClient;
|
||||
|
||||
constructor(client: BaseOwlBoardClient) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
async defineReasonCode(reasonCode: number): Promise<ReferenceV2_ReasonCode> {
|
||||
validateReasonCode(reasonCode);
|
||||
const path = `/api/v2/ref/reasonCode/${reasonCode}`
|
||||
return this.client.makeRequest("GET", path);
|
||||
}
|
||||
|
||||
async lookupLocationReference(type: ReferenceV2_LocationReferenceCodeType, referenceCode: string): Promise<ReferenceV2_LocationReferenceCodes> {
|
||||
// Validation Required Here
|
||||
const path = `/api/v2/ref/locationCode/${type}/${referenceCode}`
|
||||
return this.client.makeRequest("GET", path);
|
||||
}
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
import { validateUuid } from "../inputValidation/inputValidation";
|
||||
import { UserV2_RegistrationResponse } from "../types/user/UserTypesV2";
|
||||
import { BaseOwlBoardClient } from "./client";
|
||||
|
||||
export class UserClientV2 {
|
||||
@ -6,4 +8,15 @@ export class UserClientV2 {
|
||||
constructor(client: BaseOwlBoardClient) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
async getUser(uuid: string): Promise<UserV2_RegistrationResponse> {
|
||||
validateUuid(uuid);
|
||||
const path = `/api/v2/user/${uuid}`;
|
||||
return this.client.makeRequest("GET", path);
|
||||
}
|
||||
|
||||
async checkAuth(): Promise<any> {
|
||||
const path = '/api/v2/checkAuth'
|
||||
return this.client.makeRequest("GET", path);
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
import { version } from "../constants";
|
||||
import { LdbClientV2 } from "./LdbClientV2";
|
||||
import { LocationReferenceClientV2 } from "./LocationReferenceClientV2";
|
||||
import { ReferenceClientV2 } from "./ReferenceClientV2";
|
||||
import { MiscClientV2 } from "./MiscClientV2";
|
||||
import { PisClientV2 } from "./PisClientV2";
|
||||
import { TrainClientV2 } from "./TrainClientV2";
|
||||
@ -80,7 +80,7 @@ export class OwlBoardClientV2 extends BaseOwlBoardClient {
|
||||
pis: PisClientV2;
|
||||
train: TrainClientV2;
|
||||
user: UserClientV2;
|
||||
locationReference: LocationReferenceClientV2;
|
||||
reference: ReferenceClientV2;
|
||||
misc: MiscClientV2;
|
||||
ldb: LdbClientV2;
|
||||
|
||||
@ -89,7 +89,7 @@ export class OwlBoardClientV2 extends BaseOwlBoardClient {
|
||||
this.pis = new PisClientV2(this);
|
||||
this.train = new TrainClientV2(this);
|
||||
this.user = new UserClientV2(this);
|
||||
this.locationReference = new LocationReferenceClientV2(this);
|
||||
this.reference = new ReferenceClientV2(this);
|
||||
this.misc = new MiscClientV2(this);
|
||||
this.ldb = new LdbClientV2(this);
|
||||
}
|
||||
|
110
src/inputValidation/inputValidation.test.ts
Normal file
110
src/inputValidation/inputValidation.test.ts
Normal file
@ -0,0 +1,110 @@
|
||||
|
||||
import { validateCrs,
|
||||
validatePisCode,
|
||||
validateReasonCode,
|
||||
validateTiploc,
|
||||
validateUuid } from "./inputValidation";
|
||||
import { ValidationError } from "../errors";
|
||||
|
||||
describe("PIS Validation Tests", () => {
|
||||
test("PIS inputs of the correct format should return true", () => {
|
||||
expect(validatePisCode(1234)).toBe(true);
|
||||
expect(validatePisCode("0999")).toBe(true);
|
||||
expect(validatePisCode(9343)).toBe(true);
|
||||
})
|
||||
|
||||
test("PIS inputs of an invalid format should throw a ValidationError", () => {
|
||||
expect(() => validatePisCode(21)).toThrow(ValidationError);
|
||||
expect(() => validatePisCode("hoo")).toThrow(ValidationError);
|
||||
expect(() => validatePisCode({test: "Value"})).toThrow(ValidationError);
|
||||
})
|
||||
})
|
||||
|
||||
describe("TIPLOC Validation Tests", () => {
|
||||
test("TIPLOC inputs of the correct format should return true", () => {
|
||||
expect(validateTiploc("elyy")).toBe(true);
|
||||
expect(validateTiploc("STROUD")).toBe(true);
|
||||
expect(validateTiploc("GLOSTER")).toBe(true);
|
||||
expect(validateTiploc("PaDtOn")).toBe(true);
|
||||
})
|
||||
|
||||
test("TIPLOC inputs of an invalid format should throw a ValidationError", () => {
|
||||
expect(() => validateTiploc("wey")).toThrow(ValidationError);
|
||||
expect(() => validateTiploc("PMS")).toThrow(ValidationError);
|
||||
expect(() => validateTiploc(89)).toThrow(ValidationError);
|
||||
expect(() => validateTiploc(["STROUD", "GLOSTER"])).toThrow(ValidationError);
|
||||
})
|
||||
})
|
||||
|
||||
describe("CRS Validation Tests", () => {
|
||||
test("CRS inputs of the correct format should return true", () => {
|
||||
expect(validateCrs("bth")).toBe(true);
|
||||
expect(validateCrs("BTH")).toBe(true);
|
||||
expect(validateCrs("PoA")).toBe(true);
|
||||
});
|
||||
|
||||
test("CRS inputs of an invalid format should throw a ValidationError", () => {
|
||||
expect(() => validateCrs("BATHSPA")).toThrow(ValidationError);
|
||||
expect(() => validateCrs("popo")).toThrow(ValidationError);
|
||||
expect(() => validateCrs("BT")).toThrow(ValidationError);
|
||||
})
|
||||
|
||||
test("CRS inputs that are not strings should throw ValidationError", () => {
|
||||
expect(() => validateCrs(34)).toThrow(ValidationError);
|
||||
expect(() => validateCrs([])).toThrow(ValidationError);
|
||||
expect(() => validateCrs({})).toThrow(ValidationError);
|
||||
expect(() => validateCrs(null)).toThrow(ValidationError);
|
||||
expect(() => validateCrs(undefined)).toThrow(ValidationError);
|
||||
})
|
||||
})
|
||||
|
||||
describe("UUID Validation Tests", () => {
|
||||
test("UUID inputs that are valid v4-UUIDs should return true", () => {
|
||||
expect(validateUuid("5c5db50b-91c8-440c-80af-afc7bb375b4b")).toBe(true);
|
||||
expect(validateUuid("9a9a3849-bbcb-452c-a0db-599a53cb27f3")).toBe(true);
|
||||
})
|
||||
|
||||
test("UUID inputs that are valid v1-UUIDs should return true", () => {
|
||||
expect(validateUuid("e7dd36f0-ff86-11ef-9cd2-0242ac120002")).toBe(true);
|
||||
expect(validateUuid("e052e8b6-ff87-11ef-9cd2-0242ac120002")).toBe(true);
|
||||
})
|
||||
|
||||
test("UUID inputs that are valid v7-UUIDs should return true", () => {
|
||||
expect(validateUuid("01958c36-8b96-7ff1-be2c-d1488273f5e0")).toBe(true);
|
||||
expect(validateUuid("01958c3a-f9d0-7c6a-b510-6de0dd36a49a")).toBe(true);
|
||||
})
|
||||
|
||||
test("UUID inputs that are a nil UUID should return true", () => {
|
||||
expect(validateUuid("00000000-0000-0000-0000-000000000000")).toBe(true);
|
||||
})
|
||||
|
||||
test("UUID inputs that are actually not UUIDs should throw ValidationError", () => {
|
||||
expect(() => validateUuid("thisisjustastring")).toThrow(ValidationError);
|
||||
expect(() => validateUuid(789)).toThrow(ValidationError);
|
||||
})
|
||||
})
|
||||
|
||||
describe("Reason Code Validation Tests", () => {
|
||||
test("Reason codes that are valid numbers or strings should return true", () => {
|
||||
expect(validateReasonCode("999")).toBe(true);
|
||||
expect(validateReasonCode("100")).toBe(true);
|
||||
expect(validateReasonCode(402)).toBe(true);
|
||||
expect(validateReasonCode(100)).toBe(true);
|
||||
expect(validateReasonCode(999)).toBe(true);
|
||||
})
|
||||
|
||||
test("Reason codes that are out of range numbers should throw ValidationError", () => {
|
||||
expect(() => validateReasonCode(99)).toThrow(ValidationError);
|
||||
expect(() => validateReasonCode("1000")).toThrow(ValidationError);
|
||||
expect(() => validateReasonCode(1000)).toThrow(ValidationError);
|
||||
expect(() => validateReasonCode("99")).toThrow(ValidationError);
|
||||
})
|
||||
|
||||
test("Reason code inputs that are non numeric values should throw ValidationError", () => {
|
||||
expect(() => validateReasonCode("19f")).toThrow(ValidationError);
|
||||
expect(() => validateReasonCode(null)).toThrow(ValidationError);
|
||||
expect(() => validateReasonCode(true)).toThrow(ValidationError);
|
||||
expect(() => validateReasonCode([])).toThrow(ValidationError);
|
||||
expect(() => validateReasonCode({})).toThrow(ValidationError);
|
||||
})
|
||||
})
|
@ -1,6 +1,13 @@
|
||||
import { ValidationError } from "../errors";
|
||||
import { validate as uuidlibValidate, version as uuidlibVersion } from 'uuid'
|
||||
|
||||
export function validatePisCode(code: string): boolean {
|
||||
export function validatePisCode(code: unknown): boolean {
|
||||
if (typeof code == "number") {
|
||||
code = code.toString();
|
||||
}
|
||||
if (typeof code !== "string") {
|
||||
throw new ValidationError("Invalid input: code must be a four digit number or string")
|
||||
}
|
||||
const codeRegex = /^\d{4}$/;
|
||||
if (!codeRegex.test(code)) {
|
||||
throw new ValidationError("Invalid input: code must be a four-character string consisting of only numerals");
|
||||
@ -8,18 +15,56 @@ export function validatePisCode(code: string): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
export function validateTiploc(tiploc: string): boolean {
|
||||
const tiplocRegex = /[a-zA-z0-9]{4,7}/;
|
||||
export function validateTiploc(tiploc: unknown): boolean {
|
||||
if (typeof tiploc !== "string") {
|
||||
throw new ValidationError("Invalid input: TIPLOC must be a string");
|
||||
}
|
||||
const tiplocRegex = /[a-zA-Z0-9]{4,7}/;
|
||||
if (!tiplocRegex.test(tiploc)) {
|
||||
throw new ValidationError("Invalid input: TIPLOC must be between four and seven characters");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export function validateCrs(crs: string): boolean {
|
||||
const crsRegex = /[a-zA-z]{3}/;
|
||||
export function validateCrs(crs: unknown): boolean {
|
||||
if (typeof crs !== "string") {
|
||||
throw new ValidationError("Invalid input: CRS/3ALPHA must be a string")
|
||||
}
|
||||
|
||||
const crsRegex = /^[a-zA-Z]{3}$/;
|
||||
if (!crsRegex.test(crs)) {
|
||||
throw new ValidationError("Invalid input: CRS/3ALPHA must be exactly three letters")
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export function validateUuid(uuid: unknown): boolean {
|
||||
if (typeof uuid !== "string") {
|
||||
throw new ValidationError("Invalid input: The UUID/api_key should be a string");
|
||||
}
|
||||
if (!uuidlibValidate(uuid)) {
|
||||
throw new ValidationError("Invalid input: The UUID/api_key is the expexted value")
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export function validateReasonCode(code: unknown): boolean {
|
||||
if (typeof code === "number") {
|
||||
// Ensure it's a 3-digit number (100-999)
|
||||
if (!Number.isInteger(code) || code < 100 || code > 999) {
|
||||
throw new ValidationError("Invalid input: Reason code must be a three-digit number (100-999).");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (typeof code === "string") {
|
||||
// Ensure it consists of exactly 3 numeric characters
|
||||
if (!/^\d{3}$/.test(code)) {
|
||||
throw new ValidationError("Invalid input: Reason code must be a string of three digits.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// If it's neither a number nor a string, throw an error
|
||||
throw new ValidationError("Invalid input: Reason code must be a number or a string of three digits.");
|
||||
}
|
22
src/types/reference/ReferenceTypesV2.ts
Normal file
22
src/types/reference/ReferenceTypesV2.ts
Normal file
@ -0,0 +1,22 @@
|
||||
// Location Reference codes are returned as an array containing one object
|
||||
interface ReferenceV2_LocationReferenceCodesObject {
|
||||
"3ALPHA": string;
|
||||
NLC: number;
|
||||
NLCDESC: string;
|
||||
STANOX: string;
|
||||
TIPLOC: string;
|
||||
UIC: string;
|
||||
};
|
||||
|
||||
export type ReferenceV2_LocationReferenceCodes = ReferenceV2_LocationReferenceCodesObject[];
|
||||
|
||||
// Reason codes are returned as an array containing one object
|
||||
interface ReferenceV2_ReasonCodeObject {
|
||||
code: string;
|
||||
lateReason: string;
|
||||
cancReason: string;
|
||||
}
|
||||
|
||||
export type ReferenceV2_ReasonCode = ReferenceV2_ReasonCodeObject[]
|
||||
|
||||
export type ReferenceV2_LocationReferenceCodeType = "tiploc" | "crs" | "stanox" | "nlc"
|
5
src/types/user/UserTypesV2.ts
Normal file
5
src/types/user/UserTypesV2.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export interface UserV2_RegistrationResponse {
|
||||
status: number;
|
||||
message: string;
|
||||
api_key: string;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user