From ae64087c36923b203c4f8663abe67afefe1abfe0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Santiago=20Ace=C3=B1olaza?= Date: Wed, 15 Apr 2015 14:59:10 -0300 Subject: [PATCH] Added REST proxy class --- .../JiraConnectorFactory.cs | 18 +- .../Rest/JiraRestProxy.cs | 298 ++++++++++++++++++ .../VersionOne.JiraConnector.csproj | 10 + VersionOne.JiraConnector/packages.config | 4 + .../JiraHostedService.cs | 89 ++++-- VersionOne.ServiceHost/App.config | 16 +- .../VersionOne.ServiceHost.csproj | 4 + VersionOne.ServiceHost/packages.config | 4 + 8 files changed, 399 insertions(+), 44 deletions(-) create mode 100644 VersionOne.JiraConnector/Rest/JiraRestProxy.cs create mode 100644 VersionOne.JiraConnector/packages.config create mode 100644 VersionOne.ServiceHost/packages.config diff --git a/VersionOne.JiraConnector/JiraConnectorFactory.cs b/VersionOne.JiraConnector/JiraConnectorFactory.cs index e1fe1ac..75b7619 100644 --- a/VersionOne.JiraConnector/JiraConnectorFactory.cs +++ b/VersionOne.JiraConnector/JiraConnectorFactory.cs @@ -1,22 +1,28 @@ /*(c) Copyright 2012, VersionOne, Inc. All rights reserved. (c)*/ using System; +using VersionOne.JiraConnector.Rest; using VersionOne.JiraConnector.Soap; -namespace VersionOne.JiraConnector { - public class JiraConnectorFactory { +namespace VersionOne.JiraConnector +{ + public class JiraConnectorFactory + { public readonly JiraConnectorType ConnectorType; - public JiraConnectorFactory(JiraConnectorType connectorType) { + public JiraConnectorFactory(JiraConnectorType connectorType) + { ConnectorType = connectorType; } - public IJiraConnector Create(string url, string username, string password) { - switch (ConnectorType) { + public IJiraConnector Create(string url, string username, string password) + { + switch (ConnectorType) + { case JiraConnectorType.Soap: return new JiraSoapProxy(url, username, password); case JiraConnectorType.Rest: - throw new NotImplementedException(); + return new JiraRestProxy(url, username, password); default: throw new NotSupportedException(); diff --git a/VersionOne.JiraConnector/Rest/JiraRestProxy.cs b/VersionOne.JiraConnector/Rest/JiraRestProxy.cs new file mode 100644 index 0000000..f0103ad --- /dev/null +++ b/VersionOne.JiraConnector/Rest/JiraRestProxy.cs @@ -0,0 +1,298 @@ +using System; +using System.Collections.Generic; +using System.Dynamic; +using System.Linq; +using System.Net; +using RestSharp; +using System.Web.Helpers; +using VersionOne.JiraConnector.Exceptions; + +namespace VersionOne.JiraConnector.Rest +{ + public class JiraRestProxy : IJiraConnector + { + private readonly RestClient client; + + public JiraRestProxy(string baseUrl) + : this(baseUrl, string.Empty, string.Empty) + { + } + + public JiraRestProxy(string baseUrl, string username, string password) + { + client = new RestClient(baseUrl); + + if (!string.IsNullOrEmpty(username) && !string.IsNullOrEmpty(password)) + { + client.Authenticator = new HttpBasicAuthenticator(username, password); + } + } + + public void Login() + { + //throw new NotImplementedException(); + } + + public void Logout() + { + //throw new NotImplementedException(); + } + + public Issue[] GetIssuesFromFilter(string issueFilterId) + { + var request = new RestRequest + { + Method = Method.GET, + Resource = "search", + RequestFormat = DataFormat.Json + }; + request.AddQueryParameter("jql", string.Format("filter={0}", issueFilterId)); + + var response = client.Execute(request); + + if (response.StatusCode.Equals(HttpStatusCode.OK)) + { + dynamic data = Json.Decode(response.Content); + return ((IEnumerable)data.issues).Select(i => (Issue)CreateIssue(i)).ToArray(); + } + if (response.StatusCode.Equals(HttpStatusCode.Unauthorized)) + throw new JiraLoginException(); + throw new JiraException(response.StatusDescription, new Exception(response.Content)); + } + + public Issue UpdateIssue(string issueKey, string fieldName, string fieldValue) + { + dynamic editMetadata = GetEditMetadata(issueKey); + dynamic fieldMeta = editMetadata.fields[fieldName]; + + if (fieldMeta == null) + throw new JiraException("Field metadata is missing", null); + if (fieldMeta.schema.type == null) + throw new JiraException("Field metadata is missing a type", null); + + var request = new RestRequest + { + Method = Method.PUT, + Resource = "issue/{issueIdOrKey}", + RequestFormat = DataFormat.Json, + }; + request.AddUrlSegment("issueIdOrKey", issueKey); + + dynamic body; + if (fieldMeta.schema.type.Equals("array")) + { + dynamic operation = new ExpandoObject(); + ((IDictionary)operation).Add(fieldName, new List + { + new + { + set = new List { fieldValue } + } + }); + body = new { update = operation }; + } + else + { + dynamic field = new ExpandoObject(); + ((IDictionary)field).Add(fieldName, fieldValue); + body = new { fields = field }; + } + request.AddBody(body); + + var response = client.Execute(request); + + if (response.StatusCode.Equals(HttpStatusCode.NoContent)) + return GetIssue(issueKey); + if (response.StatusCode.Equals(HttpStatusCode.Unauthorized)) + throw new JiraLoginException(); + throw new JiraException(response.StatusDescription, new Exception(response.Content)); + } + + public IList GetPriorities() + { + var request = new RestRequest + { + Method = Method.GET, + Resource = "priority", + RequestFormat = DataFormat.Json + }; + + var response = client.Execute(request); + + if (response.StatusCode.Equals(HttpStatusCode.OK)) + { + dynamic data = Json.Decode(response.Content); + return ((IEnumerable)data).Select(i => new Item(i.id, i.name)).ToList(); + } + if (response.StatusCode.Equals(HttpStatusCode.Unauthorized)) + throw new JiraLoginException(); + throw new JiraException(response.StatusDescription, new Exception(response.Content)); + } + + public IList GetProjects() + { + var request = new RestRequest + { + Method = Method.GET, + Resource = "project", + RequestFormat = DataFormat.Json + }; + + var response = client.Execute(request); + + if (response.StatusCode.Equals(HttpStatusCode.OK)) + { + dynamic data = Json.Decode(response.Content); + return ((IEnumerable)data).Select(i => new Item(i.id, i.name)).ToList(); + } + if (response.StatusCode.Equals(HttpStatusCode.Unauthorized)) + throw new JiraLoginException(); + throw new JiraException(response.StatusDescription, new Exception(response.Content)); + } + + public void AddComment(string issueKey, string comment) + { + var request = new RestRequest + { + Method = Method.POST, + Resource = "issue/{issueIdOrKey}/comment", + RequestFormat = DataFormat.Json, + }; + request.AddUrlSegment("issueIdOrKey", issueKey); + + request.AddBody(new { body = comment }); + + var response = client.Execute(request); + + if (response.StatusCode.Equals(HttpStatusCode.Created)) + return; + if (response.StatusCode.Equals(HttpStatusCode.Unauthorized)) + throw new JiraLoginException(); + throw new JiraException(response.StatusDescription, new Exception(response.Content)); + } + + public void ProgressWorkflow(string issueKey, string action, string assignee) + { + var request = new RestRequest + { + Method = Method.POST, + Resource = "issue/{issueIdOrKey}/transitions", + RequestFormat = DataFormat.Json, + }; + request.AddUrlSegment("issueIdOrKey", issueKey); + + dynamic body = new ExpandoObject(); + ((IDictionary)body).Add("transition", new { id = action }); + if (assignee != null) + ((IDictionary)body).Add("fields", new { assignee = new { name = assignee } }); + request.AddBody(body); + + var response = client.Execute(request); + + if (response.StatusCode.Equals(HttpStatusCode.NoContent)) + return; + if (response.StatusCode.Equals(HttpStatusCode.Unauthorized)) + throw new JiraLoginException(); + throw new JiraException(response.StatusDescription, new Exception(response.Content)); + } + + public IEnumerable GetAvailableActions(string issueId) + { + var request = new RestRequest + { + Method = Method.GET, + Resource = "issue/{issueIdOrKey}/transitions?expand=transitions.fields", + RequestFormat = DataFormat.Json + }; + request.AddUrlSegment("issueIdOrKey", issueId); + + var response = client.Execute(request); + + if (response.StatusCode.Equals(HttpStatusCode.OK)) + { + dynamic data = Json.Decode(response.Content); + return ((IEnumerable)data.transitions).Select(i => new Item(i.id, i.name)).ToList(); + } + if (response.StatusCode.Equals(HttpStatusCode.Unauthorized)) + throw new JiraLoginException(); + throw new JiraException(response.StatusDescription, new Exception(response.Content)); + } + + public IEnumerable GetCustomFields() + { + var request = new RestRequest + { + Method = Method.GET, + Resource = "field", + RequestFormat = DataFormat.Json + }; + + var response = client.Execute(request); + + if (response.StatusCode.Equals(HttpStatusCode.OK)) + { + dynamic data = Json.Decode(response.Content); + return ((IEnumerable)data).Where(i => i.custom).Select(i => new Item(i.id, i.name)).ToList(); + } + if (response.StatusCode.Equals(HttpStatusCode.Unauthorized)) + throw new JiraLoginException(); + throw new JiraException(response.StatusDescription, new Exception(response.Content)); + } + + private dynamic GetEditMetadata(string issueIdOrKey) + { + var request = new RestRequest + { + Method = Method.GET, + Resource = "issue/{issueIdOrKey}/editmeta", + RequestFormat = DataFormat.Json + }; + request.AddUrlSegment("issueIdOrKey", issueIdOrKey); + + var response = client.Execute(request); + + if (response.StatusCode.Equals(HttpStatusCode.OK)) + return Json.Decode(response.Content); + if (response.StatusCode.Equals(HttpStatusCode.Unauthorized)) + throw new JiraLoginException(); + throw new JiraException(response.StatusDescription, new Exception(response.Content)); + } + + private Issue GetIssue(string issueIdOrKey) + { + var request = new RestRequest + { + Method = Method.GET, + Resource = "issue/{issueIdOrKey}", + RequestFormat = DataFormat.Json + }; + request.AddUrlSegment("issueIdOrKey", issueIdOrKey); + + var response = client.Execute(request); + + if (response.StatusCode.Equals(HttpStatusCode.OK)) + { + dynamic data = Json.Decode(response.Content); + return CreateIssue(data); + } + if (response.StatusCode.Equals(HttpStatusCode.Unauthorized)) + throw new JiraLoginException(); + throw new JiraException(response.StatusDescription, new Exception(response.Content)); + } + + private Issue CreateIssue(dynamic data) + { + return new Issue + { + Id = data.id, + Key = data.key, + Summary = data.fields.summary, + Description = data.fields.description, + Project = data.fields.project != null ? data.fields.project.name : string.Empty, + IssueType = data.fields.issuetype != null ? data.fields.issuetype.name : string.Empty, + Assignee = data.fields.assignee != null ? data.fields.assignee.name : string.Empty, + Priority = data.fields.priority != null ? data.fields.priority.name : string.Empty + }; + } + } +} diff --git a/VersionOne.JiraConnector/VersionOne.JiraConnector.csproj b/VersionOne.JiraConnector/VersionOne.JiraConnector.csproj index 4549654..c3d1561 100644 --- a/VersionOne.JiraConnector/VersionOne.JiraConnector.csproj +++ b/VersionOne.JiraConnector/VersionOne.JiraConnector.csproj @@ -72,12 +72,20 @@ false + + False + + + False + ..\packages\RestSharp.105.0.1\lib\net4\RestSharp.dll + 3.5 + @@ -91,6 +99,7 @@ + @@ -118,6 +127,7 @@ + - http://jira-6.cloudapp.net:8080/rpc/soap/jirasoapservice-v2 + http://jira-64.cloudapp.net:8080/rest/api/latest admin admin - - + + @@ -55,12 +55,12 @@ Open customfield_10001 Closed - 21 - 31 + 11 + 21 -1 - http://jira-6.cloudapp.net:8080/browse/#key# + http://jira-64.cloudapp.net:8080/browse/#key# JIRA @@ -72,7 +72,7 @@ - ScrumTest + JIRA Integration Test @@ -99,7 +99,7 @@ - 2000 + 5000 VersionOne.ServiceHost.JiraServices.JiraHostedService+IntervalSync, VersionOne.ServiceHost.JiraServices diff --git a/VersionOne.ServiceHost/VersionOne.ServiceHost.csproj b/VersionOne.ServiceHost/VersionOne.ServiceHost.csproj index 497cbb7..7393d83 100644 --- a/VersionOne.ServiceHost/VersionOne.ServiceHost.csproj +++ b/VersionOne.ServiceHost/VersionOne.ServiceHost.csproj @@ -79,6 +79,9 @@ false + + ..\packages\RestSharp.105.0.1\lib\net4\RestSharp.dll + @@ -107,6 +110,7 @@ Always Designer + diff --git a/VersionOne.ServiceHost/packages.config b/VersionOne.ServiceHost/packages.config new file mode 100644 index 0000000..02326fb --- /dev/null +++ b/VersionOne.ServiceHost/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file