Merge pull request #46 from brendan-ch/feat/data-timestamps

This commit is contained in:
2025-04-29 17:29:29 -07:00
committed by GitHub
19 changed files with 153 additions and 46 deletions

View File

@@ -1,3 +1,5 @@
scalar DateTime
# The Interchange system schema. # The Interchange system schema.
# Note how Passio ID and parking ID are abstracted away # Note how Passio ID and parking ID are abstracted away
# from the endpoints. # from the endpoints.
@@ -27,6 +29,7 @@ type ParkingStructure {
spotsAvailable: Int! spotsAvailable: Int!
coordinates: Coordinates! coordinates: Coordinates!
address: String! address: String!
updatedTime: DateTime
} }
type Route { type Route {
@@ -37,6 +40,7 @@ type Route {
shuttles: [Shuttle!] shuttles: [Shuttle!]
polylineCoordinates: [Coordinates!]! polylineCoordinates: [Coordinates!]!
color: String! color: String!
updatedTime: DateTime
} }
type OrderedStop { type OrderedStop {
@@ -47,6 +51,7 @@ type OrderedStop {
stop: Stop stop: Stop
stopId: ID! stopId: ID!
systemId: ID! systemId: ID!
updatedTime: DateTime
} }
type Stop { type Stop {
@@ -56,6 +61,7 @@ type Stop {
coordinates: Coordinates! coordinates: Coordinates!
etas: [ETA!] etas: [ETA!]
orderedStops: [OrderedStop!] orderedStops: [OrderedStop!]
updatedTime: DateTime
} }
type Coordinates { type Coordinates {
@@ -70,6 +76,7 @@ type ETA {
shuttleId: ID! shuttleId: ID!
secondsRemaining: Float! secondsRemaining: Float!
systemId: ID! systemId: ID!
updatedTime: DateTime
} }
type Shuttle { type Shuttle {
@@ -82,6 +89,7 @@ type Shuttle {
etas: [ETA!] etas: [ETA!]
eta(forStopId: ID): ETA eta(forStopId: ID): ETA
orientationInDegrees: Float! orientationInDegrees: Float!
updatedTime: DateTime
} }
# Queries # Queries

View File

@@ -1,4 +1,4 @@
import { Coordinates, Eta, OrderedStop, Resolvers } from "./generated/graphql"; import { Resolvers } from "./generated/graphql";
import { ServerContext } from "./ServerContext"; import { ServerContext } from "./ServerContext";
import { QueryResolvers } from "./resolvers/QueryResolvers"; import { QueryResolvers } from "./resolvers/QueryResolvers";
import { SystemResolvers } from "./resolvers/SystemResolvers"; import { SystemResolvers } from "./resolvers/SystemResolvers";
@@ -9,6 +9,7 @@ import { ShuttleResolvers } from "./resolvers/ShuttleResolvers";
import { RouteResolvers } from "./resolvers/RouteResolvers"; import { RouteResolvers } from "./resolvers/RouteResolvers";
import { MutationResolvers } from "./resolvers/MutationResolvers"; import { MutationResolvers } from "./resolvers/MutationResolvers";
import { ParkingSystemResolvers } from "./resolvers/ParkingSystemResolvers"; import { ParkingSystemResolvers } from "./resolvers/ParkingSystemResolvers";
import { DateTime } from "./scalars/DateTime";
export const MergedResolvers: Resolvers<ServerContext> = { export const MergedResolvers: Resolvers<ServerContext> = {
...QueryResolvers, ...QueryResolvers,
@@ -20,4 +21,5 @@ export const MergedResolvers: Resolvers<ServerContext> = {
...OrderedStopResolvers, ...OrderedStopResolvers,
...EtaResolvers, ...EtaResolvers,
...MutationResolvers, ...MutationResolvers,
DateTime: DateTime,
}; };

View File

@@ -1,6 +1,6 @@
import { ICoordinates, IEntityWithId, IEntityWithOptionalTimestamp } from "./SharedEntities"; import { ICoordinates, IEntityWithId, IEntityWithTimestamp } from "./SharedEntities";
export interface IParkingStructure extends IEntityWithOptionalTimestamp, IEntityWithId { export interface IParkingStructure extends IEntityWithTimestamp, IEntityWithId {
address: string; address: string;
capacity: number; capacity: number;
spotsAvailable: number; spotsAvailable: number;

View File

@@ -1,5 +1,5 @@
export interface IEntityWithOptionalTimestamp { export interface IEntityWithTimestamp {
millisecondsSinceEpoch?: number; updatedTime: Date;
} }
export interface IEntityWithId { export interface IEntityWithId {

View File

@@ -1,19 +1,19 @@
import { ICoordinates, IEntityWithId, IEntityWithOptionalTimestamp } from "./SharedEntities"; import { ICoordinates, IEntityWithId, IEntityWithTimestamp } from "./SharedEntities";
export interface IRoute extends IEntityWithId, IEntityWithOptionalTimestamp { export interface IRoute extends IEntityWithId, IEntityWithTimestamp {
name: string; name: string;
color: string; color: string;
polylineCoordinates: ICoordinates[]; polylineCoordinates: ICoordinates[];
systemId: string; systemId: string;
} }
export interface IStop extends IEntityWithId, IEntityWithOptionalTimestamp { export interface IStop extends IEntityWithId, IEntityWithTimestamp {
name: string; name: string;
systemId: string; systemId: string;
coordinates: ICoordinates; coordinates: ICoordinates;
} }
export interface IShuttle extends IEntityWithId, IEntityWithOptionalTimestamp { export interface IShuttle extends IEntityWithId, IEntityWithTimestamp {
coordinates: ICoordinates; coordinates: ICoordinates;
name: string; name: string;
routeId: string; routeId: string;
@@ -21,14 +21,14 @@ export interface IShuttle extends IEntityWithId, IEntityWithOptionalTimestamp {
orientationInDegrees: number; orientationInDegrees: number;
} }
export interface IEta extends IEntityWithOptionalTimestamp { export interface IEta extends IEntityWithTimestamp {
secondsRemaining: number; secondsRemaining: number;
shuttleId: string; shuttleId: string;
stopId: string; stopId: string;
systemId: string; systemId: string;
} }
export interface IOrderedStop extends IEntityWithOptionalTimestamp { export interface IOrderedStop extends IEntityWithTimestamp {
nextStop?: IOrderedStop; nextStop?: IOrderedStop;
previousStop?: IOrderedStop; previousStop?: IOrderedStop;
routeId: string; routeId: string;

View File

@@ -58,7 +58,8 @@ export class ChapmanApiBasedParkingRepositoryLoader implements ParkingRepository
id: ChapmanApiBasedParkingRepositoryLoader.generateId(jsonStructure.Address), id: ChapmanApiBasedParkingRepositoryLoader.generateId(jsonStructure.Address),
name: jsonStructure.Name, name: jsonStructure.Name,
spotsAvailable: jsonStructure.CurrentCount > jsonStructure.Capacity ? jsonStructure.Capacity : jsonStructure.CurrentCount, spotsAvailable: jsonStructure.CurrentCount > jsonStructure.Capacity ? jsonStructure.Capacity : jsonStructure.CurrentCount,
address: jsonStructure.Address address: jsonStructure.Address,
updatedTime: new Date(),
} }
return structureToReturn; return structureToReturn;

View File

@@ -12,6 +12,7 @@ const parkingStructures: IParkingStructure[] = [
}, },
name: "Anderson Structure", name: "Anderson Structure",
id: "1", id: "1",
updatedTime: new Date(),
}, },
{ {
address: "200 W Sycamore Ave, Orange, CA 92866-1053", address: "200 W Sycamore Ave, Orange, CA 92866-1053",
@@ -23,6 +24,7 @@ const parkingStructures: IParkingStructure[] = [
}, },
name: "Barrera", name: "Barrera",
id: "2", id: "2",
updatedTime: new Date(),
} }
]; ];

View File

@@ -73,6 +73,7 @@ export class ApiBasedShuttleRepositoryLoader implements ShuttleRepositoryLoader
id: jsonRoute.myid, id: jsonRoute.myid,
polylineCoordinates: [], polylineCoordinates: [],
systemId: this.systemIdForConstructedData, systemId: this.systemIdForConstructedData,
updatedTime: new Date(),
}; };
await this.repository.addOrUpdateRoute(constructedRoute); await this.repository.addOrUpdateRoute(constructedRoute);
@@ -172,7 +173,8 @@ export class ApiBasedShuttleRepositoryLoader implements ShuttleRepositoryLoader
routeId: jsonBus.routeId, routeId: jsonBus.routeId,
systemId: this.systemIdForConstructedData, systemId: this.systemIdForConstructedData,
id: `${jsonBus.busId}`, id: `${jsonBus.busId}`,
orientationInDegrees: parseFloat(jsonBus.calculatedCourse) orientationInDegrees: parseFloat(jsonBus.calculatedCourse),
updatedTime: new Date(),
} }
await this.repository.addOrUpdateShuttle(constructedShuttle); await this.repository.addOrUpdateShuttle(constructedShuttle);
@@ -221,7 +223,7 @@ export class ApiBasedShuttleRepositoryLoader implements ShuttleRepositoryLoader
secondsRemaining: jsonEta.secondsSpent, secondsRemaining: jsonEta.secondsSpent,
shuttleId: `${shuttleId}`, shuttleId: `${shuttleId}`,
stopId: stopId, stopId: stopId,
millisecondsSinceEpoch: Date.now(), updatedTime: new Date(),
systemId: this.systemIdForConstructedData, systemId: this.systemIdForConstructedData,
}; };
@@ -249,6 +251,7 @@ export class ApiBasedShuttleRepositoryLoader implements ShuttleRepositoryLoader
latitude: parseFloat(stop.latitude), latitude: parseFloat(stop.latitude),
longitude: parseFloat(stop.longitude), longitude: parseFloat(stop.longitude),
}, },
updatedTime: new Date(),
}; };
await this.repository.addOrUpdateStop(constructedStop); await this.repository.addOrUpdateStop(constructedStop);
@@ -280,6 +283,7 @@ export class ApiBasedShuttleRepositoryLoader implements ShuttleRepositoryLoader
stopId, stopId,
position: index + 1, position: index + 1,
systemId: this.systemIdForConstructedData, systemId: this.systemIdForConstructedData,
updatedTime: new Date(),
}; };
} }
@@ -289,6 +293,7 @@ export class ApiBasedShuttleRepositoryLoader implements ShuttleRepositoryLoader
stopId: jsonOrderedStopData[index - 1][1], stopId: jsonOrderedStopData[index - 1][1],
position: index, position: index,
systemId: this.systemIdForConstructedData, systemId: this.systemIdForConstructedData,
updatedTime: new Date(),
}; };
} }
if (index < jsonOrderedStopData.length - 1) { if (index < jsonOrderedStopData.length - 1) {
@@ -297,6 +302,7 @@ export class ApiBasedShuttleRepositoryLoader implements ShuttleRepositoryLoader
stopId: jsonOrderedStopData[index + 1][1], stopId: jsonOrderedStopData[index + 1][1],
position: index + 2, position: index + 2,
systemId: this.systemIdForConstructedData, systemId: this.systemIdForConstructedData,
updatedTime: new Date(),
}; };
} }

