Skip to content

Commit

Permalink
implement derivedState + derivedParam
Browse files Browse the repository at this point in the history
  • Loading branch information
chentsulin committed Apr 29, 2018
1 parent 3bd7355 commit 4301204
Show file tree
Hide file tree
Showing 5 changed files with 224 additions and 24 deletions.
62 changes: 62 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,22 @@ npm install bottender-recognizer
(state, event) => intent | undefined
```

Example:

```js
async (state, intent) => ({
name: 'INTENT_NAME',
payload: {},
});
```

### Intent

* `name` - Must have.
* `payload` - Recommended.

Example:

```js
{
name: "INTENT_NAME",
Expand All @@ -35,12 +49,60 @@ npm install bottender-recognizer
(state, intent) => action
```

Example:

```js
(state, intent) => doSomething;
```

With `derivedState` and `derivedParam`:

```
(state, intent) => derivedState + derivedParam + action
```

Example:

```js
(state, intent) => ({
action: doSomething,
derivedState: {
x: 1,
},
derivedParam: {
y: 2,
},
});
```

### Action

```
context => void
```

Example:

```js
async context => {
// ...
};
```

With parameters:

```
context + param => void
```

Example:

```js
async (context, param) => {
// ...
};
```

## API Reference

### `createHandler({ recognizer, resolver, chatbase, debug })`
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
},
"dependencies": {
"@google/chatbase": "^1.0.0",
"delay": "^2.0.0"
"delay": "^2.0.0",
"warning": "^3.0.0"
},
"devDependencies": {
"eslint": "^4.13.1",
Expand Down
130 changes: 110 additions & 20 deletions src/__tests__/index.spec.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,23 @@
const delay = require('delay');
const warning = require('warning');

const { createHandler, combineRecognizers } = require('../');

jest.mock('warning');

function createContext() {
return {
state: {},
event: {},
setState(state) {
this.state = {
...this.state,
...state,
};
},
};
}

it('exports public apis', () => {
expect(createHandler).toBeDefined();
expect(combineRecognizers).toBeDefined();
Expand All @@ -14,10 +30,7 @@ describe('#combineRecognizers', () => {
() => Promise.resolve({ name: 'intent' }),
]);

const context = {
state: {},
event: {},
};
const context = createContext();

const intent = await recognizers(context.state, context.event);

Expand All @@ -30,10 +43,7 @@ describe('#combineRecognizers', () => {
() => Promise.resolve(null),
]);

const context = {
state: {},
event: {},
};
const context = createContext();

const intent = await recognizers(context.state, context.event);

Expand All @@ -46,10 +56,7 @@ describe('#combineRecognizers', () => {
{ timeout: 100 }
);

const context = {
state: {},
event: {},
};
const context = createContext();

const intent = recognizers(context.state, context.event);
await Promise.resolve();
Expand All @@ -65,10 +72,7 @@ describe('#combineRecognizers', () => {
{ timeout: 500 }
);

const context = {
state: {},
event: {},
};
const context = createContext();

const intent = recognizers(context.state, context.event);
await Promise.resolve();
Expand All @@ -84,10 +88,7 @@ describe('#createHandler', () => {
const targetHandler = jest.fn();
const recognizer = () => Promise.resolve({ name: 'intent' });
const resolver = () => targetHandler;
const context = {
state: {},
event: {},
};
const context = createContext();

const handler = createHandler({ recognizer, resolver });

Expand All @@ -97,4 +98,93 @@ describe('#createHandler', () => {

expect(targetHandler).toBeCalledWith(context, otherArg);
});

it('should warning when resolver resolve undefined', async () => {
const recognizer = () => Promise.resolve({ name: 'intent' });
const resolver = () => {};
const context = createContext();

const handler = createHandler({ recognizer, resolver });

await handler(context);

expect(warning).toBeCalled();
});

it('should support derivedState', async () => {
const targetHandler = jest.fn();
const recognizer = () => Promise.resolve({ name: 'intent' });
const resolver = () => ({
action: targetHandler,
derivedState: {
x: 1,
},
});
const context = createContext();

const handler = createHandler({ recognizer, resolver });

await handler(context);

expect(targetHandler.mock.calls[0][0].state).toEqual({
x: 1,
});
});

describe('derivedParam', () => {
it('should warning when second arg is not an object', async () => {
const targetHandler = jest.fn();
const recognizer = () => Promise.resolve({ name: 'intent' });
const resolver = () => ({
action: targetHandler,
derivedParam: {
x: 1,
},
});
const context = createContext();

const handler = createHandler({ recognizer, resolver });

await handler(context, 1);

expect(targetHandler).toBeCalledWith(context, 1);
expect(warning).toBeCalled();
});

it('should merge when second arg is an object', async () => {
const targetHandler = jest.fn();
const recognizer = () => Promise.resolve({ name: 'intent' });
const resolver = () => ({
action: targetHandler,
derivedParam: {
x: 1,
},
});
const context = createContext();

const handler = createHandler({ recognizer, resolver });

await handler(context, { y: 2 });

expect(targetHandler).toBeCalledWith(context, { x: 1, y: 2 });
});

it('should provide param when second arg is undefined', async () => {
const targetHandler = jest.fn();
const recognizer = () => Promise.resolve({ name: 'intent' });
const resolver = () => ({
action: targetHandler,
derivedParam: {
x: 1,
},
});
const context = createContext();

const handler = createHandler({ recognizer, resolver });

await handler(context);

expect(targetHandler).toBeCalledWith(context, { x: 1 });
});
});
});
47 changes: 44 additions & 3 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const delay = require('delay');
const warning = require('warning');

exports.combineRecognizers = function combineRecognizers(
recognizers,
Expand Down Expand Up @@ -42,7 +43,7 @@ exports.createHandler = function createHandler({
.setPlatform(chatbase.platform); // The platform you are interacting with the user over
}

return async (context, ...otherArgs) => {
return async (context, arg, ...otherArgs) => {
const intent = await recognizer(context.state, context.event);

// log intent to chatbase or other service here
Expand All @@ -63,7 +64,27 @@ exports.createHandler = function createHandler({
.catch(console.error);
}

const action = resolver(context.state, intent);
const result = resolver(context.state, intent);

if (
!result ||
!(typeof result === 'object' || typeof result === 'function')
) {
warning(false, 'resolver must return a function or an object.');
return;
}

let action;
let derivedState;
let derivedParam;

if (typeof result === 'object') {
action = result.action;
derivedState = result.derivedState;
derivedParam = result.derivedParam;
} else {
action = result;
}

if (debug) {
console.log('Action: ', action.displayName || action.name);
Expand All @@ -80,6 +101,26 @@ exports.createHandler = function createHandler({
.catch(console.error);
}

await action(context, ...otherArgs);
if (derivedState) {
context.setState(derivedState);
}

if (!derivedParam) {
await action(context, arg, ...otherArgs);
return;
}
if (arg && typeof arg !== 'object') {
warning(
false,
'should not provide not object type param with derivedParam. derivedParam will not be applied.'
);
await action(context, arg, ...otherArgs);
} else {
const param = {
...arg,
...derivedParam,
};
await action(context, param, ...otherArgs);
}
};
};
6 changes: 6 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3347,6 +3347,12 @@ walker@~1.0.5:
dependencies:
makeerror "1.0.x"

warning@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/warning/-/warning-3.0.0.tgz#32e5377cb572de4ab04753bdf8821c01ed605b7c"
dependencies:
loose-envify "^1.0.0"

watch@~0.18.0:
version "0.18.0"
resolved "https://registry.yarnpkg.com/watch/-/watch-0.18.0.tgz#28095476c6df7c90c963138990c0a5423eb4b986"
Expand Down

0 comments on commit 4301204

Please sign in to comment.