Skip to content

Commit

Permalink
feat(core): changes ErrorCode to ErrorLabel; adds ClientInvalid and C…
Browse files Browse the repository at this point in the history
…lientLegal error labels
  • Loading branch information
rafamel committed Oct 26, 2019
1 parent 8b947e5 commit 75c8098
Show file tree
Hide file tree
Showing 21 changed files with 100 additions and 79 deletions.
4 changes: 2 additions & 2 deletions packages/core/src/application/intercept-response.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ export function addInterceptResponse(
collection: CollectionTreeImplementation
): CollectionTreeImplementation {
const errors = {
ServerError: error({ code: 'ServerError' }),
ClientError: error({ code: 'ClientError' })
ServerError: error({ label: 'ServerError' }),
ClientError: error({ label: 'ClientError' })
};

const tree = {
Expand Down
12 changes: 9 additions & 3 deletions packages/core/src/create/implement/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@ import {
CollectionTreeImplementation,
TreeTypesImplementation,
ErrorTypeImplementation,
RequestTypeImplementation
RequestTypeImplementation,
ErrorLabel
} from '~/types';

export type ErrorTypeInput = Omit<ErrorTypeImplementation, 'kind'>;
export type ErrorTypeInput<L extends ErrorLabel> = Omit<
ErrorTypeImplementation<L>,
'kind'
>;
export type RequestTypeInput = Omit<RequestTypeImplementation, 'kind'>;
export type ResponseTypeInput = Omit<ResponseTypeImplementation, 'kind'>;

Expand All @@ -27,7 +31,9 @@ export function types<T extends TreeTypesImplementation>(
/**
* Creates an `ErrorTypeImplementation`.
*/
export function error(error: ErrorTypeInput): ErrorTypeImplementation {
export function error<L extends ErrorLabel>(
error: ErrorTypeInput<L>
): ErrorTypeImplementation<L> {
return {
kind: 'error',
...error
Expand Down
10 changes: 5 additions & 5 deletions packages/core/src/errors.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import { ErrorCode, CollectionTree } from '~/types';
import { ErrorLabel, CollectionTree } from '~/types';
import { isTypeError } from './inspect';

export class PublicError extends Error {
public id: string;
public code: ErrorCode;
public label: ErrorLabel;
public source?: Error;
public constructor(
id: string,
code: ErrorCode,
label: ErrorLabel,
source?: Error | null,
message?: string,
clear?: boolean
) {
super(message);
this.id = id;
this.code = code;
this.label = label;
this.source = source || undefined;
if (clear) this.stack = `${this.name}: ${this.message}`;
}
Expand Down Expand Up @@ -46,6 +46,6 @@ export class CollectionError<
if (!isTypeError(error)) {
throw Error(`Type "${id}" is not an error on collection`);
}
super(id as string, error.code, source, error.description, clear);
super(id as string, error.label, source, error.description, clear);
}
}
6 changes: 3 additions & 3 deletions packages/core/src/types/collection/abstract.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ErrorCode, Schema } from '../types';
import { ErrorLabel, Schema } from '../types';
import {
CollectionTreeKind,
ScopeTreeKind,
Expand Down Expand Up @@ -142,9 +142,9 @@ export interface AbstractServiceErrors {
}

// Types
export interface AbstractErrorType {
export interface AbstractErrorType<L extends ErrorLabel = ErrorLabel> {
kind: ErrorTypeKind;
code: ErrorCode;
label: L;
description?: string;
}

Expand Down
5 changes: 4 additions & 1 deletion packages/core/src/types/collection/collection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
AbstractResponseTypeChildren,
AbstractResponseType
} from './abstract';
import { ErrorLabel } from '../types';

// Groups
export type Element = AbstractElement<
Expand Down Expand Up @@ -111,7 +112,9 @@ export type ServiceErrors =
| ServiceErrorsImplementation;

// Types
export type ErrorType = ErrorTypeDeclaration | ErrorTypeImplementation;
export type ErrorType<L extends ErrorLabel = ErrorLabel> =
| ErrorTypeDeclaration<L>
| ErrorTypeImplementation<L>;

export type RequestType = RequestTypeDeclaration | RequestTypeImplementation;

Expand Down
5 changes: 4 additions & 1 deletion packages/core/src/types/collection/declaration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
AbstractErrorType,
AbstractServiceErrors
} from './abstract';
import { ErrorLabel } from '../types';

// Groups
export type ElementDeclaration = AbstractElement<
Expand Down Expand Up @@ -127,7 +128,9 @@ export type ServiceTypesDeclaration = AbstractServiceTypes<
export type ServiceErrorsDeclaration = AbstractServiceErrors;

// Types
export type ErrorTypeDeclaration = AbstractErrorType;
export type ErrorTypeDeclaration<
L extends ErrorLabel = ErrorLabel
> = AbstractErrorType<L>;

export type RequestTypeDeclaration = AbstractRequestType;

Expand Down
6 changes: 4 additions & 2 deletions packages/core/src/types/collection/implementation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
AbstractServiceErrors
} from './abstract';
import { Observable } from 'rxjs';
import { Schema, ElementInfo, ServiceInfo } from '../types';
import { Schema, ElementInfo, ServiceInfo, ErrorLabel } from '../types';

// Groups
export type ElementImplementation = AbstractElement<
Expand Down Expand Up @@ -166,7 +166,9 @@ export interface InterceptSchemasImplementation {
}

// Types
export type ErrorTypeImplementation = AbstractErrorType;
export type ErrorTypeImplementation<
L extends ErrorLabel = ErrorLabel
> = AbstractErrorType<L>;

export type RequestTypeImplementation = AbstractRequestType;

Expand Down
22 changes: 16 additions & 6 deletions packages/core/src/types/types.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
import { JSONSchema4 } from 'json-schema';
import { ServiceKind } from './collection';
import { ServiceKind, Element } from './collection';

export type Schema = JSONSchema4;

export type ErrorCode = ClientErrorCode | ServerErrorCode;
export type ErrorLabel = ClientErrorLabel | ServerErrorLabel;

export type ClientErrorCode =
export type ClientErrorLabel =
| 'ClientError'
| 'ClientUnauthorized'
| 'ClientForbidden'
| 'ClientNotFound'
| 'ClientConflict'
| 'ClientUnsupported'
| 'ClientConflict'
| 'ClientInvalid'
| 'ClientTooEarly'
| 'ClientRateLimit';
| 'ClientRateLimit'
| 'ClientLegal';

export type ServerErrorCode =
export type ServerErrorLabel =
| 'ServerError'
| 'ServerNotImplemented'
| 'ServerGateway'
Expand All @@ -32,3 +34,11 @@ export interface ElementInfo {
export interface ServiceInfo extends ElementInfo {
kind: ServiceKind;
}

export interface ElementItem<
E extends Element = Element,
N extends string = string
> {
name: N;
item: E;
}
2 changes: 1 addition & 1 deletion packages/intercepts/src/logging.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export function logging(
? {
name: err.name,
id: err.id,
code: err.code,
label: err.label,
description: err.message,
stack: err.stack,
source: err.source
Expand Down
38 changes: 20 additions & 18 deletions packages/intercepts/src/validation.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import {
InterceptImplementation,
intercept,
ErrorTypeImplementation,
error,
ServiceErrorsImplementation,
PublicError
PublicError,
ElementItem,
ErrorType
} from '@karmic/core';
import Ajv from 'ajv';
import draft04 from 'ajv/lib/refs/json-schema-draft-04.json';
Expand All @@ -18,19 +19,14 @@ export interface ValidationOptions {
* If a `ValidationError` is passed, it will be used to fail instead.
* Default: `true`.
*/
request?: ValidationError | boolean;
request?: ElementItem<ErrorType<'ClientInvalid'>> | boolean;
/**
* If `false`, it won't validate responses.
* If `true`, it will validate responses and use a default `ValidationError` to fail.
* If a `ValidationError` is passed, it will be used to fail instead.
* Default: `false`.
*/
response?: ValidationError | boolean;
}

export interface ValidationError {
name: string;
type: ErrorTypeImplementation;
response?: ElementItem<ErrorType> | boolean;
}

const ajv = new Ajv({ schemaId: 'id', logger: false });
Expand All @@ -44,27 +40,33 @@ export function validation(
): InterceptImplementation<any, any, any> {
const opts = Object.assign({ request: true, response: false }, options);

const requestError: ValidationError | null = opts.request
const requestError: ElementItem<ErrorType> | null = opts.request
? opts.request === true
? {
name: 'RequestValidationError',
type: error({ code: 'ClientError' })
item: error({ label: 'ClientInvalid' })
}
: opts.request
: null;
const responseError: ValidationError | null = opts.response
const responseError: ElementItem<ErrorType> | null = opts.response
? opts.response === true
? {
name: 'ResponseValidationError',
type: error({ code: 'ServerError' })
item: error({ label: 'ServerError' })
}
: opts.response
: null;

if (requestError && requestError.item.label !== 'ClientInvalid') {
throw Error(
`Request error must have label ClientInvalid: ${requestError.item.label}`
);
}

return intercept({
errors: [requestError, responseError].reduce(
(acc: ServiceErrorsImplementation, error) => {
if (error) acc[error.name] = error.type;
if (error) acc[error.name] = error.item;
return acc;
},
{}
Expand All @@ -78,9 +80,9 @@ export function validation(
if (!valid) {
throw new PublicError(
requestError.name,
requestError.type.code,
requestError.item.label,
null,
requestError.type.description,
requestError.item.description,
true
);
}
Expand All @@ -94,9 +96,9 @@ export function validation(
: throwError(
new PublicError(
responseError.name,
responseError.type.code,
responseError.item.label,
null,
responseError.type.description,
responseError.item.description,
true
)
);
Expand Down
4 changes: 2 additions & 2 deletions packages/rest-adapter/src/defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export default function createDefaults(): Required<RESTAdapterOptions> {
status: 'error',
error: {
id: error.id,
code: error.code,
label: error.label,
description: error.message || null
}
}
Expand All @@ -26,7 +26,7 @@ export default function createDefaults(): Required<RESTAdapterOptions> {
notFound: query({
types: {
errors: {
NotFoundError: error({ code: 'ClientNotFound' })
NotFoundError: error({ label: 'ClientNotFound' })
}
},
async resolve() {
Expand Down
10 changes: 6 additions & 4 deletions packages/rest-adapter/src/helpers/map-error.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import { ErrorCode, PublicError } from '@karmic/core';
import { ErrorLabel, PublicError } from '@karmic/core';

export const hash: { [P in ErrorCode]: number } = {
export const hash: { [P in ErrorLabel]: number } = {
// Client
ClientError: 400,
ClientUnauthorized: 401,
ClientForbidden: 403,
ClientNotFound: 404,
ClientConflict: 409,
ClientUnsupported: 406,
ClientConflict: 409,
ClientInvalid: 422,
ClientTooEarly: 425,
ClientRateLimit: 429,
ClientLegal: 451,
// Server
ServerError: 500,
ServerNotImplemented: 501,
Expand All @@ -22,7 +24,7 @@ export default function mapError(
error: PublicError,
serverError: PublicError
): { error: PublicError; status: number } {
const status = hash[error.code];
const status = hash[error.label];

return status ? { error, status } : { error: serverError, status: 500 };
}
2 changes: 1 addition & 1 deletion packages/rest-adapter/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export interface RESTAdapterOptions {
*/
envelope?: RESTEnvelopeFn;
/**
* A service handling not found errors.
* A service handling not found routes.
*/
notFound?: QueryServiceImplementation;
}
Expand Down
4 changes: 2 additions & 2 deletions packages/rpc-adapter/src/ChannelManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,12 @@ export default class ChannelMananger {
return err instanceof PublicError
? {
id: err.id,
code: err.code,
label: err.label,
message: err.message || undefined
}
: {
id: this.errors.ServerError.id,
code: this.errors.ServerError.code,
label: this.errors.ServerError.label,
message: this.errors.ServerError.message || undefined
};
}
Expand Down
2 changes: 1 addition & 1 deletion packages/rpc-adapter/src/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export default function adapter(
const app = application(
collections(
collection,
types({ [opts.routeError.name]: opts.routeError.type })
types({ [opts.routeError.name]: opts.routeError.item })
),
{ children: opts.children }
);
Expand Down
2 changes: 1 addition & 1 deletion packages/rpc-adapter/src/defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default function createDefaults(): Required<RPCAdapterOptions> {
context: () => ({}),
routeError: {
name: 'RouteError',
type: error({ code: 'ClientNotFound' })
item: error({ label: 'ClientNotFound' })
}
};
}

0 comments on commit 75c8098

Please sign in to comment.