Add initial PIS route
This commit is contained in:
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',
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user