Заметки консультанта

Шамрай Александр Владимирович

Posts Tagged ‘tfs’

Azure DevOps Rest Api. 12. Просмотр содержимого плана тестирования

Posted by Shamrai Alexander на Апрель 23, 2019

<< Перейти в радел «Azure DevOps Services (TFS/VSTS) Rest Api»

Примечание: На основе Microsoft.TeamFoundationServer.Client 16.150.0-preview

Для работы с объектами тестирования используется клиент TestPlanHttpClient (пространство имен Microsoft.VisualStudio.Services.TestManagement.TestPlanning.WebApi). В рамках планирования тестирования используются следующие основные объекты:

  1. План тестирования – класс TestPlan. Он содержит в основном информацию о названии, планируемых датах тестирования и т.д.
  2. Набор тестов – класс TestSuite. Содержит дочерние наборы, а также сценарии тестирования. Наборы тестов могут быть статическими, наборам для требований (т.е. ссылаются на конкретное требование) и динамическими (на основе запроса по рабочим элементам).
  3. Тестовый сценарий – класс TestCase. Содержит информацию о рабочем элементе тестового сценария, тестировщике и конфигурации для тестирования.

Для того, чтобы получить информацию о всех объектах тестирования, необходимо:

  1. Получить план тестирования.
  2. Получить дерево наборов тестов плана тестирования.
  3. Получить содержимое набора тестов, т.е. тестовые сценарии.

Для получения тестового сценария используется метод GetTestPlanByIdAsync с использованием имени командного проекта и идентификатора. Идентификатор можно увидеть через интерфейс:

Рисунок 1. Редактирование плана тестирование

Рисунок 2. Идентификатор плана тестирования

Набор планов тестирования с использованием имени командного проекта можно получить через метод GetTestPlansAsync, который вернет список List планов тестирования.

Пример получения плана тестирования и его свойств:

TestPlan testPlan = TestPlanClient.GetTestPlanByIdAsync(TeamProjectName, TestPlanId).Result;

Console.WriteLine(«================================================================»);

Console.WriteLine(«Test Plan : {0} : {1} : {2}», testPlan.Id, testPlan.State, testPlan.Name);

Console.WriteLine(«Area Path : {0} : Iteration Path : {1}», testPlan.AreaPath, testPlan.Iteration);

Console.WriteLine(«Plan Dates : {0} — {1}»,

(testPlan.StartDate.HasValue) ? testPlan.StartDate.Value.ToShortDateString() : «none»,

(testPlan.EndDate.HasValue) ? testPlan.EndDate.Value.ToShortDateString() : «none»);

Дерево наборов тестов для плана тестирования можно получить через метод GetTestSuitesForPlanAsync, который использует наименование проекта, идентификатор плана тестирования. Пример получения дерева набора тестов и просмотра его структуры:

List<TestSuite> suitesDetail = TestPlanClient.GetTestSuitesForPlanAsync(TeamProjectName, TestPlanId, asTreeView: true).Result;

ExploreTestSuiteTree(TeamProjectName, TestPlanId, suitesDetail, «»);

static
void ExploreTestSuiteTree(string TeamProjectName, int TestPlanId, List<TestSuite> SuitesSubTree, string ParentPath)

{

foreach (TestSuite testSuite in SuitesSubTree)

{

PrintSuiteInfo(testSuite, ParentPath);

if (testSuite.HasChildren) ExploreTestSuiteTree(TeamProjectName, TestPlanId, testSuite.Children, ParentPath + «\\» + testSuite.Name);

}

}

static
void PrintSuiteInfo(TestSuite Suite, string ParentPath)

{

Console.WriteLine(«================================================================»);

Console.WriteLine(«Test Suite : {0} : {1}», Suite.Id, Suite.Name);

Console.WriteLine(«Suite Type : {0} : {1}», Suite.SuiteType,

(Suite.SuiteType == TestSuiteType.StaticTestSuite) ? «» :

(Suite.SuiteType == TestSuiteType.DynamicTestSuite) ? «\nQuery: « + Suite.QueryString : «Requirement ID « + Suite.RequirementId.ToString());


if (Suite.ParentSuite == null) Console.WriteLine(«This is a root suite»);


else Console.WriteLine(«Parent Path: « + ParentPath);

Console.WriteLine(«—————————————————————-«);

}

Список тестов в наборе тестов можно получить через метод GetTestCaseListAsync, который использует наименование проекта, идентификатор плана тестирования и идентификатор набора тестирования. Кроме этого, тестовый сценарий содержит идентификатор рабочего элемента и его поля. Пример получения списка тестов и свойств рабочих элементов:

List<TestCase> testCases = TestPlanClient.GetTestCaseListAsync(TeamProjectName, TestPlanId, testSuite.Id).Result;

if (testCases.Count > 0)

{

foreach (TestCase testCase in testCases)

{

Console.WriteLine(«Test: {0} — {1}», testCase.workItem.Id, testCase.workItem.Name);

var wiFields = GetWorkItemFields(testCase.workItem.WorkItemFields);

if (wiFields.ContainsKey(«System.State»))

Console.WriteLine(«Test Case State: {0}», wiFields[«System.State»].ToString());


foreach (var config in testCase.PointAssignments)

Console.WriteLine(«Run for: {0} : {1}», config.Tester.DisplayName, config.ConfigurationName);

}

}

private
static Dictionary<string, object> GetWorkItemFields(List<object> WorkItemFieldsList)

{

Dictionary<string, object> wiFields = new Dictionary<string, object>();


foreach (object wiField in WorkItemFieldsList)

{

Dictionary<string, object> fld = JsonConvert.DeserializeObject<Dictionary<string, object>>(wiField.ToString());

wiFields.Add(fld.Keys.ElementAt(0), fld[fld.Keys.ElementAt(0)]);

}

return wiFields;

}

Пример тестового приложения можно посмотреть здесь:

https://github.com/ashamrai/TFRestApi/tree/master/12.TFRestApiAppTestPlanDelails

Posted in Microsoft, Team Foundation Server, Team Foundation Server FAQ, Visual Studio, visual studio team services | Отмечено: , , , | Leave a Comment »

Azure DevOps Rest Api. 19. Выполнение сборки и получение результатов

Posted by Shamrai Alexander на Апрель 22, 2019

Для управление сборками используется клиент BuildHttpClient.

Выполнение сборки

Для запуска сборки используется метод QueueBuildAsync, который принимает экземпляр класса Build на вход. Для создания нового объекта класса Build используются два основных параметра:

  • Definition – определение сборки, которые мы получаем через метод GetDefinitionAsync.
  • Project – командные проект, который мы получаем через метод GetProject клиента ProjectHttpClient.

Пример:

var buildDefinition = BuildClient.GetDefinitionAsync(TeamProjectName, BuildDefId).Result;

var teamProject = ProjectClient.GetProject(TeamProjectName).Result;

BuildClient.QueueBuildAsync(new Build() { Definition = buildDefinition, Project = teamProject }).Wait();

Собственно, это все, что нужно для запуска сборки. Метод QueueBuildAsync возвращает экземпляр класса Build, который содержит идентификатор сборки для дальнейших операций с ней. Например, для получения текущей информации о сборке можно использовать метод GetBuildAsync, который вернет все тот же класс Build со следующими полезными атрибутами:

  • Id – идентификатор сборки
  • BuildNumber – номер сборки
  • Status – состояние сборки
  • Definition – определение сборки

Просмотр задач сборки

Если есть необходимость просмотреть состав задач и детализацию их выполнения, то для этого можно воспользоваться методом GetBuildTimelineAsync с параметрами имя проекта и идентификатор сборки. Метод вернет экземпляр класса Timeline со списком TimelineRecord, который включает в себя следующие атрибуты:

  • RecordType – тип записи. Нас интересует значение Task.
  • Name – имя задачи.
  • StartTime – дата и время запуска задачи.
  • FinishTime – дата и время завершения задачи.
  • Result – результат выполнения задачи. Список значений доступен в перечисленииTaskResult.

