diff --git a/.gitignore b/.gitignore index de4d1f0..6ee3e11 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ dist node_modules +.env diff --git a/package.json b/package.json index a2c35ff..29c31a6 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "license": "MIT", "devDependencies": { "@types/express": "^4.17.21", + "dotenv": "^16.4.5", "nodemon": "^3.1.0", "ts-node": "^10.9.2", "typescript": "^5.3.3" diff --git a/src/controllers/route.ts b/src/controllers/route.ts new file mode 100644 index 0000000..90bc74a --- /dev/null +++ b/src/controllers/route.ts @@ -0,0 +1,69 @@ +export interface IResponseRoute { + code: string; + directionId: number; + headsign: string; + route: { + gtfsId: string; + shortName: string; + longName: string; + mode: string; + } +} + +export interface IRoute { + gtfsId: string; + number: string; + name: string; + direction: number; + headsign: string; + mode: string; +} + + + + +export const parseRoute = (obj: IResponseRoute): IRoute | undefined => { + if (!("route" in obj)) { + console.error("No route in parsed object"); + return; + } + + const routeObj = obj.route; + const gtfsId = routeObj.gtfsId; + const number = routeObj.shortName; + const name = routeObj.longName; + const direction = obj.directionId; + const headsign = obj.headsign; + const mode = routeObj.mode; + + const route: IRoute = { + gtfsId, + number, + name, + direction, + headsign, + mode + } + + return route; +} + + +export const isRoute = (obj: any): obj is IRoute => { + if ( + "code" in obj && + "number" in obj && + "name" in obj && + "direction" in obj && + "headsign" in obj && + "mode" in obj + ) { + return true; + } + return false; +} + +export default { + parseRoute, + isRoute +} diff --git a/src/controllers/stop.ts b/src/controllers/stop.ts new file mode 100644 index 0000000..7c70cdf --- /dev/null +++ b/src/controllers/stop.ts @@ -0,0 +1,46 @@ +import { IResponseRoute, IRoute, parseRoute } from "./route"; + + +export interface IStop { + gtfsId: string; + name: string; + code: string; + routes: IRoute[]; +} + +export const isStop = (obj: any): obj is IStop => { + if ( + "name" in obj && + "gtfsId" in obj && + "code" in obj && + "routes" in obj + ) { + return true; + } + return false; +} + +export const parseStop = (obj: any): IStop | undefined => { + if (!obj.patterns) { + console.error("Incorrect query response, no patterns", obj); + return; + } + const patterns: IResponseRoute[] = obj.patterns; + const routes: IRoute[] = patterns.map(route => { + return parseRoute(route); + }).filter((route): route is IRoute => route != null); + + // TODO all values are not typechecked + const stop: IStop = { + gtfsId: obj.gtfsId, + name: obj.name, + code: obj.code, + routes: routes + } + return stop; +} + +export default { + isStop, + parseStop +} diff --git a/src/controllers/trip.ts b/src/controllers/trip.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/index.ts b/src/index.ts index c14c65a..f096787 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,5 @@ import express from "express"; -import { queryStop, IStop } from "./query"; +import { queryStopById } from "./query"; const PORT: Number = 5000; @@ -12,9 +12,24 @@ api.get("/", (_req: express.Request, res: express.Response) => { res.send("Moi"); }); -api.get("/stops", async (_req, res) => { - const stop: IStop = await queryStop(10); - res.send(stop); +api.get("/bus", async (_req, res) => { + const busStop = await queryStopById("HSL:1220106"); + if (busStop) { + res.send(busStop); + return; + } + res.send({"message": "No stops found"}); + return; +}); + +api.get("/tram", async (_req, res) => { + const tramStop = await queryStopById("HSL:1220419"); + if (tramStop) { + res.send(tramStop); + return; + } + res.send({"message": "No stops found"}); + return; }); api.listen(PORT, () => { diff --git a/src/query.ts b/src/query.ts index e7463b9..852f046 100644 --- a/src/query.ts +++ b/src/query.ts @@ -1,29 +1,14 @@ +import config from "./utils/config"; +import { IStop, parseStop } from "./controllers/stop"; -const API_KEY: string = "25e625c8546141aa83b21b9ba94d4847"; const API_URL_BASE: string = "https://api.digitransit.fi/routing/v1/routers/hsl/index/graphql"; -const API_URL: string = `${API_URL_BASE}?digitransit-subscription-key=${API_KEY}`; - -export interface IStop { - id: number; - name: string; -} +const API_URL: string = `${API_URL_BASE}?digitransit-subscription-key=${config.API_KEY}`; -export const queryStop = async (stopId: number): Promise => { - console.log("Querying the HSL-api for stop", stopId); - console.log(API_URL); - const query: string = ` - { - stops(name: "mäkelänrinne") { - gtfsId - name - code - lat - lon - } - }` +const queryDigitransit = async (query: string) => { + console.log("Querying the HSL-api with query", query); const response = await fetch(API_URL, { method: "post", @@ -32,15 +17,53 @@ export const queryStop = async (stopId: number): Promise => { }, body: JSON.stringify({ query }) }); + const parsed_res = await response.json(); - console.log(parsed_res); + return parsed_res.data; +} +export const queryStopById = async (stopId: string): Promise => { + console.info("Querying the HSL-api for stop", stopId); + console.debug(API_URL); + const query: string = ` + { + stop(id: "${stopId}") { + gtfsId + name + code + patterns { + code + directionId + headsign + route { + gtfsId + shortName + longName + mode + } + } + } + }` - const stop: IStop = { - id: stopId, - name: "TOOO" + const response = await queryDigitransit(query); + if (!response) { + console.error("No response from API"); + return; } - return stop; + + if (!response.stop || !response.stop.patterns) { + console.error("Incorrect response from API", response); + return; + } + + const stop: IStop | undefined = parseStop(response.stop); + if (stop) { + console.debug("Parsed stop:", stop); + return stop; + } + + console.error("Failed parsing stop", stop); + return; }; diff --git a/src/utils/config.ts b/src/utils/config.ts new file mode 100644 index 0000000..424a906 --- /dev/null +++ b/src/utils/config.ts @@ -0,0 +1,12 @@ +import "dotenv/config"; + + +if (process.env.API_KEY == undefined) { + console.error("FATAL ERROR: API_KEY not set"); + process.exit(1); +} + +export const API_KEY = process.env.API_KEY; + + +export default { API_KEY }; diff --git a/yarn.lock b/yarn.lock index 682418d..590d839 100644 --- a/yarn.lock +++ b/yarn.lock @@ -316,6 +316,11 @@ diff@^4.0.1: resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== +dotenv@^16.4.5: + version "16.4.5" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f" + integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg== + ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"