Перейти к содержанию

Описание скриптовой автоматизации

Скрипт — это набор инструкций на C#, которые составляют тело скрытого внутреннего метода, затем компилируемого и выполняемого по триггерам.

Технически скрипты транслируются на внутренний промежуточный язык .NET - CIL.

Скрипты вызываются событиями в системе. На одно событие в рамках пространства может быть создано несколько автоматизаций.

Контекст событий

Каждое событие имеет свой контекст - набор данных, связанный с ним. Контексты для разных событий отличаются. Контекст передаётся в память выполняющегося скрипта в виде типизированного объекта. Доступ к контексту осуществляется через переменную e.

debug($"Пространство: {e.WorkspaceKey}");

Контекст создания задачи

Когда триггером автоматизации является событие "Создание задачи" передаются следующие данные события:

public class WorkitemCreatedEventContext : BaseWorkitemEventContext
{
    public Guid WorkitemId { get; init; } // Идентификатор созданной задачи
    public string WorkitemKey { get; init; } // Ключ созданной задачи
    public string WorkitemName { get; init; } // Название созданной задачи
    public required string? WorkitemDescription { get; set; } // Описание созданной задачи
    public Guid? WorkitemAssigneeId { get; init; } // Идентификатор ответсвенного на задаче
    public Guid WorkitemTypeId { get; init; } // Идентификатор типа задачи
    public required DateTime WorkitemCreationDate { get; set; } // Дата создания задачи
    public required Guid? WorkitemWorkflowId { get; init; } // Идентификатор рабочего процесса задачи

    public Guid? TenantId { get; init; } // Идентификатор тенанта
    public Guid EventId { get; init; } // Идентификатор события
    public EventTypes EventType { get; init; } // Тип события
    public string EventName { get; init; } // Название события
    public Guid UserId { get; init; } // Идентификатор пользователя, инициировавшего событие
    public Guid WorkspaceId { get; init; } // Идентификатор пространства, в котором произошло событие
    public string WorkspaceKey { get; init; } // Ключ пространства, в котором произошло событие
}

Контекст событий изменения задачи

Событиями изменения задачи являются:

  • Изменение родительской задачи или папки
  • Изменение типа задачи
  • Изменение процесса
  • Изменение статуса
  • Изменение имени
  • Изменение описания
  • Изменение ответственного
  • Изменение даты начала
  • Изменение даты выполнения
  • Изменение оценки в часах
  • Изменение оценки в сторипоинтах
  • Изменение спринта
  • Изменение значения прогресса
  • Изменение пользовательского атрибута

Для этих событий передаются следующие данные контекста:

public class WorkitemUpdatedEventContext : BaseWorkitemEventContext
{
    public object? OldValue { get; init; } // Значение параметра до изменения
    public object? NewValue { get; init; } // Значение параметра после изменения

    public Guid WorkitemId { get; init; } // Идентификатор задачи
    public string WorkitemKey { get; init; } // Ключ задачи
    public string WorkitemName { get; init; } // Название задачи
    public required string? WorkitemDescription { get; set; } // Описание задачи
    public Guid? WorkitemAssigneeId { get; init; } // Идентификатор ответственного на задаче
    public Guid WorkitemTypeId { get; init; } // Идентификатор типа задачи
    public required DateTime WorkitemCreationDate { get; set; } // Дата создания задачи
    public required Guid? WorkitemWorkflowId { get; init; } // Идентификатор рабочего процесса задачи

    public Guid WorkitemParentId { get; init; } // Идентификатор родительского элемента (задачи или папки)
    public Guid? WorkitemStatusId { get; init; } // Идентификатор статуса
    public Guid WorkitemCreatedBy { get; set; } // Идентификатор пользователя, который создал задачу
    public Guid WorkitemUpdatedBy { get; set; } // Идентификатор пользователя, который последний изменил задачу
    public DateTime WorkitemUpdateDate { get; set; } // Дата изменения задачи
    public DateTime? WorkitemDueDate { get; set; } // Дата выполнения
    public DateTime? WorkitemEndDate { get; set; } // Дата завершения
    public DateTime? WorkitemStartDate { get; set; } // Дата начала
    public Dictionary<Guid, object?> WorkitemAttributesValues { get; set; } = new(); // Значения пользовательских атрибутов
    public OkrProgressType? WorkitemProgressType { get; set; }
    public string? WorkitemProgressData { get; set; }
    public int? WorkitemProgressValue { get; set; }
    public int? WorkitemEstimatedTime { get; set; } // Оценка в часах
    public int? WorkitemSpentTime { get; set; } // Сумма затраченного времени
    public int? WorkitemLeftTime { get; set; } // Оставшееся время
    public int? WorkitemEstimatedStoryPoints { get; set; } // Оценка в сторипоинтах

    public Guid? TenantId { get; init; } // Идентификатор тенанта
    public Guid EventId { get; init; } // Идентификатор события
    public EventTypes EventType { get; init; } // Тип события
    public string EventName { get; init; } // Название события
    public Guid UserId { get; init; } // Идентификатор пользователя, инициировавшего событие
    public Guid WorkspaceId { get; init; } // Идентификатор пространства, в котором произошло событие
    public string WorkspaceKey { get; init; } // Ключ пространства, в котором произошло событие
}

