refactor(http): interface change of HttpOutputEffect + HttpErrorEffect
MODIFY
packages/http/src/effects/http.effects.interface.ts
packages/http/src/effects/http.effects.interface.ts
import { Event, Effect } from '@marblejs/core';
-import { HttpRequest, HttpStatus, HttpHeaders, HttpServer } from '../http.interface';
+import { HttpRequest, HttpStatus, HttpHeaders, HttpServer, WithHttpRequest } from '../http.interface';
export interface HttpEffectResponse<T = any> {
request?: HttpRequest;
@@ -16,8 +16,10 @@ export interface HttpMiddlewareEffect<
export interface HttpErrorEffect<
Err extends Error = Error,
Req extends HttpRequest = HttpRequest,
- Res extends HttpEffectResponse = HttpEffectResponse
-> extends HttpEffect<{ req: Req; error: Err }, { req: Req, res: Res }> {}
+> extends HttpEffect<
+ WithHttpRequest<{ error: Err }, Req>,
+ WithHttpRequest<HttpEffectResponse>
+> {}
export interface HttpServerEffect<
Ev extends Event = Event
@@ -25,8 +27,10 @@ export interface HttpServerEffect<
export interface HttpOutputEffect<
Req extends HttpRequest = HttpRequest,
- Res extends HttpEffectResponse = HttpEffectResponse
-> extends HttpEffect<{ req: Req; res: Res }, { req: Req; res: Res }> {}
+> extends HttpEffect<
+ WithHttpRequest<HttpEffectResponse, Req>,
+ WithHttpRequest<HttpEffectResponse>
+> {}
export interface HttpEffect<
I = HttpRequest,

MODIFY
packages/http/src/effects/http.requestMetadata.effect.ts
packages/http/src/effects/http.requestMetadata.effect.ts
@@ -6,15 +6,15 @@ import { HttpRequestMetadataStorageToken } from '../server/internal-dependencies
import { getHttpRequestMetadataIdHeader } from '../+internal/metadata.util';
import { HttpOutputEffect } from './http.effects.interface';
-export const requestMetadata$: HttpOutputEffect = (out$, ctx) => {
+export const requestMetadata$: HttpOutputEffect = (output$, ctx) => {
const httpRequestMetadataStorage = useContext(HttpRequestMetadataStorageToken)(ctx.ask);
- return out$.pipe(
- map(out => pipe(
- getHttpRequestMetadataIdHeader(out.req.headers),
- O.fold(constant(out), requestId => {
- httpRequestMetadataStorage.set(requestId, out.req.meta);
- return out;
+ return output$.pipe(
+ map(response => pipe(
+ getHttpRequestMetadataIdHeader(response.request.headers),
+ O.fold(constant(response), requestId => {
+ httpRequestMetadataStorage.set(requestId, response.request.meta);
+ return response;
}),
)),
);

MODIFY
packages/http/src/error/http.error.effect.spec.ts
packages/http/src/error/http.error.effect.spec.ts
@@ -4,7 +4,7 @@ import { defaultError$ } from './http.error.effect';
import { HttpError } from './http.error.model';
describe('defaultError$', () => {
- const req = createHttpRequest();
+ const request = createHttpRequest();
const client = createHttpResponse();
const ctx = { client };
@@ -19,8 +19,8 @@ describe('defaultError$', () => {
};
Marbles.assertEffect(defaultError$, [
- ['-a-', { a: { req, error } }],
- ['-a-', { a: { req, res: outgoingResponse } }],
+ ['-a-', { a: { request, error } }],
+ ['-a-', { a: { request, ...outgoingResponse } }],
], { ctx });
});
@@ -35,8 +35,8 @@ describe('defaultError$', () => {
};
Marbles.assertEffect(defaultError$, [
- ['-a-', { a: { req, error } }],
- ['-a-', { a: { req, res: outgoingResponse } }],
+ ['-a-', { a: { request, error } }],
+ ['-a-', { a: { request, ...outgoingResponse } }],
], { ctx });
});
@@ -51,8 +51,8 @@ describe('defaultError$', () => {
};
Marbles.assertEffect(defaultError$, [
- ['-a-', { a: { req, error } }],
- ['-a-', { a: { req, res: outgoingResponse } }],
+ ['-a-', { a: { request, error } }],
+ ['-a-', { a: { request, ...outgoingResponse } }],
], { ctx });
});
});

MODIFY
packages/http/src/error/http.error.effect.ts
packages/http/src/error/http.error.effect.ts
@@ -30,9 +30,9 @@ const errorFactory = (status: HttpStatus) => (error: Error): HttpErrorResponse =
export const defaultError$: HttpErrorEffect = req$ =>
req$.pipe(
- map(({ req, error = defaultHttpError }) => {
+ map(({ request, error = defaultHttpError }) => {
const status = getStatusCode(error);
const body = errorFactory(status)(error);
- return ({ req, res: { status, body } });
+ return ({ status, body, request });
}),
);

MODIFY
packages/http/src/http.interface.ts
packages/http/src/http.interface.ts
@@ -20,6 +20,9 @@ export interface HttpRequest<
[key: string]: any;
}
+export type WithHttpRequest<T, Req extends HttpRequest = HttpRequest> =
+ { request: Req } & T;
+
export interface RouteParameters {
[key: string]: any;
}

MODIFY
packages/http/src/router/http.router.helpers.ts
packages/http/src/router/http.router.helpers.ts
@@ -18,7 +18,7 @@ export const decorateEffect = (stream: Observable<HttpRequest>, errorSubject: Er
...operations,
map((res: HttpEffectResponse) => ({ ...res, request })),
catchError((error: Error) => {
- errorSubject.next({ error, req: request });
+ errorSubject.next({ error, request });
return EMPTY;
}),
])(of(request)) as Observable<HttpEffectResponse>),
@@ -32,7 +32,7 @@ export const decorateMiddleware = (stream: Observable<HttpRequest>, errorSubject
mergeMap((request: HttpRequest) => pipeFromArray([
...operations,
catchError((error: Error) => {
- errorSubject.next({ error, req: request });
+ errorSubject.next({ error, request });
return EMPTY;
}),
])(of(request)) as Observable<HttpRequest>),

MODIFY
packages/http/src/router/http.router.interface.ts
packages/http/src/router/http.router.interface.ts
import { Subject } from 'rxjs';
-import { HttpMethod, HttpRequest } from '../http.interface';
+import { HttpMethod, HttpRequest, WithHttpRequest } from '../http.interface';
import { HttpEffect, HttpMiddlewareEffect, HttpEffectResponse } from '../effects/http.effects.interface';
-export type ErrorSubject = Subject<{
- error: Error;
- req: HttpRequest<unknown, unknown, unknown>;
-}>;
+export type ErrorSubject = Subject<WithHttpRequest<{ error: Error }>>;
// Route
export interface RouteMeta extends Record<string, any> {

MODIFY
packages/http/src/router/http.router.resolver.ts
packages/http/src/router/http.router.resolver.ts
@@ -3,7 +3,7 @@ import { takeUntil, share, take, mergeMap, map, catchError } from 'rxjs/operator
import { flow, pipe } from 'fp-ts/lib/function';
import { EffectContext, useContext, LoggerToken, LoggerTag, LoggerLevel } from '@marblejs/core';
import { throwException } from '@marblejs/core/dist/+internal/utils';
-import { HttpServer, HttpRequest, HttpStatus } from '../http.interface';
+import { HttpServer, HttpRequest, HttpStatus, WithHttpRequest } from '../http.interface';
import { defaultError$ } from '../error/http.error.effect';
import { HttpEffectResponse, HttpErrorEffect, HttpOutputEffect } from '../effects/http.effects.interface';
import {
@@ -34,8 +34,8 @@ export const resolveRouting = (config: ResolveRoutingConfig) => {
const environmentConfig = provideConfig();
const requestBus = useContext(HttpRequestBusToken)(config.ctx.ask);
const logger = useContext(LoggerToken)(config.ctx.ask);
- const outputSubject = new Subject<{ res: HttpEffectResponse; req: HttpRequest}>();
- const errorSubject = new Subject<{ error: Error; req: HttpRequest }>();
+ const outputSubject = new Subject<WithHttpRequest<HttpEffectResponse>>();
+ const errorSubject = new Subject<WithHttpRequest<{ error: Error }>>();
/**
* Server close stream (closes all active streams)
@@ -60,19 +60,19 @@ export const resolveRouting = (config: ResolveRoutingConfig) => {
*/
const error$ = pipe(
errorSubject.asObservable(),
- map(({ error , req }) => isHttpRequestError(error) ? { req, error: error.error } : ({ error, req })),
+ map(({ request, error }) => isHttpRequestError(error) ? { request, error: error.error } : ({ request, error })),
e$ => config.error$ ? config.error$(e$, config.ctx) : defaultError$(e$, config.ctx),
takeUntil(close$),
);
/**
* Subscribe to all outgoing HTTP responses and trigger side effect
- * @param stream$ incoming `{ res, req }` pair
+ * @param stream$ incoming `HttpEffectResponse`
* @returns `Subscription`
*/
- const subscribeResponse = (stream$: Observable<{ res: HttpEffectResponse, req: HttpRequest }>) =>
+ const subscribeResponse = (stream$: Observable<WithHttpRequest<HttpEffectResponse>>) =>
stream$
- .pipe(mergeMap(({ res, req }) => req.response.send(res)))
+ .pipe(mergeMap(({ request, ...res }) => request.response.send(res)))
.subscribe({
error: flow(
unexpectedErrorWhileSendingResponseFactory,
@@ -100,7 +100,7 @@ export const resolveRouting = (config: ResolveRoutingConfig) => {
const processError = (error: any, originStream$: Observable<any>): Observable<any> => {
if (!error.request) throw errorNotBoundToRequestErrorFactory(error);
- errorSubject.next({ error, req: error.request });
+ errorSubject.next({ error, request: error.request });
return originStream$;
};
@@ -117,7 +117,7 @@ export const resolveRouting = (config: ResolveRoutingConfig) => {
stream$.subscribe({
next: res => {
if (!res.request) throw responseNotBoundToRequestErrorFactory(res);
- outputSubject.next({ res, req: res.request });
+ outputSubject.next({ ...res, request: res.request });
},
error: err => {
const type = 'RouterResolver';
@@ -144,32 +144,32 @@ export const resolveRouting = (config: ResolveRoutingConfig) => {
/**
* Resolve incoming request
- * @param req `HttpRequest`
+ * @param request `HttpRequest`
* @returns `void`
*/
- const resolve = (req: HttpRequest) => {
- const [urlPath, urlQuery] = req.url.split('?');
+ const resolve = (request: HttpRequest) => {
+ const [urlPath, urlQuery] = request.url.split('?');
try {
- const resolvedRoute = find(urlPath, req.method);
+ const resolvedRoute = find(urlPath, request.method);
if (!resolvedRoute) {
- return errorSubject.next({ req, error: ROUTE_NOT_FOUND_ERROR });
+ return errorSubject.next({ request, error: ROUTE_NOT_FOUND_ERROR });
}
- req.query = queryParamsFactory(urlQuery);
- req.params = resolvedRoute.params;
- req.meta = {};
- req.meta.path = resolvedRoute.path;
+ request.query = queryParamsFactory(urlQuery);
+ request.params = resolvedRoute.params;
+ request.meta = {};
+ request.meta.path = resolvedRoute.path;
- resolvedRoute.subject.next(req);
- requestBus.next(req);
+ resolvedRoute.subject.next(request);
+ requestBus.next(request);
} catch (error) {
if (error.name === 'URIError') {
- return errorSubject.next({ req, error: new HttpError(error.message, HttpStatus.BAD_REQUEST) });
+ return errorSubject.next({ request, error: new HttpError(error.message, HttpStatus.BAD_REQUEST) });
}
- return errorSubject.next({ req, error: new HttpError(`Internal server error: ${error.message}`, HttpStatus.INTERNAL_SERVER_ERROR) });
+ return errorSubject.next({ request, error: new HttpError(`Internal server error: ${error.message}`, HttpStatus.INTERNAL_SERVER_ERROR) });
}
};

MODIFY
packages/http/src/router/specs/http.router.resolver.spec.ts
packages/http/src/router/specs/http.router.resolver.spec.ts
import { Task } from 'fp-ts/lib/Task';
import { pipe } from 'fp-ts/lib/function';
import { of, merge, firstValueFrom, lastValueFrom } from 'rxjs';
-import { mapTo, take, toArray, delay, mergeMap, map, first } from 'rxjs/operators';
+import { mapTo, take, toArray, delay, mergeMap, map } from 'rxjs/operators';
import { createMockEffectContext, createHttpResponse, createHttpRequest, createTestRoute } from '../../+internal/testing.util';
import { HttpEffect, HttpErrorEffect, HttpOutputEffect } from '../../effects/http.effects.interface';
import { Routing } from '../http.router.interface';
@@ -55,10 +55,10 @@ describe('#resolveRouting', () => {
// then
outputSubject.pipe(take(4), toArray()).subscribe(
result => {
- expect(result[0].res).toEqual({ body: 'test_1', request: req1 });
- expect(result[1].res).toEqual({ body: 'test_2', request: req2 });
- expect(result[2].res).toEqual({ body: 'test_3', request: req3 });
- expect(result[3].res).toEqual({ body: 'test_4', request: req4 });
+ expect(result[0]).toEqual({ body: 'test_1', request: req1 });
+ expect(result[1]).toEqual({ body: 'test_2', request: req2 });
+ expect(result[2]).toEqual({ body: 'test_3', request: req3 });
+ expect(result[3]).toEqual({ body: 'test_4', request: req4 });
done();
},
);
@@ -129,7 +129,7 @@ describe('#resolveRouting', () => {
// given - error effect
const error$: HttpErrorEffect = error$ => {
effectSpy();
- return error$.pipe(map(({ req }) => ({ req, res: { body: 'ERROR' } })));
+ return error$.pipe(map(({ request }) => ({ request, body: 'ERROR' })));
};
// when
@@ -152,7 +152,7 @@ describe('#resolveRouting', () => {
// given
const ctx = createMockEffectContext();
const response = createHttpResponse();
- const req = createHttpRequest(({ url: '/unknown', method: 'GET', response }));
+ const request = createHttpRequest(({ url: '/unknown', method: 'GET', response }));
const path = factorizeRegExpWithParams('/');
const effect$: HttpEffect = req$ => req$.pipe(mapTo({ body: 'test' }));
@@ -165,13 +165,13 @@ describe('#resolveRouting', () => {
// when
const { resolve, errorSubject } = resolveRouting({ routing, ctx });
- const errorPromise = firstValueFrom(errorSubject.pipe(first()));
+ const errorPromise = firstValueFrom(errorSubject);
- resolve(req);
+ resolve(request);
// then
await expect(errorPromise).resolves.toEqual({
- req,
+ request,
error: new HttpError('Route not found', HttpStatus.NOT_FOUND),
});
});
@@ -180,7 +180,7 @@ describe('#resolveRouting', () => {
// given
const ctx = createMockEffectContext();
const response = createHttpResponse();
- const req = createHttpRequest(({ url: '/group/%test', method: 'GET', response }));
+ const request = createHttpRequest(({ url: '/group/%test', method: 'GET', response }));
const path = factorizeRegExpWithParams('/group/:id');
const effect$: HttpEffect = req$ => req$.pipe(mapTo({ body: 'test' }));
@@ -193,13 +193,13 @@ describe('#resolveRouting', () => {
// when
const { resolve, errorSubject } = resolveRouting({ routing, ctx });
- const errorPromise = firstValueFrom(errorSubject.pipe(first()));
+ const errorPromise = firstValueFrom(errorSubject);
- resolve(req);
+ resolve(request);
// then
await expect(errorPromise).resolves.toEqual({
- req,
+ request,
error: new HttpError('URI malformed', HttpStatus.BAD_REQUEST),
});
});
@@ -238,10 +238,10 @@ describe('#resolveRouting', () => {
// then
outputSubject.pipe(take(4), toArray()).subscribe(
result => {
- expect(result[0].res).toEqual({ body: 'delay_10', request: testData[0] });
- expect(result[1].res).toEqual({ body: 'delay_20', request: testData[1] });
- expect(result[2].res).toEqual({ body: 'delay_30', request: testData[2] });
- expect(result[3].res).toEqual({ body: 'delay_40', request: testData[3] });
+ expect(result[0]).toEqual({ body: 'delay_10', request: testData[0] });
+ expect(result[1]).toEqual({ body: 'delay_20', request: testData[1] });
+ expect(result[2]).toEqual({ body: 'delay_30', request: testData[2] });
+ expect(result[3]).toEqual({ body: 'delay_40', request: testData[3] });
done();
},
);
@@ -274,10 +274,10 @@ describe('#resolveRouting', () => {
// then
outputSubject.pipe(take(4), toArray()).subscribe(
result => {
- expect(result[0].res).toEqual({ body: 'delay_10', request: testData[0].req });
- expect(result[1].res).toEqual({ body: 'delay_20', request: testData[1].req });
- expect(result[2].res).toEqual({ body: 'delay_30', request: testData[2].req });
- expect(result[3].res).toEqual({ body: 'delay_40', request: testData[3].req });
+ expect(result[0]).toEqual({ body: 'delay_10', request: testData[0].req });
+ expect(result[1]).toEqual({ body: 'delay_20', request: testData[1].req });
+ expect(result[2]).toEqual({ body: 'delay_30', request: testData[2].req });
+ expect(result[3]).toEqual({ body: 'delay_40', request: testData[3].req });
done();
},
);
@@ -309,7 +309,7 @@ describe('#resolveRouting', () => {
// then
merge(
- outputSubject.pipe(take(3), map(res => res.res)),
+ outputSubject.pipe(take(3)),
errorSubject.pipe(take(1), map(res => res.error)),
).pipe(
toArray(),

refactor(http): interface change of HttpOutputEffect + HttpErrorEffect