Пример:

var timeline = BuildClient.GetBuildTimelineAsync(TeamProjectName, BuildId).Result;

if (timeline.Records.Count > 0)

{

     Console.WriteLine(«Task Name——————————Start Time—Finish Time—Result»);

     foreach(var record in timeline.Records)

          if (record.RecordType == «Task»)

               Console.WriteLine(«{0, -35} | {1, -10} | {2, -10} | {3}»,

               (record.Name.Length < 35) ? record.Name : record.Name.Substring(0, 35),

               (record.StartTime.HasValue) ? record.StartTime.Value.ToLongTimeString() : «»,

               (record.FinishTime.HasValue) ? record.FinishTime.Value.ToLongTimeString() : «»,

               (record.Result.HasValue) ? record.Result.Value.ToString() : «»);

}

Скачивание результатов сборки

Когда нужно получить результаты сборки в виде определенного артефакта, то для этого можно воспользоваться следующими методами:

  • GetArtifactAsync – получить информацию об артефакте со следующими полезными атрибутами:
    • BuildNumber – номер сборки.
    • Resource.DownloadUrl – ссылка для скачивания результатов.
  • GetArtifactContentZipAsync – возвращает поток архива артефакта.

При этом оба метода принимают два параметра: идентификатор сборки и имя артефакта, которое обычно имеет значение drop.

Пример:

BuildArtifact drop = BuildClient.GetArtifactAsync(StartedBuild.Id, ArtifactName).Result;

string dropFileName = String.Format(«{0}_{1}.zip», StartedBuild.Definition.Name, StartedBuild.BuildNumber);

Stream zipStream = BuildClient.GetArtifactContentZipAsync(StartedBuild.Id, ArtifactName).Result;

using (FileStream zipFile = new FileStream(dropFileName, FileMode.Create))

zipStream.CopyTo(zipFile);

Пример тестового приложения можно посмотреть здесь:

https://github.com/ashamrai/TFRestApi/tree/master/19.TFRestApiAppQueueBuild

Posted in Microsoft, Team Foundation Server, Team Foundation Server FAQ, Visual Studio, visual studio team services | Отмечено: , , , , | Leave a Comment »

Azure DevOps Services Rest Api. 18. Создание и клонирование определения сборки

Posted by Shamrai Alexander на Апрель 9, 2019

В рамках данной статьи мы рассмотрим созданием и клонирование определений сборки для yaml формата и графического мастера. В общем их структура одинаковая, но они отличаются только параметром, который характеризует шаги выполнения сборки. В yaml формате это ссылка на соответствующий файл в версионном контроле, а для графического мастера – детальное определение фаз и шагов выполнения. Основным хранилищем версионного контроля будет выступать внутренний GIT. Для управления сборками используется клиент BuildHttpClient.

Создание определения сборки

Создание определения сборки мы рассмотрим на примере yaml формата, т.к. он проще для реализации. В данном случае можно создать типовой файл с описанием его шагов, а такие элементы как путь к решению, конфигурация и т.д. вынести в параметры и определять в процессе создания. Перед созданием сборки необходимо сформировать yaml файл. С нуля его можно написать, но проще подсмотреть в существующей сборки. Увидеть yaml шаги можно следующим образом:

  1. Открыть на редактирование существующее определение сборки.
  2. Выбрать фазу выполнения на агенте и нажать на ссылку View YAML:

Для определения сборки используется класс BuildDefinition, который содержит следующие основные атрибуты:

  • Path – путь или каталог, в котором будет храниться определение сборки.
  • Name – наименование определения сборки.
  • Queue – пул через который будет запускаться новая сборка. Определяется через класс AgentPoolQueue, в котором указывается наименование пула.
  • Process – описание процесса выполнения шагов. Для yaml формата используется класс YamlProcess, в котором через атрибут YamlFilename указывается путь к yaml файлу в версионном хранилище.
  • Repository – описывает используемое хранилище исходного кода. Описывается классом BuildRepository, который включает в себя:
    • Url – url для клонирования репозитория. Посмотреть его можно на основе следующих шагов: Clone the repo to your computer
    • Name – имя репозитория
    • Type – тип репозитория, который описывается через статический класс RepositoryTypes.
  • Variables – переменные, которыми определяем путь к решению, конфигурацию и платформу сборки.

Далее запускается метод CreateDefinitionAsync, в который передается наименование командного проекта и сформированное определение сборки. Пример создания:

BuildDefinition newBuild = new BuildDefinition();
newBuild.Path = BuildPath;

newBuild.Name = BuildName;

newBuild.Queue = new AgentPoolQueue() { Name = «Hosted VS2017» };

YamlProcess yamlProcess = new YamlProcess();

yamlProcess.YamlFilename = «<yaml file>»;

newBuild.Process = yamlProcess;

newBuild.Repository = new BuildRepository();

newBuild.Repository.Url = new Uri(String.Format(GitRepoFormat, TeamProjectName, GitRepoName));

newBuild.Repository.Name = GitRepoName;

newBuild.Repository.DefaultBranch = RepoBranch;

newBuild.Repository.Type = RepositoryTypes.TfsGit;

newBuild.Variables.Add(«BuildConfiguration», new BuildDefinitionVariable { AllowOverride = false, IsSecret = false, Value = «Debug» });

newBuild.Variables.Add(«BuildPlatform», new BuildDefinitionVariable { AllowOverride = false, IsSecret = false, Value = «Any CPU» });

newBuild.Variables.Add(«Parameters.solution», new BuildDefinitionVariable { AllowOverride = false, IsSecret = false, Value = SlnPath });

newBuild = BuildClient.CreateDefinitionAsync(newBuild, TeamProjectName).Result;

Клонирование определения сборки

Клонирование сборки рассмотрим на примере определений в графическом мастере. Т.к. для создания данных определений необходимо описывать процесс через класс DesignerProcess, что может быть длительным набором действий. Проще создать типовое определение сборки и в дальнейшем выполнять ее клонирование с подменой необходимых параметров. Т.е. выполняться будут следующие шаги:

  1. Получение существующего определения сборки.
  2. Обновление необходимых параметров.
  3. Создание новой сборки.

Для получения существующего определения сборки используется метод GetDefinitionAsync, который принимает на вход имя командного проекта и идентификатор определения сборки. Идентификатор определения сборки можно увидеть в строке браузера, когда выполняется просмотр деталей определения сборки:

Далее необходимо изменить необходимые параметры в полученной сборке, например:

  • Repository – описывает используемое хранилище исходного кода. Описывается классом BuildRepository, который включает в себя:
    • Url – url для клонирования репозитория. Посмотреть его можно на основе следующих шагов: Clone the repo to your computer
    • Name – имя репозитория
    • DefaultBranch – наименование ветви репозитория.
  • Path – путь к новому определению сборки.
  • Name – наименование нового определения сборки.

Далее вызывается метод CreateDefinitionAsync:

var bld = BuildClient.GetDefinitionAsync(TeamProjectName, SourceBuildId).Result;
var clonedBuild = bld;

clonedBuild.Repository.Url = new Uri(String.Format(GitRepoFormat, TeamProjectName, GitRepoName));

clonedBuild.Repository.Name = GitRepoName;

clonedBuild.Repository.Id = null;

if (NewBranch != null) clonedBuild.Repository.DefaultBranch = NewBranch;

clonedBuild.Path = NewPath;

clonedBuild.Name = NewName;

if (NewProjectPath != null && clonedBuild.ProcessParameters.Inputs.Count == 1)

clonedBuild.ProcessParameters.Inputs[0].DefaultValue = NewProjectPath;

clonedBuild = BuildClient.CreateDefinitionAsync(clonedBuild, TeamProjectName).Result;

Пример тестового приложения можно посмотреть здесь:

https://github.com/ashamrai/TFRestApi/tree/master/18.TFRestApiAppCreateCloneBuild

