Skip to content

Commit

Permalink
Added labels filtering to events
Browse files Browse the repository at this point in the history
  • Loading branch information
vitalii-bezuhlyi committed Aug 14, 2024
1 parent d0afecf commit 053f8d1
Show file tree
Hide file tree
Showing 8 changed files with 138 additions and 23 deletions.
5 changes: 5 additions & 0 deletions Apps.Jira/Apps.Jira.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,9 @@
<ItemGroup>
<EmbeddedResource CopyToOutputDirectory="Always" Include="image\icon.png" />
</ItemGroup>
<ItemGroup>
<Content Include="..\README.md">
<Link>README.md</Link>
</Content>
</ItemGroup>
</Project>
38 changes: 38 additions & 0 deletions Apps.Jira/DataSourceHandlers/LabelDataHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using Apps.Jira.Dtos;
using Blackbird.Applications.Sdk.Common.Dynamic;
using Blackbird.Applications.Sdk.Common.Invocation;
using RestSharp;

namespace Apps.Jira.DataSourceHandlers;

public class LabelDataHandler(InvocationContext invocationContext)
: JiraInvocable(invocationContext), IAsyncDataSourceHandler
{
public async Task<Dictionary<string, string>> GetDataAsync(DataSourceContext context, CancellationToken cancellationToken)
{
const int maxResultsPerPage = 1000;
int startAt = 0;
bool isLast = false;

var allLabels = new List<string>();

while (!isLast)
{
var request = new JiraRequest($"/label?startAt={startAt}&maxResults={maxResultsPerPage}", Method.Get);
var labels = await Client.ExecuteWithHandling<LabelsPaginationDto>(request);

if (labels?.Values != null)
{
allLabels.AddRange(labels.Values);
}

startAt += labels?.MaxResults ?? maxResultsPerPage;
isLast = labels?.IsLast ?? true;
}

return allLabels
.Where(x => context.SearchString == null
|| x.Contains(context.SearchString, StringComparison.OrdinalIgnoreCase))
.ToDictionary(l => l, l => l);
}
}
21 changes: 21 additions & 0 deletions Apps.Jira/Dtos/LabelsPaginationDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using Newtonsoft.Json;

namespace Apps.Jira.Dtos;

public class LabelsPaginationDto
{
[JsonProperty("maxResults")]
public int MaxResults { get; set; }

[JsonProperty("startAt")]
public int StartAt { get; set; }

[JsonProperty("total")]
public int Total { get; set; }

[JsonProperty("isLast")]
public bool IsLast { get; set; }

[JsonProperty("values")]
public List<string> Values { get; set; } = new();
}
11 changes: 11 additions & 0 deletions Apps.Jira/Models/Requests/LabelsOptionalInput.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using Apps.Jira.DataSourceHandlers;
using Blackbird.Applications.Sdk.Common;
using Blackbird.Applications.Sdk.Common.Dynamic;

namespace Apps.Jira.Models.Requests;

public class LabelsOptionalInput
{
[Display("Labels", Description = "Use this input if you want to filter results based on labels"), DataSource(typeof(LabelDataHandler))]
public IEnumerable<string>? Labels { get; set; }
}
72 changes: 49 additions & 23 deletions Apps.Jira/Webhooks/IssueWebhooks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,22 @@
using Blackbird.Applications.Sdk.Common.Invocation;
using RestSharp;
using Apps.Jira.Models.Identifiers;
using Apps.Jira.Models.Requests;

namespace Apps.Jira.Webhooks
{
[WebhookList]
public class IssueWebhooks : JiraInvocable
public class IssueWebhooks(InvocationContext invocationContext) : JiraInvocable(invocationContext)
{
private IEnumerable<AuthenticationCredentialsProvider> Creds =>
InvocationContext.AuthenticationCredentialsProviders;

public IssueWebhooks(InvocationContext invocationContext) : base(invocationContext)
{
}


[Webhook("On issue updated", typeof(IssueUpdatedHandler),
Description = "This webhook is triggered when an issue is updated.")]
public async Task<WebhookResponse<IssueResponse>> OnIssueUpdated(WebhookRequest request,
[WebhookParameter] IssueInput issue, [WebhookParameter] ProjectInput project)
[WebhookParameter] IssueInput issue,
[WebhookParameter] ProjectInput project,
[WebhookParameter] LabelsOptionalInput labels)
{
var payload = DeserializePayload(request);

Expand All @@ -38,14 +37,15 @@ public async Task<WebhookResponse<IssueResponse>> OnIssueUpdated(WebhookRequest
ReceivedWebhookRequestType = WebhookRequestType.Preflight
};

var issueResponse = CreateIssueResponse(payload);
var issueResponse = CreateIssueResponse(payload, labels);
return issueResponse;
}

[Webhook("On issue created", typeof(IssueCreatedHandler),
Description = "This webhook is triggered when an issue is created.")]
public async Task<WebhookResponse<IssueResponse>> OnIssueCreated(WebhookRequest request,
[WebhookParameter] ProjectInput project)
[WebhookParameter] ProjectInput project,
[WebhookParameter] LabelsOptionalInput labels)
{
var payload = DeserializePayload(request);

Expand All @@ -56,14 +56,16 @@ public async Task<WebhookResponse<IssueResponse>> OnIssueCreated(WebhookRequest
ReceivedWebhookRequestType = WebhookRequestType.Preflight
};

var issueResponse = CreateIssueResponse(payload);
var issueResponse = CreateIssueResponse(payload, labels);
return issueResponse;
}

