Exercise 5: Calling the Microsoft Graph

Workshop content and sample code from the Global Microsoft 365 Developer Bootcamp in October 2020 focused on building an emergency response center with Teams and SharePoint


Exercise 5: Calling the Microsoft Graph

In this exercise, you’ll add code to the solution which posts a message in the Teams channel when a new map point is added. The completed solution is in the “Exercise 5” branch of this repo.

If you don’t have developer tools, grab the map-view.sppkg file from the Exercise 5 folder and skip to Step 3.

Step 1: Modify the code

a. Ensure you have downloaded or cloned this repository, and open the Solution/MapViewer folder in your code editor.

The src/webparts/mapViewer/services/GraphService folder contains the Microsoft Graph code for the solution. Open IGraphService.ts and add a function to the IGraphService interface:

sendToChannel(message: string): Promise<void | string>;

The completed file is here.

b. In the GraphService.ts file, import a reference to the Teams JavaScript SDK at the top of the file.

import * as microsoftTeams from "@microsoft/teams-js";

Then add a function to get the Teams context from the Teams JavaScript SDK.

private async getTeamsContext(): Promise<any> {

    return new Promise<any> ((resolve, reject) => {
        if (microsoftTeams) {
            microsoftTeams.getContext((context) => {
                resolve(context);
            });
        } else {
            reject ("Error: Teams context not found");
        }    
    });
}

Finally, add a function to send a message to the current Teams channel using the Microsoft Graph.

public async sendToChannel(message: string): Promise<void | string> {

    const teamsContext = await this.getTeamsContext();
    const teamId = teamsContext.groupId;
    const channelId = teamsContext.channelId;

    return new Promise<void | string>((resolve, reject) => {
        this.serviceProps.graphClient.api(
            `/teams/${teamId}/channels/${channelId}/messages`)
            .post({
                body: {
                    content: message,
                    contentType: "text"
                }
            }, ((err, res) => {
                if (!err) {
                    resolve();
                } else {
                    reject(err);
                }
            }));
    });
}

The completed file is here.

c. The src/webparts/mapViewer/services/MapDataService folder contains the code that reads and updates map points using the Graph service and the Bing Maps service. Modify MapDataService.ts to add a call to the new sendToChannel() function. Add it to the getMapPoints() function where you see the comment,

/* 
 * Add the call to sendToChannel() here 
 */

Here is the snippet of code to add:

await this.serviceProps.graphService.sendToChannel(
    `New point ${p.title} added at ${p.latitude}, ${p.longitude}`
);

The updated getMapPoints() function should look like this:

public async getMapPoints(geocode: boolean): Promise<ILocation[]> {

    const locationMapper = new LocationMapper();

    let listId: string;
    try {
        listId = await this.serviceProps.graphService.getListId(
            this.serviceProps.siteId, this.serviceProps.listName
        );    
    }
    catch (error) {
        if (error.statusCode === 404) {
            listId = await this.serviceProps.graphService.createList(
                this.serviceProps.siteId, this.serviceProps.listName,
                locationMapper
            );
        } else throw(error);
    }

    const points = await this.serviceProps.graphService.getListItems<ILocation>(
        this.serviceProps.siteId, listId, locationMapper
    );

    if (geocode) {
        for (let p of points) {
            if ((!p.latitude || !p.longitude) &&
                    (p.address || p.city || p.stateProvince || p.country)) {
                
                // If here, we're missing the geo-coordinates for an item and have
                // address or other info. Try to geocode it.
                let coordinates = await this.serviceProps.bingMapsService.geoCode(
                    p.country, p.stateProvince, p.city, p.address
                );

                if (typeof coordinates === 'object') {
                    // If here, the geocode was succesful - update the item
                    p.latitude = coordinates.latitude;
                    p.longitude = coordinates.longitude;
                    await this.serviceProps.graphService.updateListItem(
                        this.serviceProps.siteId, listId, locationMapper, p.id,
                        {
                            latitude: p.latitude,
                            longitude: p.longitude
                        }
                    );
                    await this.serviceProps.graphService.sendToChannel(
                        `New point ${p.title} added at ${p.latitude}, ${p.longitude}`
                    );
                }
            }
        }
    }

    return (points);
}

The completed file is here.

d. We’re going to need permission to use the Graph API to post in the Teams channel. To request this, edit the config/package-solution.json and add a new item in the webApiPermissionRequests array:

      {
        "resource": "Microsoft Graph",
        "scope": "ChannelMessage.Send"
      }

The completed file is here.

Step 2: Rebuild the SharePoint solution package

This assumes you already built the package as explained in Exercise 4 Step 1.

a. If you’re running locally, you can make simple code changes without re-deploying the SharePoint solution package, but in this case we added a permission request so it’s time to rebuild the package.

Return to the command line to rebuild the solution package. If you’re running locally,

gulp bundle
gulp package-solution

If you want to deploy the JavaScript bundle to the SharePoint CDN,

gulp bundle --ship
gulp package-solution --ship

Step 3: Re-deploy the SharePoint solution package and approve permissions

You need to repeat steps 2 and 3 of Exercise 4 to update your work in SharePoint.

a. Return to the SharePoint App catalog and upload the map-viewer.sppkg file again.

Part4

b. Return to the API Access screen; you should see the new permission . Select it 1️⃣ and use Approve 2️⃣.

Part4

Step 4: Test the change

a. If you’re running locally (you chose Option 1 in Exercise 4), ensure your local web server is running in the terminal window. If not, start it again.

gulp serve --nobrowser

a. Return to Microsoft Teams and refresh the Map View tab. Add a new point to the map; a notification should appear in the channel.

Part4

Congratulations, you’ve completed all 5 parts of the workshop! Please check out these resources and thanks for your interest in Microsoft 365 development!