Skip to content
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
5 changes: 5 additions & 0 deletions pkg/jira/fake.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ type Fake struct {

// FileIssue files the issue using injected behavior
func (f *Fake) FileIssue(issueType, title, description, reporter string, logger *logrus.Entry) (*jira.Issue, error) {
return f.FileIssueWithFields(issueType, title, description, reporter, nil, logger)
}

// FileIssueWithFields files the issue with custom fields using injected behavior
func (f *Fake) FileIssueWithFields(issueType, title, description, reporter string, customFields map[string]interface{}, logger *logrus.Entry) (*jira.Issue, error) {
request := IssueRequest{
IssueType: issueType,
Title: title,
Expand Down
38 changes: 33 additions & 5 deletions pkg/jira/issues.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,20 @@ const (
IssueTypeBug = "Bug"
IssueTypeStory = "Story"
IssueTypeTask = "Task"

// Activity Type field ID in Jira - this should match Jira instance's custom field ID
// goto https://issues.redhat.com/rest/api/2/field/ to get the field ID
ActivityTypeFieldID = "customfield_12320040"

// Activity Type values
ActivityTypeBugFix = "Bug Fix"
ActivityTypeEnhancement = "Enhancement"
)

// IssueFiler knows how to file an issue in Jira
type IssueFiler interface {
FileIssue(issueType, title, description, reporter string, logger *logrus.Entry) (*jira.Issue, error)
FileIssueWithFields(issueType, title, description, reporter string, customFields map[string]interface{}, logger *logrus.Entry) (*jira.Issue, error)
}

type slackClient interface {
Expand Down Expand Up @@ -78,20 +87,39 @@ type filer struct {
// quirks like how issue types and projects are provided, as well as
// transforming the Slack reporter ID to a Jira user, when possible.
func (f *filer) FileIssue(issueType, title, description, reporter string, logger *logrus.Entry) (*jira.Issue, error) {
return f.FileIssueWithFields(issueType, title, description, reporter, nil, logger)
}

// FileIssueWithFields files an issue with custom fields
func (f *filer) FileIssueWithFields(issueType, title, description, reporter string, customFields map[string]interface{}, logger *logrus.Entry) (*jira.Issue, error) {
suffix, requester := f.resolveRequester(reporter, logger)
description = fmt.Sprintf("%s\n\nThis issue was filed by %s", description, suffix)
logger.WithFields(logrus.Fields{
"title": title,
"reporter": requester.Name,
"type": issueType,
"title": title,
"reporter": requester.Name,
"type": issueType,
"customFields": len(customFields),
}).Debug("Filing Jira issue.")
toCreate := &jira.Issue{Fields: &jira.IssueFields{

issueFields := &jira.IssueFields{
Project: f.project,
Reporter: requester,
Type: f.issueTypesByName[issueType],
Summary: title,
Description: description,
}}
}

// Add custom fields if provided
if len(customFields) > 0 {
if issueFields.Unknowns == nil {
issueFields.Unknowns = make(map[string]interface{})
}
for key, value := range customFields {
issueFields.Unknowns[key] = value
}
}

toCreate := &jira.Issue{Fields: issueFields}
issue, response, err := f.jiraClient.CreateIssue(toCreate)
return issue, jirautil.HandleJiraError(response, err)
}
Expand Down
11 changes: 7 additions & 4 deletions pkg/slack/modals/bug/view.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"github.com/sirupsen/logrus"
"github.com/slack-go/slack"

"github.com/openshift/ci-tools/pkg/jira"
localjira "github.com/openshift/ci-tools/pkg/jira"
"github.com/openshift/ci-tools/pkg/slack/interactions"
"github.com/openshift/ci-tools/pkg/slack/modals"
"github.com/openshift/ci-tools/pkg/slack/modals/helpdesk"
Expand Down Expand Up @@ -164,7 +164,7 @@ func validateSubmissionHandler() interactions.PartialHandler {
func issueParameters() modals.JiraIssueParameters {
return modals.JiraIssueParameters{
Id: Identifier,
IssueType: jira.IssueTypeBug,
IssueType: localjira.IssueTypeBug,
Template: template.Must(template.New(string(Identifier)).Parse(`h3. Symptomatic Behavior
{{ .` + blockIdSymptom + ` }}

Expand All @@ -180,16 +180,19 @@ h3. Category
h3. How to Reproduce
{{ .` + blockIdReproduction + ` }}`)),
Fields: []string{modals.BlockIdTitle, blockIdCategory, blockIdOptional, blockIdSymptom, blockIdExpected, blockIdImpact, blockIdReproduction},
CustomFields: map[string]interface{}{
localjira.ActivityTypeFieldID: localjira.ActivityTypeBugFix,
},
}
}

// processSubmissionHandler files a Jira issue for this form
func processSubmissionHandler(filer jira.IssueFiler, updater modals.ViewUpdater) interactions.PartialHandler {
func processSubmissionHandler(filer localjira.IssueFiler, updater modals.ViewUpdater) interactions.PartialHandler {
return interactions.PartialFromHandler(modals.ToJiraIssue(issueParameters(), filer, updater))
}

// Register creates a registration entry for the bug form
func Register(filer jira.IssueFiler, client *slack.Client) *modals.FlowWithViewAndFollowUps {
func Register(filer localjira.IssueFiler, client *slack.Client) *modals.FlowWithViewAndFollowUps {
return modals.ForView(Identifier, View()).WithFollowUps(map[slack.InteractionType]interactions.Handler{
slack.InteractionTypeBlockActions: helpdeskButtonHandler(client),
slack.InteractionTypeViewSubmission: interactions.MultiHandler(
Expand Down
11 changes: 7 additions & 4 deletions pkg/slack/modals/enhancement/view.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (

"github.com/slack-go/slack"

"github.com/openshift/ci-tools/pkg/jira"
localjira "github.com/openshift/ci-tools/pkg/jira"
"github.com/openshift/ci-tools/pkg/slack/interactions"
"github.com/openshift/ci-tools/pkg/slack/modals"
)
Expand Down Expand Up @@ -105,7 +105,7 @@ func View() slack.ModalViewRequest {
func issueParameters() modals.JiraIssueParameters {
return modals.JiraIssueParameters{
Id: Identifier,
IssueType: jira.IssueTypeStory,
IssueType: localjira.IssueTypeStory,
Template: template.Must(template.New(string(Identifier)).Funcs(modals.BulletListFunc()).Parse(`h3. Overview
As a {{ .` + blockIdAsA + ` }}
I want {{ .` + blockIdIWant + ` }}
Expand All @@ -126,16 +126,19 @@ h3. Implementation Details
{{ .` + blockIdImplementation + ` }}
{{- end }}`)),
Fields: []string{modals.BlockIdTitle, blockIdAsA, blockIdIWant, blockIdSoThat, blockIdSummary, blockIdImpact, blockIdAcceptanceCriteria, blockIdImplementation},
CustomFields: map[string]interface{}{
localjira.ActivityTypeFieldID: localjira.ActivityTypeEnhancement,
},
}
}

// processSubmissionHandler files a Jira issue for this form
func processSubmissionHandler(filer jira.IssueFiler, updater modals.ViewUpdater) interactions.Handler {
func processSubmissionHandler(filer localjira.IssueFiler, updater modals.ViewUpdater) interactions.Handler {
return modals.ToJiraIssue(issueParameters(), filer, updater)
}

// Register creates a registration entry for the enhancment request form
func Register(filer jira.IssueFiler, client *slack.Client) *modals.FlowWithViewAndFollowUps {
func Register(filer localjira.IssueFiler, client *slack.Client) *modals.FlowWithViewAndFollowUps {
return modals.ForView(Identifier, View()).WithFollowUps(map[slack.InteractionType]interactions.Handler{
slack.InteractionTypeViewSubmission: processSubmissionHandler(filer, client),
})
Expand Down
21 changes: 14 additions & 7 deletions pkg/slack/modals/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ import (
"strings"
"text/template"

"github.com/andygrunwald/go-jira"
"github.com/sirupsen/logrus"
"github.com/slack-go/slack"

"github.com/openshift/ci-tools/pkg/jira"
localjira "github.com/openshift/ci-tools/pkg/jira"
"github.com/openshift/ci-tools/pkg/slack/interactions"
)

Expand Down Expand Up @@ -48,10 +49,11 @@ func UpdateViewForButtonPress(identifier, buttonId string, updater ViewUpdater,

// JiraIssueParameters holds the metadata used to create a Jira issue
type JiraIssueParameters struct {
Id Identifier
IssueType string
Template *template.Template
Fields []string
Id Identifier
IssueType string
Template *template.Template
Fields []string
CustomFields map[string]interface{} // Custom fields to set on the Jira issue
}

// Process processes the interaction callback data to render the Jira issue title and body
Expand All @@ -69,7 +71,7 @@ func (p *JiraIssueParameters) Process(callback *slack.InteractionCallback) (stri
// has finished. We need this asynchronous response mechanism as the API
// calls needed to file the issue often take longer than the 3sec TTL on
// responding to the interaction payload we have.
func ToJiraIssue(parameters JiraIssueParameters, filer jira.IssueFiler, updater ViewUpdater) interactions.Handler {
func ToJiraIssue(parameters JiraIssueParameters, filer localjira.IssueFiler, updater ViewUpdater) interactions.Handler {
return interactions.HandlerFunc(string(parameters.Id)+".jira", func(callback *slack.InteractionCallback, logger *logrus.Entry) (output []byte, err error) {
logger.Infof("Submitting new %s to Jira.", parameters.Id)

Expand All @@ -89,7 +91,12 @@ func ToJiraIssue(parameters JiraIssueParameters, filer jira.IssueFiler, updater
return
}

issue, err := filer.FileIssue(parameters.IssueType, title, body, callback.User.ID, logger)
var issue *jira.Issue
if len(parameters.CustomFields) > 0 {
issue, err = filer.FileIssueWithFields(parameters.IssueType, title, body, callback.User.ID, parameters.CustomFields, logger)
} else {
issue, err = filer.FileIssue(parameters.IssueType, title, body, callback.User.ID, logger)
}
if err != nil {
logger.WithError(err).Errorf("Failed to create %s Jira.", parameters.Id)
overwriteView(ErrorView(fmt.Sprintf("create %s Jira issue", parameters.Id), err))
Expand Down