diff --git a/app/controlplane/pkg/data/ent/client.go b/app/controlplane/pkg/data/ent/client.go index f056b65fc..c7953250c 100644 --- a/app/controlplane/pkg/data/ent/client.go +++ b/app/controlplane/pkg/data/ent/client.go @@ -2769,6 +2769,22 @@ func (c *WorkflowClient) QueryProject(w *Workflow) *ProjectQuery { return query } +// QueryLatestWorkflowRun queries the latest_workflow_run edge of a Workflow. +func (c *WorkflowClient) QueryLatestWorkflowRun(w *Workflow) *WorkflowRunQuery { + query := (&WorkflowRunClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := w.ID + step := sqlgraph.NewStep( + sqlgraph.From(workflow.Table, workflow.FieldID, id), + sqlgraph.To(workflowrun.Table, workflowrun.FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, workflow.LatestWorkflowRunTable, workflow.LatestWorkflowRunColumn), + ) + fromV = sqlgraph.Neighbors(w.driver.Dialect(), step) + return fromV, nil + } + return query +} + // QueryReferrers queries the referrers edge of a Workflow. func (c *WorkflowClient) QueryReferrers(w *Workflow) *ReferrerQuery { query := (&ReferrerClient{config: c.config}).Query() diff --git a/app/controlplane/pkg/data/ent/migrate/migrations/20241112093035.sql b/app/controlplane/pkg/data/ent/migrate/migrations/20241112093035.sql new file mode 100644 index 000000000..ad289886a --- /dev/null +++ b/app/controlplane/pkg/data/ent/migrate/migrations/20241112093035.sql @@ -0,0 +1,16 @@ +-- Modify "workflows" table +ALTER TABLE "workflows" ADD COLUMN "updated_at" timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP, ADD COLUMN "latest_run" uuid NULL, ADD CONSTRAINT "workflows_workflow_runs_latest_workflow_run" FOREIGN KEY ("latest_run") REFERENCES "workflow_runs" ("id") ON UPDATE NO ACTION ON DELETE SET NULL; + +-- This query modifies all existing Workflows to have the latest_run set to the most recent run +WITH ranked_runs AS ( + SELECT wr.*, + ROW_NUMBER() OVER (PARTITION BY wr.workflow_workflowruns ORDER BY wr.created_at DESC) AS run_rank + FROM workflow_runs wr + JOIN workflows w ON wr.workflow_workflowruns = w.id +) +UPDATE workflows w +SET latest_run = rr.id, + updated_at = CURRENT_TIMESTAMP +FROM ranked_runs rr +WHERE rr.run_rank = 1 + AND w.id = rr.workflow_workflowruns; diff --git a/app/controlplane/pkg/data/ent/migrate/migrations/atlas.sum b/app/controlplane/pkg/data/ent/migrate/migrations/atlas.sum index b40a1136b..89ad13749 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:1T7gv7Lcd8qIpPZUQuXQm5N3/NMypjjLzgzI//wb0jE= +h1:pHdF6up5OQHLlGTE7H4+pns0dc5ISfZKp0NHKCa6R1M= 20230706165452_init-schema.sql h1:VvqbNFEQnCvUVyj2iDYVQQxDM0+sSXqocpt/5H64k8M= 20230710111950-cas-backend.sql h1:A8iBuSzZIEbdsv9ipBtscZQuaBp3V5/VMw7eZH6GX+g= 20230712094107-cas-backends-workflow-runs.sql h1:a5rzxpVGyd56nLRSsKrmCFc9sebg65RWzLghKHh5xvI= @@ -60,3 +60,4 @@ h1:1T7gv7Lcd8qIpPZUQuXQm5N3/NMypjjLzgzI//wb0jE= 20241104110642.sql h1:zy1jgVPvHklL1o6EtgK104u1juXGxZMPIJfBzAPX9H8= 20241104122145.sql h1:Y8P3QxXUtM/zOmHIl+3H9j5RQWZtknCiZPgj1HgrbOQ= 20241107213854.sql h1:QqJSP0a0U1VMeYWmcXphUvJaF5SE22i99vRWJGUcGOU= +20241112093035.sql h1:ccnLSbDjlsVXu7nB30bnR/iIN4MexNi8gI+XkQtVTpA= diff --git a/app/controlplane/pkg/data/ent/migrate/schema.go b/app/controlplane/pkg/data/ent/migrate/schema.go index ae5f19316..d8b9875f1 100644 --- a/app/controlplane/pkg/data/ent/migrate/schema.go +++ b/app/controlplane/pkg/data/ent/migrate/schema.go @@ -410,12 +410,14 @@ var ( {Name: "team", Type: field.TypeString, Nullable: true}, {Name: "runs_count", Type: field.TypeInt, Default: 0}, {Name: "created_at", Type: field.TypeTime, Default: "CURRENT_TIMESTAMP"}, + {Name: "updated_at", Type: field.TypeTime, Default: "CURRENT_TIMESTAMP"}, {Name: "deleted_at", Type: field.TypeTime, Nullable: true}, {Name: "public", Type: field.TypeBool, Default: false}, {Name: "description", Type: field.TypeString, Nullable: true}, {Name: "organization_id", Type: field.TypeUUID}, {Name: "project_id", Type: field.TypeUUID}, {Name: "workflow_contract", Type: field.TypeUUID}, + {Name: "latest_run", Type: field.TypeUUID, Nullable: true}, } // WorkflowsTable holds the schema information for the "workflows" table. WorkflowsTable = &schema.Table{ @@ -425,28 +427,34 @@ var ( ForeignKeys: []*schema.ForeignKey{ { Symbol: "workflows_organizations_workflows", - Columns: []*schema.Column{WorkflowsColumns[9]}, + Columns: []*schema.Column{WorkflowsColumns[10]}, RefColumns: []*schema.Column{OrganizationsColumns[0]}, OnDelete: schema.Cascade, }, { Symbol: "workflows_projects_workflows", - Columns: []*schema.Column{WorkflowsColumns[10]}, + Columns: []*schema.Column{WorkflowsColumns[11]}, RefColumns: []*schema.Column{ProjectsColumns[0]}, OnDelete: schema.NoAction, }, { Symbol: "workflows_workflow_contracts_contract", - Columns: []*schema.Column{WorkflowsColumns[11]}, + Columns: []*schema.Column{WorkflowsColumns[12]}, RefColumns: []*schema.Column{WorkflowContractsColumns[0]}, OnDelete: schema.NoAction, }, + { + Symbol: "workflows_workflow_runs_latest_workflow_run", + Columns: []*schema.Column{WorkflowsColumns[13]}, + RefColumns: []*schema.Column{WorkflowRunsColumns[0]}, + OnDelete: schema.SetNull, + }, }, Indexes: []*schema.Index{ { Name: "workflow_name_organization_id_project_id", Unique: true, - Columns: []*schema.Column{WorkflowsColumns[1], WorkflowsColumns[9], WorkflowsColumns[10]}, + Columns: []*schema.Column{WorkflowsColumns[1], WorkflowsColumns[10], WorkflowsColumns[11]}, Annotation: &entsql.IndexAnnotation{ Where: "deleted_at IS NULL", }, @@ -454,7 +462,7 @@ var ( { Name: "workflow_organization_id_id", Unique: true, - Columns: []*schema.Column{WorkflowsColumns[9], WorkflowsColumns[0]}, + Columns: []*schema.Column{WorkflowsColumns[10], WorkflowsColumns[0]}, Annotation: &entsql.IndexAnnotation{ Where: "deleted_at IS NULL", }, @@ -715,6 +723,7 @@ func init() { WorkflowsTable.ForeignKeys[0].RefTable = OrganizationsTable WorkflowsTable.ForeignKeys[1].RefTable = ProjectsTable WorkflowsTable.ForeignKeys[2].RefTable = WorkflowContractsTable + WorkflowsTable.ForeignKeys[3].RefTable = WorkflowRunsTable WorkflowContractsTable.ForeignKeys[0].RefTable = OrganizationsTable WorkflowContractVersionsTable.ForeignKeys[0].RefTable = WorkflowContractsTable WorkflowRunsTable.ForeignKeys[0].RefTable = ProjectVersionsTable diff --git a/app/controlplane/pkg/data/ent/mutation.go b/app/controlplane/pkg/data/ent/mutation.go index 843b0fe26..370702065 100644 --- a/app/controlplane/pkg/data/ent/mutation.go +++ b/app/controlplane/pkg/data/ent/mutation.go @@ -9858,6 +9858,7 @@ type WorkflowMutation struct { runs_count *int addruns_count *int created_at *time.Time + updated_at *time.Time deleted_at *time.Time public *bool description *string @@ -9877,6 +9878,8 @@ type WorkflowMutation struct { clearedintegration_attachments bool project *uuid.UUID clearedproject bool + latest_workflow_run *uuid.UUID + clearedlatest_workflow_run bool referrers map[uuid.UUID]struct{} removedreferrers map[uuid.UUID]struct{} clearedreferrers bool @@ -10215,6 +10218,42 @@ func (m *WorkflowMutation) ResetCreatedAt() { m.created_at = nil } +// SetUpdatedAt sets the "updated_at" field. +func (m *WorkflowMutation) SetUpdatedAt(t time.Time) { + m.updated_at = &t +} + +// UpdatedAt returns the value of the "updated_at" field in the mutation. +func (m *WorkflowMutation) UpdatedAt() (r time.Time, exists bool) { + v := m.updated_at + if v == nil { + return + } + return *v, true +} + +// OldUpdatedAt returns the old "updated_at" field's value of the Workflow entity. +// If the Workflow 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 *WorkflowMutation) OldUpdatedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldUpdatedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldUpdatedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldUpdatedAt: %w", err) + } + return oldValue.UpdatedAt, nil +} + +// ResetUpdatedAt resets all changes to the "updated_at" field. +func (m *WorkflowMutation) ResetUpdatedAt() { + m.updated_at = nil +} + // SetDeletedAt sets the "deleted_at" field. func (m *WorkflowMutation) SetDeletedAt(t time.Time) { m.deleted_at = &t @@ -10372,6 +10411,55 @@ func (m *WorkflowMutation) ResetProjectID() { m.project = nil } +// SetLatestRun sets the "latest_run" field. +func (m *WorkflowMutation) SetLatestRun(u uuid.UUID) { + m.latest_workflow_run = &u +} + +// LatestRun returns the value of the "latest_run" field in the mutation. +func (m *WorkflowMutation) LatestRun() (r uuid.UUID, exists bool) { + v := m.latest_workflow_run + if v == nil { + return + } + return *v, true +} + +// OldLatestRun returns the old "latest_run" field's value of the Workflow entity. +// If the Workflow 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 *WorkflowMutation) OldLatestRun(ctx context.Context) (v *uuid.UUID, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldLatestRun is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldLatestRun requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldLatestRun: %w", err) + } + return oldValue.LatestRun, nil +} + +// ClearLatestRun clears the value of the "latest_run" field. +func (m *WorkflowMutation) ClearLatestRun() { + m.latest_workflow_run = nil + m.clearedFields[workflow.FieldLatestRun] = struct{}{} +} + +// LatestRunCleared returns if the "latest_run" field was cleared in this mutation. +func (m *WorkflowMutation) LatestRunCleared() bool { + _, ok := m.clearedFields[workflow.FieldLatestRun] + return ok +} + +// ResetLatestRun resets all changes to the "latest_run" field. +func (m *WorkflowMutation) ResetLatestRun() { + m.latest_workflow_run = nil + delete(m.clearedFields, workflow.FieldLatestRun) +} + // SetDescription sets the "description" field. func (m *WorkflowMutation) SetDescription(s string) { m.description = &s @@ -10676,6 +10764,46 @@ func (m *WorkflowMutation) ResetProject() { m.clearedproject = false } +// SetLatestWorkflowRunID sets the "latest_workflow_run" edge to the WorkflowRun entity by id. +func (m *WorkflowMutation) SetLatestWorkflowRunID(id uuid.UUID) { + m.latest_workflow_run = &id +} + +// ClearLatestWorkflowRun clears the "latest_workflow_run" edge to the WorkflowRun entity. +func (m *WorkflowMutation) ClearLatestWorkflowRun() { + m.clearedlatest_workflow_run = true + m.clearedFields[workflow.FieldLatestRun] = struct{}{} +} + +// LatestWorkflowRunCleared reports if the "latest_workflow_run" edge to the WorkflowRun entity was cleared. +func (m *WorkflowMutation) LatestWorkflowRunCleared() bool { + return m.LatestRunCleared() || m.clearedlatest_workflow_run +} + +// LatestWorkflowRunID returns the "latest_workflow_run" edge ID in the mutation. +func (m *WorkflowMutation) LatestWorkflowRunID() (id uuid.UUID, exists bool) { + if m.latest_workflow_run != nil { + return *m.latest_workflow_run, true + } + return +} + +// LatestWorkflowRunIDs returns the "latest_workflow_run" edge IDs in the mutation. +// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use +// LatestWorkflowRunID instead. It exists only for internal usage by the builders. +func (m *WorkflowMutation) LatestWorkflowRunIDs() (ids []uuid.UUID) { + if id := m.latest_workflow_run; id != nil { + ids = append(ids, *id) + } + return +} + +// ResetLatestWorkflowRun resets all changes to the "latest_workflow_run" edge. +func (m *WorkflowMutation) ResetLatestWorkflowRun() { + m.latest_workflow_run = nil + m.clearedlatest_workflow_run = false +} + // AddReferrerIDs adds the "referrers" edge to the Referrer entity by ids. func (m *WorkflowMutation) AddReferrerIDs(ids ...uuid.UUID) { if m.referrers == nil { @@ -10764,7 +10892,7 @@ func (m *WorkflowMutation) Type() string { // order to get all numeric fields that were incremented/decremented, call // AddedFields(). func (m *WorkflowMutation) Fields() []string { - fields := make([]string, 0, 10) + fields := make([]string, 0, 12) if m.name != nil { fields = append(fields, workflow.FieldName) } @@ -10780,6 +10908,9 @@ func (m *WorkflowMutation) Fields() []string { if m.created_at != nil { fields = append(fields, workflow.FieldCreatedAt) } + if m.updated_at != nil { + fields = append(fields, workflow.FieldUpdatedAt) + } if m.deleted_at != nil { fields = append(fields, workflow.FieldDeletedAt) } @@ -10792,6 +10923,9 @@ func (m *WorkflowMutation) Fields() []string { if m.project != nil { fields = append(fields, workflow.FieldProjectID) } + if m.latest_workflow_run != nil { + fields = append(fields, workflow.FieldLatestRun) + } if m.description != nil { fields = append(fields, workflow.FieldDescription) } @@ -10813,6 +10947,8 @@ func (m *WorkflowMutation) Field(name string) (ent.Value, bool) { return m.RunsCount() case workflow.FieldCreatedAt: return m.CreatedAt() + case workflow.FieldUpdatedAt: + return m.UpdatedAt() case workflow.FieldDeletedAt: return m.DeletedAt() case workflow.FieldPublic: @@ -10821,6 +10957,8 @@ func (m *WorkflowMutation) Field(name string) (ent.Value, bool) { return m.OrganizationID() case workflow.FieldProjectID: return m.ProjectID() + case workflow.FieldLatestRun: + return m.LatestRun() case workflow.FieldDescription: return m.Description() } @@ -10842,6 +10980,8 @@ func (m *WorkflowMutation) OldField(ctx context.Context, name string) (ent.Value return m.OldRunsCount(ctx) case workflow.FieldCreatedAt: return m.OldCreatedAt(ctx) + case workflow.FieldUpdatedAt: + return m.OldUpdatedAt(ctx) case workflow.FieldDeletedAt: return m.OldDeletedAt(ctx) case workflow.FieldPublic: @@ -10850,6 +10990,8 @@ func (m *WorkflowMutation) OldField(ctx context.Context, name string) (ent.Value return m.OldOrganizationID(ctx) case workflow.FieldProjectID: return m.OldProjectID(ctx) + case workflow.FieldLatestRun: + return m.OldLatestRun(ctx) case workflow.FieldDescription: return m.OldDescription(ctx) } @@ -10896,6 +11038,13 @@ func (m *WorkflowMutation) SetField(name string, value ent.Value) error { } m.SetCreatedAt(v) return nil + case workflow.FieldUpdatedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUpdatedAt(v) + return nil case workflow.FieldDeletedAt: v, ok := value.(time.Time) if !ok { @@ -10924,6 +11073,13 @@ func (m *WorkflowMutation) SetField(name string, value ent.Value) error { } m.SetProjectID(v) return nil + case workflow.FieldLatestRun: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetLatestRun(v) + return nil case workflow.FieldDescription: v, ok := value.(string) if !ok { @@ -10985,6 +11141,9 @@ func (m *WorkflowMutation) ClearedFields() []string { if m.FieldCleared(workflow.FieldDeletedAt) { fields = append(fields, workflow.FieldDeletedAt) } + if m.FieldCleared(workflow.FieldLatestRun) { + fields = append(fields, workflow.FieldLatestRun) + } if m.FieldCleared(workflow.FieldDescription) { fields = append(fields, workflow.FieldDescription) } @@ -11011,6 +11170,9 @@ func (m *WorkflowMutation) ClearField(name string) error { case workflow.FieldDeletedAt: m.ClearDeletedAt() return nil + case workflow.FieldLatestRun: + m.ClearLatestRun() + return nil case workflow.FieldDescription: m.ClearDescription() return nil @@ -11037,6 +11199,9 @@ func (m *WorkflowMutation) ResetField(name string) error { case workflow.FieldCreatedAt: m.ResetCreatedAt() return nil + case workflow.FieldUpdatedAt: + m.ResetUpdatedAt() + return nil case workflow.FieldDeletedAt: m.ResetDeletedAt() return nil @@ -11049,6 +11214,9 @@ func (m *WorkflowMutation) ResetField(name string) error { case workflow.FieldProjectID: m.ResetProjectID() return nil + case workflow.FieldLatestRun: + m.ResetLatestRun() + return nil case workflow.FieldDescription: m.ResetDescription() return nil @@ -11058,7 +11226,7 @@ func (m *WorkflowMutation) ResetField(name string) error { // AddedEdges returns all edge names that were set/added in this mutation. func (m *WorkflowMutation) AddedEdges() []string { - edges := make([]string, 0, 7) + edges := make([]string, 0, 8) if m.robotaccounts != nil { edges = append(edges, workflow.EdgeRobotaccounts) } @@ -11077,6 +11245,9 @@ func (m *WorkflowMutation) AddedEdges() []string { if m.project != nil { edges = append(edges, workflow.EdgeProject) } + if m.latest_workflow_run != nil { + edges = append(edges, workflow.EdgeLatestWorkflowRun) + } if m.referrers != nil { edges = append(edges, workflow.EdgeReferrers) } @@ -11117,6 +11288,10 @@ func (m *WorkflowMutation) AddedIDs(name string) []ent.Value { if id := m.project; id != nil { return []ent.Value{*id} } + case workflow.EdgeLatestWorkflowRun: + if id := m.latest_workflow_run; id != nil { + return []ent.Value{*id} + } case workflow.EdgeReferrers: ids := make([]ent.Value, 0, len(m.referrers)) for id := range m.referrers { @@ -11129,7 +11304,7 @@ func (m *WorkflowMutation) AddedIDs(name string) []ent.Value { // RemovedEdges returns all edge names that were removed in this mutation. func (m *WorkflowMutation) RemovedEdges() []string { - edges := make([]string, 0, 7) + edges := make([]string, 0, 8) if m.removedrobotaccounts != nil { edges = append(edges, workflow.EdgeRobotaccounts) } @@ -11179,7 +11354,7 @@ func (m *WorkflowMutation) RemovedIDs(name string) []ent.Value { // ClearedEdges returns all edge names that were cleared in this mutation. func (m *WorkflowMutation) ClearedEdges() []string { - edges := make([]string, 0, 7) + edges := make([]string, 0, 8) if m.clearedrobotaccounts { edges = append(edges, workflow.EdgeRobotaccounts) } @@ -11198,6 +11373,9 @@ func (m *WorkflowMutation) ClearedEdges() []string { if m.clearedproject { edges = append(edges, workflow.EdgeProject) } + if m.clearedlatest_workflow_run { + edges = append(edges, workflow.EdgeLatestWorkflowRun) + } if m.clearedreferrers { edges = append(edges, workflow.EdgeReferrers) } @@ -11220,6 +11398,8 @@ func (m *WorkflowMutation) EdgeCleared(name string) bool { return m.clearedintegration_attachments case workflow.EdgeProject: return m.clearedproject + case workflow.EdgeLatestWorkflowRun: + return m.clearedlatest_workflow_run case workflow.EdgeReferrers: return m.clearedreferrers } @@ -11239,6 +11419,9 @@ func (m *WorkflowMutation) ClearEdge(name string) error { case workflow.EdgeProject: m.ClearProject() return nil + case workflow.EdgeLatestWorkflowRun: + m.ClearLatestWorkflowRun() + return nil } return fmt.Errorf("unknown Workflow unique edge %s", name) } @@ -11265,6 +11448,9 @@ func (m *WorkflowMutation) ResetEdge(name string) error { case workflow.EdgeProject: m.ResetProject() return nil + case workflow.EdgeLatestWorkflowRun: + m.ResetLatestWorkflowRun() + return nil case workflow.EdgeReferrers: m.ResetReferrers() return nil diff --git a/app/controlplane/pkg/data/ent/runtime.go b/app/controlplane/pkg/data/ent/runtime.go index d2d8ed23b..1b882db97 100644 --- a/app/controlplane/pkg/data/ent/runtime.go +++ b/app/controlplane/pkg/data/ent/runtime.go @@ -208,8 +208,12 @@ func init() { workflowDescCreatedAt := workflowFields[5].Descriptor() // workflow.DefaultCreatedAt holds the default value on creation for the created_at field. workflow.DefaultCreatedAt = workflowDescCreatedAt.Default.(func() time.Time) + // workflowDescUpdatedAt is the schema descriptor for updated_at field. + workflowDescUpdatedAt := workflowFields[6].Descriptor() + // workflow.DefaultUpdatedAt holds the default value on creation for the updated_at field. + workflow.DefaultUpdatedAt = workflowDescUpdatedAt.Default.(func() time.Time) // workflowDescPublic is the schema descriptor for public field. - workflowDescPublic := workflowFields[7].Descriptor() + workflowDescPublic := workflowFields[8].Descriptor() // workflow.DefaultPublic holds the default value on creation for the public field. workflow.DefaultPublic = workflowDescPublic.Default.(bool) // workflowDescID is the schema descriptor for id field. diff --git a/app/controlplane/pkg/data/ent/schema-viz.html b/app/controlplane/pkg/data/ent/schema-viz.html index 26ddbb028..731e02c1b 100644 --- a/app/controlplane/pkg/data/ent/schema-viz.html +++ b/app/controlplane/pkg/data/ent/schema-viz.html @@ -70,7 +70,7 @@ } - const entGraph = JSON.parse("{\"nodes\":[{\"id\":\"APIToken\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"expires_at\",\"type\":\"time.Time\"},{\"name\":\"revoked_at\",\"type\":\"time.Time\"},{\"name\":\"organization_id\",\"type\":\"uuid.UUID\"}]},{\"id\":\"CASBackend\",\"fields\":[{\"name\":\"location\",\"type\":\"string\"},{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"provider\",\"type\":\"biz.CASBackendProvider\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"secret_name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"validation_status\",\"type\":\"biz.CASBackendValidationStatus\"},{\"name\":\"validated_at\",\"type\":\"time.Time\"},{\"name\":\"default\",\"type\":\"bool\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"fallback\",\"type\":\"bool\"},{\"name\":\"max_blob_size_bytes\",\"type\":\"int64\"}]},{\"id\":\"CASMapping\",\"fields\":[{\"name\":\"digest\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"}]},{\"id\":\"Integration\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"kind\",\"type\":\"string\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"secret_name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"configuration\",\"type\":\"[]byte\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"}]},{\"id\":\"IntegrationAttachment\",\"fields\":[{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"configuration\",\"type\":\"[]byte\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"workflow_id\",\"type\":\"uuid.UUID\"}]},{\"id\":\"Membership\",\"fields\":[{\"name\":\"current\",\"type\":\"bool\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"updated_at\",\"type\":\"time.Time\"},{\"name\":\"role\",\"type\":\"authz.Role\"}]},{\"id\":\"OrgInvitation\",\"fields\":[{\"name\":\"receiver_email\",\"type\":\"string\"},{\"name\":\"status\",\"type\":\"biz.OrgInvitationStatus\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"organization_id\",\"type\":\"uuid.UUID\"},{\"name\":\"sender_id\",\"type\":\"uuid.UUID\"},{\"name\":\"role\",\"type\":\"authz.Role\"}]},{\"id\":\"Organization\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"}]},{\"id\":\"Project\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"organization_id\",\"type\":\"uuid.UUID\"}]},{\"id\":\"ProjectVersion\",\"fields\":[{\"name\":\"version\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"project_id\",\"type\":\"uuid.UUID\"},{\"name\":\"prerelease\",\"type\":\"bool\"}]},{\"id\":\"Referrer\",\"fields\":[{\"name\":\"digest\",\"type\":\"string\"},{\"name\":\"kind\",\"type\":\"string\"},{\"name\":\"downloadable\",\"type\":\"bool\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"metadata\",\"type\":\"map[string]string\"},{\"name\":\"annotations\",\"type\":\"map[string]string\"}]},{\"id\":\"RobotAccount\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"revoked_at\",\"type\":\"time.Time\"}]},{\"id\":\"User\",\"fields\":[{\"name\":\"email\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"}]},{\"id\":\"Workflow\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"project_old\",\"type\":\"string\"},{\"name\":\"team\",\"type\":\"string\"},{\"name\":\"runs_count\",\"type\":\"int\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"public\",\"type\":\"bool\"},{\"name\":\"organization_id\",\"type\":\"uuid.UUID\"},{\"name\":\"project_id\",\"type\":\"uuid.UUID\"},{\"name\":\"description\",\"type\":\"string\"}]},{\"id\":\"WorkflowContract\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"description\",\"type\":\"string\"}]},{\"id\":\"WorkflowContractVersion\",\"fields\":[{\"name\":\"body\",\"type\":\"[]byte\"},{\"name\":\"raw_body\",\"type\":\"[]byte\"},{\"name\":\"raw_body_format\",\"type\":\"unmarshal.RawFormat\"},{\"name\":\"revision\",\"type\":\"int\"},{\"name\":\"created_at\",\"type\":\"time.Time\"}]},{\"id\":\"WorkflowRun\",\"fields\":[{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"finished_at\",\"type\":\"time.Time\"},{\"name\":\"state\",\"type\":\"biz.WorkflowRunStatus\"},{\"name\":\"reason\",\"type\":\"string\"},{\"name\":\"run_url\",\"type\":\"string\"},{\"name\":\"runner_type\",\"type\":\"string\"},{\"name\":\"attestation\",\"type\":\"*dsse.Envelope\"},{\"name\":\"attestation_digest\",\"type\":\"string\"},{\"name\":\"attestation_state\",\"type\":\"[]byte\"},{\"name\":\"contract_revision_used\",\"type\":\"int\"},{\"name\":\"contract_revision_latest\",\"type\":\"int\"},{\"name\":\"version_id\",\"type\":\"uuid.UUID\"}]}],\"edges\":[{\"from\":\"CASMapping\",\"to\":\"CASBackend\",\"label\":\"cas_backend\"},{\"from\":\"CASMapping\",\"to\":\"WorkflowRun\",\"label\":\"workflow_run\"},{\"from\":\"CASMapping\",\"to\":\"Organization\",\"label\":\"organization\"},{\"from\":\"IntegrationAttachment\",\"to\":\"Integration\",\"label\":\"integration\"},{\"from\":\"IntegrationAttachment\",\"to\":\"Workflow\",\"label\":\"workflow\"},{\"from\":\"OrgInvitation\",\"to\":\"Organization\",\"label\":\"organization\"},{\"from\":\"OrgInvitation\",\"to\":\"User\",\"label\":\"sender\"},{\"from\":\"Organization\",\"to\":\"Membership\",\"label\":\"memberships\"},{\"from\":\"Organization\",\"to\":\"WorkflowContract\",\"label\":\"workflow_contracts\"},{\"from\":\"Organization\",\"to\":\"Workflow\",\"label\":\"workflows\"},{\"from\":\"Organization\",\"to\":\"CASBackend\",\"label\":\"cas_backends\"},{\"from\":\"Organization\",\"to\":\"Integration\",\"label\":\"integrations\"},{\"from\":\"Organization\",\"to\":\"APIToken\",\"label\":\"api_tokens\"},{\"from\":\"Organization\",\"to\":\"Project\",\"label\":\"projects\"},{\"from\":\"Project\",\"to\":\"Workflow\",\"label\":\"workflows\"},{\"from\":\"Project\",\"to\":\"ProjectVersion\",\"label\":\"versions\"},{\"from\":\"ProjectVersion\",\"to\":\"WorkflowRun\",\"label\":\"runs\"},{\"from\":\"Referrer\",\"to\":\"Referrer\",\"label\":\"references\"},{\"from\":\"Referrer\",\"to\":\"Workflow\",\"label\":\"workflows\"},{\"from\":\"User\",\"to\":\"Membership\",\"label\":\"memberships\"},{\"from\":\"Workflow\",\"to\":\"RobotAccount\",\"label\":\"robotaccounts\"},{\"from\":\"Workflow\",\"to\":\"WorkflowRun\",\"label\":\"workflowruns\"},{\"from\":\"Workflow\",\"to\":\"WorkflowContract\",\"label\":\"contract\"},{\"from\":\"WorkflowContract\",\"to\":\"WorkflowContractVersion\",\"label\":\"versions\"},{\"from\":\"WorkflowRun\",\"to\":\"WorkflowContractVersion\",\"label\":\"contract_version\"},{\"from\":\"WorkflowRun\",\"to\":\"CASBackend\",\"label\":\"cas_backends\"}]}"); + const entGraph = JSON.parse("{\"nodes\":[{\"id\":\"APIToken\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"expires_at\",\"type\":\"time.Time\"},{\"name\":\"revoked_at\",\"type\":\"time.Time\"},{\"name\":\"organization_id\",\"type\":\"uuid.UUID\"}]},{\"id\":\"CASBackend\",\"fields\":[{\"name\":\"location\",\"type\":\"string\"},{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"provider\",\"type\":\"biz.CASBackendProvider\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"secret_name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"validation_status\",\"type\":\"biz.CASBackendValidationStatus\"},{\"name\":\"validated_at\",\"type\":\"time.Time\"},{\"name\":\"default\",\"type\":\"bool\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"fallback\",\"type\":\"bool\"},{\"name\":\"max_blob_size_bytes\",\"type\":\"int64\"}]},{\"id\":\"CASMapping\",\"fields\":[{\"name\":\"digest\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"}]},{\"id\":\"Integration\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"kind\",\"type\":\"string\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"secret_name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"configuration\",\"type\":\"[]byte\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"}]},{\"id\":\"IntegrationAttachment\",\"fields\":[{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"configuration\",\"type\":\"[]byte\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"workflow_id\",\"type\":\"uuid.UUID\"}]},{\"id\":\"Membership\",\"fields\":[{\"name\":\"current\",\"type\":\"bool\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"updated_at\",\"type\":\"time.Time\"},{\"name\":\"role\",\"type\":\"authz.Role\"}]},{\"id\":\"OrgInvitation\",\"fields\":[{\"name\":\"receiver_email\",\"type\":\"string\"},{\"name\":\"status\",\"type\":\"biz.OrgInvitationStatus\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"organization_id\",\"type\":\"uuid.UUID\"},{\"name\":\"sender_id\",\"type\":\"uuid.UUID\"},{\"name\":\"role\",\"type\":\"authz.Role\"}]},{\"id\":\"Organization\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"}]},{\"id\":\"Project\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"organization_id\",\"type\":\"uuid.UUID\"}]},{\"id\":\"ProjectVersion\",\"fields\":[{\"name\":\"version\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"project_id\",\"type\":\"uuid.UUID\"},{\"name\":\"prerelease\",\"type\":\"bool\"}]},{\"id\":\"Referrer\",\"fields\":[{\"name\":\"digest\",\"type\":\"string\"},{\"name\":\"kind\",\"type\":\"string\"},{\"name\":\"downloadable\",\"type\":\"bool\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"metadata\",\"type\":\"map[string]string\"},{\"name\":\"annotations\",\"type\":\"map[string]string\"}]},{\"id\":\"RobotAccount\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"revoked_at\",\"type\":\"time.Time\"}]},{\"id\":\"User\",\"fields\":[{\"name\":\"email\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"}]},{\"id\":\"Workflow\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"project_old\",\"type\":\"string\"},{\"name\":\"team\",\"type\":\"string\"},{\"name\":\"runs_count\",\"type\":\"int\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"updated_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"public\",\"type\":\"bool\"},{\"name\":\"organization_id\",\"type\":\"uuid.UUID\"},{\"name\":\"project_id\",\"type\":\"uuid.UUID\"},{\"name\":\"latest_run\",\"type\":\"uuid.UUID\"},{\"name\":\"description\",\"type\":\"string\"}]},{\"id\":\"WorkflowContract\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"description\",\"type\":\"string\"}]},{\"id\":\"WorkflowContractVersion\",\"fields\":[{\"name\":\"body\",\"type\":\"[]byte\"},{\"name\":\"raw_body\",\"type\":\"[]byte\"},{\"name\":\"raw_body_format\",\"type\":\"unmarshal.RawFormat\"},{\"name\":\"revision\",\"type\":\"int\"},{\"name\":\"created_at\",\"type\":\"time.Time\"}]},{\"id\":\"WorkflowRun\",\"fields\":[{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"finished_at\",\"type\":\"time.Time\"},{\"name\":\"state\",\"type\":\"biz.WorkflowRunStatus\"},{\"name\":\"reason\",\"type\":\"string\"},{\"name\":\"run_url\",\"type\":\"string\"},{\"name\":\"runner_type\",\"type\":\"string\"},{\"name\":\"attestation\",\"type\":\"*dsse.Envelope\"},{\"name\":\"attestation_digest\",\"type\":\"string\"},{\"name\":\"attestation_state\",\"type\":\"[]byte\"},{\"name\":\"contract_revision_used\",\"type\":\"int\"},{\"name\":\"contract_revision_latest\",\"type\":\"int\"},{\"name\":\"version_id\",\"type\":\"uuid.UUID\"}]}],\"edges\":[{\"from\":\"CASMapping\",\"to\":\"CASBackend\",\"label\":\"cas_backend\"},{\"from\":\"CASMapping\",\"to\":\"WorkflowRun\",\"label\":\"workflow_run\"},{\"from\":\"CASMapping\",\"to\":\"Organization\",\"label\":\"organization\"},{\"from\":\"IntegrationAttachment\",\"to\":\"Integration\",\"label\":\"integration\"},{\"from\":\"IntegrationAttachment\",\"to\":\"Workflow\",\"label\":\"workflow\"},{\"from\":\"OrgInvitation\",\"to\":\"Organization\",\"label\":\"organization\"},{\"from\":\"OrgInvitation\",\"to\":\"User\",\"label\":\"sender\"},{\"from\":\"Organization\",\"to\":\"Membership\",\"label\":\"memberships\"},{\"from\":\"Organization\",\"to\":\"WorkflowContract\",\"label\":\"workflow_contracts\"},{\"from\":\"Organization\",\"to\":\"Workflow\",\"label\":\"workflows\"},{\"from\":\"Organization\",\"to\":\"CASBackend\",\"label\":\"cas_backends\"},{\"from\":\"Organization\",\"to\":\"Integration\",\"label\":\"integrations\"},{\"from\":\"Organization\",\"to\":\"APIToken\",\"label\":\"api_tokens\"},{\"from\":\"Organization\",\"to\":\"Project\",\"label\":\"projects\"},{\"from\":\"Project\",\"to\":\"Workflow\",\"label\":\"workflows\"},{\"from\":\"Project\",\"to\":\"ProjectVersion\",\"label\":\"versions\"},{\"from\":\"ProjectVersion\",\"to\":\"WorkflowRun\",\"label\":\"runs\"},{\"from\":\"Referrer\",\"to\":\"Referrer\",\"label\":\"references\"},{\"from\":\"Referrer\",\"to\":\"Workflow\",\"label\":\"workflows\"},{\"from\":\"User\",\"to\":\"Membership\",\"label\":\"memberships\"},{\"from\":\"Workflow\",\"to\":\"RobotAccount\",\"label\":\"robotaccounts\"},{\"from\":\"Workflow\",\"to\":\"WorkflowRun\",\"label\":\"workflowruns\"},{\"from\":\"Workflow\",\"to\":\"WorkflowContract\",\"label\":\"contract\"},{\"from\":\"Workflow\",\"to\":\"WorkflowRun\",\"label\":\"latest_workflow_run\"},{\"from\":\"WorkflowContract\",\"to\":\"WorkflowContractVersion\",\"label\":\"versions\"},{\"from\":\"WorkflowRun\",\"to\":\"WorkflowContractVersion\",\"label\":\"contract_version\"},{\"from\":\"WorkflowRun\",\"to\":\"CASBackend\",\"label\":\"cas_backends\"}]}"); const nodes = new vis.DataSet((entGraph.nodes || []).map(n => ({ id: n.id, diff --git a/app/controlplane/pkg/data/ent/schema/workflow.go b/app/controlplane/pkg/data/ent/schema/workflow.go index 77af8b1ad..92fba08d8 100644 --- a/app/controlplane/pkg/data/ent/schema/workflow.go +++ b/app/controlplane/pkg/data/ent/schema/workflow.go @@ -46,11 +46,17 @@ func (Workflow) Fields() []ent.Field { Annotations(&entsql.Annotation{ Default: "CURRENT_TIMESTAMP", }), + field.Time("updated_at"). + Default(time.Now). + Annotations(&entsql.Annotation{ + Default: "CURRENT_TIMESTAMP", + }), field.Time("deleted_at").Optional(), // public means that the workflow runs, attestations and materials are reachable field.Bool("public").Default(false), field.UUID("organization_id", uuid.UUID{}), field.UUID("project_id", uuid.UUID{}), + field.UUID("latest_run", uuid.UUID{}).Optional().Nillable(), field.String("description").Optional(), } } @@ -65,6 +71,7 @@ func (Workflow) Edges() []ent.Edge { edge.From("integration_attachments", IntegrationAttachment.Type). Ref("workflow"), edge.From("project", Project.Type).Unique().Field("project_id").Ref("workflows").Required(), + edge.To("latest_workflow_run", WorkflowRun.Type).Field("latest_run").Unique(), // M2M. referrer can be part of multiple workflows edge.From("referrers", Referrer.Type).Ref("workflows"), diff --git a/app/controlplane/pkg/data/ent/workflow.go b/app/controlplane/pkg/data/ent/workflow.go index 378a92f2f..2eaec1b12 100644 --- a/app/controlplane/pkg/data/ent/workflow.go +++ b/app/controlplane/pkg/data/ent/workflow.go @@ -13,6 +13,7 @@ import ( "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/project" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/workflow" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/workflowcontract" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/workflowrun" "github.com/google/uuid" ) @@ -31,6 +32,8 @@ type Workflow struct { RunsCount int `json:"runs_count,omitempty"` // CreatedAt holds the value of the "created_at" field. CreatedAt time.Time `json:"created_at,omitempty"` + // UpdatedAt holds the value of the "updated_at" field. + UpdatedAt time.Time `json:"updated_at,omitempty"` // DeletedAt holds the value of the "deleted_at" field. DeletedAt time.Time `json:"deleted_at,omitempty"` // Public holds the value of the "public" field. @@ -39,6 +42,8 @@ type Workflow struct { OrganizationID uuid.UUID `json:"organization_id,omitempty"` // ProjectID holds the value of the "project_id" field. ProjectID uuid.UUID `json:"project_id,omitempty"` + // LatestRun holds the value of the "latest_run" field. + LatestRun *uuid.UUID `json:"latest_run,omitempty"` // Description holds the value of the "description" field. Description string `json:"description,omitempty"` // Edges holds the relations/edges for other nodes in the graph. @@ -62,11 +67,13 @@ type WorkflowEdges struct { IntegrationAttachments []*IntegrationAttachment `json:"integration_attachments,omitempty"` // Project holds the value of the project edge. Project *Project `json:"project,omitempty"` + // LatestWorkflowRun holds the value of the latest_workflow_run edge. + LatestWorkflowRun *WorkflowRun `json:"latest_workflow_run,omitempty"` // Referrers holds the value of the referrers edge. Referrers []*Referrer `json:"referrers,omitempty"` // loadedTypes holds the information for reporting if a // type was loaded (or requested) in eager-loading or not. - loadedTypes [7]bool + loadedTypes [8]bool } // RobotaccountsOrErr returns the Robotaccounts value or an error if the edge @@ -129,10 +136,21 @@ func (e WorkflowEdges) ProjectOrErr() (*Project, error) { return nil, &NotLoadedError{edge: "project"} } +// LatestWorkflowRunOrErr returns the LatestWorkflowRun value or an error if the edge +// was not loaded in eager-loading, or loaded but was not found. +func (e WorkflowEdges) LatestWorkflowRunOrErr() (*WorkflowRun, error) { + if e.LatestWorkflowRun != nil { + return e.LatestWorkflowRun, nil + } else if e.loadedTypes[6] { + return nil, &NotFoundError{label: workflowrun.Label} + } + return nil, &NotLoadedError{edge: "latest_workflow_run"} +} + // ReferrersOrErr returns the Referrers value or an error if the edge // was not loaded in eager-loading. func (e WorkflowEdges) ReferrersOrErr() ([]*Referrer, error) { - if e.loadedTypes[6] { + if e.loadedTypes[7] { return e.Referrers, nil } return nil, &NotLoadedError{edge: "referrers"} @@ -143,13 +161,15 @@ func (*Workflow) scanValues(columns []string) ([]any, error) { values := make([]any, len(columns)) for i := range columns { switch columns[i] { + case workflow.FieldLatestRun: + values[i] = &sql.NullScanner{S: new(uuid.UUID)} case workflow.FieldPublic: values[i] = new(sql.NullBool) case workflow.FieldRunsCount: values[i] = new(sql.NullInt64) case workflow.FieldName, workflow.FieldProjectOld, workflow.FieldTeam, workflow.FieldDescription: values[i] = new(sql.NullString) - case workflow.FieldCreatedAt, workflow.FieldDeletedAt: + case workflow.FieldCreatedAt, workflow.FieldUpdatedAt, workflow.FieldDeletedAt: values[i] = new(sql.NullTime) case workflow.FieldID, workflow.FieldOrganizationID, workflow.FieldProjectID: values[i] = new(uuid.UUID) @@ -206,6 +226,12 @@ func (w *Workflow) assignValues(columns []string, values []any) error { } else if value.Valid { w.CreatedAt = value.Time } + case workflow.FieldUpdatedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field updated_at", values[i]) + } else if value.Valid { + w.UpdatedAt = value.Time + } case workflow.FieldDeletedAt: if value, ok := values[i].(*sql.NullTime); !ok { return fmt.Errorf("unexpected type %T for field deleted_at", values[i]) @@ -230,6 +256,13 @@ func (w *Workflow) assignValues(columns []string, values []any) error { } else if value != nil { w.ProjectID = *value } + case workflow.FieldLatestRun: + if value, ok := values[i].(*sql.NullScanner); !ok { + return fmt.Errorf("unexpected type %T for field latest_run", values[i]) + } else if value.Valid { + w.LatestRun = new(uuid.UUID) + *w.LatestRun = *value.S.(*uuid.UUID) + } case workflow.FieldDescription: if value, ok := values[i].(*sql.NullString); !ok { return fmt.Errorf("unexpected type %T for field description", values[i]) @@ -286,6 +319,11 @@ func (w *Workflow) QueryProject() *ProjectQuery { return NewWorkflowClient(w.config).QueryProject(w) } +// QueryLatestWorkflowRun queries the "latest_workflow_run" edge of the Workflow entity. +func (w *Workflow) QueryLatestWorkflowRun() *WorkflowRunQuery { + return NewWorkflowClient(w.config).QueryLatestWorkflowRun(w) +} + // QueryReferrers queries the "referrers" edge of the Workflow entity. func (w *Workflow) QueryReferrers() *ReferrerQuery { return NewWorkflowClient(w.config).QueryReferrers(w) @@ -329,6 +367,9 @@ func (w *Workflow) String() string { builder.WriteString("created_at=") builder.WriteString(w.CreatedAt.Format(time.ANSIC)) builder.WriteString(", ") + builder.WriteString("updated_at=") + builder.WriteString(w.UpdatedAt.Format(time.ANSIC)) + builder.WriteString(", ") builder.WriteString("deleted_at=") builder.WriteString(w.DeletedAt.Format(time.ANSIC)) builder.WriteString(", ") @@ -341,6 +382,11 @@ func (w *Workflow) String() string { builder.WriteString("project_id=") builder.WriteString(fmt.Sprintf("%v", w.ProjectID)) builder.WriteString(", ") + if v := w.LatestRun; v != nil { + builder.WriteString("latest_run=") + builder.WriteString(fmt.Sprintf("%v", *v)) + } + builder.WriteString(", ") builder.WriteString("description=") builder.WriteString(w.Description) builder.WriteByte(')') diff --git a/app/controlplane/pkg/data/ent/workflow/where.go b/app/controlplane/pkg/data/ent/workflow/where.go index 329a3b3d3..507bf2f8e 100644 --- a/app/controlplane/pkg/data/ent/workflow/where.go +++ b/app/controlplane/pkg/data/ent/workflow/where.go @@ -81,6 +81,11 @@ func CreatedAt(v time.Time) predicate.Workflow { return predicate.Workflow(sql.FieldEQ(FieldCreatedAt, v)) } +// UpdatedAt applies equality check predicate on the "updated_at" field. It's identical to UpdatedAtEQ. +func UpdatedAt(v time.Time) predicate.Workflow { + return predicate.Workflow(sql.FieldEQ(FieldUpdatedAt, v)) +} + // DeletedAt applies equality check predicate on the "deleted_at" field. It's identical to DeletedAtEQ. func DeletedAt(v time.Time) predicate.Workflow { return predicate.Workflow(sql.FieldEQ(FieldDeletedAt, v)) @@ -101,6 +106,11 @@ func ProjectID(v uuid.UUID) predicate.Workflow { return predicate.Workflow(sql.FieldEQ(FieldProjectID, v)) } +// LatestRun applies equality check predicate on the "latest_run" field. It's identical to LatestRunEQ. +func LatestRun(v uuid.UUID) predicate.Workflow { + return predicate.Workflow(sql.FieldEQ(FieldLatestRun, v)) +} + // Description applies equality check predicate on the "description" field. It's identical to DescriptionEQ. func Description(v string) predicate.Workflow { return predicate.Workflow(sql.FieldEQ(FieldDescription, v)) @@ -401,6 +411,46 @@ func CreatedAtLTE(v time.Time) predicate.Workflow { return predicate.Workflow(sql.FieldLTE(FieldCreatedAt, v)) } +// UpdatedAtEQ applies the EQ predicate on the "updated_at" field. +func UpdatedAtEQ(v time.Time) predicate.Workflow { + return predicate.Workflow(sql.FieldEQ(FieldUpdatedAt, v)) +} + +// UpdatedAtNEQ applies the NEQ predicate on the "updated_at" field. +func UpdatedAtNEQ(v time.Time) predicate.Workflow { + return predicate.Workflow(sql.FieldNEQ(FieldUpdatedAt, v)) +} + +// UpdatedAtIn applies the In predicate on the "updated_at" field. +func UpdatedAtIn(vs ...time.Time) predicate.Workflow { + return predicate.Workflow(sql.FieldIn(FieldUpdatedAt, vs...)) +} + +// UpdatedAtNotIn applies the NotIn predicate on the "updated_at" field. +func UpdatedAtNotIn(vs ...time.Time) predicate.Workflow { + return predicate.Workflow(sql.FieldNotIn(FieldUpdatedAt, vs...)) +} + +// UpdatedAtGT applies the GT predicate on the "updated_at" field. +func UpdatedAtGT(v time.Time) predicate.Workflow { + return predicate.Workflow(sql.FieldGT(FieldUpdatedAt, v)) +} + +// UpdatedAtGTE applies the GTE predicate on the "updated_at" field. +func UpdatedAtGTE(v time.Time) predicate.Workflow { + return predicate.Workflow(sql.FieldGTE(FieldUpdatedAt, v)) +} + +// UpdatedAtLT applies the LT predicate on the "updated_at" field. +func UpdatedAtLT(v time.Time) predicate.Workflow { + return predicate.Workflow(sql.FieldLT(FieldUpdatedAt, v)) +} + +// UpdatedAtLTE applies the LTE predicate on the "updated_at" field. +func UpdatedAtLTE(v time.Time) predicate.Workflow { + return predicate.Workflow(sql.FieldLTE(FieldUpdatedAt, v)) +} + // DeletedAtEQ applies the EQ predicate on the "deleted_at" field. func DeletedAtEQ(v time.Time) predicate.Workflow { return predicate.Workflow(sql.FieldEQ(FieldDeletedAt, v)) @@ -501,6 +551,36 @@ func ProjectIDNotIn(vs ...uuid.UUID) predicate.Workflow { return predicate.Workflow(sql.FieldNotIn(FieldProjectID, vs...)) } +// LatestRunEQ applies the EQ predicate on the "latest_run" field. +func LatestRunEQ(v uuid.UUID) predicate.Workflow { + return predicate.Workflow(sql.FieldEQ(FieldLatestRun, v)) +} + +// LatestRunNEQ applies the NEQ predicate on the "latest_run" field. +func LatestRunNEQ(v uuid.UUID) predicate.Workflow { + return predicate.Workflow(sql.FieldNEQ(FieldLatestRun, v)) +} + +// LatestRunIn applies the In predicate on the "latest_run" field. +func LatestRunIn(vs ...uuid.UUID) predicate.Workflow { + return predicate.Workflow(sql.FieldIn(FieldLatestRun, vs...)) +} + +// LatestRunNotIn applies the NotIn predicate on the "latest_run" field. +func LatestRunNotIn(vs ...uuid.UUID) predicate.Workflow { + return predicate.Workflow(sql.FieldNotIn(FieldLatestRun, vs...)) +} + +// LatestRunIsNil applies the IsNil predicate on the "latest_run" field. +func LatestRunIsNil() predicate.Workflow { + return predicate.Workflow(sql.FieldIsNull(FieldLatestRun)) +} + +// LatestRunNotNil applies the NotNil predicate on the "latest_run" field. +func LatestRunNotNil() predicate.Workflow { + return predicate.Workflow(sql.FieldNotNull(FieldLatestRun)) +} + // DescriptionEQ applies the EQ predicate on the "description" field. func DescriptionEQ(v string) predicate.Workflow { return predicate.Workflow(sql.FieldEQ(FieldDescription, v)) @@ -714,6 +794,29 @@ func HasProjectWith(preds ...predicate.Project) predicate.Workflow { }) } +// HasLatestWorkflowRun applies the HasEdge predicate on the "latest_workflow_run" edge. +func HasLatestWorkflowRun() predicate.Workflow { + return predicate.Workflow(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, LatestWorkflowRunTable, LatestWorkflowRunColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasLatestWorkflowRunWith applies the HasEdge predicate on the "latest_workflow_run" edge with a given conditions (other predicates). +func HasLatestWorkflowRunWith(preds ...predicate.WorkflowRun) predicate.Workflow { + return predicate.Workflow(func(s *sql.Selector) { + step := newLatestWorkflowRunStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + // HasReferrers applies the HasEdge predicate on the "referrers" edge. func HasReferrers() predicate.Workflow { return predicate.Workflow(func(s *sql.Selector) { diff --git a/app/controlplane/pkg/data/ent/workflow/workflow.go b/app/controlplane/pkg/data/ent/workflow/workflow.go index 867fa3048..8d3ef35e3 100644 --- a/app/controlplane/pkg/data/ent/workflow/workflow.go +++ b/app/controlplane/pkg/data/ent/workflow/workflow.go @@ -25,6 +25,8 @@ const ( FieldRunsCount = "runs_count" // FieldCreatedAt holds the string denoting the created_at field in the database. FieldCreatedAt = "created_at" + // FieldUpdatedAt holds the string denoting the updated_at field in the database. + FieldUpdatedAt = "updated_at" // FieldDeletedAt holds the string denoting the deleted_at field in the database. FieldDeletedAt = "deleted_at" // FieldPublic holds the string denoting the public field in the database. @@ -33,6 +35,8 @@ const ( FieldOrganizationID = "organization_id" // FieldProjectID holds the string denoting the project_id field in the database. FieldProjectID = "project_id" + // FieldLatestRun holds the string denoting the latest_run field in the database. + FieldLatestRun = "latest_run" // FieldDescription holds the string denoting the description field in the database. FieldDescription = "description" // EdgeRobotaccounts holds the string denoting the robotaccounts edge name in mutations. @@ -47,6 +51,8 @@ const ( EdgeIntegrationAttachments = "integration_attachments" // EdgeProject holds the string denoting the project edge name in mutations. EdgeProject = "project" + // EdgeLatestWorkflowRun holds the string denoting the latest_workflow_run edge name in mutations. + EdgeLatestWorkflowRun = "latest_workflow_run" // EdgeReferrers holds the string denoting the referrers edge name in mutations. EdgeReferrers = "referrers" // Table holds the table name of the workflow in the database. @@ -93,6 +99,13 @@ const ( ProjectInverseTable = "projects" // ProjectColumn is the table column denoting the project relation/edge. ProjectColumn = "project_id" + // LatestWorkflowRunTable is the table that holds the latest_workflow_run relation/edge. + LatestWorkflowRunTable = "workflows" + // LatestWorkflowRunInverseTable is the table name for the WorkflowRun entity. + // It exists in this package in order to avoid circular dependency with the "workflowrun" package. + LatestWorkflowRunInverseTable = "workflow_runs" + // LatestWorkflowRunColumn is the table column denoting the latest_workflow_run relation/edge. + LatestWorkflowRunColumn = "latest_run" // ReferrersTable is the table that holds the referrers relation/edge. The primary key declared below. ReferrersTable = "referrer_workflows" // ReferrersInverseTable is the table name for the Referrer entity. @@ -108,10 +121,12 @@ var Columns = []string{ FieldTeam, FieldRunsCount, FieldCreatedAt, + FieldUpdatedAt, FieldDeletedAt, FieldPublic, FieldOrganizationID, FieldProjectID, + FieldLatestRun, FieldDescription, } @@ -147,6 +162,8 @@ var ( DefaultRunsCount int // DefaultCreatedAt holds the default value on creation for the "created_at" field. DefaultCreatedAt func() time.Time + // DefaultUpdatedAt holds the default value on creation for the "updated_at" field. + DefaultUpdatedAt func() time.Time // DefaultPublic holds the default value on creation for the "public" field. DefaultPublic bool // DefaultID holds the default value on creation for the "id" field. @@ -186,6 +203,11 @@ func ByCreatedAt(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldCreatedAt, opts...).ToFunc() } +// ByUpdatedAt orders the results by the updated_at field. +func ByUpdatedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldUpdatedAt, opts...).ToFunc() +} + // ByDeletedAt orders the results by the deleted_at field. func ByDeletedAt(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldDeletedAt, opts...).ToFunc() @@ -206,6 +228,11 @@ func ByProjectID(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldProjectID, opts...).ToFunc() } +// ByLatestRun orders the results by the latest_run field. +func ByLatestRun(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldLatestRun, opts...).ToFunc() +} + // ByDescription orders the results by the description field. func ByDescription(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldDescription, opts...).ToFunc() @@ -274,6 +301,13 @@ func ByProjectField(field string, opts ...sql.OrderTermOption) OrderOption { } } +// ByLatestWorkflowRunField orders the results by latest_workflow_run field. +func ByLatestWorkflowRunField(field string, opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newLatestWorkflowRunStep(), sql.OrderByField(field, opts...)) + } +} + // ByReferrersCount orders the results by referrers count. func ByReferrersCount(opts ...sql.OrderTermOption) OrderOption { return func(s *sql.Selector) { @@ -329,6 +363,13 @@ func newProjectStep() *sqlgraph.Step { sqlgraph.Edge(sqlgraph.M2O, true, ProjectTable, ProjectColumn), ) } +func newLatestWorkflowRunStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(LatestWorkflowRunInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, LatestWorkflowRunTable, LatestWorkflowRunColumn), + ) +} func newReferrersStep() *sqlgraph.Step { return sqlgraph.NewStep( sqlgraph.From(Table, FieldID), diff --git a/app/controlplane/pkg/data/ent/workflow_create.go b/app/controlplane/pkg/data/ent/workflow_create.go index 66d9f3424..b9460ba18 100644 --- a/app/controlplane/pkg/data/ent/workflow_create.go +++ b/app/controlplane/pkg/data/ent/workflow_create.go @@ -93,6 +93,20 @@ func (wc *WorkflowCreate) SetNillableCreatedAt(t *time.Time) *WorkflowCreate { return wc } +// SetUpdatedAt sets the "updated_at" field. +func (wc *WorkflowCreate) SetUpdatedAt(t time.Time) *WorkflowCreate { + wc.mutation.SetUpdatedAt(t) + return wc +} + +// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil. +func (wc *WorkflowCreate) SetNillableUpdatedAt(t *time.Time) *WorkflowCreate { + if t != nil { + wc.SetUpdatedAt(*t) + } + return wc +} + // SetDeletedAt sets the "deleted_at" field. func (wc *WorkflowCreate) SetDeletedAt(t time.Time) *WorkflowCreate { wc.mutation.SetDeletedAt(t) @@ -133,6 +147,20 @@ func (wc *WorkflowCreate) SetProjectID(u uuid.UUID) *WorkflowCreate { return wc } +// SetLatestRun sets the "latest_run" field. +func (wc *WorkflowCreate) SetLatestRun(u uuid.UUID) *WorkflowCreate { + wc.mutation.SetLatestRun(u) + return wc +} + +// SetNillableLatestRun sets the "latest_run" field if the given value is not nil. +func (wc *WorkflowCreate) SetNillableLatestRun(u *uuid.UUID) *WorkflowCreate { + if u != nil { + wc.SetLatestRun(*u) + } + return wc +} + // SetDescription sets the "description" field. func (wc *WorkflowCreate) SetDescription(s string) *WorkflowCreate { wc.mutation.SetDescription(s) @@ -227,6 +255,25 @@ func (wc *WorkflowCreate) SetProject(p *Project) *WorkflowCreate { return wc.SetProjectID(p.ID) } +// SetLatestWorkflowRunID sets the "latest_workflow_run" edge to the WorkflowRun entity by ID. +func (wc *WorkflowCreate) SetLatestWorkflowRunID(id uuid.UUID) *WorkflowCreate { + wc.mutation.SetLatestWorkflowRunID(id) + return wc +} + +// SetNillableLatestWorkflowRunID sets the "latest_workflow_run" edge to the WorkflowRun entity by ID if the given value is not nil. +func (wc *WorkflowCreate) SetNillableLatestWorkflowRunID(id *uuid.UUID) *WorkflowCreate { + if id != nil { + wc = wc.SetLatestWorkflowRunID(*id) + } + return wc +} + +// SetLatestWorkflowRun sets the "latest_workflow_run" edge to the WorkflowRun entity. +func (wc *WorkflowCreate) SetLatestWorkflowRun(w *WorkflowRun) *WorkflowCreate { + return wc.SetLatestWorkflowRunID(w.ID) +} + // AddReferrerIDs adds the "referrers" edge to the Referrer entity by IDs. func (wc *WorkflowCreate) AddReferrerIDs(ids ...uuid.UUID) *WorkflowCreate { wc.mutation.AddReferrerIDs(ids...) @@ -285,6 +332,10 @@ func (wc *WorkflowCreate) defaults() { v := workflow.DefaultCreatedAt() wc.mutation.SetCreatedAt(v) } + if _, ok := wc.mutation.UpdatedAt(); !ok { + v := workflow.DefaultUpdatedAt() + wc.mutation.SetUpdatedAt(v) + } if _, ok := wc.mutation.Public(); !ok { v := workflow.DefaultPublic wc.mutation.SetPublic(v) @@ -306,6 +357,9 @@ func (wc *WorkflowCreate) check() error { if _, ok := wc.mutation.CreatedAt(); !ok { return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "Workflow.created_at"`)} } + if _, ok := wc.mutation.UpdatedAt(); !ok { + return &ValidationError{Name: "updated_at", err: errors.New(`ent: missing required field "Workflow.updated_at"`)} + } if _, ok := wc.mutation.Public(); !ok { return &ValidationError{Name: "public", err: errors.New(`ent: missing required field "Workflow.public"`)} } @@ -380,6 +434,10 @@ func (wc *WorkflowCreate) createSpec() (*Workflow, *sqlgraph.CreateSpec) { _spec.SetField(workflow.FieldCreatedAt, field.TypeTime, value) _node.CreatedAt = value } + if value, ok := wc.mutation.UpdatedAt(); ok { + _spec.SetField(workflow.FieldUpdatedAt, field.TypeTime, value) + _node.UpdatedAt = value + } if value, ok := wc.mutation.DeletedAt(); ok { _spec.SetField(workflow.FieldDeletedAt, field.TypeTime, value) _node.DeletedAt = value @@ -491,6 +549,23 @@ func (wc *WorkflowCreate) createSpec() (*Workflow, *sqlgraph.CreateSpec) { _node.ProjectID = nodes[0] _spec.Edges = append(_spec.Edges, edge) } + if nodes := wc.mutation.LatestWorkflowRunIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: workflow.LatestWorkflowRunTable, + Columns: []string{workflow.LatestWorkflowRunColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(workflowrun.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _node.LatestRun = &nodes[0] + _spec.Edges = append(_spec.Edges, edge) + } if nodes := wc.mutation.ReferrersIDs(); len(nodes) > 0 { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.M2M, @@ -613,6 +688,18 @@ func (u *WorkflowUpsert) AddRunsCount(v int) *WorkflowUpsert { return u } +// SetUpdatedAt sets the "updated_at" field. +func (u *WorkflowUpsert) SetUpdatedAt(v time.Time) *WorkflowUpsert { + u.Set(workflow.FieldUpdatedAt, v) + return u +} + +// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create. +func (u *WorkflowUpsert) UpdateUpdatedAt() *WorkflowUpsert { + u.SetExcluded(workflow.FieldUpdatedAt) + return u +} + // SetDeletedAt sets the "deleted_at" field. func (u *WorkflowUpsert) SetDeletedAt(v time.Time) *WorkflowUpsert { u.Set(workflow.FieldDeletedAt, v) @@ -667,6 +754,24 @@ func (u *WorkflowUpsert) UpdateProjectID() *WorkflowUpsert { return u } +// SetLatestRun sets the "latest_run" field. +func (u *WorkflowUpsert) SetLatestRun(v uuid.UUID) *WorkflowUpsert { + u.Set(workflow.FieldLatestRun, v) + return u +} + +// UpdateLatestRun sets the "latest_run" field to the value that was provided on create. +func (u *WorkflowUpsert) UpdateLatestRun() *WorkflowUpsert { + u.SetExcluded(workflow.FieldLatestRun) + return u +} + +// ClearLatestRun clears the value of the "latest_run" field. +func (u *WorkflowUpsert) ClearLatestRun() *WorkflowUpsert { + u.SetNull(workflow.FieldLatestRun) + return u +} + // SetDescription sets the "description" field. func (u *WorkflowUpsert) SetDescription(v string) *WorkflowUpsert { u.Set(workflow.FieldDescription, v) @@ -802,6 +907,20 @@ func (u *WorkflowUpsertOne) UpdateRunsCount() *WorkflowUpsertOne { }) } +// SetUpdatedAt sets the "updated_at" field. +func (u *WorkflowUpsertOne) SetUpdatedAt(v time.Time) *WorkflowUpsertOne { + return u.Update(func(s *WorkflowUpsert) { + s.SetUpdatedAt(v) + }) +} + +// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create. +func (u *WorkflowUpsertOne) UpdateUpdatedAt() *WorkflowUpsertOne { + return u.Update(func(s *WorkflowUpsert) { + s.UpdateUpdatedAt() + }) +} + // SetDeletedAt sets the "deleted_at" field. func (u *WorkflowUpsertOne) SetDeletedAt(v time.Time) *WorkflowUpsertOne { return u.Update(func(s *WorkflowUpsert) { @@ -865,6 +984,27 @@ func (u *WorkflowUpsertOne) UpdateProjectID() *WorkflowUpsertOne { }) } +// SetLatestRun sets the "latest_run" field. +func (u *WorkflowUpsertOne) SetLatestRun(v uuid.UUID) *WorkflowUpsertOne { + return u.Update(func(s *WorkflowUpsert) { + s.SetLatestRun(v) + }) +} + +// UpdateLatestRun sets the "latest_run" field to the value that was provided on create. +func (u *WorkflowUpsertOne) UpdateLatestRun() *WorkflowUpsertOne { + return u.Update(func(s *WorkflowUpsert) { + s.UpdateLatestRun() + }) +} + +// ClearLatestRun clears the value of the "latest_run" field. +func (u *WorkflowUpsertOne) ClearLatestRun() *WorkflowUpsertOne { + return u.Update(func(s *WorkflowUpsert) { + s.ClearLatestRun() + }) +} + // SetDescription sets the "description" field. func (u *WorkflowUpsertOne) SetDescription(v string) *WorkflowUpsertOne { return u.Update(func(s *WorkflowUpsert) { @@ -1170,6 +1310,20 @@ func (u *WorkflowUpsertBulk) UpdateRunsCount() *WorkflowUpsertBulk { }) } +// SetUpdatedAt sets the "updated_at" field. +func (u *WorkflowUpsertBulk) SetUpdatedAt(v time.Time) *WorkflowUpsertBulk { + return u.Update(func(s *WorkflowUpsert) { + s.SetUpdatedAt(v) + }) +} + +// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create. +func (u *WorkflowUpsertBulk) UpdateUpdatedAt() *WorkflowUpsertBulk { + return u.Update(func(s *WorkflowUpsert) { + s.UpdateUpdatedAt() + }) +} + // SetDeletedAt sets the "deleted_at" field. func (u *WorkflowUpsertBulk) SetDeletedAt(v time.Time) *WorkflowUpsertBulk { return u.Update(func(s *WorkflowUpsert) { @@ -1233,6 +1387,27 @@ func (u *WorkflowUpsertBulk) UpdateProjectID() *WorkflowUpsertBulk { }) } +// SetLatestRun sets the "latest_run" field. +func (u *WorkflowUpsertBulk) SetLatestRun(v uuid.UUID) *WorkflowUpsertBulk { + return u.Update(func(s *WorkflowUpsert) { + s.SetLatestRun(v) + }) +} + +// UpdateLatestRun sets the "latest_run" field to the value that was provided on create. +func (u *WorkflowUpsertBulk) UpdateLatestRun() *WorkflowUpsertBulk { + return u.Update(func(s *WorkflowUpsert) { + s.UpdateLatestRun() + }) +} + +// ClearLatestRun clears the value of the "latest_run" field. +func (u *WorkflowUpsertBulk) ClearLatestRun() *WorkflowUpsertBulk { + return u.Update(func(s *WorkflowUpsert) { + s.ClearLatestRun() + }) +} + // SetDescription sets the "description" field. func (u *WorkflowUpsertBulk) SetDescription(v string) *WorkflowUpsertBulk { return u.Update(func(s *WorkflowUpsert) { diff --git a/app/controlplane/pkg/data/ent/workflow_query.go b/app/controlplane/pkg/data/ent/workflow_query.go index 7d101ff0c..b2e5dff05 100644 --- a/app/controlplane/pkg/data/ent/workflow_query.go +++ b/app/controlplane/pkg/data/ent/workflow_query.go @@ -38,6 +38,7 @@ type WorkflowQuery struct { withContract *WorkflowContractQuery withIntegrationAttachments *IntegrationAttachmentQuery withProject *ProjectQuery + withLatestWorkflowRun *WorkflowRunQuery withReferrers *ReferrerQuery withFKs bool modifiers []func(*sql.Selector) @@ -209,6 +210,28 @@ func (wq *WorkflowQuery) QueryProject() *ProjectQuery { return query } +// QueryLatestWorkflowRun chains the current query on the "latest_workflow_run" edge. +func (wq *WorkflowQuery) QueryLatestWorkflowRun() *WorkflowRunQuery { + query := (&WorkflowRunClient{config: wq.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := wq.prepareQuery(ctx); err != nil { + return nil, err + } + selector := wq.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(workflow.Table, workflow.FieldID, selector), + sqlgraph.To(workflowrun.Table, workflowrun.FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, workflow.LatestWorkflowRunTable, workflow.LatestWorkflowRunColumn), + ) + fromU = sqlgraph.SetNeighbors(wq.driver.Dialect(), step) + return fromU, nil + } + return query +} + // QueryReferrers chains the current query on the "referrers" edge. func (wq *WorkflowQuery) QueryReferrers() *ReferrerQuery { query := (&ReferrerClient{config: wq.config}).Query() @@ -429,6 +452,7 @@ func (wq *WorkflowQuery) Clone() *WorkflowQuery { withContract: wq.withContract.Clone(), withIntegrationAttachments: wq.withIntegrationAttachments.Clone(), withProject: wq.withProject.Clone(), + withLatestWorkflowRun: wq.withLatestWorkflowRun.Clone(), withReferrers: wq.withReferrers.Clone(), // clone intermediate query. sql: wq.sql.Clone(), @@ -502,6 +526,17 @@ func (wq *WorkflowQuery) WithProject(opts ...func(*ProjectQuery)) *WorkflowQuery return wq } +// WithLatestWorkflowRun tells the query-builder to eager-load the nodes that are connected to +// the "latest_workflow_run" edge. The optional arguments are used to configure the query builder of the edge. +func (wq *WorkflowQuery) WithLatestWorkflowRun(opts ...func(*WorkflowRunQuery)) *WorkflowQuery { + query := (&WorkflowRunClient{config: wq.config}).Query() + for _, opt := range opts { + opt(query) + } + wq.withLatestWorkflowRun = query + return wq +} + // WithReferrers tells the query-builder to eager-load the nodes that are connected to // the "referrers" edge. The optional arguments are used to configure the query builder of the edge. func (wq *WorkflowQuery) WithReferrers(opts ...func(*ReferrerQuery)) *WorkflowQuery { @@ -592,13 +627,14 @@ func (wq *WorkflowQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Wor nodes = []*Workflow{} withFKs = wq.withFKs _spec = wq.querySpec() - loadedTypes = [7]bool{ + loadedTypes = [8]bool{ wq.withRobotaccounts != nil, wq.withWorkflowruns != nil, wq.withOrganization != nil, wq.withContract != nil, wq.withIntegrationAttachments != nil, wq.withProject != nil, + wq.withLatestWorkflowRun != nil, wq.withReferrers != nil, } ) @@ -670,6 +706,12 @@ func (wq *WorkflowQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Wor return nil, err } } + if query := wq.withLatestWorkflowRun; query != nil { + if err := wq.loadLatestWorkflowRun(ctx, query, nodes, nil, + func(n *Workflow, e *WorkflowRun) { n.Edges.LatestWorkflowRun = e }); err != nil { + return nil, err + } + } if query := wq.withReferrers; query != nil { if err := wq.loadReferrers(ctx, query, nodes, func(n *Workflow) { n.Edges.Referrers = []*Referrer{} }, @@ -863,6 +905,38 @@ func (wq *WorkflowQuery) loadProject(ctx context.Context, query *ProjectQuery, n } return nil } +func (wq *WorkflowQuery) loadLatestWorkflowRun(ctx context.Context, query *WorkflowRunQuery, nodes []*Workflow, init func(*Workflow), assign func(*Workflow, *WorkflowRun)) error { + ids := make([]uuid.UUID, 0, len(nodes)) + nodeids := make(map[uuid.UUID][]*Workflow) + for i := range nodes { + if nodes[i].LatestRun == nil { + continue + } + fk := *nodes[i].LatestRun + if _, ok := nodeids[fk]; !ok { + ids = append(ids, fk) + } + nodeids[fk] = append(nodeids[fk], nodes[i]) + } + if len(ids) == 0 { + return nil + } + query.Where(workflowrun.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return fmt.Errorf(`unexpected foreign-key "latest_run" returned %v`, n.ID) + } + for i := range nodes { + assign(nodes[i], n) + } + } + return nil +} func (wq *WorkflowQuery) loadReferrers(ctx context.Context, query *ReferrerQuery, nodes []*Workflow, init func(*Workflow), assign func(*Workflow, *Referrer)) error { edgeIDs := make([]driver.Value, len(nodes)) byID := make(map[uuid.UUID]*Workflow) @@ -959,6 +1033,9 @@ func (wq *WorkflowQuery) querySpec() *sqlgraph.QuerySpec { if wq.withProject != nil { _spec.Node.AddColumnOnce(workflow.FieldProjectID) } + if wq.withLatestWorkflowRun != nil { + _spec.Node.AddColumnOnce(workflow.FieldLatestRun) + } } if ps := wq.predicates; len(ps) > 0 { _spec.Predicate = func(selector *sql.Selector) { diff --git a/app/controlplane/pkg/data/ent/workflow_update.go b/app/controlplane/pkg/data/ent/workflow_update.go index 8b1026b32..b5166cdf7 100644 --- a/app/controlplane/pkg/data/ent/workflow_update.go +++ b/app/controlplane/pkg/data/ent/workflow_update.go @@ -98,6 +98,20 @@ func (wu *WorkflowUpdate) AddRunsCount(i int) *WorkflowUpdate { return wu } +// SetUpdatedAt sets the "updated_at" field. +func (wu *WorkflowUpdate) SetUpdatedAt(t time.Time) *WorkflowUpdate { + wu.mutation.SetUpdatedAt(t) + return wu +} + +// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil. +func (wu *WorkflowUpdate) SetNillableUpdatedAt(t *time.Time) *WorkflowUpdate { + if t != nil { + wu.SetUpdatedAt(*t) + } + return wu +} + // SetDeletedAt sets the "deleted_at" field. func (wu *WorkflowUpdate) SetDeletedAt(t time.Time) *WorkflowUpdate { wu.mutation.SetDeletedAt(t) @@ -160,6 +174,26 @@ func (wu *WorkflowUpdate) SetNillableProjectID(u *uuid.UUID) *WorkflowUpdate { return wu } +// SetLatestRun sets the "latest_run" field. +func (wu *WorkflowUpdate) SetLatestRun(u uuid.UUID) *WorkflowUpdate { + wu.mutation.SetLatestRun(u) + return wu +} + +// SetNillableLatestRun sets the "latest_run" field if the given value is not nil. +func (wu *WorkflowUpdate) SetNillableLatestRun(u *uuid.UUID) *WorkflowUpdate { + if u != nil { + wu.SetLatestRun(*u) + } + return wu +} + +// ClearLatestRun clears the value of the "latest_run" field. +func (wu *WorkflowUpdate) ClearLatestRun() *WorkflowUpdate { + wu.mutation.ClearLatestRun() + return wu +} + // SetDescription sets the "description" field. func (wu *WorkflowUpdate) SetDescription(s string) *WorkflowUpdate { wu.mutation.SetDescription(s) @@ -246,6 +280,25 @@ func (wu *WorkflowUpdate) SetProject(p *Project) *WorkflowUpdate { return wu.SetProjectID(p.ID) } +// SetLatestWorkflowRunID sets the "latest_workflow_run" edge to the WorkflowRun entity by ID. +func (wu *WorkflowUpdate) SetLatestWorkflowRunID(id uuid.UUID) *WorkflowUpdate { + wu.mutation.SetLatestWorkflowRunID(id) + return wu +} + +// SetNillableLatestWorkflowRunID sets the "latest_workflow_run" edge to the WorkflowRun entity by ID if the given value is not nil. +func (wu *WorkflowUpdate) SetNillableLatestWorkflowRunID(id *uuid.UUID) *WorkflowUpdate { + if id != nil { + wu = wu.SetLatestWorkflowRunID(*id) + } + return wu +} + +// SetLatestWorkflowRun sets the "latest_workflow_run" edge to the WorkflowRun entity. +func (wu *WorkflowUpdate) SetLatestWorkflowRun(w *WorkflowRun) *WorkflowUpdate { + return wu.SetLatestWorkflowRunID(w.ID) +} + // AddReferrerIDs adds the "referrers" edge to the Referrer entity by IDs. func (wu *WorkflowUpdate) AddReferrerIDs(ids ...uuid.UUID) *WorkflowUpdate { wu.mutation.AddReferrerIDs(ids...) @@ -347,6 +400,12 @@ func (wu *WorkflowUpdate) ClearProject() *WorkflowUpdate { return wu } +// ClearLatestWorkflowRun clears the "latest_workflow_run" edge to the WorkflowRun entity. +func (wu *WorkflowUpdate) ClearLatestWorkflowRun() *WorkflowUpdate { + wu.mutation.ClearLatestWorkflowRun() + return wu +} + // ClearReferrers clears all "referrers" edges to the Referrer entity. func (wu *WorkflowUpdate) ClearReferrers() *WorkflowUpdate { wu.mutation.ClearReferrers() @@ -445,6 +504,9 @@ func (wu *WorkflowUpdate) sqlSave(ctx context.Context) (n int, err error) { if value, ok := wu.mutation.AddedRunsCount(); ok { _spec.AddField(workflow.FieldRunsCount, field.TypeInt, value) } + if value, ok := wu.mutation.UpdatedAt(); ok { + _spec.SetField(workflow.FieldUpdatedAt, field.TypeTime, value) + } if value, ok := wu.mutation.DeletedAt(); ok { _spec.SetField(workflow.FieldDeletedAt, field.TypeTime, value) } @@ -682,6 +744,35 @@ func (wu *WorkflowUpdate) sqlSave(ctx context.Context) (n int, err error) { } _spec.Edges.Add = append(_spec.Edges.Add, edge) } + if wu.mutation.LatestWorkflowRunCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: workflow.LatestWorkflowRunTable, + Columns: []string{workflow.LatestWorkflowRunColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(workflowrun.FieldID, field.TypeUUID), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := wu.mutation.LatestWorkflowRunIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: workflow.LatestWorkflowRunTable, + Columns: []string{workflow.LatestWorkflowRunColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(workflowrun.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } if wu.mutation.ReferrersCleared() { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.M2M, @@ -810,6 +901,20 @@ func (wuo *WorkflowUpdateOne) AddRunsCount(i int) *WorkflowUpdateOne { return wuo } +// SetUpdatedAt sets the "updated_at" field. +func (wuo *WorkflowUpdateOne) SetUpdatedAt(t time.Time) *WorkflowUpdateOne { + wuo.mutation.SetUpdatedAt(t) + return wuo +} + +// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil. +func (wuo *WorkflowUpdateOne) SetNillableUpdatedAt(t *time.Time) *WorkflowUpdateOne { + if t != nil { + wuo.SetUpdatedAt(*t) + } + return wuo +} + // SetDeletedAt sets the "deleted_at" field. func (wuo *WorkflowUpdateOne) SetDeletedAt(t time.Time) *WorkflowUpdateOne { wuo.mutation.SetDeletedAt(t) @@ -872,6 +977,26 @@ func (wuo *WorkflowUpdateOne) SetNillableProjectID(u *uuid.UUID) *WorkflowUpdate return wuo } +// SetLatestRun sets the "latest_run" field. +func (wuo *WorkflowUpdateOne) SetLatestRun(u uuid.UUID) *WorkflowUpdateOne { + wuo.mutation.SetLatestRun(u) + return wuo +} + +// SetNillableLatestRun sets the "latest_run" field if the given value is not nil. +func (wuo *WorkflowUpdateOne) SetNillableLatestRun(u *uuid.UUID) *WorkflowUpdateOne { + if u != nil { + wuo.SetLatestRun(*u) + } + return wuo +} + +// ClearLatestRun clears the value of the "latest_run" field. +func (wuo *WorkflowUpdateOne) ClearLatestRun() *WorkflowUpdateOne { + wuo.mutation.ClearLatestRun() + return wuo +} + // SetDescription sets the "description" field. func (wuo *WorkflowUpdateOne) SetDescription(s string) *WorkflowUpdateOne { wuo.mutation.SetDescription(s) @@ -958,6 +1083,25 @@ func (wuo *WorkflowUpdateOne) SetProject(p *Project) *WorkflowUpdateOne { return wuo.SetProjectID(p.ID) } +// SetLatestWorkflowRunID sets the "latest_workflow_run" edge to the WorkflowRun entity by ID. +func (wuo *WorkflowUpdateOne) SetLatestWorkflowRunID(id uuid.UUID) *WorkflowUpdateOne { + wuo.mutation.SetLatestWorkflowRunID(id) + return wuo +} + +// SetNillableLatestWorkflowRunID sets the "latest_workflow_run" edge to the WorkflowRun entity by ID if the given value is not nil. +func (wuo *WorkflowUpdateOne) SetNillableLatestWorkflowRunID(id *uuid.UUID) *WorkflowUpdateOne { + if id != nil { + wuo = wuo.SetLatestWorkflowRunID(*id) + } + return wuo +} + +// SetLatestWorkflowRun sets the "latest_workflow_run" edge to the WorkflowRun entity. +func (wuo *WorkflowUpdateOne) SetLatestWorkflowRun(w *WorkflowRun) *WorkflowUpdateOne { + return wuo.SetLatestWorkflowRunID(w.ID) +} + // AddReferrerIDs adds the "referrers" edge to the Referrer entity by IDs. func (wuo *WorkflowUpdateOne) AddReferrerIDs(ids ...uuid.UUID) *WorkflowUpdateOne { wuo.mutation.AddReferrerIDs(ids...) @@ -1059,6 +1203,12 @@ func (wuo *WorkflowUpdateOne) ClearProject() *WorkflowUpdateOne { return wuo } +// ClearLatestWorkflowRun clears the "latest_workflow_run" edge to the WorkflowRun entity. +func (wuo *WorkflowUpdateOne) ClearLatestWorkflowRun() *WorkflowUpdateOne { + wuo.mutation.ClearLatestWorkflowRun() + return wuo +} + // ClearReferrers clears all "referrers" edges to the Referrer entity. func (wuo *WorkflowUpdateOne) ClearReferrers() *WorkflowUpdateOne { wuo.mutation.ClearReferrers() @@ -1187,6 +1337,9 @@ func (wuo *WorkflowUpdateOne) sqlSave(ctx context.Context) (_node *Workflow, err if value, ok := wuo.mutation.AddedRunsCount(); ok { _spec.AddField(workflow.FieldRunsCount, field.TypeInt, value) } + if value, ok := wuo.mutation.UpdatedAt(); ok { + _spec.SetField(workflow.FieldUpdatedAt, field.TypeTime, value) + } if value, ok := wuo.mutation.DeletedAt(); ok { _spec.SetField(workflow.FieldDeletedAt, field.TypeTime, value) } @@ -1424,6 +1577,35 @@ func (wuo *WorkflowUpdateOne) sqlSave(ctx context.Context) (_node *Workflow, err } _spec.Edges.Add = append(_spec.Edges.Add, edge) } + if wuo.mutation.LatestWorkflowRunCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: workflow.LatestWorkflowRunTable, + Columns: []string{workflow.LatestWorkflowRunColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(workflowrun.FieldID, field.TypeUUID), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := wuo.mutation.LatestWorkflowRunIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: workflow.LatestWorkflowRunTable, + Columns: []string{workflow.LatestWorkflowRunColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(workflowrun.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } if wuo.mutation.ReferrersCleared() { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.M2M, diff --git a/app/controlplane/pkg/data/orgmetrics.go b/app/controlplane/pkg/data/orgmetrics.go index b24dfb6a7..ee9408b69 100644 --- a/app/controlplane/pkg/data/orgmetrics.go +++ b/app/controlplane/pkg/data/orgmetrics.go @@ -155,7 +155,7 @@ func (repo *OrgMetricsRepo) TopWorkflowsByRunsCount(ctx context.Context, orgID u return nil, err } - wfRes, err := entWFToBizWF(ctx, wf, nil) + wfRes, err := entWFToBizWF(ctx, wf) if err != nil { return nil, fmt.Errorf("converting entity: %w", err) } diff --git a/app/controlplane/pkg/data/workflow.go b/app/controlplane/pkg/data/workflow.go index 6352b547d..6b5bb18a2 100644 --- a/app/controlplane/pkg/data/workflow.go +++ b/app/controlplane/pkg/data/workflow.go @@ -26,7 +26,6 @@ import ( "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/integrationattachment" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/project" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/workflow" - "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/workflowrun" "github.com/go-kratos/kratos/v2/log" "github.com/google/uuid" ) @@ -144,7 +143,8 @@ func (r *WorkflowRepo) List(ctx context.Context, orgID uuid.UUID, projectID uuid QueryWorkflows(). Where(workflow.DeletedAtIsNil()). WithContract(). - WithOrganization() + WithOrganization(). + WithLatestWorkflowRun() if projectID != uuid.Nil { baseQuery = baseQuery.Where(workflow.ProjectID(projectID)) @@ -159,13 +159,7 @@ func (r *WorkflowRepo) List(ctx context.Context, orgID uuid.UUID, projectID uuid result := make([]*biz.Workflow, 0, len(workflows)) for _, wf := range workflows { - // Not efficient, we need to do a query limit = 1 grouped by workflowID - lastRun, err := getLastRun(ctx, wf) - if err != nil { - return nil, err - } - - r, err := entWFToBizWF(ctx, wf, lastRun) + r, err := entWFToBizWF(ctx, wf) if err != nil { return nil, fmt.Errorf("converting entity: %w", err) } @@ -181,7 +175,7 @@ func (r *WorkflowRepo) GetOrgScoped(ctx context.Context, orgID, workflowID uuid. workflow, err := orgScopedQuery(r.data.DB, orgID). QueryWorkflows(). Where(workflow.ID(workflowID), workflow.DeletedAtIsNil()). - WithContract().WithOrganization(). + WithContract().WithOrganization().WithLatestWorkflowRun(). Order(ent.Desc(workflow.FieldCreatedAt)). Only(ctx) @@ -192,20 +186,14 @@ func (r *WorkflowRepo) GetOrgScoped(ctx context.Context, orgID, workflowID uuid. return nil, err } - // Not efficient, we need to do a query limit = 1 grouped by workflowID - lastRun, err := getLastRun(ctx, workflow) - if err != nil { - return nil, err - } - - return entWFToBizWF(ctx, workflow, lastRun) + return entWFToBizWF(ctx, workflow) } // GetOrgScopedByProjectAndName Gets a workflow by name making sure it belongs to a given org func (r *WorkflowRepo) GetOrgScopedByProjectAndName(ctx context.Context, orgID uuid.UUID, projectName, workflowName string) (*biz.Workflow, error) { wf, err := orgScopedQuery(r.data.DB, orgID).QueryWorkflows(). Where(workflow.HasProjectWith(project.Name(projectName)), workflow.Name(workflowName), workflow.DeletedAtIsNil()). - WithContract().WithOrganization().WithProject(). + WithContract().WithOrganization().WithProject().WithLatestWorkflowRun(). Order(ent.Desc(workflow.FieldCreatedAt)). Only(ctx) @@ -216,13 +204,7 @@ func (r *WorkflowRepo) GetOrgScopedByProjectAndName(ctx context.Context, orgID u return nil, err } - // Not efficient, we need to do a query limit = 1 grouped by workflowID - lastRun, err := getLastRun(ctx, wf) - if err != nil { - return nil, err - } - - return entWFToBizWF(ctx, wf, lastRun) + return entWFToBizWF(ctx, wf) } func (r *WorkflowRepo) IncRunsCounter(ctx context.Context, workflowID uuid.UUID) error { @@ -232,7 +214,7 @@ func (r *WorkflowRepo) IncRunsCounter(ctx context.Context, workflowID uuid.UUID) func (r *WorkflowRepo) FindByID(ctx context.Context, id uuid.UUID) (*biz.Workflow, error) { workflow, err := r.data.DB.Workflow.Query(). Where(workflow.DeletedAtIsNil(), workflow.ID(id)). - WithContract().WithOrganization(). + WithContract().WithOrganization().WithLatestWorkflowRun(). Only(ctx) if err != nil { if ent.IsNotFound(err) { @@ -241,13 +223,7 @@ func (r *WorkflowRepo) FindByID(ctx context.Context, id uuid.UUID) (*biz.Workflo return nil, err } - // Not efficient, we need to do a query limit = 1 grouped by workflowID - lastRun, err := getLastRun(ctx, workflow) - if err != nil { - return nil, err - } - - return entWFToBizWF(ctx, workflow, lastRun) + return entWFToBizWF(ctx, workflow) } // Soft delete workflow, attachments and related projects (if applicable) @@ -263,7 +239,7 @@ func (r *WorkflowRepo) SoftDelete(ctx context.Context, id uuid.UUID) error { } // Soft delete workflow - wf, err := tx.Workflow.UpdateOneID(id).SetDeletedAt(time.Now()).Save(ctx) + wf, err := tx.Workflow.UpdateOneID(id).SetDeletedAt(time.Now()).SetUpdatedAt(time.Now()).Save(ctx) if err != nil { return err } @@ -282,7 +258,7 @@ func (r *WorkflowRepo) SoftDelete(ctx context.Context, id uuid.UUID) error { return tx.Commit() } -func entWFToBizWF(ctx context.Context, w *ent.Workflow, r *ent.WorkflowRun) (*biz.Workflow, error) { +func entWFToBizWF(ctx context.Context, w *ent.Workflow) (*biz.Workflow, error) { wf := &biz.Workflow{Name: w.Name, ID: w.ID, CreatedAt: toTimePtr(w.CreatedAt), Team: w.Team, RunsCounter: w.RunsCount, @@ -317,8 +293,8 @@ func entWFToBizWF(ctx context.Context, w *ent.Workflow, r *ent.WorkflowRun) (*bi wf.ContractRevisionLatest = lv.Revision } - if r != nil { - lastRun, err := entWrToBizWr(ctx, r) + if latestRun := w.Edges.LatestWorkflowRun; latestRun != nil { + lastRun, err := entWrToBizWr(ctx, latestRun) if err != nil { return nil, fmt.Errorf("converting workflow run: %w", err) } @@ -328,12 +304,3 @@ func entWFToBizWF(ctx context.Context, w *ent.Workflow, r *ent.WorkflowRun) (*bi return wf, nil } - -func getLastRun(ctx context.Context, wf *ent.Workflow) (*ent.WorkflowRun, error) { - lastRun, err := wf.QueryWorkflowruns().WithWorkflow().Order(ent.Desc(workflowrun.FieldCreatedAt)).Limit(1).All(ctx) - if len(lastRun) == 0 { - return nil, err - } - - return lastRun[0], nil -} diff --git a/app/controlplane/pkg/data/workflowcontract.go b/app/controlplane/pkg/data/workflowcontract.go index cc095f94e..fb3ce5a25 100644 --- a/app/controlplane/pkg/data/workflowcontract.go +++ b/app/controlplane/pkg/data/workflowcontract.go @@ -403,7 +403,7 @@ func getWorkflowReferences(ctx context.Context, schema *ent.WorkflowContract) ([ references := make([]*biz.WorkflowRef, 0, len(workflows)) for _, wf := range workflows { - wfBiz, err := entWFToBizWF(ctx, wf, nil) + wfBiz, err := entWFToBizWF(ctx, wf) if err != nil { return nil, err } diff --git a/app/controlplane/pkg/data/workflowrun.go b/app/controlplane/pkg/data/workflowrun.go index 8b53567ad..af58b9125 100644 --- a/app/controlplane/pkg/data/workflowrun.go +++ b/app/controlplane/pkg/data/workflowrun.go @@ -93,6 +93,15 @@ func (r *WorkflowRunRepo) Create(ctx context.Context, opts *biz.WorkflowRunRepoC return nil, err } + // Update the workflow with the last run reference + _, err = tx.Workflow.UpdateOneID(wf.ID). + SetLatestWorkflowRunID(p.ID). + SetUpdatedAt(time.Now()). + Save(ctx) + if err != nil { + return nil, fmt.Errorf("updating workflow: %w", err) + } + if err := tx.Commit(); err != nil { return nil, fmt.Errorf("committing transaction: %w", err) } @@ -287,7 +296,7 @@ func entWrToBizWr(ctx context.Context, wr *ent.WorkflowRun) (*biz.WorkflowRun, e } if wf := wr.Edges.Workflow; wf != nil { - w, err := entWFToBizWF(ctx, wf, nil) + w, err := entWFToBizWF(ctx, wf) if err != nil { return nil, fmt.Errorf("failed to convert workflow: %w", err) }