Add getNearestStations to Reference client, along with types, validators and tests
All checks were successful
Testing / run-tests (push) Successful in 6m18s

This commit is contained in:
Fred Boniface 2025-09-09 20:46:43 +01:00
parent a600d1f91d
commit ca85bbd6be
4 changed files with 75 additions and 4 deletions

View File

@ -1,5 +1,5 @@
import { validateCrs, validateNlc, validateReasonCode, validateStanox, validateTiploc, validateStation } from "../inputValidation/inputValidation";
import { ReferenceV2_LocationReferenceCodes, ReferenceV2_LocationReferenceCodeType, ReferenceV2_ReasonCode } from "../types/reference/ReferenceTypesV2";
import { validateCrs, validateNlc, validateReasonCode, validateStanox, validateTiploc, validateStation, validateLatLon } from "../inputValidation/inputValidation";
import { ReferenceV2_LocationReferenceCodes, ReferenceV2_LocationReferenceCodeType, ReferenceV2_NearestStations, ReferenceV2_ReasonCode } from "../types/reference/ReferenceTypesV2";
import { BaseOwlBoardClient } from "./client";
export class ReferenceClientV2 {
@ -15,6 +15,12 @@ export class ReferenceClientV2 {
return this.client.makeRequest("GET", path);
}
async getNearestStations(latitude: number, longitude: number): Promise<ReferenceV2_NearestStations> {
validateLatLon(latitude, longitude);
const path = `/api/v2/live/station/nearest/${latitude}/${longitude}`
return this.client.makeRequest("GET", path);
}
async lookupLocationReference(type: ReferenceV2_LocationReferenceCodeType, referenceCode: string): Promise<ReferenceV2_LocationReferenceCodes> {
const validators: Record<ReferenceV2_LocationReferenceCodeType, (code: string) => boolean> = {
crs: validateCrs,

View File

@ -7,7 +7,8 @@ import { validateCrs,
validateHeadcode,
validateNlc,
validateStanox,
validateStation} from "./inputValidation";
validateStation,
validateLatLon} from "./inputValidation";
import { ValidationError } from "../errors";
describe("PIS Validation Tests", () => {
@ -228,3 +229,36 @@ describe("Headcode Validation Tests", () => {
expect(() => validateHeadcode(true)).toThrow(ValidationError);
})
})
describe("Latitude/Logitude Validation Tests", () => {
test("Valid Latitude/Logitude values should return true", () => {
expect(validateLatLon(51.5074, "-0.1278")).toBe(true);
expect(validateLatLon("51.5074", "-0.1278")).toBe(true);
expect(validateLatLon(0, 0)).toBe(true);
expect(validateLatLon("0", "0")).toBe(true);
expect(validateLatLon(90, 180)).toBe(true);
expect(validateLatLon(-90, -180)).toBe(true);
expect(validateLatLon(" 51.5074 ", " -0.1278 ")).toBe(true);
expect(validateLatLon(45.123456, -73.987654)).toBe(true);
expect(validateLatLon("89.9999999999", "179.9999999999")).toBe(true);
})
test("Invalid types should throw Validation Error", () => {
expect(() => validateLatLon(true, false)).toThrow(ValidationError);
expect(() => validateLatLon([], [])).toThrow(ValidationError);
expect(() => validateLatLon(null, null)).toThrow(ValidationError);
expect(() => validateLatLon("str", "ing")).toThrow(ValidationError);
expect(() => validateLatLon({}, {})).toThrow(ValidationError);
expect(() => validateLatLon(14.235, "whale")).toThrow(ValidationError);
expect(() => validateLatLon("", "")).toThrow(ValidationError);
})
test("Out of bound inputs should throw Validation Error", () => {
expect(() => validateLatLon(91, 0)).toThrow(ValidationError);
expect(() => validateLatLon(-91, 47)).toThrow(ValidationError);
expect(() => validateLatLon(12, 181)).toThrow(ValidationError);
expect(() => validateLatLon(78, -181)).toThrow(ValidationError);
expect(() => validateLatLon("90.0000001", "0")).toThrow(ValidationError);
expect(() => validateLatLon("0", "180.0000001")).toThrow(ValidationError);
})
})

View File

@ -126,3 +126,26 @@ export function validateHeadcode(headcode: unknown): boolean {
return true;
}
export function validateLatLon(lat: unknown, lon: unknown): boolean {
const toNumber = (val: unknown, label: string): number => {
if (typeof val === "number") return val;
if (typeof val === "string" && val.trim() !== "") {
const num = Number(val);
if (Number.isFinite(num)) return num;
}
throw new ValidationError(`Invalid input: ${label} must be a valid number`)
};
const latNum = toNumber(lat, "latitude");
const lonNum = toNumber(lon, "longitude");
if (latNum < -90 || latNum > 90) {
throw new ValidationError("Latitude must be between -90 and 90");
}
if (lonNum < -180 || lonNum > 180) {
throw new ValidationError("Longitude must be between -180 and 180");
}
return true;
}

View File

@ -19,4 +19,12 @@ interface ReferenceV2_ReasonCodeObject {
export type ReferenceV2_ReasonCode = ReferenceV2_ReasonCodeObject[]
export type ReferenceV2_LocationReferenceCodeType = "tiploc" | "crs" | "stanox" | "nlc" | "station"
export type ReferenceV2_LocationReferenceCodeType = "tiploc" | "crs" | "stanox" | "nlc" | "station"
interface ReferenceV2_NearestStationItem {
"3ALPHA": string;
NLCDESC: string;
miles: number; // Float
}
export type ReferenceV2_NearestStations = ReferenceV2_NearestStationItem[]