[Webhook("On issue assigned", typeof(IssueCreatedOrUpdatedHandler),
Description = "This webhook is triggered when an issue is assigned to specific user.")]
public async Task<WebhookResponse<IssueResponse>> IssueAssigned(WebhookRequest request,
[WebhookParameter] AssigneeInput assignee, [WebhookParameter] ProjectInput project)
[WebhookParameter] AssigneeInput assignee,
[WebhookParameter] ProjectInput project,
[WebhookParameter] LabelsOptionalInput labels)
{
var payload = DeserializePayload(request);
var actualAssignee = payload.Changelog.Items.FirstOrDefault(item => item.FieldId == "assignee");
Expand Down Expand Up @@ -99,14 +101,16 @@ public async Task<WebhookResponse<IssueResponse>> IssueAssigned(WebhookRequest r
};
}

var issueResponse = CreateIssueResponse(payload);
var issueResponse = CreateIssueResponse(payload, labels);
return issueResponse;
}

[Webhook("On issue with specific type created", typeof(IssueCreatedOrUpdatedHandler),
Description = "This webhook is triggered when an issue created has specific type or issue was updated to have specific type.")]
public async Task<WebhookResponse<IssueResponse>> OnIssueWithSpecificTypeCreated(WebhookRequest request,
[WebhookParameter] IssueTypeInput issueType, [WebhookParameter] ProjectInput project)
[WebhookParameter] IssueTypeInput issueType,
[WebhookParameter] ProjectInput project,
[WebhookParameter] LabelsOptionalInput labels)
{
var payload = DeserializePayload(request);
var issueTypeItem = payload.Changelog.Items.FirstOrDefault(item => item.FieldId == "issuetype");
Expand All @@ -120,14 +124,16 @@ public async Task<WebhookResponse<IssueResponse>> OnIssueWithSpecificTypeCreated
ReceivedWebhookRequestType = WebhookRequestType.Preflight
};

var issueResponse = CreateIssueResponse(payload);
var issueResponse = CreateIssueResponse(payload, labels);
return issueResponse;
}

[Webhook("On issue with specific priority created", typeof(IssueCreatedOrUpdatedHandler),
Description = "This webhook is triggered when an issue created has specified priority or issue was updated to have specified priority.")]
public async Task<WebhookResponse<IssueResponse>> OnIssueWithSpecificPriorityCreated(WebhookRequest request,
[WebhookParameter] PriorityInput priority, [WebhookParameter] ProjectInput project)
[WebhookParameter] PriorityInput priority,
[WebhookParameter] ProjectInput project,
[WebhookParameter] LabelsOptionalInput labels)
{
var payload = DeserializePayload(request);
var priorityItem = payload.Changelog.Items.FirstOrDefault(item => item.FieldId == "priority");
Expand All @@ -141,14 +147,15 @@ public async Task<WebhookResponse<IssueResponse>> OnIssueWithSpecificPriorityCre
ReceivedWebhookRequestType = WebhookRequestType.Preflight
};

var issueResponse = CreateIssueResponse(payload);
var issueResponse = CreateIssueResponse(payload, labels);
return issueResponse;
}

[Webhook("On issue deleted", typeof(IssueDeletedHandler),
Description = "This webhook is triggered when an issue is deleted.")]
public async Task<WebhookResponse<IssueResponse>> OnIssueDeleted(WebhookRequest request,
[WebhookParameter] ProjectInput project)
[WebhookParameter] ProjectInput project,
[WebhookParameter] LabelsOptionalInput labels)
{
var payload = DeserializePayload(request);

Expand All @@ -159,14 +166,15 @@ public async Task<WebhookResponse<IssueResponse>> OnIssueDeleted(WebhookRequest
ReceivedWebhookRequestType = WebhookRequestType.Preflight
};

var issueResponse = CreateIssueResponse(payload);
var issueResponse = CreateIssueResponse(payload, labels);
return issueResponse;
}

