SPFx Web Part. Получение информации о присутствии из Microsoft Teams
Microsoft Teams был выпущен в 2017 году и потребовалось около трех лет, чтобы мы получили API, позволяющий получить информацию о присутствии. Приходилось использовать обходные пути. Но сейчас у нас есть методы REST API для получения онлайн статуса пользователя в Microsoft Teams.
В этом посте я покажу как в веб-части SPFx получить информацию о присутствии пользователя из Microsoft Teams.
Создание новой веб-части
Откройте терминал и выполните команду для создания новой SPFx веб-части:
yo @microsoft/sharepoint
Пакеты
Для получение информации о присутствии необходимо предоставить идентификатор пользователя из AAD. Чтобы получить идентификатор AAD необходимо знать логин пользователя. И вот для получение логина мы будем использовать spfx-controls react. Выполните команду, чтобы добавить этот пакет в проект:
npm install @pnp/spfx-controls-react --save --save-exact
Это единственный дополнительный npm-пакет, который понадобится.
Когда Yeoman закончит свою работу и пакет будет добавлен в проект выполните code .
, чтобы запустить Visual Studio Code или любой другой редактор, который Вы предпочитаете.
Отмеченные файлы будут изменены.
Обзор решения
Решение не сильно красивое в угоду простоте кода.
UI
Веб-часть содержит три элемента: Контрол выбора пользователя для поиска пользователей в тенанте, Кнопка для инициализации процесса получения статуса и набор текстовых элементов для отображения информации о статусе из Microsoft Teams.
Процесс
Процесс веб-части состоит из трех шагов:
- Выбор пользователя с помощью контрола People Picker (People Picker возвращает логин пользователя)
- Передать логин пользователя в Microsoft Graph, чтобы получить AAD идентификатор пользователя
- Передать AAD идентификатор пользователя в Microsoft Graph, чтобы получить статус присутствия
Код
Код веб-части содержит только минимально необходимые модификации для демонстрации новых возможностей.
Модель данных
Прежде всего нам надо определить объект статуса присутствия. Создайте папку модель model в папке src, затем создайте файл IPresence.ts со следующим содержимым:
export interface IPresence {
Availability: string;
Activity: string;
}
Далее необходимо расширить объекты свойства и состояние компонента веб-части для хранения информации о присутствии и контекста веб-части (логин пользователя, ID пользователя, статус присутствия). Откройте файл src\components\IUserPresenceProps.ts и замените его содержимое на следующее:
import { WebPartContext } from "@microsoft/sp-webpart-base";
import { IPresence } from "../../../model/IPresence";
/**
* Свойства компонента
*/
export interface IUserPresenceProps {
context: WebPartContext;
}
/**
* Состояние компонента
*/
export interface IUserPresenceState{
userUPN?: string;
userId?: string;
presence?: IPresence;
}
Клиент Microsoft Graph
Веб-часть использует Microsoft Graph как источник данных о пользователях и информации о присутствия. Создайте папку services в папке src и создайте в ней файл GraphService.ts:
import { WebPartContext } from "@microsoft/sp-webpart-base";
import { MSGraphClient } from "@microsoft/sp-http";
import { IPresence } from '../model/IPresence';
export default class GraphService {
private context: WebPartContext;
constructor(private _context: WebPartContext) {
this.context = _context;
}
/**
* Получение информации о присутствии
* @param userId Идентификатор пользователя в AAD
*/
public getPresence(userId: string): Promise<IPresence> {
return new Promise<IPresence>((resolve, reject) => {
this.context.msGraphClientFactory
.getClient() // Инициализация клиента Microsoft Graph
.then((client: MSGraphClient): Promise<IPresence> => {
return client
.api(`users/${userId}/presence`) //Получение статуса
.version("beta") // Бета-версия
.get((err, res) => {
if (err) {
reject(err);
return;
}
// Разрешаем статус
resolve({
Availability: res.availability,
Activity: res.activity,
});
});
});
});
}
public getUserId(userUPN: string): Promise<string> {
return new Promise<string>((resolve, reject) => {
this.context.msGraphClientFactory
.getClient() // Инициализация клиента Microsoft Graph
.then((client: MSGraphClient): Promise<IPresence> => {
return client
.api(`users/${userUPN}`) //Получение статуса
.version("beta") // Бета-версия
.select("id") // Получаем только ID пользователя
.get((err, res) => {
if (err) {
reject(err);
return;
}
// Разрешаем ИД пользователя
resolve(res.id);
});
});
});
}
}
В клиенте определены два метода:
- getUserId принимает логин пользователя (user@domain.com) и возвращает идентификатор пользователя в Azure AD
- getPresence принимает идентификатор пользователя и возвращает статус присутствия
Компонент
Откройте файл src\webparts\userPresence\userPresence.tsx и замените его содержимое следующим:
import * as React from 'react';
import styles from './UserPresence.module.scss';
import { IUserPresenceProps, IUserPresenceState } from './IUserPresenceProps';
import { PeoplePicker, PrincipalType } from "@pnp/spfx-controls-react/lib/PeoplePicker";
import { Button } from 'office-ui-fabric-react';
import GraphService from '../../../services/GraphService';
export default class UserPresence extends React.Component<IUserPresenceProps, IUserPresenceState> {
constructor(props: IUserPresenceProps) {
super(props);
this.state = {};
// Связывание обработчиков событий
this._buttonClick = this._buttonClick.bind(this);
this._getPeoplePickerItems = this._getPeoplePickerItems.bind(this);
// Инициализация клиента Microsoft Graph
this._graphService = new GraphService(this.props.context);
}
private _graphService: GraphService
public render(): React.ReactElement<IUserPresenceProps> {
return (
<div className={styles.userPresence}>
{/* People Picker */}
<div className={styles.container}>
<PeoplePicker context={this.props.context} titleText="Step 1. Find user"
selectedItems={this._getPeoplePickerItems} principalTypes={[PrincipalType.User]} />
</div>
{/* / People Picker */}
{/* Кнопка */}
<div className={styles.container}>
<Button text="Step 2. Get presence" className={styles.button}
disabled={!this.state.userUPN} onClick={this._buttonClick} />
</div>
{/* / Кнопка */}
{/* Состояние */}
<div className={styles.container}>
<label>useUPN:</label> {this.state.userUPN}<br />
<label>userId:</label> {this.state.userId}<br />
<label>Presence.Activity:</label> {this.state.presence ? this.state.presence.Activity : ""}<br />
<label>Presence.Availability:</label> {this.state.presence ? this.state.presence.Availability : ""}
</div>
{/* / Состояние */}
</div>
);
}
private _buttonClick() {
this._graphService.getUserId(this.state.userUPN)
.then(userId => {
// Обновляем Id пользователя
this.setState({
userId: userId
});
this._graphService.getPresence(this.state.userId)
.then(presence => {
// Обновляем статус присутствия
this.setState({ presence: presence });
});
});
}
private _getPeoplePickerItems(items: any[]): void {
if (items.length != 1) {
return;
}
// Обновляем логин
this.setState({
userUPN: items[0].secondaryText
});
}
}
Изменения в компоненте:
- Добавлен объект состояния
- Добавлен конструктор, принимающий WebPartContext вместо стандартного свойства description
- Изменен интерфейс для отображения элементов (пикер, кнопка, информация)
- Добавлены два обработчика: для пикера и для кнопки.
Веб-часть
Последний файл, который необходимо изменить - это src\webparts\userPresence\UserPresenceWebPart.ts. Откройте его и обновите его содержимое:
import * as React from 'react';
import * as ReactDom from 'react-dom';
import { Version } from '@microsoft/sp-core-library';
import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base';
import UserPresence from './components/UserPresence';
import { IUserPresenceProps } from './components/IUserPresenceProps';
export default class UserPresenceWebPart extends BaseClientSideWebPart <{}> {
public render(): void {
const element: React.ReactElement<IUserPresenceProps> = React.createElement(
UserPresence,
{
context: this.context // WebPartContext для инициализации клиента Microsoft Graph
}
);
ReactDom.render(element, this.domElement);
}
protected onDispose(): void {
ReactDom.unmountComponentAtNode(this.domElement);
}
protected get dataVersion(): Version {
return Version.parse('1.0');
}
}
Итоги
Что было сделано:
- Добавлен пакет spfx-controls-react для использования контрола People Picker
- Создан интерфейс IPresence для определения статуса присутствия
- Создан IUserPresenceState для хранения состояния веб-части (выбранный пользователь, id пользователя, статус присутствия)
- Изменен IUserPresenceProps для передачи объекта WebPartContext из веб-части в дочерний компонент
- Создан GraphService для работы с Microsoft Graph
- Изменен компонент UserPresence для отображения элементов (пикер, кнопка, информация)
- Изменен UserPresenceWebPart для передачи контекста в компонент UserPresence
Просмотр веб-части
Выполните комнады в терминале для запуска веб-части:
npm run --nobrowser
откройте SharePoint Online workbench:
https://[tenant].sharepoint.com/_layouts/15/workbench.aspx
и добавьте веб-часть на страницу
Права
Для получение статуса пользователя в Microsoft Teams с помощью Microsoft Graph необходимо предоставить права Presence.Read (минимальные привилегии) или Presence.Read.All (максимальные привилегии) для веб-части.
Исходные коды
Исходные коды опубликованы на GitHub: https://github.com/vzhukov/spfx-teams-presence.