Соответствие типа объекта в полях NewValue и OldValue в зависимости от типа события приведено в таблице:

Событие Тип объекта Описание объекта
Изменение родительской задачи или папки Guid Идентификатор задачи или папки
Изменение типа задачи Guid? Идентификатор типа
Изменение процесса Guid? Идентификатор процесса
Изменение статуса Guid Идентификатор статуса
Изменение имени string Название задачи
Изменение описания string Описание задачи
Изменение ответственного Guid? Идентификатор отвественного
Изменение даты начала DateTime? Дата начала
Изменение даты выполнения DateTime? Дата выполнения
Изменение оценки в часах int? Оценка в секундах
Изменение оценки в сторипоинтах int? Оценка в сторипоинтах
Изменение спринта Guid? Идентификатор спринта
Изменение значения прогресса int? Значение прогресса в %
Изменение пользовательского атрибута "Число" int? Числовое значение атрибута
Изменение пользовательского атрибута "Строка" int? Строковое значение атрибута
Изменение пользовательского атрибута "Выбор из списка" string? Значение опции атрибута
Изменение пользовательского атрибута "Тег" string? Значение опции атрибута
Изменение пользовательского атрибута "Пользователь" Guid? Идентификатор пользователя
Изменение пользовательского атрибута "Дата" DateTime? Значение даты атрибута
Изменение пользовательского атрибута "Время" int? Значение времени в секундах

Контекст создания списания

Когда триггером автоматизации является событие "Добавление списания времени" передаются следующие данные события:

public class TimeTrackingCreatedEventContext : EventContext
{
    public Guid WorkitemId { get; set; } // Идентификатор задачи по которой добавлено списание
    public Guid EntryId { get; set; } // Идентификатор списания
    public DateTime CreationDate { get; set; } // Дата добавления списания
    public DateTime StartDate { get; set; } // Дата списания
    public int Duration { get; set; } // Время списания в секундах
    public string? Description { get; set; } // Описание списания

    public Guid? TenantId { get; init; } // Идентификатор тенанта
    public Guid EventId { get; init; } // Идентификатор события
    public EventTypes EventType { get; init; } // Тип события
    public string EventName { get; init; } // Название события
    public Guid UserId { get; init; } // Идентификатор пользователя, инициировавшего событие
    public Guid WorkspaceId { get; init; } // Идентификатор пространства, в котором произошло событие
    public string WorkspaceKey { get; init; } // Ключ пространства, в котором произошло событие
}

Контекст изменения списания

Когда триггером автоматизации является событие "Редактирование списания времени" передаются следующие данные события:

public class TimeTrackingUpdatedEventContext : EventContext
{
    public Guid WorkitemId { get; set; } // Идентификатор задачи по которой добавлено списание
    public Guid EntryId { get; set; } // Идентификатор списания
    public DateTime CreationDate { get; set; } // Дата добавления списания
    public DateTime StartDate { get; set; } // Дата списания
    public int Duration { get; set; } // Время списания в секундах
    public string? Description { get; set; } // Описание списания

    public Guid? TenantId { get; init; } // Идентификатор тенанта
    public Guid EventId { get; init; } // Идентификатор события
    public EventTypes EventType { get; init; } // Тип события
    public string EventName { get; init; } // Название события
    public Guid UserId { get; init; } // Идентификатор пользователя, инициировавшего событие
    public Guid WorkspaceId { get; init; } // Идентификатор пространства, в котором произошло событие
    public string WorkspaceKey { get; init; } // Ключ пространства, в котором произошло событие
}

Контекст добавления задачи в элемент портфолио

Когда триггером автоматизации является событие "Добавление задачи в портфолио-элемент" передаются следующие данные события:

public class WorkitemAddedToPortfolioElementEventContext : EventContext
{
    public Guid WorkitemId { get; set; } // Идентификатор задачи, добавленной в элемент портфолио
    public Guid PortfolioElementId { get; set; } // Идентификатор элемента портфолио
    public string PortfolioElementName { get; set; } = default!; // Название элемента портфолио
    public Guid? PortfolioElementStatusId { get; set; } // Идентификатор статуса элемента портфолио

    public Guid? TenantId { get; init; } // Идентификатор тенанта
    public Guid EventId { get; init; } // Идентификатор события
    public EventTypes EventType { get; init; } // Тип события
    public string EventName { get; init; } // Название события
    public Guid UserId { get; init; } // Идентификатор пользователя, инициировавшего событие
    public Guid WorkspaceId { get; init; } // Идентификатор пространства, в котором произошло событие
    public string WorkspaceKey { get; init; } // Ключ пространства, в котором произошло событие
}

Контекст удаления задачи из элемента портфолио

Когда триггером автоматизации является событие "Удаление задачи из портфолио-элемента" передаются следующие данные события:

public class WorkitemDeletedFromPortfolioElementEventContext : EventContext
{
    public Guid WorkitemId { get; set; } // Идентификатор задачи, добавленной в элемент портфолио
    public Guid PortfolioElementId { get; set; } // Идентификатор элемента портфолио
    public string PortfolioElementName { get; set; } = default!; // Название элемента портфолио
    public Guid? PortfolioElementStatusId { get; set; } // Идентификатор статуса элемента портфолио