[Webhook("On file attached to issue", typeof(IssueUpdatedHandler),
Description = "This webhook is triggered when a file is attached to an issue.")]
public async Task<WebhookResponse<IssueAttachmentResponse>> OnFileAttachedToIssue(WebhookRequest request,
[WebhookParameter] IssueInput issue, [WebhookParameter] ProjectInput project)
[WebhookParameter] IssueInput issue,
[WebhookParameter] ProjectInput project)
{
var payload = DeserializePayload(request);
var attachmentItem = payload.Changelog.Items.FirstOrDefault(item => item.FieldId == "attachment");
Expand Down Expand Up @@ -196,7 +204,10 @@ public async Task<WebhookResponse<IssueAttachmentResponse>> OnFileAttachedToIssu
[Webhook("On issue status changed", typeof(IssueUpdatedHandler),
Description = "This webhook is triggered when issue status is changed.")]
public async Task<WebhookResponse<IssueResponse>> OnIssueStatusChanged(WebhookRequest request,
[WebhookParameter] ProjectIdentifier project, [WebhookParameter] OptionalStatusInput status, [WebhookParameter] IssueInput issue)
[WebhookParameter] ProjectIdentifier project,
[WebhookParameter] OptionalStatusInput status,
[WebhookParameter] IssueInput issue,
[WebhookParameter] LabelsOptionalInput labels)
{
var payload = DeserializePayload(request);
var statusItem = payload.Changelog.Items.FirstOrDefault(item => item.FieldId == "status");
Expand All @@ -211,7 +222,7 @@ public async Task<WebhookResponse<IssueResponse>> OnIssueStatusChanged(WebhookRe
ReceivedWebhookRequestType = WebhookRequestType.Preflight
};

var issueResponse = CreateIssueResponse(payload);
var issueResponse = CreateIssueResponse(payload, labels);
return issueResponse;
}

Expand All @@ -225,13 +236,27 @@ private WebhookPayload DeserializePayload(WebhookRequest request)
return payload;
}

private WebhookResponse<IssueResponse> CreateIssueResponse(WebhookPayload payload)
private WebhookResponse<IssueResponse> CreateIssueResponse(WebhookPayload payload, LabelsOptionalInput labelsInput)
{
var issue = payload.Issue;

if (labelsInput.Labels is not null && labelsInput.Labels.Any())
{
var labelsMatch = labelsInput.Labels.All(label => issue.Fields.Labels.Contains(label));
if (!labelsMatch)
{
return new WebhookResponse<IssueResponse>
{
HttpResponseMessage = new HttpResponseMessage(HttpStatusCode.OK),
ReceivedWebhookRequestType = WebhookRequestType.Preflight
};
}
}

return new WebhookResponse<IssueResponse>
{
HttpResponseMessage = new HttpResponseMessage(statusCode: HttpStatusCode.OK),
ReceivedWebhookRequestType = WebhookRequestType.Default,
Result = new IssueResponse
{
IssueKey = issue.Key,
Expand All @@ -243,7 +268,8 @@ private WebhookResponse<IssueResponse> CreateIssueResponse(WebhookPayload payloa
AssigneeName = issue.Fields.Assignee?.DisplayName,
AssigneeAccountId = issue.Fields.Assignee?.AccountId,
Status = issue.Fields.Status.Name,
Attachments = issue.Fields.Attachment
Attachments = issue.Fields.Attachment,
Labels = issue.Fields.Labels
}
};
}
Expand Down
10 changes: 10 additions & 0 deletions Apps.Jira/Webhooks/Payload/Issue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,23 @@ public class Fields
{
[JsonPropertyName("issuetype")]
public IssueType IssueType { get; set; }

public Project Project { get; set; }

public Priority? Priority { get; set; }

public Assignee? Assignee { get; set; }

public Status Status { get; set; }

public string Summary { get; set; }

public IEnumerable<AttachmentDto> Attachment { get; set; }

public string? Description { get; set; }

[JsonPropertyName("labels")]
public List<string> Labels { get; set; } = new();
}

public class IssueType
Expand Down
3 changes: 3 additions & 0 deletions Apps.Jira/Webhooks/Responses/IssueResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,8 @@ public class IssueResponse
public string Status { get; set; }

public IEnumerable<AttachmentDto> Attachments { get; set; }

[Display("Labels")]
public List<string> Labels { get; set; } = new();
}
}
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ Note: this app currently supports only short text (plain text only) custom field
- **Add attachment** adds attachment to an issue.
- **Update issue**. Specify only the fields that require updating.
- **Delete issue**.
- **Add labels to issue** adds labels to an issue. Returns the updated issue.

### Issue custom fields

Expand Down

0 comments on commit 053f8d1

Please sign in to comment.