View File

@@ -4324,6 +4324,7 @@ const routes: IRoute[] = [
systemId: supportedIntegrationTestSystems[0].id, systemId: supportedIntegrationTestSystems[0].id,
polylineCoordinates: redRoutePolylineCoordinates, polylineCoordinates: redRoutePolylineCoordinates,
color: "#db2316", color: "#db2316",
updatedTime: new Date(),
}, },
{ {
name: "Teal Route", name: "Teal Route",
@@ -4331,6 +4332,7 @@ const routes: IRoute[] = [
systemId: supportedIntegrationTestSystems[0].id, systemId: supportedIntegrationTestSystems[0].id,
polylineCoordinates: tealRoutePolylineCoordinates, polylineCoordinates: tealRoutePolylineCoordinates,
color: "#21bdd1", color: "#21bdd1",
updatedTime: new Date(),
}, },
]; ];
@@ -4343,6 +4345,7 @@ const stops: IStop[] = [
longitude: -117.8892805, longitude: -117.8892805,
}, },
systemId: supportedIntegrationTestSystems[0].id, systemId: supportedIntegrationTestSystems[0].id,
updatedTime: new Date(),
}, },
{ {
id: "2", id: "2",
@@ -4352,6 +4355,7 @@ const stops: IStop[] = [
longitude: -117.895966, longitude: -117.895966,
}, },
systemId: supportedIntegrationTestSystems[0].id, systemId: supportedIntegrationTestSystems[0].id,
updatedTime: new Date(),
}, },
{ {
id: "3", id: "3",
@@ -4361,6 +4365,7 @@ const stops: IStop[] = [
"longitude": -117.85281 "longitude": -117.85281
}, },
systemId: supportedIntegrationTestSystems[0].id, systemId: supportedIntegrationTestSystems[0].id,
updatedTime: new Date(),
} }
]; ];
@@ -4370,12 +4375,14 @@ const orderedStopsForRedRoute: IOrderedStop[] = [
stopId: stops[0].id, stopId: stops[0].id,
position: 1, position: 1,
systemId: "1", systemId: "1",
updatedTime: new Date(),
}, },
{ {
routeId: routes[0].id, routeId: routes[0].id,
stopId: stops[2].id, stopId: stops[2].id,
position: 2, position: 2,
systemId: "1", systemId: "1",
updatedTime: new Date(),
}, },
]; ];
@@ -4385,18 +4392,21 @@ const orderedStopsForTealRoute: IOrderedStop[] = [
stopId: stops[0].id, stopId: stops[0].id,
position: 1, position: 1,
systemId: supportedIntegrationTestSystems[0].id, systemId: supportedIntegrationTestSystems[0].id,
updatedTime: new Date(),
}, },
{ {
routeId: routes[1].id, routeId: routes[1].id,
stopId: stops[1].id, stopId: stops[1].id,
position: 2, position: 2,
systemId: supportedIntegrationTestSystems[0].id, systemId: supportedIntegrationTestSystems[0].id,
updatedTime: new Date(),
}, },
{ {
routeId: routes[1].id, routeId: routes[1].id,
stopId: stops[2].id, stopId: stops[2].id,
position: 2, position: 2,
systemId: supportedIntegrationTestSystems[0].id, systemId: supportedIntegrationTestSystems[0].id,
updatedTime: new Date(),
}, },
] ]
@@ -4416,6 +4426,7 @@ const shuttles: IShuttle[] = [
routeId: routes[0].id, routeId: routes[0].id,
systemId: supportedIntegrationTestSystems[0].id, systemId: supportedIntegrationTestSystems[0].id,
orientationInDegrees: 45.91, orientationInDegrees: 45.91,
updatedTime: new Date(),
}, },
{ {
name: "24", name: "24",
@@ -4427,6 +4438,7 @@ const shuttles: IShuttle[] = [
routeId: routes[0].id, routeId: routes[0].id,
systemId: supportedIntegrationTestSystems[0].id, systemId: supportedIntegrationTestSystems[0].id,
orientationInDegrees: 90.24, orientationInDegrees: 90.24,
updatedTime: new Date(),
} }
]; ];
@@ -4436,24 +4448,28 @@ const etas: IEta[] = [
shuttleId: shuttles[0].id, shuttleId: shuttles[0].id,
secondsRemaining: 12.023, secondsRemaining: 12.023,
systemId: supportedIntegrationTestSystems[0].id, systemId: supportedIntegrationTestSystems[0].id,
updatedTime: new Date(),
}, },
{ {
stopId: stops[2].id, stopId: stops[2].id,
shuttleId: shuttles[0].id, shuttleId: shuttles[0].id,
secondsRemaining: 600.123, secondsRemaining: 600.123,
systemId: supportedIntegrationTestSystems[0].id, systemId: supportedIntegrationTestSystems[0].id,
updatedTime: new Date(),
}, },
{ {
stopId: stops[2].id, stopId: stops[2].id,
shuttleId: shuttles[1].id, shuttleId: shuttles[1].id,
secondsRemaining: 172.015, secondsRemaining: 172.015,
systemId: supportedIntegrationTestSystems[0].id, systemId: supportedIntegrationTestSystems[0].id,
updatedTime: new Date(),
}, },
{ {
stopId: stops[0].id, stopId: stops[0].id,
shuttleId: shuttles[1].id, shuttleId: shuttles[1].id,
secondsRemaining: 710.152, secondsRemaining: 710.152,
systemId: supportedIntegrationTestSystems[0].id, systemId: supportedIntegrationTestSystems[0].id,
updatedTime: new Date(),
} }
]; ];

