Перейти к содержимому

SDK (Portal.EventReceiver.Sdk)

C# SDK для разработки Event Receiver.

Настройка SDK: Extension SDK с korport.ru/developers.

using Portal.EventReceiver.Sdk;
public sealed class MyHandler : IEventReceiver
{
public async Task<object?> HandleAsync(
EventContext context,
IReceiverApi api,
CancellationToken cancellationToken = default)
{
// Текущий изменённый элемент (без ручного разбора Details)
var item = await context.GetListItemAsync(api, cancellationToken);
if (item is null) return new { skipped = true };
api.Log.Info("Updated item", context.GetItemRef(), item.GetString("title"));
return new { ok = true };
}
}
ПолеТипОписание
EventNamestringИмя события (listItem.updated и т.д.)
DetailsJsonElementДанные события (listId, item, user)
ReceiverConfigJsonElementConfig привязки из UI (listId, note)
UserEventUserInfo?Пользователь, инициировавший событие
PayloadJsonElementПолный JSON задания

Для listItem.created / updated / deleted не разбирайте Details вручную:

МетодОписание
IsListItemEvent()Событие элемента списка
GetListId()GUID списка из details.listId
GetItemRef()Номер/GUID элемента (details.item.id или fallback на deleted)
TryGetItemJson(out item)Сырой снимок details.item
GetListItemAsync(api)Актуальный элемент с сервера → PortalListItem?
GetListItemSnapshotAsync(api)Элемент из снимка события (удобно для deleted)
if (!context.IsListItemEvent()) return new { skipped = true };
var listId = context.GetListId();
var itemRef = context.GetItemRef();
// created / updated — свежие данные с правами пользователя события
var item = await context.GetListItemAsync(api, ct);
var title = item?.GetString("title");
// deleted — элемент уже удалён; читайте снимок из события
if (context.EventName == "listItem.deleted")
{
var snapshot = await context.GetListItemSnapshotAsync(api, ct);
api.Log.Info("Deleted", snapshot?.GetString("title"));
}
СвойствоТипОписание
IdGuidUUID пользователя
Loginstring?Логин
ОбъектМетодОписание
ListsGetAsyncМетаданные списка
GetFieldsAsyncСхема полей
GetListItemAsync / NewListItemAsync / QueryListItemsAsyncSharePoint-подобный PortalListItem: item["title"], CreateAsync / UpdateAsync
GetItemAsyncОдин элемент (сырой JSON)
GetItemVersionsAsyncИстория версий (fieldValues в каждой версии)
GetItemVersionAsyncОдна версия по номеру
GetItemsAsync / QueryItemsAsyncВыборка элементов (сырой JSON)
CreateItemAsync / UpdateItemAsyncНизкоуровневое создание / изменение (с проверкой прав)
LibrariesGetAsyncМетаданные библиотеки
GetFilesAsync / QueryFilesAsyncВыборка файлов
NodesGetAsyncУзел
UsersGetProfileAsync / GetOrgChainAsync / GetOrgChildrenAsync / AvatarUrlПрофиль сотрудника и оргструктура
PermissionsForListAsync / ForListItemAsync / GrantToUserAsync / RevokeAsync / …Проверка и назначение ACL (детали)
KedoCreatePackageAsync / GetPackageStatusAsyncОтправка документа на подпись/ознакомление (КЭДО)
JobsEnqueueAsync(serviceKey, jobType, jobPayload?)Поставить задание в очередь
LogInfo / Warn / ErrorЛогирование Worker (params object[])

Права и ACL выполняются от имени пользователя из context.User.

using Portal.Contracts.Lists;
// Fluent-builder (C# DLL)
var items = await api.Lists.QueryItemsAsync(listId!,
ListItemQueryBuilder.ForList(Guid.Parse(listId!))
.WhereField("title", f => f.Eq("GlobalSettings"))
.Take(1)
.Build(), cancellationToken);
// Объект opts (тот же формат, что query-параметры REST)
var batch = await api.Lists.GetItemsAsync(listId!, new
{
viewId,
top = 50,
orderBy = new[] { new { column = "title", direction = "asc" } },
filter = new
{
logic = "and",
conditions = new[] { new { column = "title", op = "eq", value = "A" } },
},
q = "поиск",
}, cancellationToken);

column в фильтре — UUID поля или internal_name ("title"). Фильтрация выполняется на сервере (SQL push-down), см. API списков.

using Portal.Contracts.Libraries;
var files = await api.Libraries.QueryFilesAsync(libraryId!,
LibraryFileQueryBuilder.ForLibrary(Guid.Parse(libraryId!))
.InFolder(folderId)
.WhereField("name", f => f.Contains("договор"))
.Take(50)
.Build(), cancellationToken);
// Объект opts (тот же формат, что query-параметры REST)
var batch = await api.Libraries.GetFilesAsync(libraryId!, new
{
parentId = folderId,
top = 50,
orderBy = new[] { new { column = "updated_at", direction = "desc" } },
filter = new
{
logic = "and",
conditions = new[] { new { column = "item_type", op = "eq", value = "file" } },
},
q = "отчёт",
}, cancellationToken);

Колонки фильтра: name, item_type, file_size, mime_type, updated_at, created_at, uploaded_by, version_number, item_number. См. API библиотек.

Рекомендуемый путь — текущий элемент события + доступ по internal_name (гайд):

using Portal.Contracts.Lists;
using Portal.EventReceiver.Sdk;
var item = await context.GetListItemAsync(api, ct)
?? throw new InvalidOperationException("В событии нет listId/item");
var currentTitle = item.GetString("title");
var status = item.GetString("status");
item["status"] = "Готово";
await item.UpdateAsync(ct);
// Создание в том же списке
var listId = context.GetListId()!.Value;
var created = await api.Lists.NewListItemAsync(listId, ct);
created["title"] = "Новая задача";
created["status"] = "Новая";
await created.CreateAsync(ct);
// Выборка
var found = await api.Lists.QueryListItemsAsync(listId,
ListItemQueryBuilder.ForList(listId)
.WhereField("title", f => f.Eq("GlobalSettings"))
.Take(1)
.Build(), ct);

Низкоуровнево (UUID / сырой JSON, версии) — GetItemAsync + field_values[fieldId] или PortalItemJson. Полные примеры: Значения полей списков.

Окно терминала
dotnet new install ./portal-sdk-1.0.0/templates/portal-eventreceiver
dotnet new portal-eventreceiver -n MyHandler -o ./my-handler

Пример в SDK ZIP: examples/list-change-handler. См. Extension SDK.