diff --git a/app/controlplane/api/controlplane/v1/response_messages.pb.go b/app/controlplane/api/controlplane/v1/response_messages.pb.go index 0fff67449..33a2e4477 100644 --- a/app/controlplane/api/controlplane/v1/response_messages.pb.go +++ b/app/controlplane/api/controlplane/v1/response_messages.pb.go @@ -1358,7 +1358,9 @@ type WorkflowContractItem struct { WorkflowNames []string `protobuf:"bytes,5,rep,name=workflow_names,json=workflowNames,proto3" json:"workflow_names,omitempty"` WorkflowRefs []*WorkflowRef `protobuf:"bytes,7,rep,name=workflow_refs,json=workflowRefs,proto3" json:"workflow_refs,omitempty"` // wether the contract is scoped to an entity in the organization - ScopedEntity *ScopedEntity `protobuf:"bytes,9,opt,name=scoped_entity,json=scopedEntity,proto3" json:"scoped_entity,omitempty"` + ScopedEntity *ScopedEntity `protobuf:"bytes,9,opt,name=scoped_entity,json=scopedEntity,proto3" json:"scoped_entity,omitempty"` + // Whether this contract is provisioned and operated by Chainloop + IsManaged bool `protobuf:"varint,11,opt,name=is_managed,json=isManaged,proto3" json:"is_managed,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -1465,6 +1467,13 @@ func (x *WorkflowContractItem) GetScopedEntity() *ScopedEntity { return nil } +func (x *WorkflowContractItem) GetIsManaged() bool { + if x != nil { + return x.IsManaged + } + return false +} + type ScopedEntity struct { state protoimpl.MessageState `protogen:"open.v1"` // Type is the type of the scoped entity i.e project or org @@ -2771,7 +2780,7 @@ const file_controlplane_v1_response_messages_proto_rawDesc = "" + "\x03uri\x18\x04 \x01(\tR\x03uri\x1a9\n" + "\vDigestEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\x8a\x04\n" + + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\xa9\x04\n" + "\x14WorkflowContractItem\x12\x0e\n" + "\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n" + "\x04name\x18\x02 \x01(\tR\x04name\x12$\n" + @@ -2785,7 +2794,9 @@ const file_controlplane_v1_response_messages_proto_rawDesc = "" + "\x1alatest_revision_created_at\x18\b \x01(\v2\x1a.google.protobuf.TimestampR\x17latestRevisionCreatedAt\x12)\n" + "\x0eworkflow_names\x18\x05 \x03(\tB\x02\x18\x01R\rworkflowNames\x12A\n" + "\rworkflow_refs\x18\a \x03(\v2\x1c.controlplane.v1.WorkflowRefR\fworkflowRefs\x12B\n" + - "\rscoped_entity\x18\t \x01(\v2\x1d.controlplane.v1.ScopedEntityR\fscopedEntity\"F\n" + + "\rscoped_entity\x18\t \x01(\v2\x1d.controlplane.v1.ScopedEntityR\fscopedEntity\x12\x1d\n" + + "\n" + + "is_managed\x18\v \x01(\bR\tisManaged\"F\n" + "\fScopedEntity\x12\x12\n" + "\x04type\x18\x01 \x01(\tR\x04type\x12\x0e\n" + "\x02id\x18\x02 \x01(\tR\x02id\x12\x12\n" + diff --git a/app/controlplane/api/controlplane/v1/response_messages.proto b/app/controlplane/api/controlplane/v1/response_messages.proto index dc3e71779..7c5a649b2 100644 --- a/app/controlplane/api/controlplane/v1/response_messages.proto +++ b/app/controlplane/api/controlplane/v1/response_messages.proto @@ -195,6 +195,8 @@ message WorkflowContractItem { repeated WorkflowRef workflow_refs = 7; // wether the contract is scoped to an entity in the organization ScopedEntity scoped_entity = 9; + // Whether this contract is provisioned and operated by Chainloop + bool is_managed = 11; } message ScopedEntity { diff --git a/app/controlplane/api/gen/frontend/controlplane/v1/response_messages.ts b/app/controlplane/api/gen/frontend/controlplane/v1/response_messages.ts index 51e86aa40..237a71756 100644 --- a/app/controlplane/api/gen/frontend/controlplane/v1/response_messages.ts +++ b/app/controlplane/api/gen/frontend/controlplane/v1/response_messages.ts @@ -498,6 +498,8 @@ export interface WorkflowContractItem { workflowRefs: WorkflowRef[]; /** wether the contract is scoped to an entity in the organization */ scopedEntity?: ScopedEntity; + /** Whether this contract is provisioned and operated by Chainloop */ + isManaged: boolean; } export interface ScopedEntity { @@ -2974,6 +2976,7 @@ function createBaseWorkflowContractItem(): WorkflowContractItem { workflowNames: [], workflowRefs: [], scopedEntity: undefined, + isManaged: false, }; } @@ -3009,6 +3012,9 @@ export const WorkflowContractItem = { if (message.scopedEntity !== undefined) { ScopedEntity.encode(message.scopedEntity, writer.uint32(74).fork()).ldelim(); } + if (message.isManaged === true) { + writer.uint32(88).bool(message.isManaged); + } return writer; }, @@ -3089,6 +3095,13 @@ export const WorkflowContractItem = { message.scopedEntity = ScopedEntity.decode(reader, reader.uint32()); continue; + case 11: + if (tag !== 88) { + break; + } + + message.isManaged = reader.bool(); + continue; } if ((tag & 7) === 4 || tag === 0) { break; @@ -3114,6 +3127,7 @@ export const WorkflowContractItem = { ? object.workflowRefs.map((e: any) => WorkflowRef.fromJSON(e)) : [], scopedEntity: isSet(object.scopedEntity) ? ScopedEntity.fromJSON(object.scopedEntity) : undefined, + isManaged: isSet(object.isManaged) ? Boolean(object.isManaged) : false, }; }, @@ -3139,6 +3153,7 @@ export const WorkflowContractItem = { } message.scopedEntity !== undefined && (obj.scopedEntity = message.scopedEntity ? ScopedEntity.toJSON(message.scopedEntity) : undefined); + message.isManaged !== undefined && (obj.isManaged = message.isManaged); return obj; }, @@ -3160,6 +3175,7 @@ export const WorkflowContractItem = { message.scopedEntity = (object.scopedEntity !== undefined && object.scopedEntity !== null) ? ScopedEntity.fromPartial(object.scopedEntity) : undefined; + message.isManaged = object.isManaged ?? false; return message; }, }; diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.WorkflowContractItem.jsonschema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.WorkflowContractItem.jsonschema.json index 3041d3023..f7c88554f 100644 --- a/app/controlplane/api/gen/jsonschema/controlplane.v1.WorkflowContractItem.jsonschema.json +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.WorkflowContractItem.jsonschema.json @@ -6,6 +6,10 @@ "^(created_at)$": { "$ref": "google.protobuf.Timestamp.jsonschema.json" }, + "^(is_managed)$": { + "description": "Whether this contract is provisioned and operated by Chainloop", + "type": "boolean" + }, "^(latest_revision)$": { "maximum": 2147483647, "minimum": -2147483648, @@ -46,6 +50,10 @@ "id": { "type": "string" }, + "isManaged": { + "description": "Whether this contract is provisioned and operated by Chainloop", + "type": "boolean" + }, "latestRevision": { "maximum": 2147483647, "minimum": -2147483648, diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.WorkflowContractItem.schema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.WorkflowContractItem.schema.json index a781d0d79..4ee3161b6 100644 --- a/app/controlplane/api/gen/jsonschema/controlplane.v1.WorkflowContractItem.schema.json +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.WorkflowContractItem.schema.json @@ -6,6 +6,10 @@ "^(createdAt)$": { "$ref": "google.protobuf.Timestamp.schema.json" }, + "^(isManaged)$": { + "description": "Whether this contract is provisioned and operated by Chainloop", + "type": "boolean" + }, "^(latestRevision)$": { "maximum": 2147483647, "minimum": -2147483648, @@ -46,6 +50,10 @@ "id": { "type": "string" }, + "is_managed": { + "description": "Whether this contract is provisioned and operated by Chainloop", + "type": "boolean" + }, "latest_revision": { "maximum": 2147483647, "minimum": -2147483648, diff --git a/app/controlplane/internal/service/workflowcontract.go b/app/controlplane/internal/service/workflowcontract.go index b0f9f9596..71a7f5cdd 100644 --- a/app/controlplane/internal/service/workflowcontract.go +++ b/app/controlplane/internal/service/workflowcontract.go @@ -349,6 +349,7 @@ func bizWorkFlowContractToPb(schema *biz.WorkflowContract) *pb.WorkflowContractI LatestRevisionCreatedAt: timestamppb.New(*schema.LatestRevisionCreatedAt), WorkflowNames: workflowNames, WorkflowRefs: workflowRefs, + IsManaged: schema.Managed, //nolint:staticcheck Description: schema.Description, } diff --git a/app/controlplane/pkg/biz/workflowcontract.go b/app/controlplane/pkg/biz/workflowcontract.go index 0615678a9..ee5bde278 100644 --- a/app/controlplane/pkg/biz/workflowcontract.go +++ b/app/controlplane/pkg/biz/workflowcontract.go @@ -47,6 +47,8 @@ type WorkflowContract struct { WorkflowRefs []*WorkflowRef // entity the contract is scoped to, if not set it's scoped to the organization ScopedEntity *ScopedEntity + // Managed indicates this contract is provisioned and operated by Chainloop + Managed bool } type ScopedEntity struct { @@ -128,6 +130,8 @@ type ContractCreateOpts struct { Contract *Contract // ProjectID indicates the project to be scoped to ProjectID *uuid.UUID + // Managed indicates the contract is provisioned and operated by Chainloop + Managed *bool } type ContractUpdateOpts struct { @@ -408,6 +412,11 @@ func (uc *WorkflowContractUseCase) Update(ctx context.Context, orgID, name strin return nil, fmt.Errorf("failed to find contract %s in org %s: %w", name, orgUUID, err) } + // Managed contracts are owned and operated by Chainloop and cannot be modified by users. + if wfContractPreUpdate.Managed { + return nil, NewErrValidationStr("managed contracts cannot be modified") + } + args := &ContractUpdateOpts{Description: opts.Description, Contract: contract} c, err := uc.repo.Update(ctx, orgUUID, name, args) if err != nil { @@ -657,6 +666,11 @@ func (uc *WorkflowContractUseCase) Delete(ctx context.Context, orgID, contractID return NewErrValidation(errors.New("there are associated workflows with this contract, delete them first")) } + // Prevent deletion of managed contracts - they are owned and operated by Chainloop + if contract.Managed { + return NewErrValidation(errors.New("can't delete a managed contract")) + } + // Check that the workflow to delete belongs to the provided organization if err := uc.repo.SoftDelete(ctx, contractUUID); err != nil { return fmt.Errorf("failed to delete contract: %w", err) diff --git a/app/controlplane/pkg/data/ent/migrate/migrations/20260512094407.sql b/app/controlplane/pkg/data/ent/migrate/migrations/20260512094407.sql new file mode 100644 index 000000000..4a58d7c30 --- /dev/null +++ b/app/controlplane/pkg/data/ent/migrate/migrations/20260512094407.sql @@ -0,0 +1,2 @@ +-- Modify "workflow_contracts" table +ALTER TABLE "workflow_contracts" ADD COLUMN "managed" boolean NOT NULL DEFAULT false; diff --git a/app/controlplane/pkg/data/ent/migrate/migrations/atlas.sum b/app/controlplane/pkg/data/ent/migrate/migrations/atlas.sum index 7a9b904e4..e230a4d8c 100644 --- a/app/controlplane/pkg/data/ent/migrate/migrations/atlas.sum +++ b/app/controlplane/pkg/data/ent/migrate/migrations/atlas.sum @@ -1,4 +1,4 @@ -h1:POeLVZ5wn0luHTIuCxBdUZBNVPEq0gfgfoaNFgQzR4g= +h1:yhiwazV5IpUmc03WN2gsiovl7haggXBd1HJ6M2DawkM= 20230706165452_init-schema.sql h1:VvqbNFEQnCvUVyj2iDYVQQxDM0+sSXqocpt/5H64k8M= 20230710111950-cas-backend.sql h1:A8iBuSzZIEbdsv9ipBtscZQuaBp3V5/VMw7eZH6GX+g= 20230712094107-cas-backends-workflow-runs.sql h1:a5rzxpVGyd56nLRSsKrmCFc9sebg65RWzLghKHh5xvI= @@ -127,3 +127,4 @@ h1:POeLVZ5wn0luHTIuCxBdUZBNVPEq0gfgfoaNFgQzR4g= 20260211225609.sql h1:DTkyg3oZSV99uPGl+vOuK9FSlEumXwoYCgchUhsg/P4= 20260303120000.sql h1:msXy2MRkzMOGxWbG1NOHh+PN5qjaBZcRzVT+7SFIwaA= 20260318160301.sql h1:kH88s6pOi7Vprydb7xrzgY55JhMxfzY32txpQ8a1wEE= +20260512094407.sql h1:y5MoNBye22Xnspdx76+zdvzJHLXddCzqYZHcj2rtIoY= diff --git a/app/controlplane/pkg/data/ent/migrate/schema.go b/app/controlplane/pkg/data/ent/migrate/schema.go index 3e59365a6..aac7f7c68 100644 --- a/app/controlplane/pkg/data/ent/migrate/schema.go +++ b/app/controlplane/pkg/data/ent/migrate/schema.go @@ -693,6 +693,7 @@ var ( {Name: "description", Type: field.TypeString, Nullable: true}, {Name: "scoped_resource_type", Type: field.TypeEnum, Nullable: true, Enums: []string{"project", "org"}}, {Name: "scoped_resource_id", Type: field.TypeUUID, Nullable: true}, + {Name: "managed", Type: field.TypeBool, Default: false}, {Name: "organization_workflow_contracts", Type: field.TypeUUID, Nullable: true}, } // WorkflowContractsTable holds the schema information for the "workflow_contracts" table. @@ -703,7 +704,7 @@ var ( ForeignKeys: []*schema.ForeignKey{ { Symbol: "workflow_contracts_organizations_workflow_contracts", - Columns: []*schema.Column{WorkflowContractsColumns[8]}, + Columns: []*schema.Column{WorkflowContractsColumns[9]}, RefColumns: []*schema.Column{OrganizationsColumns[0]}, OnDelete: schema.Cascade, }, @@ -712,7 +713,7 @@ var ( { Name: "workflowcontract_name_organization_workflow_contracts", Unique: true, - Columns: []*schema.Column{WorkflowContractsColumns[1], WorkflowContractsColumns[8]}, + Columns: []*schema.Column{WorkflowContractsColumns[1], WorkflowContractsColumns[9]}, Annotation: &entsql.IndexAnnotation{ Where: "deleted_at IS NULL", }, diff --git a/app/controlplane/pkg/data/ent/mutation.go b/app/controlplane/pkg/data/ent/mutation.go index 6bb19f4d0..929df4193 100644 --- a/app/controlplane/pkg/data/ent/mutation.go +++ b/app/controlplane/pkg/data/ent/mutation.go @@ -16222,6 +16222,7 @@ type WorkflowContractMutation struct { description *string scoped_resource_type *biz.ContractScope scoped_resource_id *uuid.UUID + managed *bool clearedFields map[string]struct{} versions map[uuid.UUID]struct{} removedversions map[uuid.UUID]struct{} @@ -16644,6 +16645,42 @@ func (m *WorkflowContractMutation) ResetScopedResourceID() { delete(m.clearedFields, workflowcontract.FieldScopedResourceID) } +// SetManaged sets the "managed" field. +func (m *WorkflowContractMutation) SetManaged(b bool) { + m.managed = &b +} + +// Managed returns the value of the "managed" field in the mutation. +func (m *WorkflowContractMutation) Managed() (r bool, exists bool) { + v := m.managed + if v == nil { + return + } + return *v, true +} + +// OldManaged returns the old "managed" field's value of the WorkflowContract entity. +// If the WorkflowContract object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *WorkflowContractMutation) OldManaged(ctx context.Context) (v bool, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldManaged is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldManaged requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldManaged: %w", err) + } + return oldValue.Managed, nil +} + +// ResetManaged resets all changes to the "managed" field. +func (m *WorkflowContractMutation) ResetManaged() { + m.managed = nil +} + // AddVersionIDs adds the "versions" edge to the WorkflowContractVersion entity by ids. func (m *WorkflowContractMutation) AddVersionIDs(ids ...uuid.UUID) { if m.versions == nil { @@ -16825,7 +16862,7 @@ func (m *WorkflowContractMutation) Type() string { // order to get all numeric fields that were incremented/decremented, call // AddedFields(). func (m *WorkflowContractMutation) Fields() []string { - fields := make([]string, 0, 7) + fields := make([]string, 0, 8) if m.name != nil { fields = append(fields, workflowcontract.FieldName) } @@ -16847,6 +16884,9 @@ func (m *WorkflowContractMutation) Fields() []string { if m.scoped_resource_id != nil { fields = append(fields, workflowcontract.FieldScopedResourceID) } + if m.managed != nil { + fields = append(fields, workflowcontract.FieldManaged) + } return fields } @@ -16869,6 +16909,8 @@ func (m *WorkflowContractMutation) Field(name string) (ent.Value, bool) { return m.ScopedResourceType() case workflowcontract.FieldScopedResourceID: return m.ScopedResourceID() + case workflowcontract.FieldManaged: + return m.Managed() } return nil, false } @@ -16892,6 +16934,8 @@ func (m *WorkflowContractMutation) OldField(ctx context.Context, name string) (e return m.OldScopedResourceType(ctx) case workflowcontract.FieldScopedResourceID: return m.OldScopedResourceID(ctx) + case workflowcontract.FieldManaged: + return m.OldManaged(ctx) } return nil, fmt.Errorf("unknown WorkflowContract field %s", name) } @@ -16950,6 +16994,13 @@ func (m *WorkflowContractMutation) SetField(name string, value ent.Value) error } m.SetScopedResourceID(v) return nil + case workflowcontract.FieldManaged: + v, ok := value.(bool) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetManaged(v) + return nil } return fmt.Errorf("unknown WorkflowContract field %s", name) } @@ -17047,6 +17098,9 @@ func (m *WorkflowContractMutation) ResetField(name string) error { case workflowcontract.FieldScopedResourceID: m.ResetScopedResourceID() return nil + case workflowcontract.FieldManaged: + m.ResetManaged() + return nil } return fmt.Errorf("unknown WorkflowContract field %s", name) } diff --git a/app/controlplane/pkg/data/ent/runtime.go b/app/controlplane/pkg/data/ent/runtime.go index adb88e48d..7bb40d01e 100644 --- a/app/controlplane/pkg/data/ent/runtime.go +++ b/app/controlplane/pkg/data/ent/runtime.go @@ -341,6 +341,10 @@ func init() { workflowcontractDescUpdatedAt := workflowcontractFields[3].Descriptor() // workflowcontract.DefaultUpdatedAt holds the default value on creation for the updated_at field. workflowcontract.DefaultUpdatedAt = workflowcontractDescUpdatedAt.Default.(func() time.Time) + // workflowcontractDescManaged is the schema descriptor for managed field. + workflowcontractDescManaged := workflowcontractFields[8].Descriptor() + // workflowcontract.DefaultManaged holds the default value on creation for the managed field. + workflowcontract.DefaultManaged = workflowcontractDescManaged.Default.(bool) // workflowcontractDescID is the schema descriptor for id field. workflowcontractDescID := workflowcontractFields[0].Descriptor() // workflowcontract.DefaultID holds the default value on creation for the id field. diff --git a/app/controlplane/pkg/data/ent/schema/workflowcontract.go b/app/controlplane/pkg/data/ent/schema/workflowcontract.go index ea3c2d77d..9f398c544 100644 --- a/app/controlplane/pkg/data/ent/schema/workflowcontract.go +++ b/app/controlplane/pkg/data/ent/schema/workflowcontract.go @@ -1,5 +1,5 @@ // -// Copyright 2024-2025 The Chainloop Authors. +// Copyright 2024-2026 The Chainloop Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -53,6 +53,8 @@ func (WorkflowContract) Fields() []ent.Field { // If this value is set, the contract is scoped to a resource field.Enum("scoped_resource_type").GoType(biz.ContractScope("")).Optional(), field.UUID("scoped_resource_id", uuid.UUID{}).Optional(), + // managed indicates this contract is provisioned and operated by Chainloop + field.Bool("managed").Default(false), } } diff --git a/app/controlplane/pkg/data/ent/workflowcontract.go b/app/controlplane/pkg/data/ent/workflowcontract.go index 078c8cac4..ca6638d6f 100644 --- a/app/controlplane/pkg/data/ent/workflowcontract.go +++ b/app/controlplane/pkg/data/ent/workflowcontract.go @@ -34,6 +34,8 @@ type WorkflowContract struct { ScopedResourceType biz.ContractScope `json:"scoped_resource_type,omitempty"` // ScopedResourceID holds the value of the "scoped_resource_id" field. ScopedResourceID uuid.UUID `json:"scoped_resource_id,omitempty"` + // Managed holds the value of the "managed" field. + Managed bool `json:"managed,omitempty"` // Edges holds the relations/edges for other nodes in the graph. // The values are being populated by the WorkflowContractQuery when eager-loading is set. Edges WorkflowContractEdges `json:"edges"` @@ -88,6 +90,8 @@ func (*WorkflowContract) scanValues(columns []string) ([]any, error) { values := make([]any, len(columns)) for i := range columns { switch columns[i] { + case workflowcontract.FieldManaged: + values[i] = new(sql.NullBool) case workflowcontract.FieldName, workflowcontract.FieldDescription, workflowcontract.FieldScopedResourceType: values[i] = new(sql.NullString) case workflowcontract.FieldCreatedAt, workflowcontract.FieldUpdatedAt, workflowcontract.FieldDeletedAt: @@ -159,6 +163,12 @@ func (_m *WorkflowContract) assignValues(columns []string, values []any) error { } else if value != nil { _m.ScopedResourceID = *value } + case workflowcontract.FieldManaged: + if value, ok := values[i].(*sql.NullBool); !ok { + return fmt.Errorf("unexpected type %T for field managed", values[i]) + } else if value.Valid { + _m.Managed = value.Bool + } case workflowcontract.ForeignKeys[0]: if value, ok := values[i].(*sql.NullScanner); !ok { return fmt.Errorf("unexpected type %T for field organization_workflow_contracts", values[i]) @@ -237,6 +247,9 @@ func (_m *WorkflowContract) String() string { builder.WriteString(", ") builder.WriteString("scoped_resource_id=") builder.WriteString(fmt.Sprintf("%v", _m.ScopedResourceID)) + builder.WriteString(", ") + builder.WriteString("managed=") + builder.WriteString(fmt.Sprintf("%v", _m.Managed)) builder.WriteByte(')') return builder.String() } diff --git a/app/controlplane/pkg/data/ent/workflowcontract/where.go b/app/controlplane/pkg/data/ent/workflowcontract/where.go index 3349309a0..ca420800c 100644 --- a/app/controlplane/pkg/data/ent/workflowcontract/where.go +++ b/app/controlplane/pkg/data/ent/workflowcontract/where.go @@ -87,6 +87,11 @@ func ScopedResourceID(v uuid.UUID) predicate.WorkflowContract { return predicate.WorkflowContract(sql.FieldEQ(FieldScopedResourceID, v)) } +// Managed applies equality check predicate on the "managed" field. It's identical to ManagedEQ. +func Managed(v bool) predicate.WorkflowContract { + return predicate.WorkflowContract(sql.FieldEQ(FieldManaged, v)) +} + // NameEQ applies the EQ predicate on the "name" field. func NameEQ(v string) predicate.WorkflowContract { return predicate.WorkflowContract(sql.FieldEQ(FieldName, v)) @@ -447,6 +452,16 @@ func ScopedResourceIDNotNil() predicate.WorkflowContract { return predicate.WorkflowContract(sql.FieldNotNull(FieldScopedResourceID)) } +// ManagedEQ applies the EQ predicate on the "managed" field. +func ManagedEQ(v bool) predicate.WorkflowContract { + return predicate.WorkflowContract(sql.FieldEQ(FieldManaged, v)) +} + +// ManagedNEQ applies the NEQ predicate on the "managed" field. +func ManagedNEQ(v bool) predicate.WorkflowContract { + return predicate.WorkflowContract(sql.FieldNEQ(FieldManaged, v)) +} + // HasVersions applies the HasEdge predicate on the "versions" edge. func HasVersions() predicate.WorkflowContract { return predicate.WorkflowContract(func(s *sql.Selector) { diff --git a/app/controlplane/pkg/data/ent/workflowcontract/workflowcontract.go b/app/controlplane/pkg/data/ent/workflowcontract/workflowcontract.go index 5ba08a3ae..cab6a30b1 100644 --- a/app/controlplane/pkg/data/ent/workflowcontract/workflowcontract.go +++ b/app/controlplane/pkg/data/ent/workflowcontract/workflowcontract.go @@ -31,6 +31,8 @@ const ( FieldScopedResourceType = "scoped_resource_type" // FieldScopedResourceID holds the string denoting the scoped_resource_id field in the database. FieldScopedResourceID = "scoped_resource_id" + // FieldManaged holds the string denoting the managed field in the database. + FieldManaged = "managed" // EdgeVersions holds the string denoting the versions edge name in mutations. EdgeVersions = "versions" // EdgeOrganization holds the string denoting the organization edge name in mutations. @@ -72,6 +74,7 @@ var Columns = []string{ FieldDescription, FieldScopedResourceType, FieldScopedResourceID, + FieldManaged, } // ForeignKeys holds the SQL foreign-keys that are owned by the "workflow_contracts" @@ -100,6 +103,8 @@ var ( DefaultCreatedAt func() time.Time // DefaultUpdatedAt holds the default value on creation for the "updated_at" field. DefaultUpdatedAt func() time.Time + // DefaultManaged holds the default value on creation for the "managed" field. + DefaultManaged bool // DefaultID holds the default value on creation for the "id" field. DefaultID func() uuid.UUID ) @@ -157,6 +162,11 @@ func ByScopedResourceID(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldScopedResourceID, opts...).ToFunc() } +// ByManaged orders the results by the managed field. +func ByManaged(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldManaged, opts...).ToFunc() +} + // ByVersionsCount orders the results by versions count. func ByVersionsCount(opts ...sql.OrderTermOption) OrderOption { return func(s *sql.Selector) { diff --git a/app/controlplane/pkg/data/ent/workflowcontract_create.go b/app/controlplane/pkg/data/ent/workflowcontract_create.go index 60bbe77ee..f1e143bf2 100644 --- a/app/controlplane/pkg/data/ent/workflowcontract_create.go +++ b/app/controlplane/pkg/data/ent/workflowcontract_create.go @@ -118,6 +118,20 @@ func (_c *WorkflowContractCreate) SetNillableScopedResourceID(v *uuid.UUID) *Wor return _c } +// SetManaged sets the "managed" field. +func (_c *WorkflowContractCreate) SetManaged(v bool) *WorkflowContractCreate { + _c.mutation.SetManaged(v) + return _c +} + +// SetNillableManaged sets the "managed" field if the given value is not nil. +func (_c *WorkflowContractCreate) SetNillableManaged(v *bool) *WorkflowContractCreate { + if v != nil { + _c.SetManaged(*v) + } + return _c +} + // SetID sets the "id" field. func (_c *WorkflowContractCreate) SetID(v uuid.UUID) *WorkflowContractCreate { _c.mutation.SetID(v) @@ -224,6 +238,10 @@ func (_c *WorkflowContractCreate) defaults() { v := workflowcontract.DefaultUpdatedAt() _c.mutation.SetUpdatedAt(v) } + if _, ok := _c.mutation.Managed(); !ok { + v := workflowcontract.DefaultManaged + _c.mutation.SetManaged(v) + } if _, ok := _c.mutation.ID(); !ok { v := workflowcontract.DefaultID() _c.mutation.SetID(v) @@ -246,6 +264,9 @@ func (_c *WorkflowContractCreate) check() error { return &ValidationError{Name: "scoped_resource_type", err: fmt.Errorf(`ent: validator failed for field "WorkflowContract.scoped_resource_type": %w`, err)} } } + if _, ok := _c.mutation.Managed(); !ok { + return &ValidationError{Name: "managed", err: errors.New(`ent: missing required field "WorkflowContract.managed"`)} + } return nil } @@ -310,6 +331,10 @@ func (_c *WorkflowContractCreate) createSpec() (*WorkflowContract, *sqlgraph.Cre _spec.SetField(workflowcontract.FieldScopedResourceID, field.TypeUUID, value) _node.ScopedResourceID = value } + if value, ok := _c.mutation.Managed(); ok { + _spec.SetField(workflowcontract.FieldManaged, field.TypeBool, value) + _node.Managed = value + } if nodes := _c.mutation.VersionsIDs(); len(nodes) > 0 { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.O2M, @@ -495,6 +520,18 @@ func (u *WorkflowContractUpsert) ClearScopedResourceID() *WorkflowContractUpsert return u } +// SetManaged sets the "managed" field. +func (u *WorkflowContractUpsert) SetManaged(v bool) *WorkflowContractUpsert { + u.Set(workflowcontract.FieldManaged, v) + return u +} + +// UpdateManaged sets the "managed" field to the value that was provided on create. +func (u *WorkflowContractUpsert) UpdateManaged() *WorkflowContractUpsert { + u.SetExcluded(workflowcontract.FieldManaged) + return u +} + // UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. // Using this option is equivalent to using: // @@ -647,6 +684,20 @@ func (u *WorkflowContractUpsertOne) ClearScopedResourceID() *WorkflowContractUps }) } +// SetManaged sets the "managed" field. +func (u *WorkflowContractUpsertOne) SetManaged(v bool) *WorkflowContractUpsertOne { + return u.Update(func(s *WorkflowContractUpsert) { + s.SetManaged(v) + }) +} + +// UpdateManaged sets the "managed" field to the value that was provided on create. +func (u *WorkflowContractUpsertOne) UpdateManaged() *WorkflowContractUpsertOne { + return u.Update(func(s *WorkflowContractUpsert) { + s.UpdateManaged() + }) +} + // Exec executes the query. func (u *WorkflowContractUpsertOne) Exec(ctx context.Context) error { if len(u.create.conflict) == 0 { @@ -966,6 +1017,20 @@ func (u *WorkflowContractUpsertBulk) ClearScopedResourceID() *WorkflowContractUp }) } +// SetManaged sets the "managed" field. +func (u *WorkflowContractUpsertBulk) SetManaged(v bool) *WorkflowContractUpsertBulk { + return u.Update(func(s *WorkflowContractUpsert) { + s.SetManaged(v) + }) +} + +// UpdateManaged sets the "managed" field to the value that was provided on create. +func (u *WorkflowContractUpsertBulk) UpdateManaged() *WorkflowContractUpsertBulk { + return u.Update(func(s *WorkflowContractUpsert) { + s.UpdateManaged() + }) +} + // Exec executes the query. func (u *WorkflowContractUpsertBulk) Exec(ctx context.Context) error { if u.create.err != nil { diff --git a/app/controlplane/pkg/data/ent/workflowcontract_update.go b/app/controlplane/pkg/data/ent/workflowcontract_update.go index 61e369803..eeb9e4fc3 100644 --- a/app/controlplane/pkg/data/ent/workflowcontract_update.go +++ b/app/controlplane/pkg/data/ent/workflowcontract_update.go @@ -128,6 +128,20 @@ func (_u *WorkflowContractUpdate) ClearScopedResourceID() *WorkflowContractUpdat return _u } +// SetManaged sets the "managed" field. +func (_u *WorkflowContractUpdate) SetManaged(v bool) *WorkflowContractUpdate { + _u.mutation.SetManaged(v) + return _u +} + +// SetNillableManaged sets the "managed" field if the given value is not nil. +func (_u *WorkflowContractUpdate) SetNillableManaged(v *bool) *WorkflowContractUpdate { + if v != nil { + _u.SetManaged(*v) + } + return _u +} + // AddVersionIDs adds the "versions" edge to the WorkflowContractVersion entity by IDs. func (_u *WorkflowContractUpdate) AddVersionIDs(ids ...uuid.UUID) *WorkflowContractUpdate { _u.mutation.AddVersionIDs(ids...) @@ -312,6 +326,9 @@ func (_u *WorkflowContractUpdate) sqlSave(ctx context.Context) (_node int, err e if _u.mutation.ScopedResourceIDCleared() { _spec.ClearField(workflowcontract.FieldScopedResourceID, field.TypeUUID) } + if value, ok := _u.mutation.Managed(); ok { + _spec.SetField(workflowcontract.FieldManaged, field.TypeBool, value) + } if _u.mutation.VersionsCleared() { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.O2M, @@ -547,6 +564,20 @@ func (_u *WorkflowContractUpdateOne) ClearScopedResourceID() *WorkflowContractUp return _u } +// SetManaged sets the "managed" field. +func (_u *WorkflowContractUpdateOne) SetManaged(v bool) *WorkflowContractUpdateOne { + _u.mutation.SetManaged(v) + return _u +} + +// SetNillableManaged sets the "managed" field if the given value is not nil. +func (_u *WorkflowContractUpdateOne) SetNillableManaged(v *bool) *WorkflowContractUpdateOne { + if v != nil { + _u.SetManaged(*v) + } + return _u +} + // AddVersionIDs adds the "versions" edge to the WorkflowContractVersion entity by IDs. func (_u *WorkflowContractUpdateOne) AddVersionIDs(ids ...uuid.UUID) *WorkflowContractUpdateOne { _u.mutation.AddVersionIDs(ids...) @@ -761,6 +792,9 @@ func (_u *WorkflowContractUpdateOne) sqlSave(ctx context.Context) (_node *Workfl if _u.mutation.ScopedResourceIDCleared() { _spec.ClearField(workflowcontract.FieldScopedResourceID, field.TypeUUID) } + if value, ok := _u.mutation.Managed(); ok { + _spec.SetField(workflowcontract.FieldManaged, field.TypeBool, value) + } if _u.mutation.VersionsCleared() { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.O2M, diff --git a/app/controlplane/pkg/data/workflowcontract.go b/app/controlplane/pkg/data/workflowcontract.go index 511350456..e9d23e0ce 100644 --- a/app/controlplane/pkg/data/workflowcontract.go +++ b/app/controlplane/pkg/data/workflowcontract.go @@ -1,5 +1,5 @@ // -// Copyright 2024-2025 The Chainloop Authors. +// Copyright 2024-2026 The Chainloop Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -122,7 +122,8 @@ func (r *WorkflowContractRepo) Create(ctx context.Context, opts *biz.ContractCre func (r *WorkflowContractRepo) addCreateToTx(ctx context.Context, tx *ent.Tx, opts *biz.ContractCreateOpts) (*ent.WorkflowContract, *ent.WorkflowContractVersion, error) { contractQuery := tx.WorkflowContract.Create(). SetName(opts.Name).SetOrganizationID(opts.OrgID). - SetNillableDescription(opts.Description) + SetNillableDescription(opts.Description). + SetNillableManaged(opts.Managed) if opts.ProjectID != nil { contractQuery = contractQuery.SetScopedResourceID(*opts.ProjectID).SetScopedResourceType(biz.ContractScopeProject) @@ -441,6 +442,7 @@ func (r *WorkflowContractRepo) entContractToBizContract(ctx context.Context, w * LatestRevisionCreatedAt: toTimePtr(version.CreatedAt), WorkflowRefs: workflowReferences, Description: w.Description, + Managed: w.Managed, } if w.ScopedResourceID != uuid.Nil {