View File

@@ -24,6 +24,7 @@ export const OrderedStopResolvers: Resolvers<ServerContext> = {
routeId: parent.routeId, routeId: parent.routeId,
stopId: nextOrderedStopObject.id, stopId: nextOrderedStopObject.id,
systemId: system.id, systemId: system.id,
updatedTime: nextOrderedStopObject.updatedTime
} }
}, },
previousStop: async (parent, args, contextValue, _info): Promise<OrderedStop | null> => { previousStop: async (parent, args, contextValue, _info): Promise<OrderedStop | null> => {
@@ -47,6 +48,7 @@ export const OrderedStopResolvers: Resolvers<ServerContext> = {
routeId: parent.routeId, routeId: parent.routeId,
stopId: previousOrderedStopObject.id, stopId: previousOrderedStopObject.id,
systemId: system.id, systemId: system.id,
updatedTime: previousOrderedStopObject.updatedTime,
} }
}, },
stop: async (parent, args, contextValue, _info) => { stop: async (parent, args, contextValue, _info) => {

View File

@@ -3,7 +3,7 @@ import { ServerContext } from "../ServerContext";
export const RouteResolvers: Resolvers<ServerContext> = { export const RouteResolvers: Resolvers<ServerContext> = {
Route: { Route: {
shuttles: async (parent, args, contextValue, info) => { shuttles: async (parent, args, contextValue, _info) => {
const system = contextValue.findSystemById(parent.systemId); const system = contextValue.findSystemById(parent.systemId);
if (!system) return null; if (!system) return null;
@@ -13,7 +13,8 @@ export const RouteResolvers: Resolvers<ServerContext> = {
coordinates, coordinates,
name, name,
id, id,
orientationInDegrees orientationInDegrees,
updatedTime
}) => ({ }) => ({
coordinates: coordinates as Coordinates, coordinates: coordinates as Coordinates,
name, name,
@@ -22,9 +23,10 @@ export const RouteResolvers: Resolvers<ServerContext> = {
id, id,
orientationInDegrees, orientationInDegrees,
systemId: parent.systemId, systemId: parent.systemId,
updatedTime,
})); }));
}, },
orderedStop: async (parent, args, contextValue, info) => { orderedStop: async (parent, args, contextValue, _info) => {
if (!args.forStopId) return null; if (!args.forStopId) return null;
const system = contextValue.findSystemById(parent.systemId); const system = contextValue.findSystemById(parent.systemId);
@@ -41,6 +43,7 @@ export const RouteResolvers: Resolvers<ServerContext> = {
routeId: parent.id, routeId: parent.id,
route: parent, route: parent,
systemId: system.id, systemId: system.id,
updatedTimeMs: orderedStop.updatedTime,
} }
}, },
}, },

