Skip to content

Commit

Permalink
Implemented error parsing for executor lib, cleaned up client lib
Browse files Browse the repository at this point in the history
  • Loading branch information
GrzegorzManiak committed Jun 4, 2024
1 parent 0cff2f0 commit c7ad291
Show file tree
Hide file tree
Showing 6 changed files with 131 additions and 85 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "noise_validator",
"version": "1.2.9",
"version": "1.2.10",
"main": "./src/index.ts",
"license": "MIT",
"author": {
Expand Down
114 changes: 40 additions & 74 deletions src/client/executor.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ArrayModifier, BinderNamespace, SchemaOutput } from 'noise_validator/src/binder/types';
import { SchemaNamespace } from 'noise_validator/src/schema/types';
import { HTTPMethods } from 'fastify';
import { BinderInputObject } from './types.d';
import { BinderInputObject, GenericAPIDataParamaters, ExecutedAPIResponse, APIRequestObject } from './types.d';
import { GenericError } from 'noise_validator/src/error';


Expand Down Expand Up @@ -30,7 +30,7 @@ const register_api_route = <
BodyOutputSchema extends ArrayModifier.ArrayOrSingle<SchemaNamespace.NestedSchemaLike>,
HeadersOutputSchema extends ArrayModifier.ArrayOrSingle<SchemaNamespace.FlatSchmeaLike>,

