Skip to content

Commit

Permalink
Merge pull request #47 from marblejs/feature/combineRoutesMiddleware
Browse files Browse the repository at this point in the history
feat(core): use middlewares with `combinedRoutes`
  • Loading branch information
JozefFlakus authored Jun 13, 2018
2 parents cab26aa + fd8e61e commit 2b52807
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 21 deletions.
87 changes: 81 additions & 6 deletions packages/core/src/effects/effects.combiner.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { mapTo, tap } from 'rxjs/operators';
import { map, mapTo, tap } from 'rxjs/operators';
import { Marbles } from '../../../util/marbles.spec-util';
import { HttpRequest, HttpResponse } from '../http.interface';
import { combineEffects, combineMiddlewareEffects, combineRoutes } from './effects.combiner';
Expand Down Expand Up @@ -50,12 +50,11 @@ describe('Effects combiner', () => {
const c$: Effect = request$ => request$.pipe(mapTo({ status: 203 }));
const d$: Effect = request$ => request$.pipe(mapTo({ status: 204 }));

const group1$: GroupedEffects = { path: '/test', effects: [c$, d$] };
const group2$: GroupedEffects = { path: '/test/foo', effects: [c$, d$] };
const group1$: GroupedEffects = { path: '/test', effects: [c$, d$], middlewares: [] };

const req = createMockReq('/test');
const res = createMockRes();
const combinedEffects = combineEffects([ a$, b$, group1$, group2$ ]);
const combinedEffects = combineEffects([ a$, b$, group1$ ]);
const http$ = combinedEffects(res)(req);

Marbles.assertCombinedEffects(http$, [
Expand All @@ -68,6 +67,36 @@ describe('Effects combiner', () => {
]);
});

it('#combineEffects combines effects for grouped effects with middlewares', () => {
// given
const a$: Effect = request$ => request$.pipe(map(req => ({ status: 201, mid: req.mid })));
const b$: Effect = request$ => request$.pipe(map(req => ({ status: 202, mid: req.mid })));
const c$: Effect = request$ => request$.pipe(map(req => ({ status: 203, mid: req.mid })));
const d$: Effect = request$ => request$.pipe(map(req => ({ status: 204, mid: req.mid })));

const m$: Effect<HttpRequest> = request$ => request$.pipe(tap(req => req.mid = (req.mid || 0) + 1));

const group1$: GroupedEffects = { path: '/test', effects: [c$, d$], middlewares: [m$, m$] };

// when
const req = createMockReq('/test/foo');
const res = createMockRes();
const combinedEffects = combineEffects([ a$, b$, group1$, c$, d$ ]);
const http$ = combinedEffects(res)(req);

// then
Marbles.assertCombinedEffects(http$, [
'(abcdef|)', {
a: { status: 201, mid: undefined },
b: { status: 202, mid: undefined },
c: { status: 203, mid: 2 },
d: { status: 204, mid: 2 },
e: { status: 203, mid: 2 },
f: { status: 204, mid: 2 },
}
]);
});

it('#combineMiddlewareEffects combines chained middlewares', () => {
const a$: Effect<HttpRequest> = request$ => request$.pipe(tap(req => { req.test = 1; }));
const b$: Effect<HttpRequest> = request$ => request$.pipe(tap(req => { req.test = req.test + 1; }));
Expand All @@ -85,13 +114,59 @@ describe('Effects combiner', () => {
]);
});

it('#combineRoutes factorizes route combiner', () => {
it('#combineRoutes factorizes combined routes for effects only', () => {
// given
const a$: Effect = request$ => request$.pipe(mapTo({}));
const b$: Effect = request$ => request$.pipe(mapTo({}));

expect(combineRoutes('/test', [a$, b$])).toEqual({
// when
const combiner = combineRoutes('/test', [a$, b$]);

// then
expect(combiner).toEqual({
path: '/test',
effects: [a$, b$],
middlewares: [],
});
});

it('#combineRoutes factorizes combined routes for effects and middlewares', () => {
// given
const a$: Effect = request$ => request$.pipe(mapTo({}));
const b$: Effect = request$ => request$.pipe(mapTo({}));

const m1$: Effect<HttpRequest> = request$ => request$;
const m2$: Effect<HttpRequest> = request$ => request$;

// when
const combiner = combineRoutes('/test', {
effects: [a$, b$],
middlewares: [m1$, m2$],
});

// then
expect(combiner).toEqual({
path: '/test',
effects: [a$, b$],
middlewares: [m1$, m2$],
});
});

it('#combineRoutes factorizes combined routes for effects and empty middlewares', () => {
// given
const a$: Effect = request$ => request$.pipe(mapTo({}));
const b$: Effect = request$ => request$.pipe(mapTo({}));

// when
const combiner = combineRoutes('/test', {
effects: [a$, b$],
});

// then
expect(combiner).toEqual({
path: '/test',
effects: [a$, b$],
middlewares: [],
});
});

Expand Down
12 changes: 8 additions & 4 deletions packages/core/src/effects/effects.combiner.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import { concat, of } from 'rxjs';
import { concatMap, switchMap } from 'rxjs/operators';
import { concatMap, mergeMap, switchMap } from 'rxjs/operators';
import { HttpRequest } from '../http.interface';
import { matchPath } from '../operators';
import { isGroup } from './effects.helpers';
import { isGroup, isRouteCombinerConfig } from './effects.helpers';
import { EffectCombiner, MiddlewareCombiner, RouteCombiner } from './effects.interface';

export const combineEffects: EffectCombiner = effects => res => req => {
const req$ = of(req);
const mappedEffects = effects.map(effect => isGroup(effect)
? req$.pipe(
matchPath(effect.path, { suffix: '/:foo*', combiner: true }),
mergeMap(combineMiddlewareEffects(effect.middlewares)(res)),
concatMap(combineEffects(effect.effects)(res))
)
: effect(req$, res, undefined)
Expand All @@ -26,5 +27,8 @@ export const combineMiddlewareEffects: MiddlewareCombiner = effects => res => re
return req$.pipe(...mappedEffects);
};

export const combineRoutes: RouteCombiner = (path, effects) =>
({ path, effects });
export const combineRoutes: RouteCombiner = (path, configOrEffects) => ({
path,
effects: isRouteCombinerConfig(configOrEffects) ? configOrEffects.effects : configOrEffects,
middlewares: isRouteCombinerConfig(configOrEffects) ? (configOrEffects.middlewares || []) : [],
});
52 changes: 43 additions & 9 deletions packages/core/src/effects/effects.helpers.spec.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,55 @@
import { mapTo } from 'rxjs/operators';
import { isEffect, isGroup } from './effects.helpers';
import { Effect, GroupedEffects } from './effects.interface';
import { isEffect, isGroup, isRouteCombinerConfig, isRouteCombinerEffects } from './effects.helpers';
import { Effect, GroupedEffects, RouteCombinerConfig } from './effects.interface';

describe('Effects helpers', () => {

it('#isGroup checks if parameters is GroupedEffects type', () => {
expect(isGroup({ path: '/test', effects: [] })).toBe(true);
expect(isGroup({ path: '/test', effects: {} } as GroupedEffects)).toBe(false);
test('#isGroup checks if parameters is GroupedEffects type', () => {
expect(isGroup({ path: '/test', effects: [] } as GroupedEffects)).toBe(true);
expect(isGroup({ path: '/test', effects: {} } as GroupedEffects)).toBe(false);
expect(isGroup({ effects: [] } as any as GroupedEffects)).toBe(false);
});

it('#isEffect checks if parameters is GroupedEffects type', () => {
test('#isEffect checks if parameters is GroupedEffects type', () => {
// given
const effect$: Effect = request$ => request$.pipe(mapTo({}));
const groupedEffects = { path: '/test', effects: [effect$] };
const groupedEffects = { path: '/test', effects: [effect$], middlewares: [] };

// when
const effect = isEffect(effect$);
const group = isEffect(groupedEffects);

// then
expect(effect).toBe(true);
expect(group).toBe(false);
});

test('#isRouteCombinerConfig checks if parameter is a configuration object', () => {
// given
const effects = [];
const combinedRoutes: RouteCombinerConfig = { middlewares: [], effects: [] };

// when
const combinedRoutesEffects = isRouteCombinerConfig(effects);
const combinedRoutesConfig = isRouteCombinerConfig(combinedRoutes);

expect(isEffect(effect$)).toBe(true);
expect(isEffect(groupedEffects)).toBe(false);
// then
expect(combinedRoutesConfig).toBe(true);
expect(combinedRoutesEffects).toBe(false);
});

test('#isRouteCombinerEffects checks if parameters is a collection of effects', () => {
// given
const effects = [];
const combinedRoutes: RouteCombinerConfig = { middlewares: [], effects: [] };

// when
const combinedRoutesEffects = isRouteCombinerEffects(effects);
const combinedRoutesConfig = isRouteCombinerEffects(combinedRoutes);

// then
expect(combinedRoutesConfig).toBe(false);
expect(combinedRoutesEffects).toBe(true);
});

});
8 changes: 7 additions & 1 deletion packages/core/src/effects/effects.helpers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Effect, EffectResponse, GroupedEffects } from './effects.interface';
import { Effect, EffectResponse, Effects, GroupedEffects, RouteCombinerConfig } from './effects.interface';

export const isGroup = (item: Effect | GroupedEffects): item is GroupedEffects =>
typeof item === 'object'
Expand All @@ -10,3 +10,9 @@ export const isGroup = (item: Effect | GroupedEffects): item is GroupedEffects =
export const isEffect = (item: Effect | GroupedEffects): item is Effect<EffectResponse> =>
!isGroup(item)
&& typeof item === 'function';

export const isRouteCombinerConfig = (item: RouteCombinerConfig | Effects): item is RouteCombinerConfig =>
!Array.isArray(item);

export const isRouteCombinerEffects = (item: RouteCombinerConfig | Effects): item is Effects =>
!isRouteCombinerConfig(item);
8 changes: 7 additions & 1 deletion packages/core/src/effects/effects.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export interface EffectResponse {
export interface GroupedEffects {
path: string;
effects: Effects;
middlewares: Effect<HttpRequest>[];
}

export interface EffectCombiner {
Expand All @@ -21,7 +22,12 @@ export interface MiddlewareCombiner {
}

export interface RouteCombiner {
(path: string, effects: Effects): GroupedEffects;
(path: string, config: RouteCombinerConfig | Effects): GroupedEffects;
}

export interface RouteCombinerConfig {
middlewares?: Effect<HttpRequest>[];
effects: Effects;
}

export type Effect<T = EffectResponse, U = any> = (
Expand Down

0 comments on commit 2b52807

Please sign in to comment.