View File

@@ -18,6 +18,7 @@ export const ShuttleResolvers: Resolvers<ServerContext> = {
shuttleId: parent.id, shuttleId: parent.id,
shuttle: parent, shuttle: parent,
systemId: system.id, systemId: system.id,
updatedTimeMs: etaForStopId.updatedTime,
}; };
}, },
etas: async (parent, args, contextValue, info) => { etas: async (parent, args, contextValue, info) => {
@@ -27,17 +28,20 @@ export const ShuttleResolvers: Resolvers<ServerContext> = {
const etasForShuttle = await system.shuttleRepository.getEtasForShuttleId(parent.id); const etasForShuttle = await system.shuttleRepository.getEtasForShuttleId(parent.id);
if (!etasForShuttle) return null; if (!etasForShuttle) return null;
const computedEtas = await Promise.all(etasForShuttle.map(async ({ const computedEtas = await Promise.all(
secondsRemaining, etasForShuttle.map(async ({
stopId,
}): Promise<Eta | null> => {
return {
secondsRemaining, secondsRemaining,
stopId, stopId,
shuttle: parent, updatedTime
shuttleId: parent.id, }): Promise<Eta | null> => {
systemId: system.id, return {
} secondsRemaining,
stopId,
shuttle: parent,
shuttleId: parent.id,
systemId: system.id,
updatedTime: updatedTime,
}
})); }));
if (computedEtas.every((eta) => eta !== null)) { if (computedEtas.every((eta) => eta !== null)) {
@@ -59,6 +63,7 @@ export const ShuttleResolvers: Resolvers<ServerContext> = {
name: route.name, name: route.name,
polylineCoordinates: route.polylineCoordinates, polylineCoordinates: route.polylineCoordinates,
systemId: system.id, systemId: system.id,
updatedTimeMs: route.updatedTime
} }
} }
}, },