Posted in Microsoft, Team Foundation Server, Visual Studio, visual studio team services | Отмечено: , , , , | Leave a Comment »

Azure DevOps Services Rest Api. 14. Создание и добавление тестовых сценариев

Posted by Shamrai Alexander на Февраль 28, 2019

<< Перейти в радел «Azure DevOps Services (TFS/VSTS) Rest Api»

Примечание: Устарело с версии 16.150.0-preview. Новые подходы описаны здесь.

Создание тестового сценария

Примечание: На момент написания статьи не существовало официальных задокументированных возможностей для создания тестовых сценариев через Rest Api. Ниже описанное о тестовых шагах получено опытным путем.

Клиент TestManagementHttpClient не содержит каких-либо методов для создания тестовых сценариев. Тестовые сценарии создаются как обычные рабочие элементы через клиент WorkItemTrackingHttpClient (см. Создание и редактирование рабочих элементов). Особенностью является формирование шагов тестирования и параметров для них.

Структура содержимого шагов тестирования в сценарии

Шаги тестирования содержатся в поле Microsoft.VSTS.TCM.Steps в следующем формате:

<steps id=»0″ last=»{LAST_STEP_ID}»>

<step type=»ActionStep» id=»{STEP_ID}»>

<parameterizedString isformatted=»true»>{ACTION_DESCRIPTION}</parameterizedString>

<parameterizedString isformatted=»true»></parameterizedString>

</step>

<step type=»ValidateStep» id=»{STEP_ID}»>

<parameterizedString isformatted=»true»>{ ACTION _DESCRIPTION}</parameterizedString>

<parameterizedString isformatted=»true»>{VALIDATION_DESCRIPTION}</parameterizedString>

</step>

</steps>

Тут можно выделить следующие атрибуты:

  1. {LAST_STEP_ID} – идентификатор последнего шага.
  2. type – тип шага. Если установлено значение ActionStep, то заполняется только содержимое для первой строки шага(parameterizedString), содержимое второй строки остается пустым. Если же установлено ValidateStep, то обе строки заполняются.
  3. {STEP_ID} – порядковый номер шага. Отсчет начинается с 2.
  4. {ACTION_DESCRIPTION} – описание действий в шаге.
  5. {VALIDATION_DESCRIPTION} – описание проверок правильного выполнения шага.

Структура содержимого параметров шагов тестирования

Если тестовый сценарий не использует локальные параметры, то этот пункт можно упустить. Описание параметров разделяется на две части:

  1. Описание набора параметров.
  2. Описание значений параметров.

Описание набора параметров

Набор параметров содержится в поле Microsoft.VSTS.TCM.Parameters в следующем виде:

<parameters>

<param name=»param1″ bind=»default»/>

<param name=»param2″ bind=»default»/>

</parameters>

Т.е. для каждого параметра в блоке parameters описывается строка <param name=»ИМЯ_ПАРАМЕТРА» bind=»default»/>.

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

Набор параметров содержится в поле Microsoft.VSTS.TCM.LocalDataSource в следующем виде:

<NewDataSet>

<xs:schema id=’NewDataSet’ xmlns:xs=’http://www.w3.org/2001/XMLSchema&#8217; xmlns:msdata=’urn:schemas-microsoft-com:xml-msdata’>

    <xs:element name=’NewDataSet’ msdata:IsDataSet=’true’ msdata:Locale=»>

        <xs:complexType>

<xs:choice minOccurs=’0′ maxOccurs = ‘unbounded’>

            <xs:element name=’Table1′>

<xs:complexType>

                <xs:sequence>

                    <xs:element name=’param1′ type=’xs:string’ minOccurs=’0′ />

                    <xs:element name=’param2′ type=’xs:string’ minOccurs=’0′ />

                </xs:sequence>

            </xs:complexType>

        </xs:element>

    </xs:choice>

</xs:complexType>

</xs:element>

</xs:schema>

<Table1><param1>value1</ param1>< param2>value2</ param2></Table1>

<Table1>< param1>value3</ param1>< param2>value4</ param2></Table1>

</NewDataSet>

Т.е. это описание набора данных с его содержимым. Для каждого нового параметра необходимо в разделе <xs:sequence> добавить новую строку с описанием параметра: <xs:element name=’ИМЯ_ПАРАМЕТРА’ type=’xs:string’ minOccurs=’0′ /> .

Далее внизу в таблице <Table1> прописываются значения для каждого из параметров. Сколько новых строк будет в таблице – столько итераций тестирования будет запускать мастер выполнения тестовых сценариев.

Пример генерирования содержимого тестовых шагов и параметров находится в файле в TestStepsHelper.cs тестового решения ниже.

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

Dictionary<string, object> fields = new Dictionary<string, object>();
LocalStepsDefinition stepsDefinition = new LocalStepsDefinition();

stepsDefinition.AddStep(«Run Application»);

stepsDefinition.AddStep(«Enter creds @user_name @user_password»);

stepsDefinition.AddStep(«Check available functions», «Functions for: @user_role»);

LocalTestParams testParams = new LocalTestParams();

testParams.AddParam(«user_name», new
string[] { «admin», «user», «manager» });

testParams.AddParam(«user_password», new
string[] { «admin_pswrd», «user_pswrd», «manager_pswrd» });

testParams.AddParam(«user_role», new
string[] { «Administrator», «Local User», «Shop Manager» });

fields.Add(«Title», «new test case»);

fields.Add(FieldSteps, stepsDefinition.StepsDefinitionStr);

fields.Add(FieldParameters, testParams.ParamDefinitionStr);

fields.Add(FieldDataSource, testParams.ParamDataSetStr);

CreateWorkItem(TeamProjectName, «Test Case», fields);

А выполнение шагов будет отображаться так:

Рисунок 1. Выполнение теста

Добавление тестового сценария в существующий набор тестов

Тестовые сценарии могут быть добавлены только в статический набор тестов или набор тестов для требования. Для добавления используется метод AddTestCasesToSuiteAsync клиента TestManagementHttpClient с параметрами: наименование командного проекта, идентификатор плана тестирования для новых тестовых сценариев, идентификатор набора тестов и строка с идентификаторами рабочих элементов типа Сценарий тестирования.

TestSuite testSuite = TestManagementClient.GetTestSuiteByIdAsync(TeamProjectName, TestPlanId, testSuiteId, 1).Result;
if (testSuite.SuiteType == «StaticTestSuite» || testSuite.SuiteType == «RequirementTestSuite»)

TestManagementClient.AddTestCasesToSuiteAsync(TeamProjectName, TestPlanId, testSuiteId, String.Join(«,», TestCasesIds)).Wait();

else

Console.WriteLine(«The Test Suite ‘» + StaticSuitePath + «‘ is not static or requiremet»);

Пример тестового приложения можно посмотреть здесь:

https://github.com/ashamrai/TFRestApi/tree/master/14.TFRestApiAppCreateAndAddTestCase

Posted in Microsoft, Team Foundation Server, Team Foundation Server FAQ, Visual Studio, visual studio team services | Отмечено: , , , , | Leave a Comment »

Azure DevOps Services Rest Api. 13. Создание плана тестирования и наборов тестов

Posted by Shamrai Alexander на Февраль 28, 2019

<< Перейти в радел «Azure DevOps Services (TFS/VSTS) Rest Api»

Примечание: Устарело с версии 16.150.0-preview. Новые подходы описаны здесь.

Создание плана тестирования

План тестирования создается с помощью метода CreateTestPlanAsync из клиента TestManagementHttpClient. Данный метод принимает только два параметра:

  • Объект класса PlanUpdateModel, который описывает основные параметры плана тестирования.
  • И имя командного проекта.

Класс PlanUpdateModel включает следующие атрибуты, которые интересуют нас при создании на данном этапе:

  • name – наименование нового плана тестирования
  • startDate – дата начала плана тестирования в виде строки
  • endDate – дата окончания плана тестирования в виде строки
  • area – путь области, который описывается классом ShallowReference
  • iteration – путь итерации

