diff --git a/go.mod b/go.mod index d66e09214..1834305ef 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/dghubble/oauth1 v0.5.0 github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/gorilla/mux v1.8.0 + github.com/hashicorp/go-version v1.2.0 github.com/jarcoal/httpmock v1.0.8 github.com/mattermost/mattermost-plugin-api v0.0.26-0.20220223141232-cb8b1984774a github.com/mattermost/mattermost-plugin-autolink v1.2.2-0.20210709183311-c8fa30db649f diff --git a/go.sum b/go.sum index 64fa7ea74..dc2f421df 100644 --- a/go.sum +++ b/go.sum @@ -838,6 +838,7 @@ github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjG github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= diff --git a/server/client_server.go b/server/client_server.go index 8b19bf3f4..e81d5ecb6 100644 --- a/server/client_server.go +++ b/server/client_server.go @@ -4,12 +4,20 @@ package main import ( + "fmt" "net/http" jira "github.com/andygrunwald/go-jira" + "github.com/hashicorp/go-version" "github.com/pkg/errors" ) +const ( + ServerInfoApiEndpoint = "rest/api/2/serverInfo" + CreateMetaAPIEndpoint = "rest/api/2/issue/createmeta/" + PivotVersion = "8.4.0" +) + type jiraServerClient struct { JiraClient } @@ -22,10 +30,120 @@ func newServerClient(jiraClient *jira.Client) Client { } } +type IssueInfo struct { + Values []*jira.MetaIssueType `json:"values,omitempty"` +} + +type FieldInfo struct { + Values []interface{} `json:"values,omitempty"` +} + +type FieldValues struct { + FieldID string `json:"fieldId,omitempty"` +} + +type FieldID struct { + Values []FieldValues `json:"values,omitempty"` +} + +type ServerVersion struct { + VersionInfo string `json:"version,omitempty"` +} + +// GetIssueInfo returns the issues information based on project id. +func (client jiraServerClient) GetIssueInfo(projectID string) (*IssueInfo, *jira.Response, error) { + apiEndpoint := fmt.Sprintf("%s%s/issuetypes", CreateMetaAPIEndpoint, projectID) + req, err := client.Jira.NewRequest(http.MethodGet, apiEndpoint, nil) + if err != nil { + return nil, nil, err + } + + issues := new(IssueInfo) + response, err := client.Jira.Do(req, issues) + return issues, response, err +} + // GetCreateMeta returns the metadata needed to implement the UI and validation of // creating new Jira issues. func (client jiraServerClient) GetCreateMeta(options *jira.GetQueryOptions) (*jira.CreateMetaInfo, error) { - cimd, resp, err := client.Jira.Issue.GetCreateMetaWithOptions(options) + v := new(ServerVersion) + req, err := client.Jira.NewRequest(http.MethodGet, ServerInfoApiEndpoint, nil) + if err != nil { + return nil, err + } + + if _, err = client.Jira.Do(req, v); err != nil { + return nil, err + } + + currentVersion, err := version.NewVersion(v.VersionInfo) + if err != nil { + return nil, err + } + + pivotVersion, err := version.NewVersion(PivotVersion) + if err != nil { + return nil, err + } + + var info *jira.CreateMetaInfo + var resp *jira.Response + var issues *IssueInfo + var projectList *jira.ProjectList + if currentVersion.LessThan(pivotVersion) { + info, resp, err = client.Jira.Issue.GetCreateMetaWithOptions(options) + } else { + projectList, resp, err = client.Jira.Project.ListWithOptions(options) + meta := new(jira.CreateMetaInfo) + + if err == nil { + for _, proj := range *projectList { + meta.Expand = proj.Expand + issues, resp, err = client.GetIssueInfo(proj.ID) + if err != nil { + break + } + + project := &jira.MetaProject{ + Expand: proj.Expand, + Self: proj.Self, + Id: proj.ID, + Key: proj.Key, + Name: proj.Name, + IssueTypes: issues.Values, + } + + for _, issue := range project.IssueTypes { + apiEndpoint := fmt.Sprintf("%s%s/issuetypes/%s", CreateMetaAPIEndpoint, proj.ID, issue.Id) + req, err = client.Jira.NewRequest(http.MethodGet, apiEndpoint, nil) + if err != nil { + break + } + + field := new(FieldInfo) + resp, err = client.Jira.Do(req, field) + if err != nil { + break + } + + fieldID := new(FieldID) + resp, err = client.Jira.Do(req, fieldID) + if err != nil { + break + } + + fieldMap := make(map[string]interface{}) + for index, fieldValue := range field.Values { + fieldMap[fieldID.Values[index].FieldID] = fieldValue + } + issue.Fields = fieldMap + } + meta.Projects = append(meta.Projects, project) + } + } + info = meta + } + if err != nil { if resp == nil { return nil, err @@ -36,7 +154,7 @@ func (client jiraServerClient) GetCreateMeta(options *jira.GetQueryOptions) (*ji } return nil, RESTError{err, resp.StatusCode} } - return cimd, nil + return info, nil } // SearchUsersAssignableToIssue finds all users that can be assigned to an issue.