View File

@@ -36,6 +36,7 @@ export const SystemResolvers: Resolvers<ServerContext> = {
name: stop.name, name: stop.name,
coordinates: stop.coordinates as Coordinates, coordinates: stop.coordinates as Coordinates,
systemId: parent.id, systemId: parent.id,
updatedTimeMs: stop.updatedTime,
}; };
}, },
route: async (parent, args, contextValue, _info) => { route: async (parent, args, contextValue, _info) => {
@@ -55,6 +56,7 @@ export const SystemResolvers: Resolvers<ServerContext> = {
name: route.name, name: route.name,
polylineCoordinates: route.polylineCoordinates as Coordinates[], polylineCoordinates: route.polylineCoordinates as Coordinates[],
systemId: parent.id, systemId: parent.id,
updatedTimeMs: route.updatedTime,
}; };
}, },
shuttle: async (parent, args, contextValue, _info) => { shuttle: async (parent, args, contextValue, _info) => {

28
src/scalars/DateTime.ts Normal file
View File

@@ -0,0 +1,28 @@
import { GraphQLScalarType } from "graphql/type";
import { Kind } from "graphql/language";
// See Apollo documentation: https://www.apollographql.com/docs/apollo-server/schema/custom-scalars#providing-custom-scalars-to-apollo-server
export const DateTime = new GraphQLScalarType({
name: 'DateTime',
description: 'DateTime custom scalar type',
serialize(value) {
if (value instanceof Date) {
return value.getTime();
} else if (value instanceof Number) {
return value;
}
throw Error('GraphQL Date Scalar serializer expected a `Date` object or number');
},
parseValue(value) {
if (typeof value === 'number') {
return new Date(value);
}
throw new Error('GraphQL Date Scalar parser expected a `number`');
},
parseLiteral(ast) {
if (ast.kind === Kind.INT) {
return new Date(parseInt(ast.value, 10));
}
return null;
},
})

View File

@@ -59,6 +59,7 @@ describe("ChapmanApiBasedParkingRepositoryLoader", () => {
}, },
name: "Anderson Structure", name: "Anderson Structure",
id: "", id: "",
updatedTime: new Date(),
}, },
{ {
address: "200 W Sycamore Ave, Orange, CA 92866-1053", address: "200 W Sycamore Ave, Orange, CA 92866-1053",
@@ -70,12 +71,18 @@ describe("ChapmanApiBasedParkingRepositoryLoader", () => {
}, },
name: "Barrera", name: "Barrera",
id: "", id: "",
updatedTime: new Date(),
} }
]; ];
expectedStructures[0].id = ChapmanApiBasedParkingRepositoryLoader.generateId(expectedStructures[0].address); expectedStructures[0].id = ChapmanApiBasedParkingRepositoryLoader.generateId(expectedStructures[0].address);
expectedStructures[1].id = ChapmanApiBasedParkingRepositoryLoader.generateId(expectedStructures[1].address); expectedStructures[1].id = ChapmanApiBasedParkingRepositoryLoader.generateId(expectedStructures[1].address);
const structuresFromLoader = await loader.repository.getParkingStructures(); const structuresFromLoader = await loader.repository.getParkingStructures();
// Set updatedTimeMs on expected data to avoid comparison
expectedStructures[0].updatedTime = structuresFromLoader[0].updatedTime;
expectedStructures[1].updatedTime = structuresFromLoader[1].updatedTime;
expect(structuresFromLoader).toEqual(expectedStructures); expect(structuresFromLoader).toEqual(expectedStructures);
}); });

