Microsoft Teams Operations In SPFx Webpart Using Graph API

Introduction 

 
In this article, we will learn how to integrate with MS teams in SPFx using Graph API. The below three concepts will be explored here.
  • Getting Current Users Teams
  • Getting Channels of a particular team
  • Sending Messages to a particular channel
We will use the Teams Graph API endpoint to do these operations. Microsoft has provided many endpoints to access MS Teams data and do operations using Graph API endpoint. We can explore these in detail here.
 
Pre-requisites
  • SPFx web part created with React framework(you can also use No javascript framework or any other framework). For this sample, we are using a React-based webpart.
  • You have provided permission in SharePoint admin for accessing Graph API on behalf of your solution. So here, we are doing this before deployment as proactive steps, but you can also do this after deployment. You can refer to steps here about how to do this post-deployment. Basically you have to use API Access Page of SharePoint admin and add below permission for our use case. 
  • User.Read.All
  • User.ReadWrite.All
  • Group.Read.All
  • Group.ReadWrite.All
Here are the details of the solution which we are using here in our article:
  • Solution Name - react-teams-message
  • Web part Name - MyTeams
Step – Passing WebPart context to React components
 
We need a Sharepoint site context to work with, hence we will pass it from our web part file to React components. 
 
Open src\webparts\controls\components\IMyTeamsProps.ts 
 
Modify the code to what's shown below: 
  1. import { WebPartContext } from "@microsoft/sp-webpart-base";  
  2.   
  3. export interface IMyTeamsProps {  
  4.   context: WebPartContext;  
  5. }  
Open src\webparts\controls\MyTeamsWebPart.ts
 
Modify the render method to pass the context. 
  1. public render(): void {  
  2.    const element: React.ReactElement<IMyTeamsProps> = React.createElement(  
  3.      MyTeams,  
  4.      {  
  5.        context: this.context  
  6.      }  
  7.    );  
  8.   
  9.    ReactDom.render(element, this.domElement);  
  10.  }  
Please note we have just added line ‘context:this.context’.
 
Step – Create a Service Provider class and add the required methods.
 
Create a file at src\shared\services\ServiceProvider.ts
 
Add the below code:
  1. import { WebPartContext } from "@microsoft/sp-webpart-base";  
  2. import { MSGraphClient } from "@microsoft/sp-http";  
  3.   
  4. export class ServiceProvider {  
  5.   public _graphClient: MSGraphClient;  
  6.   private spcontext: WebPartContext;  
  7.   public constructor(spcontext: WebPartContext) {  
  8.     this.spcontext = spcontext;  
  9.   }  
  10.   
  11.   public getmyTeams = async (): Promise<[]> => {  
  12.     this._graphClient = await this.spcontext.msGraphClientFactory.getClient(); //TODO  
  13.   
  14.     let myTeams: [] = [];  
  15.     try {  
  16.       const teamsResponse = await this._graphClient.api('me/joinedTeams').version('v1.0').get();  
  17.       myTeams = teamsResponse.value as [];  
  18.     } catch (error) {  
  19.       console.log('Unable to get teams', error);  
  20.     }  
  21.     return myTeams;  
  22.   }  
  23.   
  24.   public getChannel = async (teamID): Promise<[]> => {  
  25.     this._graphClient = await this.spcontext.msGraphClientFactory.getClient(); //TODO  
  26.   
  27.     let myTeams: [] = [];  
  28.     try {  
  29.       const teamsResponse = await this._graphClient.api('teams/' + teamID + '/channels').version('v1.0').get();  
  30.       myTeams = teamsResponse.value as [];  
  31.     } catch (error) {  
  32.       console.log('unable to get channels', error);  
  33.     }  
  34.     return myTeams;  
  35.   }  
  36.   
  37.   public sendMessage = async (teamId, channelId, message): Promise<[]> => {  
  38.     this._graphClient = await this.spcontext.msGraphClientFactory.getClient();  
  39.     try {  
  40.       // https://graph.microsoft.com/beta/teams/{team-id}/channels/{channel-id}/messages  
  41.       var content = {  
  42.         "body": {  
  43.           "content": message  
  44.         }  
  45.       };  
  46.       const messageResponse = await this._graphClient.api('/teams/' + teamId + '/channels/' + channelId + "/messages/")  
  47.         .version("beta").post(content);  
  48.       return messageResponse;  
  49.   
  50.     } catch (error) {  
  51.       console.log('Unable to send message', error);  
  52.       return null;  
  53.     }  
  54.   
  55.   }  
  56. }  
