Skip to content

Commit ba8b595

Browse files
authored
feat(contracts): updates by name (#674)
Signed-off-by: Miguel Martinez Trivino <[email protected]>
1 parent 8e4475b commit ba8b595

File tree

13 files changed

+237
-218
lines changed

13 files changed

+237
-218
lines changed

app/cli/cmd/workflow_contract_update.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ func newWorkflowContractUpdateCmd() *cobra.Command {
3232
if contractPath == "" && name == "" && description == "" {
3333
return errors.New("no updates provided")
3434
}
35+
3536
return nil
3637
},
3738
RunE: func(cmd *cobra.Command, args []string) error {
@@ -51,11 +52,13 @@ func newWorkflowContractUpdateCmd() *cobra.Command {
5152
}
5253

5354
cmd.Flags().StringVar(&contractID, "id", "", "contract ID")
54-
err := cmd.MarkFlagRequired("id")
55+
err := cmd.Flags().MarkDeprecated("id", "use --name instead")
5556
cobra.CheckErr(err)
5657

5758
cmd.Flags().StringVarP(&contractPath, "contract", "f", "", "path or URL to the contract schema")
5859
cmd.Flags().StringVar(&name, "name", "", "name of the contract")
60+
61+
cobra.CheckErr(err)
5962
cmd.Flags().StringVar(&description, "description", "", "description of the contract")
6063

6164
return cmd

app/controlplane/api/controlplane/v1/workflow_contract.pb.go

Lines changed: 115 additions & 95 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/controlplane/api/controlplane/v1/workflow_contract.proto

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
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.
@@ -17,8 +17,8 @@ syntax = "proto3";
1717

1818
package controlplane.v1;
1919

20-
import "controlplane/v1/response_messages.proto";
2120
import "buf/validate/validate.proto";
21+
import "controlplane/v1/response_messages.proto";
2222
import "workflowcontract/v1/crafting_schema.proto";
2323

2424
option go_package = "github.com/chainloop-dev/chainloop/app/controlplane/api/controlplane/v1;v1";
@@ -52,8 +52,36 @@ message WorkflowContractServiceCreateResponse {
5252
}
5353

5454
message WorkflowContractServiceUpdateRequest {
55-
string id = 1 [(buf.validate.field).string.uuid = true];
56-
string name = 2;
55+
// TODO: remove once the id is fully removed
56+
// Checks that either name or id is provided
57+
// NOTE: we did not use oneof in this case because we don't want to break compatibility with other clients
58+
// and we'll drop the id field in the future
59+
option (buf.validate.message).cel = {
60+
id: "either-name-or-id-required",
61+
message: "either name or id are required",
62+
expression: "has(this.id) || has(this.name)"
63+
};
64+
65+
// Maintained for backward compatibility until we mover all identifiers to name
66+
// Moving forward, the contract name will be used as the identifier
67+
string id = 1 [
68+
deprecated = true,
69+
(buf.validate.field) = {
70+
string: {uuid: true},
71+
ignore_empty: true,
72+
}
73+
];
74+
75+
// Name is the identifier of the contract
76+
string name = 2 [(buf.validate.field) = {
77+
ignore_empty: true,
78+
// NOTE: validations can not be shared yet https://github.com/bufbuild/protovalidate/issues/51
79+
cel: {
80+
message: "name must be a valid DNS-1123 subdomain",
81+
expression: "this.matches('^[a-z0-9]([-a-z0-9]*[a-z0-9])?$')",
82+
id: "name.dns-1123",
83+
},
84+
}];
5785

5886
oneof contract {
5987
workflowcontract.v1.CraftingSchema v1 = 3;

app/controlplane/api/controlplane/v1/workflow_contract_grpc.pb.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/controlplane/api/gen/frontend/controlplane/v1/workflow_contract.ts

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/controlplane/internal/biz/workflow_integration_test.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,11 @@ func (s *workflowIntegrationTestSuite) TestContractLatestAvailable() {
4444
})
4545

4646
s.Run("it will increment if the contract is updated", func() {
47-
_, err := s.WorkflowContract.Update(ctx, s.org.ID, workflow.ContractID.String(),
48-
&biz.WorkflowContractUpdateOpts{Name: "new-name", Schema: &v1.CraftingSchema{
47+
contract, err := s.WorkflowContract.FindByIDInOrg(ctx, s.org.ID, workflow.ContractID.String())
48+
require.NoError(s.T(), err)
49+
50+
_, err = s.WorkflowContract.Update(ctx, s.org.ID, contract.Name,
51+
&biz.WorkflowContractUpdateOpts{Schema: &v1.CraftingSchema{
4952
Runner: &v1.CraftingSchema_Runner{Type: v1.CraftingSchema_Runner_CIRCLECI_BUILD},
5053
}})
5154
s.NoError(err)

app/controlplane/internal/biz/workflowcontract.go

Lines changed: 6 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ type WorkflowContractRepo interface {
5656
FindByIDInOrg(ctx context.Context, orgID, ID uuid.UUID) (*WorkflowContract, error)
5757
Describe(ctx context.Context, orgID, contractID uuid.UUID, revision int) (*WorkflowContractWithVersion, error)
5858
FindVersionByID(ctx context.Context, versionID uuid.UUID) (*WorkflowContractVersion, error)
59-
Update(ctx context.Context, opts *ContractUpdateOpts) (*WorkflowContractWithVersion, error)
59+
Update(ctx context.Context, orgID uuid.UUID, name string, opts *ContractUpdateOpts) (*WorkflowContractWithVersion, error)
6060
SoftDelete(ctx context.Context, contractID uuid.UUID) error
6161
}
6262

@@ -68,10 +68,8 @@ type ContractCreateOpts struct {
6868
}
6969

7070
type ContractUpdateOpts struct {
71-
Name string
72-
OrgID, ContractID uuid.UUID
73-
Description *string
74-
ContractBody []byte
71+
Description *string
72+
ContractBody []byte
7573
}
7674

7775
type WorkflowContractUseCase struct {
@@ -233,12 +231,11 @@ func (uc *WorkflowContractUseCase) FindVersionByID(ctx context.Context, versionI
233231
}
234232

235233
type WorkflowContractUpdateOpts struct {
236-
Name string
237234
Schema *schemav1.CraftingSchema
238235
Description *string
239236
}
240237

241-
func (uc *WorkflowContractUseCase) Update(ctx context.Context, orgID, contractID string, opts *WorkflowContractUpdateOpts) (*WorkflowContractWithVersion, error) {
238+
func (uc *WorkflowContractUseCase) Update(ctx context.Context, orgID, name string, opts *WorkflowContractUpdateOpts) (*WorkflowContractWithVersion, error) {
242239
if opts == nil {
243240
return nil, NewErrValidationStr("no updates provided")
244241
}
@@ -248,30 +245,14 @@ func (uc *WorkflowContractUseCase) Update(ctx context.Context, orgID, contractID
248245
return nil, err
249246
}
250247

251-
contractUUID, err := uuid.Parse(contractID)
252-
if err != nil {
253-
return nil, err
254-
}
255-
256-
if opts.Name != "" {
257-
if err := ValidateIsDNS1123(opts.Name); err != nil {
258-
return nil, NewErrValidation(err)
259-
}
260-
}
261-
262248
rawSchema, err := proto.Marshal(opts.Schema)
263249
if err != nil {
264250
return nil, err
265251
}
266252

267-
args := &ContractUpdateOpts{OrgID: orgUUID, ContractID: contractUUID, ContractBody: rawSchema, Name: opts.Name, Description: opts.Description}
268-
269-
c, err := uc.repo.Update(ctx, args)
253+
args := &ContractUpdateOpts{ContractBody: rawSchema, Description: opts.Description}
254+
c, err := uc.repo.Update(ctx, orgUUID, name, args)
270255
if err != nil {
271-
if errors.Is(err, ErrAlreadyExists) {
272-
return nil, NewErrValidationStr("name already taken")
273-
}
274-
275256
return nil, fmt.Errorf("failed to update contract: %w", err)
276257
} else if c == nil {
277258
return nil, NewErrNotFound("contract")

app/controlplane/internal/biz/workflowcontract_integration_test.go

Lines changed: 20 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -31,82 +31,58 @@ func (s *workflowContractIntegrationTestSuite) TestUpdate() {
3131
ctx := context.Background()
3232

3333
testCases := []struct {
34-
name string
35-
OrgID, ID string
36-
input *biz.WorkflowContractUpdateOpts
37-
inputSchema *schemav1.CraftingSchema
38-
wantErrMsg string
39-
wantRevision int
40-
wantName string
41-
wantDescription string
34+
name string
35+
orgID, contractName string
36+
input *biz.WorkflowContractUpdateOpts
37+
inputSchema *schemav1.CraftingSchema
38+
wantErrMsg string
39+
wantRevision int
40+
wantDescription string
4241
}{
4342
{
4443
name: "non-updates",
4544
wantErrMsg: "no updates",
4645
},
4746
{
48-
name: "non-existing contract",
49-
wantName: "non-existing",
50-
OrgID: s.org.ID,
51-
input: &biz.WorkflowContractUpdateOpts{},
52-
ID: uuid.NewString(),
53-
wantErrMsg: "not found",
54-
},
55-
{
56-
name: "existing contract invalid name",
57-
input: &biz.WorkflowContractUpdateOpts{Name: "invalid name"},
58-
OrgID: s.org.ID,
59-
ID: s.contractOrg1.ID.String(),
60-
wantErrMsg: "RFC 1123",
61-
},
62-
{
63-
name: "existing contract valid name, does not bump revision",
64-
input: &biz.WorkflowContractUpdateOpts{Name: "valid-name"},
65-
wantName: "valid-name",
66-
OrgID: s.org.ID,
67-
ID: s.contractOrg1.ID.String(),
68-
wantRevision: 1,
47+
name: "non-existing contract",
48+
orgID: s.org.ID,
49+
input: &biz.WorkflowContractUpdateOpts{},
50+
contractName: uuid.NewString(),
51+
wantErrMsg: "not found",
6952
},
7053
{
7154
name: "updating schema bumps revision",
72-
OrgID: s.org.ID,
73-
ID: s.contractOrg1.ID.String(),
74-
wantName: "valid-name",
55+
orgID: s.org.ID,
56+
contractName: s.contractOrg1.Name,
7557
input: &biz.WorkflowContractUpdateOpts{Schema: &schemav1.CraftingSchema{SchemaVersion: "v123"}},
7658
wantRevision: 2,
7759
},
7860
{
7961
name: "updating with same schema DOES NOT bump revision",
80-
OrgID: s.org.ID,
81-
ID: s.contractOrg1.ID.String(),
62+
orgID: s.org.ID,
63+
contractName: s.contractOrg1.Name,
8264
input: &biz.WorkflowContractUpdateOpts{Schema: &schemav1.CraftingSchema{SchemaVersion: "v123"}},
83-
wantName: "valid-name",
8465
wantRevision: 2,
8566
},
8667
{
87-
name: "you can update the description",
88-
OrgID: s.org.ID,
89-
ID: s.contractOrg1.ID.String(),
68+
name: "updating description bumps revision",
69+
orgID: s.org.ID,
70+
contractName: s.contractOrg1.Name,
9071
input: &biz.WorkflowContractUpdateOpts{Description: toPtrS("new description")},
91-
wantName: "valid-name",
9272
wantDescription: "new description",
9373
wantRevision: 2,
9474
},
9575
}
9676

9777
for _, tc := range testCases {
9878
s.Run(tc.name, func() {
99-
contract, err := s.WorkflowContract.Update(ctx, tc.OrgID, tc.ID, tc.input)
79+
contract, err := s.WorkflowContract.Update(ctx, tc.orgID, tc.contractName, tc.input)
10080
if tc.wantErrMsg != "" {
10181
s.ErrorContains(err, tc.wantErrMsg)
10282
return
10383
}
10484
require.NoError(s.T(), err)
10585

106-
if tc.wantName != "" {
107-
s.Equal(tc.wantName, contract.Contract.Name)
108-
}
109-
11086
if tc.wantDescription != "" {
11187
s.Equal(tc.wantDescription, contract.Contract.Description)
11288
}

app/controlplane/internal/biz/workflowrun_integration_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -269,8 +269,8 @@ func (s *workflowRunIntegrationTestSuite) TestContractInformation() {
269269
})
270270

271271
s.Run("if the contract gets a new revision but it's not used, it shows spread", func() {
272-
updatedContractRevision, err := s.WorkflowContract.Update(ctx, s.org.ID, s.contractVersion.Contract.ID.String(),
273-
&biz.WorkflowContractUpdateOpts{Name: "new-name", Schema: &schemav1.CraftingSchema{
272+
updatedContractRevision, err := s.WorkflowContract.Update(ctx, s.org.ID, s.contractVersion.Contract.Name,
273+
&biz.WorkflowContractUpdateOpts{Schema: &schemav1.CraftingSchema{
274274
Runner: &schemav1.CraftingSchema_Runner{Type: schemav1.CraftingSchema_Runner_CIRCLECI_BUILD},
275275
}})
276276
s.NoError(err)

app/controlplane/internal/data/ent/schema/workflowcontract.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ type WorkflowContract struct {
3535
func (WorkflowContract) Fields() []ent.Field {
3636
return []ent.Field{
3737
field.UUID("id", uuid.UUID{}).Default(uuid.New).Unique(),
38-
field.String("name"),
38+
field.String("name").Immutable(),
3939
field.Time("created_at").
4040
Default(time.Now).
4141
Immutable().

0 commit comments

Comments
 (0)