refactor(core): removed second argument from effect definition
MODIFY
packages/core/src/+internal/testing/http.helper.ts
packages/core/src/+internal/testing/http.helper.ts
@@ -21,6 +21,7 @@ interface HttpRequestMockParams {
interface HttpResponseMockParams {
statusCode?: number;
+ finished?: boolean;
[key: string]: any;
}

MODIFY
packages/core/src/+internal/testing/marbles.helper.ts
packages/core/src/+internal/testing/marbles.helper.ts
@@ -5,7 +5,7 @@ type MarbleFlow =
| [string, { [marble: string]: any } | undefined]
| [string, { [marble: string]: any } | undefined, any]
;
-type MarbleDependencies = { client?: any; meta?: Partial<EffectMetadata> };
+type MarbleDependencies = { meta?: Partial<EffectMetadata<any>> };
export const Marbles = {
deepEquals: (actual: any, expected: any) => expect(actual).toEqual(expected),
@@ -24,7 +24,7 @@ export const Marbles = {
const stream$ = scheduler.createColdObservable(initStream, initValues, initError);
scheduler
- .expectObservable(effect(stream$, dependencies.client, dependencies.meta))
+ .expectObservable(effect(stream$, dependencies.meta))
.toBe(expectedStream, expectedValues, expectedError);
scheduler.flush();

MODIFY
packages/core/src/effects/effects.combiner.ts
packages/core/src/effects/effects.combiner.ts
@@ -3,10 +3,10 @@ import { Effect, EffectMetadata } from './effects.interface';
export const combineMiddlewares = <T, U>
(...effects: Effect<T, T, U>[]) =>
- (input$: Observable<T>, client: U, meta: EffectMetadata): Observable<T> =>
- effects.reduce((i$, effect) => effect(i$, client, meta), input$);
+ (input$: Observable<T>, meta: EffectMetadata<U>): Observable<T> =>
+ effects.reduce((i$, effect) => effect(i$, meta), input$);
export const combineEffects = <T, U, V>
(...effects: Effect<T, U, V>[]) =>
- (input$: Observable<T>, client: V, meta: EffectMetadata): Observable<U> =>
- merge(...effects.map(effect => effect(input$, client, meta)));
+ (input$: Observable<T>, meta: EffectMetadata<V>): Observable<U> =>
+ merge(...effects.map(effect => effect(input$, meta)));

MODIFY
packages/core/src/effects/effects.interface.ts
packages/core/src/effects/effects.interface.ts
@@ -5,12 +5,16 @@ export interface EffectLike {
(input$: Observable<any>, ...args: any[]): Observable<any>;
}
+export interface EffectMiddlewareLike<I, O> {
+ (i$: Observable<I>, ...args: any[]): Observable<O>;
+}
+
export interface Effect<I, O, Client> {
- (input$: Observable<I>, client: Client, meta: EffectMetadata): Observable<O>;
+ (input$: Observable<I>, meta: EffectMetadata<Client>): Observable<O>;
}
-export interface EffectMetadata {
+export interface EffectMetadata<T, U extends SchedulerLike = SchedulerLike> {
ask: ContextProvider;
- scheduler: SchedulerLike;
- [key: string]: any;
+ scheduler: U;
+ client: T;
}

MODIFY
packages/core/src/effects/effectsMetadata.factory.ts
packages/core/src/effects/effectsMetadata.factory.ts
@@ -3,12 +3,14 @@ import { AsyncScheduler } from 'rxjs/internal/scheduler/AsyncScheduler';
import { SchedulerLike } from 'rxjs';
import { ContextProvider } from '../context/context.factory';
-export const createEffectMetadata = <Scheduler extends SchedulerLike>(
+export const createEffectMetadata = <Client, Scheduler extends SchedulerLike>(
metadata: {
ask: ContextProvider;
+ client: Client;
scheduler?: Scheduler;
},
-): EffectMetadata => ({
+): EffectMetadata<Client> => ({
ask: metadata.ask,
+ client: metadata.client,
scheduler: metadata.scheduler || AsyncScheduler as any,
});

MODIFY
packages/core/src/error/specs/error.effect.spec.ts
packages/core/src/error/specs/error.effect.spec.ts
@@ -5,6 +5,7 @@ import { HttpError } from '../error.model';
describe('defaultError$', () => {
const req = createHttpRequest();
const client = createHttpResponse();
+ const meta = { client };
test('maps HttpError', () => {
const error = new HttpError('test-message', 400);
@@ -20,7 +21,7 @@ describe('defaultError$', () => {
Marbles.assertEffect(defaultError$, [
['-a-', { a: incomingRequest }],
['-a-', { a: outgoingResponse }],
- ], { client });
+ ], { meta });
});
test('maps other errors', () => {
@@ -37,7 +38,7 @@ describe('defaultError$', () => {
Marbles.assertEffect(defaultError$, [
['-a-', { a: incomingRequest }],
['-a-', { a: outgoingResponse }],
- ], { client, meta: { error } });
+ ], { meta });
});
test('maps to "Internal server error" if "error" is not provided', () => {
@@ -54,6 +55,6 @@ describe('defaultError$', () => {
Marbles.assertEffect(defaultError$, [
['-a-', { a: incomingRequest }],
['-a-', { a: outgoingResponse }],
- ], { client, meta: {} });
+ ], { meta });
});
});

MODIFY
packages/core/src/listener/http.listener.ts
packages/core/src/listener/http.listener.ts
@@ -39,27 +39,31 @@ export const httpListener = ({
}: HttpListenerConfig): R.Reader<Context, HttpListener> => pipe(
R.ask<Context>(),
R.map(ctx => {
+ const ask = lookup(ctx);
const requestSubject$ = new Subject<{ req: HttpRequest; res: HttpResponse }>();
const combinedMiddlewares = combineMiddlewares(...middlewares);
const routing = factorizeRouting(effects);
- const metadata = createEffectMetadata({ ask: lookup(ctx) });
const defaultResponse = { status: HttpStatus.NOT_FOUND } as HttpEffectResponse;
requestSubject$.pipe(
tap(({ req, res }) => res.send = handleResponse(res)(req)),
- mergeMap(({ req, res }) => combinedMiddlewares(of(req), res, metadata).pipe(
+ mergeMap(({ req, res }) => {
+ const metadata = createEffectMetadata({ ask, client: res });
+
+ return combinedMiddlewares(of(req), metadata).pipe(
takeWhile(() => !res.finished),
- mergeMap(resolveRouting(routing, metadata)(res)),
+ mergeMap(resolveRouting(routing, metadata)),
defaultIfEmpty(defaultResponse),
- mergeMap(out => output$(of({ req, res: out }), res, metadata)),
+ mergeMap(out => output$(of({ req, res: out }), metadata)),
tap(res.send),
catchError(error =>
- error$(of({ req, error }), res, metadata).pipe(
- mergeMap(out => output$(of({ req, res: out }), res, metadata)),
+ error$(of({ req, error }), metadata).pipe(
+ mergeMap(out => output$(of({ req, res: out }), metadata)),
tap(res.send),
),
),
- )),
+ );
+ }),
).subscribe();
const httpServer = (req: IncomingMessage, res: OutgoingMessage) => requestSubject$.next({

MODIFY
packages/core/src/operators/use/use.operator.ts
packages/core/src/operators/use/use.operator.ts
import { Observable, of } from 'rxjs';
import { mergeMap } from 'rxjs/operators';
-import { EffectMetadata } from '../../effects/effects.interface';
-
-interface MiddlewareLike<I, O> {
- (i$: Observable<I>, client: any, meta: any): Observable<O>;
-}
+import { EffectMetadata, EffectMiddlewareLike } from '../../effects/effects.interface';
export const use = <I, O>
- (middleware: MiddlewareLike<I, O>, client?: any, meta?: EffectMetadata) =>
- (source$: Observable<I>) =>
+ (middleware: EffectMiddlewareLike<I, O>, meta?: EffectMetadata<any>) =>
+ (source$: Observable<I>): Observable<O> =>
source$.pipe(
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- mergeMap(req => middleware(of(req), client!, meta!) as Observable<O>)
+ mergeMap(req => middleware(of(req), meta))
);

MODIFY
packages/core/src/router/router.resolver.ts
packages/core/src/router/router.resolver.ts
@@ -38,10 +38,8 @@ export const findRoute = (
};
export const resolveRouting =
- (routing: Routing, metadata: EffectMetadata) =>
- (res: HttpResponse) =>
- (req: HttpRequest): Observable<HttpEffectResponse> => {
- if (res.finished) { return EMPTY; }
+ (routing: Routing, metadata: EffectMetadata<HttpResponse>) => (req: HttpRequest): Observable<HttpEffectResponse> => {
+ if (metadata.client.finished) { return EMPTY; }
req.meta = {};
const [urlPath, urlQuery] = req.url.split('?');
@@ -56,9 +54,9 @@ export const resolveRouting =
const middleware = routeMatched.middleware;
return middleware
- ? middleware(of(req), res, metadata).pipe(
- takeWhile(() => !res.finished),
- mergeMap(req => routeMatched.effect(of(req), res, metadata))
+ ? middleware(of(req), metadata).pipe(
+ takeWhile(() => !metadata.client.finished),
+ mergeMap(req => routeMatched.effect(of(req), metadata))
)
- : routeMatched.effect(of(req), res, metadata);
+ : routeMatched.effect(of(req), metadata);
};

MODIFY
packages/core/src/router/specs/router.resolver.spec.ts
packages/core/src/router/specs/router.resolver.spec.ts
@@ -7,6 +7,7 @@ import { RouteMatched, Routing } from '../router.interface';
import { createContext } from '../../context/context.factory';
import { createEffectMetadata } from '../../effects/effectsMetadata.factory';
import { lookup } from '../../context/context.factory';
+import { createHttpResponse } from '../../+internal';
describe('#findRoute', () => {
test('finds route inside collection', () => {
@@ -104,7 +105,7 @@ describe('#findRoute', () => {
describe('#resolveRouting', () => {
let router;
let queryFactory;
- let metadata: EffectMetadata;
+ let metadata: EffectMetadata<HttpResponse>;
const context = createContext();
beforeEach(() => {
@@ -112,7 +113,10 @@ describe('#resolveRouting', () => {
jest.unmock('../router.query.factory');
router = require('../router.resolver.ts');
queryFactory = require('../router.query.factory');
- metadata = createEffectMetadata({ ask: lookup(context) });
+ metadata = createEffectMetadata({
+ ask: lookup(context),
+ client: createHttpResponse(),
+ });
});
test('resolves found effect', done => {
@@ -120,12 +124,11 @@ describe('#resolveRouting', () => {
const effect$: HttpEffect = req$ => req$.pipe(mapTo({ body: 'test' }));
const expectedMachingResult: RouteMatched = { effect: effect$, params: {}, path: '/' };
const req = { url: '/', method: 'GET', query: {}, params: {} } as HttpRequest;
- const res = {} as HttpResponse;
// when
router.findRoute = jest.fn(() => expectedMachingResult);
queryFactory.queryParamsFactory = jest.fn(() => ({}));
- const resolvedRoute = resolveRouting([], metadata)(res)(req);
+ const resolvedRoute = resolveRouting([], metadata)(req);
// then
resolvedRoute.subscribe(effect => {
@@ -139,11 +142,10 @@ describe('#resolveRouting', () => {
test('returns empty stream if effect has been not found', done => {
// given
const req = { url: '/', method: 'GET', query: {}, params: {} } as HttpRequest;
- const res = {} as HttpResponse;
// when
router.findRoute = jest.fn(() => undefined);
- const resolvedRoute = resolveRouting([], metadata)(res)(req);
+ const resolvedRoute = resolveRouting([], metadata)(req);
// then
resolvedRoute.subscribe(
@@ -165,10 +167,10 @@ describe('#resolveRouting', () => {
test('returns empty stream if response has been finished', done => {
// given
const req = {} as HttpRequest;
- const res = { finished: true } as HttpResponse;
+ metadata.client.finished = true;
// when
- const resolvedRoute = resolveRouting([], metadata)(res)(req);
+ const resolvedRoute = resolveRouting([], metadata)(req);
// then
resolvedRoute.subscribe(
@@ -194,12 +196,11 @@ describe('#resolveRouting', () => {
const expectedMachingResult: RouteMatched = { middleware: middleware$, effect: effect$, params: {}, path: '/' };
const req = { url: '/', method: 'GET', query: {}, params: {} } as HttpRequest;
- const res = {} as HttpResponse;
// when
router.findRoute = jest.fn(() => expectedMachingResult);
queryFactory.queryParamsFactory = jest.fn(() => ({}));
- const resolvedRoute = resolveRouting([], metadata)(res)(req);
+ const resolvedRoute = resolveRouting([], metadata)(req);
// then
resolvedRoute.subscribe(effect => {

MODIFY
packages/core/src/server/server.factory.ts
packages/core/src/server/server.factory.ts
@@ -25,8 +25,8 @@ export const createServer = (config: CreateServerConfig): Server => {
subscribeServerEvents(hostname || DEFAULT_HOSTNAME)(serverEventSubject)(server);
if (event$) {
- const metadata = createEffectMetadata({ ask: lookup(context) });
- event$(serverEventSubject.pipe(takeWhile(e => !isCloseEvent(e))), server, metadata).subscribe();
+ const metadata = createEffectMetadata({ ask: lookup(context), client: server });
+ event$(serverEventSubject.pipe(takeWhile(e => !isCloseEvent(e))), metadata).subscribe();
}
const listen = () => new Promise<https.Server | http.Server>((resolve, reject) => {

refactor(core): removed second argument from effect definition