feat(core): ability to pass `data` object inside `HttpError` constructor
RENAME
packages/core/src/middlewares/error.middleware.spec.ts
packages/core/src/error/error.middleware.spec.ts
import { mapTo } from 'rxjs/operators';
import { Marbles } from '../../../util/marbles.spec-util';
import { HttpRequest, HttpResponse } from '../http.interface';
-import { HttpError } from '../util/error.util';
import { error$, getErrorMiddleware } from './error.middleware';
+import { HttpError } from './error.model';
const createMockRes = () => ({} as HttpResponse);
const createMockReq = (url = '/') => ({ url } as HttpRequest);

RENAME
packages/core/src/middlewares/error.middleware.ts
packages/core/src/error/error.middleware.ts
import { map } from 'rxjs/operators';
import { Effect, EffectResponse } from '../effects/effects.interface';
import { HttpStatus } from '../http.interface';
-import { HttpError, isHttpError } from '../util/error.util';
+import { HttpError, isHttpError } from './error.model';
-export type ThrownError = HttpError | Error;
+export type ThrownError = HttpError & Error;
export const getErrorMiddleware = (customError$?: Effect<EffectResponse, ThrownError>) => !!customError$
? customError$
@@ -20,9 +20,9 @@ const errorFactory = (message: string, status: HttpStatus, data?: any) => ({
export const error$: Effect<EffectResponse, ThrownError> = (request$, response, error) => request$
.pipe(
map(req => {
- const { message } = error;
+ const { message, data } = error;
const status = getStatusCode(error);
- const body = errorFactory(message, status);
+ const body = errorFactory(message, status, data);
return { status, body };
}),

RENAME
packages/core/src/util/error.util.spec.ts
packages/core/src/error/error.model.spec.ts
-import { ExtendableError, HttpError, isHttpError } from './error.util';
+import { ExtendableError, HttpError, isHttpError } from './error.model';
describe('Error', () => {

RENAME
packages/core/src/util/error.util.ts
packages/core/src/error/error.model.ts
import { HttpStatus } from '../http.interface';
export class ExtendableError extends Error {
- constructor(
- public name: string,
- message: string
- ) {
+ constructor(public name: string, message: string) {
super(message);
}
}
@@ -13,6 +10,7 @@ export class HttpError extends ExtendableError {
constructor(
public readonly message: string,
public readonly status: HttpStatus,
+ public readonly data?: object,
) {
super('HttpError', message);
}

MODIFY
packages/core/src/http.listener.spec.ts
packages/core/src/http.listener.spec.ts
@@ -2,9 +2,9 @@ import { throwError } from 'rxjs';
import { mapTo, switchMap } from 'rxjs/operators';
import * as request from 'supertest';
import { Effect } from './effects/effects.interface';
+import { HttpError } from './error/error.model';
import { httpListener } from './http.listener';
import { matchPath } from './operators';
-import { HttpError } from './util/error.util';
describe('Http listener', () => {
it('reacts to attached effect', async () => {

MODIFY
packages/core/src/http.listener.ts
packages/core/src/http.listener.ts
@@ -2,8 +2,8 @@ import { Subject, of } from 'rxjs';
import { catchError, defaultIfEmpty, mergeMap, switchMap, tap } from 'rxjs/operators';
import { combineEffects, combineMiddlewareEffects } from './effects/effects.combiner';
import { Effect, EffectResponse, GroupedEffects } from './effects/effects.interface';
+import { getErrorMiddleware } from './error/error.middleware';
import { Http, HttpRequest, HttpResponse, HttpStatus } from './http.interface';
-import { getErrorMiddleware } from './middlewares/error.middleware';
import { handleResponse } from './response/response.handler';
type HttpListenerConfig = {

MODIFY
packages/core/src/index.ts
packages/core/src/index.ts
export * from './effects/effects.interface';
export { combineRoutes } from './effects/effects.combiner';
-export * from './middlewares/error.middleware';
+export { error$ } from './error/error.middleware';
+export { HttpError } from './error/error.model';
export * from './operators';
export * from './http.interface';
export * from './http.listener';
-export { HttpError } from './util/error.util';
export { ContentType } from './util/contentType.util';

MODIFY
packages/spec/api.integration.spec.ts
packages/spec/api.integration.spec.ts
import * as path from 'path';
-import { of } from 'rxjs';
+import { of, throwError } from 'rxjs';
import { filter, map, mapTo, switchMap } from 'rxjs/operators';
import * as request from 'supertest';
+import { HttpStatus } from '../core/dist/http.interface';
import { ContentType } from '../core/dist/util/contentType.util';
-import { Effect, HttpRequest, combineRoutes, httpListener, matchPath, matchType, use } from '../core/src';
+import { Effect, HttpError, HttpRequest, combineRoutes, httpListener, matchPath, matchType, use } from '../core/src';
import { bodyParser$ } from '../middleware-body/src';
import { readFile } from '../util/fileReader.helper';
@@ -46,6 +47,13 @@ const postUser$: Effect = request$ =>
map(response => ({ body: response }))
);
+const error$: Effect = request$ =>
+ request$.pipe(
+ matchPath('/error'),
+ matchType('GET'),
+ switchMap(() => throwError(new HttpError('test', HttpStatus.METHOD_NOT_ALLOWED, { test: 'test' }))),
+ );
+
const file$: Effect = request$ =>
request$.pipe(
matchPath('/static/:dir'),
@@ -57,7 +65,7 @@ const file$: Effect = request$ =>
const user$ = combineRoutes('/user', [getUserList$, getUserSingle$, postUser$]);
-const api$ = combineRoutes('/api/:version', [root$, file$, user$]);
+const api$ = combineRoutes('/api/:version', [root$, file$, error$, user$]);
const app = httpListener({
middlewares: [bodyParser$],
@@ -70,11 +78,20 @@ describe('API integration', () => {
.post('/')
.expect(404));
- it('returns only status 200: /api/v1', async () =>
+ it('returns status 200: /api/v1', async () =>
request(app)
.get('/api/v1')
.expect(200, '"root"'));
+ it('returns error response: /api/v1/error', async () =>
+ request(app)
+ .get('/api/v1/error')
+ .expect(HttpStatus.METHOD_NOT_ALLOWED, { error: {
+ status: HttpStatus.METHOD_NOT_ALLOWED,
+ data: { test: 'test' },
+ message: 'test',
+ }}));
+
it('returns object: /api/v1/user', async () =>
request(app)
.get('/api/v1/user')

feat(core): ability to pass `data` object inside `HttpError` constructor