Skip to content

Commit

Permalink
fix createmeta issue on jira 9.x
Browse files Browse the repository at this point in the history
 * Jira changed the format of the call to createmeta

 * config.yml should default to old way
 * add a jira server version variable to config.yml
   to tell cli which method call to make
  • Loading branch information
prpht9 committed Dec 17, 2022
1 parent 4263bd2 commit 3c01795
Show file tree
Hide file tree
Showing 8 changed files with 463 additions and 45 deletions.
13 changes: 13 additions & 0 deletions TODO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# TODO

* https://confluence.atlassian.com/jiracore/createmeta-rest-endpoint-to-be-removed-975040986.html
* Jira 9.0 moved the parameters on api/2/issue/createmeta into path values
Need to allow both versions to function
Need a configuration item to indicate which url/method to call
Need to write the new method to conform to the new api method format
Need new jiradata type because the api endpoint changed the output json schema
* slipstream to create jiradata types
slipscheme -stdout schema/IssueTypes.json > jiradata/IssueTypes.go

* Old API Doc: https://docs.atlassian.com/software/jira/docs/api/REST/7.2.7/#api/2/issue-getCreateIssueMeta
* New API Doc: https://docs.atlassian.com/software/jira/docs/api/REST/9.0.0/#issue-getCreateIssueMetaProjectIssueTypes
34 changes: 12 additions & 22 deletions issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,43 +247,38 @@ func CreateIssue(ua HttpClient, endpoint string, iup IssueUpdateProvider) (*jira
}