Все атрибуты довольно просты, кроме area. Получить данные для пути области мы можем с помощью методов, которые рассматривались в статье Управление областями и итерациями в командном проекте. Т.е. мы получаем объект класса WorkItemClassificationNode и передаем его атрибуты в новый объект класса ShallowReference:

  1. В атрибут Id передаем Identifier.
  2. В атрибут Url передаем соответствующий Url области.

Пример создания плана тестирования:

Microsoft.TeamFoundation.TestManagement.WebApi.ShallowReference areaRef = null;
if (AreaPath != «»)

{


var area = WitClient.GetClassificationNodeAsync(TeamProjectName, TreeStructureGroup.Areas, AreaPath).Result;

areaRef = new Microsoft.TeamFoundation.TestManagement.WebApi.ShallowReference() {

Id = area.Identifier.ToString(),

Name = TeamProjectName + «\\» + AreaPath,

Url = area.Url

};

}

if (IterationPath != «») IterationPath = TeamProjectName + «\\» + IterationPath;

PlanUpdateModel newPlanDef = new PlanUpdateModel(

name: TestPlanName,

startDate: (StartDate.HasValue) ? StartDate.Value.ToString(«o») : «»,

endDate: (FinishDate.HasValue) ? FinishDate.Value.ToString(«o») : «»,

area: areaRef,

iteration: IterationPath

);

TestManagementClient.CreateTestPlanAsync(newPlanDef, TeamProjectName).Wait();

Создание тестового набора

Создание тестового набора выполняется схожим методом с созданием плана тестирования, т.е. через отдельный объект, который описывает свойства нового набора тестов. Этот объект описывается классом SuiteCreateModel, который включает атрибуты:

  1. suiteType – тип набор тестирования, который может быть одним из:
    1. StaticTestSuite – статический набор тестов, который может включать дочерние наборы тестов и тестовые сценарии.
    2. DynamicTestSuite – динамический набор тестов, который содержит тесты, отобранные его запросом через атрибут queryString.
    3. RequirementTestSuite – набор тестов, который ассоциируется с рабочим элементов группы требований и может содержать только тестовые сценарии.
  2. name – наименование нового набора тестов.
  3. queryString – WIQL запрос для отбора тестов для динамического набора тестов.
  4. requirementIds – массив идентификаторов рабочих элементов группы требований, для которых будут созданы тестовые наборы с типом RequirementTestSuite.

Создание тестового набора выполняется с помощью метода CreateTestSuiteAsync, в который передаются:

  1. Экземпляр класса SuiteCreateModel.
  2. Наименование командного проекта.
  3. Идентификатор плана тестирования, в котором будет создан новый набор тестов.
  4. Идентификатор родительского набора тестов, который должен быть статическим. Если создаем в корне плана тестирования, то такой идентификатор можно найти в его атрибуте RootSuite.Id.

Прим создания набора тестов:

SuiteCreateModel newSuite = new SuiteCreateModel(TestSuiteType, TestSuiteName, SuiteQuery, RequirementIds);
List<TestSuite> testSuiteList = TestManagementClient.CreateTestSuiteAsync(newSuite, TeamProjectName, TestPlanId, parentsuiteId).Result;

Пример тестового приложения можно посмотреть здесь:

https://github.com/ashamrai/TFRestApi/tree/master/13.TFRestApiAppCreateTestPlanAndSuites

Posted in Microsoft, Team Foundation Server, Team Foundation Server FAQ, Visual Studio, visual studio team services | Отмечено: , , , , | Leave a Comment »

Автоматизация изменений состояний рабочих элементов Azure DevOps на основе состояний дочерних элементов

Posted by Shamrai Alexander на Февраль 19, 2019

На форумах часто встречается вопросы:

  1. Есть ли какой-то сервис для автоматического изменения состояния пользовательской истории, если отрыли любую дочернюю задачу.
  2. Как можно автоматически закрыть требование, если все дочерние задачи закрыты.

На сколько верно автоматизировать эти вопросы – это тема дискуссионная, и каждый сам для себя выбирает правильный ответ. Если все-таки есть желание автоматизировать изменение состояний, то для этого существуют отдельные сервисы, например, TFS Aggregator (Web Service) или другие кастомные сервисы.

Но т.к. задачи на самом деле с точки зрения разработки не очень сложные, то содержание отдельного сервиса может быть избыточным. Иногда можно обойтись небольшими задачами на основе библиотек для взаимодействия через REST API, которые будут выполняться через стандартный планировщик задач. Такой пример мы здесь и рассмотрим.

Тут будут следующие основные шаги:

  1. Подключение к сервису. Пример есть здесь: VSTS Rest Api. 1. Подключение к сервису.
  2. Выполнения запроса по рабочим элементам, чтобы иметь список интересующих нас «родителей». Пример выполнения запроса: Azure DevOps Services Rest Api. 4. Выполнение запросов по рабочим элементам
  3. Обновить состояние «родителям», которые удовлетворяют нашим условиям. Пример обновления: Azure DevOps Services Rest Api. 3. Создание и редактирование рабочих элементов

Изменение родителя, если любой дочерний элемент изменился

Примеры тут могут быть следующие:

  1. Открывается пользовательская история, если исполнитель отрыл дочернюю задачу.
  2. Открывается фича, если любая дочерняя пользовательская история открылась.

Это простой сценарий, т.к. нам нужно сделать запрос по рабочим элементам, который отберет пользовательские истории в состоянии New и с дочерними задачами в состоянии Active. Пример запроса в формате WIQL может быть следующий:

SELECT [System.Id] FROM WorkItemLinks WHERE ([Source].[System.TeamProject] = ‘Team project name’ AND [Source].[System.WorkItemType] = ‘User story’ AND [Source].[System.State] = ‘New’) And ([System.Links.LinkType] = ‘System.LinkTypes.Hierarchy-Forward’) And ([Target].[System.WorkItemType] = ‘Task’ AND [Target].[System.State] = ‘Active’) ORDER BY [System.Id] mode(MustContain)

Нас интересуют только идентификаторы рабочих элементов верхнего, которые в результате выполнения запроса не имеют участника Source. Пример обработки результата:

static List<int> GetParentIdsFromQueryResult(string pWiql)

{

Wiql _wiql = new Wiql { Query = pWiql };

WorkItemQueryResult result = WITClient.QueryByWiqlAsync(_wiql).Result;

if (result.WorkItemRelations != null)

return (from links in result.WorkItemRelations where links.Source == null select links.Target.Id).ToList();


return new List<int>();

}

Далее все найденные идентификаторы обновляем до активного состояния. При этом функция обновления проста, т.к. нам нужно обновить только одно поле:


static void UpdateWorkItemState(int parentId, string pDestinationState)

{

JsonPatchDocument patchDocument = new JsonPatchDocument();

patchDocument.Add(new JsonPatchOperation()

{

Operation = Operation.Add,

Path = «/fields/» + StateFieldName,

Value = pDestinationState

});

WorkItem _updatedWi = WITClient.UpdateWorkItemAsync(patchDocument, parentId).Result;

Console.WriteLine(«Work Item Has Been Updated:{0} — {1}», _updatedWi.Id, _updatedWi.Fields[«System.State»].ToString());

}

Изменение родителя, если все дочерние элементы в необходимом состоянии

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

В данном случае получение необходимых родителей схож с первым вариантом. Но нам необходимо знать: все ли дочерние задачи закрыты. Это одним запросом не выполнишь. Можно использовать два варианта:

  1. Получать родителя со всеми ссылками на дочерние элементы. Потом получать дочерние задачи, анализировать их состояние и принимать необходимое решение.
  2. Также можно выполнить два запроса для каждого родителя: количество всех дочерних задач и количество закрытых дочерних задач. Если они равны, то закрываем пользовательскую историю. Рассмотрим этот вариант.

