From 4c1c9ac53f45aa28ace17f0af997571bb45d1c75 Mon Sep 17 00:00:00 2001 From: Lauren Leach Date: Fri, 4 Oct 2024 16:08:38 -0700 Subject: [PATCH 1/2] use issue type as schema --- go.mod | 2 +- go.sum | 4 +- .../v2/jira_cloud_external_ticket.pb.go | 99 +++- .../jira_cloud_external_ticket.pb.validate.go | 106 +++++ pkg/connector/tickets.go | 437 ++++++++++-------- .../v2/jira_cloud_external_ticket.proto | 6 + .../conductorone/go-jira/v2/cloud/issue.go | 5 + .../go-jira/v2/cloud/metaissue.go | 33 ++ vendor/modules.txt | 2 +- 9 files changed, 485 insertions(+), 209 deletions(-) diff --git a/go.mod b/go.mod index 44f33ea..71475a6 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.22 require ( github.com/conductorone/baton-sdk v0.2.34 - github.com/conductorone/go-jira/v2 v2.0.0-20240808231011-dc00f11d2470 + github.com/conductorone/go-jira/v2 v2.0.0-20241004225805-fdb598da80b3 github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 go.uber.org/zap v1.27.0 ) diff --git a/go.sum b/go.sum index b5a6b38..febb3a2 100644 --- a/go.sum +++ b/go.sum @@ -54,8 +54,8 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/conductorone/baton-sdk v0.2.34 h1:AACW62113a4j4+GGtxpXRBbZT0IB/phVuAizUY2ZFuo= github.com/conductorone/baton-sdk v0.2.34/go.mod h1:hmd/Oz3DPIKD+9QmkusZaA18ZoiinnTDdrxh2skcdUc= -github.com/conductorone/go-jira/v2 v2.0.0-20240808231011-dc00f11d2470 h1:Valo8acCYEfZp2neq+yi4wqqLh/CBU6UuxM13XQhpqo= -github.com/conductorone/go-jira/v2 v2.0.0-20240808231011-dc00f11d2470/go.mod h1:eMJODGFmHWC1egXOWdPaYfiACqNcHaS5Cnf1VMHVRBU= +github.com/conductorone/go-jira/v2 v2.0.0-20241004225805-fdb598da80b3 h1:x/nS/XmAZmmYa2ma9tdg0s5ul4xuUIAqRCYN4Y9Nog8= +github.com/conductorone/go-jira/v2 v2.0.0-20241004225805-fdb598da80b3/go.mod h1:eMJODGFmHWC1egXOWdPaYfiACqNcHaS5Cnf1VMHVRBU= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/pb/c1/connector/v2/jira_cloud_external_ticket.pb.go b/pb/c1/connector/v2/jira_cloud_external_ticket.pb.go index 0f76e8e..266927a 100644 --- a/pb/c1/connector/v2/jira_cloud_external_ticket.pb.go +++ b/pb/c1/connector/v2/jira_cloud_external_ticket.pb.go @@ -67,6 +67,69 @@ func (x *CustomField) GetType() string { return "" } +type IssueTypeProject struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ProjectId string `protobuf:"bytes,1,opt,name=project_id,json=projectId,proto3" json:"project_id,omitempty"` + ProjectName string `protobuf:"bytes,2,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` + ProjectKey string `protobuf:"bytes,3,opt,name=project_key,json=projectKey,proto3" json:"project_key,omitempty"` +} + +func (x *IssueTypeProject) Reset() { + *x = IssueTypeProject{} + if protoimpl.UnsafeEnabled { + mi := &file_c1_connector_v2_jira_cloud_external_ticket_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *IssueTypeProject) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*IssueTypeProject) ProtoMessage() {} + +func (x *IssueTypeProject) ProtoReflect() protoreflect.Message { + mi := &file_c1_connector_v2_jira_cloud_external_ticket_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use IssueTypeProject.ProtoReflect.Descriptor instead. +func (*IssueTypeProject) Descriptor() ([]byte, []int) { + return file_c1_connector_v2_jira_cloud_external_ticket_proto_rawDescGZIP(), []int{1} +} + +func (x *IssueTypeProject) GetProjectId() string { + if x != nil { + return x.ProjectId + } + return "" +} + +func (x *IssueTypeProject) GetProjectName() string { + if x != nil { + return x.ProjectName + } + return "" +} + +func (x *IssueTypeProject) GetProjectKey() string { + if x != nil { + return x.ProjectKey + } + return "" +} + var File_c1_connector_v2_jira_cloud_external_ticket_proto protoreflect.FileDescriptor var file_c1_connector_v2_jira_cloud_external_ticket_proto_rawDesc = []byte{ @@ -76,11 +139,18 @@ var file_c1_connector_v2_jira_cloud_external_ticket_proto_rawDesc = []byte{ 0x74, 0x6f, 0x12, 0x0f, 0x63, 0x31, 0x2e, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x32, 0x22, 0x21, 0x0a, 0x0b, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x42, 0x37, 0x5a, 0x35, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, - 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x6e, 0x64, 0x75, 0x63, 0x74, 0x6f, 0x72, 0x6f, 0x6e, - 0x65, 0x2f, 0x62, 0x61, 0x74, 0x6f, 0x6e, 0x2d, 0x6a, 0x69, 0x72, 0x61, 0x2f, 0x70, 0x62, 0x2f, - 0x63, 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x2f, 0x76, 0x32, 0x62, - 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0x75, 0x0a, 0x10, 0x49, 0x73, 0x73, 0x75, 0x65, 0x54, + 0x79, 0x70, 0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, + 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, + 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4b, 0x65, 0x79, 0x42, 0x37, 0x5a, + 0x35, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x6e, 0x64, + 0x75, 0x63, 0x74, 0x6f, 0x72, 0x6f, 0x6e, 0x65, 0x2f, 0x62, 0x61, 0x74, 0x6f, 0x6e, 0x2d, 0x6a, + 0x69, 0x72, 0x61, 0x2f, 0x70, 0x62, 0x2f, 0x63, 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x74, 0x6f, 0x72, 0x2f, 0x76, 0x32, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -95,9 +165,10 @@ func file_c1_connector_v2_jira_cloud_external_ticket_proto_rawDescGZIP() []byte return file_c1_connector_v2_jira_cloud_external_ticket_proto_rawDescData } -var file_c1_connector_v2_jira_cloud_external_ticket_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_c1_connector_v2_jira_cloud_external_ticket_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_c1_connector_v2_jira_cloud_external_ticket_proto_goTypes = []interface{}{ - (*CustomField)(nil), // 0: c1.connector.v2.CustomField + (*CustomField)(nil), // 0: c1.connector.v2.CustomField + (*IssueTypeProject)(nil), // 1: c1.connector.v2.IssueTypeProject } var file_c1_connector_v2_jira_cloud_external_ticket_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for method output_type @@ -125,6 +196,18 @@ func file_c1_connector_v2_jira_cloud_external_ticket_proto_init() { return nil } } + file_c1_connector_v2_jira_cloud_external_ticket_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*IssueTypeProject); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -132,7 +215,7 @@ func file_c1_connector_v2_jira_cloud_external_ticket_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_c1_connector_v2_jira_cloud_external_ticket_proto_rawDesc, NumEnums: 0, - NumMessages: 1, + NumMessages: 2, NumExtensions: 0, NumServices: 0, }, diff --git a/pb/c1/connector/v2/jira_cloud_external_ticket.pb.validate.go b/pb/c1/connector/v2/jira_cloud_external_ticket.pb.validate.go index a74e806..6d70dd2 100644 --- a/pb/c1/connector/v2/jira_cloud_external_ticket.pb.validate.go +++ b/pb/c1/connector/v2/jira_cloud_external_ticket.pb.validate.go @@ -135,3 +135,109 @@ var _ interface { Cause() error ErrorName() string } = CustomFieldValidationError{} + +// Validate checks the field values on IssueTypeProject with the rules defined +// in the proto definition for this message. If any rules are violated, the +// first error encountered is returned, or nil if there are no violations. +func (m *IssueTypeProject) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on IssueTypeProject with the rules +// defined in the proto definition for this message. If any rules are +// violated, the result is a list of violation errors wrapped in +// IssueTypeProjectMultiError, or nil if none found. +func (m *IssueTypeProject) ValidateAll() error { + return m.validate(true) +} + +func (m *IssueTypeProject) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + // no validation rules for ProjectId + + // no validation rules for ProjectName + + // no validation rules for ProjectKey + + if len(errors) > 0 { + return IssueTypeProjectMultiError(errors) + } + + return nil +} + +// IssueTypeProjectMultiError is an error wrapping multiple validation errors +// returned by IssueTypeProject.ValidateAll() if the designated constraints +// aren't met. +type IssueTypeProjectMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m IssueTypeProjectMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m IssueTypeProjectMultiError) AllErrors() []error { return m } + +// IssueTypeProjectValidationError is the validation error returned by +// IssueTypeProject.Validate if the designated constraints aren't met. +type IssueTypeProjectValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e IssueTypeProjectValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e IssueTypeProjectValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e IssueTypeProjectValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e IssueTypeProjectValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e IssueTypeProjectValidationError) ErrorName() string { return "IssueTypeProjectValidationError" } + +// Error satisfies the builtin error interface +func (e IssueTypeProjectValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sIssueTypeProject.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = IssueTypeProjectValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = IssueTypeProjectValidationError{} diff --git a/pkg/connector/tickets.go b/pkg/connector/tickets.go index cb8b55b..aa4693e 100644 --- a/pkg/connector/tickets.go +++ b/pkg/connector/tickets.go @@ -31,6 +31,26 @@ type TicketManager interface { ListTicketSchemas(ctx context.Context, pToken *pagination.Token) ([]*v2.TicketSchema, string, annotations.Annotations, error) } +// Format is projectKey:issueID. +type ProjectKeyIssueTypeIDSchemaID struct { + ProjectKey string + IssueTypeID string +} + +func (p ProjectKeyIssueTypeIDSchemaID) String() string { + return fmt.Sprintf("%s:%s", p.ProjectKey, p.IssueTypeID) +} + +func (p *ProjectKeyIssueTypeIDSchemaID) Parse(schemaID string) error { + schemaIDParts := strings.Split(schemaID, ":") + if len(schemaIDParts) != 2 { + return errors.New("invalid schemaID format, expected 'projectKey:issueTypeID'") + } + p.ProjectKey = schemaIDParts[0] + p.IssueTypeID = schemaIDParts[1] + return nil +} + type JiraName struct { Name string `json:"name,omitempty"` } @@ -154,87 +174,148 @@ func (j *Jira) constructMetaDataFields(issues []*jira.MetaIssueType) (map[string return fieldsMap, nil } -func (j *Jira) getCustomFieldsForProject(ctx context.Context, projectKey string, issueTypeIDs []string) ([]*v2.TicketCustomField, error) { - metadata, _, err := j.client.Issue.GetCreateMeta(ctx, &jira.GetQueryOptions{ - ProjectKeys: projectKey, - Expand: "projects.issuetypes.fields", - IssueTypeIds: strings.Join(issueTypeIDs, ","), - }) +func (j *Jira) schemaForProjectIssueType(ctx context.Context, project *jira.Project, issueType *jira.IssueType, statuses []*v2.TicketStatus, includeProjectInName bool) (*v2.TicketSchema, error) { + customFieldsMap := make(map[string]*v2.TicketCustomField) + + issueTypeCustomFields, err := j.getCustomFieldsForIssueType(ctx, project.ID, issueType) if err != nil { return nil, err } - if len(metadata.Projects) == 0 { - return nil, nil + for _, cf := range issueTypeCustomFields { + customFieldsMap[cf.GetId()] = cf + } + + projectKeySchemaID := &ProjectKeyIssueTypeIDSchemaID{ + ProjectKey: project.Key, + IssueTypeID: issueType.ID, + } + schemaId := projectKeySchemaID.String() + + displayName := issueType.Name + + if includeProjectInName { + displayName = fmt.Sprintf("%s (%s)", displayName, project.Key) + } + + projectAnno := &pbjira.IssueTypeProject{ + ProjectId: project.ID, + ProjectName: project.Name, + ProjectKey: project.Key, + } + + ret := &v2.TicketSchema{ + Id: schemaId, + DisplayName: displayName, + CustomFields: customFieldsMap, + Annotations: annotations.New(projectAnno), + Statuses: statuses, } - j.metaProject = metadata.Projects[0] - fieldsMap, err := j.constructMetaDataFields(j.metaProject.IssueTypes) + + return ret, nil +} + +func (j *Jira) getCustomFieldsForIssueType(ctx context.Context, projectId string, issueType *jira.IssueType) ([]*v2.TicketCustomField, error) { + customFields := make([]*v2.TicketCustomField, 0) + + issueFields, err := j.GetIssueTypeFields(ctx, projectId, issueType.ID, &jira.GetQueryIssueTypeOptions{ + MaxResults: 100, + StartAt: 0, + }) if err != nil { return nil, err } - customFields := make([]*v2.TicketCustomField, 0, len(fieldsMap)) - for _, field := range fieldsMap { - var customField *v2.TicketCustomField - var allowedValues []*v2.TicketCustomFieldObjectValue - - if !field.Required || field.Schema.Custom == "" { + for _, field := range issueFields { + // TODO(lauren) remove custom? + if field.Schema.Custom == "" { continue } + customField := convertMetadataFieldToCustomField(field) + customFields = append(customFields, customField) + } - hasAllowedValues := len(field.AllowedValues) > 0 - isMultiSelect := field.Schema.Items != "" + return customFields, nil +} - if hasAllowedValues { - for _, choice := range field.AllowedValues { - displayName := choice.Name - if displayName == "" { - displayName = choice.Value - } - allowedValues = append(allowedValues, &v2.TicketCustomFieldObjectValue{ - Id: choice.Id, - DisplayName: displayName, - }) - } +func (j *Jira) GetIssueTypeFields(ctx context.Context, projectKey, issueTypeId string, opts *jira.GetQueryIssueTypeOptions) ([]*jira.MetaDataFields, error) { + l := ctxzap.Extract(ctx) + + allMetaFields := make([]*jira.MetaDataFields, 0) + + for { + issueFields, resp, err := j.client.Issue.GetCreateMetaIssueType(ctx, projectKey, issueTypeId, opts) + if err != nil { + l.Error("error getting issue type fields", zap.Error(err)) + return nil, err } - id := field.Key - - switch field.Schema.Type { - case jira.TypeString: - customField = sdkTicket.StringFieldSchema(id, field.Name, false) - case jira.TypeArray: - switch { - case isMultiSelect && hasAllowedValues: - customField = sdkTicket.PickMultipleObjectValuesFieldSchema(id, field.Name, false, allowedValues) - case isMultiSelect && !hasAllowedValues: - customField = sdkTicket.StringsFieldSchema(id, field.Name, false) - case !isMultiSelect && hasAllowedValues: - customField = sdkTicket.PickObjectValueFieldSchema(id, field.Name, false, allowedValues) - default: - customField = sdkTicket.StringFieldSchema(id, field.Name, false) - } - case jira.TypeDate, jira.TypeDateTime: - customField = sdkTicket.TimestampFieldSchema(id, field.Name, false) - case jira.TypeNumber: - // TODO(lauren) use number field type - customField = sdkTicket.StringFieldSchema(id, field.Name, false) - case jira.TypeObject, jira.TypeGroup, jira.TypeUser, jira.TypeOption: - if hasAllowedValues { - customField = sdkTicket.PickObjectValueFieldSchema(id, field.Name, false, allowedValues) - } else { - customField = sdkTicket.StringFieldSchema(id, field.Name, false) + allMetaFields = append(allMetaFields, issueFields...) + + if len(allMetaFields) >= resp.Total || opts == nil { + break + } + + opts.StartAt += len(allMetaFields) + } + + return allMetaFields, nil +} + +func convertMetadataFieldToCustomField(metaDataField *jira.MetaDataFields) *v2.TicketCustomField { + var customField *v2.TicketCustomField + var allowedValues []*v2.TicketCustomFieldObjectValue + + hasAllowedValues := len(metaDataField.AllowedValues) > 0 + isMultiSelect := metaDataField.Schema.Items != "" + + if hasAllowedValues { + for _, choice := range metaDataField.AllowedValues { + displayName := choice.Name + if displayName == "" { + displayName = choice.Value } - default: - // Default to string, even if its not we this field would still be required to create a ticket - customField = sdkTicket.StringFieldSchema(id, field.Name, false) + allowedValues = append(allowedValues, &v2.TicketCustomFieldObjectValue{ + Id: choice.Id, + DisplayName: displayName, + }) } - customFieldAnno := &pbjira.CustomField{Type: field.Schema.Type} - customField.Annotations = annotations.New(customFieldAnno) - customFields = append(customFields, customField) } - return customFields, nil + id := metaDataField.Key + + switch metaDataField.Schema.Type { + case jira.TypeString: + customField = sdkTicket.StringFieldSchema(id, metaDataField.Name, metaDataField.Required) + case jira.TypeArray: + switch { + case isMultiSelect && hasAllowedValues: + customField = sdkTicket.PickMultipleObjectValuesFieldSchema(id, metaDataField.Name, metaDataField.Required, allowedValues) + case isMultiSelect && !hasAllowedValues: + customField = sdkTicket.StringsFieldSchema(id, metaDataField.Name, metaDataField.Required) + case !isMultiSelect && hasAllowedValues: + customField = sdkTicket.PickObjectValueFieldSchema(id, metaDataField.Name, metaDataField.Required, allowedValues) + default: + customField = sdkTicket.StringFieldSchema(id, metaDataField.Name, metaDataField.Required) + } + case jira.TypeDate, jira.TypeDateTime: + customField = sdkTicket.TimestampFieldSchema(id, metaDataField.Name, metaDataField.Required) + case jira.TypeNumber: + // TODO(lauren) use number field type + customField = sdkTicket.StringFieldSchema(id, metaDataField.Name, metaDataField.Required) + case jira.TypeObject, jira.TypeGroup, jira.TypeUser, jira.TypeOption: + if hasAllowedValues { + customField = sdkTicket.PickObjectValueFieldSchema(id, metaDataField.Name, metaDataField.Required, allowedValues) + } else { + customField = sdkTicket.StringFieldSchema(id, metaDataField.Name, metaDataField.Required) + } + default: + // Default to string, even if its not we this field would still be required to create a ticket + customField = sdkTicket.StringFieldSchema(id, metaDataField.Name, metaDataField.Required) + } + customFieldAnno := &pbjira.CustomField{Type: metaDataField.Schema.Type} + customField.Annotations = annotations.New(customFieldAnno) + return customField } func (j *Jira) ListTicketSchemas(ctx context.Context, p *pagination.Token) ([]*v2.TicketSchema, string, annotations.Annotations, error) { @@ -255,12 +336,31 @@ func (j *Jira) ListTicketSchemas(ctx context.Context, p *pagination.Token) ([]*v return nil, "", nil, wrapError(err, "failed to get projects") } + multipleProjects := false + if len(projects) > 1 { + multipleProjects = true + } + for _, project := range projects { - schema, err := j.schemaForProject(ctx, project) + statuses, err := j.getTicketStatuses(ctx, project.ID) if err != nil { return nil, "", nil, err } - ret = append(ret, schema) + for _, issueType := range project.IssueTypes { + if issueType.Name == "Epic" || issueType.Name == "Bug" { + continue + } + + if issueType.Subtask { + continue + } + + schema, err := j.schemaForProjectIssueType(ctx, &project, &issueType, statuses, multipleProjects) + if err != nil { + return nil, "", nil, err + } + ret = append(ret, schema) + } } nextPageToken := "" @@ -271,7 +371,11 @@ func (j *Jira) ListTicketSchemas(ctx context.Context, p *pagination.Token) ([]*v return ret, nextPageToken, nil, nil } -func (j *Jira) getTicketStatuses(statuses []jira.JiraStatus) ([]*v2.TicketStatus, error) { +func (j *Jira) getTicketStatuses(ctx context.Context, projectID string) ([]*v2.TicketStatus, error) { + statuses, err := j.getJiraStatusesForProject(ctx, projectID) + if err != nil { + return nil, err + } ret := make([]*v2.TicketStatus, 0, len(statuses)) for _, status := range statuses { ret = append(ret, &v2.TicketStatus{ @@ -283,100 +387,29 @@ func (j *Jira) getTicketStatuses(statuses []jira.JiraStatus) ([]*v2.TicketStatus return ret, nil } -func (j *Jira) schemaForProject(ctx context.Context, project jira.Project) (*v2.TicketSchema, error) { - var issueTypeAllowedValues []*v2.TicketCustomFieldObjectValue - - customFields := make(map[string]*v2.TicketCustomField) - - var components []*v2.TicketCustomFieldObjectValue - var issueTypeIDs []string - - for _, issueType := range project.IssueTypes { - if issueType.Name == "Epic" || issueType.Name == "Bug" { - continue - } - // TODO: Maybe we care about subtasks? - if !issueType.Subtask { - issueTypeAllowedValues = append(issueTypeAllowedValues, &v2.TicketCustomFieldObjectValue{ - Id: issueType.ID, - DisplayName: issueType.Name, - }) - issueTypeIDs = append(issueTypeIDs, issueType.ID) - } - } - for _, component := range project.Components { - components = append(components, &v2.TicketCustomFieldObjectValue{ - Id: component.ID, - DisplayName: component.Name, - }) - } - - otherCustomFields, err := j.getCustomFieldsForProject(ctx, project.Key, issueTypeIDs) +func (j *Jira) GetTicketSchema(ctx context.Context, schemaID string) (*v2.TicketSchema, annotations.Annotations, error) { + projectKeyIssueTypeID := &ProjectKeyIssueTypeIDSchemaID{} + err := projectKeyIssueTypeID.Parse(schemaID) if err != nil { - return nil, err - } - - for _, cf := range otherCustomFields { - customFields[cf.GetId()] = cf - } - - customFields["issue_type"] = sdkTicket.PickObjectValueFieldSchema( - "issue_type", - "Issue Type", - true, - issueTypeAllowedValues, - ) - - // Add a required field for the project - customFields["project"] = sdkTicket.PickObjectValueFieldSchema( - "project", - "Project", - true, - []*v2.TicketCustomFieldObjectValue{ - { - Id: project.ID, - DisplayName: project.Name, - }, - }, - ) - - if len(components) > 0 { - customFields["components"] = sdkTicket.PickMultipleObjectValuesFieldSchema( - "components", - "Components", - false, - components, - ) - } - - ret := &v2.TicketSchema{ - Id: project.Key, - DisplayName: project.Name, - CustomFields: customFields, + return nil, nil, err } - jiraStatuses, err := j.getJiraStatusesForProject(ctx, project.ID) + project, _, err := j.client.Project.Get(ctx, projectKeyIssueTypeID.ProjectKey) if err != nil { - return nil, err + return nil, nil, err } - // iterate through statues, if global or done or projectId - statuses, err := j.getTicketStatuses(jiraStatuses) - if err != nil { - return nil, err + issueType := findIssueTypeFromProject(project, projectKeyIssueTypeID.IssueTypeID) + if issueType == nil { + return nil, nil, errors.New("issueType not found") } - ret.Statuses = statuses - return ret, nil -} - -func (j *Jira) GetTicketSchema(ctx context.Context, schemaID string) (*v2.TicketSchema, annotations.Annotations, error) { - project, _, err := j.client.Project.Get(ctx, schemaID) + statuses, err := j.getTicketStatuses(ctx, project.ID) if err != nil { return nil, nil, err } - ret, err := j.schemaForProject(ctx, *project) + ret, err := j.schemaForProjectIssueType(ctx, project, issueType, statuses, false) if err != nil { return nil, nil, err } @@ -388,10 +421,6 @@ func (j *Jira) issueToTicket(ctx context.Context, issue *jira.Issue) (*v2.Ticket if issue.Fields == nil { return nil, errors.New("issue has no fields") } - schema, _, err := j.GetTicketSchema(ctx, issue.Fields.Project.ID) - if err != nil { - return nil, err - } issueURL, err := j.generateIssueURL(issue.Key) if err != nil { @@ -428,32 +457,6 @@ func (j *Jira) issueToTicket(ctx context.Context, issue *jira.Issue) (*v2.Ticket } } - retCustomFields := make(map[string]*v2.TicketCustomField) - for id, cf := range schema.GetCustomFields() { - switch id { - case "project": - retCustomFields[id] = sdkTicket.PickObjectValueField(cf.GetId(), &v2.TicketCustomFieldObjectValue{ - Id: issue.Fields.Project.ID, - DisplayName: issue.Fields.Project.Name, - }) - case "components": - var components []*v2.TicketCustomFieldObjectValue - for _, component := range issue.Fields.Components { - components = append(components, &v2.TicketCustomFieldObjectValue{ - Id: component.ID, - DisplayName: component.Name, - }) - } - retCustomFields[id] = sdkTicket.PickMultipleObjectValuesField(cf.GetId(), components) - case "issue_type": - retCustomFields[id] = sdkTicket.PickObjectValueField(cf.GetId(), &v2.TicketCustomFieldObjectValue{ - Id: issue.Fields.Type.ID, - DisplayName: issue.Fields.Type.Name, - }) - } - } - ret.CustomFields = retCustomFields - return ret, nil } @@ -485,22 +488,30 @@ func (j *Jira) CreateTicket(ctx context.Context, ticket *v2.Ticket, schema *v2.T ticketFields := ticket.GetCustomFields() - var projectID string + var projectKey string + var issueTypeID string + + projectAnno := GetProjectAnnotation(schema.Annotations) + if projectAnno == nil { + // If no projectAnnotation assume schema id is project + // Because the config schema may have not been updated + projectKey = schema.Id + } else { + projectKeyIssueTypeID := &ProjectKeyIssueTypeIDSchemaID{} + err := projectKeyIssueTypeID.Parse(schema.Id) + if err != nil { + return nil, nil, err + } + projectKey = projectKeyIssueTypeID.ProjectKey + issueTypeID = projectKeyIssueTypeID.IssueTypeID + // This could use projectAnno.ProjectKey but the former schemaID is the projectKey so using + // this for consistency + } for id, cf := range schema.GetCustomFields() { switch id { case "project": - project, err := sdkTicket.GetPickObjectValue(ticketFields[id]) - if err != nil { - return nil, nil, err - } - - if project.GetId() == "" { - return nil, nil, errors.New("error: unable to create ticket, project is required") - } - - projectID = project.GetId() - + continue case "components": comps, err := sdkTicket.GetPickMultipleObjectValues(ticketFields[id]) if err != nil { @@ -516,12 +527,15 @@ func (j *Jira) CreateTicket(ctx context.Context, ticket *v2.Ticket, schema *v2.T } ticketOptions = append(ticketOptions, WithComponents(componentIDs...)) case "issue_type": - issueType, err := sdkTicket.GetPickObjectValue(ticketFields[id]) - if err != nil { - return nil, nil, err + // If issueTypeID is empty, the config has not been updated to use issue type as schema + // So issue type is still stored in the custom fields + if issueTypeID == "" { + issueType, err := sdkTicket.GetPickObjectValue(ticketFields[id]) + if err != nil { + return nil, nil, err + } + issueTypeID = issueType.GetId() } - - ticketOptions = append(ticketOptions, WithType(issueType.GetId())) default: metaFieldValue, err := j.customFieldSchemaToMetaField(ticketFields[id]) if err != nil { @@ -537,6 +551,12 @@ func (j *Jira) CreateTicket(ctx context.Context, ticket *v2.Ticket, schema *v2.T } } + if issueTypeID == "" { + return nil, nil, errors.New("error: unable to create ticket, issue type is required") + } + + ticketOptions = append(ticketOptions, WithType(issueTypeID)) + valid, err := sdkTicket.ValidateTicket(ctx, schema, ticket) if err != nil { return nil, nil, err @@ -545,7 +565,7 @@ func (j *Jira) CreateTicket(ctx context.Context, ticket *v2.Ticket, schema *v2.T return nil, nil, errors.Join(errors.New("error: unable to create ticket, ticket is invalid"), sdkTicket.ErrTicketValidationError) } - iss, err := j.createIssue(ctx, projectID, ticket.GetDisplayName(), ticketOptions...) + iss, err := j.createIssue(ctx, projectKey, ticket.GetDisplayName(), ticketOptions...) if err != nil { return nil, nil, err } @@ -618,14 +638,14 @@ func WithComponents(componentIDs ...string) FieldOption { } } -func (j *Jira) createIssue(ctx context.Context, projectID string, summary string, opts ...FieldOption) (*jira.Issue, error) { +func (j *Jira) createIssue(ctx context.Context, projectKey string, summary string, opts ...FieldOption) (*jira.Issue, error) { l := ctxzap.Extract(ctx) i := &jira.Issue{ Fields: &jira.IssueFields{ Summary: summary, Project: jira.Project{ - ID: projectID, + Key: projectKey, }, }, } @@ -677,3 +697,26 @@ func GeCustomFieldTypeAnnotation(annotations []*anypb.Any) string { } return "" } + +func GetProjectAnnotation(annotations []*anypb.Any) *pbjira.IssueTypeProject { + pta := &pbjira.IssueTypeProject{} + for _, a := range annotations { + if a.MessageIs(pta) { + err := a.UnmarshalTo(pta) + if err != nil { + return nil + } + return pta + } + } + return nil +} + +func findIssueTypeFromProject(project *jira.Project, issueTypeId string) *jira.IssueType { + for _, issueType := range project.IssueTypes { + if issueType.ID == issueTypeId { + return &issueType + } + } + return nil +} diff --git a/proto/c1/connector/v2/jira_cloud_external_ticket.proto b/proto/c1/connector/v2/jira_cloud_external_ticket.proto index fd05cb2..cbf3ec3 100644 --- a/proto/c1/connector/v2/jira_cloud_external_ticket.proto +++ b/proto/c1/connector/v2/jira_cloud_external_ticket.proto @@ -4,4 +4,10 @@ option go_package = "github.com/conductorone/baton-jira/pb/c1/connector/v2"; message CustomField { string type = 1; +} + +message IssueTypeProject { + string project_id = 1; + string project_name = 2; + string project_key = 3; } \ No newline at end of file diff --git a/vendor/github.com/conductorone/go-jira/v2/cloud/issue.go b/vendor/github.com/conductorone/go-jira/v2/cloud/issue.go index 15ae9f3..5ddf710 100644 --- a/vendor/github.com/conductorone/go-jira/v2/cloud/issue.go +++ b/vendor/github.com/conductorone/go-jira/v2/cloud/issue.go @@ -552,6 +552,11 @@ type GetQueryOptions struct { IssueTypeNames string `url:"issueTypeNames,omitempty"` } +type GetQueryIssueTypeOptions struct { + MaxResults int `url:"maxResults,omitempty"` + StartAt int `url:"startAt,omitempty"` +} + // GetWorklogsQueryOptions specifies the optional parameters for the Get Worklogs method type GetWorklogsQueryOptions struct { StartAt int64 `url:"startAt,omitempty"` diff --git a/vendor/github.com/conductorone/go-jira/v2/cloud/metaissue.go b/vendor/github.com/conductorone/go-jira/v2/cloud/metaissue.go index 5bd5858..2e4371e 100644 --- a/vendor/github.com/conductorone/go-jira/v2/cloud/metaissue.go +++ b/vendor/github.com/conductorone/go-jira/v2/cloud/metaissue.go @@ -16,6 +16,13 @@ type CreateMetaInfo struct { Projects []*MetaProject `json:"projects,omitempty"` } +type CreateMetaIssueType struct { + MaxResults int `json:"maxResults,omitempty"` + StartAt int `json:"startAt,omitempty"` + Total int `json:"total,omitempty"` + Values []*MetaDataFields `json:"fields,omitempty"` +} + // EditMetaInfo contains information about fields and their attributed to edit a ticket. type EditMetaInfo struct { Fields tcontainer.MarshalMap `json:"fields,omitempty"` @@ -39,6 +46,7 @@ type MetaDataFields struct { Required bool `json:"required"` Schema Schema `json:"schema"` Name string `json:"name"` + FieldId string `json:"fieldId"` Key string `json:"key"` HasDefaultValue bool `json:"hasDefaultValue"` AllowedValues []Choice `json:"allowedValues,omitempty"` @@ -115,6 +123,31 @@ func (s *IssueService) GetCreateMeta(ctx context.Context, options *GetQueryOptio return meta, resp, nil } +func (s *IssueService) GetCreateMetaIssueType(ctx context.Context, projectKey, issueTypeId string, options *GetQueryIssueTypeOptions) ([]*MetaDataFields, *Response, error) { + apiEndpoint := fmt.Sprintf("rest/api/2/issue/createmeta/%s/issuetypes/%s", projectKey, issueTypeId) + + req, err := s.client.NewRequest(ctx, http.MethodGet, apiEndpoint, nil) + if err != nil { + return nil, nil, err + } + + if options != nil { + q, err := query.Values(options) + if err != nil { + return nil, nil, err + } + req.URL.RawQuery = q.Encode() + } + + meta := new(CreateMetaIssueType) + resp, err := s.client.Do(req, meta) + if err != nil { + return nil, resp, err + } + + return meta.Values, resp, nil +} + // GetEditMeta makes the api call to get the edit meta information for an issue // // TODO Double check this method if this works as expected, is using the latest API and the response is complete diff --git a/vendor/modules.txt b/vendor/modules.txt index 85581a3..8f5b91b 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -190,7 +190,7 @@ github.com/conductorone/baton-sdk/pkg/uhttp github.com/conductorone/baton-sdk/pkg/us3 github.com/conductorone/baton-sdk/pkg/ustrings github.com/conductorone/baton-sdk/pkg/utls -# github.com/conductorone/go-jira/v2 v2.0.0-20240808231011-dc00f11d2470 +# github.com/conductorone/go-jira/v2 v2.0.0-20241004225805-fdb598da80b3 ## explicit; go 1.22 github.com/conductorone/go-jira/v2/cloud # github.com/deckarep/golang-set/v2 v2.6.0 From 23ec4fef6ed14dec843e9442e6662258209ec669 Mon Sep 17 00:00:00 2001 From: Lauren Leach Date: Fri, 4 Oct 2024 16:25:56 -0700 Subject: [PATCH 2/2] rename proto because it conflicts with jira datacenter --- .../v2/jira_cloud_external_ticket.pb.go | 52 +++++++++--------- .../jira_cloud_external_ticket.pb.validate.go | 54 ++++++++++--------- pkg/connector/tickets.go | 6 +-- .../v2/jira_cloud_external_ticket.proto | 2 +- 4 files changed, 58 insertions(+), 56 deletions(-) diff --git a/pb/c1/connector/v2/jira_cloud_external_ticket.pb.go b/pb/c1/connector/v2/jira_cloud_external_ticket.pb.go index 266927a..8473425 100644 --- a/pb/c1/connector/v2/jira_cloud_external_ticket.pb.go +++ b/pb/c1/connector/v2/jira_cloud_external_ticket.pb.go @@ -67,7 +67,7 @@ func (x *CustomField) GetType() string { return "" } -type IssueTypeProject struct { +type JCIssueTypeProject struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields @@ -77,8 +77,8 @@ type IssueTypeProject struct { ProjectKey string `protobuf:"bytes,3,opt,name=project_key,json=projectKey,proto3" json:"project_key,omitempty"` } -func (x *IssueTypeProject) Reset() { - *x = IssueTypeProject{} +func (x *JCIssueTypeProject) Reset() { + *x = JCIssueTypeProject{} if protoimpl.UnsafeEnabled { mi := &file_c1_connector_v2_jira_cloud_external_ticket_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -86,13 +86,13 @@ func (x *IssueTypeProject) Reset() { } } -func (x *IssueTypeProject) String() string { +func (x *JCIssueTypeProject) String() string { return protoimpl.X.MessageStringOf(x) } -func (*IssueTypeProject) ProtoMessage() {} +func (*JCIssueTypeProject) ProtoMessage() {} -func (x *IssueTypeProject) ProtoReflect() protoreflect.Message { +func (x *JCIssueTypeProject) ProtoReflect() protoreflect.Message { mi := &file_c1_connector_v2_jira_cloud_external_ticket_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -104,26 +104,26 @@ func (x *IssueTypeProject) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use IssueTypeProject.ProtoReflect.Descriptor instead. -func (*IssueTypeProject) Descriptor() ([]byte, []int) { +// Deprecated: Use JCIssueTypeProject.ProtoReflect.Descriptor instead. +func (*JCIssueTypeProject) Descriptor() ([]byte, []int) { return file_c1_connector_v2_jira_cloud_external_ticket_proto_rawDescGZIP(), []int{1} } -func (x *IssueTypeProject) GetProjectId() string { +func (x *JCIssueTypeProject) GetProjectId() string { if x != nil { return x.ProjectId } return "" } -func (x *IssueTypeProject) GetProjectName() string { +func (x *JCIssueTypeProject) GetProjectName() string { if x != nil { return x.ProjectName } return "" } -func (x *IssueTypeProject) GetProjectKey() string { +func (x *JCIssueTypeProject) GetProjectKey() string { if x != nil { return x.ProjectKey } @@ -139,18 +139,18 @@ var file_c1_connector_v2_jira_cloud_external_ticket_proto_rawDesc = []byte{ 0x74, 0x6f, 0x12, 0x0f, 0x63, 0x31, 0x2e, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x32, 0x22, 0x21, 0x0a, 0x0b, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0x75, 0x0a, 0x10, 0x49, 0x73, 0x73, 0x75, 0x65, 0x54, - 0x79, 0x70, 0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x72, - 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, - 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, - 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, - 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4b, 0x65, 0x79, 0x42, 0x37, 0x5a, - 0x35, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x6e, 0x64, - 0x75, 0x63, 0x74, 0x6f, 0x72, 0x6f, 0x6e, 0x65, 0x2f, 0x62, 0x61, 0x74, 0x6f, 0x6e, 0x2d, 0x6a, - 0x69, 0x72, 0x61, 0x2f, 0x70, 0x62, 0x2f, 0x63, 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, - 0x74, 0x6f, 0x72, 0x2f, 0x76, 0x32, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0x77, 0x0a, 0x12, 0x4a, 0x43, 0x49, 0x73, 0x73, 0x75, + 0x65, 0x54, 0x79, 0x70, 0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1d, 0x0a, 0x0a, + 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x09, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x70, + 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1f, + 0x0a, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4b, 0x65, 0x79, 0x42, + 0x37, 0x5a, 0x35, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, + 0x6e, 0x64, 0x75, 0x63, 0x74, 0x6f, 0x72, 0x6f, 0x6e, 0x65, 0x2f, 0x62, 0x61, 0x74, 0x6f, 0x6e, + 0x2d, 0x6a, 0x69, 0x72, 0x61, 0x2f, 0x70, 0x62, 0x2f, 0x63, 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x6e, + 0x65, 0x63, 0x74, 0x6f, 0x72, 0x2f, 0x76, 0x32, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -167,8 +167,8 @@ func file_c1_connector_v2_jira_cloud_external_ticket_proto_rawDescGZIP() []byte var file_c1_connector_v2_jira_cloud_external_ticket_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_c1_connector_v2_jira_cloud_external_ticket_proto_goTypes = []interface{}{ - (*CustomField)(nil), // 0: c1.connector.v2.CustomField - (*IssueTypeProject)(nil), // 1: c1.connector.v2.IssueTypeProject + (*CustomField)(nil), // 0: c1.connector.v2.CustomField + (*JCIssueTypeProject)(nil), // 1: c1.connector.v2.JCIssueTypeProject } var file_c1_connector_v2_jira_cloud_external_ticket_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for method output_type @@ -197,7 +197,7 @@ func file_c1_connector_v2_jira_cloud_external_ticket_proto_init() { } } file_c1_connector_v2_jira_cloud_external_ticket_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*IssueTypeProject); i { + switch v := v.(*JCIssueTypeProject); i { case 0: return &v.state case 1: diff --git a/pb/c1/connector/v2/jira_cloud_external_ticket.pb.validate.go b/pb/c1/connector/v2/jira_cloud_external_ticket.pb.validate.go index 6d70dd2..dfe113a 100644 --- a/pb/c1/connector/v2/jira_cloud_external_ticket.pb.validate.go +++ b/pb/c1/connector/v2/jira_cloud_external_ticket.pb.validate.go @@ -136,22 +136,22 @@ var _ interface { ErrorName() string } = CustomFieldValidationError{} -// Validate checks the field values on IssueTypeProject with the rules defined -// in the proto definition for this message. If any rules are violated, the -// first error encountered is returned, or nil if there are no violations. -func (m *IssueTypeProject) Validate() error { +// Validate checks the field values on JCIssueTypeProject with the rules +// defined in the proto definition for this message. If any rules are +// violated, the first error encountered is returned, or nil if there are no violations. +func (m *JCIssueTypeProject) Validate() error { return m.validate(false) } -// ValidateAll checks the field values on IssueTypeProject with the rules +// ValidateAll checks the field values on JCIssueTypeProject with the rules // defined in the proto definition for this message. If any rules are // violated, the result is a list of violation errors wrapped in -// IssueTypeProjectMultiError, or nil if none found. -func (m *IssueTypeProject) ValidateAll() error { +// JCIssueTypeProjectMultiError, or nil if none found. +func (m *JCIssueTypeProject) ValidateAll() error { return m.validate(true) } -func (m *IssueTypeProject) validate(all bool) error { +func (m *JCIssueTypeProject) validate(all bool) error { if m == nil { return nil } @@ -165,19 +165,19 @@ func (m *IssueTypeProject) validate(all bool) error { // no validation rules for ProjectKey if len(errors) > 0 { - return IssueTypeProjectMultiError(errors) + return JCIssueTypeProjectMultiError(errors) } return nil } -// IssueTypeProjectMultiError is an error wrapping multiple validation errors -// returned by IssueTypeProject.ValidateAll() if the designated constraints +// JCIssueTypeProjectMultiError is an error wrapping multiple validation errors +// returned by JCIssueTypeProject.ValidateAll() if the designated constraints // aren't met. -type IssueTypeProjectMultiError []error +type JCIssueTypeProjectMultiError []error // Error returns a concatenation of all the error messages it wraps. -func (m IssueTypeProjectMultiError) Error() string { +func (m JCIssueTypeProjectMultiError) Error() string { var msgs []string for _, err := range m { msgs = append(msgs, err.Error()) @@ -186,11 +186,11 @@ func (m IssueTypeProjectMultiError) Error() string { } // AllErrors returns a list of validation violation errors. -func (m IssueTypeProjectMultiError) AllErrors() []error { return m } +func (m JCIssueTypeProjectMultiError) AllErrors() []error { return m } -// IssueTypeProjectValidationError is the validation error returned by -// IssueTypeProject.Validate if the designated constraints aren't met. -type IssueTypeProjectValidationError struct { +// JCIssueTypeProjectValidationError is the validation error returned by +// JCIssueTypeProject.Validate if the designated constraints aren't met. +type JCIssueTypeProjectValidationError struct { field string reason string cause error @@ -198,22 +198,24 @@ type IssueTypeProjectValidationError struct { } // Field function returns field value. -func (e IssueTypeProjectValidationError) Field() string { return e.field } +func (e JCIssueTypeProjectValidationError) Field() string { return e.field } // Reason function returns reason value. -func (e IssueTypeProjectValidationError) Reason() string { return e.reason } +func (e JCIssueTypeProjectValidationError) Reason() string { return e.reason } // Cause function returns cause value. -func (e IssueTypeProjectValidationError) Cause() error { return e.cause } +func (e JCIssueTypeProjectValidationError) Cause() error { return e.cause } // Key function returns key value. -func (e IssueTypeProjectValidationError) Key() bool { return e.key } +func (e JCIssueTypeProjectValidationError) Key() bool { return e.key } // ErrorName returns error name. -func (e IssueTypeProjectValidationError) ErrorName() string { return "IssueTypeProjectValidationError" } +func (e JCIssueTypeProjectValidationError) ErrorName() string { + return "JCIssueTypeProjectValidationError" +} // Error satisfies the builtin error interface -func (e IssueTypeProjectValidationError) Error() string { +func (e JCIssueTypeProjectValidationError) Error() string { cause := "" if e.cause != nil { cause = fmt.Sprintf(" | caused by: %v", e.cause) @@ -225,14 +227,14 @@ func (e IssueTypeProjectValidationError) Error() string { } return fmt.Sprintf( - "invalid %sIssueTypeProject.%s: %s%s", + "invalid %sJCIssueTypeProject.%s: %s%s", key, e.field, e.reason, cause) } -var _ error = IssueTypeProjectValidationError{} +var _ error = JCIssueTypeProjectValidationError{} var _ interface { Field() string @@ -240,4 +242,4 @@ var _ interface { Key() bool Cause() error ErrorName() string -} = IssueTypeProjectValidationError{} +} = JCIssueTypeProjectValidationError{} diff --git a/pkg/connector/tickets.go b/pkg/connector/tickets.go index aa4693e..6312189 100644 --- a/pkg/connector/tickets.go +++ b/pkg/connector/tickets.go @@ -198,7 +198,7 @@ func (j *Jira) schemaForProjectIssueType(ctx context.Context, project *jira.Proj displayName = fmt.Sprintf("%s (%s)", displayName, project.Key) } - projectAnno := &pbjira.IssueTypeProject{ + projectAnno := &pbjira.JCIssueTypeProject{ ProjectId: project.ID, ProjectName: project.Name, ProjectKey: project.Key, @@ -698,8 +698,8 @@ func GeCustomFieldTypeAnnotation(annotations []*anypb.Any) string { return "" } -func GetProjectAnnotation(annotations []*anypb.Any) *pbjira.IssueTypeProject { - pta := &pbjira.IssueTypeProject{} +func GetProjectAnnotation(annotations []*anypb.Any) *pbjira.JCIssueTypeProject { + pta := &pbjira.JCIssueTypeProject{} for _, a := range annotations { if a.MessageIs(pta) { err := a.UnmarshalTo(pta) diff --git a/proto/c1/connector/v2/jira_cloud_external_ticket.proto b/proto/c1/connector/v2/jira_cloud_external_ticket.proto index cbf3ec3..6886193 100644 --- a/proto/c1/connector/v2/jira_cloud_external_ticket.proto +++ b/proto/c1/connector/v2/jira_cloud_external_ticket.proto @@ -6,7 +6,7 @@ message CustomField { string type = 1; } -message IssueTypeProject { +message JCIssueTypeProject { string project_id = 1; string project_name = 2; string project_key = 3;