mirror of
https://github.com/brendan-ch/project-inter-server.git
synced 2026-04-17 07:50:31 +00:00
Merge pull request #42 from brendan-ch/feat/graphql-parking-data
[INT-64] feat/graphql-parking-data
This commit is contained in:
@@ -1,3 +1,6 @@
|
|||||||
|
# The Interchange system schema.
|
||||||
|
# Note how Passio ID and parking ID are abstracted away
|
||||||
|
# from the endpoints.
|
||||||
type System {
|
type System {
|
||||||
id: ID!
|
id: ID!
|
||||||
name: String!
|
name: String!
|
||||||
@@ -8,7 +11,11 @@ type System {
|
|||||||
shuttles: [Shuttle!]
|
shuttles: [Shuttle!]
|
||||||
shuttle(id: ID): Shuttle
|
shuttle(id: ID): Shuttle
|
||||||
|
|
||||||
# TODO: Implement these in system resolvers
|
parkingSystem: ParkingSystem
|
||||||
|
}
|
||||||
|
|
||||||
|
type ParkingSystem {
|
||||||
|
systemId: ID!
|
||||||
parkingStructures: [ParkingStructure!]
|
parkingStructures: [ParkingStructure!]
|
||||||
parkingStructure(id: ID): ParkingStructure
|
parkingStructure(id: ID): ParkingStructure
|
||||||
}
|
}
|
||||||
@@ -84,6 +91,8 @@ type Query {
|
|||||||
|
|
||||||
isNotificationScheduled(input: NotificationInput!): Boolean
|
isNotificationScheduled(input: NotificationInput!): Boolean
|
||||||
secondsThresholdForNotification(input: NotificationInput!): Int
|
secondsThresholdForNotification(input: NotificationInput!): Int
|
||||||
|
|
||||||
|
schemaVersion: ID!
|
||||||
}
|
}
|
||||||
|
|
||||||
# Mutations
|
# Mutations
|
||||||
|
|||||||
@@ -8,9 +8,11 @@ import { StopResolvers } from "./resolvers/StopResolvers";
|
|||||||
import { ShuttleResolvers } from "./resolvers/ShuttleResolvers";
|
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";
|
||||||
|
|
||||||
export const MergedResolvers: Resolvers<ServerContext> = {
|
export const MergedResolvers: Resolvers<ServerContext> = {
|
||||||
...QueryResolvers,
|
...QueryResolvers,
|
||||||
|
...ParkingSystemResolvers,
|
||||||
...SystemResolvers,
|
...SystemResolvers,
|
||||||
...RouteResolvers,
|
...RouteResolvers,
|
||||||
...ShuttleResolvers,
|
...ShuttleResolvers,
|
||||||
|
|||||||
@@ -7,6 +7,12 @@ import { ShuttleGetterSetterRepository } from "../repositories/ShuttleGetterSett
|
|||||||
import { InMemoryNotificationRepository } from "../repositories/InMemoryNotificationRepository";
|
import { InMemoryNotificationRepository } from "../repositories/InMemoryNotificationRepository";
|
||||||
import { AppleNotificationSender } from "../notifications/senders/AppleNotificationSender";
|
import { AppleNotificationSender } from "../notifications/senders/AppleNotificationSender";
|
||||||
import { ApiBasedShuttleRepositoryLoader } from "../loaders/shuttle/ApiBasedShuttleRepositoryLoader";
|
import { ApiBasedShuttleRepositoryLoader } from "../loaders/shuttle/ApiBasedShuttleRepositoryLoader";
|
||||||
|
import { ParkingGetterSetterRepository } from "../repositories/ParkingGetterSetterRepository";
|
||||||
|
import { InMemoryParkingRepository } from "../repositories/InMemoryParkingRepository";
|
||||||
|
import {
|
||||||
|
buildParkingRepositoryLoaderIfExists,
|
||||||
|
ParkingRepositoryLoaderBuilderArguments
|
||||||
|
} from "../loaders/parking/buildParkingRepositoryLoaderIfExists";
|
||||||
|
|
||||||
export interface InterchangeSystemBuilderArguments {
|
export interface InterchangeSystemBuilderArguments {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -20,16 +26,23 @@ export interface InterchangeSystemBuilderArguments {
|
|||||||
* ID for fetching shuttle data from the Passio GO! system.
|
* ID for fetching shuttle data from the Passio GO! system.
|
||||||
*/
|
*/
|
||||||
passioSystemId: string;
|
passioSystemId: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ID for the parking repository ID in the codebase.
|
||||||
|
*/
|
||||||
|
parkingSystemId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class InterchangeSystem {
|
export class InterchangeSystem {
|
||||||
constructor(
|
private constructor(
|
||||||
public name: string,
|
public name: string,
|
||||||
public id: string,
|
public id: string,
|
||||||
public shuttleTimedDataLoader: TimedApiBasedRepositoryLoader,
|
public shuttleTimedDataLoader: TimedApiBasedRepositoryLoader,
|
||||||
public shuttleRepository: ShuttleGetterSetterRepository,
|
public shuttleRepository: ShuttleGetterSetterRepository,
|
||||||
public notificationScheduler: ETANotificationScheduler,
|
public notificationScheduler: ETANotificationScheduler,
|
||||||
public notificationRepository: NotificationRepository,
|
public notificationRepository: NotificationRepository,
|
||||||
|
public parkingTimedDataLoader: TimedApiBasedRepositoryLoader | null,
|
||||||
|
public parkingRepository: ParkingGetterSetterRepository | null,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,6 +74,9 @@ export class InterchangeSystem {
|
|||||||
);
|
);
|
||||||
notificationScheduler.startListeningForUpdates();
|
notificationScheduler.startListeningForUpdates();
|
||||||
|
|
||||||
|
let { parkingRepository, timedParkingLoader } = this.buildParkingLoaderAndRepository(args.parkingSystemId);
|
||||||
|
timedParkingLoader?.start();
|
||||||
|
|
||||||
return new InterchangeSystem(
|
return new InterchangeSystem(
|
||||||
args.name,
|
args.name,
|
||||||
args.id,
|
args.id,
|
||||||
@@ -68,6 +84,8 @@ export class InterchangeSystem {
|
|||||||
shuttleRepository,
|
shuttleRepository,
|
||||||
notificationScheduler,
|
notificationScheduler,
|
||||||
notificationRepository,
|
notificationRepository,
|
||||||
|
timedParkingLoader,
|
||||||
|
parkingRepository,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,6 +118,9 @@ export class InterchangeSystem {
|
|||||||
);
|
);
|
||||||
notificationScheduler.startListeningForUpdates();
|
notificationScheduler.startListeningForUpdates();
|
||||||
|
|
||||||
|
let { parkingRepository, timedParkingLoader } = this.buildParkingLoaderAndRepository(args.parkingSystemId);
|
||||||
|
// Timed parking loader is not started
|
||||||
|
|
||||||
return new InterchangeSystem(
|
return new InterchangeSystem(
|
||||||
args.name,
|
args.name,
|
||||||
args.id,
|
args.id,
|
||||||
@@ -107,6 +128,32 @@ export class InterchangeSystem {
|
|||||||
shuttleRepository,
|
shuttleRepository,
|
||||||
notificationScheduler,
|
notificationScheduler,
|
||||||
notificationRepository,
|
notificationRepository,
|
||||||
|
timedParkingLoader,
|
||||||
|
parkingRepository,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static buildParkingLoaderAndRepository(id?: string) {
|
||||||
|
if (id === undefined) {
|
||||||
|
return { parkingRepository: null, timedParkingLoader: null };
|
||||||
|
}
|
||||||
|
|
||||||
|
let parkingRepository: ParkingGetterSetterRepository | null = new InMemoryParkingRepository();
|
||||||
|
const loaderBuilderArguments: ParkingRepositoryLoaderBuilderArguments = {
|
||||||
|
id,
|
||||||
|
repository: parkingRepository,
|
||||||
|
};
|
||||||
|
let parkingLoader = buildParkingRepositoryLoaderIfExists(
|
||||||
|
loaderBuilderArguments,
|
||||||
|
);
|
||||||
|
|
||||||
|
let timedParkingLoader = null;
|
||||||
|
if (parkingLoader == null) {
|
||||||
|
parkingRepository = null;
|
||||||
|
} else {
|
||||||
|
timedParkingLoader = new TimedApiBasedRepositoryLoader(parkingLoader);
|
||||||
|
}
|
||||||
|
return { parkingRepository, timedParkingLoader };
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,11 @@ import { ApolloServer } from "@apollo/server";
|
|||||||
import { startStandaloneServer } from "@apollo/server/standalone";
|
import { startStandaloneServer } from "@apollo/server/standalone";
|
||||||
import { MergedResolvers } from "./MergedResolvers";
|
import { MergedResolvers } from "./MergedResolvers";
|
||||||
import { ServerContext } from "./ServerContext";
|
import { ServerContext } from "./ServerContext";
|
||||||
import { loadShuttleTestData, supportedIntegrationTestSystems } from "./loaders/shuttle/loadShuttleTestData";
|
import { loadShuttleTestData } from "./loaders/shuttle/loadShuttleTestData";
|
||||||
import { InterchangeSystem, InterchangeSystemBuilderArguments } from "./entities/InterchangeSystem";
|
import { InterchangeSystem, InterchangeSystemBuilderArguments } from "./entities/InterchangeSystem";
|
||||||
|
import { ChapmanApiBasedParkingRepositoryLoader } from "./loaders/parking/ChapmanApiBasedParkingRepositoryLoader";
|
||||||
|
import { supportedIntegrationTestSystems } from "./loaders/supportedIntegrationTestSystems";
|
||||||
|
import { loadParkingTestData } from "./loaders/parking/loadParkingTestData";
|
||||||
|
|
||||||
const typeDefs = readFileSync("./schema.graphqls", "utf8");
|
const typeDefs = readFileSync("./schema.graphqls", "utf8");
|
||||||
|
|
||||||
@@ -13,6 +16,7 @@ const supportedSystems: InterchangeSystemBuilderArguments[] = [
|
|||||||
{
|
{
|
||||||
id: "1",
|
id: "1",
|
||||||
passioSystemId: "263",
|
passioSystemId: "263",
|
||||||
|
parkingSystemId: ChapmanApiBasedParkingRepositoryLoader.id,
|
||||||
name: "Chapman University",
|
name: "Chapman University",
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -35,6 +39,9 @@ async function main() {
|
|||||||
|
|
||||||
// TODO: Have loading of different data for different systems in the future
|
// TODO: Have loading of different data for different systems in the future
|
||||||
await loadShuttleTestData(system.shuttleRepository);
|
await loadShuttleTestData(system.shuttleRepository);
|
||||||
|
if (system.parkingRepository) {
|
||||||
|
await loadParkingTestData(system.parkingRepository);
|
||||||
|
}
|
||||||
|
|
||||||
return system;
|
return system;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ export class ChapmanApiBasedParkingRepositoryLoader implements ParkingRepository
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private constructIParkingStructureFromJson(jsonStructure: any) {
|
public constructIParkingStructureFromJson(jsonStructure: any) {
|
||||||
const structureToReturn: IParkingStructure = {
|
const structureToReturn: IParkingStructure = {
|
||||||
capacity: jsonStructure.Capacity,
|
capacity: jsonStructure.Capacity,
|
||||||
coordinates: {
|
coordinates: {
|
||||||
@@ -57,7 +57,7 @@ export class ChapmanApiBasedParkingRepositoryLoader implements ParkingRepository
|
|||||||
},
|
},
|
||||||
id: ChapmanApiBasedParkingRepositoryLoader.generateId(jsonStructure.Address),
|
id: ChapmanApiBasedParkingRepositoryLoader.generateId(jsonStructure.Address),
|
||||||
name: jsonStructure.Name,
|
name: jsonStructure.Name,
|
||||||
spotsAvailable: jsonStructure.CurrentCount,
|
spotsAvailable: jsonStructure.CurrentCount > jsonStructure.Capacity ? jsonStructure.Capacity : jsonStructure.CurrentCount,
|
||||||
address: jsonStructure.Address
|
address: jsonStructure.Address
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { ParkingGetterSetterRepository } from "../../repositories/ParkingGetterSetterRepository";
|
import { ParkingGetterSetterRepository } from "../../repositories/ParkingGetterSetterRepository";
|
||||||
import { ChapmanApiBasedParkingRepositoryLoader } from "./ChapmanApiBasedParkingRepositoryLoader";
|
import { ChapmanApiBasedParkingRepositoryLoader } from "./ChapmanApiBasedParkingRepositoryLoader";
|
||||||
|
|
||||||
interface ParkingRepositoryBuilderArguments {
|
export interface ParkingRepositoryLoaderBuilderArguments {
|
||||||
id: string;
|
id: string;
|
||||||
repository: ParkingGetterSetterRepository;
|
repository: ParkingGetterSetterRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildParkingRepositoryLoaderIfExists(args: ParkingRepositoryBuilderArguments) {
|
export function buildParkingRepositoryLoaderIfExists(args: ParkingRepositoryLoaderBuilderArguments) {
|
||||||
if (args.id === ChapmanApiBasedParkingRepositoryLoader.id) {
|
if (args.id === ChapmanApiBasedParkingRepositoryLoader.id) {
|
||||||
return new ChapmanApiBasedParkingRepositoryLoader(args.repository);
|
return new ChapmanApiBasedParkingRepositoryLoader(args.repository);
|
||||||
}
|
}
|
||||||
|
|||||||
33
src/loaders/parking/loadParkingTestData.ts
Normal file
33
src/loaders/parking/loadParkingTestData.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import { ParkingGetterSetterRepository } from "../../repositories/ParkingGetterSetterRepository";
|
||||||
|
import { IParkingStructure } from "../../entities/ParkingRepositoryEntities";
|
||||||
|
|
||||||
|
const parkingStructures: IParkingStructure[] = [
|
||||||
|
{
|
||||||
|
address: "300 E Walnut, Orange, CA 92867",
|
||||||
|
capacity: 871,
|
||||||
|
spotsAvailable: 211,
|
||||||
|
coordinates: {
|
||||||
|
latitude: 33.7945513,
|
||||||
|
longitude: -117.8518707,
|
||||||
|
},
|
||||||
|
name: "Anderson Structure",
|
||||||
|
id: "1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
address: "200 W Sycamore Ave, Orange, CA 92866-1053",
|
||||||
|
capacity: 692,
|
||||||
|
spotsAvailable: 282,
|
||||||
|
coordinates: {
|
||||||
|
latitude: 33.792937,
|
||||||
|
longitude: -117.854782
|
||||||
|
},
|
||||||
|
name: "Barrera",
|
||||||
|
id: "2",
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
export async function loadParkingTestData(repository: ParkingGetterSetterRepository) {
|
||||||
|
await Promise.all(parkingStructures.map(async structure => {
|
||||||
|
await repository.addOrUpdateParkingStructure(structure);
|
||||||
|
}))
|
||||||
|
}
|
||||||
@@ -1,15 +1,7 @@
|
|||||||
// Mock data
|
// Mock data
|
||||||
import { IEta, IOrderedStop, IRoute, IShuttle, IStop } from "../../entities/ShuttleRepositoryEntities";
|
import { IEta, IOrderedStop, IRoute, IShuttle, IStop } from "../../entities/ShuttleRepositoryEntities";
|
||||||
import { ShuttleGetterSetterRepository } from "../../repositories/ShuttleGetterSetterRepository";
|
import { ShuttleGetterSetterRepository } from "../../repositories/ShuttleGetterSetterRepository";
|
||||||
import { InterchangeSystemBuilderArguments } from "../../entities/InterchangeSystem";
|
import { supportedIntegrationTestSystems } from "../supportedIntegrationTestSystems";
|
||||||
|
|
||||||
export const supportedIntegrationTestSystems: InterchangeSystemBuilderArguments[] = [
|
|
||||||
{
|
|
||||||
id: "1",
|
|
||||||
name: "Chapman University",
|
|
||||||
passioSystemId: "263",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const redRoutePolylineCoordinates = [
|
const redRoutePolylineCoordinates = [
|
||||||
{
|
{
|
||||||
|
|||||||
11
src/loaders/supportedIntegrationTestSystems.ts
Normal file
11
src/loaders/supportedIntegrationTestSystems.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { InterchangeSystemBuilderArguments } from "../entities/InterchangeSystem";
|
||||||
|
import { ChapmanApiBasedParkingRepositoryLoader } from "./parking/ChapmanApiBasedParkingRepositoryLoader";
|
||||||
|
|
||||||
|
export const supportedIntegrationTestSystems: InterchangeSystemBuilderArguments[] = [
|
||||||
|
{
|
||||||
|
id: "1",
|
||||||
|
name: "Chapman University",
|
||||||
|
passioSystemId: "263",
|
||||||
|
parkingSystemId: ChapmanApiBasedParkingRepositoryLoader.id,
|
||||||
|
},
|
||||||
|
];
|
||||||
29
src/resolvers/ParkingSystemResolvers.ts
Normal file
29
src/resolvers/ParkingSystemResolvers.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { Resolvers } from "../generated/graphql";
|
||||||
|
import { ServerContext } from "../ServerContext";
|
||||||
|
|
||||||
|
export const ParkingSystemResolvers: Resolvers<ServerContext> = {
|
||||||
|
ParkingSystem: {
|
||||||
|
parkingStructures: async (parent, _args, contextValue, _info) => {
|
||||||
|
const system = contextValue.findSystemById(parent.systemId);
|
||||||
|
if (!system) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const parkingRepository = system.parkingRepository;
|
||||||
|
if (!parkingRepository) return [];
|
||||||
|
|
||||||
|
return await parkingRepository.getParkingStructures();
|
||||||
|
},
|
||||||
|
parkingStructure: async (parent, args, contextValue, info) => {
|
||||||
|
if (!args.id) return null;
|
||||||
|
|
||||||
|
const system = contextValue.findSystemById(parent.systemId);
|
||||||
|
if (!system) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const parkingRepository = system.parkingRepository;
|
||||||
|
if (!parkingRepository) return null;
|
||||||
|
|
||||||
|
return await parkingRepository.getParkingStructureById(args.id);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
import { ServerContext } from "../ServerContext";
|
import { ServerContext } from "../ServerContext";
|
||||||
import { Resolvers } from "../generated/graphql";
|
import { Resolvers } from "../generated/graphql";
|
||||||
|
|
||||||
|
const GRAPHQL_SCHEMA_VERSION = "1";
|
||||||
|
|
||||||
export const QueryResolvers: Resolvers<ServerContext> = {
|
export const QueryResolvers: Resolvers<ServerContext> = {
|
||||||
Query: {
|
Query: {
|
||||||
systems: async (_parent, args, contextValue, _info) => {
|
systems: async (_parent, args, contextValue, _info) => {
|
||||||
@@ -46,5 +48,8 @@ export const QueryResolvers: Resolvers<ServerContext> = {
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
schemaVersion: async (_parent, _args, _contextValue, _info) => {
|
||||||
|
return GRAPHQL_SCHEMA_VERSION;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,11 +78,17 @@ export const SystemResolvers: Resolvers<ServerContext> = {
|
|||||||
|
|
||||||
return await system.shuttleRepository.getShuttles();
|
return await system.shuttleRepository.getShuttles();
|
||||||
},
|
},
|
||||||
parkingStructures: async (_parent, _args, _contextValue, _info) => {
|
parkingSystem: async (parent, _args, contextValue, _info) => {
|
||||||
return [];
|
const system = contextValue.findSystemById(parent.id);
|
||||||
},
|
if (!system) {
|
||||||
parkingStructure: async (_parent, _args, _contextValue, _info) => {
|
return null;
|
||||||
return null;
|
}
|
||||||
|
|
||||||
|
if (!system.parkingRepository) return null;
|
||||||
|
|
||||||
|
return {
|
||||||
|
systemId: parent.id,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,8 +42,6 @@ describe("ChapmanApiBasedParkingRepositoryLoader", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
describe("fetchAndUpdateParkingStructures", () => {
|
describe("fetchAndUpdateParkingStructures", () => {
|
||||||
it("fetches and update parking structures with unique IDs", async () => {
|
it("fetches and update parking structures with unique IDs", async () => {
|
||||||
updateGlobalFetchMockJson(chapmanParkingStructureData);
|
updateGlobalFetchMockJson(chapmanParkingStructureData);
|
||||||
@@ -89,4 +87,20 @@ describe("ChapmanApiBasedParkingRepositoryLoader", () => {
|
|||||||
});
|
});
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("constructIParkingStructureFromJson", () => {
|
||||||
|
it("normalizes the spots available if it's over the capacity", async () => {
|
||||||
|
const sampleJsonStructure: any = {
|
||||||
|
Capacity: 10,
|
||||||
|
Latitude: 1,
|
||||||
|
Longitude: 1,
|
||||||
|
Address: "300 E Walnut, Orange, CA 92867",
|
||||||
|
Name: "Anderson Structure",
|
||||||
|
CurrentCount: 11,
|
||||||
|
};
|
||||||
|
|
||||||
|
const returnedStructure = loader.constructIParkingStructureFromJson(sampleJsonStructure);
|
||||||
|
expect(returnedStructure.spotsAvailable).toEqual(returnedStructure.capacity);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
139
test/resolvers/ParkingSystemResolverTests.test.ts
Normal file
139
test/resolvers/ParkingSystemResolverTests.test.ts
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
import { beforeEach, describe, expect, it } from "@jest/globals";
|
||||||
|
import { generateParkingStructures } from "../testHelpers/mockDataGenerators";
|
||||||
|
import { setupTestServerContext, setupTestServerHolder } from "../testHelpers/apolloTestServerHelpers";
|
||||||
|
import { InterchangeSystem } from "../../src/entities/InterchangeSystem";
|
||||||
|
import assert = require("node:assert");
|
||||||
|
|
||||||
|
describe("ParkingSystemResolver", () => {
|
||||||
|
const holder = setupTestServerHolder();
|
||||||
|
const context = setupTestServerContext();
|
||||||
|
|
||||||
|
let mockSystem: InterchangeSystem;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
mockSystem = context.systems[0];
|
||||||
|
});
|
||||||
|
|
||||||
|
async function getResponseFromQueryNeedingSystemId(query: string) {
|
||||||
|
return await holder.testServer.executeOperation({
|
||||||
|
query,
|
||||||
|
variables: {
|
||||||
|
systemId: mockSystem.id,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
contextValue: context,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("parkingStructures", () => {
|
||||||
|
const query = `
|
||||||
|
query GetParkingStructuresBySystem($systemId: ID!) {
|
||||||
|
system(id: $systemId) {
|
||||||
|
parkingSystem {
|
||||||
|
parkingStructures {
|
||||||
|
name
|
||||||
|
id
|
||||||
|
capacity
|
||||||
|
spotsAvailable
|
||||||
|
coordinates {
|
||||||
|
latitude
|
||||||
|
longitude
|
||||||
|
}
|
||||||
|
address
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
it("gets parking structures associated with the system id", async () => {
|
||||||
|
const expectedParkingStructures = generateParkingStructures();
|
||||||
|
await Promise.all(expectedParkingStructures.map(async (structure) => {
|
||||||
|
await context.systems[0].parkingRepository?.addOrUpdateParkingStructure(structure);
|
||||||
|
}));
|
||||||
|
|
||||||
|
const response = await getResponseFromQueryNeedingSystemId(query);
|
||||||
|
|
||||||
|
assert(response.body.kind === "single");
|
||||||
|
expect(response.body.singleResult.errors).toBeUndefined();
|
||||||
|
const parkingStructures = (response.body.singleResult.data as any).system.parkingSystem.parkingStructures;
|
||||||
|
expect(parkingStructures).toEqual(expectedParkingStructures);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns a blank array if there are no parking structures", async () => {
|
||||||
|
const response = await getResponseFromQueryNeedingSystemId(query);
|
||||||
|
|
||||||
|
assert(response.body.kind === "single");
|
||||||
|
expect(response.body.singleResult.errors).toBeUndefined();
|
||||||
|
const parkingStructures = (response.body.singleResult.data as any).system.parkingSystem.parkingStructures;
|
||||||
|
expect(parkingStructures).toHaveLength(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe("parkingStructure", () => {
|
||||||
|
async function getResponseForParkingStructureQuery(parkingStructureId: string) {
|
||||||
|
const query = `
|
||||||
|
query GetParkingStructureBySystem($systemId: ID!, $parkingStructureId: ID!) {
|
||||||
|
system(id: $systemId) {
|
||||||
|
parkingSystem {
|
||||||
|
parkingStructure(id: $parkingStructureId) {
|
||||||
|
name
|
||||||
|
id
|
||||||
|
capacity
|
||||||
|
spotsAvailable
|
||||||
|
coordinates {
|
||||||
|
latitude
|
||||||
|
longitude
|
||||||
|
}
|
||||||
|
address
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
return await holder.testServer.executeOperation({
|
||||||
|
query,
|
||||||
|
variables: {
|
||||||
|
systemId: mockSystem.id,
|
||||||
|
parkingStructureId,
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
contextValue: context
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
it("returns the correct parking structure given the id", async () => {
|
||||||
|
const generatedParkingStructures = generateParkingStructures();
|
||||||
|
await Promise.all(generatedParkingStructures.map(async (structure) => {
|
||||||
|
await context.systems[0].parkingRepository?.addOrUpdateParkingStructure(structure);
|
||||||
|
}));
|
||||||
|
const expectedParkingStructure = generatedParkingStructures[1];
|
||||||
|
|
||||||
|
const response = await getResponseForParkingStructureQuery(expectedParkingStructure.id);
|
||||||
|
|
||||||
|
assert(response.body.kind === "single");
|
||||||
|
expect(response.body.singleResult.errors).toBeUndefined();
|
||||||
|
const parkingStructure = (response.body.singleResult.data as any).system.parkingSystem.parkingStructure;
|
||||||
|
expect(parkingStructure).toEqual(expectedParkingStructure);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns null if there is no matching parking structure", async () => {
|
||||||
|
const generatedParkingStructures = generateParkingStructures();
|
||||||
|
await Promise.all(generatedParkingStructures.map(async (structure) => {
|
||||||
|
await context.systems[0].parkingRepository?.addOrUpdateParkingStructure(structure);
|
||||||
|
}));
|
||||||
|
|
||||||
|
const nonexistentId = generatedParkingStructures[0].id + "12345";
|
||||||
|
|
||||||
|
const response = await getResponseForParkingStructureQuery(nonexistentId);
|
||||||
|
|
||||||
|
assert(response.body.kind === "single");
|
||||||
|
expect(response.body.singleResult.errors).toBeUndefined();
|
||||||
|
const parkingStructure = (response.body.singleResult.data as any).system.parkingSystem.parkingStructure;
|
||||||
|
expect(parkingStructure).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,6 +1,11 @@
|
|||||||
import { beforeEach, describe, expect, it } from "@jest/globals";
|
import { beforeEach, describe, expect, it } from "@jest/globals";
|
||||||
import { setupTestServerContext, setupTestServerHolder } from "../testHelpers/apolloTestServerHelpers";
|
import { setupTestServerContext, setupTestServerHolder } from "../testHelpers/apolloTestServerHelpers";
|
||||||
import { generateMockRoutes, generateMockShuttles, generateMockStops } from "../testHelpers/mockDataGenerators";
|
import {
|
||||||
|
generateMockRoutes,
|
||||||
|
generateMockShuttles,
|
||||||
|
generateMockStops,
|
||||||
|
generateParkingStructures
|
||||||
|
} from "../testHelpers/mockDataGenerators";
|
||||||
import {
|
import {
|
||||||
addMockRouteToRepository,
|
addMockRouteToRepository,
|
||||||
addMockShuttleToRepository,
|
addMockShuttleToRepository,
|
||||||
@@ -308,4 +313,5 @@ describe("SystemResolvers", () => {
|
|||||||
expect(shuttles.length === expectedShuttles.length);
|
expect(shuttles.length === expectedShuttles.length);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { readFileSync } from "fs";
|
import { readFileSync } from "fs";
|
||||||
import { ApolloServer } from "@apollo/server";
|
import { ApolloServer } from "@apollo/server";
|
||||||
import { MergedResolvers } from "../../src/MergedResolvers";
|
import { MergedResolvers } from "../../src/MergedResolvers";
|
||||||
import { UnoptimizedInMemoryShuttleRepository } from "../../src/repositories/UnoptimizedInMemoryShuttleRepository";
|
|
||||||
import { beforeEach } from "@jest/globals";
|
import { beforeEach } from "@jest/globals";
|
||||||
import { ServerContext } from "../../src/ServerContext";
|
import { ServerContext } from "../../src/ServerContext";
|
||||||
import { ETANotificationScheduler } from "../../src/notifications/schedulers/ETANotificationScheduler";
|
|
||||||
import { InMemoryNotificationRepository } from "../../src/repositories/InMemoryNotificationRepository";
|
|
||||||
import { InterchangeSystem } from "../../src/entities/InterchangeSystem";
|
import { InterchangeSystem } from "../../src/entities/InterchangeSystem";
|
||||||
|
import {
|
||||||
|
ChapmanApiBasedParkingRepositoryLoader
|
||||||
|
} from "../../src/loaders/parking/ChapmanApiBasedParkingRepositoryLoader";
|
||||||
|
|
||||||
|
|
||||||
function setUpTestServer() {
|
function setUpTestServer() {
|
||||||
@@ -20,7 +20,10 @@ function setUpTestServer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const systemInfoForTesting = {
|
const systemInfoForTesting = {
|
||||||
id: "1", name: "Chapman University", passioSystemId: "263"
|
id: "1",
|
||||||
|
name: "Chapman University",
|
||||||
|
passioSystemId: "263",
|
||||||
|
parkingSystemId: ChapmanApiBasedParkingRepositoryLoader.id,
|
||||||
};
|
};
|
||||||
|
|
||||||
export function buildSystemForTesting() {
|
export function buildSystemForTesting() {
|
||||||
|
|||||||
@@ -1,8 +1,37 @@
|
|||||||
import { IEta, IOrderedStop, IRoute, IShuttle, IStop } from "../../src/entities/ShuttleRepositoryEntities";
|
import { IEta, IOrderedStop, IRoute, IShuttle, IStop } from "../../src/entities/ShuttleRepositoryEntities";
|
||||||
|
import { IParkingStructure } from "../../src/entities/ParkingRepositoryEntities";
|
||||||
|
|
||||||
// Use a single set of generators in case any of the
|
// Use a single set of generators in case any of the
|
||||||
// interfaces change in the future
|
// interfaces change in the future
|
||||||
|
|
||||||
|
export function generateParkingStructures(): IParkingStructure[] {
|
||||||
|
// Copied from debugger
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
"capacity": 871,
|
||||||
|
"coordinates": {
|
||||||
|
"latitude": 33.7945513,
|
||||||
|
"longitude": -117.8518707
|
||||||
|
},
|
||||||
|
"id": "b0723baf8a6b8bcc37c821473373049e",
|
||||||
|
"name": "Anderson Structure",
|
||||||
|
"spotsAvailable": 163,
|
||||||
|
"address": "300 E Walnut, Orange, CA 92867"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"capacity": 692,
|
||||||
|
"coordinates": {
|
||||||
|
"latitude": 33.792937,
|
||||||
|
"longitude": -117.854782
|
||||||
|
},
|
||||||
|
"id": "81b9e1ed004cf6def2e6c568aaf79ece",
|
||||||
|
"name": "Barrera",
|
||||||
|
"spotsAvailable": 179,
|
||||||
|
"address": "200 W Sycamore Ave, Orange, CA 92866-1053"
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
export function generateMockShuttles(): IShuttle[] {
|
export function generateMockShuttles(): IShuttle[] {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user