Запросы для родительских пользовательских историй:

  1. Получить родителя и закрытые дочерние задачи:

SELECT[System.Id] FROM WorkItemLinks WHERE([Source].[System.TeamProject] = ‘Team project name’ AND [Source].[System.Id] = Parent_ID) And([System.Links.LinkType] = ‘System.LinkTypes.Hierarchy-Forward’) And ([Target].[System.WorkItemType] = ‘Task’ AND [Target].[System.State] = ‘Closed’) ORDER BY [System.Id] mode(MustContain)

  1. Получить родителя и все дочерние задачи

SELECT[System.Id] FROM WorkItemLinks WHERE([Source].[System.TeamProject] = ‘Team project name’ AND [Source].[System.Id] = Parent_ID) And ([System.Links.LinkType] = ‘System.LinkTypes.Hierarchy-Forward’) And ([Target].[System.WorkItemType] = ‘Task’) ORDER BY [System.Id] mode(MustContain)

Получить количество дочерних рабочих элементов из запроса можно следующим образом:


static int GetChildsCountFromQueryResult(string pWiql)

{

Wiql _wiql = new Wiql { Query = pWiql };

WorkItemQueryResult result = WITClient.QueryByWiqlAsync(_wiql).Result;


if (result.WorkItemRelations != null)


return (from links in result.WorkItemRelations where links.Source != null select links.Target.Id).Count();


return 0;

}

Затем сравниваем количество и обновляем тем же методом, как и в первом варианте.

Решение пример

Решение можно получить и скомпилировать отсюда: https://github.com/ashamrai/AzureDevOpsExtensions/tree/master/CustomNetTasks/UpdateParentState

Перед запуском приложения необходимо:

  1. Установить правильный адрес Azure DevOps сервиса или сервера в константу ServiceUrl.
  2. Сгенерировать Personal Access Token и вставить в константу PAT.
  3. Добавить в список _projects названия командных проектов для обработки: _projects.Add(«Yor team project name»);

Posted in Microsoft, Team Foundation Server, Visual Studio, visual studio team services | Отмечено: , , , , , | Leave a Comment »

Azure DevOps Services Rest Api. 12. Просмотр содержимого плана тестирования

Posted by Shamrai Alexander на Февраль 14, 2019

<< Перейти в радел «Azure DevOps Services (TFS/VSTS) Rest Api»

Примечание: Устарело с версии 16.150.0-preview. Новые подходы описаны здесь.

Для работы с объектами тестирования используется клиент TestManagementHttpClient. В рамках планирования тестирования используются следующие основные объекты:

  1. План тестирования – класс TestPlan. Он содержит в основном информацию о названии, планируемых датах тестирования и т.д.
  2. Набор тестов – класс TestSuite. Содержит дочерние наборы, а также сценарии тестирования. Наборы тестов могут быть статическими, наборам для требований (т.е. ссылаются на конкретное требование) и динамическими (на основе запроса по рабочим элементам).
  3. Тестовый сценарий – класс SuiteTestCase. Содержит информацию о рабочем элементе тестового сценария, тестировщике и конфигурации для тестирования.

Для того, чтобы получить информацию о всех объектах тестирования, необходимо:

  1. Получить план тестирования.
  2. Получить его основной набор тестов. Т.к. план тестирования кроме того, что имеет объект, который описывает его свойства, для него также сразу создается корневой набор тестов, который уже отражает содержимое плана тестирования на первом уровне.
  3. Получить содержимое набора тестов, т.е. дочерние наборы тестов и тестовые сценарии.

Для получения тестового плана используется метод GetPlanByIdAsync с использованием имени командного проекта и идентификатора. Идентификатор можно увидеть через интерфейс:

Рисунок 1. Редактирование плана тестирование

Рисунок 2. Идентификатор плана тестирования

Набор планов тестирования с использованием имени командного проекта можно получить через метод GetPlansAsync, который вернет список List планов тестирования.

Пример получения плана тестирования и его свойств:

TestPlan testPlan = TestManagementClient.GetPlanByIdAsync(TeamProjectName, TestPlanId).Result;
Console.WriteLine(«Test Plan : {0} : {1} : {2}», testPlan.Id, testPlan.State, testPlan.Name);
Console.WriteLine(«Area Path : {0} : Iteration Path : {1}», testPlan.Area.Name, testPlan.Iteration);

Console.WriteLine(«Plan Dates : {0} — {1}», testPlan.StartDate.ToShortDateString(), testPlan.EndDate.ToShortDateString());

Набор тестов можно получить через метод GetTestSuiteByIdAsync, который использует наименование проекта, идентификатор плана тестирования и идентификатор набора тесто, а также признак необходимости получения информации о дочерних объектах. Корневой набор тестов можно получить из свойств плана тестирования testPlan.RootSuite.Id. При этом существует метод GetTestSuitesForPlanAsync, который позволяет получить наборы тестов без необходимости получения корневого набора, но в рамках примера для обеспечения единого подхода, он не используется. Пример получения набора тестов:

TestSuite suiteDetail = TestManagementClient.GetTestSuiteByIdAsync(TeamProjectName, PlanId, suiteId, 1).Result;
Console.WriteLine(«Test Suite : {0} : {1} : {2}», suiteDetail.Id, suiteDetail.State, suiteDetail.Name);
//Sute Types: StaticTestSuite, RequirementTestSuite, DynamicTestSuite

Console.WriteLine(«Suite Type : {0} : {1}», suiteDetail.SuiteType,

(suiteDetail.SuiteType == «StaticTestSuite») ? «» :

(suiteDetail.SuiteType == «DynamicTestSuite») ? «\nQuery: « + suiteDetail.QueryString : «Requirement ID « + suiteDetail.RequirementId.ToString());

if (suiteDetail.Parent == null) Console.WriteLine(«This is a root suite»);

Информация о дочерних наборах тестов находится в suiteDetail.Suites, а информация о наличии тестовых сценариев в suiteDetail.TestCaseCount.

Список тестов в наборе тестов можно получить через метод GetTestCasesAsync, который использует наименование проекта, идентификатор плана тестирования и идентификатор набора тестирования. Пример получения списка тестов и их рабочих элементов:

List<SuiteTestCase> testCases = TestManagementClient.GetTestCasesAsync(TeamProjectName, PlanId, suiteId).Result;
foreach(SuiteTestCase testCase in testCases)
{

int testId = 0;

if (!int.TryParse(testCase.Workitem.Id, out testId)) continue;


WorkItem wi = WitClient.GetWorkItemAsync(testId).Result;


Console.WriteLine(«Test: {0} — {1}», wi.Id, wi.Fields[«System.Title»].ToString());

foreach(var config in testCase.PointAssignments)


Console.WriteLine(«Run for: {0} : {1}», config.Tester.DisplayName, config.Configuration.Name);

}

Пример тестового приложения можно посмотреть здесь:

https://github.com/ashamrai/TFRestApi/tree/master/12.TFRestApiAppTestPlanDelails

Posted in Microsoft, Team Foundation Server, Visual Studio, visual studio team services | Отмечено: , , , , , | Leave a Comment »

Создание собственной задачи сборки и релиза TFS для регистрации новых рабочих элементов на основе Node.JS

Posted by Shamrai Alexander на Февраль 14, 2019

Введение

На сегодняшний день доступно довольно много различных как стандартных, так и сторонних задач для шагов автоматизации сборки и развертывания релиза. Но иногда возникают потребности, которые не покрываются существующими решениями. Например, для релизов существуют некоторые встроенные возможности для утверждений развертывания и т.д., но иногда их не хватает. Т.е., если необходимо релиз «пропустить» через несколько отделов (тестирование, поддержка, безопасность), то требуется более чем один утверждающий. Это можно решить, например, динамическим созданием проверочных задач для соответствующих ответственных в зависимости от среды, где разворачивается релиз.