You can see here that we have created 3 methods:
  • getmyTeams - To get teams of current logged in user
  • getChannel - To get channels of a particular team
  • sendMessage - To send messages to the team and a particular channel within that team. 
Please note that the last endpoint to sendMessage is still in beta version and not advisable for production use until it is generally available. 
 
Step – Modify React component
 
Below is what we will do on a high level:
  • Display a single button 'Get My Teams' on page load(component/webpart load)
  • On a click of the Get My Teams button, it calls the getmyTeams method of service provider class and sets it to the state object myteams
  • The render method will take care of looping through this myteams object creating radio buttons for every item in teams. 
  • On a click of any radio button, the Get Channels button will be displayed.
  • On a click of the Get Channels button, we will call getChannel method of service provider class and set it to state object teamChannels
  • The render method will take care of looping through this teamChannels object and creating a radio button for every item in object
  • On the click of any radio button of displayed channels, it will show an input text box and Send message button.
  • On a click on Send message button, we are using selected team and channel id and calling sendMessage of service provider to post message to particualar channel.
  • On success, it displays an alert box success message. 
Open src\webparts\controls\components\MyTeams.tsx
 
Modify the code to what's shown below:
  1. import * as React from 'react';  
  2. import styles from './MyTeams.module.scss';  
  3. import { IMyTeamsProps } from './IMyTeamsProps';  
  4. import { escape } from '@microsoft/sp-lodash-subset';  
  5. import { ServiceProvider } from '../../../shared/services/ServiceProvider';  
  6.   
  7. export interface IMyTeamsState {  
  8.   
  9.   myteams: any[];  
  10.   selectedTeam: any;  
  11.   teamChannels: any;  
  12.   selectedChannel: any;  
  13. }  
  14.   
  15. export default class MyTeams extends React.Component<IMyTeamsProps, IMyTeamsState> {  
  16.   private serviceProvider;  
  17.   private messageTextRef;  
  18.   
  19.   public constructor(props: IMyTeamsProps, state: IMyTeamsState) {  
  20.     super(props);  
  21.     this.serviceProvider = new ServiceProvider(this.props.context);  
  22.     this.state = {  
  23.       myteams: [],  
  24.       selectedTeam: null,  
  25.       selectedChannel: null,  
  26.       teamChannels: []  
  27.     };  
  28.   }  
  29.   
  30.   private GetmyTeams() {  
  31.   
  32.     this.serviceProvider.  
  33.       getmyTeams()  
  34.       .then(  
  35.         (result: any[]): void => {  
  36.           console.log(result);  
  37.           this.setState({ myteams: result });  
  38.         }  
  39.       )  
  40.       .catch(error => {  
  41.         console.log(error);  
  42.       });  
  43.   }  
  44.   
  45.   public render(): React.ReactElement<IMyTeamsProps> {  
  46.     return (  
  47.       <React.Fragment>  
  48.          <div className={ styles.myTeams }>  
  49.         <h1>Teams Operations Demo using Graph API</h1>  
  50.           
  51.         <div>  
  52.           <button className={styles.buttons} onClick={() => this.GetmyTeams()}>Get My Teams</button>  
  53.         </div>  
  54.         { this.state.myteams.length>0 &&  
  55.         <React.Fragment>  
  56.          <h3>Below is list of your teams</h3>  
  57.          <h4>Select any team and click on Get Channels</h4>  
  58.         </React.Fragment>  
  59.         }  
  60.         {this.state.myteams.map(  
  61.           (team: any, index: number) => (  
  62.             <React.Fragment>  
  63.               <input className={styles.radio} onClick={() => this.setState({ selectedTeam: team })} type="radio" id={team.id} name="myteams" value={team.id} />  
  64.               <label >{team.displayName}</label><br />  
  65.             </React.Fragment>  
  66.           )  
  67.         )  
  68.         }  
  69.   
  70.         {this.state.selectedTeam &&  
  71.         <React.Fragment>  
  72.             <br></br>  
  73.             <button  className={styles.buttons} onClick={() => this.getChannels()}>Get Channels</button>  
  74.           </React.Fragment>  
  75.         }  
  76.         { this.state.teamChannels.length>0 &&  
  77.         <React.Fragment>  
  78.         <h3>Below is list of your channels for selected team : {this.state.selectedTeam.displayName}</h3>  
  79.         <h4>Select any channel, enter message and click 'Send Message' to Post Message on MS teams</h4>  
  80.         </React.Fragment>  
  81.         }  
  82.   
  83.         {this.state.teamChannels.map(  
  84.           (channel: any, index: number) => (  
  85.             <React.Fragment>  
  86.               <input className={styles.radio} onClick={() => this.setState({ selectedChannel: channel })} type="radio" id={channel.id} name="teamchannels" value={channel.id} />  
  87.               <label >{channel.displayName}</label><br />  
  88.             </React.Fragment>  
  89.           )  
  90.         )  
  91.         }  
  92.   
  93.         {this.state.selectedChannel &&  
  94.           <React.Fragment>  
  95.           <br></br>  
  96.           <div>  
  97.             <input className={styles.textbox}  ref={(elm) => { this.messageTextRef = elm; }} type="text" id="message" name="message" />  
  98.             <br></br>  
  99.             <br></br>  
  100.             <button  className={styles.buttons} onClick={() => this.sendMesssage()}>Send Message</button>  
  101.               
  102.           </div>  
  103.           </React.Fragment>  
  104.         }  
  105.         </div>  
  106.       </React.Fragment>  
  107.     );  
  108.   }  
  109.   
  110.   private getChannels() {  
  111.   
  112.     this.serviceProvider.  
  113.       getChannel(this.state.selectedTeam.id)  
  114.       .then(  
  115.         (result: any[]): void => {  
  116.           console.log(result);  
  117.           this.setState({ teamChannels: result });  
  118.         }  
  119.       )  
  120.       .catch(error => {  
  121.         console.log(error);  
  122.       });  
  123.   }  
  124.   
  125.   private sendMesssage() {   
  126.     this.serviceProvider.  
  127.       sendMessage(this.state.selectedTeam.id, this.state.selectedChannel.id, this.messageTextRef.value)  
  128.       .then(  
  129.         (result: any[]): void => {  
  130.           alert("message posted sucessfully");  
  131.         }  
  132.       )  
  133.       .catch(error => {  
  134.         console.log(error);  
  135.       });  
  136.   }  
  137. }  
