diff --git a/cloud/field.go b/cloud/field.go index f25f49f6..cf5cc27e 100644 --- a/cloud/field.go +++ b/cloud/field.go @@ -2,6 +2,7 @@ package cloud import ( "context" + "fmt" "net/http" ) @@ -52,3 +53,112 @@ func (s *FieldService) GetList(ctx context.Context) ([]Field, *Response, error) } return fieldList, resp, nil } + +// FieldCreateOptions are passed to the Field.Create function to create a new Jira custom field +type FieldCreateOptions struct { + // Name: The name of the custom field, which is displayed in Jira. This is not the unique identifier. + // Required when creating a field. + // Optional when updating a field. + Name string `json:"name,omitempty" structs:"name,omitempty"` + + // Description: The description for the field. + // Optional when creating or updating a field. + Description string `json:"description,omitempty" structs:"description,omitempty"` + + // SearcherKey: The searcher defines the way the field is searched in Jira. For example, com.atlassian.jira.plugin.system.customfieldtypes:grouppickersearcher. The search UI (basic search and JQL search) will display different operations and values for the field, based on the field searcher. + // Can take the following values: + // com.atlassian.jira.plugin.system.customfieldtypes:cascadingselectsearcher + // com.atlassian.jira.plugin.system.customfieldtypes:daterange + // com.atlassian.jira.plugin.system.customfieldtypes:datetimerange + // com.atlassian.jira.plugin.system.customfieldtypes:exactnumber + // com.atlassian.jira.plugin.system.customfieldtypes:exacttextsearcher + // com.atlassian.jira.plugin.system.customfieldtypes:grouppickersearcher + // com.atlassian.jira.plugin.system.customfieldtypes:labelsearcher + // com.atlassian.jira.plugin.system.customfieldtypes:multiselectsearcher + // com.atlassian.jira.plugin.system.customfieldtypes:numberrange + // com.atlassian.jira.plugin.system.customfieldtypes:projectsearcher + // com.atlassian.jira.plugin.system.customfieldtypes:textsearcher + // com.atlassian.jira.plugin.system.customfieldtypes:userpickergroupsearcher + // com.atlassian.jira.plugin.system.customfieldtypes:versionsearcher + // + // Optional when creating or updating a field. + SearcherKey string `json:"searcherKey,omitempty" structs:"searcherKey,omitempty"` + + // Project: The type of the custom field. + // Can take the following values: + // com.atlassian.jira.plugin.system.customfieldtypes:cascadingselect + // com.atlassian.jira.plugin.system.customfieldtypes:datepicker + // com.atlassian.jira.plugin.system.customfieldtypes:datetime + // com.atlassian.jira.plugin.system.customfieldtypes:float + // com.atlassian.jira.plugin.system.customfieldtypes:grouppicker + // com.atlassian.jira.plugin.system.customfieldtypes:importid + // com.atlassian.jira.plugin.system.customfieldtypes:labels + // com.atlassian.jira.plugin.system.customfieldtypes:multicheckboxes + // com.atlassian.jira.plugin.system.customfieldtypes:multigrouppicker + // com.atlassian.jira.plugin.system.customfieldtypes:multiversion + // com.atlassian.jira.plugin.system.customfieldtypes:project + // com.atlassian.jira.plugin.system.customfieldtypes:radiobuttons + // com.atlassian.jira.plugin.system.customfieldtypes:readonlyfield + // com.atlassian.jira.plugin.system.customfieldtypes:select + // com.atlassian.jira.plugin.system.customfieldtypes:textarea + // com.atlassian.jira.plugin.system.customfieldtypes:textfield + // com.atlassian.jira.plugin.system.customfieldtypes:url + // com.atlassian.jira.plugin.system.customfieldtypes:userpicker + // com.atlassian.jira.plugin.system.customfieldtypes:version + // + // Required when creating a field. + // Can't be updated. + Type string `json:"type,omitempty" structs:"type,omitempty"` +} + +// Creates a custom field on Jira +// +// Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-issue-fields/#api-rest-api-2-field-post +func (s *FieldService) CreateCustom(ctx context.Context, options *FieldCreateOptions) (*Field, *Response, error) { + apiEndpoint := "rest/api/2/field" + req, err := s.client.NewRequest(ctx, http.MethodPost, apiEndpoint, options) + if err != nil { + return nil, nil, err + } + + component := new(Field) + resp, err := s.client.Do(req, component) + if err != nil { + return nil, resp, NewJiraError(resp, err) + } + + return component, resp, nil +} + +// Updates a custom field on Jira +// +// Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-issue-fields/#api-rest-api-2-field-fieldid-put +func (s *FieldService) UpdateCustom(ctx context.Context, fieldId string, options *FieldCreateOptions) (*Response, error) { + apiEndpoint := fmt.Sprintf("rest/api/2/field/%s", fieldId) + req, err := s.client.NewRequest(ctx, http.MethodPut, apiEndpoint, options) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if err != nil { + return resp, NewJiraError(resp, err) + } + + return resp, nil +} + +func (s *FieldService) DeleteCustom(ctx context.Context, fieldId string) (*Response, error) { + apiEndpoint := fmt.Sprintf("rest/api/2/field/%s", fieldId) + req, err := s.client.NewRequest(ctx, http.MethodDelete, apiEndpoint, nil) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if resp.StatusCode != 303 { + return resp, NewJiraError(resp, err) + } + + return resp, nil +} diff --git a/onpremise/field.go b/onpremise/field.go index ec548daa..87bf8e7b 100644 --- a/onpremise/field.go +++ b/onpremise/field.go @@ -2,6 +2,7 @@ package onpremise import ( "context" + "fmt" "net/http" ) @@ -52,3 +53,112 @@ func (s *FieldService) GetList(ctx context.Context) ([]Field, *Response, error) } return fieldList, resp, nil } + +// FieldCreateOptions are passed to the Field.Create function to create a new Jira cusotm field +type FieldCreateOptions struct { + // Name: The name of the custom field, which is displayed in Jira. This is not the unique identifier. + // Required when creating a field. + // Optional when updating a field. + Name string `json:"name,omitempty" structs:"name,omitempty"` + + // Description: The description for the field. + // Optional when creating or updating a field. + Description string `json:"description,omitempty" structs:"description,omitempty"` + + // SearcherKey: The searcher defines the way the field is searched in Jira. For example, com.atlassian.jira.plugin.system.customfieldtypes:grouppickersearcher. The search UI (basic search and JQL search) will display different operations and values for the field, based on the field searcher. + // Can take the following values: + // com.atlassian.jira.plugin.system.customfieldtypes:cascadingselectsearcher + // com.atlassian.jira.plugin.system.customfieldtypes:daterange + // com.atlassian.jira.plugin.system.customfieldtypes:datetimerange + // com.atlassian.jira.plugin.system.customfieldtypes:exactnumber + // com.atlassian.jira.plugin.system.customfieldtypes:exacttextsearcher + // com.atlassian.jira.plugin.system.customfieldtypes:grouppickersearcher + // com.atlassian.jira.plugin.system.customfieldtypes:labelsearcher + // com.atlassian.jira.plugin.system.customfieldtypes:multiselectsearcher + // com.atlassian.jira.plugin.system.customfieldtypes:numberrange + // com.atlassian.jira.plugin.system.customfieldtypes:projectsearcher + // com.atlassian.jira.plugin.system.customfieldtypes:textsearcher + // com.atlassian.jira.plugin.system.customfieldtypes:userpickergroupsearcher + // com.atlassian.jira.plugin.system.customfieldtypes:versionsearcher + // + // Optional when creating or updating a field. + SearcherKey string `json:"searcherKey,omitempty" structs:"searcherKey,omitempty"` + + // Project: The type of the custom field. + // Can take the following values: + // com.atlassian.jira.plugin.system.customfieldtypes:cascadingselect + // com.atlassian.jira.plugin.system.customfieldtypes:datepicker + // com.atlassian.jira.plugin.system.customfieldtypes:datetime + // com.atlassian.jira.plugin.system.customfieldtypes:float + // com.atlassian.jira.plugin.system.customfieldtypes:grouppicker + // com.atlassian.jira.plugin.system.customfieldtypes:importid + // com.atlassian.jira.plugin.system.customfieldtypes:labels + // com.atlassian.jira.plugin.system.customfieldtypes:multicheckboxes + // com.atlassian.jira.plugin.system.customfieldtypes:multigrouppicker + // com.atlassian.jira.plugin.system.customfieldtypes:multiversion + // com.atlassian.jira.plugin.system.customfieldtypes:project + // com.atlassian.jira.plugin.system.customfieldtypes:radiobuttons + // com.atlassian.jira.plugin.system.customfieldtypes:readonlyfield + // com.atlassian.jira.plugin.system.customfieldtypes:select + // com.atlassian.jira.plugin.system.customfieldtypes:textarea + // com.atlassian.jira.plugin.system.customfieldtypes:textfield + // com.atlassian.jira.plugin.system.customfieldtypes:url + // com.atlassian.jira.plugin.system.customfieldtypes:userpicker + // com.atlassian.jira.plugin.system.customfieldtypes:version + // + // Required when creating a field. + // Can't be updated. + Type string `json:"type,omitempty" structs:"type,omitempty"` +} + +// Creates a custom field on Jira +// +// Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-issue-fields/#api-rest-api-2-field-post +func (s *FieldService) CreateCustom(ctx context.Context, options *FieldCreateOptions) (*Field, *Response, error) { + apiEndpoint := "rest/api/2/field" + req, err := s.client.NewRequest(ctx, http.MethodPost, apiEndpoint, options) + if err != nil { + return nil, nil, err + } + + component := new(Field) + resp, err := s.client.Do(req, component) + if err != nil { + return nil, resp, NewJiraError(resp, err) + } + + return component, resp, nil +} + +// Updates a custom field on Jira +// +// Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-issue-fields/#api-rest-api-2-field-fieldid-put +func (s *FieldService) UpdateCustom(ctx context.Context, fieldId string, options *FieldCreateOptions) (*Response, error) { + apiEndpoint := fmt.Sprintf("rest/api/2/field/%s", fieldId) + req, err := s.client.NewRequest(ctx, http.MethodPut, apiEndpoint, options) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if err != nil { + return resp, NewJiraError(resp, err) + } + + return resp, nil +} + +func (s *FieldService) DeleteCustom(ctx context.Context, fieldId string) (*Response, error) { + apiEndpoint := fmt.Sprintf("rest/api/2/field/%s", fieldId) + req, err := s.client.NewRequest(ctx, http.MethodDelete, apiEndpoint, nil) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if resp.StatusCode != 303 { + return resp, NewJiraError(resp, err) + } + + return resp, nil +}