Skip to content

Commit

Permalink
fix:(@aws-amplify/interactions): refactor-lex-v1 (#10155)
Browse files Browse the repository at this point in the history
  • Loading branch information
ashwinkumar6 authored Aug 16, 2022
1 parent 0ce6b95 commit 51a4598
Show file tree
Hide file tree
Showing 8 changed files with 882 additions and 870 deletions.
1,124 changes: 296 additions & 828 deletions packages/interactions/__tests__/Interactions-unit-test.ts

Large diffs are not rendered by default.

475 changes: 475 additions & 0 deletions packages/interactions/__tests__/providers/AWSLexProvider-unit-test.ts

Large diffs are not rendered by default.

79 changes: 49 additions & 30 deletions packages/interactions/src/Interactions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export class InteractionsClass {
*
* @param {InteractionsOptions} options - Configuration object for Interactions
*/
constructor(options: InteractionsOptions) {
constructor(options: InteractionsOptions = {}) {
this._options = options;
logger.debug('Interactions Options', this._options);
this._pluggables = {};
Expand All @@ -44,9 +44,9 @@ export class InteractionsClass {
/**
*
* @param {InteractionsOptions} options - Configuration object for Interactions
* @return {Object} - The current configuration
* @return {InteractionsOptions} - The current configuration
*/
configure(options: InteractionsOptions) {
public configure(options: InteractionsOptions): InteractionsOptions {
const opt = options ? options.Interactions || options : {};
logger.debug('configure Interactions', { opt });
this._options = { bots: {}, ...opt, ...opt.Interactions };
Expand All @@ -63,35 +63,51 @@ export class InteractionsClass {
}
}

// Check if AWSLex provider is already on pluggables
if (
!this._pluggables.AWSLexProvider &&
bots_config &&
Object.keys(bots_config)
.map(key => bots_config[key])
.find(bot => !bot.providerName || bot.providerName === 'AWSLexProvider')
) {
this._pluggables.AWSLexProvider = new AWSLexProvider();
}

Object.keys(this._pluggables).map(key => {
this._pluggables[key].configure(this._options.bots);
// configure bots to their specific providers
Object.keys(bots_config).forEach(botKey => {
const bot = bots_config[botKey];
const providerName = bot.providerName || 'AWSLexProvider';
if (
!this._pluggables.AWSLexProvider &&
providerName === 'AWSLexProvider'
) {
this._pluggables.AWSLexProvider = new AWSLexProvider();
} else if (this._pluggables[providerName]) {
this._pluggables[providerName].configure({ [bot.name]: bot });
} else {
logger.debug(
`bot ${bot.name} was not configured as ${providerName} provider was not found`
);
}
});

return this._options;
}

public addPluggable(pluggable: InteractionsProvider) {
if (pluggable && pluggable.getCategory() === 'Interactions') {
if (!this._pluggables[pluggable.getProviderName()]) {
pluggable.configure(this._options.bots);
this._pluggables[pluggable.getProviderName()] = pluggable;
return;
} else {
throw new Error(
'Bot ' + pluggable.getProviderName() + ' already plugged'
);
}
if (!(pluggable && pluggable.getCategory() === 'Interactions')) {
throw new Error('Invalid pluggable');
}

if (!this._pluggables[pluggable.getProviderName()]) {
// configure bots for the new plugin
Object.keys(this._options.bots)
.filter(
botKey =>
this._options.bots[botKey].providerName ===
pluggable.getProviderName()
)
.forEach(botKey => {
const bot = this._options.bots[botKey];
pluggable.configure({ [bot.name]: bot });
});

this._pluggables[pluggable.getProviderName()] = pluggable;
return;
} else {
throw new Error(
'Pluggable ' + pluggable.getProviderName() + ' already plugged'
);
}
}

Expand All @@ -112,14 +128,14 @@ export class InteractionsClass {
message: string | object
): Promise<InteractionsResponse> {
if (!this._options.bots || !this._options.bots[botname]) {
throw new Error('Bot ' + botname + ' does not exist');
return Promise.reject('Bot ' + botname + ' does not exist');
}

const botProvider =
this._options.bots[botname].providerName || 'AWSLexProvider';

if (!this._pluggables[botProvider]) {
throw new Error(
return Promise.reject(
'Bot ' +
botProvider +
' does not have valid pluggin did you try addPluggable first?'
Expand All @@ -128,7 +144,10 @@ export class InteractionsClass {
return await this._pluggables[botProvider].sendMessage(botname, message);
}

public onComplete(botname: string, callback: (err, confirmation) => void) {
public onComplete(
botname: string,
callback: (err, confirmation) => void
): void {
if (!this._options.bots || !this._options.bots[botname]) {
throw new Error('Bot ' + botname + ' does not exist');
}
Expand All @@ -146,5 +165,5 @@ export class InteractionsClass {
}
}

export const Interactions = new InteractionsClass(null);
export const Interactions = new InteractionsClass();
Amplify.register(Interactions);
59 changes: 48 additions & 11 deletions packages/interactions/src/Providers/AWSLexProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,21 @@
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
* and limitations under the License.
*/

import { AbstractInteractionsProvider } from './InteractionsProvider';
import {
InteractionsOptions,
AWSLexProviderOptions,
InteractionsResponse,
InteractionsMessage,
} from '../types';
import {
LexRuntimeServiceClient,
PostTextCommand,
PostTextCommandInput,
PostTextCommandOutput,
PostContentCommand,
PostContentCommandInput,
PostContentCommandOutput,
} from '@aws-sdk/client-lex-runtime-service';
import {
ConsoleLogger as Logger,
Expand All @@ -44,7 +48,24 @@ export class AWSLexProvider extends AbstractInteractionsProvider {
return 'AWSLexProvider';
}

reportBotStatus(data, botname) {
configure(config: AWSLexProviderOptions = {}): AWSLexProviderOptions {
const propertiesToTest = ['name', 'alias', 'region'];

Object.keys(config).forEach(botKey => {
const botConfig = config[botKey];

// is bot config correct
if (!propertiesToTest.every(x => x in botConfig)) {
throw new Error('invalid bot configuration');
}
});
return super.configure(config);
}

reportBotStatus(
data: PostTextCommandOutput | PostContentCommandOutput,
botname: string
) {
// Check if state is fulfilled to resolve onFullfilment promise
logger.debug('postContent state', data.dialogState);
if (
Expand Down Expand Up @@ -94,11 +115,16 @@ export class AWSLexProvider extends AbstractInteractionsProvider {
botname: string,
message: string | InteractionsMessage
): Promise<InteractionsResponse> {
// check if bot exists
if (!this._config[botname]) {
return Promise.reject('Bot ' + botname + ' does not exist');
}
const credentials = await Credentials.get();
if (!credentials) {

// check if credentials are present
let credentials;
try {
credentials = await Credentials.get();
} catch (error) {
return Promise.reject('No credentials');
}

Expand All @@ -108,7 +134,7 @@ export class AWSLexProvider extends AbstractInteractionsProvider {
customUserAgent: getAmplifyUserAgent(),
});

let params;
let params: PostTextCommandInput | PostContentCommandInput;
if (typeof message === 'string') {
params = {
botAlias: this._config[botname].alias,
Expand All @@ -118,10 +144,10 @@ export class AWSLexProvider extends AbstractInteractionsProvider {
};

logger.debug('postText to lex', message);

try {
const postTextCommand = new PostTextCommand(params);
const data = await this.lexRuntimeServiceClient.send(postTextCommand);

this.reportBotStatus(data, botname);
return data;
} catch (err) {
Expand All @@ -133,15 +159,21 @@ export class AWSLexProvider extends AbstractInteractionsProvider {
options: { messageType },
} = message;
if (messageType === 'voice') {
if (!(content instanceof Blob || content instanceof ReadableStream))
return Promise.reject('invalid content type');

params = {
botAlias: this._config[botname].alias,
botName: botname,
contentType: 'audio/x-l16; sample-rate=16000',
inputStream: content,
contentType: 'audio/x-l16; sample-rate=16000; channel-count=1',
inputStream: await convert(content),
userId: credentials.identityId,
accept: 'audio/mpeg',
};
} else {
if (typeof content !== 'string')
return Promise.reject('invalid content type');

params = {
botAlias: this._config[botname].alias,
botName: botname,
Expand All @@ -157,7 +189,11 @@ export class AWSLexProvider extends AbstractInteractionsProvider {
const data = await this.lexRuntimeServiceClient.send(
postContentCommand
);
const audioArray = await convert(data.audioStream);

const audioArray = data.audioStream
? await convert(data.audioStream)
: undefined;

this.reportBotStatus(data, botname);
return { ...data, ...{ audioStream: audioArray } };
} catch (err) {
Expand All @@ -166,9 +202,10 @@ export class AWSLexProvider extends AbstractInteractionsProvider {
}
}

onComplete(botname: string, callback) {
onComplete(botname: string, callback: (err, confirmation) => void) {
// does bot exist
if (!this._config[botname]) {
throw new ErrorEvent('Bot ' + botname + ' does not exist');
throw new Error('Bot ' + botname + ' does not exist');
}
this._botsCompleteCallback[botname] = callback;
}
Expand Down
1 change: 1 addition & 0 deletions packages/interactions/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { Interactions } from './Interactions';
export default Interactions;

export * from './types';
export * from './Providers/InteractionsProvider';
export * from './Providers/AWSLexProvider';

export { Interactions };
2 changes: 1 addition & 1 deletion packages/interactions/src/types/Provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { InteractionsResponse } from './Response';

export interface InteractionsProvider {
// configure your provider
configure(config: object): object;
configure(config: InteractionsOptions): InteractionsOptions;

// return 'Interactions'
getCategory(): string;
Expand Down
11 changes: 11 additions & 0 deletions packages/interactions/src/types/Providers/AWSLexProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export interface AWSLexProviderOption {
name: string;
alias: string;
region: string;
providerName?: string;
onComplete?(botname: string, callback: (err, confirmation) => void): void;
}

export interface AWSLexProviderOptions {
[key: string]: AWSLexProviderOption;
}
1 change: 1 addition & 0 deletions packages/interactions/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@
*/
export * from './Interactions';
export * from './Provider';
export * from './Providers/AWSLexProvider';
export * from './Response';

0 comments on commit 51a4598

Please sign in to comment.