mirror of
https://github.com/brendan-ch/project-inter-server.git
synced 2026-04-17 16:00:32 +00:00
Merge pull request #46 from brendan-ch/feat/data-timestamps
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
export interface IEntityWithOptionalTimestamp {
|
export interface IEntityWithTimestamp {
|
||||||
millisecondsSinceEpoch?: number;
|
updatedTime: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IEntityWithId {
|
export interface IEntityWithId {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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(),
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -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(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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(),
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -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) => {
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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,9 +28,11 @@ 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(
|
||||||
|
etasForShuttle.map(async ({
|
||||||
secondsRemaining,
|
secondsRemaining,
|
||||||
stopId,
|
stopId,
|
||||||
|
updatedTime
|
||||||
}): Promise<Eta | null> => {
|
}): Promise<Eta | null> => {
|
||||||
return {
|
return {
|
||||||
secondsRemaining,
|
secondsRemaining,
|
||||||
@@ -37,6 +40,7 @@ export const ShuttleResolvers: Resolvers<ServerContext> = {
|
|||||||
shuttle: parent,
|
shuttle: parent,
|
||||||
shuttleId: parent.id,
|
shuttleId: parent.id,
|
||||||
systemId: system.id,
|
systemId: system.id,
|
||||||
|
updatedTime: updatedTime,
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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
28
src/scalars/DateTime.ts
Normal 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;
|
||||||
|
},
|
||||||
|
})
|
||||||
@@ -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);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -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 = {
|
||||||
|
|||||||
@@ -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(() => {
|
||||||
|
|||||||
@@ -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();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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() },
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user