Skip to content

Attribute field naming #19

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions TechTalk.JiraRestClient/FieldAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System;

namespace TechTalk.JiraRestClient
{
public class FieldAttribute : Attribute
{
public string FieldName { get; set; }

public FieldAttribute(string fieldName)
{
this.FieldName = fieldName;
}
}
}
5 changes: 4 additions & 1 deletion TechTalk.JiraRestClient/IJiraClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@ namespace TechTalk.JiraRestClient
/// <summary>Returns all issues for the given project</summary>
IEnumerable<Issue<TIssueFields>> GetIssues(String projectKey);
/// <summary>Returns all issues of the specified type for the given project</summary>
IEnumerable<Issue<TIssueFields>> GetIssues(String projectKey, String issueType);
IEnumerable<Issue<TIssueFields>> GetIssues(String projectKey, String issueType);
/// <summary>Returns all issues of the given type and the given project filtered by the given JQL query</summary>

IEnumerable<Issue<TIssueFields>> GetIssuesByQuery(string[] projects, string issueType, string jqlQuery);

IEnumerable<Issue<TIssueFields>> GetIssuesByQuery(String projectKey, String issueType, String jqlQuery);
/// <summary>Enumerates through all issues for the given project</summary>
IEnumerable<Issue<TIssueFields>> EnumerateIssues(String projectKey);
Expand Down
111 changes: 83 additions & 28 deletions TechTalk.JiraRestClient/JiraClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
using System.Text;
using RestSharp;
using RestSharp.Deserializers;
using RestSharp.Extensions;
using static System.String;

