Skip to content
Ivan Braiko edited this page May 31, 2022 · 5 revisions

This library provides a convenient interface for getting data from your TeamCity buildserver.

Install

Package manager:
Install-Package TeamCityAPI -Version 1.0.0
.NET CLI:
dotnet add package TeamCityAPI --version 1.0.0

NuGet: https://www.nuget.org/packages/TeamCityAPI/1.0.0

Basics

TeamCityClient

Create TeamCityClient using your server address:

var client = TeamCityClient("https://buildserver.mycompany.net");

Log in using a token or username and password:

client.UseToken("myToken");
client.UseLoginAndPass("myLogin", "myPassword");

Models

TeamCityClient contains properties, accessing which you can build requests to TeamCity and receive models:

var query = client.Builds;
Builds res = await query.GetAsync();

The GetAsync method returns the Builds entity, which we would get by making a GET request https://buildserver.mycompany.net/app/rest/builds

All models

Locators

In a number of places, you can specify a filter string which defines what entities to filter/affect in the request. This string representation is referred to as locator in the scope of the TeamCity REST API:

var builds = await client.Builds
	.WithLocator(new BuildLocator()
	{
		Agent = new AgentLocator() {Name = "linux-blade-076-vm-13"},
		Count = 50
	})
	.GetAsync();

This query is similar to https://buildserver.mycompany.net/app/rest/builds?locator=agent:(name:linux-blade-076-vm-13),count:50

All Locators

Includes

You can get children in request:

var query = client.Builds
	.Include(b => b.Build)
		.ThenInclude(x => x.Artifacts)
	.Include(x => x.Build)
		 .ThenInclude(x => x.Agent)

This query is similar to https://buildserver.mycompany.net/app/rest/builds?fields=$short,build($short,artifacts($short),agent($short))

Also there are two include types: short and long. Models can contain simple fields (bool, string, int, etc.) and composite (other models). If we use IncludeType.Short, then only simple fields will be loaded, and composite fields will be null. If we use IncludeType.Long then the composite fields will be loaded as well. Short include:

var query = client.TestOccurrences
                .Include(x => x.TestOccurrence, IncludeType.Short)
                .WithLocator(new TestOccurrenceLocator()
                {
                    Count = 1,
                    Build = new BuildLocator { Id = 156770153}
                });
var tests = await query.GetAsync();

This returns object:

<testOccurrences count="1" href="https://buildserver.mycompany.net/app/rest/testOccurrences?locator=build:(id:156770153),count:1&fields=$short,testOccurrence($short)" nextHref="/app/rest/testOccurrences?fields=$short,testOccurrence($short)&locator=build:(id:156770153),count:1,start:1">
  <testOccurrence id="build:(id:156770020),id:34195" name="TestName" status="UNKNOWN" ignored="true" href="/app/rest/testOccurrences/build:(id:156770020),id:34195"/>
</testOccurrences>

Long include:

var query = client.TestOccurrences
                .Include(x => x.TestOccurrence, IncludeType.Long)
                .WithLocator(new TestOccurrenceLocator()
                {
                    Count = 1,
                    Build = new BuildLocator { Id = 156770153}
                });
var tests = await query.GetAsync();

This returns object:

<testOccurrences count="1" href="https://buildserver.mycompany.net/app/rest/testOccurrences?locator=build:(id:156770153),count:1&fields=$short,testOccurrence($long)" nextHref="/app/rest/testOccurrences?fields=$short,testOccurrence($long)&locator=build:(id:156770153),count:1,start:1">
  <testOccurrence id="build:(id:156770020),id:34195" name="TestName" status="UNKNOWN" ignored="true" href="/app/rest/testOccurrences/build:(id:156770020),id:34195">
    <ignoreDetails>Not implemented</ignoreDetails>
    <test id="-1831283233637679723" name="TestName" href="/app/rest/tests/id:-1831283233637679723"/>
    <build id="156770020" buildTypeId="1234567" number="213.0.20211217.221911-eap09d" status="SUCCESS" state="finished" branchName="refs/heads/net213" defaultBranch="true" href="/app/rest/builds/id:156770020" webUrl="https://buildserver.mycompany.net/viewLog.html?buildId=156770020&buildTypeId=1234567">
      <finishOnAgentDate>20211218T014303+0300</finishOnAgentDate>
    </build>
  </testOccurrence>
</testOccurrences>

Pagination

Some entities, such as Builds, are returned page by page. Such entities implement the IAsyncEnumerable interface. The following code loads 250 builds with agent and then prints the Build Id and Agent IP of every 250th Build in a foreach loop

var builds = await  Client.Builds
    .Include(x => x.Build)
        .ThenInclude(x=>x.Agent)
    .GetAsync(perPage: 250);

await foreach (var buildPage in builds)
{
    var build = buildPage.Value.First();
    Console.WriteLine($"{build.Id}, {build.Agent.Ip}");
}

Clone this wiki locally