use simplified implementation without system loading

This commit is contained in:
2025-04-06 11:21:07 -07:00
parent 95eb2c8f65
commit 8c2fb3a52a
2 changed files with 21 additions and 178 deletions

View File

@@ -1,5 +1,5 @@
import { ShuttleGetterSetterRepository } from "../repositories/ShuttleGetterSetterRepository"; import { ShuttleGetterSetterRepository } from "../repositories/ShuttleGetterSetterRepository";
import { IEntityWithId, IEta, IRoute, IShuttle, IStop, IPassioSystem } from "../entities/entities"; import { IEntityWithId, IEta, IRoute, IShuttle, IStop } from "../entities/entities";
import { ShuttleRepositoryLoader } from "./ShuttleRepositoryLoader"; import { ShuttleRepositoryLoader } from "./ShuttleRepositoryLoader";
export class ApiResponseError extends Error { export class ApiResponseError extends Error {
@@ -32,49 +32,8 @@ export class ApiBasedShuttleRepositoryLoader implements ShuttleRepositoryLoader
return ids; return ids;
} }
public async fetchAndUpdateSystemData() { public async fetchAndUpdateRouteDataForSystem() {
const params = { const systemId = this.systemId;
getSystems: "2",
};
const query = new URLSearchParams(params).toString();
const system = await this.repository.getSystemIfExists();
try {
const response = await fetch(`${this.baseUrl}?${query}`);
const json = await response.json();
if (!response.ok) {
throw new Error(`HTTP error with status ${response.status}`)
}
if (typeof json.all === "object") {
// filter down to supported systems
const filteredSystem = json.all.find((jsonSystem: any) => jsonSystem.id === this.systemId);
if (filteredSystem !== undefined) {
const constructedSystem: IPassioSystem = {
id: filteredSystem.id,
name: filteredSystem.fullname,
};
await this.repository.updateSystem(constructedSystem);
}
} else {
throw new Error("Received JSON object does not contain `all` field")
}
} catch(e: any) {
throw new ApiResponseError(e.message);
}
}
public async fetchAndUpdateRouteDataForExistingSystemInRepository() {
const system = await this.repository.getSystemIfExists();
if (system !== null) {
await this.fetchAndUpdateRouteDataForSystemId(system.id);
}
}
public async fetchAndUpdateRouteDataForSystemId(systemId: string) {
const routeIdsToPrune = await this.constructExistingEntityIdSet(async () => { const routeIdsToPrune = await this.constructExistingEntityIdSet(async () => {
return await this.repository.getRoutesBySystemId(systemId); return await this.repository.getRoutesBySystemId(systemId);
}); });
@@ -123,14 +82,9 @@ export class ApiBasedShuttleRepositoryLoader implements ShuttleRepositoryLoader
} }
} }
public async fetchAndUpdateStopAndPolylineDataForRoutesInExistingSystemInRepository() { public async fetchAndUpdateStopAndPolylineDataForRoutesInSystem() {
const system = await this.repository.getSystemIfExists(); const systemId = this.systemId;
if (system !== null) {
await this.fetchAndUpdateStopAndPolylineDataForRoutesWithSystemId(system.id);
}
}
public async fetchAndUpdateStopAndPolylineDataForRoutesWithSystemId(systemId: string) {
// Fetch from the API // Fetch from the API
// Pass JSON output into two different methods to update repository // Pass JSON output into two different methods to update repository
const stopIdsToPrune = await this.constructExistingEntityIdSet(async () => { const stopIdsToPrune = await this.constructExistingEntityIdSet(async () => {
@@ -169,16 +123,8 @@ export class ApiBasedShuttleRepositoryLoader implements ShuttleRepositoryLoader
} }
} }
public async fetchAndUpdateShuttleDataForExistingSystemInRepository() { public async fetchAndUpdateShuttleDataForSystem() {
const system = await this.repository.getSystemIfExists(); const systemId = this.systemId;
if (system !== null) {
const systemId = system.id;
await this.fetchAndUpdateShuttleDataForSystemId(systemId);
}
}
public async fetchAndUpdateShuttleDataForSystemId(systemId: string) {
const shuttleIdsToPrune = await this.constructExistingEntityIdSet(async () => { const shuttleIdsToPrune = await this.constructExistingEntityIdSet(async () => {
return await this.repository.getShuttlesBySystemId(systemId); return await this.repository.getShuttlesBySystemId(systemId);
}); });
@@ -236,16 +182,8 @@ export class ApiBasedShuttleRepositoryLoader implements ShuttleRepositoryLoader
} }
} }
public async fetchAndUpdateEtaDataForExistingStopsForSystemInRepository() { public async fetchAndUpdateEtaDataForExistingStopsForSystem() {
const system = await this.repository.getSystemIfExists() const stops = await this.repository.getStopsBySystemId(this.systemId);
if (system !== null) {
const systemId = system.id;
await this.fetchAndUpdateEtaDataForExistingStopsForSystemId(systemId);
}
}
public async fetchAndUpdateEtaDataForExistingStopsForSystemId(systemId: string) {
const stops = await this.repository.getStopsBySystemId(systemId);
await Promise.all(stops.map(async (stop) => { await Promise.all(stops.map(async (stop) => {
let stopId = stop.id; let stopId = stop.id;
await this.fetchAndUpdateEtaDataForStopId(stopId); await this.fetchAndUpdateEtaDataForStopId(stopId);

View File

@@ -26,63 +26,11 @@ describe("ApiBasedRepositoryLoader", () => {
let loader: ApiBasedShuttleRepositoryLoader; let loader: ApiBasedShuttleRepositoryLoader;
beforeEach(() => { beforeEach(() => {
loader = new ApiBasedShuttleRepositoryLoader("1", new UnoptimizedInMemoryShuttleRepository()); loader = new ApiBasedShuttleRepositoryLoader("263", new UnoptimizedInMemoryShuttleRepository());
resetGlobalFetchMockJson(); resetGlobalFetchMockJson();
}); });
describe("fetchAndUpdateSystemData", () => { describe("fetchAndUpdateRouteDataForSystem", () => {
it("updates system data in repository if response received", async () => {
// Arrange
const systemsToPrune = generateMockPassioSystems();
await Promise.all(systemsToPrune.map(async (system) => {
await loader.repository.updateSystem(system);
}));
const numberOfSystemsInResponse = fetchSystemDataSuccessfulResponse.all.length;
updateGlobalFetchMockJson(fetchSystemDataSuccessfulResponse);
// Act
await loader.fetchAndUpdateSystemData();
// Assert
const system = await loader.repository.getSystemIfExists();
expect(system).not.toBeNull();
});
it("throws the correct error if the API response contains no data", async () => {
updateGlobalFetchMockJson(fetchSystemDataFailedResponse);
await assertAsyncCallbackThrowsApiResponseError(async () => {
await loader.fetchAndUpdateSystemData();
});
});
it("throws the correct error if HTTP status code is not 200", async () => {
updateGlobalFetchMockJson(fetchSystemDataFailedResponse, 400);
await assertAsyncCallbackThrowsApiResponseError(async () => {
await loader.fetchAndUpdateSystemData();
});
});
});
describe("fetchAndUpdateRouteDataForExistingSystemsInRepository", () => {
test("calls fetchAndUpdateRouteDataForSystemId for system in repository", async () => {
const spy = jest.spyOn(loader, "fetchAndUpdateRouteDataForSystemId");
const systems = generateMockPassioSystems();
await loader.repository.updateSystem(systems[0]);
await loader.fetchAndUpdateRouteDataForExistingSystemInRepository();
expect(spy.mock.calls.length).toBe(1);
expect(spy).toHaveBeenCalledWith(systems[0].id)
});
});
describe("fetchAndUpdateRouteDataForSystemId", () => {
const systemId = "263"; const systemId = "263";
it("updates route data in repository if response received", async () => { it("updates route data in repository if response received", async () => {
// Arrange // Arrange
@@ -96,7 +44,7 @@ describe("ApiBasedRepositoryLoader", () => {
updateGlobalFetchMockJson(fetchRouteDataSuccessfulResponse); updateGlobalFetchMockJson(fetchRouteDataSuccessfulResponse);
// Act // Act
await loader.fetchAndUpdateRouteDataForSystemId(systemId); await loader.fetchAndUpdateRouteDataForSystem();
// Assert // Assert
const routes = await loader.repository.getRoutesBySystemId(systemId); const routes = await loader.repository.getRoutesBySystemId(systemId);
@@ -111,26 +59,11 @@ describe("ApiBasedRepositoryLoader", () => {
updateGlobalFetchMockJsonToThrowSyntaxError(); updateGlobalFetchMockJsonToThrowSyntaxError();
await assertAsyncCallbackThrowsApiResponseError(async () => { await assertAsyncCallbackThrowsApiResponseError(async () => {
await loader.fetchAndUpdateRouteDataForSystemId(systemId); await loader.fetchAndUpdateRouteDataForSystem();
}); });
}); });
}); });
describe("fetchAndUpdateStopAndPolylineDataForRoutesInExistingSystemsInRepository", () => {
it("calls fetchAndUpdateStopAndPolylineDataForRoutesWithSystemId for system", async () => {
const spy = jest.spyOn(loader, "fetchAndUpdateStopAndPolylineDataForRoutesWithSystemId");
const systems = generateMockPassioSystems();
await loader.repository.updateSystem(systems[0]);
await loader.fetchAndUpdateStopAndPolylineDataForRoutesInExistingSystemInRepository();
expect(spy.mock.calls.length).toBe(1);
expect(spy).toHaveBeenCalledWith(systems[0].id)
});
})
describe("fetchAndUpdateStopAndPolylineDataForRoutesWithSystemId", () => { describe("fetchAndUpdateStopAndPolylineDataForRoutesWithSystemId", () => {
const systemId = "263"; const systemId = "263";
it("updates stop and polyline data if response received", async () => { it("updates stop and polyline data if response received", async () => {
@@ -146,7 +79,7 @@ describe("ApiBasedRepositoryLoader", () => {
const stopsArray = Object.values(fetchStopAndPolylineDataSuccessfulResponse.stops); const stopsArray = Object.values(fetchStopAndPolylineDataSuccessfulResponse.stops);
await loader.fetchAndUpdateStopAndPolylineDataForRoutesWithSystemId(systemId); await loader.fetchAndUpdateStopAndPolylineDataForRoutesInSystem();
const stops = await loader.repository.getStopsBySystemId(systemId); const stops = await loader.repository.getStopsBySystemId(systemId);
expect(stops.length).toEqual(stopsArray.length); expect(stops.length).toEqual(stopsArray.length);
@@ -166,26 +99,12 @@ describe("ApiBasedRepositoryLoader", () => {
updateGlobalFetchMockJsonToThrowSyntaxError(); updateGlobalFetchMockJsonToThrowSyntaxError();
await assertAsyncCallbackThrowsApiResponseError(async () => { await assertAsyncCallbackThrowsApiResponseError(async () => {
await loader.fetchAndUpdateStopAndPolylineDataForRoutesWithSystemId(systemId); await loader.fetchAndUpdateStopAndPolylineDataForRoutesInSystem();
}); });
}) })
}); });
describe("fetchAndUpdateShuttleDataForExistingSystemsInRepository", () => { describe("fetchAndUpdateShuttleDataForSystem", () => {
it("calls fetchAndUpdateShuttleDataForSystemId for every system", async () => {
const spy = jest.spyOn(loader, "fetchAndUpdateShuttleDataForSystemId");
const systems = generateMockPassioSystems();
await loader.repository.updateSystem(systems[0]);
await loader.fetchAndUpdateShuttleDataForExistingSystemInRepository();
expect(spy.mock.calls.length).toBe(1);
expect(spy).toHaveBeenCalledWith(systems[0].id)
});
});
describe("fetchAndUpdateShuttleDataForSystemId", () => {
const systemId = "263"; const systemId = "263";
it("updates shuttle data in repository if response received", async () => { it("updates shuttle data in repository if response received", async () => {
const shuttlesToPrune = generateMockShuttles(); const shuttlesToPrune = generateMockShuttles();
@@ -197,7 +116,7 @@ describe("ApiBasedRepositoryLoader", () => {
updateGlobalFetchMockJson(fetchShuttleDataSuccessfulResponse); updateGlobalFetchMockJson(fetchShuttleDataSuccessfulResponse);
const busesInResponse = Object.values(fetchShuttleDataSuccessfulResponse.buses); const busesInResponse = Object.values(fetchShuttleDataSuccessfulResponse.buses);
await loader.fetchAndUpdateShuttleDataForSystemId(systemId); await loader.fetchAndUpdateShuttleDataForSystem();
const shuttles = await loader.repository.getShuttlesBySystemId(systemId); const shuttles = await loader.repository.getShuttlesBySystemId(systemId);
@@ -208,39 +127,25 @@ describe("ApiBasedRepositoryLoader", () => {
updateGlobalFetchMockJsonToThrowSyntaxError(); updateGlobalFetchMockJsonToThrowSyntaxError();
await assertAsyncCallbackThrowsApiResponseError(async () => { await assertAsyncCallbackThrowsApiResponseError(async () => {
await loader.fetchAndUpdateShuttleDataForSystemId(systemId); await loader.fetchAndUpdateShuttleDataForSystem();
}); });
}); });
}); });
describe("fetchAndUpdateEtaDataForExistingStopsForSystemsInRepository", () => { describe("fetchAndUpdateEtaDataForExistingStopsForSystem", () => {
it("calls fetchAndUpdateEtaDataFoExistingStopsForSystemId for every system in repository", async () => {
const spy = jest.spyOn(loader, "fetchAndUpdateEtaDataForExistingStopsForSystemId");
const systems = generateMockPassioSystems();
await loader.repository.updateSystem(systems[0]);
await loader.fetchAndUpdateEtaDataForExistingStopsForSystemInRepository();
expect(spy.mock.calls.length).toBe(1);
expect(spy).toHaveBeenCalledWith(systems[0].id)
});
});
describe("fetchAndUpdateEtaDataForExistingStopsForSystemId", () => {
it("calls fetchAndUpdateEtaDataForStopId for every stop in repository", async () => { it("calls fetchAndUpdateEtaDataForStopId for every stop in repository", async () => {
const spy = jest.spyOn(loader, "fetchAndUpdateEtaDataForStopId"); const spy = jest.spyOn(loader, "fetchAndUpdateEtaDataForStopId");
const stops = generateMockStops(); const stops = generateMockStops();
stops.forEach((stop) => { stops.forEach((stop) => {
stop.systemId = "1"; stop.systemId = "263";
}); });
await Promise.all(stops.map(async (stop) => { await Promise.all(stops.map(async (stop) => {
await loader.repository.addOrUpdateStop(stop); await loader.repository.addOrUpdateStop(stop);
})); }));
await loader.fetchAndUpdateEtaDataForExistingStopsForSystemId("1"); await loader.fetchAndUpdateEtaDataForExistingStopsForSystem();
expect(spy.mock.calls.length).toEqual(stops.length); expect(spy.mock.calls.length).toEqual(stops.length);
}); });