From a9481fa9bc6025478bdfdc2b4707d0f32cdce5ee Mon Sep 17 00:00:00 2001 From: restitux Date: Sat, 8 Apr 2023 14:42:23 -0600 Subject: [PATCH] Add scaffolding for cron trigger support --- admin_api/admin_api.go | 104 +++++++++++++++++++++++++++++++++++++++++ database/db.go | 10 ++++ database/func.go | 52 +++++++++++++++++++++ database/types.go | 5 ++ 4 files changed, 171 insertions(+) diff --git a/admin_api/admin_api.go b/admin_api/admin_api.go index d0d8fa0..c29bac3 100644 --- a/admin_api/admin_api.go +++ b/admin_api/admin_api.go @@ -88,6 +88,33 @@ func createSchema(db database.Database, pollChan chan uuid.UUID) (graphql.Schema }, }) + cronType := graphql.NewObject(graphql.ObjectConfig{ + Name: "Cron", + Description: "A cron available for trigger pipeline runs.", + Fields: graphql.Fields{ + "id": &graphql.Field{ + Type: graphql.NewNonNull(graphql.String), + Description: "The id of the cron.", + Resolve: func(p graphql.ResolveParams) (interface{}, error) { + if cron, ok := p.Source.(database.Cron); ok { + return cron.Id, nil + } + return nil, nil + }, + }, + "cron": &graphql.Field{ + Type: graphql.NewNonNull(graphql.String), + Description: "The cron.", + Resolve: func(p graphql.ResolveParams) (interface{}, error) { + if cron, ok := p.Source.(database.Cron); ok { + return cron.Cron, nil + } + return nil, nil + }, + }, + }, + }) + cloneCredentialType := graphql.NewObject(graphql.ObjectConfig{ Name: "CloneCredential", Description: "A credential for authenticating with the pipeline source host.", @@ -316,6 +343,16 @@ func createSchema(db database.Database, pollChan chan uuid.UUID) (graphql.Schema return []database.Secret{}, nil }, }, + "crons": &graphql.Field{ + Type: graphql.NewNonNull(graphql.NewList(graphql.NewNonNull(cronType))), + Description: "The list of crons for the pipeline.", + Resolve: func(p graphql.ResolveParams) (interface{}, error) { + if pipeline, ok := p.Source.(database.Pipeline); ok { + return db.GetCronsForPipeline(pipeline.Id) + } + return []database.Cron{}, nil + }, + }, "webhooks": &graphql.Field{ Type: graphql.NewNonNull(graphql.NewList(graphql.NewNonNull(webhookType))), Description: "The list of webhooks for the pipeline.", @@ -728,6 +765,73 @@ func createSchema(db database.Database, pollChan chan uuid.UUID) (graphql.Schema return nil, err } + pipeline, err := db.GetPipelineById(pipelineId) + if err != nil { + return nil, err + } + return pipeline, nil + }, + }, + "addCronToPipeline": &graphql.Field{ + Type: pipelineType, + Description: "Add a cron string to trigger the pipeline", + Args: graphql.FieldConfigArgument{ + "cron": &graphql.ArgumentConfig{ + Type: graphql.NewNonNull(graphql.String), + }, + "pipelineId": &graphql.ArgumentConfig{ + Type: graphql.NewNonNull(graphql.String), + }, + }, + Resolve: func(params graphql.ResolveParams) (interface{}, error) { + + cron := params.Args["cron"].(string) + + pipelineId, err := uuid.Parse(params.Args["pipelineId"].(string)) + if err != nil { + return nil, err + } + + err = db.AddCronForPipeline(pipelineId, cron) + if err != nil { + return nil, err + } + + pipeline, err := db.GetPipelineById(pipelineId) + if err != nil { + return nil, err + } + return pipeline, nil + }, + }, + "removeCronFromPipeline": &graphql.Field{ + Type: pipelineType, + Description: "Remove a cron trigger from a pipeline.", + Args: graphql.FieldConfigArgument{ + "cronId": &graphql.ArgumentConfig{ + Type: graphql.NewNonNull(graphql.String), + }, + "pipelineId": &graphql.ArgumentConfig{ + Type: graphql.NewNonNull(graphql.String), + }, + }, + Resolve: func(params graphql.ResolveParams) (interface{}, error) { + + cronId, err := uuid.Parse(params.Args["cronId"].(string)) + if err != nil { + return nil, err + } + + pipelineId, err := uuid.Parse(params.Args["pipelineId"].(string)) + if err != nil { + return nil, err + } + + err = db.RemoveCronForPipeline(cronId) + if err != nil { + return nil, err + } + pipeline, err := db.GetPipelineById(pipelineId) if err != nil { return nil, err diff --git a/database/db.go b/database/db.go index a5ba176..5bec3ed 100644 --- a/database/db.go +++ b/database/db.go @@ -189,6 +189,16 @@ CREATE TABLE pipeline_refs ( FOREIGN KEY(pipeline_id) REFERENCES pipelines(id) ); + +CREATE TABLE crons ( + id UUID PRIMARY KEY, + pipeline_id UUID NOT NULL, + cron TEXT NOT NULL, + + CONSTRAINT fk_pipeline_id + FOREIGN KEY(pipeline_id) + REFERENCES pipelines(id) +); ` _, err := conn.Exec(context.Background(), createTablesQuery) diff --git a/database/func.go b/database/func.go index 3cbde6d..bd99a29 100644 --- a/database/func.go +++ b/database/func.go @@ -670,3 +670,55 @@ RETURNING id, name, token;` return s, nil } + +func (db *Database) GetCronsForPipeline(pipelineId uuid.UUID) ([]Cron, error) { + query := ` +SELECT id, cron +FROM crons +WHERE pipeline_id=$1;` + + var crons []Cron + + cronEntrys, err := db.Conn.Query(context.Background(), query, pipelineId) + if err != nil { + return crons, fmt.Errorf("Could not get crons for pipeline with id \"%v\": %w", pipelineId, err) + } + defer cronEntrys.Close() + + for cronEntrys.Next() { + var cron Cron + var idStr string + if err := cronEntrys.Scan( + &idStr, &cron.Cron, + ); err != nil { + return crons, err + } + + cron.Id, err = uuid.Parse(idStr) + if err != nil { + return crons, err + } + + crons = append(crons, cron) + } + + return crons, nil +} + +func (db *Database) AddCronForPipeline(pipelineId uuid.UUID, cron string) error { + query := ` +INSERT INTO crons (id, pipeline_id, cron) +VALUES (uuid_generate_v4(), $1, $2);` + + _, err := db.Conn.Exec(context.Background(), query, pipelineId, cron) + return err +} + +func (db *Database) RemoveCronForPipeline(cronId uuid.UUID) error { + query := ` +DELETE FROM crons +WHERE id=$1;` + + _, err := db.Conn.Exec(context.Background(), query, cronId) + return err +} diff --git a/database/types.go b/database/types.go index 1c2bffc..1818171 100644 --- a/database/types.go +++ b/database/types.go @@ -79,3 +79,8 @@ type Runner struct { Name string Token string } + +type Cron struct { + Id uuid.UUID + Cron string +}