Skip to content
This repository has been archived by the owner on Dec 4, 2023. It is now read-only.

Conversations#replyToActivity() throw the JsonParseException #57

Closed
yoshioterada opened this issue Aug 31, 2018 · 2 comments
Closed

Conversations#replyToActivity() throw the JsonParseException #57

yoshioterada opened this issue Aug 31, 2018 · 2 comments
Assignees
Labels
customer-replied-to Indicates that the team has replied to the issue reported by the customer. Do not delete.

Comments

@yoshioterada
Copy link
Member

When I try to implement the Bot Application on Azure Function, I faced the problem as follows.

  • Conversations#replyToActivity() throw the JsonParseException.
111: ResourceResponse response = connector.conversations().replyToActivity(activity.conversation().id(), activity.id(), replyActivity);  

This problem was also happen on the method invocation of Conversations#sendToConversation().

Following is the StackTrace of the Exception.

[2018/08/31 9:05:37] java.lang.RuntimeException: com.fasterxml.jackson.core.JsonParseException: Unrecognized token 'The': was expecting ('true', 'false' or 'null')
[2018/08/31 9:05:37]  at [Source: (String)"The page cannot be displayed because an internal server error has occurred."; line: 1, column: 4]
[2018/08/31 9:05:37] 
[2018/08/31 9:05:37] 	at rx.exceptions.Exceptions.propagate(Exceptions.java:58)
[2018/08/31 9:05:37] 	at rx.observables.BlockingObservable.blockForSingle(BlockingObservable.java:464)
[2018/08/31 9:05:37] 	at rx.observables.BlockingObservable.single(BlockingObservable.java:341)
[2018/08/31 9:05:37] 	at com.microsoft.bot.connector.implementation.ConversationsImpl.replyToActivity(ConversationsImpl.java:633)
[2018/08/31 9:05:37] 	at com.yoshio3.Function.execOperation(Function.java:111)
[2018/08/31 9:05:37] 	at com.yoshio3.Function.lambda$HttpTriggerJava$0(Function.java:55)

Following is the sample code which run on Azure Function.

package com.yoshio3;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.microsoft.aad.adal4j.AuthenticationException;
import java.util.*;
import com.microsoft.azure.functions.annotation.*;
import com.microsoft.azure.functions.*;
import com.microsoft.bot.connector.Conversations;
import com.microsoft.bot.connector.customizations.ClaimsIdentity;
import com.microsoft.bot.connector.customizations.CredentialProvider;
import com.microsoft.bot.connector.customizations.CredentialProviderImpl;
import com.microsoft.bot.connector.customizations.JwtTokenValidation;
import com.microsoft.bot.connector.customizations.MicrosoftAppCredentials;
import com.microsoft.bot.connector.implementation.ConnectorClientImpl;
import com.microsoft.bot.schema.models.Activity;
import com.microsoft.bot.schema.models.ActivityTypes;
import com.microsoft.bot.schema.models.ResourceResponse;
import java.io.IOException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import org.joda.time.DateTimeZone;
import org.joda.time.LocalDateTime;

/**
 * Azure Functions with HTTP Trigger.
 */
public class Function {

    private final static String AUTHORIZATION_HEADER = "authorization";
    private static final String APP_ID = "";
    private static final String APP_PASSWORD = "";

    /**
     *
     * @param request
     * @param context
     * @return
     */
    @FunctionName("HttpTrigger-Java")
    public HttpResponseMessage HttpTriggerJava(
            @HttpTrigger(name = "req",
                    methods = {HttpMethod.POST},
                    authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage<Optional<String>> request,
            final ExecutionContext context) {
        context.getLogger().info("Java HTTP trigger processed a request.");

        ExecutorService exec = Executors.newSingleThreadExecutor();
        exec.submit(() -> {
            try {
                execOperation(request, context);
            } catch (IOException | ExecutionException | InterruptedException ex) {
                context.getLogger().log(Level.SEVERE, null, ex);
            } catch (Exception e) {
                context.getLogger().log(Level.SEVERE, null, e);
                e.printStackTrace();
            }
        });

        return request.createResponseBuilder(HttpStatus.ACCEPTED).build();
    }

    private void execOperation(HttpRequestMessage<Optional<String>> request, ExecutionContext context) throws JsonProcessingException, IOException, ExecutionException, InterruptedException {
        try {
            CredentialProvider credentialProvider = new CredentialProviderImpl(APP_ID, APP_PASSWORD);
            MicrosoftAppCredentials credentials = new MicrosoftAppCredentials(APP_ID, APP_PASSWORD);

            Optional<String> optionalBody = request.getBody();
            String httpBody = optionalBody.orElseGet(() -> "");
            System.out.println("------- This is the Message Body from BotFramework Start -------");
            System.out.println(httpBody);
            System.out.println("------- This is the Message Body from BotFramework End -------");

            String authHeaderValue = request.getHeaders().getOrDefault(AUTHORIZATION_HEADER, "");

            CompletableFuture<ClaimsIdentity> validateAuthHeader = JwtTokenValidation.validateAuthHeader(authHeaderValue, credentialProvider);
            validateAuthHeader.thenAccept(claimIdentity -> {
                if (claimIdentity.isAuthenticated()) {
                    System.out.println("AUTENTICATE SUCCESS");
                } else {
                    System.out.println("AUTENTICATE FAILED");
                }
            });

            Activity activity = getActivity(httpBody);
            System.out.println("------- This is Activity Object from BotFramework Start ------------");
            printActivity(activity);
            System.out.println("------- This is Activity Object from BotFramework End   ------------");

            if (activity.type().equals(ActivityTypes.MESSAGE)) {
                ConnectorClientImpl connector = new ConnectorClientImpl(credentials);
                Conversations conversations = connector.conversations();
                ConversationsResult convResult = conversations.getConversations();

                Activity replyActivity = new Activity()
                        .withType(ActivityTypes.MESSAGE)
                        .withTimestamp(new LocalDateTime().toDateTime(DateTimeZone.UTC))
                        .withConversation(activity.conversation())
                        .withRecipient(activity.from())
                        .withFrom(activity.recipient())
                        .withReplyToId(activity.id())
                        .withText("Echo " + activity.text());

                System.out.println("------- This is Activity Object for Return the Message Start -------");
                printActivity(replyActivity);
                System.out.println("------- This is Activity Object for Return the Message End   -------");
                ResourceResponse response = conversations.replyToActivity(activity.conversation().id(), activity.id(), replyActivity);
            }
        } catch (AuthenticationException ae) {
            throw ae;
        } catch (IOException ioe) {
            throw ioe;
        } catch (InterruptedException inte) {
            throw inte;
        } catch (ExecutionException ex) {
            throw ex;
        } catch (Exception e) {
            throw e;
        }
    }

    private Activity getActivity(String httpBody) throws IOException {
        ObjectMapper objectMapper = new ObjectMapper()
                .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
                .findAndRegisterModules();
        return objectMapper.readValue(httpBody, Activity.class);
    }

    private void printActivity(Activity activity) throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper()
                .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
                .findAndRegisterModules();

        System.out.println("------- Print Activity Start -------");
        System.out.println("type : " + activity.type());
        System.out.println("timestamp : " + activity.timestamp());
        System.out.println("from : " + activity.from());
        System.out.println("Conversation : " + activity.conversation());
        System.out.println("Recipient : " + activity.recipient());
        System.out.println("Text : " + activity.text());
        System.out.println("replyToId : " + activity.replyToId());

        if (activity.membersAdded() != null) {
            activity.membersAdded().stream().forEach(account -> {
                System.out.println("Members Account ID : " + account.id());
                System.out.println("Members Account Name : " + account.name());
            });
        }
        System.out.println("------- Print JSON Data ----------");
        String json = objectMapper.writeValueAsString(activity);
        System.out.println(json);
    }
}