BinderCallbackReturn extends SchemaOutput.Types<BodyOutputSchema, HeadersOutputSchema>,
BinderCallbackReturn extends SchemaOutput.Types<BodyOutputSchema, HeadersOutputSchema> & ExecutedAPIResponse,
>(
api_root: string,
route: DynamicURLInputSchema,
Expand Down Expand Up @@ -65,40 +65,30 @@ const register_api_route = <
const execute_api_route = async (
route: string,
method: HTTPMethods,
data: {
body: { [x: string]: unknown },
query: { [x: string]: unknown },
headers: { [x: string]: unknown },
route: { [x: string]: unknown },
cookies: { [x: string]: unknown }
data: GenericAPIDataParamaters
): Promise<ExecutedAPIResponse> => {
const request_object: APIRequestObject = {
method,
headers: {
'Content-Type': 'application/json',
'Cookie': build_cookie_string(data.cookies),
...(data.headers || {})
},
};

// -- Build the request URL
const request_url: string = replace_route_parameters(route, data.route);
const query_string = build_query_string(data.query, request_url);

// -- Check if we can set the body
if (data.body !== undefined && method !== 'GET') {
request_object.headers['Content-Type'] = 'application/json';
request_object.body = JSON.stringify(data.body);;
}
): Promise<{
body: { [x: string]: unknown },
headers: { [x: string]: unknown },
}> => {

// -- Send the request
try {
// -- Serialize the request body
let request_body;
if (data.body !== undefined)
request_body = JSON.stringify(data.body);

// -- Prepare the request URL
let request_url: string = route;
if (data.route !== undefined) request_url = replace_route_parameters(route, data.route);

// -- Prepare the query string
const query_string = build_query_string(data.query, request_url);

// -- Build the API route
const api_request = new Request(route, {
method,
headers: {
'Content-Type': 'application/json',
...(data.headers || {}),
'Cookie': build_cookie_string(data.cookies),
},
body: request_body,
});
const api_request = new Request(query_string, request_object);

// -- Handle the response
const response = await fetch(api_request);
Expand All @@ -108,17 +98,17 @@ const execute_api_route = async (
// -- Convert the headers to an object
const headers: { [x: string]: string } = {};
response.headers.forEach((value, key) => headers[key] = value);

// -- Log headers
return {
body: response_data,
headers: headers
};

return { body: response_data, headers: headers, success: true, status: response.status };
}



// -- Easier to handle errors like this, due to the 'success' key
// as Now you wont have to do a 'instanceof' check etc.
catch (unknown_error) {
const error = await handle_error(unknown_error);
throw error;
return { error, success: false, status: error.code };
}
};

Expand All @@ -137,31 +127,18 @@ const handle_error = async (
): Promise<GenericError> => {

// -- Handle known errors
if (unknown_error instanceof Response) {

// -- Try deserializing the error
try {
const error_data = await unknown_error.json();
const error = new GenericError(
error_data.error.message,
unknown_error.status
);

error.data = error_data.error;
return error;
}

catch (error) {
return new GenericError(
`Failed to create request: ${unknown_error.statusText}`,
unknown_error.status
);
}
if (unknown_error instanceof Response)
try { return GenericError.deserialize(await unknown_error.json()); }
catch (error) {
return GenericError.from_unknown(
error,
new GenericError('Failed to create request', 500),
'handle_error: ' + unknown_error.statusText
)
}


// -- Handle unknown errors
return GenericError.from_unknown(
else return GenericError.from_unknown(
unknown_error,
new GenericError('Failed to create request', 500)
);
Expand All @@ -187,33 +164,22 @@ const build_query_string = (
): string => {
if (query === undefined) return '';
let query_string = ``;

for (let i = 0; i < Object.keys(query).length; i++) {
if (i === 0) query_string += `?`;
else query_string += `&`;

const key = Object.keys(query)[i];
const value = query[key];

query_string += `${key}=${String(value)}`;
}

return `${request_url}${query_string}`;
};



const clean_url = (url: string): string => {

// -- Remove double slashes
url = url.replace(/\/\//g, '/');

// -- Remove trailing slash
if (url.endsWith('/')) url = url.slice(0, -1);

// -- Remove starting slash
if (url.startsWith('/')) url = url.slice(1);

return url;
};

Expand Down
45 changes: 44 additions & 1 deletion src/client/types.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { ArrayModifier, ObjectModifier } from 'noise_validator/src/binder/types';
import { SchemaNamespace } from 'noise_validator/src/schema/types';
import { GenericError } from 'noise_validator/src/error';
import { HTTPMethods } from 'fastify/types/utils';



export namespace DynamicURL {

Expand Down Expand Up @@ -67,4 +71,43 @@ export type BinderInputObject<
(IsEmpty<Query> extends true ? {} : { query: QueryType }) &
(IsEmpty<Headers> extends true ? {} : { headers: HeadersType }) &
(IsEmpty<Cookies> extends true ? {} : { cookies: CookiesType }) &
(IsEmpty<UrlType> extends true ? {} : { route: UrlType });
(IsEmpty<UrlType> extends true ? {} : { route: UrlType });



export type GenericAPIDataParamaters = {
body: { [x: string]: unknown },
query: { [x: string]: unknown },
headers: { [x: string]: unknown },
route: { [x: string]: unknown },
cookies: { [x: string]: unknown }
};



export type APIRequestObject = {
method: HTTPMethods,
headers: { [x: string]: string },
body?: string
};



export type ExecutedAPISuccessResponse = {
body: { [x: string]: unknown },
headers: { [x: string]: unknown },
success: true,
status: number
};



export type ExecutedAPIFailureResponse = {
error: GenericError,
success: false,
status: number
};



export type ExecutedAPIResponse = ExecutedAPISuccessResponse | ExecutedAPIFailureResponse;
30 changes: 30 additions & 0 deletions src/error/generic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@ export class GenericError extends Error {
};



public static deserialize = (serialized: SerializedGenericError): GenericError => {
return new DeserializedGenericError(serialized);
}



public get errors(): Array<GenericError> {
const existing_ids = new Set<string>();
return Array.from(this._other_errors.values()).filter((error) => {
Expand Down Expand Up @@ -110,4 +117,27 @@ export class GenericError extends Error {

return return_error;
};
};



export class DeserializedGenericError extends GenericError {
protected readonly _id: string;
protected readonly _message: string;
protected readonly _code: number;
protected readonly _type: string;
protected _data: object;
protected _hint: string;

public constructor(serialized: SerializedGenericError) {
super(serialized.message, serialized.code, serialized.type);
this._id = serialized.id;
this._message = serialized.message;
this._code = serialized.code;
this._data = serialized.data;
this._hint = serialized.hint || '';
this._type = serialized.type;
serialized.errors.forEach((error) =>
this.add_error(new DeserializedGenericError(error)));
}
}
21 changes: 14 additions & 7 deletions tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,14 @@ class MiddlewareExecutionError extends nv.GenericError {
}


const err = new MiddlewareExecutionError('Middleware error');
console.log(err instanceof nv.GenericError);
console.log(MiddlewareExecutionError.from_unknown(err).serialize());



const server = new nv.Server();
const route = new nv.Route(server, '/test');

class MW extends nv.GenericMiddleware {
protected handler = async () => {
console.log('Middleware executed');
throw new nv.GenericError('Middleware error', 410);
throw new MiddlewareExecutionError('Middleware error');
}
}

Expand All @@ -39,4 +34,16 @@ nv.Binder(route, 'GET', {
});


await server.start();


server.start();



const test = nv.register_api_route('localhost:8080', '/test', 'GET', {});
const res = await test({

})

console.log(res.success);

0 comments on commit c7ad291

Please sign in to comment.