namespace TechTalk.JiraRestClient
{
Expand All @@ -29,8 +31,10 @@ public JiraClient(string baseUrl, string username, string password)

private RestRequest CreateRequest(Method method, String path)
{
var request = new RestRequest { Method = method, Resource = path, RequestFormat = DataFormat.Json };
request.AddHeader("Authorization", "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes(String.Format("{0}:{1}", username, password))));
var request = new RestRequest { Method = method, Resource = path };
var encodedLogin = Encoding.ASCII.GetBytes($"{username}:{password}");
var base64String = Convert.ToBase64String(encodedLogin);
request.AddHeader("Authorization", $"Basic {base64String}");
return request;
}

Expand Down Expand Up @@ -64,6 +68,46 @@ public IEnumerable<Issue<TIssueFields>> GetIssuesByQuery(string projectKey, stri
return EnumerateIssuesInternal(projectKey, issueType, jqlQuery);
}

public IEnumerable<Issue<TIssueFields>> GetIssuesByQuery(string[] projectes, string issueType, string jqlQuery)
{
return EnumerateIssuesInternal(projectes, issueType, jqlQuery);
}

private IEnumerable<Issue<TIssueFields>> EnumerateIssuesInternal(string[] projectes, string issueType, string jqlQuery)
{
{
var queryCount = 50;
var resultCount = 0;
while (true)
{
var projectsClause = string.Join("%2C+", projectes.Select(p => $"%22{Uri.EscapeUriString(p)}%22"));
var jql = $"project+in+({projectsClause})";
if (!IsNullOrEmpty(issueType))
jql += $"+AND+issueType={Uri.EscapeUriString(issueType)}";
if (!IsNullOrEmpty(jqlQuery))
jql += $"+AND+{Uri.EscapeUriString(jqlQuery)}";
var path = $"search?jql={jql}&startAt={resultCount}&maxResults={queryCount}";
var request = CreateRequest(Method.GET, path);

var response = ExecuteRequest(request);
AssertStatus(response, HttpStatusCode.OK);

var data = deserializer.Deserialize<IssueContainer<TIssueFields>>(response);
var issues = data.issues?.ToArray() ?? Enumerable.Empty<Issue<TIssueFields>>().ToArray();

foreach (var item in issues)
{
yield return item;
}

resultCount += issues.Length;

if (resultCount < data.total) continue;
else /* all issues received */ break;
}
}
}

public IEnumerable<Issue<TIssueFields>> EnumerateIssues(String projectKey)
{
return EnumerateIssues(projectKey, null);
Expand All @@ -88,12 +132,12 @@ private IEnumerable<Issue<TIssueFields>> EnumerateIssuesInternal(String projectK
var resultCount = 0;
while (true)
{
var jql = String.Format("project={0}", Uri.EscapeUriString(projectKey));
if (!String.IsNullOrEmpty(issueType))
jql += String.Format("+AND+issueType={0}", Uri.EscapeUriString(issueType));
if (!String.IsNullOrEmpty(jqlQuery))
jql += String.Format("+AND+{0}", Uri.EscapeUriString(jqlQuery));
var path = String.Format("search?jql={0}&startAt={1}&maxResults={2}", jql, resultCount, queryCount);
var jql = Format("project={0}", Uri.EscapeUriString(projectKey));
if (!IsNullOrEmpty(issueType))
jql += Format("+AND+issueType={0}", Uri.EscapeUriString(issueType));
if (!IsNullOrEmpty(jqlQuery))
jql += Format("+AND+{0}", Uri.EscapeUriString(jqlQuery));
var path = Format("search?jql={0}&startAt={1}&maxResults={2}", jql, resultCount, queryCount);
var request = CreateRequest(Method.GET, path);

var response = ExecuteRequest(request);
Expand All @@ -119,7 +163,7 @@ public Issue<TIssueFields> LoadIssue(String issueRef)
{
try
{
var path = String.Format("issue/{0}", issueRef);
var path = Format("issue/{0}", issueRef);
var request = CreateRequest(Method.GET, path);

var response = ExecuteRequest(request);
Expand Down Expand Up @@ -163,11 +207,22 @@ public Issue<TIssueFields> CreateIssue(String projectKey, String issueType, TIss
if (issueFields.timetracking != null)
issueData.Add("timetracking", new { originalEstimate = issueFields.timetracking.originalEstimate });

var propertyList = typeof(TIssueFields).GetProperties().Where(p => p.Name.StartsWith("customfield_"));
foreach (var property in propertyList)
var propertyInfos = typeof(TIssueFields).GetProperties();
var propertiesFromAttribute = propertyInfos
.Select(p => new { Property = p, FieldAttribute = p.GetAttribute<FieldAttribute>() })
.Where(a => a.FieldAttribute != null)
.Select(p => new NamedProperty(p.Property, p.FieldAttribute.FieldName));

var customFields = propertyInfos
.Where(p => p.Name.StartsWith("customfield_"))
.Select(p => new NamedProperty(p, p.Name));

var propertyList = customFields.Concat(propertiesFromAttribute);

foreach (var namedProperty in propertyList)
{
var value = property.GetValue(issueFields, null);
if (value != null) issueData.Add(property.Name, value);
var value = namedProperty.Property.GetValue(issueFields, null);
if (value != null) issueData.Add(namedProperty.FieldName, value);
}

request.AddBody(new { fields = issueData });
Expand All @@ -189,7 +244,7 @@ public Issue<TIssueFields> UpdateIssue(Issue<TIssueFields> issue)
{
try
{
var path = String.Format("issue/{0}", issue.JiraIdentifier);
var path = Format("issue/{0}", issue.JiraIdentifier);
var request = CreateRequest(Method.PUT, path);
request.AddHeader("ContentType", "application/json");

Expand Down Expand Up @@ -228,7 +283,7 @@ public void DeleteIssue(IssueRef issue)
{
try
{
var path = String.Format("issue/{0}?deleteSubtasks=true", issue.id);
var path = Format("issue/{0}?deleteSubtasks=true", issue.id);
var request = CreateRequest(Method.DELETE, path);

var response = ExecuteRequest(request);
Expand All @@ -246,7 +301,7 @@ public IEnumerable<Transition> GetTransitions(IssueRef issue)
{
try
{
var path = String.Format("issue/{0}/transitions?expand=transitions.fields", issue.id);
var path = Format("issue/{0}/transitions?expand=transitions.fields", issue.id);
var request = CreateRequest(Method.GET, path);

var response = ExecuteRequest(request);
Expand All @@ -266,7 +321,7 @@ public Issue<TIssueFields> TransitionIssue(IssueRef issue, Transition transition
{
try
{
var path = String.Format("issue/{0}/transitions", issue.id);
var path = Format("issue/{0}/transitions", issue.id);
var request = CreateRequest(Method.POST, path);
request.AddHeader("ContentType", "application/json");

Expand Down Expand Up @@ -294,7 +349,7 @@ public IEnumerable<JiraUser> GetWatchers(IssueRef issue)
{
try
{
var path = String.Format("issue/{0}/watchers", issue.id);
var path = Format("issue/{0}/watchers", issue.id);
var request = CreateRequest(Method.GET, path);

var response = ExecuteRequest(request);
Expand All @@ -314,7 +369,7 @@ public IEnumerable<Comment> GetComments(IssueRef issue)
{
try
{
var path = String.Format("issue/{0}/comment", issue.id);
var path = Format("issue/{0}/comment", issue.id);
var request = CreateRequest(Method.GET, path);

var response = ExecuteRequest(request);
Expand All @@ -334,7 +389,7 @@ public Comment CreateComment(IssueRef issue, String comment)
{
try
{
var path = String.Format("issue/{0}/comment", issue.id);
var path = Format("issue/{0}/comment", issue.id);
var request = CreateRequest(Method.POST, path);
request.AddHeader("ContentType", "application/json");
request.AddBody(new Comment { body = comment });
Expand All @@ -355,7 +410,7 @@ public void DeleteComment(IssueRef issue, Comment comment)
{
try
{
var path = String.Format("issue/{0}/comment/{1}", issue.id, comment.id);
var path = Format("issue/{0}/comment/{1}", issue.id, comment.id);
var request = CreateRequest(Method.DELETE, path);

var response = ExecuteRequest(request);
Expand All @@ -378,7 +433,7 @@ public Attachment CreateAttachment(IssueRef issue, Stream fileStream, String fil
{
try
{
var path = String.Format("issue/{0}/attachments", issue.id);
var path = Format("issue/{0}/attachments", issue.id);
var request = CreateRequest(Method.POST, path);
request.AddHeader("X-Atlassian-Token", "nocheck");
request.AddHeader("ContentType", "multipart/form-data");
Expand All @@ -400,7 +455,7 @@ public void DeleteAttachment(Attachment attachment)
{
try
{
var path = String.Format("attachment/{0}", attachment.id);
var path = Format("attachment/{0}", attachment.id);
var request = CreateRequest(Method.DELETE, path);

var response = ExecuteRequest(request);
Expand Down Expand Up @@ -470,7 +525,7 @@ public void DeleteIssueLink(IssueLink link)
{
try
{
var path = String.Format("issueLink/{0}", link.id);
var path = Format("issueLink/{0}", link.id);
var request = CreateRequest(Method.DELETE, path);

var response = ExecuteRequest(request);
Expand All @@ -488,7 +543,7 @@ public IEnumerable<RemoteLink> GetRemoteLinks(IssueRef issue)
{
try
{
var path = string.Format("issue/{0}/remotelink", issue.id);
var path = Format("issue/{0}/remotelink", issue.id);
var request = CreateRequest(Method.GET, path);
request.AddHeader("ContentType", "application/json");

Expand All @@ -509,7 +564,7 @@ public RemoteLink CreateRemoteLink(IssueRef issue, RemoteLink remoteLink)
{
try
{
var path = string.Format("issue/{0}/remotelink", issue.id);
var path = Format("issue/{0}/remotelink", issue.id);
var request = CreateRequest(Method.POST, path);
request.AddHeader("ContentType", "application/json");
request.AddBody(new
Expand Down Expand Up @@ -545,7 +600,7 @@ public RemoteLink UpdateRemoteLink(IssueRef issue, RemoteLink remoteLink)
{
try
{
var path = string.Format("issue/{0}/remotelink/{1}", issue.id, remoteLink.id);
var path = Format("issue/{0}/remotelink/{1}", issue.id, remoteLink.id);
var request = CreateRequest(Method.PUT, path);
request.AddHeader("ContentType", "application/json");

Expand All @@ -571,7 +626,7 @@ public void DeleteRemoteLink(IssueRef issue, RemoteLink remoteLink)
{
try
{
var path = string.Format("issue/{0}/remotelink/{1}", issue.id, remoteLink.id);
var path = Format("issue/{0}/remotelink/{1}", issue.id, remoteLink.id);
var request = CreateRequest(Method.DELETE, path);
request.AddHeader("ContentType", "application/json");

Expand Down
17 changes: 17 additions & 0 deletions TechTalk.JiraRestClient/NamedProperty.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace TechTalk.JiraRestClient
{
using System.Reflection;

internal class NamedProperty
{
public PropertyInfo Property { get; set; }

public string FieldName { get; set; }

public NamedProperty(PropertyInfo property, string fieldName)
{
this.Property = property;
this.FieldName = fieldName;
}
}
}
2 changes: 2 additions & 0 deletions TechTalk.JiraRestClient/TechTalk.JiraRestClient.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@
<Compile Include="Comment.cs" />
<Compile Include="CommentsContainer.cs" />
<Compile Include="Compatibility.cs" />
<Compile Include="FieldAttribute.cs" />
<Compile Include="IssueType.cs" />
<Compile Include="NamedProperty.cs" />
<Compile Include="ServerInfo.cs" />
<Compile Include="WatchersContainer.cs" />
<Compile Include="IJiraClient.cs" />
Expand Down
75 changes: 75 additions & 0 deletions TechTalk.JiraRestClient/TechTalk.JiraRestClient_NoNuget.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{210529FA-454E-4C32-A2C8-353ECBD4DA05}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>TechTalk.JiraRestClient</RootNamespace>
<AssemblyName>TechTalk.JiraRestClient</AssemblyName>
<TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
<TargetFrameworkProfile>
</TargetFrameworkProfile>
<FileAlignment>512</FileAlignment>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<RestorePackages>true</RestorePackages>
</PropertyGroup>
<PropertyGroup>
<StartupObject />
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'">
<PlatformTarget>AnyCPU</PlatformTarget>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'">
<PlatformTarget>AnyCPU</PlatformTarget>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="RestSharp">
<HintPath>..\..\packages\RestSharp.104.1\lib\net4-client\RestSharp.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
</ItemGroup>
<ItemGroup>
<Compile Include="Attachment.cs" />
<Compile Include="Comment.cs" />
<Compile Include="CommentsContainer.cs" />
<Compile Include="Compatibility.cs" />
<Compile Include="FieldAttribute.cs" />
<Compile Include="IssueType.cs" />
<Compile Include="NamedProperty.cs" />
<Compile Include="ServerInfo.cs" />
<Compile Include="WatchersContainer.cs" />
<Compile Include="IJiraClient.cs" />
<Compile Include="Issue.cs" />
<Compile Include="IssueContainer.cs" />
<Compile Include="IssueFields.cs" />
<Compile Include="IssueLink.cs" />
<Compile Include="IssueRef.cs" />
<Compile Include="JiraClient.cs" />
<Compile Include="JiraClientException.cs" />
<Compile Include="JiraUser.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RemoteLink.cs" />
<Compile Include="RemoteLinkResult.cs" />
<Compile Include="Status.cs" />
<Compile Include="Transition.cs" />
<Compile Include="Timetracking.cs" />
<Compile Include="TransitionsContainer.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config">
<SubType>Designer</SubType>
</None>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
Loading