diff --git a/TODO.md b/TODO.md new file mode 100644 index 00000000..b6d57504 --- /dev/null +++ b/TODO.md @@ -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 diff --git a/issue.go b/issue.go index d36ad27a..086e3b84 100644 --- a/issue.go +++ b/issue.go @@ -247,13 +247,13 @@ 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 @@ -261,29 +261,24 @@ func GetIssueCreateMetaProject(ua HttpClient, endpoint string, projectKey string 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 @@ -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) diff --git a/jiracmd/create.go b/jiracmd/create.go index 3888b47b..0ead59d2 100644 --- a/jiracmd/create.go +++ b/jiracmd/create.go @@ -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 { @@ -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") diff --git a/jiracmd/subtask.go b/jiracmd/subtask.go index 887d03f8..6a04d03f 100644 --- a/jiracmd/subtask.go +++ b/jiracmd/subtask.go @@ -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) diff --git a/jiradata/AllowedValues.go b/jiradata/AllowedValues.go index 982f1714..2d5a9949 100644 --- a/jiradata/AllowedValues.go +++ b/jiradata/AllowedValues.go @@ -11,9 +11,10 @@ package jiradata ///////////////////////////////////////////////////////////////////////// // AllowedValues defined from schema: -// { -// "title": "allowedValues", -// "type": "array", -// "items": {} -// } +// +// { +// "title": "allowedValues", +// "type": "array", +// "items": {} +// } type AllowedValues []interface{} diff --git a/jiradata/PageOfCreateMetaIssueType.go b/jiradata/PageOfCreateMetaIssueType.go new file mode 100644 index 00000000..cda37b99 --- /dev/null +++ b/jiradata/PageOfCreateMetaIssueType.go @@ -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"` +} diff --git a/jiradata/Values.go b/jiradata/Values.go new file mode 100644 index 00000000..a10a2bd5 --- /dev/null +++ b/jiradata/Values.go @@ -0,0 +1,130 @@ +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 // +///////////////////////////////////////////////////////////////////////// + +// Values defined from schema: +// +// { +// "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 Values []*CreateMetaIssueType diff --git a/schema/IssueTypes.json b/schema/IssueTypes.json new file mode 100644 index 00000000..6aabd0dc --- /dev/null +++ b/schema/IssueTypes.json @@ -0,0 +1,126 @@ +{ + "id": "https://docs.atlassian.com/jira/REST/schema/page-of-create-meta-issue-type#", + "title": "Page of Create Meta Issue Type", + "type": "object", + "properties": { + "last": { + "type": "boolean" + }, + "size": { + "type": "integer" + }, + "start": { + "type": "integer" + }, + "total": { + "type": "integer" + }, + "values": { + "type": "array", + "items": { + "title": "Create Meta Issue Type", + "type": "object", + "properties": { + "self": { + "type": "string" + }, + "id": { + "type": "string" + }, + "description": { + "type": "string" + }, + "iconUrl": { + "type": "string" + }, + "name": { + "type": "string" + }, + "subtask": { + "type": "boolean" + }, + "avatarId": { + "type": "integer" + }, + "expand": { + "type": "string" + }, + "fields": { + "type": "object", + "patternProperties": { + ".+": { + "title": "Field Meta", + "type": "object", + "properties": { + "required": { + "type": "boolean" + }, + "schema": { + "title": "Json Type", + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "items": { + "type": "string" + }, + "system": { + "type": "string" + }, + "custom": { + "type": "string" + }, + "customId": { + "type": "integer" + } + }, + "additionalProperties": false + }, + "name": { + "type": "string" + }, + "fieldId": { + "type": "string" + }, + "autoCompleteUrl": { + "type": "string" + }, + "hasDefaultValue": { + "type": "boolean" + }, + "operations": { + "type": "array", + "items": { + "type": "string" + } + }, + "allowedValues": { + "type": "array", + "items": {} + }, + "defaultValue": {} + }, + "additionalProperties": false, + "required": [ + "required" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false, + "required": [ + "subtask" + ] + } + } + }, + "additionalProperties": false, + "required": [ + "last", + "size", + "start" + ] +}