View File

@@ -52,6 +52,7 @@ describe("ETANotificationScheduler", () => {
stopId: stop.id, stopId: stop.id,
secondsRemaining: 120, secondsRemaining: 120,
systemId: "1", systemId: "1",
updatedTime: new Date(),
}; };
const notificationData1 = { const notificationData1 = {

View File

@@ -13,7 +13,8 @@ describe("InMemoryParkingRepository", () => {
id: "1", id: "1",
name: "Anderson Parking Structure", name: "Anderson Parking Structure",
capacity: 100, capacity: 100,
address: "300 E Walnut Ave, Orange, CA 92867" address: "300 E Walnut Ave, Orange, CA 92867",
updatedTime: new Date(),
}; };
beforeEach(() => { beforeEach(() => {

View File

@@ -4,6 +4,7 @@ import { setupTestServerContext, setupTestServerHolder } from "../testHelpers/ap
import { InterchangeSystem } from "../../src/entities/InterchangeSystem"; import { InterchangeSystem } from "../../src/entities/InterchangeSystem";
import assert = require("node:assert"); import assert = require("node:assert");
describe("ParkingSystemResolver", () => { describe("ParkingSystemResolver", () => {
const holder = setupTestServerHolder(); const holder = setupTestServerHolder();
const context = setupTestServerContext(); const context = setupTestServerContext();
@@ -40,6 +41,7 @@ describe("ParkingSystemResolver", () => {
longitude longitude
} }
address address
updatedTime
} }
} }
} }
@@ -47,15 +49,24 @@ describe("ParkingSystemResolver", () => {
` `
it("gets parking structures associated with the system id", async () => { it("gets parking structures associated with the system id", async () => {
const expectedParkingStructures = generateParkingStructures(); let expectedParkingStructures = generateParkingStructures();
await Promise.all(expectedParkingStructures.map(async (structure) => { await Promise.all(expectedParkingStructures.map(async (structure) => {
await context.systems[0].parkingRepository?.addOrUpdateParkingStructure(structure); await context.systems[0].parkingRepository?.addOrUpdateParkingStructure(structure);
})); }));
// Dates are transformed into epoch timestamps when serialized
expectedParkingStructures = expectedParkingStructures.map((structure) => {
const newStructure = { ...structure };
// @ts-ignore
newStructure.updatedTime = newStructure.updatedTime.getTime();
return newStructure;
});
const response = await getResponseFromQueryNeedingSystemId(query); const response = await getResponseFromQueryNeedingSystemId(query);
assert(response.body.kind === "single"); assert(response.body.kind === "single");
expect(response.body.singleResult.errors).toBeUndefined(); expect(response.body.singleResult.errors).toBeUndefined();
const parkingStructures = (response.body.singleResult.data as any).system.parkingSystem.parkingStructures; const parkingStructures = (response.body.singleResult.data as any).system.parkingSystem.parkingStructures;
expect(parkingStructures).toEqual(expectedParkingStructures); expect(parkingStructures).toEqual(expectedParkingStructures);
}); });
@@ -65,7 +76,9 @@ describe("ParkingSystemResolver", () => {
assert(response.body.kind === "single"); assert(response.body.kind === "single");
expect(response.body.singleResult.errors).toBeUndefined(); expect(response.body.singleResult.errors).toBeUndefined();
const parkingStructures = (response.body.singleResult.data as any).system.parkingSystem.parkingStructures; const parkingStructures = (response.body.singleResult.data as any).system.parkingSystem.parkingStructures;
expect(parkingStructures).toHaveLength(0); expect(parkingStructures).toHaveLength(0);
}); });
}); });
@@ -87,6 +100,7 @@ describe("ParkingSystemResolver", () => {
longitude longitude
} }
address address
updatedTime
} }
} }
} }
@@ -111,11 +125,14 @@ describe("ParkingSystemResolver", () => {
await context.systems[0].parkingRepository?.addOrUpdateParkingStructure(structure); await context.systems[0].parkingRepository?.addOrUpdateParkingStructure(structure);
})); }));
const expectedParkingStructure = generatedParkingStructures[1]; const expectedParkingStructure = generatedParkingStructures[1];
// @ts-ignore
expectedParkingStructure.updatedTime = expectedParkingStructure.updatedTime.getTime();
const response = await getResponseForParkingStructureQuery(expectedParkingStructure.id); const response = await getResponseForParkingStructureQuery(expectedParkingStructure.id);
assert(response.body.kind === "single"); assert(response.body.kind === "single");
expect(response.body.singleResult.errors).toBeUndefined(); expect(response.body.singleResult.errors).toBeUndefined();
const parkingStructure = (response.body.singleResult.data as any).system.parkingSystem.parkingStructure; const parkingStructure = (response.body.singleResult.data as any).system.parkingSystem.parkingStructure;
expect(parkingStructure).toEqual(expectedParkingStructure); expect(parkingStructure).toEqual(expectedParkingStructure);
}); });
@@ -132,6 +149,7 @@ describe("ParkingSystemResolver", () => {
assert(response.body.kind === "single"); assert(response.body.kind === "single");
expect(response.body.singleResult.errors).toBeUndefined(); expect(response.body.singleResult.errors).toBeUndefined();
const parkingStructure = (response.body.singleResult.data as any).system.parkingSystem.parkingStructure; const parkingStructure = (response.body.singleResult.data as any).system.parkingSystem.parkingStructure;
expect(parkingStructure).toBeNull(); expect(parkingStructure).toBeNull();
}); });

