Search code examples
botframework

How do I instantiate a turnContext using the adapter and request as parameters


I would like to instantiate a turnContext to be used in integration testing. How would I be able to instantiate one without calling on the processActivity() method of the adapter?

I am looking at the documentation but it shows that I would need the request of the post call as the parameter. I would like my testing to be independant of the post call. I would then assume that I would need to instantiate the request? How would I go about doing so?

Image of documentation


Solution

  • This is a bit hard to answer without knowing how you are planning to use the code. That being said, it's not that hard to create a new turnContext and also bypass the processActivity(). Given how you are referencing turnContext and processActivity(), I'm assuming you are using the Node SDK. Implementing in C# wouldn't be too different.

    Here are two options, both utilizing the creation of a new adapter, however you can also pass in an already established turnContext, if desired:

    1. Use .createContext in server.post in the index.js file, or
    2. Maintain the processActivity() method in the server.post. This calls a new "onTurn" method in the bot.js file. In doing so, this allows you to control when and how the new "onTurn" is accessed.

    Option 1: In the index.js file, you will want to create a new adapter or make a copy of the first depending on your needs:

    const adapter = new BotFrameworkAdapter({
        appId: endpointConfig.appId || process.env.MicrosoftAppId,
        appPassword: endpointConfig.appPassword || process.env.MicrosoftAppPassword
    });
    
    const newAdapter = adapter;
    

    or

    const adapter = new BotFrameworkAdapter({
        appId: endpointConfig.appId || process.env.MicrosoftAppId,
        appPassword: endpointConfig.appPassword || process.env.MicrosoftAppPassword
    });
    
    const newAdapter = new BotFrameworkAdapter({
        appId: endpointConfig.appId || process.env.MicrosoftAppId,
        appPassword: endpointConfig.appPassword || process.env.MicrosoftAppPassword
    });
    

    Include the onTurnError code to catch errors:

    // Catch-all for errors.
    adapter.onTurnError = async (context, error) => {
        console.error(`\n [onTurnError]: ${ error }`);
        await context.sendActivity(`Oops. Something went wrong!`);
    };
    
    // Catch-all for errors.
    newAdapter.onTurnError = async (context, error) => {
        console.error(`\n [onTurnError]: ${ error }`);
        await context.sendActivity(`Oops. Something went wrong!`);
    };
    

    Then, set the new adapters and create the new turnContext:

    server.post('/api/messages', (req, res) => {
        adapter.processActivity(req, res, async (turnContext) => {
            await bot.onTurn(turnContext);
        });
        newAdapter.createContext(req, res);
    });
    

    Options 2: In the index.js file, building off of the above code, set the adapters to await the individual "onTurn" methods:

    // Listen for incoming requests.
    server.post('/api/messages', (req, res) => {
        adapter.processActivity(req, res, async (turnContext) => {
            await bot.onTurn(turnContext);
        });
        newAdapter.processActivity(req, res, async (turnContext) => {
            await bot.newOnTurn(turnContext);
        });
    });
    

    In the bot.js file, you will have your two "onTurn" methods. In this example, the different "onTurn" methods are called based on whether a message is sent or I am deleting user data (I am sending this event via the Emulator => Conversation menu item). What you decide to match on is up to you.

        async newOnTurn(turnContext) {
            if (turnContext.activity.type === ActivityTypes.DeleteUserData) {
                const dc = await this.dialogs.createContext(turnContext);
    
                await dc.context.sendActivity(`Looks like you deleted some user data.`);
            }
        }
    
        async onTurn(turnContext) {
            if (turnContext.activity.type === ActivityTypes.Message) {
                const dc = await this.dialogs.createContext(turnContext);
    
                await dc.context.sendActivity(`Looks like you sent a message.`);
            }
        }
    

    Hope of help!