    public Guid? TenantId { get; init; } // Идентификатор тенанта
    public Guid EventId { get; init; } // Идентификатор события
    public EventTypes EventType { get; init; } // Тип события
    public string EventName { get; init; } // Название события
    public Guid UserId { get; init; } // Идентификатор пользователя, инициировавшего событие
    public Guid WorkspaceId { get; init; } // Идентификатор пространства, в котором произошло событие
    public string WorkspaceKey { get; init; } // Ключ пространства, в котором произошло событие
}

Контекст создания спринта

Когда триггером автоматизации является событие "Создание спринта" передаются следующие данные события:

public class SprintCreatedEventContext : EventContext
{
    public Guid ExtensionId { get; set; } // Идентификатор расширения Agile
    public Guid SprintId { get; set; } // Идентификатор спринта
    public string SprintName { get; set; } // Название спринта
    public SprintStatus SprintStatus { get; set; } // Статус спринта

    public Guid? TenantId { get; init; } // Идентификатор тенанта
    public Guid EventId { get; init; } // Идентификатор события
    public EventTypes EventType { get; init; } // Тип события
    public string EventName { get; init; } // Название события
    public Guid UserId { get; init; } // Идентификатор пользователя, инициировавшего событие
    public Guid WorkspaceId { get; init; } // Идентификатор пространства, в котором произошло событие
    public string WorkspaceKey { get; init; } // Ключ пространства, в котором произошло событие
}

Контекст изменения статуса спринта

Когда триггером автоматизации является событие "Изменение статуса спринта" передаются следующие данные события:

public class SprintUpdatedEventContext : EventContext
{
    public Guid ExtensionId { get; set; } // Идентификатор расширения Agile
    public Guid SprintId { get; set; } // Идентификатор спринта
    public string SprintName { get; set; } // Название спринта
    public SprintStatus SprintStatus { get; set; } // Статус спринта

    public Guid? TenantId { get; init; } // Идентификатор тенанта
    public Guid EventId { get; init; } // Идентификатор события
    public EventTypes EventType { get; init; } // Тип события
    public string EventName { get; init; } // Название события
    public Guid UserId { get; init; } // Идентификатор пользователя, инициировавшего событие
    public Guid WorkspaceId { get; init; } // Идентификатор пространства, в котором произошло событие
    public string WorkspaceKey { get; init; } // Ключ пространства, в котором произошло событие
}

Методы Script API

Метод, в котором выполняется пользовательский код, технически находится в классе, наследуемом от базового класса, имеющим веб-клиент CwmPublicAPI и набор методов.

Примеры использования функций приведены в подразделе Примеры.

Сигнатуры методов

Сигнатуры методов имеют своими аргументами идентификаторы или имена/ключи задач, пространств, папок и т.д., там где это применимо, но они, как правило, необязательные. Если что-либо пропущено, то подразумевается текущее пространство, текущая задача и т.п., а для удобства использования они перечислены в порядке возрастания иерархии:

GetWorkitem(string? workitemKey = null, string? workspaceKey = null);

Методы работы с пространствами

Task<List<WorkspaceModel>?> GetWorkspaces()
Task<WorkspaceModel?> GetWorkspace(Guid workspaceId)
Task<WorkspaceModel?> GetWorkspace(string workspaceKey = null)

Task<WorkspaceModel> CreateWorkspace(string name, string key, string? description = null)

Task<WorkspaceModel> UpdateWorkspaceName(string name, string? workspaceKey = null)

Task DeleteWorkspace(string? workspaceKey = null)

Методы работы с ролями

Task<List<RoleModel>?> GetRoles(string? workspaceKey = null)
Task<List<RoleModel>?> GetRoles(bool? isSystem, string? workspaceKey = null)
Task<RoleModel?> GetRole(Guid roleId, string? workspaceKey = null)
Task<RoleModel?> GetRole(string role, string? workspaceKey = null)

Task<RoleModel> CreateRole(string name, List<Permission?>? permissions, string? workspaceKey = null)

Task<RoleModel> UpdateRolePermissions(Guid roleId, List<Permission?>? permissions = null, string? workspaceKey = null)

Методы работы с пользователями

Task<List<UserModel>?> GetUsers()
Task<UserModel?> GetUser(Guid userId)
Task<UserModel?> GetUser(string userName)
Task<List<UserModel>?> GetUsersByRole(Guid roleId, string? workspaceKey = null)

Task AddUserRole(Guid userId, Guid roleId, string? workspaceKey = null)

Task DeleteUserRole(Guid userId, Guid roleId, string? workspaceKey = null)

Методы работы с группами

Task<List<GroupModel>?> GetUsersGroups()
Task<GroupModel?> GetUserGroup(Guid userGroupId)
Task<List<GroupModel>?> GetUsersGroups(string userName)

Task DeleteGroupRole(Guid groupId, Guid roleId, string? workspaceKey = null)

Методы работы с папками

Task<List<FolderModel>?> GetFolders(string? workspaceKey = null)
Task<List<FolderModel>?> GetFolderByParent(Guid parentId, string? workspaceKey = null)
Task<FolderModel?> GetFolder(Guid folderId, string? workspaceKey = null)
Task<FolderModel?> GetFolder(string name, string? workspaceKey = null)

