Skip to content
This repository has been archived by the owner on May 17, 2019. It is now read-only.

Commit

Permalink
Add transformer to payloads (ActionEmitterTransformerToken)
Browse files Browse the repository at this point in the history
  • Loading branch information
dennisgl authored and fusion-bot[bot] committed Sep 5, 2018
1 parent af69851 commit 2859133
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 14 deletions.
56 changes: 54 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@ This is useful for when you want to collect data about redux actions, potentiall

- [Installation](#installation)
- [Usage](#usage)
- [Capturing action emits](#capturing-action-emits)
- [Capturing action emits](#capturing-action-emits)
- [Transforming action payloads on emission](#transforming-action-payloads-on-emission)
- [Setup](#setup)
- [API](#api)
- [Registration API](#registration-api)
- [`ReduxActionEmitterEnhancer`](#reduxactionemitterenhancer)
- [`EnhancerToken`](#enhancertoken)
- [`ActionEmitterTransformerToken`](#actionemittertransformertoken)
- [Dependencies](#dependencies)
- [Service API](#service-api)
- [Emit API](#emit-api)
Expand All @@ -36,7 +38,7 @@ yarn add fusion-redux-action-emitter-enhancer

### Usage

### Capturing action emits
#### Capturing action emits

We can register a simple callback to listen for the events emitted by this enhancer - in this case, `redux-action-emitter:action`. Normally we might want to log these to a backend service, but for simplicity, we'll log them to console.

Expand All @@ -52,6 +54,45 @@ export default createPlugin({
});
```

#### Transforming action payloads on emission

This plugin depends on [fusion-plugin-universal-events](https://github.com/fusionjs/fusion-plugin-universal-events) to emit the action payloads, which means the payloads are sent over-the-wire to the server-side, which **can consume much of the network bandwidth**, or even cause 413 Payload Too Large HTTP errors. The dependence is the reason why by default the plugin will only emit [certain properties](#default-transformer) from the raw action payload.

By default, `_trackingMeta` is an opinionated property to be picked and emitted from the raw payload for tracking(analytics) purposes. **For customizations, you should provide a transformer function to mainly filter/pick properties for emission.**

```js
// src/app.js
import {ActionEmitterTransformerToken} from 'fusion-redux-action-emitter-enhancer';

app.register(ActionEmitterTransformerToken, action => {
const base = {type: action.type};
switch (action.type) {
case 'ADD_TO_SHOPPING_CART':
case 'REMOVE_FROM_SHOPPING_CART':
return {
...base,
items: action.payload.items,
};
case 'ADD_COUPON': {
return {
...base,
couponId: action.payload.couponId,
};
}
case 'SUPER_BIG_PAYLOAD':
return null; // !!Omit the action type from emission entirely!!
default:
return base;
}
});
```

Or, if you are certain about emitting everything from the raw payload, maybe when bandwidth is actually not a concern for your application:

```js
app.register(ActionEmitterTransformerToken, action => action);
```

---

### Setup
Expand Down Expand Up @@ -106,6 +147,17 @@ import {EnhancerToken} from 'fusion-plugin-react-redux';

If you are using [`fusion-plugin-react-redux`](https://github.com/fusionjs/fusion-plugin-react-redux), we recommend registering this plugin to the `EnhancerToken`.

##### `ActionEmitterTransformerToken`

```js
import {ActionEmitterTransformerToken} from 'fusion-redux-action-emitter-enhancer';
```
###### Default transformer
`action => ({type: action.type, _trackingMeta: action._trackingMeta})`

Providing a transform function for the raw action payloads. See ["Transforming action payloads on emission"](#transforming-action-payloads-on-emission) for more information.


#### Dependencies

```js
Expand Down
1 change: 1 addition & 0 deletions docs/migrations/00115.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
If you were consuming properties on the raw `action` payload other than the default(`{type, _trackingMeta}`), add a transformer for you payloads. See README section ["Transforming action payloads on emission"](https://github.com/fusionjs/fusion-plugin-redux-action-emitter-enhancer#transforming-action-payloads-on-emission)
2 changes: 1 addition & 1 deletion src/__tests__/app.node.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ test('plugin - service resolved as expected', t => {
};
const mockReducer: Reducer<*, *> = s => s;
const enhanced = enhancer(createStore)(mockReducer);
enhanced.dispatch();
enhanced.dispatch({});
wasResolved = true;
},
})
Expand Down
64 changes: 58 additions & 6 deletions src/__tests__/index.node.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {UniversalEventsToken} from 'fusion-plugin-universal-events';
import type {Context} from 'fusion-core';
import {getService} from 'fusion-test-utils';

import actionEmitterPlugin from '../index.js';
import actionEmitterPlugin, {ActionEmitterTransformerToken} from '../index.js';

type ExtractReturnType = <V>(() => V) => V;
type IEmitter = $Call<typeof UniversalEventsToken, ExtractReturnType>;
Expand Down Expand Up @@ -52,11 +52,16 @@ const sampleReducer = (state = [], action) => {
}
};

const appCreator = (emitter?: IEmitter) => {
const appCreator = (deps?: {emitter?: IEmitter, transformer?: Function}) => {
const {emitter, transformer} = deps || {};

const app = new App('test', el => el);
if (emitter) {
app.register(UniversalEventsToken, emitter);
}
if (transformer) {
app.register(ActionEmitterTransformerToken, transformer);
}
return () => app;
};

Expand All @@ -67,7 +72,8 @@ test('Instantiation', t => {
'requires the EventEmitter dependency'
);
t.doesNotThrow(
() => getService(appCreator(mockEventEmitter), actionEmitterPlugin),
() =>
getService(appCreator({emitter: mockEventEmitter}), actionEmitterPlugin),
'provide the EventEmitter dependency'
);
t.end();
Expand All @@ -77,7 +83,7 @@ test('Emits actions', t => {
// Setup
const mockEventEmitter = getMockEventEmitterFactory();
const enhancer = getService(
appCreator(mockEventEmitter),
appCreator({emitter: mockEventEmitter}),
actionEmitterPlugin
);
const mockCtx = {mock: true};
Expand Down Expand Up @@ -105,14 +111,60 @@ test('Emits actions', t => {
'SAMPLE_SET',
'payload type is SAMPLE_SET, as expected'
);
t.equal(payload.value, true, 'payload value is true, as expected');
t.ok(
typeof payload.foo === 'undefined',
'By default properties other than {type, _trackingMeta} is emitted'
);
t.equal(ctx, mockCtxTyped, 'ctx was provided');
});
store.dispatch({
type: 'SAMPLE_SET',
value: true,
foo: {bar: 1},
});

t.plan(3);
t.end();
});

test('transformers', t => {
// Setup
const mockEventEmitter = getMockEventEmitterFactory();

const enhancer = getService(
appCreator({
emitter: mockEventEmitter,
transformer: action => ({foo: action.foo}),
}),
actionEmitterPlugin
);
const mockCtx = {mock: true};
const mockCtxTyped = ((mockCtx: any): Context);
const store = createStore(
sampleReducer,
[],
compose(
enhancer,
createStore => (...args) => {
const store = createStore(...args);
// $FlowFixMe
store.ctx = mockCtx;
return store;
}
)
);

// Test Emits
mockEventEmitter
.from(mockCtxTyped)
.on('redux-action-emitter:action', (payload, ctx) => {
t.deepEqual(payload, {foo: 1}, 'payload is transformed');
t.equal(ctx, mockCtxTyped, 'ctx was provided');
});
store.dispatch({
type: 'SAMPLE_SET',
foo: 1,
});

t.plan(2);
t.end();
});
35 changes: 30 additions & 5 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,35 @@
/* eslint-env browser,node */

import type {StoreEnhancer, StoreCreator, Store} from 'redux';
import type {Token} from 'fusion-core';

import {createPlugin} from 'fusion-core';
import {createPlugin, createToken} from 'fusion-core';
import {UniversalEventsToken} from 'fusion-plugin-universal-events';

type ExtractReturnType = <V>(() => V) => V;
type IEmitter = $Call<typeof UniversalEventsToken, ExtractReturnType>;

export const ActionEmitterTransformerToken: Token<Function> = createToken(
'ActionEmitterTransformerToken'
);

const defaultTransformer = action => {
const {type, _trackingMeta} = action;
return {type, _trackingMeta};
};

const plugin = createPlugin({
deps: {
emitter: UniversalEventsToken,
transformer: ActionEmitterTransformerToken.optional,
},
provides({emitter}: {emitter: IEmitter}) {
provides({
emitter,
transformer,
}: {
emitter: IEmitter,
transformer?: Function,
}) {
if (__DEV__ && !emitter) {
throw new Error(`emitter is required, but was: ${emitter}`);
}
Expand All @@ -31,9 +48,17 @@ const plugin = createPlugin({
const store: Store<*, *, *> = createStore(...args);
return {
...store,
dispatch: action => {
// $FlowFixMe
emitter.from(store.ctx).emit('redux-action-emitter:action', action);
dispatch: (action: Object) => {
let payload: Object = !transformer
? defaultTransformer(action)
: transformer(action);

if (payload) {
emitter // $FlowFixMe
.from(store.ctx)
.emit('redux-action-emitter:action', payload);
}

return store.dispatch(action);
},
};
Expand Down

0 comments on commit 2859133

Please sign in to comment.