// https://docs.atlassian.com/jira/REST/cloud/#api/2/issue-getCreateIssueMeta
func (j *Jira) GetIssueCreateMetaProject(projectKey string) (*jiradata.CreateMetaProject, error) {
func (j *Jira) GetIssueCreateMetaProject(projectKey string) (jiradata.Values, error) {
return GetIssueCreateMetaProject(j.UA, j.Endpoint, projectKey)
}

func GetIssueCreateMetaProject(ua HttpClient, endpoint string, projectKey string) (*jiradata.CreateMetaProject, error) {
func GetIssueCreateMetaProject(ua HttpClient, endpoint string, projectKey string) (jiradata.Values, error) {
uri := URLJoin(endpoint, "rest/api/2/issue/createmeta")
uri += fmt.Sprintf("?projectKeys=%s&expand=projects.issuetypes.fields", projectKey)
uri += fmt.Sprintf("/%s/issuetypes", projectKey)
resp, err := ua.GetJSON(uri)
if err != nil {
return nil, err
}
defer resp.Body.Close()

if resp.StatusCode == 200 {
results := &jiradata.CreateMeta{}
results := &jiradata.PageOfCreateMetaIssueType{}
err = json.NewDecoder(resp.Body).Decode(results)
if err != nil {
return nil, err
}
for _, project := range results.Projects {
if project.Key == projectKey {
return project, nil
}
}
return nil, fmt.Errorf("project %s not found", projectKey)
return results.Values, nil
}
return nil, responseError(resp)
}

// https://docs.atlassian.com/jira/REST/cloud/#api/2/issue-getCreateIssueMeta
func (j *Jira) GetIssueCreateMetaIssueType(projectKey, issueTypeName string) (*jiradata.IssueType, error) {
func (j *Jira) GetIssueCreateMetaIssueType(projectKey, issueTypeName string) (*jiradata.CreateMetaIssueType, error) {
return GetIssueCreateMetaIssueType(j.UA, j.Endpoint, projectKey, issueTypeName)
}

func GetIssueCreateMetaIssueType(ua HttpClient, endpoint string, projectKey, issueTypeName string) (*jiradata.IssueType, error) {
func GetIssueCreateMetaIssueType(ua HttpClient, endpoint string, projectKey, issueTypeName string) (*jiradata.CreateMetaIssueType, error) {
uri := URLJoin(endpoint, "rest/api/2/issue/createmeta")
uri += fmt.Sprintf("?projectKeys=%s&issuetypeNames=%s&expand=projects.issuetypes.fields", projectKey, url.QueryEscape(issueTypeName))
uri += fmt.Sprintf("/%s/issuetypes", projectKey)
resp, err := ua.GetJSON(uri)
if err != nil {
return nil, err
Expand All @@ -293,18 +288,13 @@ func GetIssueCreateMetaIssueType(ua HttpClient, endpoint string, projectKey, iss
if resp.StatusCode != 200 {
return nil, responseError(resp)
}
results := &jiradata.CreateMeta{}
results := &jiradata.PageOfCreateMetaIssueType{}
if err := json.NewDecoder(resp.Body).Decode(results); err != nil {
return nil, err
}
for _, project := range results.Projects {
if project.Key != projectKey {
continue
}
for _, issueType := range project.IssueTypes {
if issueType.Name == issueTypeName {
return issueType, nil
}
for _, issueType := range results.Values {
if issueType.Name == issueTypeName {
return issueType, nil
}
}
return nil, fmt.Errorf("project %s and IssueType %s not found", projectKey, issueTypeName)
Expand Down
29 changes: 14 additions & 15 deletions jiracmd/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@ func CmdCreate(o *oreo.Client, globals *jiracli.GlobalOptions, opts *CreateOptio
}

type templateInput struct {
Meta *jiradata.IssueType `yaml:"meta" json:"meta"`
Overrides map[string]string `yaml:"overrides" json:"overrides"`
Meta *jiradata.CreateMetaIssueType `yaml:"meta" json:"meta"`
Overrides map[string]string `yaml:"overrides" json:"overrides"`
}

if err := defaultIssueType(o, globals.Endpoint.Value, &opts.Project, &opts.IssueType); err != nil {
Expand Down Expand Up @@ -157,26 +157,25 @@ func defaultIssueType(o *oreo.Client, endpoint string, project, issuetype *strin
if issuetype != nil && *issuetype != "" {
return nil
}
projectMeta, err := jira.GetIssueCreateMetaProject(o, endpoint, *project)
issueTypes, err := jira.GetIssueCreateMetaProject(o, endpoint, *project)
if err != nil {
return err
}

issueTypes := map[string]bool{}

for _, issuetype := range projectMeta.IssueTypes {
issueTypes[issuetype.Name] = true
}

// prefer "Bug" type
if _, ok := issueTypes["Bug"]; ok {
*issuetype = "Bug"
return nil
for _, issueType := range issueTypes {
if issueType.Name == "Bug" {
*issuetype = "Bug"
return nil
}
}

// next best default it "Task"
if _, ok := issueTypes["Task"]; ok {
*issuetype = "Task"
return nil
for _, issueType := range issueTypes {
if issueType.Name == "Task" {
*issuetype = "Task"
return nil
}
}

return fmt.Errorf("Unable to find default issueType of Bug or Task, please set --issuetype argument or set the `issuetype` config property")
Expand Down
6 changes: 3 additions & 3 deletions jiracmd/subtask.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,9 @@ func CmdSubtask(o *oreo.Client, globals *jiracli.GlobalOptions, opts *SubtaskOpt
}

type templateInput struct {
Meta *jiradata.IssueType `yaml:"meta" json:"meta"`
Overrides map[string]string `yaml:"overrides" json:"overrides"`
Parent *jiradata.Issue `yaml:"parent" json:"parent"`
Meta *jiradata.CreateMetaIssueType `yaml:"meta" json:"meta"`
Overrides map[string]string `yaml:"overrides" json:"overrides"`
Parent *jiradata.Issue `yaml:"parent" json:"parent"`
}

parent, err := jira.GetIssue(o, globals.Endpoint.Value, opts.Issue, nil)
Expand Down
11 changes: 6 additions & 5 deletions jiradata/AllowedValues.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ package jiradata
/////////////////////////////////////////////////////////////////////////

// AllowedValues defined from schema:
// {
// "title": "allowedValues",
// "type": "array",
// "items": {}
// }
//
// {
// "title": "allowedValues",
// "type": "array",
// "items": {}
// }
type AllowedValues []interface{}
159 changes: 159 additions & 0 deletions jiradata/PageOfCreateMetaIssueType.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
package jiradata

/////////////////////////////////////////////////////////////////////////
// This Code is Generated by SlipScheme Project:
// https://github.com/coryb/slipscheme
//
// Generated with command:
// slipscheme -dir jiradata schema/IssueTypes.json
/////////////////////////////////////////////////////////////////////////
// DO NOT EDIT //
/////////////////////////////////////////////////////////////////////////

// PageOfCreateMetaIssueType defined from schema:
//
// {
// "title": "Page of Create Meta Issue Type",
// "id": "https://docs.atlassian.com/jira/REST/schema/page-of-create-meta-issue-type#",
// "type": "object",
// "properties": {
// "last": {
// "title": "last",
// "type": "boolean"
// },
// "size": {
// "title": "size",
// "type": "integer"
// },
// "start": {
// "title": "start",
// "type": "integer"
// },
// "total": {
// "title": "total",
// "type": "integer"
// },
// "values": {
// "title": "values",
// "type": "array",
// "items": {
// "title": "Create Meta Issue Type",
// "type": "object",
// "properties": {
// "avatarId": {
// "title": "avatarId",
// "type": "integer"
// },
// "description": {
// "title": "description",
// "type": "string"
// },
// "expand": {
// "title": "expand",
// "type": "string"
// },
// "fields": {
// "title": "fields",
// "type": "object",
// "patternProperties": {
// ".+": {
// "title": "Field Meta",
// "type": "object",
// "properties": {
// "allowedValues": {
// "title": "allowedValues",
// "type": "array",
// "items": {}
// },
// "autoCompleteUrl": {
// "title": "autoCompleteUrl",
// "type": "string"
// },
// "defaultValue": {
// "title": "defaultValue"
// },
// "fieldId": {
// "title": "fieldId",
// "type": "string"
// },
// "hasDefaultValue": {
// "title": "hasDefaultValue",
// "type": "boolean"
// },
// "name": {
// "title": "name",
// "type": "string"
// },
// "operations": {
// "title": "operations",
// "type": "array",
// "items": {
// "type": "string"
// }
// },
// "required": {
// "title": "required",
// "type": "boolean"
// },
// "schema": {
// "title": "Json Type",
// "type": "object",
// "properties": {
// "custom": {
// "title": "custom",
// "type": "string"
// },
// "customId": {
// "title": "customId",
// "type": "integer"
// },
// "items": {
// "title": "items",
// "type": "string"
// },
// "system": {
// "title": "system",
// "type": "string"
// },
// "type": {
// "title": "type",
// "type": "string"
// }
// }
// }
// }
// }
// }
// },
// "iconUrl": {
// "title": "iconUrl",
// "type": "string"
// },
// "id": {
// "title": "id",
// "type": "string"
// },
// "name": {
// "title": "name",
// "type": "string"
// },
// "self": {
// "title": "self",
// "type": "string"
// },
// "subtask": {
// "title": "subtask",
// "type": "boolean"
// }
// }
// }
// }
// }
// }
type PageOfCreateMetaIssueType struct {
Last bool `json:"last,omitempty" yaml:"last,omitempty"`
Size int `json:"size,omitempty" yaml:"size,omitempty"`
Start int `json:"start,omitempty" yaml:"start,omitempty"`
Total int `json:"total,omitempty" yaml:"total,omitempty"`
Values Values `json:"values,omitempty" yaml:"values,omitempty"`
}
Loading

0 comments on commit 3c01795

Please sign in to comment.