Task<FolderModel> CreateFolder(string name, Guid? parentId = null, string? workspaceKey = null)

Task<FolderModel?> UpdateFolder(PatchFolderRequestBody body, Guid folderId, string? workspaceKey = null) // общего вида - сразу все свойства скопом
Task<WorkitemModel> UpdateFolderName(string name, Guid folderId, string? workspaceKey = null)

Task DeleteFolder(Guid folderId, string? workspaceKey = null)

Методы работы с типами

Task<List<TypeModel>?> GetTypes(string? workspaceKey = null)
Task<TypeModel?> GetType(Guid typeId, string? workspaceKey = null)
Task<TypeModel?> GetType(string typeName, string? workspaceKey = null)

Task<TypeModel> CreateType(CreateTypeRequestBody body, string? workspaceKey = null)

Task DeleteType(Guid typeId, string? workspaceKey = null)
Task DeleteType(string? type, string? workspaceKey = null)

Методы работы со статусами

Task<List<StatusCategoryModel>?> GetStatusCategories()

Task<List<StatusModel>?> GetStatuses(string? workspaceKey = null)
Task<StatusModel?> GetStatus(Guid statusId, string? workspaceKey = null)
Task<StatusModel?> GetStatus(string name, string? workspaceKey = null)

Task<StatusModel> CreateStatus(CreateStatusRequestBody body, string? workspaceKey = null)

Методы работы с атрибутами типов

Task<List<AttributeModel>?> GetAttributes(string? workspaceKey = null)
Task<AttributeModel?> GetAttribute(Guid attributeId, string? workspaceKey = null)
Task<AttributeModel?> GetAttribute(string attributeName, string? workspaceKey = null)

Task<AttributeModel> CreateNumberAttribute(string name, string? description, string? workspaceKey = null)
Task<AttributeModel> CreateUniStringAttribute(string name, string? description = null, string? workspaceKey = null)
Task<AttributeModel> CreateUserAttribute(string name, string? description = null, string? workspaceKey = null)
Task<AttributeModel> CreateDateAttribute(string name, string? description = null, string? workspaceKey = null)
Task<AttributeModel> CreateTimeAttribute( string name, string? description = null, string? workspaceKey = null)
Task<AttributeModel> CreateUniSelectAttribute(string name, IEnumerable<string> listOptions, string? description = null, string? workspaceKey = null)
Task<AttributeModel> CreateTagAttribute(string name, LIEnumerablest<string> listOptions, string? description = null, string? workspaceKey = null)

Task<AttributeModel> UpdateWorkitemAttribute(Guid attributeId, PatchAttributeRequestBody body, string? workspaceKey = null)
Task<AttributeModel> UpdateScalarWorkitemAttribute(Guid attributeId, string name, string? description = null, string? workspaceKey = null) // Все скалярные
Task<AttributeModel> UpdateListWorkitemAttribute(Guid attributeId, string name, List<PatchAttributeOptionModel> listOptions, string? description = null, string? workspaceKey = null) // UniSelect & Tag

Task DeleteAttribute(Guid attributeId, string? workspaceKey = null)

Методы работы с задачами

Task<List<WorkitemModel>?> GetWorkitems(string? workspaceKey = null)
Task<WorkitemModel?> GetWorkitem(Guid workitemId, string? workspaceKey = null)
Task<WorkitemModel?> GetWorkitem(string? workitemKey = null, string? workspaceKey = null)
Task<List<WorkitemModel>?> GetWorkitemByParent(bool withSubitems = false, string? parentKey = null, string? workspaceKey = null) // планарный список для иерархии потомков (withSubitems = true)
Task<WorkitemModelTree?> GetWorkitemTree(Guid? parentId = null, string? workspaceKey = null) // потомки в виде иерархического дерева, WorkitemModelTree - собственный тип ScriptAPI (https://git.testit.ru/teamstorm/teamstorm/-/blob/feature/ib/TS-8917-scripts-child-tasks/src/backend/dotnet/AutomationService.ScriptGate/Contracts/Tree.cs?ref_type=heads)

Task<WorkitemModel> CreateWorkitem(CreateWorkitemRequestBody body, string? workspaceKey = null)

Task<WorkitemModel?> UpdateWorkitem(PatchWorkitemRequestBody body, string? workitemKey = null, string? workspaceKey = null) // общего вида
Task<WorkitemModel> UpdateWorkitemParent(Guid? parentId, string? workitemKey = null, string? workspaceKey = null)
Task<WorkitemModel> UpdateWorkitemStatus(string status, string? workitemKey = null, string? workspaceKey = null)
Task<WorkitemModel> UpdateWorkitemType(string typeName, string? workitemKey = null, string? workspaceKey = null)
Task<WorkitemModel> UpdateWorkitemName(string name, string? workitemKey = null, string? workspaceKey = null)
Task<WorkitemModel> UpdateWorkitemDescription(string desc, string? workitemKey = null, string? workspaceKey = null)
Task<WorkitemModel> UpdateWorkitemAssignee(Guid userId, string? workitemKey = null, string? workspaceKey = null)
Task<WorkitemModel> UpdateWorkitemAssignee(string? userName, string? workitemKey = null, string? workspaceKey = null)
Task<WorkitemModel> UpdateWorkitemStartDate(DateTime? startDate, string? workitemKey = null, string? workspaceKey = null)
Task<WorkitemModel> UpdateWorkitemDueDate(DateTime? dueDate, string? workitemKey = null, string? workspaceKey = null)
Task<WorkitemModel> UpdateWorkitemSprint(Guid? sprintId, string? workitemKey = null, string? workspaceKey = null)
Task<WorkitemModel> UpdateWorkitemStoryPoints(int? points, string? workitemKey = null, string? workspaceKey = null)
Task<WorkitemModel> UpdateWorkitemOriginalEstimate(int? estimate, string? workitemKey = null, string? workspaceKey = null)
Task<WorkitemModel> UpdateWorkitemWorkflow(Guid? workflowId, string? workitemKey = null, string? workspaceKey = null)
Task<WorkitemModel> UpdateWorkitemPortfolioElement(List<Guid?>? elementIds, string? workitemKey = null, string? workspaceKey = null)

Task DeleteWorkitem(string? workitemKey = null, string? workspaceKey = null)

Методы работы со значениями атрибутов

Атрибуты с типом Число

Task<NumberFieldValueModel?> GetNumberAttributeValue(Guid attributeId, string? workitemKey = null, string? workspaceKey = null)
Task<NumberFieldValueModel?> GetNumberAttributeValue(string attributeName, string? workitemKey = null, string? workspaceKey = null)

Task<NumberFieldValueModel?> UpdateNumberAttributeValue(string attributeName, double? value, string? workitemKey = null, string? workspaceKey = null)
Task<NumberFieldValueModel?> UpdateNumberAttributeValue(Guid attributeId, double? value, string? workitemKey = null, string? workspaceKey = null)

Атрибуты с типом Строка

Task<UniStringFieldValueModel?> GetUniStringAttributeValue(Guid attributeId, string? workitemKey = null, string? workspaceKey = null)
Task<UniStringFieldValueModel?> GetUniStringAttributeValue(string attributeName, string? workitemKey = null, string? workspaceKey = null)

Task<UniStringFieldValueModel?> UpdateUniStringAttributeValue(string attributeName, string value, string? workitemKey = null, string? workspaceKey = null)
Task<UniStringFieldValueModel?> UpdateUniStringAttributeValue(Guid attributeId, string value, string? workitemKey = null, string? workspaceKey = null)

Атрибуты с типом Выбор из списка

Task<UniSelectFieldValueModel?> GetUniSelectAttributeValue(Guid attributeId, string? workitemKey = null, string? workspaceKey = null)
Task<UniSelectFieldValueModel?> GetUniSelectAttributeValue(string attributeName, string? workitemKey = null, string? workspaceKey = null)

Task<UniSelectFieldValueModel?> UpdateUniSelectAttributeValue(string attributeName, string option, string? workitemKey = null, string? workspaceKey = null)
Task<UniSelectFieldValueModel?> UpdateUniSelectAttributeValue(Guid attributeId, string option, string? workitemKey = null, string? workspaceKey = null)

Атрибуты с типом Тег

Task<TagFieldValueModel?> GetTagAttributeValue(Guid attributeId, string? workitemKey = null, string? workspaceKey = null)
Task<TagFieldValueModel?> GetTagAttributeValue(string attributeName, string? workitemKey = null, string? workspaceKey = null)

Task<TagFieldValueModel?> UpdateTagAttributeValue(string attributeName, List<string> options, string? workitemKey = null, string? workspaceKey = null)
Task<TagFieldValueModel?> UpdateTagAttributeValue(Guid attributeId, List<string> options, string? workitemKey = null, string? workspaceKey = null)

Атрибуты с типом Дата

Task<DateFieldValueModel?> GetDateAttributeValue(Guid attributeId, string? workitemKey = null, string? workspaceKey = null)
Task<DateFieldValueModel?> GetDateAttributeValue(string attributeName, string? workitemKey = null, string? workspaceKey = null)

Task<DateFieldValueModel?> UpdateDateAttributeValue(string attributeName, DateTime? value, string? workitemKey = null, string? workspaceKey = null)
Task<DateFieldValueModel?> UpdateDateAttributeValue(Guid attributeId, DateTime? value, string? workitemKey = null, string? workspaceKey = null)

Атрибуты с типом Пользователь

Task<UserFieldValueModel?> GetUserAttributeValue(Guid attributeId, string? workitemKey = null, string? workspaceKey = null)
Task<UserFieldValueModel?> GetUserAttributeValue(string attributeName, string? workitemKey = null, string? workspaceKey = null)

Task<UserFieldValueModel?> UpdateUserAttributeValue(string attributeName, Guid userId, string? workitemKey = null, string? workspaceKey = null)
Task<UserFieldValueModel?> UpdateUserAttributeValue(Guid attributeId, Guid userId, string? workitemKey = null, string? workspaceKey = null)

Атрибуты с типом Время

Task<TimeFieldValueModel?> GetTimeAttributeValue(Guid attributeId, string? workitemKey = null, string? workspaceKey = null)
Task<TimeFieldValueModel?> GetTimeAttributeValue(string attributeName, string? workitemKey = null, string? workspaceKey = null)