В рамках данного поста мы рассмотрим пример создания одного рабочего элемента для демонстрации возможностей. Данный пример может быть доработан для создания группы связанных задач.

Исходный код решения

Полный пример исходного кода решения находится здесь: https://github.com/ashamrai/AzureDevOpsExtensions/tree/master/CustomBuildTask

Решение подготовлено с помощью Visual Studio Code.

Предварительные настройки

В качестве подготовки рекомендуется выполнить следующие шаги:

  1. Развернуть пакет TFS Cross Platform Command Line Interface через команду npm i -g tfx-cli.
  2. Создать каталог, в котором будет находится все решение (Каталог решения).
  3. Создать каталог, в котором будет находится исходный код для создания рабочего элемента (Каталог исходного кода).
  4. Перейти в каталог исходного кода и выполнить следующие действия:
    1. Выполнить команду npm init, которая создаст файл package.json.
    2. Добавить библиотеки azure-pipelines-task-lib через команду npm install azure-pipelines-task-lib –save.
    3. Добавить внешние библиотеки через команды npm install @types/node —save-dev и npm install @types/q —save-dev.
    4. Добавить решение для создания запросов к сервису через команду npm install xmlhttprequest –save.
    5. Добавить решение для кодирования личного ключа доступа к сервису npm install buffer —save.
    6. Создать файл настройки компиляции tsconfig.json через команду tsc –init. В данном файле устанавливаем целевую версию typescript «target»: «es6».

Создание расширения

Файл описания задачи

Перед написанием основного исходного кода, необходимо сформировать файл описания задачи для определения сборки или релиза. Для этого необходимо создать файл task.json, который описывает задачу и ее параметры. Содержимое файла приблизительно следующее:

{

«id»: «c6ff84bf-0260-47d6-889d-6c2ecbbe2aa5»,

«name»: «CreateWorkItemCustomTask»,

«friendlyName»: «Create New Work Item Custom Task»,

«description»: «Create a new work item and link it to a new build or release»,

«helpMarkDown»: «»,

«category»: «Utility»,

«author»: «Aleksandr Shamrai»,

«version»: {

«Major»: 0,

«Minor»: 1,

«Patch»: 32

},

«instanceNameFormat»: «New Work Item $(workitemtype) $(titletemplate)»,

«inputs»: [

{

«name»: «workitemtype»,

«type»: «pickList»,

«label»: «Worm Item Type»,

«defaultValue»: «Task»,

«required»: true,

«helpMarkDown»: «Select the type for new work item»,

«options»: {

«Task»: «Task»,

«Bug»: «Bug»

}

},

{

«name»: «titletemplate»,

«type»: «string»,

«label»: «Title Template»,

«defaultValue»: «»,

«required»: false,

«helpMarkDown»: «A title template»

},

{

«name»: «assignedto»,

«type»: «string»,

«label»: «Assigne to»,

«defaultValue»: «»,

«required»: false,

«helpMarkDown»: «Set an owner of a work item»

},

{

«name»: «linktobuild»,

«type»: «radio»,

«label»: «Link New Work Item to Build»,

«defaultValue»: «No»,

«required»: true,

«helpMarkDown»: «Link a new work item to build»,

«options»: {

«No»: «No»,

«LinkAsArtifact»: «Link as artifact (only for builds)»,

«LinkAsWebUrl»: «Link as Web Url»

}

}

],

«execution»: {

«Node»: {

«target»: «index.js»

}

}

}

Полное описание формата файла можно найти здесь: https://github.com/Microsoft/azure-pipelines-task-lib/blob/master/tasks.schema.json

В нашем случае основные пункты:

  1. Id – уникальный идентификатор
  2. Name – наименование задачи без пробелов.
  3. friendlyName – описательное наименование задачи
  4. category – категория, в которой мы найдем нашу задачу
  5. author – автор задачи
  6. instanceNameFormat – как данный шаг будет отражаться в списке задач сборки или релиза.
  7. Inputs – список входных параметров для задачи. Каждый параметр имеет следующие основные элементы:
    1. Name – наименование параметра
    2. Type – тип параметра (boolean, filePath, multiLine, pickList, radio, secureFile, string).
    3. Label – название параметра на форме задачи
  8. Execution – описание того, что нужно выполнять. В данном случае будет выполняться файл index.js, который создается ниже.

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

Создадим файл index.ts, который будет содержать основной код. В данном файле можно выделить следующие моменты:

  • Как основная запускаемая функция используется функция run, которая детализируется под конкретные задачи.
  • Для получения входных параметров задачи, которые описаны в «task.json», используется метод getInput с передачей наименования параметра.
  • Для получения переменных окружения используется метод getVariable. Все переменные окружения, которые присутствуют при выполнении задачи можно посмотреть здесь: Default and custom release variables and debugging. При этом в рамках задачи используются следующие важные переменные:
    • System.AccessToken – содержит персональный ключ доступа к сервису. При этом доступ к нему обеспечивается отдельно в самом определении сборки или релиза в параметре Allow scripts to access the OAuth token. Если данный параметр не будет выставлен, то авторизация будет невозможна.
    • System.TeamFoundationCollectionUri – адрес сервера TFS.
    • System.TeamProject – командный проект, в котором выполняется сборки или развертывается релиз.
    • И т.д.

Пример получение значения параметров и переменных окружения:

const inputWIType: string = tl.getInput(‘workitemtype’, true);

const inputTitle: string = tl.getInput(‘titletemplate’, false);

const inputAssignedTo: string = tl.getInput(‘assignedto’, false);

const inputLinkToBuild: string = tl.getInput(‘linktobuild’, true);

const strAC = tl.getVariable(‘System.AccessToken’);

if (strAC == null)

{

tl.setResult(tl.TaskResult.Failed, ‘Access to OAuth token is not allowed’);

return;

}

const strServiceUrl = tl.getVariable(‘System.TeamFoundationCollectionUri’);

const strTeamProjectName = tl.getVariable(‘System.TeamProject’);

  • Создание рабочего элемента выполняется на основе запроса Rest Api Work Items – Create. Код для выполнения запроса следующий:
let devRequest = new XMLHttpRequest();

let encodedData = new Buffer(«:» + strAC).toString(‘base64’);

devRequest.open(«POST», strServiceUrl + strTeamProjectName + urlCreateTask)

devRequest.setRequestHeader(‘Authorization’, ‘Basic ‘ + encodedData);

devRequest.setRequestHeader(‘Accept’, ‘application/json’);

devRequest.setRequestHeader(‘Content-Type’, ‘application/json-patch+json’);

devRequest.send(JSON.stringify(patchFields));

devRequest.onreadystatechange = function () {

if (devRequest.readyState === 4) {

let newWorkItem = JSON.parse(devRequest.responseText);

if («id» in newWorkItem)

console.log(«The work item has been created», newWorkItem.id);

else

console.log(‘Error’,devRequest.responseText);

}

}

В данном примере создается запрос на основе авторизации Personal Access Token с использования адреса для создания нового рабочего элемента:

POST http://{server_name}:{port}/{collection name}/{project}/_apis/wit/workitems/${type}?api-version=2.0

В тело запроса в формате JSON добавляется список создаваемых полей и связей с другими объектами:

  • Для указания содержимого в поле применяется формат:
[

{

«op»: «add»,

«path»: «/fields/System.Title»,

«from»: null,

«value»: «Sample task»

}

]

  • Для создания новой гиперссылки используется формат:
[

{

«op»: «add»,

«path»: «/relations/-«,

«value»: {

«rel»: «Hyperlink»,

«url»: «URL»

}

}

]

  • Для создания ссылки на сборку используется следующий формат:
[

{

«op»: «add»,

«path»: «/relations/-«,

«value»: {

«rel»: «ArtifactLink»,

«url»: «artifact URI»,

«attributes»: {«name»:»Build»}

}

}

]

Компиляция решения

Для формирования файла index.js необходимо выполнить в каталоге исходного кода команду tsc.

Файл манифест расширения