Following is the JSON Data of Activity Object for return the messages.

$ jq . after.json 
{
  "type": "message",
  "id": null,
  "timestamp": 1535738736966,
  "localTimestamp": null,
  "serviceUrl": null,
  "channelId": null,
  "from": {
    "id": "Java-BotBuilder-Function@KLSKBveC4Gw",
    "name": "Java-BotBuilder-Function",
    "role": null
  },
  "conversation": {
    "group": null,
    "isGroup": null,
    "conversationType": null,
    "id": "eecdbc948df1409eb3d85fd52a2e0b0e",
    "name": null,
    "role": null
  },
  "recipient": {
    "id": "GqmZkaUErhj",
    "name": "You",
    "role": null
  },
  "textFormat": null,
  "attachmentLayout": null,
  "membersAdded": null,
  "membersRemoved": null,
  "reactionsAdded": null,
  "reactionsRemoved": null,
  "topicName": null,
  "historyDisclosed": null,
  "locale": null,
  "text": "Echo test",
  "speak": null,
  "inputHint": null,
  "summary": null,
  "suggestedActions": null,
  "attachments": null,
  "entities": null,
  "channelData": null,
  "action": null,
  "replyToId": "eecdbc948df1409eb3d85fd52a2e0b0e|0000000",
  "label": null,
  "valueType": null,
  "value": null,
  "name": null,
  "relatesTo": null,
  "code": null,
  "expiration": null,
  "importance": null,
  "deliveryMode": null,
  "textHighlights": null
}

From the above JSON data, there is no invalid value of "The".

[2018/08/31 9:05:37] java.lang.RuntimeException: com.fasterxml.jackson.core.JsonParseException: Unrecognized token 'The': was expecting ('true', 'false' or 'null')
[2018/08/31 9:05:37]  at [Source: (String)"The page cannot be displayed because an internal server error has occurred."; line: 1, column: 4]

Please refer to the detail log on log.txt ?

@daveta daveta self-assigned this Nov 1, 2018
@daveta
Copy link
Contributor

daveta commented Nov 6, 2018

Hi Yoshio,
The json in your post - i noticed the timestamp appears to be in the wrong format.

        _mapper = new ObjectMapper();
        _mapper.registerModule(new JavaTimeModule());
        SimpleModule simpleModule = new SimpleModule();
        simpleModule.addSerializer(OffsetDateTime.class, new JsonSerializer<OffsetDateTime>() {
            @Override
            public void serialize(OffsetDateTime offsetDateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
                jsonGenerator.writeString(DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(offsetDateTime));
            }
        });
        _mapper.registerModule(simpleModule);

I believe the format is supposed to be as a string in ISO-8601 format. Perhaps that's the issue.

https://github.com/Microsoft/botframework-obi/blob/master/botframework-activity/botframework-activity.md#timestamp

Thanks, -Dave

@tracyboehrer tracyboehrer added the customer-replied-to Indicates that the team has replied to the issue reported by the customer. Do not delete. label Aug 30, 2019
@tracyboehrer
Copy link
Member

Closed due to inactivity

tracyboehrer added a commit that referenced this issue Apr 6, 2020
Added Teams botbuilder changes and sample #57
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
customer-replied-to Indicates that the team has replied to the issue reported by the customer. Do not delete.
Projects
None yet
Development

No branches or pull requests

3 participants