diff --git a/README.md b/README.md index 4db5e6a8..ac1a3d61 100644 --- a/README.md +++ b/README.md @@ -153,6 +153,7 @@ Each area has its own list of methods available Change ByChangeId(string id); Change LastChangeDetailByBuildConfigId(string buildConfigId); List ByBuildConfigId(string buildConfigId); + List ByLocator(ChangeLocator changeLocator); ###BuildArtifacts void DownloadArtifactsByBuildId(string buildId, Action downloadHandler); diff --git a/packages/repositories.config b/packages/repositories.config index bcd17bb0..3468aa5e 100644 --- a/packages/repositories.config +++ b/packages/repositories.config @@ -2,5 +2,6 @@ + \ No newline at end of file diff --git a/src/TeamCitySharp/ActionTypes/Builds.cs b/src/TeamCitySharp/ActionTypes/Builds.cs index 4c52f340..795558fa 100644 --- a/src/TeamCitySharp/ActionTypes/Builds.cs +++ b/src/TeamCitySharp/ActionTypes/Builds.cs @@ -39,6 +39,12 @@ public void Add2QueueBuildByBuildConfigId(string buildConfigId) _caller.GetFormat("/action.html?add2Queue={0}", buildConfigId); } + public Build ByBuildId(string id) + { + var build = _caller.GetFormat("/app/rest/builds/id:{0}", id); + return build; + } + public List SuccessfulBuildsByBuildConfigId(string buildConfigId) { return ByBuildLocator(BuildLocator.WithDimensions(BuildTypeLocator.WithId(buildConfigId), diff --git a/src/TeamCitySharp/ActionTypes/Changes.cs b/src/TeamCitySharp/ActionTypes/Changes.cs index 5218b4aa..ce4597aa 100644 --- a/src/TeamCitySharp/ActionTypes/Changes.cs +++ b/src/TeamCitySharp/ActionTypes/Changes.cs @@ -2,6 +2,7 @@ using System.Linq; using TeamCitySharp.Connection; using TeamCitySharp.DomainEntities; +using TeamCitySharp.Locators; namespace TeamCitySharp.ActionTypes { @@ -42,5 +43,14 @@ public Change LastChangeDetailByBuildConfigId(string buildConfigId) return changes.FirstOrDefault(); } + public List ByLocator(ChangeLocator locator) + { + var changeWrapper = _caller.GetFormat("/app/rest/changes?locator={0}", locator); + if (changeWrapper.Change.Count > 0) + { + return changeWrapper.Change; + } + return new List(); + } } } \ No newline at end of file diff --git a/src/TeamCitySharp/ActionTypes/IBuilds.cs b/src/TeamCitySharp/ActionTypes/IBuilds.cs index 6afba988..cffa9a4c 100644 --- a/src/TeamCitySharp/ActionTypes/IBuilds.cs +++ b/src/TeamCitySharp/ActionTypes/IBuilds.cs @@ -7,6 +7,7 @@ namespace TeamCitySharp.ActionTypes { public interface IBuilds { + Build ByBuildId(string id); List SuccessfulBuildsByBuildConfigId(string buildConfigId); Build LastSuccessfulBuildByBuildConfigId(string buildConfigId); List FailedBuildsByBuildConfigId(string buildConfigId); diff --git a/src/TeamCitySharp/ActionTypes/IChanges.cs b/src/TeamCitySharp/ActionTypes/IChanges.cs index 1ae51cd5..7886a3ae 100644 --- a/src/TeamCitySharp/ActionTypes/IChanges.cs +++ b/src/TeamCitySharp/ActionTypes/IChanges.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using TeamCitySharp.DomainEntities; +using TeamCitySharp.Locators; namespace TeamCitySharp.ActionTypes { @@ -9,5 +10,6 @@ public interface IChanges Change ByChangeId(string id); Change LastChangeDetailByBuildConfigId(string buildConfigId); List ByBuildConfigId(string buildConfigId); + List ByLocator(ChangeLocator locator); } } \ No newline at end of file diff --git a/src/TeamCitySharp/ActionTypes/IStatistics.cs b/src/TeamCitySharp/ActionTypes/IStatistics.cs new file mode 100644 index 00000000..4444c9d2 --- /dev/null +++ b/src/TeamCitySharp/ActionTypes/IStatistics.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using TeamCitySharp.DomainEntities; + +namespace TeamCitySharp.ActionTypes +{ + public interface IStatistics + { + List GetByBuildId(string buildId); + } +} diff --git a/src/TeamCitySharp/ActionTypes/ITestOccurrences.cs b/src/TeamCitySharp/ActionTypes/ITestOccurrences.cs new file mode 100644 index 00000000..876a885d --- /dev/null +++ b/src/TeamCitySharp/ActionTypes/ITestOccurrences.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using TeamCitySharp.DomainEntities; + +namespace TeamCitySharp.ActionTypes +{ + public interface ITestOccurrences + { + List ByBuildId(string buildId, int count); + } +} \ No newline at end of file diff --git a/src/TeamCitySharp/ActionTypes/Statistics.cs b/src/TeamCitySharp/ActionTypes/Statistics.cs new file mode 100644 index 00000000..7fe8a9ad --- /dev/null +++ b/src/TeamCitySharp/ActionTypes/Statistics.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using TeamCitySharp.Connection; +using TeamCitySharp.DomainEntities; + +namespace TeamCitySharp.ActionTypes +{ + public class Statistics : IStatistics + { + private readonly TeamCityCaller _caller; + + internal Statistics(TeamCityCaller caller) + { + _caller = caller; + } + + public List GetByBuildId(string buildId) + { + return _caller.GetFormat("/app/rest/builds/id:{0}/statistics", buildId).Property; + } + } +} diff --git a/src/TeamCitySharp/ActionTypes/TestOccurrences.cs b/src/TeamCitySharp/ActionTypes/TestOccurrences.cs new file mode 100644 index 00000000..dfd688be --- /dev/null +++ b/src/TeamCitySharp/ActionTypes/TestOccurrences.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using TeamCitySharp.Connection; +using TeamCitySharp.DomainEntities; + +namespace TeamCitySharp.ActionTypes +{ + internal class TestOccurrences : ITestOccurrences + { + private readonly TeamCityCaller _caller; + + internal TestOccurrences(TeamCityCaller caller) + { + _caller = caller; + } + + public List ByBuildId(string buildId, int count) + { + var testOccurenceWrapper = _caller.GetFormat("/app/rest/testOccurrences?locator=build:{0},count:{1}", buildId, count); + + return testOccurenceWrapper.TestOccurrence; + } + } +} diff --git a/src/TeamCitySharp/DomainEntities/Build.cs b/src/TeamCitySharp/DomainEntities/Build.cs index 7d16fc34..1454b8b1 100644 --- a/src/TeamCitySharp/DomainEntities/Build.cs +++ b/src/TeamCitySharp/DomainEntities/Build.cs @@ -1,27 +1,27 @@ using System; -namespace TeamCitySharp.DomainEntities -{ - public class Build - { - public string Id { get; set; } - public string Number { get; set; } - public string Status { get; set; } - public string BuildTypeId { get; set; } - public string Href { get; set; } - public string WebUrl { get; set; } - public string StatusText { get; set; } +namespace TeamCitySharp.DomainEntities +{ + public class Build + { + public string Id { get; set; } + public string Number { get; set; } + public string Status { get; set; } + public BuildConfig BuildType { get; set; } + public string Href { get; set; } + public string WebUrl { get; set; } + public string StatusText { get; set; } public DateTime StartDate { get; set; } - public DateTime FinishDate { get; set; } - - public BuildConfig BuildConfig { get; set; } - public Agent Agent { get; set;} - public ChangeWrapper Changes { get; set; } - - public override string ToString() - { - return Number; - } - - } + public DateTime FinishDate { get; set; } + + public BuildConfig BuildConfig { get; set; } + public Agent Agent { get; set;} + public ChangeWrapper Changes { get; set; } + + public override string ToString() + { + return Number; + } + + } } \ No newline at end of file diff --git a/src/TeamCitySharp/DomainEntities/TestOccurrence.cs b/src/TeamCitySharp/DomainEntities/TestOccurrence.cs new file mode 100644 index 00000000..3af9030a --- /dev/null +++ b/src/TeamCitySharp/DomainEntities/TestOccurrence.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace TeamCitySharp.DomainEntities +{ + public class TestOccurrence + { + public string Name { get; set; } + public string Status { get; set; } + public int Duration { get; set; } + public bool Muted { get; set; } + public bool Ignored { get; set; } + public string Href { get; set; } + } +} diff --git a/src/TeamCitySharp/DomainEntities/TestOccurrenceWrapper.cs b/src/TeamCitySharp/DomainEntities/TestOccurrenceWrapper.cs new file mode 100644 index 00000000..4e94488e --- /dev/null +++ b/src/TeamCitySharp/DomainEntities/TestOccurrenceWrapper.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace TeamCitySharp.DomainEntities +{ + public class TestOccurrenceWrapper + { + public List TestOccurrence { get; set; } + } +} diff --git a/src/TeamCitySharp/ITeamCityClient.cs b/src/TeamCitySharp/ITeamCityClient.cs index f63f97d3..4533c1b4 100644 --- a/src/TeamCitySharp/ITeamCityClient.cs +++ b/src/TeamCitySharp/ITeamCityClient.cs @@ -1,4 +1,4 @@ -using TeamCitySharp.ActionTypes; +using TeamCitySharp.ActionTypes; namespace TeamCitySharp { @@ -7,7 +7,7 @@ public interface ITeamCityClient void Connect(string userName, string password); void ConnectAsGuest(); bool Authenticate(); - + IBuilds Builds { get; } IBuildConfigs BuildConfigs { get; } IProjects Projects { get; } @@ -16,6 +16,8 @@ public interface ITeamCityClient IAgents Agents { get; } IVcsRoots VcsRoots { get; } IChanges Changes { get; } - IBuildArtifacts Artifacts { get; } + IBuildArtifacts Artifacts { get; } + ITestOccurrences TestOccurrences { get; } + IStatistics Statistics { get; } } } \ No newline at end of file diff --git a/src/TeamCitySharp/Locators/ChangeLocator.cs b/src/TeamCitySharp/Locators/ChangeLocator.cs new file mode 100644 index 00000000..ed2d72f7 --- /dev/null +++ b/src/TeamCitySharp/Locators/ChangeLocator.cs @@ -0,0 +1,155 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace TeamCitySharp.Locators +{ + public class ChangeLocator + { + public static ChangeLocator WithId(string id) + { + return new ChangeLocator() {Id = id}; + } + + public static ChangeLocator WithBuildId(long id) + { + return new ChangeLocator {Build = BuildLocator.WithId(id)}; + } + + public static ChangeLocator WithDimensions(string id, + string project = null, + BuildTypeLocator buildType = null, + BuildLocator build = null, + string vcsRoot = null, + string vcsRootInstance = null, + string userName = null, + UserLocator user = null, + string version = null, + string internalVersion = null, + string comment = null, + string file = null, + ChangeLocator sinceChange = null, + int? maxResults = null, + int? startIndex = null + ) + { + return new ChangeLocator + { + Build = build, + BuildType = buildType, + Comment = comment, + File = file, + Id = id, + InternalVersion = internalVersion, + MaxResults = maxResults, + Project = project, + SinceChange = sinceChange, + StartIndex = startIndex, + Version = version, + VcsRoot = vcsRoot, + VcsRootInstance = vcsRootInstance, + User = user, + UserName = userName + }; + } + + public string Id { get; private set; } + public string Project { get; private set; } + public BuildTypeLocator BuildType { get; private set; } + public BuildLocator Build { get; private set; } + public string VcsRoot { get; private set; } + public string VcsRootInstance { get; private set; } + public string UserName { get; private set; } + public UserLocator User { get; private set; } + public string Version { get; private set; } + public string InternalVersion { get; private set; } + public string Comment { get; private set; } + public string File { get; private set; } + public ChangeLocator SinceChange { get; private set; } + public int? MaxResults { get; private set; } + public int? StartIndex { get; private set; } + + public override string ToString() + { + if (Id != null) + { + return "id:" + Id; + } + + var locatorFields = new List(); + + if (BuildType != null) + { + locatorFields.Add("buildType:(" + BuildType + ")"); + } + + if (Build != null) + { + locatorFields.Add("build:(" + Build + ")"); + } + + if (User != null) + { + locatorFields.Add("user:(" + User + ")"); + } + + if (SinceChange != null) + { + locatorFields.Add("sinceChange:(" + SinceChange + ")"); + } + + if (!String.IsNullOrEmpty(Project)) + { + locatorFields.Add("project:" + Project); + } + + if (!String.IsNullOrEmpty(VcsRoot)) + { + locatorFields.Add("vcsRoot:" + VcsRoot); + } + + if (!String.IsNullOrEmpty(VcsRootInstance)) + { + locatorFields.Add("vcsRootInstance:" + VcsRootInstance); + } + + if (!String.IsNullOrEmpty(UserName)) + { + locatorFields.Add("userName:" + UserName); + } + + if (!String.IsNullOrEmpty(Version)) + { + locatorFields.Add("version:" + Version); + } + + if (!String.IsNullOrEmpty(InternalVersion)) + { + locatorFields.Add("internalVersion:" + InternalVersion); + } + + if (!String.IsNullOrEmpty(Comment)) + { + locatorFields.Add("comment:" + Comment); + } + + if (!String.IsNullOrEmpty(File)) + { + locatorFields.Add("file:" + File); + } + + if (MaxResults.HasValue) + { + locatorFields.Add("count:" + MaxResults.Value.ToString()); + } + + if (StartIndex.HasValue) + { + locatorFields.Add("start:" + StartIndex.Value.ToString()); + } + + return string.Join(",", locatorFields.ToArray()); + } + } +} diff --git a/src/TeamCitySharp/TeamCityClient.cs b/src/TeamCitySharp/TeamCityClient.cs index eeb9bb82..b44076be 100644 --- a/src/TeamCitySharp/TeamCityClient.cs +++ b/src/TeamCitySharp/TeamCityClient.cs @@ -15,6 +15,8 @@ public class TeamCityClient : IClientConnection, ITeamCityClient private IVcsRoots _vcsRoots; private IChanges _changes; private IBuildArtifacts _artifacts; + private ITestOccurrences _testOccurrences; + private IStatistics _statistics; public TeamCityClient(string hostName, bool useSsl = false) { @@ -80,5 +82,15 @@ public IBuildArtifacts Artifacts { get { return _artifacts ?? (_artifacts = new BuildArtifacts(_caller)); } } + + public ITestOccurrences TestOccurrences + { + get { return _testOccurrences ?? (_testOccurrences = new TestOccurrences(_caller)); } + } + + public IStatistics Statistics + { + get { return _statistics ?? (_statistics = new Statistics(_caller)); } + } } } diff --git a/src/TeamCitySharp/TeamCitySharp.csproj b/src/TeamCitySharp/TeamCitySharp.csproj index 2e7df5aa..807cd407 100644 --- a/src/TeamCitySharp/TeamCitySharp.csproj +++ b/src/TeamCitySharp/TeamCitySharp.csproj @@ -42,6 +42,7 @@ + @@ -62,11 +63,18 @@ + + + + + + + diff --git a/src/TeamCitySharp/TeamCitySharp.nuspec b/src/TeamCitySharp/TeamCitySharp.nuspec new file mode 100644 index 00000000..bc0a951b --- /dev/null +++ b/src/TeamCitySharp/TeamCitySharp.nuspec @@ -0,0 +1,20 @@ + + + + TeamCitySharp-forked + 1.0.0.5 + TeamCitySharp-forked + Yury Timofeev + Yury Timofeev + http://stack72.mit-license.org/ + https://github.com/y-gagar1n/TeamCitySharp + false + Fork of TeamCitySharp project, which allows access to TeamCity via their REST API + Copyright 2015 + teamcity + + + + + + \ No newline at end of file diff --git a/src/TeamCitySharp/pack.bat b/src/TeamCitySharp/pack.bat new file mode 100644 index 00000000..dfab0e02 --- /dev/null +++ b/src/TeamCitySharp/pack.bat @@ -0,0 +1 @@ +nuget pack TeamCitySharp.csproj \ No newline at end of file diff --git a/src/Tests/IntegrationTests/SampleStatisticsUsage.cs b/src/Tests/IntegrationTests/SampleStatisticsUsage.cs new file mode 100644 index 00000000..c005173b --- /dev/null +++ b/src/Tests/IntegrationTests/SampleStatisticsUsage.cs @@ -0,0 +1,28 @@ +using NUnit.Framework; +using System.Linq; + +namespace TeamCitySharp.IntegrationTests +{ + [TestFixture] + public class when_interacting_to_get_build_statistics + { + private ITeamCityClient _client; + + [SetUp] + public void SetUp() + { + _client = new TeamCityClient("teamcity.codebetter.com"); + _client.Connect("teamcitysharpuser", "qwerty"); + } + + [Test] + public void it_returns_no_of_tests_from_last_successful_build() + { + var proj = _client.Projects.ById("AutoFixture"); + var build = _client.Builds.LastSuccessfulBuildByBuildConfigId(proj.BuildTypes.BuildType[0].Id); + var stats = _client.Statistics.GetByBuildId(build.Id); + + Assert.That(stats.Any(property => property.Name.Equals("PassedTestCount"))); + } + } +} diff --git a/src/Tests/IntegrationTests/TeamCitySharp.IntegrationTests.csproj b/src/Tests/IntegrationTests/TeamCitySharp.IntegrationTests.csproj index 95e8426c..be4abbcd 100644 --- a/src/Tests/IntegrationTests/TeamCitySharp.IntegrationTests.csproj +++ b/src/Tests/IntegrationTests/TeamCitySharp.IntegrationTests.csproj @@ -60,6 +60,7 @@ Code + Code