Манифест расширения описывает расширение для TFS и создается через файл vss-extension.json. Файл содержит следующее:

{

«manifestVersion»: 1,

«id»: «new-work-item-custom-task»,

«name»: «Create New Work Item Custom Task»,

«version»: «0.1.32»,

«publisher»: «AShamrai»,

«targets»: [

{

«id»: «Microsoft.TeamFoundation.Server»

}

],

«description»: «Create a new work item and link it a build or release»,

«content»: {

«details»: {

«path»: «details.md»

}

},

«categories»: [

«Build and release»

],

«icons»: {

«default»: «images/newworkitem.png»

},

«files»: [

{

«path»: «NewWICustomTask»

}

],

«contributions»: [

{

«id»: «mew-work-tem-custom-build-release-task»,

«type»: «ms.vss-distributed-task.task»,

«targets»: [

«ms.vss-distributed-task.tasks»

],

«properties»: {

«name»: «NewWICustomTask»

}

}

]

}

Описание важных пунктов:

  • Id – уникальный идентификатор задачи
  • Targets:id – целевая платформа
  • Contributions:Id – уникальный идентификатор задачи
  • Сontributions :type – тип задачи
  • Contributions:targets – целевые задачи, в которых будет участвовать расширение.
  • Contributions:properties:name – наименование задачи, которое совпадает с каталогом, котором находятся скомпилированные исходники.
  • Files: path – путь к каталогу с содержимым задачи.

Полная спецификация содержимого находится по этому адресу: Extension manifest reference.

Сборка решения

Для сборки решения необходимо из каталога решения выполнить команду:

tfx extension create —manifest-globs vss-extension.json

Установка

Для развертывания решения можно выполнить следующие шаги:

  • Перейти на страницу управления расширениями Team Foundation Server. Она обычно находится по адресу http://server_name:8080/tfs/_gallery/manage. На этой странице необходимо нажать кнопку Upload new extension, найти сформированный после сборки решения файл расширения и выполнить его установку. Эта операция установит решение в сервер.

Рисунок 1. Страница управления расширениями

  • После того как появилось решение в списке, мы нажимаем на его ссылку и выполняем установку на необходимую коллекцию проектов.

Рисунок 2. Развертывание решения в необходимой коллекции

  • После развертывания решения мы можем приступить к созданию необходимого определения сборки или релиза.

Рисунок 3. Новая задача в списке утилит

  • При этом нужно помнить, что для того, чтобы решение могло взаимодействовать с сервером, необходимо дать ему возможность получить персональный код доступа, который используется агентом сборки для доступа к серверу.

Рисунок 4. Доступ к персональному ключу доступа

  • Результат можно увидеть в логах выполнения сборки или релиза, а также в виде созданного рабочего элемента.

Рисунок 5. Лог работы задачи

Рисунок 6. Новый рабочий элемент, который ссылается на сборку

Posted in Microsoft, Team Foundation Server, Visual Studio | Отмечено: , , , | Leave a Comment »

Azure DevOps Services Rest Api. 11. Перемещение рабочих элементов в другой командный проект

Posted by Shamrai Alexander на Январь 29, 2019

Перемещение рабочих элементов может понадобиться в различных случаях:

  1. Рабочий элемент не там изначально зарегистрировали.
  2. Для команды создали новый командный проект и необходимо забрать ее требования и ошибки.
  3. И т.д.

Сама операция переноса не очень сложная (Move a work item to another project), но если необходимо перенести тысячи рабочих элементов, то ручная работа займет много времени.

Для переноса рабочего элемента необходимо обновить всего три поля:

  1. Командный проект
  2. Путь итерации
  3. Путь области

В эти поля нужно установить наименование нового командного проекта. Пример будет базироваться на статьях Выполнение запросов по рабочим элементам и Создание и редактирование рабочих элементов.

Пример для обновления одного рабочего элемента очень прост:

static
int MoveWorkItem(int WIId, string NewTeamProject)

{

Dictionary<string, object> fields = new Dictionary<string, object>();

fields.Add(«System.TeamProject», NewTeamProject);

fields.Add(«System.AreaPath», NewTeamProject);

fields.Add(«System.IterationPath», NewTeamProject);


var editedWI = UpdateWorkItem(WIId, fields);

Console.WriteLine(«Work item has been moved: « + editedWI.Id.Value);


return editedWI.Id.Value;

}

static WorkItem UpdateWorkItem(int WIId, Dictionary<string, object> Fields)

{

JsonPatchDocument patchDocument = new JsonPatchDocument();


foreach (var key in Fields.Keys)

patchDocument.Add(new JsonPatchOperation()

{

Operation = Operation.Add,

Path = «/fields/» + key,

Value = Fields[key]

});


return WitClient.UpdateWorkItemAsync(patchDocument, WIId).Result;

}

Если же нам необходимо перенести большое количество рабочих элементов, то предварительно необходимо создать запрос по рабочим элементам, который выберет их все (Create and save managed queries with the query editor). Далее запрос можно выполнить и получить все идентификаторы и выполнить обновление каждого рабочего элемента:

List<int> wis = RunStoredQuery(teamProjectOld, queryPath);

foreach (int wiId in wis) MoveWorkItem(wiId, teamProjectNew);

В истории рабочего элемента будет соответствующая запись:

Пример тестового приложения можно посмотреть здесь:

https://github.com/ashamrai/TFRestApi/tree/master/11.TFRestApiAppMoveWorkItems

Posted in Microsoft, Team Foundation Server, Team Foundation Server FAQ, Visual Studio, visual studio team services | Отмечено: , , , , , | Leave a Comment »

Azure DevOps Services Rest Api. 10. Управление настройками команд

Posted by Shamrai Alexander на Декабрь 29, 2018

<< Перейти в радел «Azure DevOps Services (TFS/VSTS) Rest Api»

Настройки команды редактируются через клиент WorkHttpClient, который включает в себя как настройки команды, так и настройки журналов, досок, планов поставок, производительности команды и т.д. В рамках данной статьи мы рассмотрим только наиболее часто изменяемые настройки: путь итерации, путь области и используемые командой журналы.

Перед тем как приступить к работе с настройками команды, необходимо создать ее контекст. Для этого создается новый объект TeamContext, который инициализируется с двумя параметрами: имя проекта и имя команды. Если же нам необходимо получить контекст команды по умолчанию, то можно передавать только наименование проекта. Создание контекста для команды по умолчанию:

TeamContext teamContext = new TeamContext(TeamProjectName);

Итерации

Управление итерациями позволяет минимизировать трудозатраты администраторов команд. Если используются общие итерации во всех или многих командах, то их можно добавлять в автоматическом режиме сразу всем командам после добавления в командный проект. Также можно периодически очищать старые итерации, чтоб они не накапливались в настройках команды.

Перед добавлением итерации в команду, необходимо, чтобы она была уже создана в настройках проекта. Создание итерации рассматривалось в статье Управление областями и итерациями в командном проекте.

Для добавления итерации в команду используется метод PostTeamIterationAsync, который принимает в качестве аргументов TeamSettingsIteration и контекст команды. Для объекта TeamSettingsIteration важен только уникальный идентификатор итерации, который можно получить через метод GetClassificationNodeAsync класса WorkItemTrackingHttpClient. Пример добавления итерации:

WorkItemClassificationNode projectIt = WitClient.GetClassificationNodeAsync(TeamProjectName, TreeStructureGroup.Iterations, iterationName).Result; // get iteration from project

TeamSettingsIteration teamIt = WorkClient.PostTeamIterationAsync(new TeamSettingsIteration { Id = projectIt.Identifier }, teamContext).Result; //add iteration to a team by guid

Console.WriteLine(«Added iteration « + teamIt.Name);

Удаление итерации из настроек команды выполняется подобным методом через метод DeleteTeamIterationAsync, при этом передаются контекст команды и идентификатор итерации:

