Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Building Java Messaging Extensions and Connectors for Microsoft Teams Part 3: Building Webhooks and Notification Connectors

0.00/5 (No votes)
1 Feb 2022 1  
How to build webhooks and notification connectors
This article demonstrates how to create a Spring Boot web app in Java that replicates the functionality of the Sample #1 under the “Webhooks and connectors” heading on GitHub.

So far in this three-part series, we’ve built a messaging extension for search in Microsoft Teams, then made a link unfurling message extension. In this third and final article, we’ll create webhooks and notification connectors.

Exploring Webhooks

We relied on the Microsoft Bot Framework in all previous message extensions we developed for Microsoft Teams. More specifically, our web services used Bot infrastructure to establish secure communication. Sometimes, especially in legacy systems, you might not be able to use the Bot Framework SDK. In such cases, you can use webhooks and connectors.

Webhooks are custom HTTP callbacks. In the Microsoft Teams channel, you use them to send a notification to your web service (an outgoing webhook) through mentions — for example, @ToDoService. Or, you use webhooks to push notifications to the channel (an incoming webhook).

Connectors are similar to incoming webhooks but work in the publisher-subscriber model. Connectors enable users to subscribe to notifications that external web services publish.

In practice, we provide the URL to the REST API resources for outgoing webhooks. Microsoft Teams sends the POST request under this endpoint whenever someone mentions the service. Our app sends POST requests to the HTTP endpoint for incoming webhooks and connectors.

During this communication, we use the JSON-formatted strings. The requests conform to the Activity interface. Our service receives this when we use an outgoing webhook. We also send the JSON string conforming to the Activity interface to send the response (for an outgoing webhook) or post messages to Microsoft Team channels (incoming webhook and connectors). However, we also attach the Adaptive Card. The latter is the JSON-formatted string, representing the user interface (UI) component, rendering the Microsoft Teams conversation.

In this tutorial, I explain how to use webhooks and connectors. We will implement the ToDoController using Java Spring Boot. We’ll configure the controller for the outgoing webhook named ToDoService. So, we’ll be able to send requests to this service by mentioning @ToDoService.

Additionally, the message text will pass the ToDo item’s identifier. Specifically, after posting this message in Microsoft Teams:

Java
@ToDoService Id=2

The ToDoController will extract “2” as the ToDo identifier. Then, ToDoController will send the request to the JSON Placeholder todos resource: https://jsonplaceholder.typicode.com/todos/2.

The app will send the data from this API back to Microsoft Teams in the form of an Adaptive Card like below:

You can find the project’s code on GitHub.

Using the ToDoController

We start our project by creating the Java Spring Boot app. Here, I use the Spring initializer with Spring Boot selected. Then, I add the com.microsoft.bot dependency to my pom.xml to access an Activity interface:

XML
<dependency>
    <groupId>com.microsoft.bot</groupId>
    <artifactId>bot-integration-spring</artifactId>
    <version>4.13.0</version>
    <scope>compile</scope>
</dependency>

Note that this step is not necessary. You just need to provide the corresponding plain old Java object (POJO) class.

Afterward, we supplement the project with ToDo.java, representing the ToDo objects from the JSON placeholder REST API’s todos.

Then, we start implementing the ToDoController:

Java
@RestController
public class ToDoController {
    private final RestTemplate restTemplate;
    public ToDoController(RestTemplateBuilder restTemplateBuilder) {
        this.restTemplate = restTemplateBuilder.build();
    } 
}

We use the RestTemplate to automatically convert ToDo JSON strings from the JSON placeholder to instances of the ToDo Java classes:

Java
private ToDo GetToDoById(Activity activity) {
    // Get id from activity's text
    String todoId = GetToDoIdFromActivityText(activity);
    // Send request
    String url = "https://jsonplaceholder.typicode.com/todos/" + todoId;
    // Get todo
    return this.restTemplate.getForObject(url, ToDo.class, todoId);
}

The above method first extracts the ToDo item’s ID from Microsoft Teams. Then, it uses the ID to construct the request and send it to the JSON placeholder. Finally, the code wraps the result into a ToDo Java class instance.

Use the following helper method to extract the ToDo ID from the Activity:

Java
private String GetToDoIdFromActivityText(Activity activity) {
    // Split text at 'Id='
    var splittedText = activity.getText().split("Id=");
    // Return the id value (assuming the input string has the correct format)
    return splittedText[splittedText.length-1];
}

The Activity class has the getText method in the code above, enabling us to retrieve the message the user posted in Microsoft Teams. For example, it will represent @ToDoService Id=1 as <at>ToDoService</at> Id=1.

To extract the ID, we split the input text using the “Id=” pattern. Assuming all messages have the same format, we get the last element of the split string collection and return it as the ToDo ID.

Once we have the ToDo Java object, we need to send it back to Microsoft Teams:

Java
@PostMapping("/todos/")
public Activity sendToDo(@RequestBody Activity activity) throws URISyntaxException, 
                         IOException {
    // Get ToDo
    ToDo todo = GetToDoById(activity);
    // Create adaptive card
    Attachment cardAttachment = createAdaptiveCardAttachment("card.json", todo);
    // Send response
    Activity responseActivity = Activity.createMessageActivity();
    responseActivity.setAttachment(cardAttachment);
    return responseActivity;
}

As the code above shows, we need to wrap the ToDo into the Adaptive Card. Then, we attach this card to the Activity class instance and send the resulting response. We create the Adaptive Card in the next section.

Editing the Adaptive Card

An Adaptive Card is a JSON string conforming to this schema. For C#, you can use dedicated classes that accelerate the creation of Adaptive Cards. In Java, you can use the Card editor from App Studio. It provides the JSON editor with the sample Adaptive Card code and the preview, where you can see the changes in real-time.

Using the Card editor, create the following Adaptive Card:

Java
{
  "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
  "type": "AdaptiveCard",
  "version": "1.0",
  "body": [
    {
      "type": "Container",
      "items": [
        {
          "type": "TextBlock",
          "text": "Webhooks and Connectors",
          "weight": "bolder",
          "size": "medium"
        },
        {
          "type": "ColumnSet",
          "columns": [           
            {
              "type": "Column",
              "width": "stretch",
              "items": [
                {
                  "type": "TextBlock",
                  "text": "ToDo Description",
                  "weight": "bolder",
                  "wrap": true
                }               
              ]
            }
          ]
        }
      ]
    },
    {
      "type": "Container",
      "items": [       
        {
          "type": "FactSet",
          "facts": [
            {
              "title": "UserID:",
              "value": "<USER_ID>"
            },
            {
              "title": "Id:",
              "value": "<ID>"
            },
            {
              "title": "Title:",
              "value": "<TITLE>"
            },
            {
              "title": "Completed:",
              "value": "<COMPLETED>"
            }
          ]
        }
      ]
    }
  ],
  "actions": [
    {
      "type": "Action.ShowCard",
      "title": "Set due date",
      "card": {
        "type": "AdaptiveCard",
        "version": "1.0",
        "body": [
          {
            "type": "Input.Date",
            "id": "dueDate"
          }
        ],
        "actions": [
          {
            "type": "Action.Submit",
            "title": "OK"
          }
        ]
      }
    },
    {
      "type": "Action.ShowCard",
      "title": "Comment",
      "card": {
        "type": "AdaptiveCard",
        "version": "1.0",
        "body": [
          {
            "type": "Input.Text",
            "id": "comment",
            "isMultiline": true,
            "placeholder": "Enter your comment"
          }
        ],
        "actions": [
          {
            "type": "Action.Submit",
            "title": "OK"
          }
        ]
      }
    }
  ]
}

The card will display UserID, ID, Title, and Completed properties of the ToDo item retrieved from the JSON Placeholder. To dynamically configure those values, we use <USER_ID>, <ID>, <TITLE>, and <COMPLETED> placeholders in our JSON. Then, we save the above JSON in the resources/card.json file of our Java Spring Boot project.