Task<TimeFieldValueModel?> UpdateTimeAttributeValue(string attributeName, int? seconds, string? workitemKey = null, string? workspaceKey = null)
Task<TimeFieldValueModel?> UpdateTimeAttributeValue(Guid attributeId, int? seconds, string? workitemKey = null, string? workspaceKey = null)

Методы работы с комментариями

Task<List<CommentModel>?> GetWorkitemComments(string? workitemKey = null, string? workspaceKey = null)

Task<CommentModel> CreateWorkitemComment(string text, string? workitemKey = null, string? workspaceKey = null)

Методы работы с вложениями

Task<List<AttachmentModel>?> GetWorkitemAttachments(string? workitemKey = null, string? workspaceKey = null)
Task<AttachmentModel?> GetWorkitemAttachment(Guid attachmentId, string? workitemKey = null, string? workspaceKey = null)

Task DeleteAttachment(Guid attachmentId, string? workitemKey = null, string? workspaceKey = null)

Методы работы с портфолио

Task<List<PortfolioModel>?> GetPortfolios(string? workspaceKey = null)
Task<List<PortfolioModel>?> GetPortfoliosInFolder(Guid folderId, string? workspaceKey = null)
Task<PortfolioModel?> GetPortfolio(Guid portfolioId, string? workspaceKey = null)
Task<PortfolioModel?> GetPortfolio(string name, string? workspaceKey = null)

Task<PortfolioModel> CreatePortfolio(string name, Guid folderId, string? workspaceKey = null)

Task<PortfolioModel> UpdatePortfolio(Guid portfolioId, string name, string? workspaceKey = null)

Task DeletePortfolio(Guid portfolioId, string? workspaceKey = null)

Работа с элементами портфолио

Task<List<PortfolioElementModel>?> GetPortfolioElements(string? workspaceKey = null)
Task<List<PortfolioElementModel>?> GetPortfolioElementsInFolder(Guid folderId, string? workspaceKey = null)
Task<List<PortfolioElementModel>?> GetPortfolioElementsForStatus(string status, string? workspaceKey = null)
Task<List<PortfolioElementModel>?> GetPortfolioElements(Guid portfolioId, string? workspaceKey = null)
Task<PortfolioElementModel?> GetPortfolioElement(Guid elementId, string? workspaceKey = null)
Task<PortfolioElementModel?> GetPortfolioElement(string elementName, string? workspaceKey = null)

Task<PortfolioElementModel> CreatePortfolioElement(CreatePortfolioElementRequestBody body, string? workspaceKey = null)

Task<PortfolioElementModel> UpdatePortfolioElement(PortfolioElementModel body, string? workspaceKey = null)

Task DeletePortfolioElement(Guid elementId, string? workspaceKey = null)

Получение задач в элементе портфолио

Task<List<WorkitemModel>?> GetWorkitemsByPortfolioElement(Guid elementId, string? workspaceKey = null)

Методы работы с Agile

Task<List<AgileModel>?> GetAgileExtensions(string? workspaceKey = null)
Task<AgileModel?> GetAgileExtension(Guid agileExtensionId, string? workspaceKey = null)

Task<AgileModel> CreateAgile(CreateAgileRequestBody body, string? workspaceKey = null)

Task DeleteAgile(Guid agileExtensionId, string? workspaceKey = null)

Работа со спринтами

Task<List<SprintModel>?> GetSprints(string? workspaceKey = null)
Task<SprintModel?> GetSprint(Guid sprintId, string? workspaceKey = null)
Task<SprintModel?> GetSprint(string name, string? workspaceKey = null)

Task<SprintModel> CreateSprint(CreateSprintRequestBody body, string? workspaceKey = null)

Task DeleteSprint(Guid sprintId, string? workspaceKey = null)

Методы работы со списаниями

Task<List<TimeTrackingEntryModel>?> GetTimeTrackingEntries(DateTime? startDate = null, DateTime? endDate = null,
    List<string>? users = null, int? maxItemsCount = 50, string? fromToken = null)

Методы работы со связями

Task<List<LinkTypeModel>?> GetLinkTypes(string? workspaceKey = null)

Методы для отправки HTTP-запросов

Task<string> SendHttpRequest(string url, HttpRequest request) // аргумент request содержит в себе все возможные свойства, передаваемые в следующей функции напрямую

Task<string> SendHttpRequest(string url, HttpMethod method = HttpMethod.Get, object? body = null, 
    List<(string Name, string Value)>? headers = null, string mediaType = "application/json", 
    IReadOnlyDictionary<string, object?>? options = null)

Примеры

Примечание

Для всех функций ScriptAPI (доступ к PublicAPI и логирование) необходимо указывать впереди ключевое слово await, так как они асинхронные.

Синтаксис языка — C11, поэтому инициализация коллекций происходит следующим образом: List<int> list = new { 1, 2 }, а не List<int> list = [ 1, 2 ].

Простейший пример скрипта, который даже не использует PublicAPI:

debug("USER = " + e.UserId);

Примечание

Если тело скрипта передается в запросе напрямую (на странице Swagger или утилите типа Postman), все внутренние двойные кавычки свойства, передаваемого JSON, необходимо экранировать:

