OfficeDev / teams-toolkit

Developer tools for building Teams apps
Other
459 stars 189 forks source link

SendAdaptiveCards does not work with generic objects #10689

Open MichaMican opened 9 months ago

MichaMican commented 9 months ago

Describe the bug When calling the SendAdaptiveCard() function of a TeamsBotInstallation with a generic object of type object the internal serialization does not wokr properly and a empty adaptive card is send (see Screenshots)

To Reproduce Steps to reproduce the behavior:

  1. Create a Notification bot (with toolkit).
  2. Replace the notification PostAsync controller code with the following:

    [HttpPost]
    public async Task<ActionResult> PostAsync([FromBody] object body, CancellationToken cancellationToken = default)
    {
    var pageSize = 100;
    string continuationToken = null;
    do
    {
        var pagedInstallations = await _conversation.Notification.GetPagedInstallationsAsync(pageSize, continuationToken, cancellationToken);
        continuationToken = pagedInstallations.ContinuationToken;
        var installations = pagedInstallations.Data;
        var usersInstallation = pagedInstallations.Data.Where((i) => (i.ConversationReference.Conversation.TenantId == incidentHandlerRequest.TenantId && i.ConversationReference.User.AadObjectId == incidentHandlerRequest.AadUserId));
    
        foreach (var installation in usersInstallation)
        {
            var response = await installation.SendAdaptiveCard(body, cancellationToken);
        }
    } while (!string.IsNullOrEmpty(continuationToken));
    
    return Ok();
    }
  3. Send a valid adaptive card to that endpoint (e.g. via Postman)
  4. See empty adaptive card in the chat

Expected behavior The adaptivecard that was send to the endpoint should be send properly

Screenshots If applicable, add screenshots to help explain your problem. image

~VS Code Extension Information (please complete the following information):~ ~- OS: [e.g. iOS]~ ~ - Version [e.g. 22]~

~CLI Information (please complete the following information):~ ~ - OS: [e.g. iOS8.1]~ ~ - Version [e.g. 22]~

TeamsFx Info:

Additional context I worked arround the issue in a very weird way. I basically converted the object to something that Newtonsoft.Json can deserialize properly

var jsonSerializerOptions = new System.Text.Json.JsonSerializerOptions(System.Text.Json.JsonSerializerDefaults.Web);
serializedAdaptiveCard = System.Text.Json.JsonSerializer.Serialize(body, jsonSerializerOptions); // System text json can serialize generic objects properly

then i Deserialize the serializedAdaptiveCard with Newtonsoft JSON

var response = await installation.SendAdaptiveCard(JsonConvert.DeserializeObject(serializedAdaptiveCard), cancellationToken);

This is an unbelievably dirty workarround. I am not sure if Newtonsoft.Json can be configured to serialize object properly but swapping to System.Text.Json should fix te issue

This issue was initialy opened incorrectly in the botframework-sdk repo. There i was guided towarts this repo

swatDong commented 9 months ago

Have you tried directly sending card via turnContext.SendActivityAsync()?

TeamsFx SDK passed your input object to BotBuilder SDK without any serialize/deserialize. See (https://github.com/OfficeDev/TeamsFx/blob/main/packages/dotnet-sdk/src/TeamsFx/Conversation/TeamsBotInstallation.cs#L107-L117)

Also noticed that BotBuilder SDK is based on Newtonsoft.Json (https://github.com/microsoft/botbuilder-dotnet/blob/main/libraries/Microsoft.Bot.Schema/Attachment.cs)

MichaMican commented 9 months ago

Yes - Same issue unfortunately.

Indeed the SDK is based on Newtonsoft.Json and I think that is exactly the problem here.

swatDong commented 9 months ago

So one option is to make input object recognized by Newtonsoft.Json - your workaround already did that.

And another option may be to let ASP.NET Core to use Newtonsoft.Json (https://learn.microsoft.com/en-us/aspnet/core/web-api/advanced/formatting?view=aspnetcore-8.0#add-newtonsoftjson-based-json-format-support-2). But that's a global setting and may impact other APIs in your app.