Code Monkey home page Code Monkey logo

textanalytics-botframework-middleware's Introduction

Text Analytics API Bot Framework Middleware Module

This is a Node module to provide the Text Analytics API Proxy with a Bot Framework-friendly interface so that it can be easily inserted into the Bot Framework communication flow

Installation

Install the module from NPM

npm install textanalytics-botframework-middleware

Usage

Including the module in the source defines the textanalytics_botframework_middleware function. The function takes a config object and a callback function as arguments and returns a botbuilder function.

var botbuilder = require('textanalytics-botframework-middleware')(config, callback);

The callback function is passed along to the textanlytics.analyze() function, and is called to handle error and success conditions:

var callback = function (err, rsp){ }

Where rsp is the body of the (JSON) response in object form, and err is an Error object containing information about the failure, or null if the call succeeded.

Full Example

var config = {
	apikey: process.env.TEXTANALYTICS_APIKEY,
};

var callback = function(err, rsp) {
	if(err)
		console.log(err);
	console.log(rsp);
};

var textanalytics = require('textanalytics-botframework-middleware')(config, callback);
bot.use(textanalytics);

License

This module is licensed under the MIT License. Copyright © 2017, Verint Inc.

textanalytics-botframework-middleware's People

Contributors

jadler1 avatar ladenedge avatar

Watchers

 avatar  avatar

textanalytics-botframework-middleware's Issues

Various fixes for unit tests

Naming

First of all, I encourage you to change a few token names around in your unit test file. You have:

var TextAnalytic = require('../index.js');
var text = require('textanalytics');
var textanalytic = new text(config);

CamelCase tokens are conventionally used for classes (ie. things that require a new). It's also conventional to use the name of the package for its required variable. (eg. var p = require('p')) However, our package name is super long, so let's abbreviate it. We could abbreviate it 'textanalytic' (rather than 'TextAnalytic', because it's not a class), but that might be confusing against the actual textanalytics package.

The textanalytics package, on the other hand, does import a class definition, so CamelCase is good for that. However, the name is TextAnalytics, not TextAnalytic. So I might recommend something like this:

var middleware = require('../index.js');
var TextAnalytics = require('textanalytics');
var textanalytics = new TextAnalytics(config);

Importing

This is a minor thing, but there are two things to note about require('../index.js'):

  1. You don't need to include '.js' in require statements due to how Node searches for files, and

  2. when a package.json file is present (ie. when you're importing a full module), you can just specify the directory to import rather than a specific file. So you might consider:

    var middleware = require('..');

(Again, not a big deal. But if you change the name of index.js, you won't have to change it in your tests. )

Stubbing Classes

The broken unit test problem is related to how classes work in JS. This is a little inane, so I'll try to be clear.

When you write a class in JS (as you have in textanalytics), you're changing what is called the "prototype" for an object. This is a form of inheritance. Changing an object's prototype means that any object of that type will "inherit" changes to the prototype. Traditionally we did this like so:

TextAnalytics.prototype.analyze = function () { ... }

Then for any TextAnalytics object, there would be a "free" analyze() function that comes with it. Today we write that same code with a more object-oriented look and feel:

class TextAnalytics {
  analyze() { ... }
}

This is (roughly) the same code.

SO, what does this mean for your unit tests? Instead of stubbing out the analyze() function on a particular object, as you're doing here:

var text = require('textanalytics');
var textanalytic = new text(config);
this.analyze = sinon.stub(textanalytic, 'analyze');

You need to stub the object's prototype, like so:

var text = require('textanalytics');
this.analyze = sinon.stub(text.prototype, 'analyze');

Or, with our new naming convention:

var TextAnalytics = require('textanalytics');
this.analyze = sinon.stub(TextAnalytics.prototype, 'analyze');

Now all calls to analyze() by any TextAnalytics object will go through your stub.

Hopefully that makes sense. Good luck!!

Provide full response to middleware consumer

This might be a flaw in my requirements (if so, sorry!), but let's keep the middleware as a full pass-through for the data the TAAPI returns. In other words, the middleware's full job is to stick the analyze() call in that receive() function, not to interpret or deal with the response.

The summary you've created is great, but let's move that into the bot itself (that is, the middleware consumer) in the final stage of this project.

Must export a function that returns an object

Your current export is almost right -- the function that takes the config and callback is good, but instead of returning the receive function directly, we need to return an object with a receive property that points to that function. In other words:

return {
    receive: <your function>
};

Hopefully that makes sense.

Update README

Couple little problems with the readme:

  1. There's a formatting typo with a header.
  2. It doesn't show the callback.
  3. It should probably include the update with the session/botbuilder, too.

Change source of text to send to API

This one is my fault. The text does not appear to be in event.message.text, but rather event.text. You'll need to change where the middleware gets its text to the latter.

Sorry!

Remove assumptions about next() in unit tests

This falls under the 'best practice' heading rather than fixing anything functional, but you're making more assumptions than you need to about next() in your tests. Here's a current sample:

assert.equal(TextAnalytic(config, (err, rsp) => { }).receive(null, () => { return 2; }), 2);

This test assumes the value of next() is going to be returned, but the test says "receive should call next()". You could change the behavior here to set a local variable, something like this:

var wasCalled = false;
receive(null, () => { wasCalled = true; })

This would work, too, but this is a good opportunity to use a spy to verify that a function is called instead. So: set up a Sinon spy for all your next functions and assert on its called property. (NB: be sure to reset the spy after every test!)

Simplify asserts in unit tests

This is a good practice sort of thing, but try to think about unit tests in terms of three sections:

  1. setup,
  2. exercise,
  3. verify.

Right now, though you have very simply unit tests, all of these are packed into a single line:

assert.equal(TextAnalytic(config, (err, rsp) => { }).receive(null, () => { return 2; }), 2);

There are still distinct sections in here, though, and you want your unit tests to be as easy to parse as possible for that time six months down the road when something breaks and you have no idea what a unit test is doing. Consider this equivalent test:

var ta = TextAnalytic(config, (err, rsp) => { });  // setup
var actual = ta.receive(null, () => { return 2; });  // exercise
assert.equal(actual, 2);  // verify

You don't need the comments, but if you always conceptualize unit tests as having these sections, parsing three lines becomes a quick way to understand what the test is doing. (In longer unit tests, separating these sections with a single newline is a nice way to do the same thing.)

Let's do this with these unit tests so our future selves don't have to parse 100+ character lines of code!

Switch to the *botbuilder* event from *receive*

Links to the definitions in question:

And what to do:

  1. Change receive(event, next) to botbuilder(session, next) in your middleware (sorry!!), and
  2. Find the message to send to TA in session.message.text rather than the event.
  3. Update/add/remove unit tests, etc.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.