{
   Name: "Script1",
   ScriptType: "Method",
   EventType: "WorkitemNameChanged",
   Script: "debug(\"USER = \" + e.UserId);", // вот тут в тексте самого скрипта - экранирование кавычек
}

Пример создания папки со случайным именем

var r = new Random();
var newName = $"name{r.Next(0, 1000)}";
await CreateFolder(newName); // вот тут должно быть await
debug("New name = " + newName);

Примеры скриптов, созданных через обертки

// Сравнение с текущим событием можно будет делать как по его Id, так и по его имени (enum лучше)
// Кейс 1: e.EventId == Events.WorkitemCreated (Events - класс с полями в виде именованных гуидов + некоторые вспомогательные функции)
// Кейс 2: e.EventType == EventTypes.WorkitemCreated (EventTypes - enum)
// Events также имеет статический словарь '<Guid, EventTypes> Map' - Id и enum события для системных полей
// Имена обсуждаемы

// Сценарий: автозаполнение исполнителя в зависимости от типа запроса
var wiType = await GetType(e.WorkitemTypeId);
if (e.EventType == EventTypes.WorkitemCreated && wiType.Name == "Bug") {
    // UserModel? assignee = await GetUser("ivan.ivanov"); // в данном случае получение модели излишне
    WorkitemModel? workitem = await UpdateWorkitemAssignee("ivan.ivanov");
}

//  Сценарий: по умолчанию проставление среднего приоритета при регистрации запроса
var wiType = await GetType(e.WorkitemTypeId);
if (e.EventId == EventTypes.WorkitemCreated && wiType.Name == "Bug") {
    // если уже имеется Id атрибута, лучше использовать функцию, принимающую именно его, а не имя (производительность)
    await UpdateUniSelectAttributeValue("Приоритет", "Средний"); // текущий воркайтем
    //await SetUniSelectAttributeValue(attributeId, "Средний", workitemId2); // сторонний воркайтем, но текущего воркспейса
    //await SetUniSelectAttributeValue(attributeId, "Средний", workitemId2, workspaceId2); // воркайтем в стороннем воркспейсе
}

// Сценарий: проставление команды в зависимости от исполнителя
var user = await GetUser(e.WorkitemAssigneeId);
// 'WorkitemChanged' нет - все события на изменение гранулярные
if (e.EventType == EventTypes.WorkitemNameChanged && user.Username == "ivan.ivanov") {
    await UpdateUniSelectAttributeValue("Команда", "Alfa");
}

// Сценарий: Автоматическое назначение на задачу определенной проектной роли. Как следствие назначается пользователь, у которого установлена такая проектная роль.
List<UserModel> users = await GetUsersByRole(roleId); // можно так (все функции планарно на одном уровне, но функции могут быть длиннее по имени - с префиксом сущности)
// List<UserModel> users = await WorkspaceUsers.FindByRole(role); // а можно эдак (в АПИ организовать отдельные 'свойства доступа': для текущего воркайтема, общего доступа в воркспейсам, юзерам и т.д.)

// Сценарий: Автоматическое присвоение типа документа по вхождению определенного текста в название документа. 
// Сценарий: Создание задачи с автоматическим определением типа на базе локации пользователя, то есть где пользователь инициировал создание задачи. Чаще всего применимо к спискам (портфолио).
WorkitemModel wi = await UpdateWorkitemType("SomeType"); // текущему воркайтему

// Сценарий: Расчет и заполнение атрибута в зависимости от значений других атрибутов текущей задачи. Например, автоматическое присваивание задаче портфолио, если она является определенного типа.
PortfolioElementModel? elem1 = await GetPortfolioElement(elementId);
PortfolioElementModel? elem1 = await GetPortfolioElement("Element1"); // или по elementName
// PorfolioElementModel? elem1 = await PorfolioElements.FindByName(name); // либо так
elem1.StartDate = DateTimeOffset.UtcNow.AddDays(10).Date; // изменить сам элемент
await UpdatePortfolioElement(elem1);
await UpdateWorkitemPortfolioElements(elementIds); // привязка к воркайтему списка элементов портфолио - по гуидам

// Сценарий: Установить ответственного при определённых значениях
var priorityValue = await GetUniSelectAttributeValue(priorityAttributeName);
var tagValue = await GetTagAttributeValue(tagAttributeName); // текущий воркайтем
if (priorityValue.Value.Name == "Высокий" && tagValue.Value.Any(a => a.Name == "UX")) {
    await UpdateWorkitemAssignee(userId);  // текущему воркайтему!
}

// Автоматически рассчитать стоимость задачи и записать результат в атрибут "Общая стоимость", перемножив значения атрибутов "Оценка в часах" и "Стоимость часа".
NumberFieldValueModel attr1 = await GetNumberAttributeValue("Оценка в часах");
NumberFieldValueModel attr2 = await GetNumberAttributeValue("Стоимость часа");
if(attr1.Value != null && attr2.Value != null)
{
    var val3 = attr1.Value * attr2.Value;
    await UpdateNumberAttributeValue("Общая стоимость", val3);
}

// Автоматически рассчитать сумму атрибутов “Оценка” во всех дочерних задачах и заполнить рассчитанным значением атрибута “Оценка“ в родительской задаче.
var tree = await GetWorkitemTree();
List<WorkitemModel> sons = tree.GetChildren();
var result = 0.0;
foreach (var son in sons)
{
    var attribute = son.Attributes.Single(a => a.NumberFieldValueModel.Name == "Оценка");
    result += attribute.NumberFieldValueModel.Value ?? 0;
}

