https://habrahabr.ru/company/microsoft/blog/327518/- Системы обмена сообщениями
- JavaScript
- Блог компании Microsoft
Маленький интересный рассказ о том, как ребята из Wrike интегировали Microsoft Teams в свой сервис и к чему это привело, а также большая техническая часть про сам процесс интеграции. Кстати, они одни из первых в мире сделали это в день релиза чата.
1+1=3
По опыту нашему опыту во
Wrike, потенциальные покупатели, выбирая систему управления проектами, всё чаще интересуются доступными интеграциями. Фактически, из приятного бонуса адаптация продукта к имеющейся экосистеме превратилась в фактор, который определяет выбор клиента.
Конечно, разработчику важно ориентироваться на наиболее распространённые у корпоративных пользователей инструменты, и здесь продукты Microsoft в неизбежном приоритете. Однако ещё важнее не делать интеграцию ради интеграции. 1+1 должно давать 3, в том смысле, что связка решений должна создавать дополнительную ценность для клиента.
Поэтому мы последовательно добавляли в наш сервис интеграцию с Azure Active Directory, OneDrive и Office 365. Конечная цель этих шагов — создать бесшовное рабочее пространство, где пользователь не будет каждые несколько минут переключаться между приложениями и копипастить информацию из одного окна в другое, а между личными инструментами и сервисом для командной работы не будет лежать пропасть.
MS Teams стал логичным новым шагом. 53% компаний, в которых работает 500+ сотрудников, используют корпоративные мессенджеры (данные
SpiceWorks). Мы подумали, что пользователям, обсуждающим работу, было бы удобно сразу, воплощать в жизнь результаты обсуждения. Поэтому вместе с Microsoft команда отобрала наиболее подходящие в такой ситуации функции сервиса и продумала их органичную реализацию в интерфейсе Teams.
В результате пользователи Wrike теперь могут создавать проекты и задачи прямо из мессенджера, а любой проект теперь можно добавить в Teams как новую вкладку. Также интеграция поддерживает внесение изменений в задачу (статус, исполнитель, срок, комментарии) параллельно с обсуждением.
Обсуждения проекта стали более предметными и точными, за счёт возможности показать внутри мессенджера диаграмму Ганта со сроками, таким образом она находится перед глазами участников обсуждения.
Ну и конечно, была добавлена очевидная возможность — уведомления по своим задачам теперь можно получать не только по email, но и сообщениями в Teams.
Разработка этой функциональности заняла 2 месяца. За это время команда приобрела интересный опыт, который может быть полезен для вас.
О технической стороне
Любое приложение-вкладка (
tab
) для Microsoft Teams состоит из:
- архива с манифестом, в котором должен находиться файл manifest.json и файлы ресурсов;
- внешнего HTTP-сервиса, обрабатывающего запросы на страницы: конфигурации, основной страницы таба, удаления.
Все страницы рендерятся внутри iframe в Microsoft Teams. Примеры манифеста для приложения-вкладки
описаны в MSDN. Там же можно найти
схему манифеста. На момент написания статьи, актуальной версией схемы была 0.4.
Основные поля манифеста:
Id
— GUID, должен быть уникальным для разных приложений или разных окружений.
Icons
— иконки разрешением 44x44 и 88x88, каждая не более 1,5 Kb.
configUrl
— URL, который пользователи увидят в первую очередь. С него начнется конфигурация приложения.
canUpdateConfig
— признак того, смогут ли пользователи менять конфигурацию уже добавленного приложения.
needsIdentity
— позволяет получать информацию о пользователе через Teams JS API.
validDomains
— массив доверенных доменов, в котором должен быть наш домен. Допускаются wildcards.
После подготовки манифеста и ресурсов, полученный архив можно
заливать в Microsoft Teams.
Взаимодействие с инфраструктурой Microsoft Teams обеспечивается б
иблиотекой Microsoft Teams JS API. Библиотека позволяет:
- запустить flow авторизации через Azure Oauth2 или любой другой Oauth2-провайдер;
- получить информацию о текущем пользователе, идентификаторы канала, команды и группы в терминах Azure Active Directory;
- получать и сохранять настройки вкладки;
- устанавливать обработчики на события: сохранение настроек, смена темы оформления, удаление вкладки;
- формировать и копировать в буфер обмена ссылку на объект, выбранный во вкладке.
Для использования библиотеки необходимо на каждой странице приложения подключить её и инициализировать:
<script src="https://statics.teams.microsoft.com/sdk/v0.4/js/MicrosoftTeams.min.js"></script>
...
<script type="text/javascript">
microsoftTeams.initialize();
</script>
Рассмотрим пример приложения для MsTeams с многостраничной конфигурацией и связыванием аккаунтов Azure AD и
Wrike.
Специфика работы приложения в нескольких iframe (конфигурация, вкладка) и с Oauth2-редиректами требует усложненный, но типизированный flow как для конфигурации, так и для показа приложения в ранее добавленной вкладке.
Основные процессы:
- Пользователь добавляет новую вкладку в Microsoft Teams.
- Неавторизованный юзер заходит на добавленную ранее вкладку.
- Авторизованный пользователь заходит на добавленную ранее вкладку.
Процесс добавления вкладки представлен на рисунке ниже. Голубым цветом обозначены страницы Microsoft Teams, зелёным — страницы нашего приложения, белым — код и действия по обработки запросов.
Страница «Welcome page» показывает основную информацию о нашем приложении и кнопку «Подключиться», которая стартует флоу авторизации в Azure AD.
Обработчик Callback URL должен:
- Запросить информацию о пользователе в Azure AD (например, через Microsoft Azure AD Graph API).
- Решить, что делать с полученной информацией: создать новый аккаунт в нашей системе или использовать существующий.
- «Залогинить» пользователя в нашу систему, например, выдав сессионную куку.
- В случае успешной авторизации, страница Callback URL должна просто закрыться, так как авторизация стартует в новом окне, а Callback является точкой окончания авторизации.
- В случае неудачной авторизации, страница Callback URL может показать пользователю причину неудачи, например, отказ пользователя предоставить доступ, отсутствие доступа к Azure-приложению и так далее.
Страница «Setup page» — это страница настроек. Она запрашивается при добавлении таба и через опцию «Конфигурация» в меню добавленного ранее таба (если в манифесте был выставлен флаг
canUpdateConfig
).
Пример кода для запуска Oauth2-авторизации через multi-tenant приложение в Azure AD (то есть пример кода Welcome page) выглядит примерно так:
<script src="https://statics.teams.microsoft.com/sdk/v0.4/js/MicrosoftTeams.min.js"></script>
<input type="button" onclick="startAuth()" value="Подключиться..."/>
<script type="text/javascript">
microsoftTeams.initialize();
var startAuth = function() {
microsoftTeams.authentication.authenticate({
url: 'https://login.windows.net/common/oauth2/authorize' +
'?response_type=code%20id_token' +
'&scope=openid' +
'&response_mode=' + form_post +
'&client_id=' + <YOUR_AZURE_APP_CLIENT_ID> +
'&resource=https%3a%2f%2fgraph.windows.net' +
'&nonce=' + <MS_NONCE> +
'&state=' + <OAUTH2_STATE> +
'&redirect_uri=' + encodeURIComponent('http://localhost/callback'),
width: 700,
height: 500,
successCallback: onAuthSuccess,
failureCallback: onAuthFailure
});
};
var onAuthSuccess = function() {
console.log('Auth success');
document.location.href='http://localhost/setup';
};
var onAuthFailure = function() {
console.log('Auth failed');
microsoftTeams.settings.setValidityState(false);
};
</script>
Параметры:
redirect_uri
— URL callback-обработчика, в GET-запросе которого будет передан idToken. По нему можно будет получить access token, refresh token и информацию по пользователю для связи аккаунта из Azure AD с аккаунтом в нашей системе.
YOUR_AZURE_APP_CLIENT_ID
— идентификатор приложения.
MS_NONCE
— случайно сгенерированный nonce, аналог state для Azure Oauth2.
OAUTH2_STATE
— Oauth2 state, который необходимо проверить в callback-обработчике.
Запуск flow-авторизации обеспечивается методом
microsoftTeams.authenticate
из Teams JS API. Не требуется явно вызывать
window.open
. Обработчики
onAuthSuccess
,
onAuthFailure
вызываются в контексте исходной страницы, откуда было открыто окно авторизации.
Более безопасный и надёжный в плане таймаутов способ начать
Oauth2 flow
предполагает запрос на наш обработчик, который будет генерировать
OAUTH2_STATE
и редиректить пользователя на
login.windows.net.
Подробную информацию по созданию и настройке приложений для Azure AD можно почитать
здесь.
Пример кода страницы «Setup page» для сохранения настроек приведен ниже.
<script src="https://statics.teams.microsoft.com/sdk/v0.4/js/MicrosoftTeams.min.js"></script>
<input id="name" type="text" placeholder="Enter your name" onkeyup="validate()"/>
<script type="text/javascript">
microsoftTeams.initialize();
var isNameValid = function() {
return jQuery('#name').val().trim().length > 0;
}
var validate = function() {
//Включить или выключить кнопку Save
microsoftTeams.settings.setValidityState(isNameValid());
};
microsoftTeams.settings.registerOnSaveHandler(function (saveEvent) {
if (!isNameValid()) {
saveEvent.notifyFailure("We couldn't save your tab settings. Please enter your name.");
return;
}
microsoftTeams.settings.setSettings({
contentUrl: 'http://localhost/tabContent',
suggestedDisplayName: "Your Tab Name",
websiteUrl: "http://localhost"
});
saveEvent.notifySuccess();
});
</script>
Параметры:
contentUrl
— адрес страницы, которая будет показана при открытии вкладки;
suggestedDisplayName
— название вкладки;
websiteUrl
— ссылка открывается из меню Microsoft Teams.
В приведенном коде используются следующие методы Teams JS API:
microsoftTeams.settings.setValidityState
— разрешение или запрет кнопки «Save»;
microsoftTeams.settings.registerOnSaveHandler
— обработчик события сохранения настроек;
saveEvent.notifyFailure
, notifySuccess
— сообщает инфраструктуре Teams об успехе или неудаче при сохранении настроек;
microsoftTeams.settings.setSettings
— выполняет сохранение настроек вкладки.
Рассмотрим процесс входа пользователя на ранее добавленную вкладку. Здесь возможны следующие варианты:
- Пользователь новый, не связанный с нашей системой.
- Пользователь, известный нашей системе, но не авторизованный.
- Авторизованный пользователь.
Авторизация у нас «бесшовная», поэтому нового пользователя просто добавим в аккаунт в нашей системе после авторизации в Azure AD. Это процесс входа на ранее добавленную вкладку.
Напомним, что организационная структура внутри Microsoft Teams следующая:
- в аккаунте Teams может быть много команд (teams);
- в команде может быть много каналов (channels);
- в канале может быть много наших вкладок.
Настройки вкладки и информация о пользователе доступны через
context
и
settings
(из Microsoft Teams JS API):
Сложности и решения
Какие трудности могут возникнуть при такой структуре?
Команда Wrike приняла решение, что авторизацию пользователи будут проходить в Azure AD. Данные о пользователе:
upn
(user principal name или email),
teamId
,
channelId
доступны через JS API, но эти данные нельзя провалидировать на стороне на стороне бэкенда — только если приложение в Azure AD будет запрашивать права, требующие админского разрешения. Это связано с особенностью Azure Graph API, согласно которой информация о структуре организация доступна только после согласия админа. Такой подход неудобен тем, что добавить вкладку в канал может только админ Azure AD.
Мы не знаем, какую именно вкладку открывает пользователь, если их несколько в канале (у нас нет
tabId
, хотя мы можем его генерировать, но об этом ниже). Хранение кастомных настроек доступно только в
settings.customSetting
s, но доступа к
settings
нет со страниц таба, только со страниц конфигурации.
Как решить эти проблемы? Сбор данных о
teamId
,
channelId
,
tabId
можно организовать при авторизации пользователя и хранить на стороне нашего приложения. Запросы от пользователя внутри вкладки можно валидировать на основе данных из текущего контекста (
microsoftTeams.getContext
).
Идентификатор таба можно сгенерировать при добавлении, на странице конфигурации, например, так:
microsoftTeams.settings.registerOnSaveHandler(function (saveEvent) {
microsoftTeams.settings.getSettings(function(settings) {
var tabId = generateOrGetTabId(settings);
var customSettings = {
tabId: tabId
};
microsoftTeams.settings.setSettings({
contentUrl: 'http://localhost/tabContent?tabId=' + tabId,
...
customSettings: JSON.stringify(customSettings)
});
saveEvent.notifySuccess();
})
});
В коде страницы вкладки доступа к
settings
нет, поэтому параметры мы передаем в
query string
для
contentUrl
. Генерировать
tabId
будем только при первом добавлении вкладки. При реконфигурации будем использовать заданный ранее. За это отвечает
generateOrGetTabId
. Поле
settings.customSettings
позволяет хранить сгенерированный
tabId
как строку, так и в виде сериализованного JSON-объекта.
В заключении хочется рассказать про
deepLinks
— способ генерации ссылки на вкладку или на конкретное содержимое внутри вкладки.
На момент написания статьи существует два способа генерации ссылки:
- Интерактивный — с помощью
microsoftTeams.shareDeepLink
. При этом у пользователя запрашивается имя и ссылка копируется в буфер обмена. Такие ссылки распознаются в чате, рендерятся как текст, введенный пользователем, и ведут на вкладку с заданными entityId
и subEntityId
. Подробное описание есть тут.
- Неинтерактивный — путем формирования ссылки с учетом id приложения (из манифеста), контекста вкладки и прочих параметров:
entityId
, subEntityId
и так далее. Такие ссылки можно использовать для различных нотификаций, например, в email или сообщениях от ботов.
Описание способов формирования
deepLinks
есть в
MSDN. Дополнительно хочется заметить, что оба способа работоспособны в коде страницы вкладки, так как не требуют использования
microsoftTeams.settings
.
Teams JS API активно развивается, и в процессе разработки версия сменилась с 0.2 до 0.4. Помимо этого манифест для ботов и вкладок стал общим.
Для интеграции с Wrike использовались также Azure AD Graph API и Microsoft Graph API. В процессе и после релиза у нас было тесное общение с коллегами из Microsoft, и наш фидбек принимался во внимание.
Часть пожеланий, которые ребята из Wrike озвучили в процессе общения.- Информация о команде должна быть доступна в Teams JS API и/или в Graph API.
- Доступ к настройкам microsoftTeams.settings нужен во вкладке, а не только на странице настроек.
- Не хватает tabId, хотя два типа Deep Links частично компенсируют отсутствие tabId.
- Нужна более гибкая система scopes для Graph Oauth2-авторизации, что необходимо для получения списка участников текущей команды (team) без привлечения админа Azure AD Tenant к настройке вкладки.
- Более гибкие настройки страницы конфигурации для кастомизации стилей, доступа к «родным» стилям Microsoft Teams, смены стандартных контролов. Такие настройки дадут больше свободы по созданию Configuration Wizards.
.
Вопросы доступа к составу команды уже решаются в
Bot API. Часть функциональности по Deep Links была опубликована незадолго до релиза Teams. Возможно, поддержка Teams на уровне REST API будет реализована в Microsoft Graph API, так как этот API активно развивается и предполагает взаимодействия с различными компонентами Office 365.
Кстати, форму для отправки нового приложения на ревью можно найти
здесь. Надеемся, опыт Wrike окажется вам полезным, и пользователи Microsoft Teams смогут получить ещё больше полезных инструментов для командной работы.
Об авторах
Александр Беляев, менеджер по продукту. Отвечает за проработку Job stories (в рамках концепции Jobs to be Done) для аудитории Wrike и, соответственно, за функции, которые пользователи применяют в рамках типичных сценариев своей работы. Также развивает Wrike API и отвечает за интеграции со стратегическими партнерами. Любит котов и дзен.
Олег Вашенков, бэкенд-разработчик. В контексте интеграции с Microsoft Teams отвечает за авторизацию, создание и интеграцию аккаунтов, поддержку и расширение API для работы внутри Teams. Любит мини-хакатоны — реализовывать идеи под новые платформы и устройства и пробовать свои решения для работы и жизни, а также машины, включая подготовку, тюнинг и соревнования.
Напоминаем, что бесплатно попробовать Microsoft Azure можно здесь.