CreateIssue creates a issue.
(ctx context.Context, req *connect.Request[v1pb.CreateIssueRequest])
| 380 | |
| 381 | // CreateIssue creates a issue. |
| 382 | func (s *IssueService) CreateIssue(ctx context.Context, req *connect.Request[v1pb.CreateIssueRequest]) (*connect.Response[v1pb.Issue], error) { |
| 383 | projectID, err := common.GetProjectID(req.Msg.Parent) |
| 384 | if err != nil { |
| 385 | return nil, connect.NewError(connect.CodeInvalidArgument, errors.Errorf("%v", err.Error())) |
| 386 | } |
| 387 | project, err := s.store.GetProject(ctx, &store.FindProjectMessage{ |
| 388 | Workspace: common.GetWorkspaceIDFromContext(ctx), |
| 389 | ResourceID: &projectID, |
| 390 | }) |
| 391 | if err != nil { |
| 392 | return nil, connect.NewError(connect.CodeInternal, errors.Wrapf(err, "failed to get project")) |
| 393 | } |
| 394 | if project == nil { |
| 395 | return nil, connect.NewError(connect.CodeNotFound, errors.Errorf("project not found for id: %v", projectID)) |
| 396 | } |
| 397 | |
| 398 | issueLabels := store.CanonicalizeIssueLabels(req.Msg.Issue.Labels) |
| 399 | if project.Setting.ForceIssueLabels && len(issueLabels) == 0 { |
| 400 | return nil, connect.NewError(connect.CodeInvalidArgument, errors.Errorf("require issue labels")) |
| 401 | } |
| 402 | |
| 403 | // enforceIssueTitle is enforced on CreatePlan (plan.Name is gated there). Issues |
| 404 | // with empty Title inherit plan.Name via buildIssueMessage; ROLE_GRANT issues |
| 405 | // have their own title-required check below. No gate needed here. |
| 406 | |
| 407 | user, ok := GetUserFromContext(ctx) |
| 408 | if !ok { |
| 409 | return nil, connect.NewError(connect.CodeInternal, errors.Errorf("user not found")) |
| 410 | } |
| 411 | |
| 412 | issue, err := s.buildIssueMessage(ctx, project, user.Email, req.Msg, issueLabels) |
| 413 | if err != nil { |
| 414 | return nil, err |
| 415 | } |
| 416 | issue, err = s.store.CreateIssue(ctx, issue) |
| 417 | if err != nil { |
| 418 | return nil, connect.NewError(connect.CodeInternal, errors.Wrapf(err, "failed to create issue")) |
| 419 | } |
| 420 | |
| 421 | issue, err = postCreateIssue(ctx, s.store, s.webhookManager, s.licenseService, s.bus, project, user.Name, user.Email, issue) |
| 422 | if err != nil { |
| 423 | return nil, connect.NewError(connect.CodeInternal, err) |
| 424 | } |
| 425 | |
| 426 | converted, err := s.convertToIssue(issue) |
| 427 | if err != nil { |
| 428 | return nil, connect.NewError(connect.CodeInternal, errors.Wrapf(err, "failed to convert to issue")) |
| 429 | } |
| 430 | |
| 431 | return connect.NewResponse(converted), nil |
| 432 | } |
| 433 | |
| 434 | func (s *IssueService) buildIssueMessage(ctx context.Context, project *store.ProjectMessage, userEmail string, request *v1pb.CreateIssueRequest, labels []string) (*store.IssueMessage, error) { |
| 435 | var planUID *int64 |
no test coverage detected