await UpdateNumberAttributeValue("Оценка", result);

Примеры создания скриптов напрямую

// Пример обёртки создания пространства
public async Task<WorkspaceModel> CreateWorkspace(string name, string key, string? description = null)
{
   var response = await Latest
    .Workspaces
    .PostAsync(new CreateWorkspaceRequestBody
    {
        Key = key,
        Name = name,
        Description = description,
    });

   if (response is null)
   {
    throw new OperationFailedException($"Failed to create the workspace with name [{name}] and key [{key}]");
   }

   return response;
}

// создание задачи (при этом заранее создаём объект задачи в body)
public async Task<WorkitemModel> CreateWorkitem(CreateWorkitemRequestBody body, string? workspaceKey = null)
{
    CheckWorkspaceKey(ref workspaceKey);

    return await Latest
    .Workspaces[workspaceKey]
    .Workitems
    .PostAsync(body);
}

// создание комментария к задаче
public async Task<CommentModel> CreateWorkitemComment(string text, string? workitemKey = null, string? workspaceKey = null)
{
    CheckWorkspaceKey(ref workspaceKey);
    CheckWorkitemKey(ref workitemKey);

    var body = new CreateCommentRequestBody { Text = text };

    return await Latest
    .Workspaces[workspaceKey]
    .Workitems[workitemKey]
    .Comments
    .PostAsync(body);
}

// получение пространств
public async Task<List<WorkspaceModel>?> GetWorkspaces()
{
    var response = await Latest
        .Workspaces
        .GetAsync();
    return response?.Items;
}

// Получение модели папки
public async Task<FolderModel?> GetFolder(Guid folderId, string? workspaceKey = null)
{
   CheckWorkspaceKey(ref workspaceKey);

   return await Latest
    .Workspaces[workspaceKey]
    .Folders[folderId]
    .GetAsync();
}

// Получение статусов
public async Task<List<StatusModel>?> GetStatuses(string? workspaceKey = null)
{
   CheckWorkspaceKey(ref workspaceKey);

   var response = await Latest
    .Workspaces[workspaceKey]
    .Statuses
    .GetAsync();
   return response?.Items;
}

// получение атрибута
public async Task<AttributeModel?> GetAttribute(string attributeName, string? workspaceKey = null)
{
    CheckWorkspaceKey(ref workspaceKey);

    var response = await Latest
        .Workspaces[workspaceKey]
        .Attributes
        .GetAsync(x =>
           x.QueryParameters.Name = attributeName);
    return response?.Items?.FirstOrDefault();
}

// Получение значений атрибутов указанной задачи:
AttributeValueModelList? response = await Latest
    .Workspaces[workspaceKey]
    .Workitems[workitemKey]
    .Attributes
    .GetAsync();
return response;

// Изменение имени задачи:
var body = new PatchWorkitemRequestBody { Name = name };
var patchResponse = await Latest
    .Workspaces[workspaceKey]
    .Workitems[workitemKey]
    .PatchAsync(body);

// изменение значения атрибута типа "Время" для указанной задачи
public async Task<TimeFieldValueModel?> UpdateTimeAttributeValue(Guid attributeId, int? seconds,
    string? workitemKey = null, string? workspaceKey = null)
{
    CheckWorkspaceKey(ref workspaceKey);
    CheckWorkitemKey(ref workitemKey);

    var body = new WithAttributeItemRequestBuilder.WithAttributePutRequestBody
    {
    UpdateTimeFieldRequestBody = new UpdateTimeFieldRequestBody
    {
        Type = AttributeType.TimeDuration,
        Value = seconds,
    },
    };

    var response = await Latest
     .Workspaces[workspaceKey]
     .Workitems[workitemKey]
     .Attributes[attributeId]
     .PutAsync(body);

    return response?.TimeFieldValueModel;
}

// Изменение портфолио (по факту - просто имени):
public async Task<PortfolioModel> UpdatePortfolio(Guid portfolioId, string name, string? workspaceKey = null)
{
    CheckWorkspaceKey(ref workspaceKey);

    var patchResponse = await Latest
    .Workspaces[workspaceKey]
    .Portfolios[portfolioId]
    .PatchAsync(new PatchPortfolioRequestBody
        {
        Name = name,
        });

    if (patchResponse is null)
    {
        throw new OperationFailedException($"Failed to update the portfolio in workspace {workspaceKey}");
    }

    return patchResponse;
}

// добавить роль пользователю:
public async Task AddUserRole(Guid userId, Guid roleId, string? workspaceKey = null)
{
    CheckWorkspaceKey(ref workspaceKey);

    await Latest
    .Workspaces[workspaceKey]
    .Users[userId]
    .Roles[roleId]
    .PostAsync();
}

// Удаление атрибута:
public async Task DeleteAttribute(Guid attributeId, string? workspaceKey = null)
{
   CheckWorkspaceKey(ref workspaceKey);

   await Latest
    .Workspaces[workspaceKey]
    .Attributes[attributeId]
    .DeleteAsync();
}