@@ -252,6 +252,51 @@ func WithRedactedFiles(paths []string) BlueprintBodyOption {
252252 }
253253}
254254
255+ // validateBlueprintRequest performs common validation for blueprint requests
256+ // used by both CreateBlueprint and UpdateBlueprint handlers
257+ func validateBlueprintRequest (ctx echo.Context , blueprintRequest * CreateBlueprintRequest , existingUsers []User ) error {
258+ // Validate blueprint name
259+ if ! blueprintNameRegex .MatchString (blueprintRequest .Name ) {
260+ return ctx .JSON (http .StatusUnprocessableEntity , HTTPErrorList {
261+ Errors : []HTTPError {{
262+ Title : "Invalid blueprint name" ,
263+ Detail : blueprintInvalidNameDetail ,
264+ }},
265+ })
266+ }
267+
268+ // Validate users if present
269+ users := blueprintRequest .Customizations .Users
270+ if users != nil {
271+ for i , user := range * users {
272+ // For update operations, merge with existing users first
273+ if existingUsers != nil {
274+ err := (* users )[i ].MergeForUpdate (existingUsers )
275+ if err != nil {
276+ return ctx .JSON (http .StatusUnprocessableEntity , HTTPErrorList {
277+ Errors : []HTTPError {{
278+ Title : "Invalid user" ,
279+ Detail : err .Error (),
280+ }},
281+ })
282+ }
283+ } else {
284+ // For create operations, validate directly
285+ if err := user .Valid (); err != nil {
286+ return ctx .JSON (http .StatusUnprocessableEntity , HTTPErrorList {
287+ Errors : []HTTPError {{
288+ Title : "Invalid user" ,
289+ Detail : err .Error (),
290+ }},
291+ })
292+ }
293+ }
294+ }
295+ }
296+
297+ return nil
298+ }
299+
255300func (h * Handlers ) CreateBlueprint (ctx echo.Context ) error {
256301 userID , err := h .server .getIdentity (ctx )
257302 if err != nil {
@@ -272,13 +317,9 @@ func (h *Handlers) CreateBlueprint(ctx echo.Context) error {
272317 }
273318 }
274319
275- if ! blueprintNameRegex .MatchString (blueprintRequest .Name ) {
276- return ctx .JSON (http .StatusUnprocessableEntity , HTTPErrorList {
277- Errors : []HTTPError {{
278- Title : "Invalid blueprint name" ,
279- Detail : blueprintInvalidNameDetail ,
280- }},
281- })
320+ // Validate blueprint request (name and users)
321+ if err := validateBlueprintRequest (ctx , & blueprintRequest , nil ); err != nil {
322+ return err
282323 }
283324
284325 id := uuid .New ()
@@ -294,21 +335,6 @@ func (h *Handlers) CreateBlueprint(ctx echo.Context) error {
294335 desc = * blueprintRequest .Description
295336 }
296337
297- users := blueprintRequest .Customizations .Users
298- if users != nil {
299- for _ , user := range * users {
300- // Make sure every user has either ssh key or password set
301- if err := user .Valid (); err != nil {
302- return ctx .JSON (http .StatusUnprocessableEntity , HTTPErrorList {
303- Errors : []HTTPError {{
304- Title : "Invalid user" ,
305- Detail : err .Error (),
306- }},
307- })
308- }
309- }
310- }
311-
312338 blueprint , err := BlueprintFromAPI (blueprintRequest )
313339 if err != nil {
314340 return err
@@ -526,15 +552,8 @@ func (h *Handlers) UpdateBlueprint(ctx echo.Context, blueprintId uuid.UUID) erro
526552 return err
527553 }
528554
529- if ! blueprintNameRegex .MatchString (blueprintRequest .Name ) {
530- return ctx .JSON (http .StatusUnprocessableEntity , HTTPErrorList {
531- Errors : []HTTPError {{
532- Title : "Invalid blueprint name" ,
533- Detail : blueprintInvalidNameDetail ,
534- }},
535- })
536- }
537-
555+ // Get existing blueprint for user validation if users are present
556+ var existingUsers []User
538557 if blueprintRequest .Customizations .Users != nil {
539558 be , err := h .server .db .GetBlueprint (ctx .Request ().Context (), blueprintId , userID .OrgID (), nil )
540559 if err != nil {
@@ -549,20 +568,15 @@ func (h *Handlers) UpdateBlueprint(ctx echo.Context, blueprintId uuid.UUID) erro
549568 }
550569
551570 if eb .Customizations .Users != nil {
552- for i := range * blueprintRequest .Customizations .Users {
553- err := (* blueprintRequest .Customizations .Users )[i ].MergeForUpdate (* eb .Customizations .Users )
554- if err != nil {
555- return ctx .JSON (http .StatusUnprocessableEntity , HTTPErrorList {
556- Errors : []HTTPError {{
557- Title : "Invalid user" ,
558- Detail : err .Error (),
559- }},
560- })
561- }
562- }
571+ existingUsers = * eb .Customizations .Users
563572 }
564573 }
565574
575+ // Validate blueprint request (name and users with merge logic for updates)
576+ if err := validateBlueprintRequest (ctx , & blueprintRequest , existingUsers ); err != nil {
577+ return err
578+ }
579+
566580 blueprint , err := BlueprintFromAPI (blueprintRequest )
567581 if err != nil {
568582 return ctx .JSON (http .StatusUnprocessableEntity , HTTPErrorList {
0 commit comments