feat: split client to separate class
This commit is contained in:
@@ -1,5 +1,4 @@
|
|||||||
import { RequestInput } from "../libraries/relay.ts";
|
import type { RelayAdapter, RequestInput } from "../libraries/adapter.ts";
|
||||||
import { RelayAdapter } from "../mod.ts";
|
|
||||||
|
|
||||||
export const adapter: RelayAdapter = {
|
export const adapter: RelayAdapter = {
|
||||||
async fetch({ method, url, search, body }: RequestInput) {
|
async fetch({ method, url, search, body }: RequestInput) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@valkyr/relay",
|
"name": "@valkyr/relay",
|
||||||
"version": "0.1.2",
|
"version": "0.2.0",
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./mod.ts",
|
".": "./mod.ts",
|
||||||
"./http": "./adapters/http.ts"
|
"./http": "./adapters/http.ts"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import z, { ZodObject, ZodRawShape } from "zod";
|
import z, { ZodObject, ZodRawShape } from "zod";
|
||||||
|
|
||||||
import { RelayError } from "./errors.ts";
|
import type { RelayError } from "./errors.ts";
|
||||||
|
|
||||||
export class Action<TActionState extends ActionState = ActionState> {
|
export class Action<TActionState extends ActionState = ActionState> {
|
||||||
constructor(readonly state: TActionState) {}
|
constructor(readonly state: TActionState) {}
|
||||||
|
|||||||
12
libraries/adapter.ts
Normal file
12
libraries/adapter.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import type { RouteMethod } from "./route.ts";
|
||||||
|
|
||||||
|
export type RelayAdapter = {
|
||||||
|
fetch(input: RequestInput): Promise<unknown>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type RequestInput = {
|
||||||
|
method: RouteMethod;
|
||||||
|
url: string;
|
||||||
|
search: string;
|
||||||
|
body?: string;
|
||||||
|
};
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
import z from "zod";
|
import z from "zod";
|
||||||
|
|
||||||
import { BadRequestError, InternalServerError, NotFoundError, RelayError } from "./errors.ts";
|
import { BadRequestError, InternalServerError, NotFoundError, RelayError } from "./errors.ts";
|
||||||
import { Route, RouteMethod } from "./route.ts";
|
import type { Route, RouteMethod } from "./route.ts";
|
||||||
|
|
||||||
const SUPPORTED_MEHODS = ["GET", "POST", "PUT", "PATCH", "DELETE"];
|
const SUPPORTED_MEHODS = ["GET", "POST", "PUT", "PATCH", "DELETE"];
|
||||||
|
|
||||||
export class Api<TRoutes extends Route[]> {
|
export class RelayAPI<TRoutes extends Route[]> {
|
||||||
/**
|
/**
|
||||||
* Route maps funneling registered routes to the specific methods supported by
|
* Route maps funneling registered routes to the specific methods supported by
|
||||||
* the relay instance.
|
* the relay instance.
|
||||||
@@ -35,7 +35,7 @@ export class Api<TRoutes extends Route[]> {
|
|||||||
*
|
*
|
||||||
* @param routes - Routes to register with the instance.
|
* @param routes - Routes to register with the instance.
|
||||||
*/
|
*/
|
||||||
constructor(routes: TRoutes) {
|
constructor({ routes }: Config<TRoutes>) {
|
||||||
const methods: (keyof typeof this.routes)[] = [];
|
const methods: (keyof typeof this.routes)[] = [];
|
||||||
for (const route of routes) {
|
for (const route of routes) {
|
||||||
this.#validateRoutePath(route);
|
this.#validateRoutePath(route);
|
||||||
@@ -264,6 +264,10 @@ function toResponse(result: object | RelayError | Response | void): Response {
|
|||||||
|--------------------------------------------------------------------------------
|
|--------------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
type Config<TRoutes extends Route[]> = {
|
||||||
|
routes: TRoutes;
|
||||||
|
};
|
||||||
|
|
||||||
type Routes = {
|
type Routes = {
|
||||||
POST: Route[];
|
POST: Route[];
|
||||||
GET: Route[];
|
GET: Route[];
|
||||||
|
|||||||
146
libraries/client.ts
Normal file
146
libraries/client.ts
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
import z, { ZodType } from "zod";
|
||||||
|
|
||||||
|
import type { RelayAdapter, RequestInput } from "./adapter.ts";
|
||||||
|
import type { Route, RouteMethod } from "./route.ts";
|
||||||
|
|
||||||
|
export class RelayClient<TRoutes extends Route[]> {
|
||||||
|
/**
|
||||||
|
* Route index in the '${method} ${path}' format allowing for quick access to
|
||||||
|
* a specific route.
|
||||||
|
*/
|
||||||
|
readonly #index = new Map<string, Route>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiate a new Relay instance.
|
||||||
|
*
|
||||||
|
* @param config - Relay configuration to apply to the instance.
|
||||||
|
* @param routes - Routes to register with the instance.
|
||||||
|
*/
|
||||||
|
constructor(readonly config: RelayConfig<TRoutes>) {
|
||||||
|
for (const route of config.routes) {
|
||||||
|
this.#index.set(`${route.method} ${route.path}`, route);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a "POST" request through the relay `fetch` adapter.
|
||||||
|
*
|
||||||
|
* @param path - Path to send request to.
|
||||||
|
* @param args - List of request arguments.
|
||||||
|
*/
|
||||||
|
async post<
|
||||||
|
TPath extends Extract<TRoutes[number], { state: { method: "POST" } }>["state"]["path"],
|
||||||
|
TRoute extends Extract<TRoutes[number], { state: { method: "POST"; path: TPath } }>,
|
||||||
|
>(path: TPath, ...args: TRoute["args"]): Promise<RelayResponse<TRoute>> {
|
||||||
|
return this.#send("POST", path, args) as RelayResponse<TRoute>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a "GET" request through the relay `fetch` adapter.
|
||||||
|
*
|
||||||
|
* @param path - Path to send request to.
|
||||||
|
* @param args - List of request arguments.
|
||||||
|
*/
|
||||||
|
async get<
|
||||||
|
TPath extends Extract<TRoutes[number], { state: { method: "GET" } }>["state"]["path"],
|
||||||
|
TRoute extends Extract<TRoutes[number], { state: { method: "GET"; path: TPath } }>,
|
||||||
|
>(path: TPath, ...args: TRoute["args"]): Promise<RelayResponse<TRoute>> {
|
||||||
|
return this.#send("GET", path, args) as RelayResponse<TRoute>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a "PUT" request through the relay `fetch` adapter.
|
||||||
|
*
|
||||||
|
* @param path - Path to send request to.
|
||||||
|
* @param args - List of request arguments.
|
||||||
|
*/
|
||||||
|
async put<
|
||||||
|
TPath extends Extract<TRoutes[number], { state: { method: "PUT" } }>["state"]["path"],
|
||||||
|
TRoute extends Extract<TRoutes[number], { state: { method: "PUT"; path: TPath } }>,
|
||||||
|
>(path: TPath, ...args: TRoute["args"]): Promise<RelayResponse<TRoute>> {
|
||||||
|
return this.#send("PUT", path, args) as RelayResponse<TRoute>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a "PATCH" request through the relay `fetch` adapter.
|
||||||
|
*
|
||||||
|
* @param path - Path to send request to.
|
||||||
|
* @param args - List of request arguments.
|
||||||
|
*/
|
||||||
|
async patch<
|
||||||
|
TPath extends Extract<TRoutes[number], { state: { method: "PATCH" } }>["state"]["path"],
|
||||||
|
TRoute extends Extract<TRoutes[number], { state: { method: "PATCH"; path: TPath } }>,
|
||||||
|
>(path: TPath, ...args: TRoute["args"]): Promise<RelayResponse<TRoute>> {
|
||||||
|
return this.#send("PATCH", path, args) as RelayResponse<TRoute>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a "DELETE" request through the relay `fetch` adapter.
|
||||||
|
*
|
||||||
|
* @param path - Path to send request to.
|
||||||
|
* @param args - List of request arguments.
|
||||||
|
*/
|
||||||
|
async delete<
|
||||||
|
TPath extends Extract<TRoutes[number], { state: { method: "DELETE" } }>["state"]["path"],
|
||||||
|
TRoute extends Extract<TRoutes[number], { state: { method: "DELETE"; path: TPath } }>,
|
||||||
|
>(path: TPath, ...args: TRoute["args"]): Promise<RelayResponse<TRoute>> {
|
||||||
|
return this.#send("DELETE", path, args) as RelayResponse<TRoute>;
|
||||||
|
}
|
||||||
|
|
||||||
|
async #send(method: RouteMethod, url: string, args: any[]) {
|
||||||
|
const route = this.#index.get(`${method} ${url}`);
|
||||||
|
if (route === undefined) {
|
||||||
|
throw new Error(`RelayClient > Failed to send request for '${method} ${url}' route, not found.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ### Input
|
||||||
|
|
||||||
|
const input: RequestInput = { method, url: `${this.config.url}${url}`, search: "" };
|
||||||
|
|
||||||
|
let index = 0; // argument incrementor
|
||||||
|
|
||||||
|
if (route.state.params !== undefined) {
|
||||||
|
const params = args[index++] as { [key: string]: string };
|
||||||
|
for (const key in params) {
|
||||||
|
input.url = input.url.replace(`:${key}`, params[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (route.state.search !== undefined) {
|
||||||
|
const search = args[index++] as { [key: string]: string };
|
||||||
|
const pieces: string[] = [];
|
||||||
|
for (const key in search) {
|
||||||
|
pieces.push(`${key}=${search[key]}`);
|
||||||
|
}
|
||||||
|
if (pieces.length > 0) {
|
||||||
|
input.search = `?${pieces.join("&")}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (route.state.body !== undefined) {
|
||||||
|
input.body = JSON.stringify(args[index++]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ### Fetch
|
||||||
|
|
||||||
|
const data = await this.config.adapter.fetch(input);
|
||||||
|
if (route.state.output !== undefined) {
|
||||||
|
return route.state.output.parse(data);
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------------
|
||||||
|
| Types
|
||||||
|
|--------------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
type RelayResponse<TRoute extends Route> = TRoute["state"]["output"] extends ZodType ? z.infer<TRoute["state"]["output"]> : void;
|
||||||
|
|
||||||
|
type RelayConfig<TRoutes extends Route[]> = {
|
||||||
|
url: string;
|
||||||
|
adapter: RelayAdapter;
|
||||||
|
routes: TRoutes;
|
||||||
|
};
|
||||||
@@ -1,6 +1,4 @@
|
|||||||
import z, { ZodType } from "zod";
|
import type { Route, RouteMethod } from "./route.ts";
|
||||||
|
|
||||||
import { Route, RouteMethod } from "./route.ts";
|
|
||||||
|
|
||||||
export class Relay<TRoutes extends Route[]> {
|
export class Relay<TRoutes extends Route[]> {
|
||||||
/**
|
/**
|
||||||
@@ -9,28 +7,20 @@ export class Relay<TRoutes extends Route[]> {
|
|||||||
*/
|
*/
|
||||||
readonly #index = new Map<string, Route>();
|
readonly #index = new Map<string, Route>();
|
||||||
|
|
||||||
|
declare readonly $inferRoutes: TRoutes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instantiate a new Relay instance.
|
* Instantiate a new Relay instance.
|
||||||
*
|
*
|
||||||
* @param config - Relay configuration to apply to the instance.
|
* @param config - Relay configuration to apply to the instance.
|
||||||
* @param routes - Routes to register with the instance.
|
* @param routes - Routes to register with the instance.
|
||||||
*/
|
*/
|
||||||
constructor(
|
constructor(readonly routes: TRoutes) {
|
||||||
readonly config: RelayConfig,
|
|
||||||
routes: TRoutes,
|
|
||||||
) {
|
|
||||||
for (const route of routes) {
|
for (const route of routes) {
|
||||||
this.#index.set(`${route.method} ${route.path}`, route);
|
this.#index.set(`${route.method} ${route.path}`, route);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Override relay url configuration.
|
|
||||||
*/
|
|
||||||
set url(value: string) {
|
|
||||||
this.config.url = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve a route for the given method/path combination which can be further extended
|
* Retrieve a route for the given method/path combination which can be further extended
|
||||||
* for serving incoming third party requests.
|
* for serving incoming third party requests.
|
||||||
@@ -71,139 +61,4 @@ export class Relay<TRoutes extends Route[]> {
|
|||||||
}
|
}
|
||||||
return route as TRoute;
|
return route as TRoute;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
|--------------------------------------------------------------------------------
|
|
||||||
| Client
|
|
||||||
|--------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send a "POST" request through the relay `fetch` adapter.
|
|
||||||
*
|
|
||||||
* @param path - Path to send request to.
|
|
||||||
* @param args - List of request arguments.
|
|
||||||
*/
|
|
||||||
async post<
|
|
||||||
TPath extends Extract<TRoutes[number], { state: { method: "POST" } }>["state"]["path"],
|
|
||||||
TRoute extends Extract<TRoutes[number], { state: { method: "POST"; path: TPath } }>,
|
|
||||||
>(path: TPath, ...args: TRoute["args"]): Promise<RelayResponse<TRoute>> {
|
|
||||||
return this.#send("POST", path, args) as RelayResponse<TRoute>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send a "GET" request through the relay `fetch` adapter.
|
|
||||||
*
|
|
||||||
* @param path - Path to send request to.
|
|
||||||
* @param args - List of request arguments.
|
|
||||||
*/
|
|
||||||
async get<
|
|
||||||
TPath extends Extract<TRoutes[number], { state: { method: "GET" } }>["state"]["path"],
|
|
||||||
TRoute extends Extract<TRoutes[number], { state: { method: "GET"; path: TPath } }>,
|
|
||||||
>(path: TPath, ...args: TRoute["args"]): Promise<RelayResponse<TRoute>> {
|
|
||||||
return this.#send("GET", path, args) as RelayResponse<TRoute>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send a "PUT" request through the relay `fetch` adapter.
|
|
||||||
*
|
|
||||||
* @param path - Path to send request to.
|
|
||||||
* @param args - List of request arguments.
|
|
||||||
*/
|
|
||||||
async put<
|
|
||||||
TPath extends Extract<TRoutes[number], { state: { method: "PUT" } }>["state"]["path"],
|
|
||||||
TRoute extends Extract<TRoutes[number], { state: { method: "PUT"; path: TPath } }>,
|
|
||||||
>(path: TPath, ...args: TRoute["args"]): Promise<RelayResponse<TRoute>> {
|
|
||||||
return this.#send("PUT", path, args) as RelayResponse<TRoute>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send a "PATCH" request through the relay `fetch` adapter.
|
|
||||||
*
|
|
||||||
* @param path - Path to send request to.
|
|
||||||
* @param args - List of request arguments.
|
|
||||||
*/
|
|
||||||
async patch<
|
|
||||||
TPath extends Extract<TRoutes[number], { state: { method: "PATCH" } }>["state"]["path"],
|
|
||||||
TRoute extends Extract<TRoutes[number], { state: { method: "PATCH"; path: TPath } }>,
|
|
||||||
>(path: TPath, ...args: TRoute["args"]): Promise<RelayResponse<TRoute>> {
|
|
||||||
return this.#send("PATCH", path, args) as RelayResponse<TRoute>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send a "DELETE" request through the relay `fetch` adapter.
|
|
||||||
*
|
|
||||||
* @param path - Path to send request to.
|
|
||||||
* @param args - List of request arguments.
|
|
||||||
*/
|
|
||||||
async delete<
|
|
||||||
TPath extends Extract<TRoutes[number], { state: { method: "DELETE" } }>["state"]["path"],
|
|
||||||
TRoute extends Extract<TRoutes[number], { state: { method: "DELETE"; path: TPath } }>,
|
|
||||||
>(path: TPath, ...args: TRoute["args"]): Promise<RelayResponse<TRoute>> {
|
|
||||||
return this.#send("DELETE", path, args) as RelayResponse<TRoute>;
|
|
||||||
}
|
|
||||||
|
|
||||||
async #send(method: RouteMethod, url: string, args: any[]) {
|
|
||||||
const route = this.route(method, url);
|
|
||||||
|
|
||||||
// ### Input
|
|
||||||
|
|
||||||
const input: RequestInput = { method, url: `${this.config.url}${url}`, search: "" };
|
|
||||||
|
|
||||||
let index = 0; // argument incrementor
|
|
||||||
|
|
||||||
if (route.state.params !== undefined) {
|
|
||||||
const params = args[index++] as { [key: string]: string };
|
|
||||||
for (const key in params) {
|
|
||||||
input.url = input.url.replace(`:${key}`, params[key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (route.state.search !== undefined) {
|
|
||||||
const search = args[index++] as { [key: string]: string };
|
|
||||||
const pieces: string[] = [];
|
|
||||||
for (const key in search) {
|
|
||||||
pieces.push(`${key}=${search[key]}`);
|
|
||||||
}
|
|
||||||
if (pieces.length > 0) {
|
|
||||||
input.search = `?${pieces.join("&")}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (route.state.body !== undefined) {
|
|
||||||
input.body = JSON.stringify(args[index++]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ### Fetch
|
|
||||||
|
|
||||||
const data = await this.config.adapter.fetch(input);
|
|
||||||
if (route.state.output !== undefined) {
|
|
||||||
return route.state.output.parse(data);
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
|--------------------------------------------------------------------------------
|
|
||||||
| Types
|
|
||||||
|--------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
type RelayResponse<TRoute extends Route> = TRoute["state"]["output"] extends ZodType ? z.infer<TRoute["state"]["output"]> : void;
|
|
||||||
|
|
||||||
type RelayConfig = {
|
|
||||||
url: string;
|
|
||||||
adapter: RelayAdapter;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type RelayAdapter = {
|
|
||||||
fetch(input: RequestInput): Promise<unknown>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type RequestInput = {
|
|
||||||
method: RouteMethod;
|
|
||||||
url: string;
|
|
||||||
search: string;
|
|
||||||
body?: string;
|
|
||||||
};
|
|
||||||
|
|||||||
2
mod.ts
2
mod.ts
@@ -1,5 +1,7 @@
|
|||||||
export * from "./libraries/action.ts";
|
export * from "./libraries/action.ts";
|
||||||
|
export * from "./libraries/adapter.ts";
|
||||||
export * from "./libraries/api.ts";
|
export * from "./libraries/api.ts";
|
||||||
|
export * from "./libraries/client.ts";
|
||||||
export * from "./libraries/errors.ts";
|
export * from "./libraries/errors.ts";
|
||||||
export * from "./libraries/relay.ts";
|
export * from "./libraries/relay.ts";
|
||||||
export * from "./libraries/route.ts";
|
export * from "./libraries/route.ts";
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
import z from "zod";
|
import z from "zod";
|
||||||
|
|
||||||
import { adapter } from "../../adapters/http.ts";
|
|
||||||
import { Relay } from "../../libraries/relay.ts";
|
import { Relay } from "../../libraries/relay.ts";
|
||||||
import { route } from "../../libraries/route.ts";
|
import { route } from "../../libraries/route.ts";
|
||||||
import { UserSchema } from "./user.ts";
|
import { UserSchema } from "./user.ts";
|
||||||
|
|
||||||
export const relay = new Relay({ url: "http://localhost:36573", adapter }, [
|
export const relay = new Relay([
|
||||||
route
|
route
|
||||||
.post("/users")
|
.post("/users")
|
||||||
.body(UserSchema.omit({ id: true, createdAt: true }))
|
.body(UserSchema.omit({ id: true, createdAt: true }))
|
||||||
@@ -21,3 +20,5 @@ export const relay = new Relay({ url: "http://localhost:36573", adapter }, [
|
|||||||
route.delete("/users/:userId").params({ userId: z.string().check(z.uuid()) }),
|
route.delete("/users/:userId").params({ userId: z.string().check(z.uuid()) }),
|
||||||
route.get("/add-two").search({ a: z.coerce.number(), b: z.coerce.number() }).response(z.number()),
|
route.get("/add-two").search({ a: z.coerce.number(), b: z.coerce.number() }).response(z.number()),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
export type RelayRoutes = typeof relay.$inferRoutes;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Api } from "../../libraries/api.ts";
|
import { RelayAPI } from "../../libraries/api.ts";
|
||||||
import { NotFoundError } from "../../mod.ts";
|
import { NotFoundError } from "../../mod.ts";
|
||||||
import { addTwoNumbers } from "./actions.ts";
|
import { addTwoNumbers } from "./actions.ts";
|
||||||
import { relay } from "./relay.ts";
|
import { relay } from "./relay.ts";
|
||||||
@@ -6,33 +6,35 @@ import { User } from "./user.ts";
|
|||||||
|
|
||||||
export let users: User[] = [];
|
export let users: User[] = [];
|
||||||
|
|
||||||
export const api = new Api([
|
export const api = new RelayAPI({
|
||||||
relay.route("POST", "/users").handle(async ({ name, email }) => {
|
routes: [
|
||||||
const id = crypto.randomUUID();
|
relay.route("POST", "/users").handle(async ({ name, email }) => {
|
||||||
users.push({ id, name, email, createdAt: new Date() });
|
const id = crypto.randomUUID();
|
||||||
return id;
|
users.push({ id, name, email, createdAt: new Date() });
|
||||||
}),
|
return id;
|
||||||
relay.route("GET", "/users/:userId").handle(async ({ userId }) => {
|
}),
|
||||||
const user = users.find((user) => user.id === userId);
|
relay.route("GET", "/users/:userId").handle(async ({ userId }) => {
|
||||||
if (user === undefined) {
|
const user = users.find((user) => user.id === userId);
|
||||||
return new NotFoundError();
|
if (user === undefined) {
|
||||||
}
|
return new NotFoundError();
|
||||||
return user;
|
|
||||||
}),
|
|
||||||
relay.route("PUT", "/users/:userId").handle(async ({ userId, name, email }) => {
|
|
||||||
for (const user of users) {
|
|
||||||
if (user.id === userId) {
|
|
||||||
user.name = name;
|
|
||||||
user.email = email;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
return user;
|
||||||
}),
|
}),
|
||||||
relay.route("DELETE", "/users/:userId").handle(async ({ userId }) => {
|
relay.route("PUT", "/users/:userId").handle(async ({ userId, name, email }) => {
|
||||||
users = users.filter((user) => user.id !== userId);
|
for (const user of users) {
|
||||||
}),
|
if (user.id === userId) {
|
||||||
relay
|
user.name = name;
|
||||||
.route("GET", "/add-two")
|
user.email = email;
|
||||||
.actions([addTwoNumbers])
|
break;
|
||||||
.handle(async ({ added }) => added),
|
}
|
||||||
]);
|
}
|
||||||
|
}),
|
||||||
|
relay.route("DELETE", "/users/:userId").handle(async ({ userId }) => {
|
||||||
|
users = users.filter((user) => user.id !== userId);
|
||||||
|
}),
|
||||||
|
relay
|
||||||
|
.route("GET", "/add-two")
|
||||||
|
.actions([addTwoNumbers])
|
||||||
|
.handle(async ({ added }) => added),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|||||||
@@ -3,11 +3,14 @@ import "./mocks/server.ts";
|
|||||||
import { assertEquals, assertObjectMatch, assertRejects } from "@std/assert";
|
import { assertEquals, assertObjectMatch, assertRejects } from "@std/assert";
|
||||||
import { afterAll, beforeAll, describe, it } from "@std/testing/bdd";
|
import { afterAll, beforeAll, describe, it } from "@std/testing/bdd";
|
||||||
|
|
||||||
import { relay } from "./mocks/relay.ts";
|
import { adapter } from "../adapters/http.ts";
|
||||||
|
import { RelayClient } from "../libraries/client.ts";
|
||||||
|
import { relay, RelayRoutes } from "./mocks/relay.ts";
|
||||||
import { api, users } from "./mocks/server.ts";
|
import { api, users } from "./mocks/server.ts";
|
||||||
|
|
||||||
describe("Relay", () => {
|
describe("Relay", () => {
|
||||||
let server: Deno.HttpServer<Deno.NetAddr>;
|
let server: Deno.HttpServer<Deno.NetAddr>;
|
||||||
|
let client: RelayClient<RelayRoutes>;
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
server = Deno.serve(
|
server = Deno.serve(
|
||||||
@@ -20,6 +23,7 @@ describe("Relay", () => {
|
|||||||
},
|
},
|
||||||
async (request) => api.handle(request),
|
async (request) => api.handle(request),
|
||||||
);
|
);
|
||||||
|
client = new RelayClient({ url: "http://localhost:36573", adapter, routes: relay.routes });
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(async () => {
|
afterAll(async () => {
|
||||||
@@ -27,16 +31,16 @@ describe("Relay", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should successfully relay users", async () => {
|
it("should successfully relay users", async () => {
|
||||||
const userId = await relay.post("/users", { name: "John Doe", email: "john.doe@fixture.none" });
|
const userId = await client.post("/users", { name: "John Doe", email: "john.doe@fixture.none" });
|
||||||
|
|
||||||
assertEquals(typeof userId, "string");
|
assertEquals(typeof userId, "string");
|
||||||
assertEquals(users.length, 1);
|
assertEquals(users.length, 1);
|
||||||
|
|
||||||
const user = await relay.get("/users/:userId", { userId });
|
const user = await client.get("/users/:userId", { userId });
|
||||||
|
|
||||||
assertEquals(user.createdAt instanceof Date, true);
|
assertEquals(user.createdAt instanceof Date, true);
|
||||||
|
|
||||||
await relay.put("/users/:userId", { userId }, { name: "Jane Doe", email: "jane.doe@fixture.none" });
|
await client.put("/users/:userId", { userId }, { name: "Jane Doe", email: "jane.doe@fixture.none" });
|
||||||
|
|
||||||
assertEquals(users.length, 1);
|
assertEquals(users.length, 1);
|
||||||
assertObjectMatch(users[0], {
|
assertObjectMatch(users[0], {
|
||||||
@@ -44,16 +48,16 @@ describe("Relay", () => {
|
|||||||
email: "jane.doe@fixture.none",
|
email: "jane.doe@fixture.none",
|
||||||
});
|
});
|
||||||
|
|
||||||
await relay.delete("/users/:userId", { userId });
|
await client.delete("/users/:userId", { userId });
|
||||||
|
|
||||||
assertEquals(users.length, 0);
|
assertEquals(users.length, 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should successfully run .actions", async () => {
|
it("should successfully run .actions", async () => {
|
||||||
assertEquals(await relay.get("/add-two", { a: 1, b: 1 }), 2);
|
assertEquals(await client.get("/add-two", { a: 1, b: 1 }), 2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should reject .actions with error", async () => {
|
it("should reject .actions with error", async () => {
|
||||||
await assertRejects(() => relay.get("/add-two", { a: -1, b: 1 }), "Invalid input numbers added");
|
await assertRejects(() => client.get("/add-two", { a: -1, b: 1 }), "Invalid input numbers added");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user