Step – Add styling
 
Open src\webparts\controls\components\MyTeams.module.scss
 
Modify the code to below, we have removed any classes that are not used. 
  1. @import '~office-ui-fabric-react/dist/sass/References.scss';  
  2.   
  3. .myTeams {  
  4.   label{  
  5.     font-size: 16px;  
  6.     line-height: 1.5;  
  7.   }  
  8.   .radio{  
  9.     width: 16px;  
  10.     height: 16px;  
  11.   }  
  12.   
  13.   .textbox{  
  14.     height: 30px;  
  15.     width: 300px;    
  16.   }  
  17.    
  18.   h4{  
  19.     font-style: italic;  
  20.     font-weight: normal;  
  21.   }  
  22.   
  23.   .buttons{  
  24.     background-color: $ms-color-themePrimary;  
  25.     width: 200px;  
  26.     height: 40px;  
  27.     color: white;  
  28.     font-size: 18px;  
  29.   
  30.    }  
  31. }  
Now we are done with our code changes. Let's see this web part in the action. Run gulp serve.
  1. gulp serve  
Open SharePoint workbench
  1. https://covid19.com/sites/stayhome/_layouts/15/workbench.aspx    
Add your target webpart, and on On-Page load, we will see the below output.
 
Click on Get My Teams, we will see below output:
Select any team, I selected the MyFirstTeam:
Click on Get Channels, we will see below output. In my case, I have 2 channels one general channel(which is created by default for every team) and DemoChannel1 which I have created manually.
Click on DemoChannel1, type in some message and click on Send Message.
If everything goes well, we will see the below message.
 
 
Now, go to teams. Below is what we see in my tenant. You can see team names and channel matches as it was displayed in the web part and as in teams.
 
 

Conclusion

 
This example describes how to call Teams Graph API endpoints to integrate your SPFx web part with MS Teams. Attached is the source code for this sample. You can download and run NPM install to try this out. Please remember to allow permissions in SharePoint Admin API Access. 
I hope this helps, happy coding!!!!