WorkItemClassificationNode projectIt = WitClient.GetClassificationNodeAsync(TeamProjectName, TreeStructureGroup.Iterations, iterationNameToRemove).Result;

WorkClient.DeleteTeamIterationAsync(teamContext, projectIt.Identifier).SyncResult(); //delete iteration from team settings by guid

Console.WriteLine(«Removed iteration « + projectIt.Name);

Просмотреть набор итераций, которые использует команда, можно через метод GetTeamIterationsAsync. Данный метод принимает в качестве параметров контекст команды и временной фрейм итерации (прошедшая, текущая и будущая). Пример выполнения метода:

TeamSettingsIteration currentiteration = (WorkClient.GetTeamIterationsAsync(teamContext, «Current»).Result).FirstOrDefault();

В результаты мы получаем объект TeamSettingsIteration, который включает:

  • Id – GUID итерации
  • Name – наименование итерации
  • Path – путь итерации
  • Attributes – дополнительные атрибуты итерации:
    • StartDate – дата начала итерации
    • FinishDate – дата окончания итерации
    • TimeFrame – фрейм итерации Past (прошедшая), Current (текущая), Future (будущая)

Пример использования:

List<TeamSettingsIteration> teamIterations = WorkClient.GetTeamIterationsAsync(teamContext).Result; //get all iterations

Console.WriteLine(«Team Iterations: «);

foreach (TeamSettingsIteration teamIteration in teamIterations)

Console.WriteLine(«{0} : {1} : {2}-{3}», teamIteration.Attributes.TimeFrame, teamIteration.Name, teamIteration.Attributes.StartDate, teamIteration.Attributes.FinishDate);

Области

Области имеет смысл автоматически обновлять, если основное управление ответственностями команд выполняется во внешней системе, например, через CMDB и их необходимо переносить в соответствующие команды Azure DevOps.

Как и с итерациями область должна быть сначала добавлена в настройки командного проекта (Управление областями и итерациями в командном проекте). Но оперирование областями в настройках команды отличается от управления итерациями.

Посмотреть список областей команды можно через метод GetTeamFieldValuesAsync, который вернет объект TeamFieldValues с атрибутами:

  • DefaultValue – строковое значение области по умолчанию.
  • Values – список областей TeamFieldValue, которые будет контролировать команда. TeamFieldValue включает наименование области Value и признак включения в контроль дочерних областей IncludeChildren.

Пример использования:

TeamFieldValues teamAreas = WorkClient.GetTeamFieldValuesAsync(teamContext).Result; //Get All Areas

Console.WriteLine(«Default Area: « + teamAreas.DefaultValue);

Console.WriteLine(«Team Areas : «);

foreach (TeamFieldValue teamField in teamAreas.Values)

Console.WriteLine(«\t» + teamField.Value + ((teamField.IncludeChildren)? » (include sub-areas)» : «»));

Обновление набора областей команды выполняется через один метод UpdateTeamFieldValuesAsync с параметром TeamFieldValuesPatch. TeamFieldValuesPatch включает те же атрибуты, что и TeamFieldValues. Т.е. для обновления настройки можно взять текущие значения и добавить или удалить необходимые:

string[] areas = { @»Application\WinClient», @»Application\WebClient» };

TeamFieldValues currentTeamAreas = WorkClient.GetTeamFieldValuesAsync(teamContext).Result; // get current areas

TeamFieldValuesPatch teamAreasPatch = new TeamFieldValuesPatch();

List<TeamFieldValue> newTeamAreas = new List<TeamFieldValue>(currentTeamAreas.Values); // just copy old areas. Here we may remove unneeded areas

foreach (string area in areas)

newTeamAreas.Add(new TeamFieldValue { Value = TeamProjectName + «\\» + area, IncludeChildren = false }); // add new areas

teamAreasPatch.DefaultValue = currentTeamAreas.DefaultValue;

teamAreasPatch.Values = newTeamAreas;

TeamFieldValues updatedTeamAreas = WorkClient.UpdateTeamFieldValuesAsync(teamAreasPatch, teamContext).Result;

Общие настройки

Изменение общих настроек редко когда может понадобиться через автоматизацию, возможно, если только основные журналы организации отличаются от настроек по умолчанию и их проще выполнить автоматически.

Получить текущие настройки можно через метод GetTeamSettingsAsync, который возвращает TeamSetting с атрибутами:

  • DefaultIteration – итерация, которая автоматически подставляется при создании нового рабочего элемента в контексте команды.
  • BacklogIteration – в рамках какой итерации отображаются рабочие элементы журнала требований.
  • DefaultIterationMacro – подстановка для определения текущей итерации: https://docs.microsoft.com/ru-ru/azure/devops/boards/queries/query-by-date-or-current-iteration?view=vsts#query-for-items-based-on-belonging-to-a-teams-current-iteration
  • BacklogVisibilities – список всех журналов, которые используются в проекте, и их отображение в команде.
  • WorkingDays – список дней, которые учитываются как рабочие для команды.

Пример выполнения:

TeamContext teamContext = new TeamContext(TeamProjectName, TeamName);

TeamSetting teamSetting = WorkClient.GetTeamSettingsAsync(teamContext).Result;

Console.WriteLine(«Settings for the team « + TeamName);

Console.WriteLine(«Backlog Iteration : « + teamSetting.BacklogIteration.Name);

Console.WriteLine(«Default Iteration : « + teamSetting.DefaultIteration.Name);

Console.WriteLine(«Macro of Iteration : « + teamSetting.DefaultIterationMacro);

Console.WriteLine(«Categories of backlog:»);

foreach(string bkey in teamSetting.BacklogVisibilities.Keys)


if (teamSetting.BacklogVisibilities[bkey]) Console.WriteLine(«\t» + bkey);

Console.WriteLine(«Working days :»);

foreach (var wday in teamSetting.WorkingDays) Console.WriteLine(«\t» + wday.ToString());

switch (teamSetting.BugsBehavior)

{

case BugsBehavior.AsRequirements:

Console.WriteLine(«Bugs Behavior: Bugs in a requirements backlog.»);

break;

case BugsBehavior.AsTasks:

Console.WriteLine(«Bugs Behavior: Bugs in a sprint backlog as tasks.»);

break;

case BugsBehavior.Off:

Console.WriteLine(«Bugs Behavior: Find bugs through queries.»);

break;

}

Обновление настроек выполняется через метод UpdateTeamSettingsAsync с параметром TeamSettingsPatch, который имеет те же атрибуты, как и TeamSetting. При этом будут обновляться только те настройки, атрибуты которых были проинициализированы. Обновление видимых команде журналов можно выполнить следующим образом:

TeamSetting teamSetting = WorkClient.GetTeamSettingsAsync(teamContext).Result;

TeamSettingsPatch teamSettingsPatch = new TeamSettingsPatch();

teamSettingsPatch.BacklogVisibilities = teamSetting.BacklogVisibilities;

if (teamSettingsPatch.BacklogVisibilities.ContainsKey(«Microsoft.EpicCategory») && teamSettingsPatch.BacklogVisibilities[«Microsoft.EpicCategory»])

teamSettingsPatch.BacklogVisibilities[«Microsoft.EpicCategory»] = false;

if (teamSettingsPatch.BacklogVisibilities.ContainsKey(«Microsoft.FeatureCategory») && teamSettingsPatch.BacklogVisibilities[«Microsoft.FeatureCategory»])

teamSettingsPatch.BacklogVisibilities[«Microsoft.FeatureCategory»] = false;

teamSetting = WorkClient.UpdateTeamSettingsAsync(teamSettingsPatch, teamContext).Result;

Пример тестового приложения можно посмотреть здесь:

https://github.com/ashamrai/TFRestApi/tree/master/10.TFRestApiAppManageTeamSettings

English version

Posted in Microsoft, Team Foundation Server, Team Foundation Server FAQ, Visual Studio, visual studio team services | Отмечено: , , , , | Leave a Comment »

 
%d такие блоггеры, как: