Files
server/admin_api/admin_api.go
T

479 lines
14 KiB
Go

package admin_api
import (
"fmt"
"net/http"
"git.ohea.xyz/cursorius/server/database"
"github.com/google/uuid"
"github.com/graphql-go/graphql"
"github.com/graphql-go/handler"
"github.com/op/go-logging"
)
var log = logging.MustGetLogger("cursorius-server")
func createSchema(db database.Database) (graphql.Schema, error) {
credentialType := graphql.NewObject(graphql.ObjectConfig{
Name: "Credential",
Description: "A credential for authenticating with the pipeline source host.",
Fields: graphql.Fields{
"id": &graphql.Field{
Type: graphql.NewNonNull(graphql.String),
Description: "The id of the credential.",
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
if credential, ok := p.Source.(database.Credential); ok {
return credential.Id, nil
}
return nil, nil
},
},
"name": &graphql.Field{
Type: graphql.NewNonNull(graphql.String),
Description: "The name of the credential.",
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
if credential, ok := p.Source.(database.Credential); ok {
return credential.Name, nil
}
return nil, nil
},
},
"type": &graphql.Field{
Type: graphql.NewNonNull(graphql.String),
Description: "The credential type.",
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
if credential, ok := p.Source.(database.Credential); ok {
return credential.Type, nil
}
return nil, nil
},
},
"username": &graphql.Field{
Type: graphql.NewNonNull(graphql.String),
Description: "The username to user with the credential.",
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
if credential, ok := p.Source.(database.Credential); ok {
return credential.Username, nil
}
return nil, nil
},
},
"secret": &graphql.Field{
Type: graphql.NewNonNull(graphql.String),
Description: "The secret for the credential.",
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
if credential, ok := p.Source.(database.Credential); ok {
return credential.Secret, nil
}
return nil, nil
},
},
},
})
webhookType := graphql.NewObject(graphql.ObjectConfig{
Name: "Webhook",
Description: "A webhook for triggering pipelines",
Fields: graphql.Fields{
"id": &graphql.Field{
Type: graphql.NewNonNull(graphql.String),
Description: "The id of the webhook.",
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
if webhook, ok := p.Source.(database.Webhook); ok {
return webhook.Id, nil
}
return nil, nil
},
},
"serverType": &graphql.Field{
Type: graphql.NewNonNull(graphql.String),
Description: "The format of the webhook.",
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
if webhook, ok := p.Source.(database.Webhook); ok {
return webhook.ServerType, nil
}
return nil, nil
},
},
"secret": &graphql.Field{
Type: graphql.NewNonNull(graphql.String),
Description: "The secret used to validate the webhook.",
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
if webhook, ok := p.Source.(database.Webhook); ok {
return webhook.Secret, nil
}
return nil, nil
},
},
},
})
runType := graphql.NewObject(graphql.ObjectConfig{
Name: "Run",
Description: "A pipeline run",
Fields: graphql.Fields{
"id": &graphql.Field{
Type: graphql.NewNonNull(graphql.String),
Description: "The id of the run.",
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
if run, ok := p.Source.(database.Run); ok {
return run.Id, nil
}
return nil, nil
},
},
"progress": &graphql.Field{
Type: graphql.Boolean,
Description: "The progress status of the run.",
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
if run, ok := p.Source.(database.Run); ok {
return run.InProgress, nil
}
return nil, nil
},
},
"result": &graphql.Field{
// TODO: handle bigint properly here
Type: graphql.Float,
Description: "The result of the run.",
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
if run, ok := p.Source.(database.Run); ok {
return run.Result, nil
}
return nil, nil
},
},
"stdout": &graphql.Field{
Type: graphql.String,
Description: "The stdout used to validate the run.",
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
if run, ok := p.Source.(database.Run); ok {
return run.Stdout, nil
}
return nil, nil
},
},
"stderr": &graphql.Field{
Type: graphql.String,
Description: "The stderr used to validate the run.",
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
if run, ok := p.Source.(database.Run); ok {
return run.Stderr, nil
}
return nil, nil
},
},
},
})
pipelineType := graphql.NewObject(graphql.ObjectConfig{
Name: "Pipeline",
Description: "A pipeline for running ci jobs",
Fields: graphql.Fields{
"id": &graphql.Field{
Type: graphql.NewNonNull(graphql.String),
Description: "The id of the pipeline.",
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
if pipeline, ok := p.Source.(database.Pipeline); ok {
return pipeline.Id, nil
}
return nil, nil
},
},
"name": &graphql.Field{
Type: graphql.NewNonNull(graphql.String),
Description: "The name of the pipeline.",
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
if pipeline, ok := p.Source.(database.Pipeline); ok {
return pipeline.Name, nil
}
return nil, nil
},
},
"url": &graphql.Field{
Type: graphql.NewNonNull(graphql.String),
Description: "The url of the pipeline.",
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
if pipeline, ok := p.Source.(database.Pipeline); ok {
return pipeline.Url, nil
}
return nil, nil
},
},
"pollInterval": &graphql.Field{
Type: graphql.NewNonNull(graphql.Int),
Description: "The polling interval for the pipeline.",
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
if pipeline, ok := p.Source.(database.Pipeline); ok {
return pipeline.PollInterval, nil
}
return nil, nil
},
},
"credentialId": &graphql.Field{
Type: graphql.String,
Description: "The configured credential for cloning the pipeline source.",
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
if pipeline, ok := p.Source.(database.Pipeline); ok {
return pipeline.Credential, nil
}
return nil, nil
},
},
"webhooks": &graphql.Field{
Type: graphql.NewNonNull(graphql.NewList(graphql.NewNonNull(webhookType))),
Description: "The list of webhooks for the pipeline.",
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
if pipeline, ok := p.Source.(database.Pipeline); ok {
return db.GetWebhooksForPipeline(pipeline.Id)
}
return []database.Webhook{}, nil
},
},
"runs": &graphql.Field{
Type: graphql.NewNonNull(graphql.NewList(graphql.NewNonNull(runType))),
Description: "The list of runs for the pipeline.",
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
if pipeline, ok := p.Source.(database.Pipeline); ok {
return db.GetRunsForPipeline(pipeline.Id)
}
return []database.Webhook{}, nil
},
},
},
})
queryType := graphql.NewObject(graphql.ObjectConfig{
Name: "Query",
Fields: graphql.Fields{
"Pipeline": &graphql.Field{
Type: pipelineType,
Args: graphql.FieldConfigArgument{
"id": &graphql.ArgumentConfig{
Type: graphql.NewNonNull(graphql.String),
Description: "The id of the requested pipeline.",
},
},
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
id, err := uuid.Parse(p.Args["id"].(string))
if err != nil {
return nil, err
}
return db.GetPipelineById(id)
},
},
"Pipelines": &graphql.Field{
Type: graphql.NewNonNull(graphql.NewList(pipelineType)),
Args: graphql.FieldConfigArgument{},
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
return db.GetPipelines()
},
},
"Credential": &graphql.Field{
Type: credentialType,
Args: graphql.FieldConfigArgument{
"id": &graphql.ArgumentConfig{
Type: graphql.NewNonNull(graphql.String),
Description: "The id of the requested credential.",
},
},
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
id, err := uuid.Parse(p.Args["id"].(string))
if err != nil {
return nil, err
}
return db.GetCredentialById(id)
},
},
"Credentials": &graphql.Field{
Type: graphql.NewNonNull(graphql.NewList(credentialType)),
Args: graphql.FieldConfigArgument{},
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
return db.GetCredentials()
},
},
},
})
mutationType := graphql.NewObject(graphql.ObjectConfig{
Name: "Mutation",
Fields: graphql.Fields{
"createPipeline": &graphql.Field{
Type: pipelineType,
Description: "Create a new pipeline",
Args: graphql.FieldConfigArgument{
"name": &graphql.ArgumentConfig{
Type: graphql.NewNonNull(graphql.String),
},
"url": &graphql.ArgumentConfig{
Type: graphql.NewNonNull(graphql.String),
},
"pollInterval": &graphql.ArgumentConfig{
Type: graphql.Int,
},
"credentialId": &graphql.ArgumentConfig{
Type: graphql.String,
},
},
Resolve: func(params graphql.ResolveParams) (interface{}, error) {
var interval int
if intervalVal, ok := params.Args["pollInterval"]; ok {
interval = intervalVal.(int)
} else {
interval = 0
}
var credential *uuid.UUID
if credentialVal, ok := params.Args["credentialId"]; ok {
id, err := uuid.Parse(credentialVal.(string))
if err != nil {
return nil, err
}
credential = &id
} else {
credential = nil
}
pipeline, err := db.CreatePipeline(
params.Args["name"].(string),
params.Args["url"].(string),
interval,
credential,
)
if err != nil {
return nil, err
}
return pipeline, nil
},
},
"createWebhook": &graphql.Field{
Type: webhookType,
Description: "Create a new webhook",
Args: graphql.FieldConfigArgument{
"type": &graphql.ArgumentConfig{
Type: graphql.NewNonNull(graphql.String),
},
"pipeline_id": &graphql.ArgumentConfig{
Type: graphql.NewNonNull(graphql.String),
},
},
Resolve: func(params graphql.ResolveParams) (interface{}, error) {
id, err := uuid.Parse(params.Args["id"].(string))
if err != nil {
return nil, err
}
webhook, err := db.CreateWebhook(
database.WebhookSender(params.Args["type"].(string)),
id,
)
if err != nil {
return nil, err
}
return webhook, nil
},
},
"createCredential": &graphql.Field{
Type: credentialType,
Description: "Create a new credential",
Args: graphql.FieldConfigArgument{
"name": &graphql.ArgumentConfig{
Type: graphql.NewNonNull(graphql.String),
},
"type": &graphql.ArgumentConfig{
Type: graphql.NewNonNull(graphql.String),
},
"username": &graphql.ArgumentConfig{
Type: graphql.NewNonNull(graphql.String),
},
"secret": &graphql.ArgumentConfig{
Type: graphql.NewNonNull(graphql.String),
},
},
Resolve: func(params graphql.ResolveParams) (interface{}, error) {
credential, err := db.CreateCredential(
params.Args["name"].(string),
database.CredentialType(params.Args["type"].(string)),
params.Args["username"].(string),
params.Args["secret"].(string),
)
if err != nil {
return nil, err
}
return credential, nil
},
},
"setPipelineCredential": &graphql.Field{
Type: pipelineType,
Description: "Add an credential to a pipeline",
Args: graphql.FieldConfigArgument{
"credentialId": &graphql.ArgumentConfig{
Type: graphql.String,
},
"pipelineId": &graphql.ArgumentConfig{
Type: graphql.NewNonNull(graphql.String),
},
},
Resolve: func(params graphql.ResolveParams) (interface{}, error) {
pipelineId, err := uuid.Parse(params.Args["pipelineId"].(string))
if err != nil {
return nil, err
}
if credentialIdVal, ok := params.Args["credentialId"]; ok {
credentialId, err := uuid.Parse(credentialIdVal.(string))
if err != nil {
return nil, err
}
pipeline, err := db.SetPipelineCredential(pipelineId, &credentialId)
if err != nil {
return nil, err
}
return pipeline, nil
} else {
pipeline, err := db.SetPipelineCredential(pipelineId, nil)
if err != nil {
return nil, err
}
return pipeline, nil
}
},
},
},
})
schema, err := graphql.NewSchema(graphql.SchemaConfig{
Query: queryType,
Mutation: mutationType,
})
if err != nil {
return schema, fmt.Errorf("Could not create schema: %w", err)
}
return schema, nil
}
func CreateHandler(db database.Database, mux *http.ServeMux) error {
schema, err := createSchema(db)
if err != nil {
return err
}
h := handler.New(&handler.Config{
Schema: &schema,
Pretty: true,
GraphiQL: true,
})
mux.Handle("/graphql", h)
return nil
}