Skip to content

Commit 8979ff2

Browse files
committed
Merge branch 'development' version 0.6.0
2 parents 515fbbf + 7285226 commit 8979ff2

File tree

7 files changed

+241
-70
lines changed

7 files changed

+241
-70
lines changed

CHANGELOG.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,30 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](http://keepachangelog.com/)
55
and this project adheres to [Semantic Versioning](http://semver.org/).
66

7+
## [0.6.0] - 2017-07-07
8+
### Added
9+
- Added the ability to pass dynamic configuration from parent page to the
10+
bot loader via an event
11+
- Added response cards object display to sample parent page
12+
13+
### Changed
14+
- Bot loader script now uses its own credential variable instead of setting
15+
it into the global AWS object
16+
- Bumped AWS SDK version in bot loader
17+
- Added functionality to remove event handlers in bot loader for events that
18+
only fire once
19+
20+
### Fixed
21+
- Typos, invalid links and display issues in README files
22+
23+
## [0.5.2] - 2017-07-05
24+
### Fixed
25+
- Credential loading issue in parent bot-loader.js
26+
727
## [0.5.1] - 2017-06-06
828
### Changed
929
- Copyrights and Amazon software license
10-
30+
1131
## [0.5.0] - 2017-06-05
1232
### Added
1333
- Ability to deploy a sample bot based on the OrderFlowers sample

lex-web-ui/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ environment. The files follow this directory structure:
124124

125125
Here's an example of the `config.dev.json` file:
126126

127-
```json
127+
```
128128
{
129129
"cognito": {
130130
"poolId": "us-east-1:deadbeef-cac0-babe-abcd-abcdef01234",
@@ -168,7 +168,7 @@ played back. The chatbot UI provides options to control the playback.
168168
For example, you can allow to interrupt the playback of long responses and
169169
fine tune the various values associated with interruptions:
170170

171-
```json
171+
```
172172
...
173173
lex: {
174174

lex-web-ui/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "lex-web-ui",
3-
"version": "0.5.1",
3+
"version": "0.6.0",
44
"description": "Lex ChatBot Web Interface",
55
"author": "AWS",
66
"license": "Amazon Software License",

lex-web-ui/static/iframe/README.md

Lines changed: 85 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
###Overview
1+
### Overview
22
The chatbot UI can be embedded in an existing web site by loading it as
33
an iframe. This includes embedding it in a cross-origin setup where the
44
chatbot is served from a server, S3 bucket or CloudFront distribution
55
in a domain that is different from the hosting web site.
66

77
If you want to know more about the chatbot UI component, please refer to
8-
its [README](https://github.com/awslabs/aws-lex-web-ui/lex-web-ui) file.
8+
its [README](https://github.com/awslabs/aws-lex-web-ui/blob/master/lex-web-ui/README.md) file.
99

10-
###Adding the ChatBot UI to your Website
10+
### Adding the ChatBot UI to your Website
1111
This project provides a sample JavaScript loader
1212
[bot-loader.js](./bot-loader.js") and CSS file [bot.css](./bot.css)
1313
that can be used to add the chatbot to an existing web site using a
@@ -22,12 +22,12 @@ tags to your web page:
2222
<script src="https://myboturl.example.com/static/iframe/bot-loader.js"></script>
2323
```
2424

25-
###Passing Data Between Parent and ChatBot UI
26-
The chatbot iframe supports passing data to and from the hosting site. This
27-
is done using the JavaScript
25+
### Passing Data Between Parent Page and ChatBot UI
26+
The chatbot iframe supports passing data to and from the hosting parent
27+
page. This is done using the JavaScript
2828
[postMessage](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage)
29-
call. This enables use cases such as passing credentials from
30-
the parent hosting site to the chat iframe or passing events (e.g.
29+
call. This mechanism enables use cases such as passing credentials
30+
from the parent hosting site to the chat iframe or passing events (e.g.
3131
windows resize) from the chat window to the parent window.
3232

3333
The chatbot iframe sends Lex bot state update events to the parent
@@ -36,18 +36,32 @@ of the bot. The bot-loader.js script relays these messages back to the
3636
parent by emitting the `updatelexstate` events. The event object will
3737
contain the Lex state variables in the `details.state` field.
3838

39-
###Configuration
40-
####Parent Configuration
41-
The parent page configuration is held in a JSON config file:
42-
[config.json](./config.json). This file is loaded by
43-
the bot-loader.js script. Here's an example of the file format:
44-
```json
39+
### Configuration
40+
#### Parent Configuration File
41+
The [bot-loader.js](./bot-loader.js) script loads its initial
42+
configuration from the JSON config file: [config.json](./config.json).
43+
This file is meant to be used as the build-time configuration of the
44+
bot-loader.js script. It serves as the base config so the root level
45+
keys in the JSON object should not be removed.
46+
47+
NOTE: The values in this file may be overwritten by environmental
48+
variables in the build process.
49+
50+
Here's an example of the file format:
51+
```
4552
{
53+
// iframe origin - see: Cross Origin Configuration section below
4654
"iframeOrigin": "http://localhost:8080",
55+
56+
// time to wait for the config event in ms
57+
"configEventTimeOutInMs": 10000,
58+
59+
// used to initialize the AWS SDK and Cognito
4760
"aws": {
4861
"cognitoPoolId": "us-east-1:deadbeef-cac0-babe-abcd-abcdef01234",
4962
"region": "us-east-1"
5063
}
64+
// chatbot UI configuration passed from parent - see: ChatBot UI Configuration section below
5165
"iframeConfig": {
5266
...
5367
"lex": {
@@ -59,35 +73,67 @@ the bot-loader.js script. Here's an example of the file format:
5973
}
6074
```
6175

62-
####ChatBot UI Configuration
63-
The chatbot UI has a local build-time config (see:
64-
`src/config/config.prod.json`). You can also pass or override this
65-
configuration from the parent site via two mechanisms:
66-
67-
- **ChatBot UI Configuration from File.** The parent
68-
[config.json](./config.json) file contains the `iframeConfig` field
69-
which is passed to the chatbot UI. This configuration is dynamically
70-
sent to the chatbot UI as a response of the the `onInitIframeConfig`
71-
event. The values delivered via this mechanism override the chatbot
72-
ui local config files and URL config parameter.
73-
- **ChatBot Configuration form URL Parameter.** The chatbot UI
74-
configuration can be initialized using the `config` URL parameter. Your
75-
application can dynamically add the parameter to the URL This is supported
76-
both in iframe and stand-alone mode of the chatbot UI. This config URL
77-
parameter should follow the same JSON structure of the `configDefault`
78-
object in the `src/config/index.js` file. This parameter should be a JSON
79-
serialized and URL encoded string. Values from this parameter override
80-
the ones from the chatbot ui local config files. For example to change
81-
the initialText config field, you can use a URL like this:
82-
`https://mybucket.s3.amazonaws.com/index.html#/?config=%7B%22lex%22%3A%7B%22initialText%22%3A%22Ask%20me%20a%20question%22%7D%7D`
83-
84-
####Cross Origin Configuration
76+
#### Parent Event Configuration
77+
The parent page can also set the bot loader configuration via an
78+
event. The bot-loader.js script emits the `receivelexconfig` event which
79+
signals to the parent that it is ready to receive a configuration
80+
object. At which point, the bot-loader.js script will wait a 10
81+
seconds timeout (by default) to receive a event named `loadlexconfig`.
82+
The timeout is controlled by the `configEventTimeOutInMs` field in
83+
the config JSON file. The event object contains the config in the
84+
`detail.config` field.
85+
86+
The configuration from the JSON file is merged with the value for this
87+
event. The values received via this event take precedence over the
88+
JSON file.
89+
90+
For example, to pass the browser
91+
[user agent](https://developer.mozilla.org/en-US/docs/Web/API/NavigatorID/userAgent),
92+
you can add code to your site along the lines of:
93+
```javascript
94+
document.addEventListener('receivelexconfig', onReceiveLexConfig, false);
95+
96+
function onReceiveLexConfig() {
97+
document.removeEventListener('receivelexconfig', onReceiveLexConfig, false);
98+
var config = {
99+
iframeConfig: {
100+
lex: {
101+
sessionAttributes: {
102+
userAgent: navigator.userAgent,
103+
},
104+
},
105+
},
106+
};
107+
108+
var event = new CustomEvent('loadlexconfig', { detail: { config: config } });
109+
document.dispatchEvent(event);
110+
}
111+
```
112+
113+
#### ChatBot UI Configuration
114+
The chatbot UI has its own configuration (see the
115+
[README](https://github.com/awslabs/aws-lex-web-ui/blob/master/lex-web-ui/README.md#configuration-and-customization)
116+
for details. You can also pass or override the chatbot UI configuration
117+
from the parent site via the following mechanisms:
118+
119+
1. **Config Object.** The parent configuration config object (either from
120+
the [config.json](./config.json) file or passed via the `loadlexconfig`
121+
event) contains the `iframeConfig` field which is passed to the chatbot
122+
UI. This configuration is dynamically sent to the chatbot UI as a
123+
response of the the `onInitIframeConfig` event. The values delivered
124+
via this mechanism override the chatbot UI local config files and URL
125+
config parameter.
126+
2. **URL Parameter.** The chatbot UI configuration can be initialized using
127+
the `config` URL parameter. For details, see the
128+
[URL Parameter](https://github.com/awslabs/aws-lex-web-ui/blob/master/lex-web-ui/README.md#url-parameter)
129+
section. NOTE: the bot-loader.js script does not use URL parameters.
130+
131+
#### Cross Origin Configuration
85132
If the chatbot UI is hosted on a different
86133
[Origin](https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy)
87134
from the parent window, you need to configure the `iframeOrigin` field
88-
in the parent config.json file to point to the origin of the iframe. This
135+
in the parent `config.json` file to point to the origin of the iframe. This
89136
origin configuration is used to control which sites can communicate with
90137
the iframe. Conversely, you would need to configure the `ui.parentOrigin`
91138
field in the iframe config. The origin configuration of this sample page
92139
was done at build time by the CloudFormation stack that created it.
93-

lex-web-ui/static/iframe/bot-loader.js

Lines changed: 88 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929

3030
// AWS SDK script dynamically added to the DOM
3131
// https://github.com/aws/aws-sdk-js
32-
sdkLink: 'https://sdk.amazonaws.com/js/aws-sdk-2.60.0.min.js',
32+
sdkLink: 'https://sdk.amazonaws.com/js/aws-sdk-2.82.0.min.js',
3333
};
3434

3535
/*
@@ -53,6 +53,7 @@
5353
var iframe;
5454
var container;
5555
var messageHandler = {};
56+
var credentials;
5657

5758
if (isSupported()) {
5859
// initialize iframe once the DOM is loaded
@@ -78,7 +79,10 @@
7879
}
7980

8081
function main() {
81-
loadConfig(configUrl)
82+
loadConfigFromJsonFile(configUrl)
83+
.then(function loadConfigFromEventPromise(conf) {
84+
return loadConfigFromEvent(conf);
85+
})
8286
.then(function assignConfig(conf) {
8387
config = conf;
8488
return Promise.resolve();
@@ -124,8 +128,8 @@
124128
/**
125129
* Loads the bot config from a JSON file URL
126130
*/
127-
function loadConfig(url) {
128-
return new Promise(function loadConfigPromise(resolve, reject) {
131+
function loadConfigFromJsonFile(url) {
132+
return new Promise(function loadConfigFromJsonFilePromise(resolve, reject) {
129133
var xhr = new XMLHttpRequest();
130134
xhr.open('GET', url);
131135
xhr.responseType = 'json';
@@ -147,6 +151,75 @@
147151
});
148152
};
149153

154+
/**
155+
* Loads dynamic bot config from an event
156+
* Merges it with the config passed as parameter
157+
*/
158+
function loadConfigFromEvent(conf) {
159+
return new Promise(function waitForConfigEvent(resolve, reject) {
160+
var timeoutInMs = conf.configEventTimeOutInMs || 10000;
161+
162+
var timeoutId = setTimeout(onConfigEventTimeout, timeoutInMs);
163+
document.addEventListener('loadlexconfig', onConfigEventLoaded, false);
164+
165+
var intervalId = setInterval(emitReceiveEvent, 500);
166+
// signal that we are ready to receive the dynamic config
167+
function emitReceiveEvent() {
168+
var event = new Event('receivelexconfig');
169+
document.dispatchEvent(event);
170+
};
171+
172+
function onConfigEventLoaded(evt) {
173+
clearTimeout(timeoutId);
174+
clearInterval(intervalId);
175+
document.removeEventListener('loadlexconfig', onConfigEventLoaded, false);
176+
177+
if (evt && ('detail' in evt) && evt.detail && ('config' in evt.detail)) {
178+
var evtConfig = evt.detail.config;
179+
var mergedConfig = mergeConfig(conf, evtConfig);
180+
return resolve(mergedConfig);
181+
} else {
182+
return reject('malformed config event: ' + JSON.stringify(evt));
183+
}
184+
};
185+
186+
function onConfigEventTimeout() {
187+
clearInterval(intervalId);
188+
document.removeEventListener('loadlexconfig', onConfigEventLoaded, false);
189+
return reject('config event timed out');
190+
};
191+
});
192+
193+
/**
194+
* Merges config objects. The initial set of keys to merge are driven by
195+
* the baseConfig. The srcConfig values override the baseConfig ones.
196+
*/
197+
function mergeConfig(baseConfig, srcConfig) {
198+
// use the baseConfig first level keys as the base for merging
199+
return Object.keys(baseConfig)
200+
.map(function (key) {
201+
var mergedConfig = {};
202+
var value = baseConfig[key];
203+
if (key in srcConfig) {
204+
value = (typeof baseConfig[key] === 'object') ?
205+
// recursively merge sub-objects in both directions
206+
Object.assign(
207+
mergeConfig(srcConfig[key], baseConfig[key]),
208+
mergeConfig(baseConfig[key], srcConfig[key]),
209+
) :
210+
srcConfig[key];
211+
}
212+
mergedConfig[key] = value;
213+
return mergedConfig;
214+
})
215+
.reduce(function (merged, configItem) {
216+
return Object.assign({}, merged, configItem);
217+
},
218+
{}
219+
);
220+
};
221+
}
222+
150223
/**
151224
* Adds a div container to document body which will wrap the chat bot iframe
152225
*/
@@ -202,12 +275,12 @@
202275
return Promise.reject('unable to find AWS object');
203276
}
204277

205-
AWS.config.region = config.aws.region;
206-
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
207-
IdentityPoolId: config.aws.cognitoPoolId,
208-
});
278+
credentials = new AWS.CognitoIdentityCredentials(
279+
{ IdentityPoolId: config.aws.cognitoPoolId },
280+
{ region: config.aws.region },
281+
);
209282

210-
return Promise.resolve();
283+
return credentials.getPromise()
211284
}
212285

213286
/**
@@ -220,21 +293,21 @@
220293
console.log('[INFO] found existing identity ID: ', identityId);
221294
}
222295

223-
if (!('getPromise' in AWS.config.credentials)) {
296+
if (!('getPromise' in credentials)) {
224297
console.error('getPromise not found in credentials');
225298
return Promise.reject('getPromise not found in credentials');
226299
}
227300

228-
return AWS.config.credentials.getPromise()
301+
return credentials.getPromise()
229302
.then(function storeIdentityId() {
230303
console.log('[INFO] storing identity ID:',
231-
AWS.config.credentials.identityId
304+
credentials.identityId
232305
);
233-
localStorage.setItem('cognitoid', AWS.config.credentials.identityId);
306+
localStorage.setItem('cognitoid', credentials.identityId);
234307
identityId = localStorage.getItem('cognitoid');
235308
})
236309
.then(function getCredentialsPromise() {
237-
return Promise.resolve(AWS.config.credentials);
310+
return Promise.resolve(credentials);
238311
});
239312
}
240313

@@ -261,6 +334,7 @@
261334

262335
function onIframeLoaded(evt) {
263336
clearTimeout(timeoutId);
337+
iframeElement.removeEventListener('load', onIframeLoaded, false);
264338
toggleShowUi();
265339
return resolve(iframeElement);
266340
};

0 commit comments

Comments
 (0)