11//
2- // Copyright 2023 The Chainloop Authors.
2+ // Copyright 2024 The Chainloop Authors.
33//
44// Licensed under the Apache License, Version 2.0 (the "License");
55// you may not use this file except in compliance with the License.
@@ -102,35 +102,94 @@ func (uc *WorkflowContractUseCase) FindByIDInOrg(ctx context.Context, orgID, con
102102 return uc .repo .FindByIDInOrg (ctx , orgUUID , contractUUID )
103103}
104104
105+ type WorkflowContractCreateOpts struct {
106+ OrgID , Name string
107+ Schema * schemav1.CraftingSchema
108+ // Make sure that the name is unique in the organization
109+ AddUniquePrefix bool
110+ }
111+
105112// we currently only support schema v1
106- func (uc * WorkflowContractUseCase ) Create (ctx context.Context , orgID , name string , schema * schemav1.CraftingSchema ) (* WorkflowContract , error ) {
107- orgUUID , err := uuid .Parse (orgID )
113+ func (uc * WorkflowContractUseCase ) Create (ctx context.Context , opts * WorkflowContractCreateOpts ) (* WorkflowContract , error ) {
114+ if opts .OrgID == "" || opts .Name == "" {
115+ return nil , NewErrValidationStr ("organization and name are required" )
116+ }
117+
118+ orgUUID , err := uuid .Parse (opts .OrgID )
108119 if err != nil {
109120 return nil , err
110121 }
111122
123+ if err := ValidateIsDNS1123 (opts .Name ); err != nil {
124+ return nil , NewErrValidation (err )
125+ }
126+
112127 // If no schema is provided we create an empty one
113- if schema == nil {
114- schema = & schemav1.CraftingSchema {
128+ if opts . Schema == nil {
129+ opts . Schema = & schemav1.CraftingSchema {
115130 SchemaVersion : "v1" ,
116131 }
132+ }
117133
118- if err := schema .ValidateAll (); err != nil {
119- return nil , err
120- }
134+ if err := opts .Schema .ValidateAll (); err != nil {
135+ return nil , err
121136 }
122137
123- rawSchema , err := proto .Marshal (schema )
138+ rawSchema , err := proto .Marshal (opts . Schema )
124139 if err != nil {
125140 return nil , err
126141 }
127142
128- opts := & ContractCreateOpts {
129- OrgID : orgUUID , Name : name ,
143+ // Create a workflow with a unique name if needed
144+ args := & ContractCreateOpts {
145+ OrgID : orgUUID , Name : opts .Name ,
130146 ContractBody : rawSchema ,
131147 }
132148
133- return uc .repo .Create (ctx , opts )
149+ var c * WorkflowContract
150+ if opts .AddUniquePrefix {
151+ c , err = uc .createWithUniqueName (ctx , args )
152+ } else {
153+ c , err = uc .repo .Create (ctx , args )
154+ }
155+
156+ if err != nil {
157+ if errors .Is (err , ErrAlreadyExists ) {
158+ return nil , NewErrValidationStr ("that name is already taken" )
159+ }
160+
161+ return nil , fmt .Errorf ("failed to create contract: %w" , err )
162+ }
163+
164+ return c , nil
165+ }
166+
167+ func (uc * WorkflowContractUseCase ) createWithUniqueName (ctx context.Context , opts * ContractCreateOpts ) (* WorkflowContract , error ) {
168+ originalName := opts .Name
169+
170+ for i := 0 ; i < RandomNameMaxTries ; i ++ {
171+ // append a suffix
172+ if i > 0 {
173+ var err error
174+ opts .Name , err = generateValidDNS1123WithSuffix (originalName )
175+ if err != nil {
176+ return nil , fmt .Errorf ("failed to generate random name: %w" , err )
177+ }
178+ }
179+
180+ c , err := uc .repo .Create (ctx , opts )
181+ if err != nil {
182+ if errors .Is (err , ErrAlreadyExists ) {
183+ continue
184+ }
185+
186+ return nil , fmt .Errorf ("failed to create contract: %w" , err )
187+ }
188+
189+ return c , nil
190+ }
191+
192+ return nil , NewErrValidationStr ("that name is already taken" )
134193}
135194
136195func (uc * WorkflowContractUseCase ) Describe (ctx context.Context , orgID , contractID string , revision int ) (* WorkflowContractWithVersion , error ) {
@@ -174,6 +233,10 @@ func (uc *WorkflowContractUseCase) Update(ctx context.Context, orgID, contractID
174233 return nil , err
175234 }
176235
236+ if err := ValidateIsDNS1123 (name ); err != nil {
237+ return nil , NewErrValidation (err )
238+ }
239+
177240 rawSchema , err := proto .Marshal (schema )
178241 if err != nil {
179242 return nil , err
@@ -182,11 +245,17 @@ func (uc *WorkflowContractUseCase) Update(ctx context.Context, orgID, contractID
182245 opts := & ContractUpdateOpts {OrgID : orgUUID , ContractID : contractUUID , ContractBody : rawSchema , Name : name }
183246
184247 c , err := uc .repo .Update (ctx , opts )
185- if c == nil {
248+ if err != nil {
249+ if errors .Is (err , ErrAlreadyExists ) {
250+ return nil , NewErrValidationStr ("that name is already taken" )
251+ }
252+
253+ return nil , fmt .Errorf ("failed to update contract: %w" , err )
254+ } else if c == nil {
186255 return nil , NewErrNotFound ("contract" )
187256 }
188257
189- return c , err
258+ return c , nil
190259}
191260
192261// Delete soft-deletes the entry
0 commit comments