Skip to content

Commit 44a6887

Browse files
authored
Deprecate base urls (#3)
* Add support for multi locations, add some tests, fix some parameter parsing for corridor path etc. Sample requests * multi locationId, tests,bug fixes * Correct logic for locationId * Allow hosting without setting the BASE_URLs env param
1 parent 6e13a57 commit 44a6887

File tree

11 files changed

+76
-70
lines changed

11 files changed

+76
-70
lines changed

src/apidocs/index.ts

Lines changed: 10 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,20 @@
11
import fs from "node:fs";
22
import YAML from "js-yaml";
33
import path from "node:path";
4-
import type { oas3 } from "exegesis-express";
5-
import { LanHostGenerator } from "../common/utils/lanHostHandler.js";
64
import process from "node:process";
75
import type { OpenAPIObject } from "openapi3-ts";
8-
export const NODE_ENV: "production" | "dev" = process.env?.NODE_ENV || "dev";
9-
export const PORT = process.env.PORT || 3000;
10-
11-
if (NODE_ENV === "production" && !process.env.BASE_URLS)
12-
throw new Error("BASE_URLS env variable must be set in prod");
13-
14-
//import { PORT } from '../index.js';
15-
const lanAddress = new LanHostGenerator().lanAddress();
16-
17-
const servers: oas3.ServerObject[] = (
18-
process.env?.BASE_URLS || "http://localhost"
19-
)
20-
.split(",")
21-
.concat(
22-
lanAddress && typeof lanAddress === "string" && NODE_ENV !== "production"
23-
? `http://${lanAddress}`
24-
: ""
25-
)
26-
.filter((u) => URL.canParse(u))
27-
.map((u) => ({ url: NODE_ENV === "production" ? u : `${u}:${PORT}` }));
28-
29-
console.log(`Server root is: ${servers.map((p) => p.url).join(";\t")}`);
306

317
export const apidocs = {
32-
edr: {
33-
...(YAML.load(
8+
edr:
9+
(YAML.load(
3410
fs.readFileSync(path.join(process.cwd(), `/src/apidocs/edr.yaml`), {
3511
encoding: "utf8",
3612
})
37-
) as OpenAPIObject),
38-
servers: servers.map(({ url, description }) => ({
39-
url: `${url}/edr`,
40-
description,
41-
})),
42-
},
43-
features: {
44-
...(YAML.load(
45-
fs.readFileSync(path.join(process.cwd(), `/src/apidocs/features.yaml`), {
46-
encoding: "utf8",
47-
})
48-
) as OpenAPIObject),
49-
servers: servers.map(({ url, description }) => ({
50-
url: `${url}/features`,
51-
description,
52-
})),
53-
},
54-
};
13+
) as OpenAPIObject)
14+
,
15+
features: YAML.load(
16+
fs.readFileSync(path.join(process.cwd(), `/src/apidocs/features.yaml`), {
17+
encoding: "utf8",
18+
})
19+
) as OpenAPIObject
20+
};

src/app.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,20 @@ import cors from "cors";
33
import requestLogger from "./logging/index.js";
44
import edrApi from "./standards/edr/index.js";
55
import featuresApi from "./standards/features/index.js";
6-
import { NODE_ENV, PORT } from "./apidocs/index.js";
76
import http from "node:http";
87
import { scalar } from "./common/utils/scalar.js";
8+
import type { oas3 } from "exegesis";
9+
import { LanHostGenerator } from "./common/utils/lanHostHandler.js";
10+
11+
const { lanAddress } = new LanHostGenerator();
12+
const NODE_ENV: string = process.env.NODE_ENV || "development";
13+
export const PORT = process.env.PORT || "8000";
14+
15+
if (lanAddress) console.log(`Server also accessible on LAN at ${lanAddress}`)
16+
// Declare alternative servers
17+
// Modifies the document you get :root/*/api
18+
// TODO point to a env parameter
19+
export const servers: oas3.ServerObject[] = [];
920
const app = express();
1021

1122
app.use(cors());
@@ -44,8 +55,8 @@ app.get("/", (_, res, next) => {
4455
next();
4556
});
4657