To read the JSON during runtime, we implement createAdaptiveCardAttachment within ToDoController:

Java
private Attachment createAdaptiveCardAttachment(String filePath, ToDo todo)
    throws URISyntaxException, IOException {       
    try {
        // Read JSON
        InputStream inputStream =
            this.getClass().getClassLoader().getResourceAsStream(filePath);
        String adaptiveCardJson = IOUtils.toString(inputStream,
            StandardCharsets.UTF_8);
        // Replace placeholders with the actual values
        adaptiveCardJson = StringUtils.replace(adaptiveCardJson,
            "<USER_ID>", String.valueOf(todo.getUserId()));
        adaptiveCardJson = StringUtils.replace(adaptiveCardJson,
            "<ID>", String.valueOf(todo.getId()));
        adaptiveCardJson = StringUtils.replace(adaptiveCardJson,
            "<TITLE>", todo.getTitle());
        adaptiveCardJson = StringUtils.replace(adaptiveCardJson,
            "<COMPLETED>", String.valueOf(todo.getCompleted()));
        // Create attachment
        Attachment attachment = new Attachment();
        attachment.setContentType("application/vnd.microsoft.card.adaptive");
        attachment.setContent(Serialization.jsonToTree(adaptiveCardJson));
        return attachment;
    }
    catch(Exception e) {
        e.printStackTrace();
        return new Attachment();
    }
}

The above method reads card.json, then replaces the placeholders <USER_ID>, <ID>, <TITLE>, and <COMPLETED> with the actual values from the ToDo object. Finally, the JSON creates an instance of the Attachment class. We use this class in the sendToDo method we described earlier.

Configuring Settings

Let’s now test the above solution. First, start the ngrok to forward traffic on port 8080:

ngrok http -host-header=rewrite 8080

Read the forwarding URL that the ngrok generates. You’ll need it later to configure the webhook.

Then, build (mvn clean install) and start the web service (java -jar target/todos-0.0.1-SNAPSHOT.jar). Next, open Microsoft Teams. Click on the Teams tab and Create team. You can create the team from scratch or use a template. Here, I use the Manage a Project template:

Make the team public and set its name to My Team:

Finally, click Create. When the team is ready, click the triple dots, and choose Manage team from the context menu.

In Manage team, go to the Apps tab. Then, click Create an outgoing webhook (in the bottom right corner):

In the Create an outgoing webhook page, fill in the following fields:

  • Name: ToDoService. You’ll use this name later to mention your web service.
  • Callback URL: The forwarding URL from ngrok supplemented by /todos (for example, https://14a9-87-206-227-230.ngrok.io/todos)
  • Description: Type anything

After you click Create, Teams will provide the security token:

Now, you can go to the General channel and start a conversation. Then, type @ToDoService ToDo=1. This command sends a request to your web service. The web service then determines the ToDo identifier and configures the Adaptive Card JSON accordingly.

You should see results like the following image:

Using Incoming Webhooks and Connectors

To use the incoming webhooks and connectors, proceed similarly. Register the incoming webhook as Microsoft describes. Similarly, use the Teams manifest to create the connector:

After registering the incoming webhook or connector, you get the URL where you’ll post data in the form of messages or Adaptive Cards, as Microsoft describes.

Next Steps

In this tutorial, we learned how to work with webhooks and connectors. Along the way, we also learned how to use Adaptive Cards. You can build rich and interactive UI components for Microsoft Teams apps with those cards. You can also overcome the need to use Bot Frameworks for communication with webhooks.

Now that you know how to build messaging extensions for search, link unfurling messaging extensions, and webhooks and notification connectors, you’re well-equipped to create a Java app for Microsoft Teams. Help boost your or your coworkers’ productivity with handy apps everyone can access and use where they are already collaborating daily. Get started building a Microsoft Teams app for yourself or your organization today.

To learn how you can build, migrate and scale Java applications on Azure using Azure services, check out Get started with Java on Azure.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here