CreatePlan creates a new plan.
(ctx context.Context, request *connect.Request[v1pb.CreatePlanRequest])
| 165 | |
| 166 | // CreatePlan creates a new plan. |
| 167 | func (s *PlanService) CreatePlan(ctx context.Context, request *connect.Request[v1pb.CreatePlanRequest]) (*connect.Response[v1pb.Plan], error) { |
| 168 | req := request.Msg |
| 169 | user, ok := GetUserFromContext(ctx) |
| 170 | if !ok { |
| 171 | return nil, connect.NewError(connect.CodeInternal, errors.New("user not found")) |
| 172 | } |
| 173 | projectID, err := common.GetProjectID(req.Parent) |
| 174 | if err != nil { |
| 175 | return nil, connect.NewError(connect.CodeInvalidArgument, err) |
| 176 | } |
| 177 | project, err := s.store.GetProject(ctx, &store.FindProjectMessage{ |
| 178 | Workspace: common.GetWorkspaceIDFromContext(ctx), |
| 179 | ResourceID: &projectID, |
| 180 | }) |
| 181 | if err != nil { |
| 182 | return nil, connect.NewError(connect.CodeInternal, errors.Wrapf(err, "failed to get project")) |
| 183 | } |
| 184 | if project == nil { |
| 185 | return nil, connect.NewError(connect.CodeNotFound, errors.Errorf("project not found for id: %v", projectID)) |
| 186 | } |
| 187 | |
| 188 | trimmedTitle := strings.TrimSpace(req.Plan.Title) |
| 189 | if project.Setting.EnforceIssueTitle && trimmedTitle == "" { |
| 190 | return nil, connect.NewError(connect.CodeInvalidArgument, errors.Errorf("project %q requires a manual plan title (enforce_issue_title is enabled)", req.Parent)) |
| 191 | } |
| 192 | |
| 193 | // Validate plan specs |
| 194 | databaseGroup, err := validateSpecs(ctx, s.store, projectID, req.Plan.Specs) |
| 195 | if err != nil { |
| 196 | return nil, connect.NewError(connect.CodeInvalidArgument, errors.Wrapf(err, "failed to validate plan specs")) |
| 197 | } |
| 198 | |
| 199 | planMessage := &store.PlanMessage{ |
| 200 | ProjectID: projectID, |
| 201 | Name: trimmedTitle, |
| 202 | Description: req.Plan.Description, |
| 203 | Config: convertPlan(req.Plan), |
| 204 | } |
| 205 | |
| 206 | plan, err := s.store.CreatePlan(ctx, planMessage, user.Email) |
| 207 | if err != nil { |
| 208 | return nil, connect.NewError(connect.CodeInternal, errors.Wrapf(err, "failed to create plan")) |
| 209 | } |
| 210 | |
| 211 | planCheckRun, err := getPlanCheckRunFromPlan(ctx, s.store, project, plan, databaseGroup) |
| 212 | if err != nil { |
| 213 | return nil, connect.NewError(connect.CodeInternal, errors.Wrapf(err, "failed to get plan check run for plan")) |
| 214 | } |
| 215 | if planCheckRun != nil { |
| 216 | if err := s.store.CreatePlanCheckRun(ctx, planCheckRun); err != nil { |
| 217 | return nil, connect.NewError(connect.CodeInternal, errors.Wrapf(err, "failed to create plan check run")) |
| 218 | } |
| 219 | } |
| 220 | // Tickle plan check scheduler. |
| 221 | s.bus.PlanCheckTickleChan <- 0 |
| 222 | |
| 223 | convertedPlan, err := convertToPlan(ctx, s.store, plan) |
| 224 | if err != nil { |
no test coverage detected