From 3aa0f7f13339fbe7e690e79fbaa007bba6ae5840 Mon Sep 17 00:00:00 2001 From: Kalle Fagerberg Date: Tue, 4 Oct 2022 13:06:45 +0200 Subject: [PATCH] [OP-1397] Allow use of custom field for project name (#6) * Added ProjectNameCustomField to creation & search * Added docs on new field * Updated Earthfile to also run tests --- Earthfile | 5 +++-- README.md | 1 + main.go | 42 ++++++++++++++++++++++++++++++++---------- main_test.go | 37 +++++++++++++++++++++++++++++++++++++ 4 files changed, 73 insertions(+), 12 deletions(-) create mode 100644 main_test.go diff --git a/Earthfile b/Earthfile index 425ad20..17b19f2 100644 --- a/Earthfile +++ b/Earthfile @@ -10,8 +10,9 @@ deps: build: FROM +deps - COPY main.go . - RUN go build -o build/jelease main.go + COPY *.go . + RUN go test -v ./... \ + && go build -o build/jelease main.go SAVE ARTIFACT build/jelease /jelease AS LOCAL build/jelease docker: diff --git a/README.md b/README.md index 6bb4977..a468f04 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ The application requires the following environment variables to be set: - `JELEASE_ISSUE_DESCRIPTION`: The description for created issues - `JELEASE_ISSUE_TYPE`: The issue type for created issues. E.g `Task`, `Story` (default), or `Bug` - `JELEASE_PROJECT`: Jira Project key the tickets will be created in + - `JELEASE_PROJECT_NAME_CUSTOM_FIELD`: Custom field ID (uint) to store project ID in. If left at 0 (default) then Jelease will use labels instead. - `JELEASE_LOG_FORMAT`: Logging format. One of: `pretty` (default), `json` - `JELEASE_LOG_LEVEL`: Logging minimum level/severity. One of: `trace`, `debug` (default), `info`, `warn`, `error`, `fatal`, `panic` diff --git a/main.go b/main.go index 6dcfc4d..84c6d58 100644 --- a/main.go +++ b/main.go @@ -16,6 +16,7 @@ import ( "github.com/kelseyhightower/envconfig" "github.com/rs/zerolog" "github.com/rs/zerolog/log" + "github.com/trivago/tgo/tcontainer" ) var ( @@ -35,12 +36,13 @@ type Config struct { SkipCertVerify bool `envconfig:"JELEASE_INSECURE_SKIP_CERT_VERIFY" default:"false"` // ticket creation - AddLabels []string `envconfig:"JELEASE_ADD_LABELS"` - DefaultStatus string `envconfig:"JELEASE_DEFAULT_STATUS" required:"true"` - DryRun bool `envconfig:"JELEASE_DRY_RUN" default:"false"` - IssueDescription string `envconfig:"JELEASE_ISSUE_DESCRIPTION" default:"Update issue generated by https://github.2rioffice.com/platform/jelease using newreleases.io"` - IssueType string `envconfig:"JELEASE_ISSUE_TYPE" default:"Story"` - Project string `envconfig:"JELEASE_PROJECT" required:"true"` + AddLabels []string `envconfig:"JELEASE_ADD_LABELS"` + DefaultStatus string `envconfig:"JELEASE_DEFAULT_STATUS" required:"true"` + DryRun bool `envconfig:"JELEASE_DRY_RUN" default:"false"` + IssueDescription string `envconfig:"JELEASE_ISSUE_DESCRIPTION" default:"Update issue generated by https://github.2rioffice.com/platform/jelease using newreleases.io"` + IssueType string `envconfig:"JELEASE_ISSUE_TYPE" default:"Story"` + Project string `envconfig:"JELEASE_PROJECT" required:"true"` + ProjectNameCustomField uint `envconfig:"JELEASE_PROJECT_NAME_CUSTOM_FIELD"` // logging LogFormat string `envconfig:"JELEASE_LOG_FORMAT" default:"pretty"` @@ -61,7 +63,17 @@ func (r Release) IssueSummary() string { } func (r Release) JiraIssue() jira.Issue { - labels := append(config.AddLabels, r.Project) + labels := config.AddLabels + var extraFields tcontainer.MarshalMap + + if config.ProjectNameCustomField == 0 { + labels = append(labels, r.Project) + } else { + customFieldName := fmt.Sprintf("customfield_%d", config.ProjectNameCustomField) + extraFields = tcontainer.MarshalMap{ + customFieldName: r.Project, + } + } return jira.Issue{ Fields: &jira.IssueFields{ Description: config.IssueDescription, @@ -71,8 +83,9 @@ func (r Release) JiraIssue() jira.Issue { Type: jira.IssueType{ Name: config.IssueType, }, - Labels: labels, - Summary: r.IssueSummary(), + Labels: labels, + Summary: r.IssueSummary(), + Unknowns: extraFields, }, } } @@ -118,7 +131,7 @@ func handlePostWebhook(w http.ResponseWriter, r *http.Request) { } // look for existing update tickets - existingIssuesQuery := fmt.Sprintf("status = %q and labels = %q", config.DefaultStatus, release.Project) + existingIssuesQuery := newJiraIssueSearchQuery(config.DefaultStatus, release.Project, config.ProjectNameCustomField) existingIssues, resp, err := jiraClient.Issue.Search(existingIssuesQuery, &jira.SearchOptions{}) if err != nil { err := fmt.Errorf("searching Jira for previous issues: %w", err) @@ -378,6 +391,15 @@ func loggerSetup() error { return nil } +func newJiraIssueSearchQuery(statusName, projectName string, customFieldID uint) string { + if customFieldID == 0 { + return fmt.Sprintf("status = %q and labels = %q", statusName, projectName) + } + // Checking label as well for backward compatibility + return fmt.Sprintf("status = %q and (labels = %q or cf[%d] ~ %[2]q)", + statusName, projectName, customFieldID) +} + func logJiraErrResponse(resp *jira.Response, err error) { if resp != nil { body, readErr := io.ReadAll(resp.Body) diff --git a/main_test.go b/main_test.go new file mode 100644 index 0000000..3e5665e --- /dev/null +++ b/main_test.go @@ -0,0 +1,37 @@ +package main + +import "testing" + +func TestNewJiraIssueSearchQuery(t *testing.T) { + tests := []struct { + name string + status string + project string + customField uint + want string + }{ + { + name: "no custom field", + status: "Grooming", + project: "platform/jelease", + customField: 0, + want: `status = "Grooming" and labels = "platform/jelease"`, + }, + { + name: "with custom field", + status: "Grooming", + project: "platform/jelease", + customField: 12500, + want: `status = "Grooming" and (labels = "platform/jelease" or cf[12500] ~ "platform/jelease")`, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + got := newJiraIssueSearchQuery(tc.status, tc.project, tc.customField) + if tc.want != got { + t.Errorf("Wrong query.\nwant: `%s`\ngot: `%s`", tc.want, got) + } + }) + } +}