From dc639b1ca872c17f0380e57bfc27e7cbf47e5295 Mon Sep 17 00:00:00 2001 From: Brendan Chen Date: Thu, 10 Apr 2025 16:15:30 -0700 Subject: [PATCH 01/26] add parking repository loader interface --- src/loaders/ParkingRepositoryLoader.ts | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 src/loaders/ParkingRepositoryLoader.ts diff --git a/src/loaders/ParkingRepositoryLoader.ts b/src/loaders/ParkingRepositoryLoader.ts new file mode 100644 index 0000000..64e8e5c --- /dev/null +++ b/src/loaders/ParkingRepositoryLoader.ts @@ -0,0 +1,3 @@ +export interface ParkingRepositoryLoader { + fetchAndUpdateParkingStructures(): Promise; +} From 110df5501db470eb2780e1f9da7326254a81846e Mon Sep 17 00:00:00 2001 From: Brendan Chen Date: Thu, 10 Apr 2025 16:24:16 -0700 Subject: [PATCH 02/26] add chapman repository loader and buildParkingRepositoryLoaderIfExists function --- ...ChapmanTimedApiBasedParkingRepositoryLoader.ts | 14 ++++++++++++++ .../ParkingRepositoryLoader.ts | 0 .../buildParkingRepositoryLoaderIfExists.ts | 15 +++++++++++++++ 3 files changed, 29 insertions(+) create mode 100644 src/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoader.ts rename src/loaders/{ => ParkingRepositoryLoaders}/ParkingRepositoryLoader.ts (100%) create mode 100644 src/loaders/ParkingRepositoryLoaders/buildParkingRepositoryLoaderIfExists.ts diff --git a/src/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoader.ts b/src/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoader.ts new file mode 100644 index 0000000..0ff07ef --- /dev/null +++ b/src/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoader.ts @@ -0,0 +1,14 @@ +import { ParkingRepositoryLoader } from "./ParkingRepositoryLoader"; +import { ParkingGetterSetterRepository } from "../../repositories/ParkingGetterSetterRepository"; + +export class ChapmanTimedApiBasedParkingRepositoryLoader implements ParkingRepositoryLoader { + public static readonly id = "chapman-parking-loader"; + + constructor( + public repository: ParkingGetterSetterRepository + ) {} + + async fetchAndUpdateParkingStructures(): Promise { + // TODO + } +} diff --git a/src/loaders/ParkingRepositoryLoader.ts b/src/loaders/ParkingRepositoryLoaders/ParkingRepositoryLoader.ts similarity index 100% rename from src/loaders/ParkingRepositoryLoader.ts rename to src/loaders/ParkingRepositoryLoaders/ParkingRepositoryLoader.ts diff --git a/src/loaders/ParkingRepositoryLoaders/buildParkingRepositoryLoaderIfExists.ts b/src/loaders/ParkingRepositoryLoaders/buildParkingRepositoryLoaderIfExists.ts new file mode 100644 index 0000000..85a744f --- /dev/null +++ b/src/loaders/ParkingRepositoryLoaders/buildParkingRepositoryLoaderIfExists.ts @@ -0,0 +1,15 @@ +import { ParkingGetterSetterRepository } from "../../repositories/ParkingGetterSetterRepository"; +import { ChapmanTimedApiBasedParkingRepositoryLoader } from "./ChapmanTimedApiBasedParkingRepositoryLoader"; + +interface ParkingRepositoryBuilderArguments { + id: string; + repository: ParkingGetterSetterRepository; +} + +export function buildParkingRepositoryLoaderIfExists(args: ParkingRepositoryBuilderArguments) { + if (args.id === ChapmanTimedApiBasedParkingRepositoryLoader.id) { + return new ChapmanTimedApiBasedParkingRepositoryLoader(args.repository); + } + + return null; +} From b9810057ce9e60dcf491dfa3e5eca6896c4a8ea1 Mon Sep 17 00:00:00 2001 From: Brendan Chen Date: Thu, 10 Apr 2025 16:26:41 -0700 Subject: [PATCH 03/26] add test cases --- ...pmanTimedApiBasedParkingRepositoryLoader.test.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 test/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoader.test.ts diff --git a/test/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoader.test.ts b/test/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoader.test.ts new file mode 100644 index 0000000..5ab00b3 --- /dev/null +++ b/test/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoader.test.ts @@ -0,0 +1,13 @@ +import { describe, it } from "@jest/globals"; + +describe("ChapmanTimedApiBasedParkingRepositoryLoader", () => { + describe("fetchAndUpdateParkingStructures", () => { + it("should fetch and update parking successfully", async () => { + + }); + + it("throws ApiResponseError if data is incorrect", async () => { + + }) + }); +}); From 726af526857bf072088c6c7b4e26f2bf3dfe049a Mon Sep 17 00:00:00 2001 From: Brendan Chen Date: Thu, 10 Apr 2025 16:27:15 -0700 Subject: [PATCH 04/26] rename test file --- ...s => ChapmanTimedApiBasedParkingRepositoryLoaderTests.test.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test/loaders/ParkingRepositoryLoaders/{ChapmanTimedApiBasedParkingRepositoryLoader.test.ts => ChapmanTimedApiBasedParkingRepositoryLoaderTests.test.ts} (100%) diff --git a/test/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoader.test.ts b/test/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoaderTests.test.ts similarity index 100% rename from test/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoader.test.ts rename to test/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoaderTests.test.ts From aed005f7a74aa930639d0965ad9c7201ff74fa5a Mon Sep 17 00:00:00 2001 From: Brendan Chen Date: Thu, 10 Apr 2025 16:29:13 -0700 Subject: [PATCH 05/26] add successful JSON response for chapman parking structure data --- .../chapmanParkingStructureData.ts | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 test/jsonSnapshots/chapmanParkingStructureData/chapmanParkingStructureData.ts diff --git a/test/jsonSnapshots/chapmanParkingStructureData/chapmanParkingStructureData.ts b/test/jsonSnapshots/chapmanParkingStructureData/chapmanParkingStructureData.ts new file mode 100644 index 0000000..07199d7 --- /dev/null +++ b/test/jsonSnapshots/chapmanParkingStructureData/chapmanParkingStructureData.ts @@ -0,0 +1,92 @@ +export const chapmanParkingStructureData = { + "CenterOnLatitude": 33.793379, + "CenterOnLongitude": -117.853099, + "LatitudeZoom": 0.0045, + "LongitudeZoom": 0.0045, + "Structures": [ + { + "Address": "300 E Walnut, Orange, CA 92867", + "Capacity": 871, + "CurrentCount": 211, + "HdpiDetailImage": "https://webfarm.chapman.edu/ParkingService/lastinger-hdpi.png", + "Latitude": 33.7945513, + "LdpiDetailImage": "https://webfarm.chapman.edu/ParkingService/lastinger-ldpi.png", + "Levels": [ + { + "Capacity": 871, + "CurrentCount": 211, + "FriendlyName": "All Levels", + "SystemName": "All" + }, + { + "Capacity": 401, + "CurrentCount": 26, + "FriendlyName": "B1 Level", + "SystemName": "L1" + }, + { + "Capacity": 470, + "CurrentCount": 185, + "FriendlyName": "B2 Level", + "SystemName": "L2" + } + ], + "Longitude": -117.8518707, + "MdpiDetailImage": "https://webfarm.chapman.edu/ParkingService/lastinger-mdpi.png", + "Name": "Anderson Structure", + "Timestamp": "/Date(1744327687147-0700)/", + "XhdpiDetailImage": "https://webfarm.chapman.edu/ParkingService/lastinger-xhdpi.png" + }, + { + "Address": "200 W Sycamore Ave, Orange, CA 92866-1053", + "Capacity": 692, + "CurrentCount": 282, + "HdpiDetailImage": "https://webfarm.chapman.edu/ParkingService/barrera-hdpi.png", + "Latitude": 33.792937, + "LdpiDetailImage": "https://webfarm.chapman.edu/ParkingService/barrera-ldpi.png", + "Levels": [ + { + "Capacity": 692, + "CurrentCount": 282, + "FriendlyName": "All Levels", + "SystemName": "All" + }, + { + "Capacity": 85, + "CurrentCount": 0, + "FriendlyName": "Level 1", + "SystemName": "L1" + }, + { + "Capacity": 145, + "CurrentCount": 21, + "FriendlyName": "Level 2", + "SystemName": "L2" + }, + { + "Capacity": 150, + "CurrentCount": 50, + "FriendlyName": "Level 3", + "SystemName": "L3" + }, + { + "Capacity": 150, + "CurrentCount": 79, + "FriendlyName": "Level 4", + "SystemName": "L4" + }, + { + "Capacity": 162, + "CurrentCount": 132, + "FriendlyName": "Level 5", + "SystemName": "L5" + } + ], + "Longitude": -117.854782, + "MdpiDetailImage": "https://webfarm.chapman.edu/ParkingService/barrera-mdpi.png", + "Name": "Barrera", + "Timestamp": "/Date(1744327647113-0700)/", + "XhdpiDetailImage": "https://webfarm.chapman.edu/ParkingService/barrera-xhdpi.png" + } + ] +} From 45df97f15c1c8384438b5d9deaa7b97f505a0e0d Mon Sep 17 00:00:00 2001 From: Brendan Chen Date: Thu, 10 Apr 2025 16:36:02 -0700 Subject: [PATCH 06/26] add methods to generate an ID based on address --- ...pmanTimedApiBasedParkingRepositoryLoader.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoader.ts b/src/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoader.ts index 0ff07ef..2bb88ab 100644 --- a/src/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoader.ts +++ b/src/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoader.ts @@ -1,5 +1,6 @@ import { ParkingRepositoryLoader } from "./ParkingRepositoryLoader"; import { ParkingGetterSetterRepository } from "../../repositories/ParkingGetterSetterRepository"; +import { createHash } from "node:crypto"; export class ChapmanTimedApiBasedParkingRepositoryLoader implements ParkingRepositoryLoader { public static readonly id = "chapman-parking-loader"; @@ -11,4 +12,21 @@ export class ChapmanTimedApiBasedParkingRepositoryLoader implements ParkingRepos async fetchAndUpdateParkingStructures(): Promise { // TODO } + + private normalizeAddress(address: string): string { + return address + .toLowerCase() + .split(/\s+/) + .filter(part => part.length > 0) + .join(' '); + } + + private generateId(address: string): string { + const normalized = this.normalizeAddress(address); + const hash = createHash('sha256') + .update(normalized) + .digest('hex'); + + return hash.substring(0, 12); + } } From 8c810c522476b163ae8703a2e051b533c8ae085b Mon Sep 17 00:00:00 2001 From: Brendan Chen Date: Thu, 10 Apr 2025 16:36:28 -0700 Subject: [PATCH 07/26] add setup of loader --- ...piBasedParkingRepositoryLoaderTests.test.ts | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/test/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoaderTests.test.ts b/test/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoaderTests.test.ts index 5ab00b3..15914a8 100644 --- a/test/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoaderTests.test.ts +++ b/test/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoaderTests.test.ts @@ -1,8 +1,22 @@ -import { describe, it } from "@jest/globals"; +import { beforeEach, describe, it } from "@jest/globals"; +import { + ChapmanTimedApiBasedParkingRepositoryLoader +} from "../../../src/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoader"; +import { InMemoryParkingRepository } from "../../../src/repositories/InMemoryParkingRepository"; +import { resetGlobalFetchMockJson } from "../../testHelpers/fetchMockHelpers"; describe("ChapmanTimedApiBasedParkingRepositoryLoader", () => { + let loader: ChapmanTimedApiBasedParkingRepositoryLoader; + + beforeEach(() => { + loader = new ChapmanTimedApiBasedParkingRepositoryLoader( + new InMemoryParkingRepository(), + ); + resetGlobalFetchMockJson(); + }); + describe("fetchAndUpdateParkingStructures", () => { - it("should fetch and update parking successfully", async () => { + it("fetches and update parking structures with unique IDs", async () => { }); From f6d8253719cc98288a691227b471e12b65357bd7 Mon Sep 17 00:00:00 2001 From: Brendan Chen Date: Thu, 10 Apr 2025 16:40:54 -0700 Subject: [PATCH 08/26] make generateId public --- .../ChapmanTimedApiBasedParkingRepositoryLoader.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoader.ts b/src/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoader.ts index 2bb88ab..8f43ef5 100644 --- a/src/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoader.ts +++ b/src/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoader.ts @@ -13,7 +13,7 @@ export class ChapmanTimedApiBasedParkingRepositoryLoader implements ParkingRepos // TODO } - private normalizeAddress(address: string): string { + private static normalizeAddress(address: string): string { return address .toLowerCase() .split(/\s+/) @@ -21,12 +21,12 @@ export class ChapmanTimedApiBasedParkingRepositoryLoader implements ParkingRepos .join(' '); } - private generateId(address: string): string { + public static generateId(address: string): string { const normalized = this.normalizeAddress(address); const hash = createHash('sha256') .update(normalized) .digest('hex'); - return hash.substring(0, 12); + return hash.substring(0, 32); } } From 3d6dc1e7b66bba93602c004f43b0b3d68a70f209 Mon Sep 17 00:00:00 2001 From: Brendan Chen Date: Thu, 10 Apr 2025 16:43:04 -0700 Subject: [PATCH 09/26] add pass to check validity of parking structures generated --- ...iBasedParkingRepositoryLoaderTests.test.ts | 40 ++++++++++++++++++- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/test/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoaderTests.test.ts b/test/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoaderTests.test.ts index 15914a8..582d7e8 100644 --- a/test/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoaderTests.test.ts +++ b/test/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoaderTests.test.ts @@ -1,9 +1,13 @@ -import { beforeEach, describe, it } from "@jest/globals"; +import { beforeEach, describe, expect, it } from "@jest/globals"; import { ChapmanTimedApiBasedParkingRepositoryLoader } from "../../../src/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoader"; import { InMemoryParkingRepository } from "../../../src/repositories/InMemoryParkingRepository"; -import { resetGlobalFetchMockJson } from "../../testHelpers/fetchMockHelpers"; +import { resetGlobalFetchMockJson, updateGlobalFetchMockJson } from "../../testHelpers/fetchMockHelpers"; +import { + chapmanParkingStructureData +} from "../../jsonSnapshots/chapmanParkingStructureData/chapmanParkingStructureData"; +import { IParkingStructure } from "../../../src/entities/ParkingRepositoryEntities"; describe("ChapmanTimedApiBasedParkingRepositoryLoader", () => { let loader: ChapmanTimedApiBasedParkingRepositoryLoader; @@ -17,7 +21,39 @@ describe("ChapmanTimedApiBasedParkingRepositoryLoader", () => { describe("fetchAndUpdateParkingStructures", () => { it("fetches and update parking structures with unique IDs", async () => { + updateGlobalFetchMockJson(chapmanParkingStructureData); + await loader.fetchAndUpdateParkingStructures(); + + let expectedStructures: IParkingStructure[] = [ + { + address: "300 E Walnut, Orange, CA 92867", + capacity: 211, + spotsAvailable: 871, + coordinates: { + latitude: 33.7945513, + longitude: -117.8518707, + }, + name: "Anderson Structure", + id: "", + }, + { + address: "", + capacity: 0, + spotsAvailable: 0, + coordinates: { + latitude: 0, + longitude: 0 + }, + name: "Barrera", + id: "", + } + ]; + expectedStructures[0].id = ChapmanTimedApiBasedParkingRepositoryLoader.generateId(expectedStructures[0].address); + expectedStructures[1].id = ChapmanTimedApiBasedParkingRepositoryLoader.generateId(expectedStructures[1].address); + + const structuresFromLoader = await loader.repository.getParkingStructures(); + expect(structuresFromLoader).toEqual(expectedStructures); }); it("throws ApiResponseError if data is incorrect", async () => { From 263607fc69302ba41022190893af48c48723612b Mon Sep 17 00:00:00 2001 From: Brendan Chen Date: Thu, 10 Apr 2025 16:44:57 -0700 Subject: [PATCH 10/26] move ApiResponseError to separate file --- src/loaders/ApiBasedShuttleRepositoryLoader.ts | 8 +------- src/loaders/ApiResponseError.ts | 6 ++++++ test/loaders/ApiBasedShuttleRepositoryLoaderTests.test.ts | 3 ++- 3 files changed, 9 insertions(+), 8 deletions(-) create mode 100644 src/loaders/ApiResponseError.ts diff --git a/src/loaders/ApiBasedShuttleRepositoryLoader.ts b/src/loaders/ApiBasedShuttleRepositoryLoader.ts index ff145cf..45d510f 100644 --- a/src/loaders/ApiBasedShuttleRepositoryLoader.ts +++ b/src/loaders/ApiBasedShuttleRepositoryLoader.ts @@ -2,13 +2,7 @@ import { ShuttleGetterSetterRepository } from "../repositories/ShuttleGetterSett import { IEta, IRoute, IShuttle, IStop } from "../entities/ShuttleRepositoryEntities"; import { ShuttleRepositoryLoader } from "./ShuttleRepositoryLoader"; import { IEntityWithId } from "../entities/SharedEntities"; - -export class ApiResponseError extends Error { - constructor(message: string) { - super(message); - this.name = "ApiResponseError"; - } -} +import { ApiResponseError } from "./ApiResponseError"; /** * Class which can load data into a repository from the diff --git a/src/loaders/ApiResponseError.ts b/src/loaders/ApiResponseError.ts new file mode 100644 index 0000000..5db1f99 --- /dev/null +++ b/src/loaders/ApiResponseError.ts @@ -0,0 +1,6 @@ +export class ApiResponseError extends Error { + constructor(message: string) { + super(message); + this.name = "ApiResponseError"; + } +} diff --git a/test/loaders/ApiBasedShuttleRepositoryLoaderTests.test.ts b/test/loaders/ApiBasedShuttleRepositoryLoaderTests.test.ts index 7ff472d..a522fa6 100644 --- a/test/loaders/ApiBasedShuttleRepositoryLoaderTests.test.ts +++ b/test/loaders/ApiBasedShuttleRepositoryLoaderTests.test.ts @@ -1,5 +1,5 @@ import { beforeEach, describe, expect, it, jest } from "@jest/globals"; -import { ApiBasedShuttleRepositoryLoader, ApiResponseError } from "../../src/loaders/ApiBasedShuttleRepositoryLoader"; +import { ApiBasedShuttleRepositoryLoader } from "../../src/loaders/ApiBasedShuttleRepositoryLoader"; import { UnoptimizedInMemoryShuttleRepository } from "../../src/repositories/UnoptimizedInMemoryShuttleRepository"; import { fetchRouteDataSuccessfulResponse } from "../jsonSnapshots/fetchRouteData/fetchRouteDataSuccessfulResponse"; import { @@ -15,6 +15,7 @@ import { updateGlobalFetchMockJson, updateGlobalFetchMockJsonToThrowSyntaxError } from "../testHelpers/fetchMockHelpers"; +import { ApiResponseError } from "../../src/loaders/ApiResponseError"; async function assertAsyncCallbackThrowsApiResponseError(callback: () => Promise) { await expect(callback).rejects.toThrow(ApiResponseError); From b6856a98a24ba7655e06a4deed05e92cd4bddb6e Mon Sep 17 00:00:00 2001 From: Brendan Chen Date: Thu, 10 Apr 2025 16:46:20 -0700 Subject: [PATCH 11/26] move ApiResponseError assertion function to separate file --- test/loaders/ApiBasedShuttleRepositoryLoaderTests.test.ts | 6 +----- .../assertAsyncCallbackThrowsApiResponseError.ts | 6 ++++++ 2 files changed, 7 insertions(+), 5 deletions(-) create mode 100644 test/testHelpers/assertAsyncCallbackThrowsApiResponseError.ts diff --git a/test/loaders/ApiBasedShuttleRepositoryLoaderTests.test.ts b/test/loaders/ApiBasedShuttleRepositoryLoaderTests.test.ts index a522fa6..a9b20f2 100644 --- a/test/loaders/ApiBasedShuttleRepositoryLoaderTests.test.ts +++ b/test/loaders/ApiBasedShuttleRepositoryLoaderTests.test.ts @@ -15,11 +15,7 @@ import { updateGlobalFetchMockJson, updateGlobalFetchMockJsonToThrowSyntaxError } from "../testHelpers/fetchMockHelpers"; -import { ApiResponseError } from "../../src/loaders/ApiResponseError"; - -async function assertAsyncCallbackThrowsApiResponseError(callback: () => Promise) { - await expect(callback).rejects.toThrow(ApiResponseError); -} +import { assertAsyncCallbackThrowsApiResponseError } from "../testHelpers/assertAsyncCallbackThrowsApiResponseError"; describe("ApiBasedRepositoryLoader", () => { let loader: ApiBasedShuttleRepositoryLoader; diff --git a/test/testHelpers/assertAsyncCallbackThrowsApiResponseError.ts b/test/testHelpers/assertAsyncCallbackThrowsApiResponseError.ts new file mode 100644 index 0000000..d1107a9 --- /dev/null +++ b/test/testHelpers/assertAsyncCallbackThrowsApiResponseError.ts @@ -0,0 +1,6 @@ +import { expect } from "@jest/globals"; +import { ApiResponseError } from "../../src/loaders/ApiResponseError"; + +export async function assertAsyncCallbackThrowsApiResponseError(callback: () => Promise) { + await expect(callback).rejects.toThrow(ApiResponseError); +} From e25a96f7049505395960d53cdbf4332fca98bb57 Mon Sep 17 00:00:00 2001 From: Brendan Chen Date: Thu, 10 Apr 2025 16:46:59 -0700 Subject: [PATCH 12/26] add test for ApiResponseError --- ...nTimedApiBasedParkingRepositoryLoaderTests.test.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/test/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoaderTests.test.ts b/test/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoaderTests.test.ts index 582d7e8..73cf7e2 100644 --- a/test/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoaderTests.test.ts +++ b/test/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoaderTests.test.ts @@ -3,11 +3,16 @@ import { ChapmanTimedApiBasedParkingRepositoryLoader } from "../../../src/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoader"; import { InMemoryParkingRepository } from "../../../src/repositories/InMemoryParkingRepository"; -import { resetGlobalFetchMockJson, updateGlobalFetchMockJson } from "../../testHelpers/fetchMockHelpers"; +import { + resetGlobalFetchMockJson, + updateGlobalFetchMockJson, + updateGlobalFetchMockJsonToThrowSyntaxError +} from "../../testHelpers/fetchMockHelpers"; import { chapmanParkingStructureData } from "../../jsonSnapshots/chapmanParkingStructureData/chapmanParkingStructureData"; import { IParkingStructure } from "../../../src/entities/ParkingRepositoryEntities"; +import { assertAsyncCallbackThrowsApiResponseError } from "../../testHelpers/assertAsyncCallbackThrowsApiResponseError"; describe("ChapmanTimedApiBasedParkingRepositoryLoader", () => { let loader: ChapmanTimedApiBasedParkingRepositoryLoader; @@ -57,7 +62,11 @@ describe("ChapmanTimedApiBasedParkingRepositoryLoader", () => { }); it("throws ApiResponseError if data is incorrect", async () => { + updateGlobalFetchMockJsonToThrowSyntaxError(); + await assertAsyncCallbackThrowsApiResponseError(async () => { + await loader.fetchAndUpdateParkingStructures(); + }); }) }); }); From f67e3f13f5480a2857bfa9397eed6d58fe4387c0 Mon Sep 17 00:00:00 2001 From: Brendan Chen Date: Thu, 10 Apr 2025 16:58:30 -0700 Subject: [PATCH 13/26] update test data for correctness --- ...dApiBasedParkingRepositoryLoaderTests.test.ts | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/test/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoaderTests.test.ts b/test/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoaderTests.test.ts index 73cf7e2..f96191e 100644 --- a/test/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoaderTests.test.ts +++ b/test/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoaderTests.test.ts @@ -33,8 +33,8 @@ describe("ChapmanTimedApiBasedParkingRepositoryLoader", () => { let expectedStructures: IParkingStructure[] = [ { address: "300 E Walnut, Orange, CA 92867", - capacity: 211, - spotsAvailable: 871, + capacity: 871, + spotsAvailable: 211, coordinates: { latitude: 33.7945513, longitude: -117.8518707, @@ -43,12 +43,12 @@ describe("ChapmanTimedApiBasedParkingRepositoryLoader", () => { id: "", }, { - address: "", - capacity: 0, - spotsAvailable: 0, + address: "200 W Sycamore Ave, Orange, CA 92866-1053", + capacity: 692, + spotsAvailable: 282, coordinates: { - latitude: 0, - longitude: 0 + latitude: 33.792937, + longitude: -117.854782 }, name: "Barrera", id: "", @@ -69,4 +69,6 @@ describe("ChapmanTimedApiBasedParkingRepositoryLoader", () => { }); }) }); + + // TODO: Add timing tests and start/stop methods }); From 6998ff529c7e7a675834dac5380cf0f6a900dad1 Mon Sep 17 00:00:00 2001 From: Brendan Chen Date: Thu, 10 Apr 2025 16:59:09 -0700 Subject: [PATCH 14/26] implement method to retrieve parking structures --- ...manTimedApiBasedParkingRepositoryLoader.ts | 45 ++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/src/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoader.ts b/src/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoader.ts index 8f43ef5..390f904 100644 --- a/src/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoader.ts +++ b/src/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoader.ts @@ -1,16 +1,59 @@ import { ParkingRepositoryLoader } from "./ParkingRepositoryLoader"; import { ParkingGetterSetterRepository } from "../../repositories/ParkingGetterSetterRepository"; import { createHash } from "node:crypto"; +import { ApiResponseError } from "../ApiResponseError"; +import { IParkingStructure } from "../../entities/ParkingRepositoryEntities"; + +class ApiParseError extends Error { + constructor(message: string) { + super(message); + this.name = "ApiParseError"; + } +} export class ChapmanTimedApiBasedParkingRepositoryLoader implements ParkingRepositoryLoader { public static readonly id = "chapman-parking-loader"; + private readonly fetchUrl = "https://webfarm.chapman.edu/ParkingService/ParkingService/counts"; constructor( public repository: ParkingGetterSetterRepository ) {} async fetchAndUpdateParkingStructures(): Promise { - // TODO + let json: any; + + try { + const response = await fetch(this.fetchUrl); + json = await response.json(); + } catch(e: any) { + throw new ApiResponseError(e.message); + } + + try { + if (typeof json.Structures === "object") { + const parkingStructures: IParkingStructure[] = json.Structures.map((jsonStructure: any) => { + const structureToReturn: IParkingStructure = { + capacity: jsonStructure.Capacity, + coordinates: { + latitude: jsonStructure.Latitude, + longitude: jsonStructure.Longitude, + }, + id: ChapmanTimedApiBasedParkingRepositoryLoader.generateId(jsonStructure.Address), + name: jsonStructure.Name, + spotsAvailable: jsonStructure.CurrentCount, + address: jsonStructure.Address + } + + return structureToReturn; + }); + + await Promise.all(parkingStructures.map(async (structure: IParkingStructure) => { + this.repository.addOrUpdateParkingStructure(structure); + })); + } + } catch(e: any) { + throw new ApiParseError(e.message); + } } private static normalizeAddress(address: string): string { From 2a01d007a940237cb1e3f0e8fa2943dafed916c3 Mon Sep 17 00:00:00 2001 From: Brendan Chen Date: Fri, 11 Apr 2025 16:11:27 -0700 Subject: [PATCH 15/26] refactor parking structure construction into separate method --- ...manTimedApiBasedParkingRepositoryLoader.ts | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/src/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoader.ts b/src/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoader.ts index 390f904..8682559 100644 --- a/src/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoader.ts +++ b/src/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoader.ts @@ -31,24 +31,10 @@ export class ChapmanTimedApiBasedParkingRepositoryLoader implements ParkingRepos try { if (typeof json.Structures === "object") { - const parkingStructures: IParkingStructure[] = json.Structures.map((jsonStructure: any) => { - const structureToReturn: IParkingStructure = { - capacity: jsonStructure.Capacity, - coordinates: { - latitude: jsonStructure.Latitude, - longitude: jsonStructure.Longitude, - }, - id: ChapmanTimedApiBasedParkingRepositoryLoader.generateId(jsonStructure.Address), - name: jsonStructure.Name, - spotsAvailable: jsonStructure.CurrentCount, - address: jsonStructure.Address - } - - return structureToReturn; - }); + const parkingStructures: IParkingStructure[] = json.Structures.map(this.constructIParkingStructureFromJson); await Promise.all(parkingStructures.map(async (structure: IParkingStructure) => { - this.repository.addOrUpdateParkingStructure(structure); + await this.repository.addOrUpdateParkingStructure(structure); })); } } catch(e: any) { @@ -56,6 +42,22 @@ export class ChapmanTimedApiBasedParkingRepositoryLoader implements ParkingRepos } } + private constructIParkingStructureFromJson(jsonStructure: any) { + const structureToReturn: IParkingStructure = { + capacity: jsonStructure.Capacity, + coordinates: { + latitude: jsonStructure.Latitude, + longitude: jsonStructure.Longitude, + }, + id: ChapmanTimedApiBasedParkingRepositoryLoader.generateId(jsonStructure.Address), + name: jsonStructure.Name, + spotsAvailable: jsonStructure.CurrentCount, + address: jsonStructure.Address + } + + return structureToReturn; + } + private static normalizeAddress(address: string): string { return address .toLowerCase() From c250e3ae5c846cdbca3541e4dab9a78d32ebbe12 Mon Sep 17 00:00:00 2001 From: Brendan Chen Date: Fri, 11 Apr 2025 16:12:37 -0700 Subject: [PATCH 16/26] bind fetchAndUpdateParkingStructures to class --- .../ChapmanTimedApiBasedParkingRepositoryLoader.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoader.ts b/src/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoader.ts index 8682559..7c5ce9e 100644 --- a/src/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoader.ts +++ b/src/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoader.ts @@ -17,7 +17,9 @@ export class ChapmanTimedApiBasedParkingRepositoryLoader implements ParkingRepos constructor( public repository: ParkingGetterSetterRepository - ) {} + ) { + this.fetchAndUpdateParkingStructures = this.fetchAndUpdateParkingStructures.bind(this); + } async fetchAndUpdateParkingStructures(): Promise { let json: any; From 49b823890f37ca3615ab729c1be8bde1bd1ba050 Mon Sep 17 00:00:00 2001 From: Brendan Chen Date: Fri, 11 Apr 2025 16:13:36 -0700 Subject: [PATCH 17/26] remove "Timed" from class name to prepare for subclassing --- ...s => ChapmanApiBasedParkingRepositoryLoader.ts} | 4 ++-- .../buildParkingRepositoryLoaderIfExists.ts | 6 +++--- ...medApiBasedParkingRepositoryLoaderTests.test.ts | 14 +++++++------- 3 files changed, 12 insertions(+), 12 deletions(-) rename src/loaders/ParkingRepositoryLoaders/{ChapmanTimedApiBasedParkingRepositoryLoader.ts => ChapmanApiBasedParkingRepositoryLoader.ts} (92%) diff --git a/src/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoader.ts b/src/loaders/ParkingRepositoryLoaders/ChapmanApiBasedParkingRepositoryLoader.ts similarity index 92% rename from src/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoader.ts rename to src/loaders/ParkingRepositoryLoaders/ChapmanApiBasedParkingRepositoryLoader.ts index 7c5ce9e..b66b31a 100644 --- a/src/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoader.ts +++ b/src/loaders/ParkingRepositoryLoaders/ChapmanApiBasedParkingRepositoryLoader.ts @@ -11,7 +11,7 @@ class ApiParseError extends Error { } } -export class ChapmanTimedApiBasedParkingRepositoryLoader implements ParkingRepositoryLoader { +export class ChapmanApiBasedParkingRepositoryLoader implements ParkingRepositoryLoader { public static readonly id = "chapman-parking-loader"; private readonly fetchUrl = "https://webfarm.chapman.edu/ParkingService/ParkingService/counts"; @@ -51,7 +51,7 @@ export class ChapmanTimedApiBasedParkingRepositoryLoader implements ParkingRepos latitude: jsonStructure.Latitude, longitude: jsonStructure.Longitude, }, - id: ChapmanTimedApiBasedParkingRepositoryLoader.generateId(jsonStructure.Address), + id: ChapmanApiBasedParkingRepositoryLoader.generateId(jsonStructure.Address), name: jsonStructure.Name, spotsAvailable: jsonStructure.CurrentCount, address: jsonStructure.Address diff --git a/src/loaders/ParkingRepositoryLoaders/buildParkingRepositoryLoaderIfExists.ts b/src/loaders/ParkingRepositoryLoaders/buildParkingRepositoryLoaderIfExists.ts index 85a744f..cea713b 100644 --- a/src/loaders/ParkingRepositoryLoaders/buildParkingRepositoryLoaderIfExists.ts +++ b/src/loaders/ParkingRepositoryLoaders/buildParkingRepositoryLoaderIfExists.ts @@ -1,5 +1,5 @@ import { ParkingGetterSetterRepository } from "../../repositories/ParkingGetterSetterRepository"; -import { ChapmanTimedApiBasedParkingRepositoryLoader } from "./ChapmanTimedApiBasedParkingRepositoryLoader"; +import { ChapmanApiBasedParkingRepositoryLoader } from "./ChapmanApiBasedParkingRepositoryLoader"; interface ParkingRepositoryBuilderArguments { id: string; @@ -7,8 +7,8 @@ interface ParkingRepositoryBuilderArguments { } export function buildParkingRepositoryLoaderIfExists(args: ParkingRepositoryBuilderArguments) { - if (args.id === ChapmanTimedApiBasedParkingRepositoryLoader.id) { - return new ChapmanTimedApiBasedParkingRepositoryLoader(args.repository); + if (args.id === ChapmanApiBasedParkingRepositoryLoader.id) { + return new ChapmanApiBasedParkingRepositoryLoader(args.repository); } return null; diff --git a/test/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoaderTests.test.ts b/test/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoaderTests.test.ts index f96191e..012d130 100644 --- a/test/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoaderTests.test.ts +++ b/test/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoaderTests.test.ts @@ -1,7 +1,7 @@ import { beforeEach, describe, expect, it } from "@jest/globals"; import { - ChapmanTimedApiBasedParkingRepositoryLoader -} from "../../../src/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoader"; + ChapmanApiBasedParkingRepositoryLoader +} from "../../../src/loaders/ParkingRepositoryLoaders/ChapmanApiBasedParkingRepositoryLoader"; import { InMemoryParkingRepository } from "../../../src/repositories/InMemoryParkingRepository"; import { resetGlobalFetchMockJson, @@ -14,11 +14,11 @@ import { import { IParkingStructure } from "../../../src/entities/ParkingRepositoryEntities"; import { assertAsyncCallbackThrowsApiResponseError } from "../../testHelpers/assertAsyncCallbackThrowsApiResponseError"; -describe("ChapmanTimedApiBasedParkingRepositoryLoader", () => { - let loader: ChapmanTimedApiBasedParkingRepositoryLoader; +describe("ChapmanApiBasedParkingRepositoryLoader", () => { + let loader: ChapmanApiBasedParkingRepositoryLoader; beforeEach(() => { - loader = new ChapmanTimedApiBasedParkingRepositoryLoader( + loader = new ChapmanApiBasedParkingRepositoryLoader( new InMemoryParkingRepository(), ); resetGlobalFetchMockJson(); @@ -54,8 +54,8 @@ describe("ChapmanTimedApiBasedParkingRepositoryLoader", () => { id: "", } ]; - expectedStructures[0].id = ChapmanTimedApiBasedParkingRepositoryLoader.generateId(expectedStructures[0].address); - expectedStructures[1].id = ChapmanTimedApiBasedParkingRepositoryLoader.generateId(expectedStructures[1].address); + expectedStructures[0].id = ChapmanApiBasedParkingRepositoryLoader.generateId(expectedStructures[0].address); + expectedStructures[1].id = ChapmanApiBasedParkingRepositoryLoader.generateId(expectedStructures[1].address); const structuresFromLoader = await loader.repository.getParkingStructures(); expect(structuresFromLoader).toEqual(expectedStructures); From 16dce328875c5437b92dc8f01f725df9f504286c Mon Sep 17 00:00:00 2001 From: Brendan Chen Date: Fri, 11 Apr 2025 16:43:46 -0700 Subject: [PATCH 18/26] add a RepositoryLoader interface defining a fetchAndUpdateAll method --- src/loaders/ApiBasedShuttleRepositoryLoader.ts | 11 +++++++++++ src/loaders/RepositoryLoader.ts | 3 +++ src/loaders/ShuttleRepositoryLoader.ts | 4 +++- 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 src/loaders/RepositoryLoader.ts diff --git a/src/loaders/ApiBasedShuttleRepositoryLoader.ts b/src/loaders/ApiBasedShuttleRepositoryLoader.ts index 45d510f..e02bed4 100644 --- a/src/loaders/ApiBasedShuttleRepositoryLoader.ts +++ b/src/loaders/ApiBasedShuttleRepositoryLoader.ts @@ -28,6 +28,17 @@ export class ApiBasedShuttleRepositoryLoader implements ShuttleRepositoryLoader return ids; } + public async fetchAndUpdateAll() { + await this.fetchAndUpdateRouteDataForSystem(); + await this.fetchAndUpdateStopAndPolylineDataForRoutesInSystem(); + await this.fetchAndUpdateShuttleDataForSystem(); + + // Because ETA method doesn't support pruning yet, + // add a call to the clear method here + await this.repository.clearEtaData(); + await this.fetchAndUpdateEtaDataForExistingStopsForSystem(); + } + public async fetchAndUpdateRouteDataForSystem() { const systemId = this.passioSystemId; const routeIdsToPrune = await this.constructExistingEntityIdSet(async () => { diff --git a/src/loaders/RepositoryLoader.ts b/src/loaders/RepositoryLoader.ts new file mode 100644 index 0000000..668aa26 --- /dev/null +++ b/src/loaders/RepositoryLoader.ts @@ -0,0 +1,3 @@ +export interface RepositoryLoader { + fetchAndUpdateAll(): Promise; +} diff --git a/src/loaders/ShuttleRepositoryLoader.ts b/src/loaders/ShuttleRepositoryLoader.ts index 588d06f..100cecc 100644 --- a/src/loaders/ShuttleRepositoryLoader.ts +++ b/src/loaders/ShuttleRepositoryLoader.ts @@ -1,4 +1,6 @@ -export interface ShuttleRepositoryLoader { +import { RepositoryLoader } from "./RepositoryLoader"; + +export interface ShuttleRepositoryLoader extends RepositoryLoader { fetchAndUpdateRouteDataForSystem(): Promise; fetchAndUpdateStopAndPolylineDataForRoutesInSystem(): Promise; fetchAndUpdateShuttleDataForSystem(): Promise; From d1a47baea6f3c1ecb5e6fe916c296f75ecf318c9 Mon Sep 17 00:00:00 2001 From: Brendan Chen Date: Fri, 11 Apr 2025 16:44:54 -0700 Subject: [PATCH 19/26] implement fetchAndUpdateAll for parking repository loader --- .../ChapmanApiBasedParkingRepositoryLoader.ts | 4 ++++ .../ParkingRepositoryLoaders/ParkingRepositoryLoader.ts | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/loaders/ParkingRepositoryLoaders/ChapmanApiBasedParkingRepositoryLoader.ts b/src/loaders/ParkingRepositoryLoaders/ChapmanApiBasedParkingRepositoryLoader.ts index b66b31a..f922c5b 100644 --- a/src/loaders/ParkingRepositoryLoaders/ChapmanApiBasedParkingRepositoryLoader.ts +++ b/src/loaders/ParkingRepositoryLoaders/ChapmanApiBasedParkingRepositoryLoader.ts @@ -21,6 +21,10 @@ export class ChapmanApiBasedParkingRepositoryLoader implements ParkingRepository this.fetchAndUpdateParkingStructures = this.fetchAndUpdateParkingStructures.bind(this); } + async fetchAndUpdateAll() { + await this.fetchAndUpdateParkingStructures(); + } + async fetchAndUpdateParkingStructures(): Promise { let json: any; diff --git a/src/loaders/ParkingRepositoryLoaders/ParkingRepositoryLoader.ts b/src/loaders/ParkingRepositoryLoaders/ParkingRepositoryLoader.ts index 64e8e5c..9068221 100644 --- a/src/loaders/ParkingRepositoryLoaders/ParkingRepositoryLoader.ts +++ b/src/loaders/ParkingRepositoryLoaders/ParkingRepositoryLoader.ts @@ -1,3 +1,5 @@ -export interface ParkingRepositoryLoader { +import { RepositoryLoader } from "../RepositoryLoader"; + +export interface ParkingRepositoryLoader extends RepositoryLoader { fetchAndUpdateParkingStructures(): Promise; } From a0e0c19ca37509bbd11aaba53d68f35a345b2386 Mon Sep 17 00:00:00 2001 From: Brendan Chen Date: Fri, 11 Apr 2025 16:52:27 -0700 Subject: [PATCH 20/26] update timed loader to use class composition --- src/entities/InterchangeSystem.ts | 21 ++++++++---- ...er.ts => TimedApiBasedRepositoryLoader.ts} | 19 +++-------- ...imedApiBasedRepositoryLoaderTests.test.ts} | 33 ++++++++++--------- 3 files changed, 35 insertions(+), 38 deletions(-) rename src/loaders/{TimedApiBasedShuttleRepositoryLoader.ts => TimedApiBasedRepositoryLoader.ts} (59%) rename test/loaders/{TimedApiBasedShuttleRepositoryLoaderTests.test.ts => TimedApiBasedRepositoryLoaderTests.test.ts} (57%) diff --git a/src/entities/InterchangeSystem.ts b/src/entities/InterchangeSystem.ts index abd045f..6bd743a 100644 --- a/src/entities/InterchangeSystem.ts +++ b/src/entities/InterchangeSystem.ts @@ -1,6 +1,5 @@ -import { ShuttleRepositoryLoader } from "../loaders/ShuttleRepositoryLoader"; import { ETANotificationScheduler } from "../notifications/schedulers/ETANotificationScheduler"; -import { TimedApiBasedShuttleRepositoryLoader } from "../loaders/TimedApiBasedShuttleRepositoryLoader"; +import { TimedApiBasedRepositoryLoader } from "../loaders/TimedApiBasedRepositoryLoader"; import { UnoptimizedInMemoryShuttleRepository } from "../repositories/UnoptimizedInMemoryShuttleRepository"; import { RedisNotificationRepository } from "../repositories/RedisNotificationRepository"; import { NotificationRepository } from "../repositories/NotificationRepository"; @@ -27,7 +26,7 @@ export class InterchangeSystem { constructor( public name: string, public id: string, - public shuttleDataLoader: ShuttleRepositoryLoader, + public shuttleTimedDataLoader: TimedApiBasedRepositoryLoader, public shuttleRepository: ShuttleGetterSetterRepository, public notificationScheduler: ETANotificationScheduler, public notificationRepository: NotificationRepository, @@ -43,12 +42,15 @@ export class InterchangeSystem { args: InterchangeSystemBuilderArguments, ) { const shuttleRepository = new UnoptimizedInMemoryShuttleRepository(); - const shuttleDataLoader = new TimedApiBasedShuttleRepositoryLoader( + const shuttleDataLoader = new ApiBasedShuttleRepositoryLoader( args.passioSystemId, args.id, shuttleRepository ); - await shuttleDataLoader.start(); + const timedShuttleDataLoader = new TimedApiBasedRepositoryLoader( + shuttleDataLoader, + ); + await timedShuttleDataLoader.start(); const notificationRepository = new RedisNotificationRepository(); await notificationRepository.connect(); @@ -62,7 +64,7 @@ export class InterchangeSystem { return new InterchangeSystem( args.name, args.id, - shuttleDataLoader, + timedShuttleDataLoader, shuttleRepository, notificationScheduler, notificationRepository, @@ -84,6 +86,11 @@ export class InterchangeSystem { args.id, shuttleRepository ); + // Note that this loader should not be started, + // so the test data doesn't get overwritten + const timedShuttleLoader = new TimedApiBasedRepositoryLoader( + shuttleDataLoader, + ); const notificationRepository = new InMemoryNotificationRepository(); const notificationScheduler = new ETANotificationScheduler( @@ -96,7 +103,7 @@ export class InterchangeSystem { return new InterchangeSystem( args.name, args.id, - shuttleDataLoader, + timedShuttleLoader, shuttleRepository, notificationScheduler, notificationRepository, diff --git a/src/loaders/TimedApiBasedShuttleRepositoryLoader.ts b/src/loaders/TimedApiBasedRepositoryLoader.ts similarity index 59% rename from src/loaders/TimedApiBasedShuttleRepositoryLoader.ts rename to src/loaders/TimedApiBasedRepositoryLoader.ts index 15a7d59..5add5a7 100644 --- a/src/loaders/TimedApiBasedShuttleRepositoryLoader.ts +++ b/src/loaders/TimedApiBasedRepositoryLoader.ts @@ -1,5 +1,4 @@ -import { ShuttleGetterSetterRepository } from "../repositories/ShuttleGetterSetterRepository"; -import { ApiBasedShuttleRepositoryLoader } from "./ApiBasedShuttleRepositoryLoader"; +import { RepositoryLoader } from "./RepositoryLoader"; // Ideas to break this into smaller pieces in the future: // Have one repository data loader running for each supported system @@ -15,18 +14,15 @@ import { ApiBasedShuttleRepositoryLoader } from "./ApiBasedShuttleRepositoryLoad // - OrderedStops: reload every few minutes // - Systems: reload once a day -export class TimedApiBasedShuttleRepositoryLoader extends ApiBasedShuttleRepositoryLoader { +export class TimedApiBasedRepositoryLoader { private shouldBeRunning: boolean = false; private timer: any; readonly timeout = 10000; constructor( - public passioSystemId: string, - public systemIdForConstructedData: string, - repository: ShuttleGetterSetterRepository, + public loader: RepositoryLoader, ) { - super(passioSystemId, systemIdForConstructedData, repository); this.startFetchDataAndUpdate = this.startFetchDataAndUpdate.bind(this); } @@ -48,14 +44,7 @@ export class TimedApiBasedShuttleRepositoryLoader extends ApiBasedShuttleReposit if (!this.shouldBeRunning) return; try { - await this.fetchAndUpdateRouteDataForSystem(); - await this.fetchAndUpdateStopAndPolylineDataForRoutesInSystem(); - await this.fetchAndUpdateShuttleDataForSystem(); - - // Because ETA method doesn't support pruning yet, - // add a call to the clear method here - await this.repository.clearEtaData(); - await this.fetchAndUpdateEtaDataForExistingStopsForSystem(); + await this.loader.fetchAndUpdateAll(); } catch (e) { console.error(e); } diff --git a/test/loaders/TimedApiBasedShuttleRepositoryLoaderTests.test.ts b/test/loaders/TimedApiBasedRepositoryLoaderTests.test.ts similarity index 57% rename from test/loaders/TimedApiBasedShuttleRepositoryLoaderTests.test.ts rename to test/loaders/TimedApiBasedRepositoryLoaderTests.test.ts index 80a8c4d..accafd2 100644 --- a/test/loaders/TimedApiBasedShuttleRepositoryLoaderTests.test.ts +++ b/test/loaders/TimedApiBasedRepositoryLoaderTests.test.ts @@ -1,10 +1,11 @@ import { afterEach, beforeAll, beforeEach, describe, expect, it, jest } from "@jest/globals"; -import { TimedApiBasedShuttleRepositoryLoader } from "../../src/loaders/TimedApiBasedShuttleRepositoryLoader"; +import { TimedApiBasedRepositoryLoader } from "../../src/loaders/TimedApiBasedRepositoryLoader"; import { resetGlobalFetchMockJson } from "../testHelpers/fetchMockHelpers"; import { UnoptimizedInMemoryShuttleRepository } from "../../src/repositories/UnoptimizedInMemoryShuttleRepository"; +import { ApiBasedShuttleRepositoryLoader } from "../../src/loaders/ApiBasedShuttleRepositoryLoader"; describe("TimedApiBasedRepositoryLoader", () => { - let loader: TimedApiBasedShuttleRepositoryLoader; + let timedLoader: TimedApiBasedRepositoryLoader; let spies: any; beforeAll(() => { @@ -15,17 +16,17 @@ describe("TimedApiBasedRepositoryLoader", () => { beforeEach(() => { resetGlobalFetchMockJson(); - loader = new TimedApiBasedShuttleRepositoryLoader( + const mockLoader = new ApiBasedShuttleRepositoryLoader( "1", "1", - new UnoptimizedInMemoryShuttleRepository() + new UnoptimizedInMemoryShuttleRepository(), + ); + timedLoader = new TimedApiBasedRepositoryLoader( + mockLoader, ); spies = { - fetchAndUpdateRouteDataForSystem: jest.spyOn(loader, 'fetchAndUpdateRouteDataForSystem'), - fetchAndUpdateStopAndPolylineDataForRoutesInSystem: jest.spyOn(loader, 'fetchAndUpdateStopAndPolylineDataForRoutesInSystem'), - fetchAndUpdateShuttleDataForSystem: jest.spyOn(loader, 'fetchAndUpdateShuttleDataForSystem'), - fetchAndUpdateEtaDataForExistingStopsForSystem: jest.spyOn(loader, 'fetchAndUpdateEtaDataForExistingStopsForSystem') + fetchAndUpdateAll: jest.spyOn(mockLoader, 'fetchAndUpdateAll'), }; Object.values(spies).forEach((spy: any) => { @@ -40,20 +41,20 @@ describe("TimedApiBasedRepositoryLoader", () => { describe("start", () => { it("should update internal state, call data fetching methods, and start a timer", async () => { - await loader.start(); - expect(loader["shouldBeRunning"]).toBe(true); + await timedLoader.start(); + expect(timedLoader["shouldBeRunning"]).toBe(true); Object.values(spies).forEach((spy: any) => { expect(spy).toHaveBeenCalled(); }); - expect(setTimeout).toHaveBeenCalledWith(expect.any(Function), loader.timeout); - expect(loader.timeout).not.toBeUndefined(); + expect(setTimeout).toHaveBeenCalledWith(expect.any(Function), timedLoader.timeout); + expect(timedLoader.timeout).not.toBeUndefined(); }); it("does nothing if timer is already running", async () => { - await loader.start(); - await loader.start(); + await timedLoader.start(); + await timedLoader.start(); Object.values(spies).forEach((spy: any) => { expect(spy).toHaveBeenCalledTimes(1); @@ -63,8 +64,8 @@ describe("TimedApiBasedRepositoryLoader", () => { describe("stop", () => { it("should update internal state", async () => { - loader.stop(); - expect(loader['shouldBeRunning']).toBe(false); + timedLoader.stop(); + expect(timedLoader['shouldBeRunning']).toBe(false); }); }); }); From 6cc20a4a6a3e5b23a71bb0646d8ee85e318a7cc8 Mon Sep 17 00:00:00 2001 From: Brendan Chen Date: Fri, 11 Apr 2025 16:53:45 -0700 Subject: [PATCH 21/26] remove redundant TODO --- .../ChapmanTimedApiBasedParkingRepositoryLoaderTests.test.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoaderTests.test.ts b/test/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoaderTests.test.ts index 012d130..93448ef 100644 --- a/test/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoaderTests.test.ts +++ b/test/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoaderTests.test.ts @@ -69,6 +69,4 @@ describe("ChapmanApiBasedParkingRepositoryLoader", () => { }); }) }); - - // TODO: Add timing tests and start/stop methods }); From 44095e711a09c0882580b8360252d6317c11ac98 Mon Sep 17 00:00:00 2001 From: Brendan Chen Date: Fri, 11 Apr 2025 16:57:47 -0700 Subject: [PATCH 22/26] make timeoutMs a settable property --- src/loaders/TimedApiBasedRepositoryLoader.ts | 21 +++++-------------- ...TimedApiBasedRepositoryLoaderTests.test.ts | 4 ++-- 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/src/loaders/TimedApiBasedRepositoryLoader.ts b/src/loaders/TimedApiBasedRepositoryLoader.ts index 5add5a7..63d869f 100644 --- a/src/loaders/TimedApiBasedRepositoryLoader.ts +++ b/src/loaders/TimedApiBasedRepositoryLoader.ts @@ -1,27 +1,16 @@ import { RepositoryLoader } from "./RepositoryLoader"; -// Ideas to break this into smaller pieces in the future: -// Have one repository data loader running for each supported system -// Each data loader independently updates data based on frequency of usage - -// Notes on this: we only need to reload ETA data frequently -// Other data can be reloaded periodically -// Detailed list: -// - ETA: reload frequently or switch to write-through approach -// - Shuttles: reload every minute -// - Routes: reload every few minutes -// - Stops: reload every few minutes -// - OrderedStops: reload every few minutes -// - Systems: reload once a day +// To break down timed loading in the future: +// Add flags to the repository indicating which data users are subscribed to +// In the loader's `fetchAll` method, check flags and update only needed data export class TimedApiBasedRepositoryLoader { private shouldBeRunning: boolean = false; private timer: any; - readonly timeout = 10000; - constructor( public loader: RepositoryLoader, + public timeoutMs: number = 10000, ) { this.startFetchDataAndUpdate = this.startFetchDataAndUpdate.bind(this); } @@ -49,6 +38,6 @@ export class TimedApiBasedRepositoryLoader { console.error(e); } - this.timer = setTimeout(this.startFetchDataAndUpdate, this.timeout); + this.timer = setTimeout(this.startFetchDataAndUpdate, this.timeoutMs); } } diff --git a/test/loaders/TimedApiBasedRepositoryLoaderTests.test.ts b/test/loaders/TimedApiBasedRepositoryLoaderTests.test.ts index accafd2..e62f6ad 100644 --- a/test/loaders/TimedApiBasedRepositoryLoaderTests.test.ts +++ b/test/loaders/TimedApiBasedRepositoryLoaderTests.test.ts @@ -48,8 +48,8 @@ describe("TimedApiBasedRepositoryLoader", () => { expect(spy).toHaveBeenCalled(); }); - expect(setTimeout).toHaveBeenCalledWith(expect.any(Function), timedLoader.timeout); - expect(timedLoader.timeout).not.toBeUndefined(); + expect(setTimeout).toHaveBeenCalledWith(expect.any(Function), timedLoader.timeoutMs); + expect(timedLoader.timeoutMs).not.toBeUndefined(); }); it("does nothing if timer is already running", async () => { From 463f5cd58279c0cf3f3cf65521e1d3945c6c04ba Mon Sep 17 00:00:00 2001 From: Brendan Chen Date: Fri, 11 Apr 2025 16:58:09 -0700 Subject: [PATCH 23/26] make timeoutMs a read only property (set only in constructor) --- src/loaders/TimedApiBasedRepositoryLoader.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/loaders/TimedApiBasedRepositoryLoader.ts b/src/loaders/TimedApiBasedRepositoryLoader.ts index 63d869f..8daf3be 100644 --- a/src/loaders/TimedApiBasedRepositoryLoader.ts +++ b/src/loaders/TimedApiBasedRepositoryLoader.ts @@ -10,7 +10,7 @@ export class TimedApiBasedRepositoryLoader { constructor( public loader: RepositoryLoader, - public timeoutMs: number = 10000, + public readonly timeoutMs: number = 10000, ) { this.startFetchDataAndUpdate = this.startFetchDataAndUpdate.bind(this); } From 50350d8c494db09ae352bdd7ccf13481ba490879 Mon Sep 17 00:00:00 2001 From: Brendan Chen Date: Fri, 11 Apr 2025 17:08:20 -0700 Subject: [PATCH 24/26] add test for ApiBasedShuttleRepositoryLoader.fetchAndUpdateAll --- ...iBasedShuttleRepositoryLoaderTests.test.ts | 30 +++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/test/loaders/ApiBasedShuttleRepositoryLoaderTests.test.ts b/test/loaders/ApiBasedShuttleRepositoryLoaderTests.test.ts index a9b20f2..ee93aa3 100644 --- a/test/loaders/ApiBasedShuttleRepositoryLoaderTests.test.ts +++ b/test/loaders/ApiBasedShuttleRepositoryLoaderTests.test.ts @@ -1,4 +1,4 @@ -import { beforeEach, describe, expect, it, jest } from "@jest/globals"; +import { afterEach, beforeEach, describe, expect, it, jest } from "@jest/globals"; import { ApiBasedShuttleRepositoryLoader } from "../../src/loaders/ApiBasedShuttleRepositoryLoader"; import { UnoptimizedInMemoryShuttleRepository } from "../../src/repositories/UnoptimizedInMemoryShuttleRepository"; import { fetchRouteDataSuccessfulResponse } from "../jsonSnapshots/fetchRouteData/fetchRouteDataSuccessfulResponse"; @@ -17,7 +17,7 @@ import { } from "../testHelpers/fetchMockHelpers"; import { assertAsyncCallbackThrowsApiResponseError } from "../testHelpers/assertAsyncCallbackThrowsApiResponseError"; -describe("ApiBasedRepositoryLoader", () => { +describe("ApiBasedShuttleRepositoryLoader", () => { let loader: ApiBasedShuttleRepositoryLoader; beforeEach(() => { @@ -25,7 +25,33 @@ describe("ApiBasedRepositoryLoader", () => { resetGlobalFetchMockJson(); }); + afterEach(() => { + jest.clearAllMocks(); + }); + const systemId = "1"; + + describe("fetchAndUpdateAll", () => { + it("calls all the correct methods", async () => { + const spies = { + fetchAndUpdateRouteDataForSystem: jest.spyOn(loader, "fetchAndUpdateRouteDataForSystem"), + fetchAndUpdateStopAndPolylineDataForRoutesInSystem: jest.spyOn(loader, "fetchAndUpdateStopAndPolylineDataForRoutesInSystem"), + fetchAndUpdateShuttleDataForSystem: jest.spyOn(loader, "fetchAndUpdateShuttleDataForSystem"), + fetchAndUpdateEtaDataForExistingStopsForSystem: jest.spyOn(loader, "fetchAndUpdateEtaDataForExistingStopsForSystem"), + }; + + Object.values(spies).forEach((spy: any) => { + spy.mockResolvedValue(undefined); + }); + + await loader.fetchAndUpdateAll(); + + Object.values(spies).forEach((spy: any) => { + expect(spy).toHaveBeenCalled(); + }); + }); + }); + describe("fetchAndUpdateRouteDataForSystem", () => { it("updates route data in repository if response received", async () => { // Arrange From 0e3c12bebca643d766d7230108c4f3b824c5cf7c Mon Sep 17 00:00:00 2001 From: Brendan Chen Date: Fri, 11 Apr 2025 17:09:52 -0700 Subject: [PATCH 25/26] add test for ChapmanApiBasedParkingRepositoryLoader.fetchAndUpdateAll --- ...BasedParkingRepositoryLoaderTests.test.ts} | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) rename test/loaders/ParkingRepositoryLoaders/{ChapmanTimedApiBasedParkingRepositoryLoaderTests.test.ts => ChapmanApiBasedParkingRepositoryLoaderTests.test.ts} (82%) diff --git a/test/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoaderTests.test.ts b/test/loaders/ParkingRepositoryLoaders/ChapmanApiBasedParkingRepositoryLoaderTests.test.ts similarity index 82% rename from test/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoaderTests.test.ts rename to test/loaders/ParkingRepositoryLoaders/ChapmanApiBasedParkingRepositoryLoaderTests.test.ts index 93448ef..b817d98 100644 --- a/test/loaders/ParkingRepositoryLoaders/ChapmanTimedApiBasedParkingRepositoryLoaderTests.test.ts +++ b/test/loaders/ParkingRepositoryLoaders/ChapmanApiBasedParkingRepositoryLoaderTests.test.ts @@ -1,4 +1,4 @@ -import { beforeEach, describe, expect, it } from "@jest/globals"; +import { beforeEach, describe, expect, it, jest } from "@jest/globals"; import { ChapmanApiBasedParkingRepositoryLoader } from "../../../src/loaders/ParkingRepositoryLoaders/ChapmanApiBasedParkingRepositoryLoader"; @@ -24,6 +24,26 @@ describe("ChapmanApiBasedParkingRepositoryLoader", () => { resetGlobalFetchMockJson(); }); + describe("fetchAndUpdateAll", () => { + it("calls all the correct methods", async () => { + const spies = { + fetchAndUpdateParkingStructures: jest.spyOn(loader, "fetchAndUpdateParkingStructures"), + }; + + Object.values(spies).forEach((spy: any) => { + spy.mockResolvedValue(undefined); + }); + + await loader.fetchAndUpdateAll(); + + Object.values(spies).forEach((spy: any) => { + expect(spy).toHaveBeenCalled(); + }); + }); + }); + + + describe("fetchAndUpdateParkingStructures", () => { it("fetches and update parking structures with unique IDs", async () => { updateGlobalFetchMockJson(chapmanParkingStructureData); From c9aa2c401f54a3c638fe6cc93bc20e835e67fdf0 Mon Sep 17 00:00:00 2001 From: Brendan Chen Date: Fri, 11 Apr 2025 17:13:39 -0700 Subject: [PATCH 26/26] restructure parking and shuttle repository loaders --- src/entities/InterchangeSystem.ts | 2 +- src/index.ts | 2 +- .../ChapmanApiBasedParkingRepositoryLoader.ts | 0 .../ParkingRepositoryLoader.ts | 0 .../buildParkingRepositoryLoaderIfExists.ts | 0 .../ApiBasedShuttleRepositoryLoader.ts | 8 ++++---- .../{ => shuttle}/ShuttleRepositoryLoader.ts | 2 +- .../{ => shuttle}/loadShuttleTestData.ts | 6 +++--- .../TimedApiBasedRepositoryLoaderTests.test.ts | 2 +- ...piBasedParkingRepositoryLoaderTests.test.ts | 2 +- ...piBasedShuttleRepositoryLoaderTests.test.ts | 18 +++++++++--------- 11 files changed, 21 insertions(+), 21 deletions(-) rename src/loaders/{ParkingRepositoryLoaders => parking}/ChapmanApiBasedParkingRepositoryLoader.ts (100%) rename src/loaders/{ParkingRepositoryLoaders => parking}/ParkingRepositoryLoader.ts (100%) rename src/loaders/{ParkingRepositoryLoaders => parking}/buildParkingRepositoryLoaderIfExists.ts (100%) rename src/loaders/{ => shuttle}/ApiBasedShuttleRepositoryLoader.ts (97%) rename src/loaders/{ => shuttle}/ShuttleRepositoryLoader.ts (87%) rename src/loaders/{ => shuttle}/loadShuttleTestData.ts (99%) rename test/loaders/{ParkingRepositoryLoaders => parking}/ChapmanApiBasedParkingRepositoryLoaderTests.test.ts (96%) rename test/loaders/{ => shuttle}/ApiBasedShuttleRepositoryLoaderTests.test.ts (88%) diff --git a/src/entities/InterchangeSystem.ts b/src/entities/InterchangeSystem.ts index 6bd743a..d1a33c4 100644 --- a/src/entities/InterchangeSystem.ts +++ b/src/entities/InterchangeSystem.ts @@ -6,7 +6,7 @@ import { NotificationRepository } from "../repositories/NotificationRepository"; import { ShuttleGetterSetterRepository } from "../repositories/ShuttleGetterSetterRepository"; import { InMemoryNotificationRepository } from "../repositories/InMemoryNotificationRepository"; import { AppleNotificationSender } from "../notifications/senders/AppleNotificationSender"; -import { ApiBasedShuttleRepositoryLoader } from "../loaders/ApiBasedShuttleRepositoryLoader"; +import { ApiBasedShuttleRepositoryLoader } from "../loaders/shuttle/ApiBasedShuttleRepositoryLoader"; export interface InterchangeSystemBuilderArguments { name: string; diff --git a/src/index.ts b/src/index.ts index a370258..00cd04b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,7 +3,7 @@ import { ApolloServer } from "@apollo/server"; import { startStandaloneServer } from "@apollo/server/standalone"; import { MergedResolvers } from "./MergedResolvers"; import { ServerContext } from "./ServerContext"; -import { loadShuttleTestData, supportedIntegrationTestSystems } from "./loaders/loadShuttleTestData"; +import { loadShuttleTestData, supportedIntegrationTestSystems } from "./loaders/shuttle/loadShuttleTestData"; import { InterchangeSystem, InterchangeSystemBuilderArguments } from "./entities/InterchangeSystem"; const typeDefs = readFileSync("./schema.graphqls", "utf8"); diff --git a/src/loaders/ParkingRepositoryLoaders/ChapmanApiBasedParkingRepositoryLoader.ts b/src/loaders/parking/ChapmanApiBasedParkingRepositoryLoader.ts similarity index 100% rename from src/loaders/ParkingRepositoryLoaders/ChapmanApiBasedParkingRepositoryLoader.ts rename to src/loaders/parking/ChapmanApiBasedParkingRepositoryLoader.ts diff --git a/src/loaders/ParkingRepositoryLoaders/ParkingRepositoryLoader.ts b/src/loaders/parking/ParkingRepositoryLoader.ts similarity index 100% rename from src/loaders/ParkingRepositoryLoaders/ParkingRepositoryLoader.ts rename to src/loaders/parking/ParkingRepositoryLoader.ts diff --git a/src/loaders/ParkingRepositoryLoaders/buildParkingRepositoryLoaderIfExists.ts b/src/loaders/parking/buildParkingRepositoryLoaderIfExists.ts similarity index 100% rename from src/loaders/ParkingRepositoryLoaders/buildParkingRepositoryLoaderIfExists.ts rename to src/loaders/parking/buildParkingRepositoryLoaderIfExists.ts diff --git a/src/loaders/ApiBasedShuttleRepositoryLoader.ts b/src/loaders/shuttle/ApiBasedShuttleRepositoryLoader.ts similarity index 97% rename from src/loaders/ApiBasedShuttleRepositoryLoader.ts rename to src/loaders/shuttle/ApiBasedShuttleRepositoryLoader.ts index e02bed4..062ff43 100644 --- a/src/loaders/ApiBasedShuttleRepositoryLoader.ts +++ b/src/loaders/shuttle/ApiBasedShuttleRepositoryLoader.ts @@ -1,8 +1,8 @@ -import { ShuttleGetterSetterRepository } from "../repositories/ShuttleGetterSetterRepository"; -import { IEta, IRoute, IShuttle, IStop } from "../entities/ShuttleRepositoryEntities"; +import { ShuttleGetterSetterRepository } from "../../repositories/ShuttleGetterSetterRepository"; +import { IEta, IRoute, IShuttle, IStop } from "../../entities/ShuttleRepositoryEntities"; import { ShuttleRepositoryLoader } from "./ShuttleRepositoryLoader"; -import { IEntityWithId } from "../entities/SharedEntities"; -import { ApiResponseError } from "./ApiResponseError"; +import { IEntityWithId } from "../../entities/SharedEntities"; +import { ApiResponseError } from "../ApiResponseError"; /** * Class which can load data into a repository from the diff --git a/src/loaders/ShuttleRepositoryLoader.ts b/src/loaders/shuttle/ShuttleRepositoryLoader.ts similarity index 87% rename from src/loaders/ShuttleRepositoryLoader.ts rename to src/loaders/shuttle/ShuttleRepositoryLoader.ts index 100cecc..382c97e 100644 --- a/src/loaders/ShuttleRepositoryLoader.ts +++ b/src/loaders/shuttle/ShuttleRepositoryLoader.ts @@ -1,4 +1,4 @@ -import { RepositoryLoader } from "./RepositoryLoader"; +import { RepositoryLoader } from "../RepositoryLoader"; export interface ShuttleRepositoryLoader extends RepositoryLoader { fetchAndUpdateRouteDataForSystem(): Promise; diff --git a/src/loaders/loadShuttleTestData.ts b/src/loaders/shuttle/loadShuttleTestData.ts similarity index 99% rename from src/loaders/loadShuttleTestData.ts rename to src/loaders/shuttle/loadShuttleTestData.ts index b97c99d..c7603bf 100644 --- a/src/loaders/loadShuttleTestData.ts +++ b/src/loaders/shuttle/loadShuttleTestData.ts @@ -1,7 +1,7 @@ // Mock data -import { IEta, IOrderedStop, IRoute, IShuttle, IStop } from "../entities/ShuttleRepositoryEntities"; -import { ShuttleGetterSetterRepository } from "../repositories/ShuttleGetterSetterRepository"; -import { InterchangeSystemBuilderArguments } from "../entities/InterchangeSystem"; +import { IEta, IOrderedStop, IRoute, IShuttle, IStop } from "../../entities/ShuttleRepositoryEntities"; +import { ShuttleGetterSetterRepository } from "../../repositories/ShuttleGetterSetterRepository"; +import { InterchangeSystemBuilderArguments } from "../../entities/InterchangeSystem"; export const supportedIntegrationTestSystems: InterchangeSystemBuilderArguments[] = [ { diff --git a/test/loaders/TimedApiBasedRepositoryLoaderTests.test.ts b/test/loaders/TimedApiBasedRepositoryLoaderTests.test.ts index e62f6ad..a57557d 100644 --- a/test/loaders/TimedApiBasedRepositoryLoaderTests.test.ts +++ b/test/loaders/TimedApiBasedRepositoryLoaderTests.test.ts @@ -2,7 +2,7 @@ import { afterEach, beforeAll, beforeEach, describe, expect, it, jest } from "@j import { TimedApiBasedRepositoryLoader } from "../../src/loaders/TimedApiBasedRepositoryLoader"; import { resetGlobalFetchMockJson } from "../testHelpers/fetchMockHelpers"; import { UnoptimizedInMemoryShuttleRepository } from "../../src/repositories/UnoptimizedInMemoryShuttleRepository"; -import { ApiBasedShuttleRepositoryLoader } from "../../src/loaders/ApiBasedShuttleRepositoryLoader"; +import { ApiBasedShuttleRepositoryLoader } from "../../src/loaders/shuttle/ApiBasedShuttleRepositoryLoader"; describe("TimedApiBasedRepositoryLoader", () => { let timedLoader: TimedApiBasedRepositoryLoader; diff --git a/test/loaders/ParkingRepositoryLoaders/ChapmanApiBasedParkingRepositoryLoaderTests.test.ts b/test/loaders/parking/ChapmanApiBasedParkingRepositoryLoaderTests.test.ts similarity index 96% rename from test/loaders/ParkingRepositoryLoaders/ChapmanApiBasedParkingRepositoryLoaderTests.test.ts rename to test/loaders/parking/ChapmanApiBasedParkingRepositoryLoaderTests.test.ts index b817d98..55d82c5 100644 --- a/test/loaders/ParkingRepositoryLoaders/ChapmanApiBasedParkingRepositoryLoaderTests.test.ts +++ b/test/loaders/parking/ChapmanApiBasedParkingRepositoryLoaderTests.test.ts @@ -1,7 +1,7 @@ import { beforeEach, describe, expect, it, jest } from "@jest/globals"; import { ChapmanApiBasedParkingRepositoryLoader -} from "../../../src/loaders/ParkingRepositoryLoaders/ChapmanApiBasedParkingRepositoryLoader"; +} from "../../../src/loaders/parking/ChapmanApiBasedParkingRepositoryLoader"; import { InMemoryParkingRepository } from "../../../src/repositories/InMemoryParkingRepository"; import { resetGlobalFetchMockJson, diff --git a/test/loaders/ApiBasedShuttleRepositoryLoaderTests.test.ts b/test/loaders/shuttle/ApiBasedShuttleRepositoryLoaderTests.test.ts similarity index 88% rename from test/loaders/ApiBasedShuttleRepositoryLoaderTests.test.ts rename to test/loaders/shuttle/ApiBasedShuttleRepositoryLoaderTests.test.ts index ee93aa3..d829732 100644 --- a/test/loaders/ApiBasedShuttleRepositoryLoaderTests.test.ts +++ b/test/loaders/shuttle/ApiBasedShuttleRepositoryLoaderTests.test.ts @@ -1,21 +1,21 @@ import { afterEach, beforeEach, describe, expect, it, jest } from "@jest/globals"; -import { ApiBasedShuttleRepositoryLoader } from "../../src/loaders/ApiBasedShuttleRepositoryLoader"; -import { UnoptimizedInMemoryShuttleRepository } from "../../src/repositories/UnoptimizedInMemoryShuttleRepository"; -import { fetchRouteDataSuccessfulResponse } from "../jsonSnapshots/fetchRouteData/fetchRouteDataSuccessfulResponse"; +import { ApiBasedShuttleRepositoryLoader } from "../../../src/loaders/shuttle/ApiBasedShuttleRepositoryLoader"; +import { UnoptimizedInMemoryShuttleRepository } from "../../../src/repositories/UnoptimizedInMemoryShuttleRepository"; +import { fetchRouteDataSuccessfulResponse } from "../../jsonSnapshots/fetchRouteData/fetchRouteDataSuccessfulResponse"; import { fetchStopAndPolylineDataSuccessfulResponse -} from "../jsonSnapshots/fetchStopAndPolylineData/fetchStopAndPolylineDataSuccessfulResponse"; -import { generateMockRoutes, generateMockShuttles, generateMockStops } from "../testHelpers/mockDataGenerators"; +} from "../../jsonSnapshots/fetchStopAndPolylineData/fetchStopAndPolylineDataSuccessfulResponse"; +import { generateMockRoutes, generateMockShuttles, generateMockStops } from "../../testHelpers/mockDataGenerators"; import { fetchShuttleDataSuccessfulResponse -} from "../jsonSnapshots/fetchShuttleData/fetchShuttleDataSuccessfulResponse"; -import { fetchEtaDataSuccessfulResponse } from "../jsonSnapshots/fetchEtaData/fetchEtaDataSuccessfulResponse"; +} from "../../jsonSnapshots/fetchShuttleData/fetchShuttleDataSuccessfulResponse"; +import { fetchEtaDataSuccessfulResponse } from "../../jsonSnapshots/fetchEtaData/fetchEtaDataSuccessfulResponse"; import { resetGlobalFetchMockJson, updateGlobalFetchMockJson, updateGlobalFetchMockJsonToThrowSyntaxError -} from "../testHelpers/fetchMockHelpers"; -import { assertAsyncCallbackThrowsApiResponseError } from "../testHelpers/assertAsyncCallbackThrowsApiResponseError"; +} from "../../testHelpers/fetchMockHelpers"; +import { assertAsyncCallbackThrowsApiResponseError } from "../../testHelpers/assertAsyncCallbackThrowsApiResponseError"; describe("ApiBasedShuttleRepositoryLoader", () => { let loader: ApiBasedShuttleRepositoryLoader;