Add initial PIS route
This commit is contained in:
11
package.json
11
package.json
@@ -2,7 +2,16 @@
|
||||
"name": "owlboard-ts",
|
||||
"version": "3.0.0",
|
||||
"description": "TypeScript API Library to interact with the OwlBoard API (> v3.0.0)",
|
||||
"main": "index.js",
|
||||
"main": "./dist/index.js",
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/index.js"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
|
||||
5
src/index.ts
Normal file
5
src/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export * from './lib/base.js';
|
||||
export * from './lib/client.js';
|
||||
export * from './lib/errors.js';
|
||||
|
||||
export * from './modules/pis.js';
|
||||
46
src/lib/base.ts
Normal file
46
src/lib/base.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { ApiError } from "./errors.js";
|
||||
import type { ApiErrorCode } from "./errors.js";
|
||||
import type { ApiEnvelope } from "@owlboard/api-schema-types";
|
||||
|
||||
export interface ApiResult<T> {
|
||||
data: T;
|
||||
producedAt: Date;
|
||||
}
|
||||
|
||||
export class BaseClient {
|
||||
constructor(
|
||||
protected readonly baseUrl: string,
|
||||
protected readonly apiKey?: string
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Handles the Envelope logic
|
||||
*/
|
||||
public async request<T>(path: string, options: RequestInit = {}): Promise<ApiResult<T>> {
|
||||
const url = `${this.baseUrl}${path}`;
|
||||
|
||||
const headers = new Headers(options.headers);
|
||||
headers.set('Content-Type', 'application/json');
|
||||
if (this.apiKey) headers.set('X-OWL-KEY', this.apiKey);
|
||||
|
||||
const response = await fetch(url, { ...options, headers });
|
||||
|
||||
if (!response.ok && !response.headers.get('content-type')?.includes('application/json')) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
// Parse raw envelope
|
||||
const envelope = (await response.json()) as ApiEnvelope.Envelope;
|
||||
|
||||
if (envelope.e !== undefined && envelope.e !== null) {
|
||||
throw new ApiError(envelope.e || {}, response.status);
|
||||
}
|
||||
|
||||
return {
|
||||
data: envelope.d as T,
|
||||
|
||||
// Convert seconds to milliseconds before parsing date
|
||||
producedAt: new Date(Number(envelope.t) * 1000)
|
||||
};
|
||||
}
|
||||
}
|
||||
12
src/lib/client.ts
Normal file
12
src/lib/client.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { BaseClient } from "./base.js";
|
||||
import { PisModule } from "../modules/pis.js";
|
||||
|
||||
export class OwlBoardClient extends BaseClient {
|
||||
public readonly pis: PisModule;
|
||||
|
||||
constructor(baseUrl: string, apiKey?: string) {
|
||||
super(baseUrl, apiKey);
|
||||
|
||||
this.pis = new PisModule(this);
|
||||
}
|
||||
}
|
||||
17
src/lib/errors.ts
Normal file
17
src/lib/errors.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import type { ApiEnvelope } from "@owlboard/api-schema-types";
|
||||
|
||||
type RawErrorObject = NonNullable<ApiEnvelope.Envelope["e"]>;
|
||||
export type ApiErrorCode = NonNullable<RawErrorObject['code']>
|
||||
|
||||
export class ApiError extends Error {
|
||||
public readonly code: ApiErrorCode;
|
||||
public readonly status: number;
|
||||
|
||||
constructor(errorObj: RawErrorObject, status: number) {
|
||||
super(errorObj.msg || 'An unknown error occurred');
|
||||
this.name = 'ApiError';
|
||||
this.status = status;
|
||||
// cast fallback to ensure it satisfies union
|
||||
this.code = errorObj.code || ("SERVER" as ApiErrorCode);
|
||||
}
|
||||
}
|
||||
14
src/modules/pis.ts
Normal file
14
src/modules/pis.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import type { ApiPisObject } from '@owlboard/api-schema-types';
|
||||
import type { BaseClient, ApiResult } from '../lib/base.js';
|
||||
|
||||
export class PisModule {
|
||||
constructor(private client: BaseClient) {}
|
||||
|
||||
async getByStartEndCrs(startCrs: string, endCrs: string): Promise<ApiResult<ApiPisObject.PisObjects>> {
|
||||
const path = `/pis/route/${encodeURIComponent(startCrs.toUpperCase())}/${encodeURIComponent(endCrs.toUpperCase())}`;
|
||||
|
||||
return this.client.request<ApiPisObject.PisObjects>(path, {
|
||||
method: 'GET',
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,15 @@
|
||||
// Visit https://aka.ms/tsconfig to read more about this file
|
||||
"compilerOptions": {
|
||||
// File Layout
|
||||
// "rootDir": "./src",
|
||||
// "outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"outDir": "./dist",
|
||||
|
||||
// Environment Settings
|
||||
// See also https://aka.ms/tsconfig/module
|
||||
"module": "nodenext",
|
||||
"module": "esnext",
|
||||
"target": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"baseUrl": ".",
|
||||
"types": [],
|
||||
// For nodejs:
|
||||
// "lib": ["esnext"],
|
||||
@@ -40,5 +42,6 @@
|
||||
"noUncheckedSideEffectImports": true,
|
||||
"moduleDetection": "force",
|
||||
"skipLibCheck": true,
|
||||
}
|
||||
},
|
||||
"include": ["src/**/*"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user