Guidelines for creating/migrating a dialog to channelData v3

Orderly steps for building or migrating a dialog to be compatible with channelData normalized model (version 3)

Introduction

If you want your use case to be compatible with the new channelData normalized model (v3), both for a new developed use case or for migrating an existing one, you should:

  1. Develop the use case as explained in Building an Aura Bot dialog.
  2. After that, carry out certain extra tasks that are orderly included in the following sections.

Examples in this section will be referenced to greetings dialog, which has been already prepared for channelData v3 version.

Create v3 folder

According to the general guidelines, all dialogs must be created inside the library’s src/ folder.

However, channelData v3 dialogs must be included in a v3 folder, within src/:

${your_library_root_folder}/src/v3

In this folder, you must place the dialog’s .ts files with the dialog’s implementation and the .spec.ts files for testing.

These files should specify the channelData version in the filename, for example: /my-dialog/newdialog-v3-dialog.ts.

Dialog’s id

The dialog’s id should include channelData version for its differentiation.

For example, in greetings dialog this is done appending v3 to the dialog’s id.

     * The id of the dialog.
     */
    public static id: string = 'greetings-v3';

Dialog configuration

Regarding the dialog’s configuration, which is found in the dialog-config.json file, inside settings/ folder, the channelData v3 dialog configuration must include a new property called channelDataVersion.

This property should be filled with the dialog’s version, in our use case v3. If no version is specified, it is understood that the dialog works in v1 mode (for classic use cases).

{
            "id": "greetings-v3",
            "channelDataVersion": "v3",
            "triggerConditions": [
                {
                    "intent": "intent.common.greetings"
                }
            ]
        }

It is also recommended to set suggestions to false in the dialog’s configuration.

{
            "id": "greetings-v3",
            "channelDataVersion": "v3",
            "suggestions": false,
            "triggerConditions": [
                {
                    "intent": "intent.common.greetings"
                }
            ]
        }

As shown in the example, the format for intents in channelData version 3 only changes internally. Therefore, when defining an intent, it is not necessary to specify its version, as the dialog loading system will do it internally.

Dialog implementation

Building a dialog in channelData v3 is quite similar to legacy channelData dialogs, whose process is fully explained in Building an Aura Bot dialog.The main difference is in the dialog’s output, which should be compatible with the channelData version’s schema.

For this case, the utility ChannelDataResponseMapper.formatChannelDataV3() creates a valid channelData v3 response and can be used with properties obtained from the Bot Framework’s context:

const correlator: string = ContextUtils.getCorrelator(stepContext.context);
const auraUser = ContextUtils.getAuraUser(stepContext.context);
const intentResult = DialogUtils.getDataActiveDialog(stepContext, 'options').intentResult;
const activity: Partial<Activity> = {
            text: this.localizer.getText('common:common.greetings.main', auraUser, correlator),
            channelData: ChannelDataResponseMapper.formatChannelDataV3(intentResult, intentConfigSettings, correlator)
        };

There is another important utility in this code snippet, ContextUtils.getIntentConfig(). It is used if there are configurations that refer to the same intent but have different implementations regarding channelData versions. This utility will obtain the dialog’s configuration based on this version.

Find the full waterfall step in the greetings dialog .

Dialog implementation extending existing dialogs

To avoid code repetition, you can reuse the code of existing v1 dialogs and extend it to be reused in v3 dialogs. To do this, follow these simple steps:

  1. Modify your original v1 dialog to receive the dialogId as a param:
export default class ExampleDialog extends ComponentDialog {
    public static id: string = 'example-dialog';
    private configuration: Configuration;
    private logger: AuraLog;

    // Dialog id is received as a param or set to the original v1 value as default
    constructor(configuration: Configuration, id: string = ExampleDialog.id) {
        super(id);
        this.initialDialogId = id;

        this.logger = new AuraLog(id);
        this.configuration = configuration;

        super.addDialog(new WaterfallDialog(id, [
            this.examplePrompt.bind(this),
            this.exampleResult.bind(this),
        ]));
        const confirmCurrentPrompt = new ChoicePrompt(this.promptsNames.CONFIRM_CURRENT, PromptUtils.getRetriesValidator(0));
        confirmCurrentPrompt.style = ListStyle.heroCard;
        super.addDialog(confirmCurrentPrompt);
    }
    .
    .
    .
  1. Create your v3 dialog extending your v1 dialog:
import {
    Configuration} from '@telefonica/aura-bot-utilities/lib/aura-bot-common';
import ExampleDialog from '../../whatsapp/otp-confirm-user-phone-number-dialog';
import OtpPhoneNumberV3Dialog from './otp-phone-number-v3-dialog';

/**
 * Aura bot dialog implementation for otp phone number TODO
 */
export default class ExampleV3Dialog extends ExampleDialog {
    /**
     * The id of the dialog.
     */
    public static id: string = 'otp-confirm-user-phone-number-v3-dialog';

    /**
     * Constructor dialog
     *
     * @param  {Configuration} configuration configuration dialog
     */
    constructor(configuration: Configuration) {
        super(configuration, ExampleV3Dialog.id);
    }
}
  1. If your dialog does not modify the response channelData, the process is finished.
    But if your original v1 dialog modifies some channelData values, you need to use auxiliary methods to create this channelData and overwrite them in the v3 dialog:
export default class ExampleDialog extends ComponentDialog {
    .
    .
    .
    private async registerUser(stepContext: WaterfallStepContext): Promise<DialogTurnResult> {
            const activity: Partial<Activity> = {
                type: 'message',
                text: getText('login.otp.success')
            };
            activity.channelData = await this.getRedirectChannelData(auraUser, this.configuration, this.logger, corr, dialogData?.originalIntent);

            await stepContext.context.sendActivity(activity);
    }

    protected async getRedirectChannelData(auraUser: AuraUserBaseModel<any>, configuration: Configuration, logger: AuraLog, corr: string, originalIntent?: Intent):
    Promise<AuraModels.ResponseChannelDataV3.ResponseChannelData | any> {
        return {
            customData: await LinkingUtils.checkTacAndRedirect(auraUser, this.configuration, this.logger, corr, originalIntent)
        };
    }
    .
    .
    .
export default class ExampleV3Dialog extends ExampleDialog {
    .
    .
    .
    protected async getRedirectChannelData(auraUser: AuraUserBaseModel<any>, configuration: Configuration, logger: AuraLog,
        corr: string, originalIntent?: Intent): Promise<AuraModels.ResponseChannelDataV3.ResponseChannelData> {
        const actions: AuraModels.ResponseChannelDataV3.Action[] = [];
        const redirectAction = await LinkingUtils.checkTacAndRedirectV3(auraUser, configuration, logger, corr, originalIntent);
        if (redirectAction) {
            actions.push(redirectAction);
        }
        return {
            actions
        };
    }
}