47-
app.use(await featuresApi);
48-
app.use(await edrApi);
58+
app.use("/features", await featuresApi);
59+
app.use("/edr", await edrApi);
4960

5061
const server = http.createServer(app);
5162
export default server;

src/common/common.utils.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,3 +184,26 @@ export function filterCollectionByZ({
184184
: true;
185185
return minCheck && maxCheck && levelCheck;
186186
}
187+
188+
/**
189+
* Run before other plugins that require the serverObject to be set
190+
* @param location string like this edr not /edr
191+
* @returns
192+
*/
193+
export function setRelativeServerLocation(location: string): ExegesisPlugin {
194+
return {
195+
info: { name: "x-exegesis-plugin", },
196+
makeExegesisPlugin(data) {
197+
return {
198+
//preRouting:(ctx)=>{}
199+
postRouting: (ctx: ExegesisPluginContext) => {
200+
let url = `${ctx.req.protocol || "https"}://${ctx.req.headers.host}/${location}`
201+
ctx.api.serverObject = {
202+
url,
203+
description: `Auto-generated server root`
204+
}
205+
}
206+
}
207+
}
208+
}
209+
}

src/common/utils/lanHostHandler.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ import os from "node:os";
22
import process from "node:process";
33

44
export class LanHostGenerator {
5-
constructor() {}
5+
constructor() { }
66

77
/**
88
* @returns number|string. If no address is found, then returns 0
99
*/
10-
lanAddress(): string | number {
10+
public get lanAddress(): string | number {
1111
let ips: number | string = 0;
1212
if (process.argv.includes("--host")) {
1313
const ifaces = os.networkInterfaces();
@@ -24,6 +24,6 @@ export class LanHostGenerator {
2424
}
2525
});
2626
}
27-
return ips;
27+
return ips === 0 ? undefined : ips
2828
}
2929
}

src/index.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { PORT } from "./apidocs/index.js";
2-
import app from "./app.js";
1+
import app, { PORT } from "./app.js";
32

4-
app.listen(PORT,()=>console.log(`listening on ${PORT}`));
3+
app.listen(PORT, () => console.log(`listening on ${PORT}`));

src/models/db.ts

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,17 @@
1-
/**
2-
* @description central interface to export initialized models
3-
* Use Kysely because there is a new plugin to ease use of kysley-postgis plugin
4-
*/
5-
//import { Pool } from "pg";
61
import pg from 'pg';
72
const { Pool, types } = pg;
83
pg.types.setTypeParser(types.builtins.NUMERIC, (val) => parseFloat(val));
94
pg.types.setTypeParser(types.builtins.INT4, (val) => parseInt(val));
5+
import process from "node:process";
6+
let NODE_ENV = process.env.NODE_ENV || "development";
107
import {
118
Kysely,
129
PostgresDialect,
1310
ParseJSONResultsPlugin,
14-
type TableExpression,
1511
type ReferenceExpression
1612
} from 'kysely';
1713
import * as models from './models.js';
18-
import { NODE_ENV } from '../apidocs/index.js';
14+
import { NODE_ENV } from '../app.ts';
1915

