Personal Views в Dataverse: зачем мы создали собственный инструмент управления

Сотрудник ушёл из компании, его учётную запись отключили, а личные представления — Personal Views — остались в Dataverse. Некоторые из них используются коллегами, другие нужно передать новому владельцу или удалить.
Именно с такой задачи началась разработка нашего веб-приложения Personal Views Migration. Несмотря на название, оно предназначено не только для переноса представлений, но и для их просмотра, копирования, удаления и управления общим доступом.
В этой статье расскажем, почему нам оказалось недостаточно готового плагина XrmToolBox и какие нюансы пришлось учесть в собственном решении.
А узнать больше можно в нашем видео:
Почему управление чужими Personal Views вызывает сложности
В Dataverse личные представления хранятся в таблице userquery и принадлежат конкретному владельцу. При этом пользователь может видеть как собственные представления, так и те, которыми с ним поделились.
Здесь важно различать две роли:
Контекст пользователя — чьими глазами мы смотрим на список представлений.
Владелец представления — кому принадлежит конкретная запись.
Эти роли не всегда совпадают. Например, Анна видит представление, которым с ней поделился Джон. Оно доступно в её списке, но владельцем остаётся Джон.
В сценариях, с которыми мы столкнулись, попытки удалить или переприсвоить такое представление от имени выбранного пользователя приводили к ошибкам доступа. Поэтому для соответствующих операций нам понадобилось выполнять действия от имени владельца через impersonation.
Что мы взяли за основу в XrmToolBox
Отправной точкой стал плагин Personal User Views Migration для XrmToolBox. Он работает с тремя типами личных компонентов:
представлениями — Views;
диаграммами — Charts;
панелями мониторинга — Dashboards.
Мы сосредоточились только на Personal Views, поскольку именно они были основной задачей проекта.
При разборе кода используемой версии плагина мы обнаружили важную особенность: операции Delete, Reassign и Share выполнялись от имени выбранного исходного пользователя — Source. Он не обязательно совпадал с владельцем конкретного представления.
Если выбрать Джона и работать с его собственными views, различия нет: Source и Owner совпадают. Но если выбрать Анну и попытаться удалить представление Джона из её списка Shared, действие всё равно выполняется от имени Анны.
При этом плагин учитывал владельца в отдельном сценарии просмотра и управления общим доступом для shared-записей. Поэтому проблема заключалась не в полном отсутствии логики ownership, а в её применении к конкретным операциям.
Для нашей задачи также имели значение другие ограничения:
владелец не отображался отдельной колонкой в списке;
требовался централизованный доступ через браузер;
нужна была более явная обработка отключённых пользователей и отсутствующей связи с Entra ID.
Как устроен Personal Views Migration
Мы разработали веб-приложение со следующим стеком:
ASP.NET Core API и Dataverse SDK — серверная часть;
React и Fluent UI — интерфейс;
Microsoft Entra ID и MSAL — авторизация;
Azure Web App — размещение приложения.
При необходимости доступ дополнительно ограничивается списком разрешённых email-адресов.
В текущую версию входят:
просмотр личных и общих представлений;
копирование;
назначение нового владельца;
удаление;
предоставление и изменение общего доступа.
Charts и Dashboards в эту версию не включены. Автоматическая очистка представлений отключённых пользователей вынесена в отдельный WebJob.
Главное изменение: действия от имени владельца
Основное правило нашего API: удаление, переприсвоение и управление общим доступом выполняются от имени владельца конкретного представления, независимо от того, через чей список оно было найдено.
Для копирования используется другая последовательность:
Исходное представление читается в контексте выбранного пользователя.
Новая копия создаётся от имени получателя.
Эту логику мы сделали видимой и в интерфейсе. В таблице отображается Owner, а фильтры позволяют выбрать все представления, только собственные или только общие.
Получатель — Destination — выбирается в диалоге конкретной операции. Так на основном экране остаётся меньше лишних элементов, а администратору проще понять, кому принадлежит запись и от чьего имени будет выполнено действие.
Работа с отключёнными пользователями
Для disabled-пользователей обычная impersonation в наших сценариях могла не срабатывать. За основу мы взяли подход с временным переключением в Non-Interactive Access Mode.
Последовательность выглядит так:
Сохраняем исходный Access Mode пользователя.
При необходимости временно включаем Non-Interactive.
Выполняем операцию.
Возвращаем исходный режим.
Для нас было важно восстанавливать именно прежнее значение, а не всегда переключать пользователя в Read-Write.
Также мы ограничили параллельное выполнение таких переключений и исключили Application Users из подсчёта занятых Non-Interactive мест, используемого нашей проверкой.
Если пользователь есть в CRM, но нет связи с Entra ID
Отдельный сценарий — запись пользователя остаётся в CRM, но поле Azure Active Directory Object ID пустое. В нашем решении работа с Personal Views через impersonation для такого пользователя недоступна.
Без пояснений это выглядело бы как ошибка интерфейса: администратор выбирает пользователя, но видит пустой список или получает ошибку при выполнении действия.
Поэтому мы добавили:
отметку No Entra ID в поиске и таблице;
сообщение с объяснением ограничения;
пояснение вместо обычного пустого списка;
блокировку недоступных действий с указанием причины.
Создание новой учётной записи само по себе не решает проблему старых представлений: они остаются привязанными к прежнему пользователю. Поэтому в интерфейсе важно явно показывать границы доступных операций.
Заключение
Разработка Personal Views Migration началась с практической задачи: управлять личными представлениями сотрудников после отключения их учётных записей.
Главным оказалось различие между пользователем, чей список мы просматриваем, и фактическим владельцем представления. На этой основе мы построили логику операций, добавили браузерный интерфейс и отдельную обработку проблемных сценариев.
В результате получили инструмент для нашей задачи, в котором администратор видит владельца записи, понимает, от чьего имени выполняется действие, и получает понятное объяснение, если операция недоступна.



Comments