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/Agents.cs b/src/TeamCitySharp/ActionTypes/Agents.cs index 56944acc..6b9837ee 100644 --- a/src/TeamCitySharp/ActionTypes/Agents.cs +++ b/src/TeamCitySharp/ActionTypes/Agents.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.IO; using TeamCitySharp.Connection; using TeamCitySharp.DomainEntities; @@ -13,11 +14,26 @@ internal Agents(TeamCityCaller caller) _caller = caller; } - public List All() + public List All(bool includeDisconnected = true, bool includeUnauthorized = true) { - var agentWrapper = _caller.Get("/app/rest/agents"); + var url = string.Format("/app/rest/agents?includeDisconnected={0}&includeUnauthorized={1}", + includeDisconnected.ToString().ToLower(), includeUnauthorized.ToString().ToLower()); + + var agentWrapper = _caller.Get(url); return agentWrapper.Agent; } + + private string AddqueryString(string url, string queryString) + { + if (url.Contains("?")) + url += "&"; + else + url += "?"; + + url += queryString; + + return url; + } } } \ No newline at end of file diff --git a/src/TeamCitySharp/ActionTypes/BuildConfigs.cs b/src/TeamCitySharp/ActionTypes/BuildConfigs.cs index cdcd4a0f..b01b6271 100644 --- a/src/TeamCitySharp/ActionTypes/BuildConfigs.cs +++ b/src/TeamCitySharp/ActionTypes/BuildConfigs.cs @@ -114,6 +114,24 @@ public void PostRawBuildTrigger(BuildTypeLocator locator, string rawXml) _caller.PostFormat(rawXml, HttpContentTypes.ApplicationXml, "/app/rest/buildTypes/{0}/triggers", locator); } + public void SetArtifactDependency(BuildTypeLocator locator, ArtifactDependency dependency) + { + _caller.PostFormat(dependency, HttpContentTypes.ApplicationJson, + HttpContentTypes.ApplicationJson, "/app/rest/buildTypes/{0}/artifact-dependencies", locator); + } + + public void SetSnapshotDependency(BuildTypeLocator locator, SnapshotDependency dependency) + { + _caller.PostFormat(dependency, HttpContentTypes.ApplicationJson, + HttpContentTypes.ApplicationJson, "/app/rest/buildTypes/{0}/snapshot-dependencies", locator); + } + + public void SetTrigger(BuildTypeLocator locator, BuildTrigger trigger) + { + _caller.PostFormat(trigger, HttpContentTypes.ApplicationJson, HttpContentTypes.ApplicationJson, + "/app/rest/buildTypes/{0}/triggers", locator); + } + public void SetConfigurationParameter(BuildTypeLocator locator, string key, string value) { _caller.PutFormat(value, HttpContentTypes.TextPlain, "/app/rest/buildTypes/{0}/parameters/{1}", locator, key); diff --git a/src/TeamCitySharp/ActionTypes/Builds.cs b/src/TeamCitySharp/ActionTypes/Builds.cs index 4c52f340..ca4bbe9e 100644 --- a/src/TeamCitySharp/ActionTypes/Builds.cs +++ b/src/TeamCitySharp/ActionTypes/Builds.cs @@ -95,6 +95,13 @@ public Build LastErrorBuildByBuildConfigId(string buildConfigId) return builds != null ? builds.FirstOrDefault() : new Build(); } + public Build ById(string id) + { + var build = _caller.GetFormat("/app/rest/builds/id:{0}", id); + + return build ?? new Build(); + } + public List ByBuildConfigId(string buildConfigId) { return ByBuildLocator(BuildLocator.WithDimensions(BuildTypeLocator.WithId(buildConfigId) diff --git a/src/TeamCitySharp/ActionTypes/IAgents.cs b/src/TeamCitySharp/ActionTypes/IAgents.cs index 36b5ade5..1292b1a3 100644 --- a/src/TeamCitySharp/ActionTypes/IAgents.cs +++ b/src/TeamCitySharp/ActionTypes/IAgents.cs @@ -5,6 +5,6 @@ namespace TeamCitySharp.ActionTypes { public interface IAgents { - List All(); + List All(bool includeDisconnected = false, bool includeUnauthorized = false); } } \ No newline at end of file diff --git a/src/TeamCitySharp/ActionTypes/IBuildConfigs.cs b/src/TeamCitySharp/ActionTypes/IBuildConfigs.cs index 2829ec2d..ef90817d 100644 --- a/src/TeamCitySharp/ActionTypes/IBuildConfigs.cs +++ b/src/TeamCitySharp/ActionTypes/IBuildConfigs.cs @@ -22,9 +22,15 @@ public interface IBuildConfigs void SetConfigurationSetting(BuildTypeLocator locator, string settingName, string settingValue); bool GetConfigurationPauseStatus(BuildTypeLocator locator); void SetConfigurationPauseStatus(BuildTypeLocator locator, bool isPaused); + void PostRawArtifactDependency(BuildTypeLocator locator, string rawXml); + void SetArtifactDependency(BuildTypeLocator locator, ArtifactDependency dependency); + void PostRawBuildStep(BuildTypeLocator locator, string rawXml); + void PostRawBuildTrigger(BuildTypeLocator locator, string rawXml); + void SetTrigger(BuildTypeLocator locator, BuildTrigger trigger); + void SetConfigurationParameter(BuildTypeLocator locator, string key, string value); void PostRawAgentRequirement(BuildTypeLocator locator, string rawXml); void DeleteBuildStep(BuildTypeLocator locator, string buildStepId); @@ -62,6 +68,7 @@ public interface IBuildConfigs /// ]]> /// void PostRawSnapshotDependency(BuildTypeLocator locator, XmlElement rawXml); + void SetSnapshotDependency(BuildTypeLocator locator, SnapshotDependency dependency); /// /// Locates a build type by its locator. diff --git a/src/TeamCitySharp/ActionTypes/IBuilds.cs b/src/TeamCitySharp/ActionTypes/IBuilds.cs index 6afba988..2c57fa2a 100644 --- a/src/TeamCitySharp/ActionTypes/IBuilds.cs +++ b/src/TeamCitySharp/ActionTypes/IBuilds.cs @@ -14,6 +14,7 @@ public interface IBuilds Build LastBuildByBuildConfigId(string buildConfigId); List ErrorBuildsByBuildConfigId(string buildConfigId); Build LastErrorBuildByBuildConfigId(string buildConfigId); + Build ById(string id); List ByBuildConfigId(string buildConfigId); List ByConfigIdAndTag(string buildConfigId, string tag); List ByUserName(string userName); diff --git a/src/TeamCitySharp/ActionTypes/Projects.cs b/src/TeamCitySharp/ActionTypes/Projects.cs index 16f87758..69bc82fb 100644 --- a/src/TeamCitySharp/ActionTypes/Projects.cs +++ b/src/TeamCitySharp/ActionTypes/Projects.cs @@ -1,7 +1,9 @@ using System.Collections.Generic; +using System.Text.RegularExpressions; using EasyHttp.Http; using TeamCitySharp.Connection; using TeamCitySharp.DomainEntities; +using TeamCitySharp.Locators; namespace TeamCitySharp.ActionTypes { @@ -42,7 +44,20 @@ public Project Details(Project project) public Project Create(string projectName) { - return _caller.Post(projectName, HttpContentTypes.ApplicationXml, "/app/rest/projects/", string.Empty); + return Create(projectName, "_Root"); + } + + public Project Create(string projectName, string rootProjectId) + { + var project = new NewProjectDescription + { + Name = projectName, + Id = GenerateId(projectName), + ParentProject = new ParentProjectWrapper(ProjectLocator.WithId(rootProjectId)) + }; + + return _caller.Post(project, HttpContentTypes.ApplicationJson, "/app/rest/projects/", + HttpContentTypes.ApplicationJson); } public void Delete(string projectName) @@ -59,5 +74,11 @@ public void SetProjectParameter(string projectName, string settingName, string s { _caller.PutFormat(settingValue, "/app/rest/projects/name:{0}/parameters/{1}", projectName, settingName); } + + public string GenerateId(string projectName) + { + projectName = Regex.Replace(projectName, @"[^\p{L}\p{N}]+", ""); + return projectName; + } } } \ No newline at end of file diff --git a/src/TeamCitySharp/Connection/ITeamCityCaller.cs b/src/TeamCitySharp/Connection/ITeamCityCaller.cs index aee4df5c..ec6a95f8 100644 --- a/src/TeamCitySharp/Connection/ITeamCityCaller.cs +++ b/src/TeamCitySharp/Connection/ITeamCityCaller.cs @@ -27,7 +27,7 @@ internal interface ITeamCityCaller void Get(string urlPart); - T Post(string data, string contenttype, string urlPart, string accept); + T Post(object data, string contenttype, string urlPart, string accept); bool Authenticate(string urlPart); diff --git a/src/TeamCitySharp/Connection/TeamCityCaller.cs b/src/TeamCitySharp/Connection/TeamCityCaller.cs index 35879822..dd126fc0 100644 --- a/src/TeamCitySharp/Connection/TeamCityCaller.cs +++ b/src/TeamCitySharp/Connection/TeamCityCaller.cs @@ -42,17 +42,17 @@ public void GetFormat(string urlPart, params object[] parts) public T PostFormat(object data, string contenttype, string accept, string urlPart, params object[] parts) { - return Post(data.ToString(), contenttype, string.Format(urlPart, parts), accept); + return Post(data, contenttype, string.Format(urlPart, parts), accept); } public void PostFormat(object data, string contenttype, string urlPart, params object[] parts) { - Post(data.ToString(), contenttype, string.Format(urlPart, parts), string.Empty); + Post(data, contenttype, string.Format(urlPart, parts), string.Empty); } public void PutFormat(object data, string contenttype, string urlPart, params object[] parts) { - Put(data.ToString(), contenttype, string.Format(urlPart, parts), string.Empty); + Put(data, contenttype, string.Format(urlPart, parts), string.Empty); } public void DeleteFormat(string urlPart, params object[] parts) @@ -143,7 +143,7 @@ private HttpResponse GetResponse(string urlPart) return response; } - public T Post(string data, string contenttype, string urlPart, string accept) + public T Post(object data, string contenttype, string urlPart, string accept) { return Post(data, contenttype, urlPart, accept).StaticBody(); } diff --git a/src/TeamCitySharp/Connection/TeamcityJsonEncoderDecoderConfiguration.cs b/src/TeamCitySharp/Connection/TeamcityJsonEncoderDecoderConfiguration.cs index 4a40a74f..339d06e7 100644 --- a/src/TeamCitySharp/Connection/TeamcityJsonEncoderDecoderConfiguration.cs +++ b/src/TeamCitySharp/Connection/TeamcityJsonEncoderDecoderConfiguration.cs @@ -1,9 +1,13 @@ using System.Collections.Generic; +using System.IO; using EasyHttp.Codecs; using EasyHttp.Codecs.JsonFXExtensions; using EasyHttp.Configuration; +using JsonFx.IO; using JsonFx.Json; +using JsonFx.Model; using JsonFx.Serialization; +using JsonFx.Serialization.Filters; namespace TeamCitySharp.Connection { @@ -11,7 +15,7 @@ public class TeamcityJsonEncoderDecoderConfiguration : IEncoderDecoderConfigurat { public IEncoder GetEncoder() { - var jsonWriter = new JsonWriter(new DataWriterSettings(DefaultEncoderDecoderConfiguration.CombinedResolverStrategy() + var jsonWriter = new CamelCaseJsonWriter(new DataWriterSettings(DefaultEncoderDecoderConfiguration.CombinedResolverStrategy() , new TeamCityDateFilter()), new[] { "application/.*json", "text/.*json" }); var writers = new List { jsonWriter }; @@ -29,4 +33,39 @@ public IDecoder GetDecoder() return new DefaultDecoder(dataReaderProvider); } } + + public class CamelCaseJsonWriter : JsonWriter + { + public CamelCaseJsonWriter(DataWriterSettings settings, params string[] contentTypes) + : base(settings, contentTypes) + {} + + protected override ITextFormatter GetFormatter() + { + return new CamelCaseJsonFormatter(this.Settings); + } + } + + public class CamelCaseJsonFormatter : JsonWriter.JsonFormatter + { + public CamelCaseJsonFormatter(DataWriterSettings settings) + : base(settings) + {} + + protected override void WritePropertyName(TextWriter writer, string propertyName) + { + base.WritePropertyName(writer, CamelCase(propertyName)); + } + + private static string CamelCase(string input) + { + if (string.IsNullOrEmpty(input)) + return input; + + var chars = input.ToCharArray(); + chars[0] = chars[0].ToString().ToLower().ToCharArray()[0]; + + return new string(chars); + } + } } \ No newline at end of file diff --git a/src/TeamCitySharp/DomainEntities/ArtifactDependency.cs b/src/TeamCitySharp/DomainEntities/ArtifactDependency.cs index 487b9565..4278a7c8 100644 --- a/src/TeamCitySharp/DomainEntities/ArtifactDependency.cs +++ b/src/TeamCitySharp/DomainEntities/ArtifactDependency.cs @@ -1,14 +1,40 @@ +using JsonFx.Json; + namespace TeamCitySharp.DomainEntities { public class ArtifactDependency { + public ArtifactDependency() + { + Properties = new Properties(); + Type = "artifact_dependency"; + } public override string ToString() { - return "artifact_dependency"; + return Type; } public string Id { get; set; } - public string Type { get; set; } public Properties Properties { get; set; } + [JsonName("source-buildType")] + public SourceBuildType SourceBuildType { get; set; } + public string Type { get; set; } + + public static ArtifactDependency Default(string dependsOnbuildId) + { + var dependency = new ArtifactDependency(); + + dependency.Properties.Add("cleanDestinationDirectory", "true"); + dependency.Properties.Add("pathRules", "* => Temp"); + dependency.Properties.Add("revisionName", "sameChainOrLastFinished"); + dependency.Properties.Add("revisionValue", "latest.sameChainOrLastFinished"); + + dependency.SourceBuildType = new SourceBuildType + { + Id = dependsOnbuildId + }; + + return dependency; + } } } \ No newline at end of file diff --git a/src/TeamCitySharp/DomainEntities/BuildTrigger.cs b/src/TeamCitySharp/DomainEntities/BuildTrigger.cs index f6f9ec78..96a954ac 100644 --- a/src/TeamCitySharp/DomainEntities/BuildTrigger.cs +++ b/src/TeamCitySharp/DomainEntities/BuildTrigger.cs @@ -2,6 +2,11 @@ namespace TeamCitySharp.DomainEntities { public class BuildTrigger { + public BuildTrigger() + { + Properties = new Properties(); + } + public override string ToString() { return "trigger"; @@ -10,5 +15,18 @@ public override string ToString() public string Id { get; set; } public string Type { get; set; } public Properties Properties { get; set; } + + public static BuildTrigger FinishBuildTrigger(string dependsOnbuildId) + { + var trigger = new BuildTrigger + { + Type = "buildDependencyTrigger" + }; + + trigger.Properties.Add("afterSuccessfulBuildOnly", "true"); + trigger.Properties.Add("dependsOn", dependsOnbuildId); + + return trigger; + } } } \ No newline at end of file diff --git a/src/TeamCitySharp/DomainEntities/NewProjectDescription.cs b/src/TeamCitySharp/DomainEntities/NewProjectDescription.cs new file mode 100644 index 00000000..42ccf97d --- /dev/null +++ b/src/TeamCitySharp/DomainEntities/NewProjectDescription.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using JsonFx.Json; +using TeamCitySharp.Locators; + +namespace TeamCitySharp.DomainEntities +{ + public class NewProjectDescription + { + public string Name { get; set; } + public string Id { get; set; } + public ParentProjectWrapper ParentProject { get; set; } + } +} diff --git a/src/TeamCitySharp/DomainEntities/ParentProjectWrapper.cs b/src/TeamCitySharp/DomainEntities/ParentProjectWrapper.cs new file mode 100644 index 00000000..2bec04da --- /dev/null +++ b/src/TeamCitySharp/DomainEntities/ParentProjectWrapper.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using TeamCitySharp.Locators; + +namespace TeamCitySharp.DomainEntities +{ + public class ParentProjectWrapper + { + private readonly ProjectLocator _locator; + + public ParentProjectWrapper(ProjectLocator locator) + { + _locator = locator; + } + + public string Locator { get { return _locator.ToString(); } } + } +} diff --git a/src/TeamCitySharp/DomainEntities/Properties.cs b/src/TeamCitySharp/DomainEntities/Properties.cs index bb13d698..dc796224 100644 --- a/src/TeamCitySharp/DomainEntities/Properties.cs +++ b/src/TeamCitySharp/DomainEntities/Properties.cs @@ -4,6 +4,16 @@ namespace TeamCitySharp.DomainEntities { public class Properties { + public Properties() + { + Property = new List(); + } + + public void Add(string name, string value) + { + Property.Add(new Property(name, value)); + } + public override string ToString() { return "properties"; diff --git a/src/TeamCitySharp/DomainEntities/Property.cs b/src/TeamCitySharp/DomainEntities/Property.cs index 70f3cb25..d2dadab0 100644 --- a/src/TeamCitySharp/DomainEntities/Property.cs +++ b/src/TeamCitySharp/DomainEntities/Property.cs @@ -2,6 +2,15 @@ namespace TeamCitySharp.DomainEntities { public class Property { + public Property() + {} + + public Property(string name, string value) + { + Name = name; + Value = value; + } + public override string ToString() { return Name; diff --git a/src/TeamCitySharp/DomainEntities/SnapshotDependency.cs b/src/TeamCitySharp/DomainEntities/SnapshotDependency.cs index 6f0cfe50..abde4502 100644 --- a/src/TeamCitySharp/DomainEntities/SnapshotDependency.cs +++ b/src/TeamCitySharp/DomainEntities/SnapshotDependency.cs @@ -1,13 +1,42 @@ +using JsonFx.Json; + namespace TeamCitySharp.DomainEntities { public class SnapshotDependency { + public SnapshotDependency() + { + Properties = new Properties(); + Type = "snapshot_dependency"; + } + public override string ToString() { - return "snapshot_dependency"; + return Type; } public string Id { get; set; } - public Properties Properties { get; set; } + public Properties Properties { get; set; } + [JsonName("source-buildType")] + public SourceBuildType SourceBuildType { get; set; } + public string Type { get; set; } + + public static SnapshotDependency Default(string dependsOnbuildId) + { + var dependency = new SnapshotDependency(); + + dependency.Properties.Add("run-build-if-dependency-failed", "RUN_ADD_PROBLEM"); + dependency.Properties.Add("run-build-if-dependency-failed-to-start", "MAKE_FAILED_TO_START"); + dependency.Properties.Add("run-build-on-the-same-agent", "false"); + dependency.Properties.Add("take-started-build-with-same-revisions", "true"); + dependency.Properties.Add("take-successful-builds-only", "true"); + + dependency.SourceBuildType = new SourceBuildType + { + Id = dependsOnbuildId + }; + + return dependency; + } } } \ No newline at end of file diff --git a/src/TeamCitySharp/DomainEntities/SourceBuildType.cs b/src/TeamCitySharp/DomainEntities/SourceBuildType.cs new file mode 100644 index 00000000..e5c1f89f --- /dev/null +++ b/src/TeamCitySharp/DomainEntities/SourceBuildType.cs @@ -0,0 +1,7 @@ +namespace TeamCitySharp.DomainEntities +{ + public class SourceBuildType + { + public string Id { get; set; } + } +} diff --git a/src/TeamCitySharp/Locators/ProjectLocator.cs b/src/TeamCitySharp/Locators/ProjectLocator.cs new file mode 100644 index 00000000..5291db9c --- /dev/null +++ b/src/TeamCitySharp/Locators/ProjectLocator.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; + +namespace TeamCitySharp.Locators +{ + public class ProjectLocator + { + public static ProjectLocator WithId(string id) + { + return new ProjectLocator {Id = id}; + } + + public static ProjectLocator WithName(string name) + { + return new ProjectLocator {Name = name}; + } + + public string Id { get; set; } + public string Name { get; set; } + + public override string ToString() + { + if (!string.IsNullOrEmpty(Id)) + { + return "id:" + Id; + } + + if (!string.IsNullOrEmpty(Name)) + { + return "name:" + Name; + } + + + var locatorFields = new List(); + + return string.Join(",", locatorFields.ToArray()); + } + } +} diff --git a/src/TeamCitySharp/TeamCitySharp.csproj b/src/TeamCitySharp/TeamCitySharp.csproj index 2e7df5aa..55d3469d 100644 --- a/src/TeamCitySharp/TeamCitySharp.csproj +++ b/src/TeamCitySharp/TeamCitySharp.csproj @@ -67,6 +67,10 @@ + + + + diff --git a/src/Tests/IntegrationTests/SampleBuildsUsage.cs b/src/Tests/IntegrationTests/SampleBuildsUsage.cs index 152cad5f..daa77e2e 100644 --- a/src/Tests/IntegrationTests/SampleBuildsUsage.cs +++ b/src/Tests/IntegrationTests/SampleBuildsUsage.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using System.Net; +using System.Runtime.CompilerServices; using NUnit.Framework; using TeamCitySharp.Locators; @@ -190,5 +191,18 @@ public void it_does_not_populate_the_status_text_field_of_the_build_object() Assert.That(build.Count == 1); Assert.IsNull(build[0].StatusText); } + + [Test] + public void ig_returns_correct_build_when_calling_by_id() + { + const string buildId = "5726"; + var client = new TeamCityClient("localhost:81"); + client.Connect("admin", "qwerty"); + + var build = client.Builds.ById(buildId); + + Assert.That(build != null); + Assert.That(build.Id == buildId); + } } }