PageRenderTime 1050ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/Atlassian.Jira/Remote/IssueService.cs

https://bitbucket.org/timootei/atlassian.net-sdk
C# | 550 lines | 457 code | 91 blank | 2 comment | 33 complexity | 236b035accb294ea3b92ed4c6e85ad0d MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Linq;
  5. using System.Threading;
  6. using System.Threading.Tasks;
  7. using Atlassian.Jira.Linq;
  8. using Newtonsoft.Json;
  9. using Newtonsoft.Json.Linq;
  10. using RestSharp;
  11. namespace Atlassian.Jira.Remote
  12. {
  13. internal class IssueService : IIssueService
  14. {
  15. private const int DEFAULT_MAX_ISSUES_PER_REQUEST = 20;
  16. private readonly Jira _jira;
  17. private readonly JiraRestClientSettings _restSettings;
  18. private readonly string[] _excludedFields = new string[] { "*all", "-comment", "-attachment", "-issuelinks", "-subtasks", "-watches", "-worklog" };
  19. private JsonSerializerSettings _serializerSettings;
  20. private class JqlOptions
  21. {
  22. public JqlOptions(string jql, CancellationToken token)
  23. {
  24. this.Jql = jql;
  25. this.Token = token;
  26. }
  27. public string Jql { get; private set; }
  28. public CancellationToken Token { get; private set; }
  29. public int MaxIssuesPerRequest { get; set; } = DEFAULT_MAX_ISSUES_PER_REQUEST;
  30. public int StartAt { get; set; } = 0;
  31. public bool ValidateQuery { get; set; } = true;
  32. public IEnumerable<string> Fields { get; set; }
  33. }
  34. public IssueService(Jira jira, JiraRestClientSettings restSettings)
  35. {
  36. _jira = jira;
  37. _restSettings = restSettings;
  38. }
  39. public JiraQueryable<Issue> Queryable
  40. {
  41. get
  42. {
  43. var translator = _jira.Services.Get<IJqlExpressionVisitor>();
  44. var provider = new JiraQueryProvider(translator, this);
  45. return new JiraQueryable<Issue>(provider);
  46. }
  47. }
  48. public bool ValidateQuery { get; set; } = true;
  49. public int MaxIssuesPerRequest { get; set; } = DEFAULT_MAX_ISSUES_PER_REQUEST;
  50. private async Task<JsonSerializerSettings> GetIssueSerializerSettingsAsync(CancellationToken token)
  51. {
  52. if (this._serializerSettings == null)
  53. {
  54. var fieldService = _jira.Services.Get<IIssueFieldService>();
  55. var customFields = await fieldService.GetCustomFieldsAsync(token).ConfigureAwait(false);
  56. var remoteFields = customFields.Select(f => f.RemoteField);
  57. var serializers = new Dictionary<string, ICustomFieldValueSerializer>(this._restSettings.CustomFieldSerializers, StringComparer.InvariantCultureIgnoreCase);
  58. this._serializerSettings = new JsonSerializerSettings();
  59. this._serializerSettings.NullValueHandling = NullValueHandling.Ignore;
  60. this._serializerSettings.Converters.Add(new RemoteIssueJsonConverter(remoteFields, serializers));
  61. }
  62. return this._serializerSettings;
  63. }
  64. public async Task<Issue> GetIssueAsync(string issueKey, CancellationToken token = default(CancellationToken))
  65. {
  66. var excludedFields = String.Join(",", _excludedFields);
  67. var resource = $"rest/api/2/issue/{issueKey}?fields={excludedFields}";
  68. var response = await _jira.RestClient.ExecuteRequestAsync(Method.GET, resource, null, token).ConfigureAwait(false);
  69. var serializerSettings = await GetIssueSerializerSettingsAsync(token).ConfigureAwait(false);
  70. var issue = JsonConvert.DeserializeObject<RemoteIssueWrapper>(response.ToString(), serializerSettings);
  71. return new Issue(_jira, issue.RemoteIssue);
  72. }
  73. public Task<IPagedQueryResult<Issue>> GetIssuesFromJqlAsync(string jql, int? maxIssues = default(int?), int startAt = 0, CancellationToken token = default(CancellationToken))
  74. {
  75. return GetIssuesFromJqlAsync(new JqlOptions(jql, token)
  76. {
  77. MaxIssuesPerRequest = maxIssues ?? this.MaxIssuesPerRequest,
  78. StartAt = startAt,
  79. ValidateQuery = this.ValidateQuery
  80. });
  81. }
  82. public Task<IPagedQueryResult<Issue>> GetIssuesFromJqlAsync(string jql, IEnumerable<string> fields, int? maxIssues = null, int startAt = 0, CancellationToken token = default(CancellationToken))
  83. {
  84. return GetIssuesFromJqlAsync(new JqlOptions(jql, token)
  85. {
  86. MaxIssuesPerRequest = maxIssues ?? this.MaxIssuesPerRequest,
  87. StartAt = startAt,
  88. ValidateQuery = this.ValidateQuery,
  89. Fields = fields
  90. });
  91. }
  92. private async Task<IPagedQueryResult<Issue>> GetIssuesFromJqlAsync(JqlOptions options)
  93. {
  94. if (_jira.Debug)
  95. {
  96. Trace.WriteLine("[GetFromJqlAsync] JQL: " + options.Jql);
  97. }
  98. var parameters = new
  99. {
  100. jql = options.Jql,
  101. startAt = options.StartAt,
  102. maxResults = options.MaxIssuesPerRequest,
  103. validateQuery = options.ValidateQuery,
  104. fields = options.Fields ?? _excludedFields
  105. };
  106. var result = await _jira.RestClient.ExecuteRequestAsync(Method.POST, "rest/api/2/search", parameters, options.Token).ConfigureAwait(false);
  107. var serializerSettings = await this.GetIssueSerializerSettingsAsync(options.Token).ConfigureAwait(false);
  108. var issues = result["issues"]
  109. .Cast<JObject>()
  110. .Select(issueJson =>
  111. {
  112. var remoteIssue = JsonConvert.DeserializeObject<RemoteIssueWrapper>(issueJson.ToString(), serializerSettings).RemoteIssue;
  113. return new Issue(_jira, remoteIssue);
  114. });
  115. return PagedQueryResult<Issue>.FromJson((JObject)result, issues);
  116. }
  117. public async Task UpdateIssueAsync(Issue issue, IssueUpdateOptions options, CancellationToken token = default(CancellationToken))
  118. {
  119. var resource = String.Format("rest/api/2/issue/{0}", issue.Key.Value);
  120. if (options.SuppressEmailNotification)
  121. {
  122. resource += "?notifyUsers=false";
  123. }
  124. var fieldProvider = issue as IRemoteIssueFieldProvider;
  125. var remoteFields = await fieldProvider.GetRemoteFieldValuesAsync(token).ConfigureAwait(false);
  126. var remoteIssue = await issue.ToRemoteAsync(token).ConfigureAwait(false);
  127. var fields = await this.BuildFieldsObjectFromIssueAsync(remoteIssue, remoteFields, token).ConfigureAwait(false);
  128. await _jira.RestClient.ExecuteRequestAsync(Method.PUT, resource, new { fields = fields }, token).ConfigureAwait(false);
  129. }
  130. public Task UpdateIssueAsync(Issue issue, CancellationToken token = default(CancellationToken))
  131. {
  132. var options = new IssueUpdateOptions();
  133. return UpdateIssueAsync(issue, options, token);
  134. }
  135. public async Task<string> CreateIssueAsync(Issue issue, CancellationToken token = default(CancellationToken))
  136. {
  137. var remoteIssue = await issue.ToRemoteAsync(token).ConfigureAwait(false);
  138. var remoteIssueWrapper = new RemoteIssueWrapper(remoteIssue, issue.ParentIssueKey);
  139. var serializerSettings = await this.GetIssueSerializerSettingsAsync(token).ConfigureAwait(false);
  140. var requestBody = JsonConvert.SerializeObject(remoteIssueWrapper, serializerSettings);
  141. var result = await _jira.RestClient.ExecuteRequestAsync(Method.POST, "rest/api/2/issue", requestBody, token).ConfigureAwait(false);
  142. return (string)result["key"];
  143. }
  144. private async Task<JObject> BuildFieldsObjectFromIssueAsync(RemoteIssue remoteIssue, RemoteFieldValue[] remoteFields, CancellationToken token)
  145. {
  146. var issueWrapper = new RemoteIssueWrapper(remoteIssue);
  147. var serializerSettings = await this.GetIssueSerializerSettingsAsync(token).ConfigureAwait(false);
  148. var issueJson = JsonConvert.SerializeObject(issueWrapper, serializerSettings);
  149. var fieldsJsonSerializerSettings = new JsonSerializerSettings()
  150. {
  151. DateParseHandling = DateParseHandling.None
  152. };
  153. var issueFields = JsonConvert.DeserializeObject<JObject>(issueJson, fieldsJsonSerializerSettings)["fields"] as JObject;
  154. var updateFields = new JObject();
  155. foreach (var field in remoteFields)
  156. {
  157. var issueFieldName = field.id;
  158. var issueFieldValue = issueFields[issueFieldName];
  159. if (issueFieldValue == null && issueFieldName.Equals("components", StringComparison.OrdinalIgnoreCase))
  160. {
  161. // JIRA does not accept 'null' as a valid value for the 'components' field.
  162. // So if the components field has been cleared it must be set to empty array instead.
  163. issueFieldValue = new JArray();
  164. }
  165. updateFields.Add(issueFieldName, issueFieldValue);
  166. }
  167. return updateFields;
  168. }
  169. public async Task ExecuteWorkflowActionAsync(Issue issue, string actionName, WorkflowTransitionUpdates updates, CancellationToken token = default(CancellationToken))
  170. {
  171. var actions = await this.GetActionsAsync(issue.Key.Value, token).ConfigureAwait(false);
  172. var action = actions.FirstOrDefault(a => a.Name.Equals(actionName, StringComparison.OrdinalIgnoreCase));
  173. if (action == null)
  174. {
  175. throw new InvalidOperationException(String.Format("Workflow action with name '{0}' not found.", actionName));
  176. }
  177. updates = updates ?? new WorkflowTransitionUpdates();
  178. var resource = String.Format("rest/api/2/issue/{0}/transitions", issue.Key.Value);
  179. var fieldProvider = issue as IRemoteIssueFieldProvider;
  180. var remoteFields = await fieldProvider.GetRemoteFieldValuesAsync(token).ConfigureAwait(false);
  181. var remoteIssue = await issue.ToRemoteAsync(token).ConfigureAwait(false);
  182. var fields = await BuildFieldsObjectFromIssueAsync(remoteIssue, remoteFields, token).ConfigureAwait(false);
  183. var updatesObject = new JObject();
  184. if (!String.IsNullOrEmpty(updates.Comment))
  185. {
  186. updatesObject.Add("comment", new JArray(new JObject[]
  187. {
  188. new JObject(new JProperty("add",
  189. new JObject(new JProperty("body", updates.Comment))))
  190. }));
  191. }
  192. var requestBody = new
  193. {
  194. transition = new
  195. {
  196. id = action.Id
  197. },
  198. update = updatesObject,
  199. fields = fields
  200. };
  201. await _jira.RestClient.ExecuteRequestAsync(Method.POST, resource, requestBody, token).ConfigureAwait(false);
  202. }
  203. public async Task<IssueTimeTrackingData> GetTimeTrackingDataAsync(string issueKey, CancellationToken token = default(CancellationToken))
  204. {
  205. if (String.IsNullOrEmpty(issueKey))
  206. {
  207. throw new InvalidOperationException("Unable to retrieve time tracking data, make sure the issue has been created.");
  208. }
  209. var resource = String.Format("rest/api/2/issue/{0}?fields=timetracking", issueKey);
  210. var response = await _jira.RestClient.ExecuteRequestAsync(Method.GET, resource, null, token).ConfigureAwait(false);
  211. var serializerSettings = _jira.RestClient.Settings.JsonSerializerSettings;
  212. var timeTrackingJson = response["fields"]?["timetracking"];
  213. if (timeTrackingJson != null)
  214. {
  215. return JsonConvert.DeserializeObject<IssueTimeTrackingData>(timeTrackingJson.ToString(), serializerSettings);
  216. }
  217. else
  218. {
  219. return null;
  220. }
  221. }
  222. public async Task<IDictionary<string, IssueFieldEditMetadata>> GetFieldsEditMetadataAsync(string issueKey, CancellationToken token = default(CancellationToken))
  223. {
  224. var dict = new Dictionary<string, IssueFieldEditMetadata>();
  225. var resource = String.Format("rest/api/2/issue/{0}/editmeta", issueKey);
  226. var serializer = JsonSerializer.Create(_jira.RestClient.Settings.JsonSerializerSettings);
  227. var result = await _jira.RestClient.ExecuteRequestAsync(Method.GET, resource, null, token).ConfigureAwait(false);
  228. JObject fields = result["fields"].Value<JObject>();
  229. foreach (var prop in fields.Properties())
  230. {
  231. var fieldName = (prop.Value["name"] ?? prop.Name).ToString();
  232. dict.Add(fieldName, prop.Value.ToObject<IssueFieldEditMetadata>(serializer));
  233. }
  234. return dict;
  235. }
  236. public async Task<Comment> AddCommentAsync(string issueKey, Comment comment, CancellationToken token = default(CancellationToken))
  237. {
  238. if (String.IsNullOrEmpty(comment.Author))
  239. {
  240. throw new InvalidOperationException("Unable to add comment due to missing author field.");
  241. }
  242. var resource = String.Format("rest/api/2/issue/{0}/comment", issueKey);
  243. var remoteComment = await _jira.RestClient.ExecuteRequestAsync<RemoteComment>(Method.POST, resource, comment.toRemote(), token).ConfigureAwait(false);
  244. return new Comment(remoteComment);
  245. }
  246. public async Task<IPagedQueryResult<Comment>> GetPagedCommentsAsync(string issueKey, int? maxComments = default(int?), int startAt = 0, CancellationToken token = default(CancellationToken))
  247. {
  248. var resource = $"rest/api/2/issue/{issueKey}/comment?startAt={startAt}";
  249. if (maxComments.HasValue)
  250. {
  251. resource += $"&maxResults={maxComments.Value}";
  252. }
  253. var result = await _jira.RestClient.ExecuteRequestAsync(Method.GET, resource).ConfigureAwait(false);
  254. var serializerSettings = _jira.RestClient.Settings.JsonSerializerSettings;
  255. var comments = result["comments"]
  256. .Cast<JObject>()
  257. .Select(commentJson =>
  258. {
  259. var remoteComment = JsonConvert.DeserializeObject<RemoteComment>(commentJson.ToString(), serializerSettings);
  260. return new Comment(remoteComment);
  261. });
  262. return PagedQueryResult<Comment>.FromJson((JObject)result, comments);
  263. }
  264. public async Task<IEnumerable<JiraNamedEntity>> GetActionsAsync(string issueKey, CancellationToken token = default(CancellationToken))
  265. {
  266. var resource = String.Format("rest/api/2/issue/{0}/transitions", issueKey);
  267. var serializerSettings = _jira.RestClient.Settings.JsonSerializerSettings;
  268. var result = await _jira.RestClient.ExecuteRequestAsync(Method.GET, resource, null, token).ConfigureAwait(false);
  269. var remoteTransitions = JsonConvert.DeserializeObject<RemoteNamedObject[]>(result["transitions"].ToString(), serializerSettings);
  270. return remoteTransitions.Select(transition => new JiraNamedEntity(transition));
  271. }
  272. public async Task<IEnumerable<Attachment>> GetAttachmentsAsync(string issueKey, CancellationToken token = default(CancellationToken))
  273. {
  274. var resource = String.Format("rest/api/2/issue/{0}?fields=attachment", issueKey);
  275. var serializerSettings = _jira.RestClient.Settings.JsonSerializerSettings;
  276. var result = await _jira.RestClient.ExecuteRequestAsync(Method.GET, resource, null, token).ConfigureAwait(false);
  277. var attachmentsJson = result["fields"]["attachment"];
  278. var attachments = JsonConvert.DeserializeObject<RemoteAttachment[]>(attachmentsJson.ToString(), serializerSettings);
  279. return attachments.Select(remoteAttachment => new Attachment(_jira, new WebClientWrapper(_jira), remoteAttachment));
  280. }
  281. public async Task<string[]> GetLabelsAsync(string issueKey, CancellationToken token = default(CancellationToken))
  282. {
  283. var resource = String.Format("rest/api/2/issue/{0}?fields=labels", issueKey);
  284. var serializerSettings = await this.GetIssueSerializerSettingsAsync(token).ConfigureAwait(false);
  285. var response = await _jira.RestClient.ExecuteRequestAsync(Method.GET, resource).ConfigureAwait(false);
  286. var issue = JsonConvert.DeserializeObject<RemoteIssueWrapper>(response.ToString(), serializerSettings);
  287. return issue.RemoteIssue.labels ?? new string[0];
  288. }
  289. public Task SetLabelsAsync(string issueKey, string[] labels, CancellationToken token = default(CancellationToken))
  290. {
  291. var resource = String.Format("rest/api/2/issue/{0}", issueKey);
  292. return _jira.RestClient.ExecuteRequestAsync(Method.PUT, resource, new
  293. {
  294. fields = new
  295. {
  296. labels = labels
  297. }
  298. }, token);
  299. }
  300. public async Task<IEnumerable<JiraUser>> GetWatchersAsync(string issueKey, CancellationToken token = default(CancellationToken))
  301. {
  302. if (string.IsNullOrEmpty(issueKey))
  303. {
  304. throw new InvalidOperationException("Unable to interact with the watchers resource, make sure the issue has been created.");
  305. }
  306. var resourceUrl = String.Format("rest/api/2/issue/{0}/watchers", issueKey);
  307. var serializerSettings = _jira.RestClient.Settings.JsonSerializerSettings;
  308. var result = await _jira.RestClient.ExecuteRequestAsync(Method.GET, resourceUrl, null, token).ConfigureAwait(false);
  309. var watchersJson = result["watchers"];
  310. return watchersJson.Select(watcherJson => JsonConvert.DeserializeObject<JiraUser>(watcherJson.ToString(), serializerSettings));
  311. }
  312. public async Task<IEnumerable<IssueChangeLog>> GetChangeLogsAsync(string issueKey, CancellationToken token = default(CancellationToken))
  313. {
  314. var resourceUrl = String.Format("rest/api/2/issue/{0}?fields=created&expand=changelog", issueKey);
  315. var serializerSettings = _jira.RestClient.Settings.JsonSerializerSettings;
  316. var response = await _jira.RestClient.ExecuteRequestAsync(Method.GET, resourceUrl, null, token).ConfigureAwait(false);
  317. var result = Enumerable.Empty<IssueChangeLog>();
  318. var changeLogs = response["changelog"];
  319. if (changeLogs != null)
  320. {
  321. var histories = changeLogs["histories"];
  322. if (histories != null)
  323. {
  324. result = histories.Select(history => JsonConvert.DeserializeObject<IssueChangeLog>(history.ToString(), serializerSettings));
  325. }
  326. }
  327. return result;
  328. }
  329. public Task DeleteWatcherAsync(string issueKey, string username, CancellationToken token = default(CancellationToken))
  330. {
  331. if (string.IsNullOrEmpty(issueKey))
  332. {
  333. throw new InvalidOperationException("Unable to interact with the watchers resource, make sure the issue has been created.");
  334. }
  335. var resourceUrl = String.Format("rest/api/2/issue/{0}/watchers?username={1}", issueKey, System.Uri.EscapeDataString(username));
  336. return _jira.RestClient.ExecuteRequestAsync(Method.DELETE, resourceUrl, null, token);
  337. }
  338. public Task AddWatcherAsync(string issueKey, string username, CancellationToken token = default(CancellationToken))
  339. {
  340. if (string.IsNullOrEmpty(issueKey))
  341. {
  342. throw new InvalidOperationException("Unable to interact with the watchers resource, make sure the issue has been created.");
  343. }
  344. var requestBody = String.Format("\"{0}\"", username);
  345. var resourceUrl = String.Format("rest/api/2/issue/{0}/watchers", issueKey);
  346. return _jira.RestClient.ExecuteRequestAsync(Method.POST, resourceUrl, requestBody, token);
  347. }
  348. public Task<IPagedQueryResult<Issue>> GetSubTasksAsync(string issueKey, int? maxIssues = default(int?), int startAt = 0, CancellationToken token = default(CancellationToken))
  349. {
  350. var jql = String.Format("parent = {0}", issueKey);
  351. return GetIssuesFromJqlAsync(jql, maxIssues, startAt, token);
  352. }
  353. public Task AddAttachmentsAsync(string issueKey, UploadAttachmentInfo[] attachments, CancellationToken token = default(CancellationToken))
  354. {
  355. var resource = String.Format("rest/api/2/issue/{0}/attachments", issueKey);
  356. var request = new RestRequest();
  357. request.Method = Method.POST;
  358. request.Resource = resource;
  359. request.AddHeader("X-Atlassian-Token", "nocheck");
  360. request.AlwaysMultipartFormData = true;
  361. foreach (var attachment in attachments)
  362. {
  363. request.AddFile("file", attachment.Data, attachment.Name);
  364. }
  365. return _jira.RestClient.ExecuteRequestAsync(request, token);
  366. }
  367. public Task DeleteAttachmentAsync(string issueKey, string attachmentId, CancellationToken token = default(CancellationToken))
  368. {
  369. var resource = String.Format("rest/api/2/attachment/{0}", attachmentId);
  370. return _jira.RestClient.ExecuteRequestAsync(Method.DELETE, resource, null, token);
  371. }
  372. public async Task<IDictionary<string, Issue>> GetIssuesAsync(IEnumerable<string> issueKeys, CancellationToken token = default(CancellationToken))
  373. {
  374. if (issueKeys.Any())
  375. {
  376. var distinctKeys = issueKeys.Distinct();
  377. var jql = String.Format("key in ({0})", String.Join(",", distinctKeys));
  378. var result = await this.GetIssuesFromJqlAsync(new JqlOptions(jql, token)
  379. {
  380. MaxIssuesPerRequest = distinctKeys.Count(),
  381. ValidateQuery = false
  382. }).ConfigureAwait(false);
  383. return result.ToDictionary<Issue, string>(i => i.Key.Value);
  384. }
  385. else
  386. {
  387. return new Dictionary<string, Issue>();
  388. }
  389. }
  390. public Task<IDictionary<string, Issue>> GetIssuesAsync(params string[] issueKeys)
  391. {
  392. return this.GetIssuesAsync(issueKeys, default(CancellationToken));
  393. }
  394. public async Task<IEnumerable<Comment>> GetCommentsAsync(string issueKey, CancellationToken token = default(CancellationToken))
  395. {
  396. var resource = String.Format("rest/api/2/issue/{0}/comment?expand=properties", issueKey);
  397. var serializerSettings = _jira.RestClient.Settings.JsonSerializerSettings;
  398. var issueJson = await _jira.RestClient.ExecuteRequestAsync(Method.GET, resource, null, token).ConfigureAwait(false);
  399. var commentJson = issueJson["comments"];
  400. var remoteComments = JsonConvert.DeserializeObject<RemoteComment[]>(commentJson.ToString(), serializerSettings);
  401. return remoteComments.Select(c => new Comment(c));
  402. }
  403. public Task DeleteCommentAsync(string issueKey, string commentId, CancellationToken token = default(CancellationToken))
  404. {
  405. var resource = String.Format("rest/api/2/issue/{0}/comment/{1}", issueKey, commentId);
  406. return _jira.RestClient.ExecuteRequestAsync(Method.DELETE, resource, null, token);
  407. }
  408. public async Task<Worklog> AddWorklogAsync(string issueKey, Worklog worklog, WorklogStrategy worklogStrategy = WorklogStrategy.AutoAdjustRemainingEstimate, string newEstimate = null, CancellationToken token = default(CancellationToken))
  409. {
  410. var remoteWorklog = worklog.ToRemote();
  411. string queryString = null;
  412. if (worklogStrategy == WorklogStrategy.RetainRemainingEstimate)
  413. {
  414. queryString = "adjustEstimate=leave";
  415. }
  416. else if (worklogStrategy == WorklogStrategy.NewRemainingEstimate)
  417. {
  418. queryString = "adjustEstimate=new&newEstimate=" + Uri.EscapeDataString(newEstimate);
  419. }
  420. var resource = String.Format("rest/api/2/issue/{0}/worklog?{1}", issueKey, queryString);
  421. var serverWorklog = await _jira.RestClient.ExecuteRequestAsync<RemoteWorklog>(Method.POST, resource, remoteWorklog, token).ConfigureAwait(false);
  422. return new Worklog(serverWorklog);
  423. }
  424. public Task DeleteWorklogAsync(string issueKey, string worklogId, WorklogStrategy worklogStrategy = WorklogStrategy.AutoAdjustRemainingEstimate, string newEstimate = null, CancellationToken token = default(CancellationToken))
  425. {
  426. string queryString = null;
  427. if (worklogStrategy == WorklogStrategy.RetainRemainingEstimate)
  428. {
  429. queryString = "adjustEstimate=leave";
  430. }
  431. else if (worklogStrategy == WorklogStrategy.NewRemainingEstimate)
  432. {
  433. queryString = "adjustEstimate=new&newEstimate=" + Uri.EscapeDataString(newEstimate);
  434. }
  435. var resource = String.Format("rest/api/2/issue/{0}/worklog/{1}?{2}", issueKey, worklogId, queryString);
  436. return _jira.RestClient.ExecuteRequestAsync(Method.DELETE, resource, null, token);
  437. }
  438. public async Task<IEnumerable<Worklog>> GetWorklogsAsync(string issueKey, CancellationToken token = default(CancellationToken))
  439. {
  440. var resource = String.Format("rest/api/2/issue/{0}/worklog", issueKey);
  441. var serializerSettings = _jira.RestClient.Settings.JsonSerializerSettings;
  442. var response = await _jira.RestClient.ExecuteRequestAsync(Method.GET, resource, null, token).ConfigureAwait(false);
  443. var worklogsJson = response["worklogs"];
  444. var remoteWorklogs = JsonConvert.DeserializeObject<RemoteWorklog[]>(worklogsJson.ToString(), serializerSettings);
  445. return remoteWorklogs.Select(w => new Worklog(w));
  446. }
  447. public async Task<Worklog> GetWorklogAsync(string issueKey, string worklogId, CancellationToken token = default(CancellationToken))
  448. {
  449. var resource = String.Format("rest/api/2/issue/{0}/worklog/{1}", issueKey, worklogId);
  450. var remoteWorklog = await _jira.RestClient.ExecuteRequestAsync<RemoteWorklog>(Method.GET, resource, null, token).ConfigureAwait(false);
  451. return new Worklog(remoteWorklog);
  452. }
  453. public Task DeleteIssueAsync(string issueKey, CancellationToken token = default(CancellationToken))
  454. {
  455. var resource = String.Format("rest/api/2/issue/{0}", issueKey);
  456. return _jira.RestClient.ExecuteRequestAsync(Method.DELETE, resource, null, token);
  457. }
  458. }
  459. }