View File

@@ -16,7 +16,8 @@ export function generateParkingStructures(): IParkingStructure[] {
"id": "b0723baf8a6b8bcc37c821473373049e", "id": "b0723baf8a6b8bcc37c821473373049e",
"name": "Anderson Structure", "name": "Anderson Structure",
"spotsAvailable": 163, "spotsAvailable": 163,
"address": "300 E Walnut, Orange, CA 92867" "address": "300 E Walnut, Orange, CA 92867",
updatedTime: new Date(),
}, },
{ {
"capacity": 692, "capacity": 692,
@@ -27,7 +28,8 @@ export function generateParkingStructures(): IParkingStructure[] {
"id": "81b9e1ed004cf6def2e6c568aaf79ece", "id": "81b9e1ed004cf6def2e6c568aaf79ece",
"name": "Barrera", "name": "Barrera",
"spotsAvailable": 179, "spotsAvailable": 179,
"address": "200 W Sycamore Ave, Orange, CA 92866-1053" "address": "200 W Sycamore Ave, Orange, CA 92866-1053",
updatedTime: new Date(),
} }
]; ];
} }
@@ -43,7 +45,8 @@ export function generateMockShuttles(): IShuttle[] {
latitude: 10, latitude: 10,
longitude: 20 longitude: 20
}, },
orientationInDegrees: 25.163 orientationInDegrees: 25.163,
updatedTime: new Date(),
}, },
{ {
id: "sh2", id: "sh2",
@@ -54,7 +57,8 @@ export function generateMockShuttles(): IShuttle[] {
latitude: 15, latitude: 15,
longitude: 25 longitude: 25
}, },
orientationInDegrees: 50.912 orientationInDegrees: 50.912,
updatedTime: new Date(),
}, },
{ {
id: "sh3", id: "sh3",
@@ -65,40 +69,41 @@ export function generateMockShuttles(): IShuttle[] {
latitude: 30, latitude: 30,
longitude: 40 longitude: 40
}, },
orientationInDegrees: 321.019 orientationInDegrees: 321.019,
updatedTime: new Date(),
}, },
]; ];
} }
export function generateMockRoutes(): IRoute[] { export function generateMockRoutes(): IRoute[] {
return [ return [
{ id: "r1", name: "Route 1", color: "red", systemId: "sys1", polylineCoordinates: [] }, { id: "r1", name: "Route 1", color: "red", systemId: "sys1", polylineCoordinates: [], updatedTime: new Date() },
{ id: "r2", name: "Route 2", color: "blue", systemId: "sys2", polylineCoordinates: [] }, { id: "r2", name: "Route 2", color: "blue", systemId: "sys2", polylineCoordinates: [], updatedTime: new Date() },
{ id: "r3", name: "Route 3", color: "green", systemId: "sys3", polylineCoordinates: [] }, { id: "r3", name: "Route 3", color: "green", systemId: "sys3", polylineCoordinates: [], updatedTime: new Date() },
]; ];
} }
export function generateMockStops(): IStop[] { export function generateMockStops(): IStop[] {
return [ return [
{ id: "st1", name: "Stop A", systemId: "sys1", coordinates: { latitude: 10, longitude: 20 } }, { id: "st1", name: "Stop A", systemId: "sys1", coordinates: { latitude: 10, longitude: 20 }, updatedTime: new Date() },
{ id: "st2", name: "Stop B", systemId: "sys2", coordinates: { latitude: 15, longitude: 25 } }, { id: "st2", name: "Stop B", systemId: "sys2", coordinates: { latitude: 15, longitude: 25 }, updatedTime: new Date() },
{ id: "st3", name: "Stop C", systemId: "sys3", coordinates: { latitude: 30, longitude: 40 } }, { id: "st3", name: "Stop C", systemId: "sys3", coordinates: { latitude: 30, longitude: 40 }, updatedTime: new Date() },
]; ];
} }
export function generateMockOrderedStops(): IOrderedStop[] { export function generateMockOrderedStops(): IOrderedStop[] {
return [ return [
{ stopId: "st1", routeId: "r1", position: 1, systemId: "sys1" }, { stopId: "st1", routeId: "r1", position: 1, systemId: "sys1", updatedTime: new Date(), },
{ stopId: "st1", routeId: "r2", position: 2, systemId: "sys1" }, { stopId: "st1", routeId: "r2", position: 2, systemId: "sys1", updatedTime: new Date(), },
{ stopId: "st2", routeId: "r1", position: 3, systemId: "sys1" }, { stopId: "st2", routeId: "r1", position: 3, systemId: "sys1", updatedTime: new Date(), },
{ stopId: "st2", routeId: "r2", position: 4, systemId: "sys1" }, { stopId: "st2", routeId: "r2", position: 4, systemId: "sys1", updatedTime: new Date(), },
]; ];
} }
export function generateMockEtas(): IEta[] { export function generateMockEtas(): IEta[] {
return [ return [
{ shuttleId: "sh1", stopId: "st1", secondsRemaining: 120, systemId: "sys1" }, { shuttleId: "sh1", stopId: "st1", secondsRemaining: 120, systemId: "sys1", updatedTime: new Date() },
{ shuttleId: "sh1", stopId: "st2", secondsRemaining: 180, systemId: "sys1" }, { shuttleId: "sh1", stopId: "st2", secondsRemaining: 180, systemId: "sys1", updatedTime: new Date() },
{ shuttleId: "sh2", stopId: "st3", secondsRemaining: 240, systemId: "sys1" }, { shuttleId: "sh2", stopId: "st3", secondsRemaining: 240, systemId: "sys1", updatedTime: new Date() },
]; ];
} }