2016
export interface Database {
2117
isd: models.ISD_GlobalHourly;

src/services/isd.service.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ export const isdCollection: EdrCollection = {
104104
],
105105
data_queries: {
106106
locations: {
107-
output_formats: ["GeoJSON","JSON","CoverageJSON"],
107+
output_formats: ["GeoJSON", "JSON", "CoverageJSON"],
108108
default_output_format: "GeoJSON",
109109
async handler({ datetime, z, bbox, crs, instanceId, limit, offset }) {
110110
let { rows, numberMatched } = await db
@@ -194,7 +194,7 @@ export const isdCollection: EdrCollection = {
194194
},
195195
items: {
196196
default_output_format: "GeoJSON",
197-
output_formats: ["GeoJSON", "YAML", "CoverageJSON","JSON"],
197+
output_formats: ["GeoJSON", "YAML", "CoverageJSON", "JSON"],
198198
},
199199
radius: {
200200
default_output_format: "JSON",
@@ -263,7 +263,7 @@ export const isdCollection: EdrCollection = {
263263
.select(eb => eb.fn.agg("ARRAY_AGG", ["country"])
264264
.distinct()
265265
.as("countries")), "@>", sql`ARRAY[${sql.join(locationId)}]::varchar[]`)
266-
conditions.push(check);
266+
conditions.push(eb("country", "in", locationId.map(p => p.toString())));
267267
return check;
268268
})
269269
.executeTakeFirstOrThrow(

src/standards/edr/controllers/apiController.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { ExegesisContext } from "exegesis-express";
22
import { EdrRqManager } from "../edr.utils.js";
33
import { scalar } from "../../../common/utils/scalar.js";
44
import { jsonlikeToYAML } from "../../../common/common.utils.js";
5+
import { servers } from "../../../app.js";
56

67
function getServiceDoc(ctx: ExegesisContext): void {
78
ctx.res
@@ -15,8 +16,12 @@ function getServiceDesc(ctx: ExegesisContext): void {
1516
ctx,
1617
collections: [],
1718
}).outputFormatParser("json", ["json", "yaml"]);
18-
const { openApiDoc } = ctx.api;
19-
19+
let { openApiDoc } = ctx.api;
20+
openApiDoc = {
21+
...openApiDoc,
22+
// This negates the need to set a env variable with the server url
23+
servers: [ctx.api.serverObject, ...openApiDoc.servers || [], ...servers.map(({ url, description }) => ({ url: `${url}/edr`, description }))]
24+
}
2025
ctx.res.status(200);
2126
switch (f) {
2227
case "json":

src/standards/edr/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@ import trajectoryController from "./controllers/trajectoryController.js";
1414
import { middleware, type ExegesisOptions } from "exegesis-express";
1515
import { postToGetEdrPlugin } from "./plugins/postToGetEdrPlugin.js";
1616
import { apidocs } from "../../apidocs/index.js";
17+
import { setRelativeServerLocation } from "../../common/common.utils.js";
1718

1819
//console.log(doc)
1920
const options: ExegesisOptions = {
20-
plugins: [postToGetEdrPlugin()],
21+
plugins: [setRelativeServerLocation("edr"),
22+
postToGetEdrPlugin()],
2123
lazyCompileValidationSchemas: true,
2224
//customFormats: { datetimeRegex },
2325
controllers: {

src/standards/features/controllers/apiController.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,28 @@ import type { ExegesisContext } from "exegesis-express";
22
import { scalar } from "../../../common/utils/scalar.js";
33
import { FeaturesRqManager } from "../features.utils.js";
44
import { jsonlikeToYAML } from "../../../common/common.utils.js";
5+
import { servers } from "../../../app.js";
56

67
function getServiceDesc(ctx: ExegesisContext): void {
78
const { f, contentTypeHeader } = new FeaturesRqManager({
89
ctx,
910
collections: [],
1011
}).outputFormatParser("JSON", ["JSON", "YAML", "HTML"]);
1112

13+
let { openApiDoc } = ctx.api;
14+
openApiDoc = { ...openApiDoc, servers: [ctx.api.serverObject, ...openApiDoc.servers || [], ...servers.map(({ url, description }) => ({ url: `${url}/features`, description }))] }
1215
ctx.res.status(200).set(...contentTypeHeader);
1316

1417
switch (f) {
1518
case "json":
1619
ctx.res
1720
.set("content-type", "application/vnd.oai.openapi+json;version=3.0")
18-
.setBody(ctx.api.openApiDoc);
21+
.setBody(openApiDoc);
1922
break;
2023
case "yaml":
2124
ctx.res
2225
.set("content-type", "application/vnd.oai.openapi;version=3.0")
23-
.setBody(jsonlikeToYAML(ctx.api.openApiDoc));
26+
.setBody(jsonlikeToYAML(openApiDoc));
2427
break;
2528
case "html":
2629
ctx.res.redirect(302, "/features/api.html");

0 commit comments

Comments
 (0)