Skip to content

Commit

Permalink
Merge pull request #22 from oracle/smd_v261
Browse files Browse the repository at this point in the history
Version 2.6.1
  • Loading branch information
stevendavelaar committed Mar 24, 2022
2 parents 66afb88 + 5819d32 commit ace962a
Show file tree
Hide file tree
Showing 18 changed files with 1,470 additions and 1,173 deletions.
96 changes: 16 additions & 80 deletions CUSTOM_COMPONENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ The custom component must export two objects:
- Supported transition actions
- The `invoke` function, which contains the logic to execute

Here's an example of how to use both objects. Note that the first argument (`context`) names the reference to the [CustomComponentContext](https://oracle.github.io/bots-node-sdk/CustomComponentContext.html) object, which provides a context object for reading and changing variables as well as sending back results.
Here's an example of how to use both objects. Note that the argument (`context`) names the reference to the [CustomComponentContext](https://oracle.github.io/bots-node-sdk/CustomComponentContext.html) object, which provides a context object for reading and changing variables as well as sending back results.

> **Note:** Before version 2.5.1, many code examples named the first argument `conversation`. Either name is valid.
Expand All @@ -58,7 +58,7 @@ module.exports = {
supportedActions: ['weekday', 'weekend']
},

invoke: (context, done) => {
invoke: async (context) => {
// Retrieve the value of the 'human' component property.
const { human } = context.properties();
// determine date
Expand All @@ -69,7 +69,6 @@ module.exports = {
context.reply(`Greetings ${human}`)
.reply(`Today is ${now.toLocaleDateString()}, a ${dayOfWeek}`)
.transition(isWeekend ? 'weekend' : 'weekday');
done();
}
}
```
Expand All @@ -90,20 +89,6 @@ module.exports = {
...
}
```
Bots Node SDK version 2.5.1 introduced an alternative syntax for the `invoke` method, which uses an `async` keyword instead of the `done` argument.

```javascript
invoke: async (context) => {
...
context.reply('hello world');
}
```
You can use this syntax in the following cases:
- You'll deploy to an embedded container in a Digital Assistance instance of version 21.02 or higher
- You'll deploy to a host other than the embedded container and your component package uses Bots Node SDK version 2.5.1 or higher

With the `async` function definition, you can write asynchronous code in a synchronous way using the `await` keyword.
In addition, you no longer have to call `done()` at every place in your code where the custom component logic is completed.

### Using TypeScript <a name="ts">

Expand All @@ -121,7 +106,7 @@ When you use TypeScript, the custom component class must implement the `CustomCo
The following code shows an example of defining the two methods.

```typescript
import {CustomComponent, CustomComponentMetadata, CustomComponentContext, InvocationCallback } from '@oracle/bots-node-sdk/lib';
import {CustomComponent, CustomComponentMetadata, CustomComponentContext } from '@oracle/bots-node-sdk/lib';

export class HelloWorld implements CustomComponent {

Expand All @@ -135,7 +120,7 @@ export class HelloWorld implements CustomComponent {
}
}

public invoke(context: CustomComponentContext, done: InvocationCallback): void {
public async invoke(context: CustomComponentContext): Promise<void> {
// Retrieve the value of the 'human' component property.
const { human } = context.properties();
// determine date
Expand All @@ -150,19 +135,6 @@ export class HelloWorld implements CustomComponent {
}
}
```
Bots Node SDK version 2.5.1 introduced an alternative syntax for the `invoke` method, which uses an `async` keyword instead of the `done` argument.

```typescript
public async invoke(context: CustomComponentContext): Promise<void> {
context.reply('hello world');
}
```
You can use this syntax in the following cases:
- You'll deploy to an embedded container in a Digital Assistance instance of version 21.02 or higher
- You'll deploy to a host other than the embedded container and your component package uses Bots Node SDK version 2.5.1 or higher
With the `async` function definition, you can write asynchronous code in a synchronous way using the `await` keyword.
In addition, you no longer have to call `done()` at every place in your code where the custom component logic is completed.

### The Metadata Object <a name="metadata">

Expand All @@ -184,7 +156,7 @@ The `supportedActions` aren't checked at runtime. If you call the `transition(ac

The `invoke` method contains all the logic. In this method you can read and write skill context variables, create conversation messages, set state transitions, make REST calls, and more.

The first argument of the `invoke` method is the `context` object. This object references the [CustomComponentContext](https://oracle.github.io/bots-node-sdk/CustomComponentContext.html), which provides access to many convenience methods to create your logic.
The argument of the `invoke` method is the `context` object. This object references the [CustomComponentContext](https://oracle.github.io/bots-node-sdk/CustomComponentContext.html), which provides access to many convenience methods to create your logic.

More information about creating conversation messages to send to the skill user can be found [here](https://github.com/oracle/bots-node-sdk/blob/master/MESSAGE_MODEL.md).

Expand All @@ -198,12 +170,11 @@ You use different combinations of the [CustomComponentContext](https://oracle.gi
If you don't call `transition()`, the response is sent but the dialog stays in the state and subsequent user input comes back to this component. That is, `invoke()` is called again.

```javascript
invoke: (context, done) => {
invoke: async (context) => {
...
context.reply(payload);
context.keepTurn(true);
context.transition ("success");
done();
}
```
Here's a list of typical combinations of `keepTurn()` and `transition()` that you can use depending on the use case:
Expand All @@ -227,52 +198,22 @@ const fetch = require("node-fetch");
- **TypeScript:**

```typescript
import * as fetch from 'node-fetch';
import fetch from 'node-fetch';
```

The code to make REST calls with `node fetch` looks like this:

```javascript
// Make a REST GET request
fetch('http://some-backend-server-url')
.then((response) => {
return response.json();
})
.then((data) => {
// Store the data in a context variable
context.setVariable('myContextVar', data);
done();
})
.catch((err) => {
done(err);
});

// Make a REST POST request
let payload = ...
fetch('http://some-backend-server-url',{ method: 'POST', body: payload})
.then((response) => {
if (response.status === 200) {
context.transition('success');
} else {
context.transition('failure');
}
done();
})
.catch((err) => {
done(err);
});
```
When you use the `async invoke` syntax, you can write your code in a synchronous way without callbacks or `promise-then` constructs. For example:
```javascript
// Make a REST GET request
const response = await fetch('http://some-backend-server-url');
if (response.status === 200) {
const data = await response.json();
// Store the data in a context variable
context.setVariable('myContextVar', data);
}
context.transition('success');
} else {
context.transition('failure');
}

// Make a REST POST request
let payload = ...
Expand Down Expand Up @@ -300,11 +241,10 @@ module.exports = {
}
},

invoke: (context, done) => {
invoke: async (context) => {
// Retrieve the value of the 'name' component property.
const { name } = context.properties() || '';
context.reply(`Hello ${name}`);
done();
}
}
```
Expand All @@ -330,9 +270,8 @@ const { latitudeVariable } = context.properties();
if (latitudeVariable) {
let _latitude = context.getVariable(latitudeVariable);
// ...
done();
} else {
done(new Error('State is missing latitudeVariable property.'));
throw new Error('State is missing latitudeVariable property.');
}
```

Expand All @@ -352,15 +291,14 @@ On the skill's **Settings** tab, add the custom parameter and provide a value (o
Add a `metadata` property to pass in the name of the variable, and then call `context.setVariable(<variable name>, <value>)` to set the value. For example:

```javascript
invoke (context, done) => {
invoke async (context) => {
const listVariableName = context.properties().variableName;
const fruits = [{"label": "Banana", "value": "banana"}, {"label": "Apple", "value": "apple"}, {"label": "Orange", "value": "orange"}];
// Write list of fruits to a context variable
context.setVariable(listVariableName, fruits);
// Navigate to next state without first prompting for user interaction.
context.keepTurn(true);
context.transition();
done();
}
```

Expand Down Expand Up @@ -395,7 +333,7 @@ metadata: {
supportedActions: ["validEmail", "invalidEmail"]
},

invoke: (context, done) => {
invoke: async (context) => {
// code to get component property values and validate email goes here
...
// Get composite bag entity object (cbe), create bag item object, update bag item in cbe
Expand All @@ -404,7 +342,6 @@ invoke: (context, done) => {
context.setVariable(variableName, cbe);
context.transition("validEmail");
context.keepTurn(true);
done();
}
```
> **Tip**: When working with composite bag entities, [entity event handlers](https://github.com/oracle/bots-node-sdk/blob/master/ENTITY_EVENT_HANDLER.md) are easier to use than custom components.
Expand All @@ -421,7 +358,7 @@ The payload can be a string or a rich message object. See [Conversation Messagin
This code sample keeps showing the user a random quote of the day until the user indicates they want to quit.

```javascript
invoke: (context, done) => {
invoke: async (context) => {

const quotes = require("./json/Quotes.json");
const quote = quotes[Math.floor(Math.random() * quotes.length)];
Expand All @@ -446,7 +383,6 @@ invoke: (context, done) => {
// easier to see how you intend the component to behave.
context.keepTurn(false);
}
done();
}
```
The Quotes.json file that's used in this example looks like this:
Expand Down
12 changes: 12 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Release Notes

- [Version 2.6.1](#v261)
- [Version 2.6.0](#v260)
- [Version 2.5.5](#v255)
- [Version 2.5.4](#v254)
Expand All @@ -10,6 +11,17 @@
- [Version 2.4.3](#v243)
- [Version 2.4.2](#v242)

## Version 2.6.1 <a name="v261">

### New Features

- **Support for code completion in Visual Studio Code**: When using a JavaScript-based component package, you now get code insight and code completion on newly created component services when using Visual Studio Code as your editor.
- **Improved custom component scaffolding**: Custom components created with the CLI now use the `async` syntax rather than the deprecated `done` callback.

### Fixed Issues

- EntityEvent interface was defined twice

## Version 2.6.0 <a name="v260">

### New Features
Expand Down
1 change: 1 addition & 0 deletions bin/commands/init.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ class CCInit extends CommandDelegate {
sdkBin: this.command.root()._name,
expressVersion: SDK.devDependencies.express,
expressTypesVersion: SDK.devDependencies['@types/express'],
nodeFetchTypesVersion: SDK.devDependencies['@types/node-fetch'],
typescriptVersion: SDK.devDependencies.typescript
});
}).then(() => { // run component code generator
Expand Down
1 change: 1 addition & 0 deletions bin/templates/ccpackage/ts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"devDependencies": {
"{{sdkName}}": "^{{sdkVersion}}",
"@types/express": "{{expressTypesVersion}}",
"@types/node-fetch": "{{nodeFetchTypesVersion}}",
"express": "{{expressVersion}}",
"typescript": "{{typescriptVersion}}"
},
Expand Down
11 changes: 9 additions & 2 deletions bin/templates/components/custom/template.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
'use strict';

const {CustomComponentContext} = require('@oracle/bots-node-sdk/lib'); // eslint-disable-line no-unused-vars

// You can use your favorite http client package to make REST calls, however, the node fetch API is pre-installed with the bots-node-sdk.
// Documentation can be found at https://www.npmjs.com/package/node-fetch
// Un-comment the next line if you want to make REST calls using node-fetch.
Expand All @@ -13,7 +15,13 @@ module.exports = {
},
supportedActions: ['weekday', 'weekend']
}),
invoke: (context, done) => {


/**
* invoke methods gets called when the custom component state is executed in the dialog flow
* @param {CustomComponentContext} context
*/
invoke: async (context) => {
// Retrieve the value of the 'human' component property.
const { human } = context.properties();
// Determine the current date
Expand All @@ -24,6 +32,5 @@ module.exports = {
context.reply(`Greetings ${human}`)
.reply(`Today is ${now.toLocaleDateString()}, a ${dayOfWeek}`)
.transition(isWeekend ? 'weekend' : 'weekday');
done();
}
};
7 changes: 3 additions & 4 deletions bin/templates/components/custom/template.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import {CustomComponent, CustomComponentMetadata, CustomComponentContext, InvocationCallback } from '@oracle/bots-node-sdk/lib';
import {CustomComponent, CustomComponentMetadata, CustomComponentContext} from '@oracle/bots-node-sdk/lib';

// You can use your favorite http client package to make REST calls, however, the node fetch API is pre-installed with the bots-node-sdk.
// Documentation can be found at https://www.npmjs.com/package/node-fetch
// Un-comment the next line if you want to make REST calls using node-fetch.
// import * as fetch from 'node-fetch';
// import fetch from 'node-fetch';

export class {{className}} implements CustomComponent {

Expand All @@ -17,7 +17,7 @@ export class {{className}} implements CustomComponent {
};
}

public invoke(context: CustomComponentContext, done: InvocationCallback): void {
public async invoke(context: CustomComponentContext): Promise<void> {
// Retrieve the value of the 'human' component property.
const { human } = context.properties();
// Determine the current date
Expand All @@ -28,7 +28,6 @@ export class {{className}} implements CustomComponent {
context.reply(`Greetings ${human}`)
.reply(`Today is ${now.toLocaleDateString()}, a ${dayOfWeek}`)
.transition(isWeekend ? 'weekend' : 'weekday');
done();
}

}
8 changes: 8 additions & 0 deletions bin/templates/components/entityeventhandler/template.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
'use strict';

const {EntityPublishMessageEvent, EntityBaseEvent, EntityResolutionContext} = require('@oracle/bots-node-sdk/lib'); // eslint-disable-line no-unused-vars

// You can use your favorite http client package to make REST calls, however, the node fetch API is pre-installed with the bots-node-sdk.
// Documentation can be found at https://www.npmjs.com/package/node-fetch
// Un-comment the next line if you want to make REST calls using node-fetch.
Expand All @@ -16,6 +18,8 @@ module.exports = {
/**
* Default message handler that includes acknowledgements when a bag item is updated
* or a bag item value is provided while the user was prompted for another item
* @param {EntityPublishMessageEvent} event
* @param {EntityResolutionContext} context
*/
publishMessage: async (event, context) => {
updatedItemsMessage(context);
Expand All @@ -24,6 +28,8 @@ module.exports = {
},
/**
* This handler is called when the composite bag entity is resolved
* @param {EntityBaseEvent} event
* @param {EntityResolutionContext} context
*/
resolved: async (event, context) => { // eslint-disable-line no-unused-vars
// add your back-end REST API call here
Expand All @@ -44,6 +50,7 @@ module.exports = {

/**
* Helper function to show acknowledgement message when a bag item value is updated.
* @param {EntityResolutionContext} context
*/
function updatedItemsMessage(context) {
if (context.getItemsUpdated().length > 0) {
Expand All @@ -54,6 +61,7 @@ function updatedItemsMessage(context) {

/**
* Helper function to show acknowledgement message when a bag item value is provided when user was prompted for anther bag item.
* @param {EntityResolutionContext} context
*/
function outOfOrderItemsMessage(context) {
if (context.getItemsMatchedOutOfOrder().length > 0) {
Expand Down
Loading

0 comments on commit ace962a

Please sign in to comment.