2021-03-31 07:23:48 +02:00
|
|
|
package graph
|
|
|
|
|
|
|
|
// This file will be automatically regenerated based on the schema, any resolver implementations
|
|
|
|
// will be copied through when generating and any unknown code will be moved to the end.
|
2021-10-26 10:24:43 +02:00
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2021-12-09 16:25:48 +01:00
|
|
|
"errors"
|
2021-10-26 10:24:43 +02:00
|
|
|
"fmt"
|
|
|
|
"strconv"
|
2021-12-09 16:25:48 +01:00
|
|
|
"time"
|
2021-10-26 10:24:43 +02:00
|
|
|
|
2021-12-09 16:25:48 +01:00
|
|
|
"github.com/ClusterCockpit/cc-jobarchive/auth"
|
2021-10-26 10:24:43 +02:00
|
|
|
"github.com/ClusterCockpit/cc-jobarchive/config"
|
|
|
|
"github.com/ClusterCockpit/cc-jobarchive/graph/generated"
|
|
|
|
"github.com/ClusterCockpit/cc-jobarchive/graph/model"
|
|
|
|
"github.com/ClusterCockpit/cc-jobarchive/metricdata"
|
2021-12-16 13:17:48 +01:00
|
|
|
"github.com/ClusterCockpit/cc-jobarchive/schema"
|
2021-10-26 10:24:43 +02:00
|
|
|
sq "github.com/Masterminds/squirrel"
|
|
|
|
)
|
|
|
|
|
2021-12-17 15:49:22 +01:00
|
|
|
func (r *jobResolver) Tags(ctx context.Context, obj *schema.Job) ([]*schema.Tag, error) {
|
2021-10-26 10:24:43 +02:00
|
|
|
query := sq.
|
|
|
|
Select("tag.id", "tag.tag_type", "tag.tag_name").
|
|
|
|
From("tag").
|
|
|
|
Join("jobtag ON jobtag.tag_id = tag.id").
|
|
|
|
Where("jobtag.job_id = ?", obj.ID)
|
|
|
|
|
2021-12-17 15:49:22 +01:00
|
|
|
sql, args, err := query.ToSql()
|
2021-10-26 10:24:43 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-12-17 15:49:22 +01:00
|
|
|
tags := make([]*schema.Tag, 0)
|
|
|
|
if err := r.DB.Select(&tags, sql, args...); err != nil {
|
|
|
|
return nil, err
|
2021-10-26 10:24:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return tags, nil
|
|
|
|
}
|
|
|
|
|
2021-12-17 15:49:22 +01:00
|
|
|
func (r *mutationResolver) CreateTag(ctx context.Context, typeArg string, name string) (*schema.Tag, error) {
|
2021-10-26 10:24:43 +02:00
|
|
|
res, err := r.DB.Exec("INSERT INTO tag (tag_type, tag_name) VALUES ($1, $2)", typeArg, name)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
id, err := res.LastInsertId()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-12-17 15:49:22 +01:00
|
|
|
return &schema.Tag{ID: id, Type: typeArg, Name: name}, nil
|
2021-10-26 10:24:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (r *mutationResolver) DeleteTag(ctx context.Context, id string) (string, error) {
|
|
|
|
// The UI does not allow this currently anyways.
|
|
|
|
panic(fmt.Errorf("not implemented"))
|
|
|
|
}
|
|
|
|
|
2021-12-17 15:49:22 +01:00
|
|
|
func (r *mutationResolver) AddTagsToJob(ctx context.Context, job string, tagIds []string) ([]*schema.Tag, error) {
|
2021-10-26 10:24:43 +02:00
|
|
|
jid, err := strconv.Atoi(job)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, tagId := range tagIds {
|
|
|
|
tid, err := strconv.Atoi(tagId)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, err := r.DB.Exec("INSERT INTO jobtag (job_id, tag_id) VALUES ($1, $2)", jid, tid); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-17 15:49:22 +01:00
|
|
|
dummyJob := schema.Job{}
|
|
|
|
dummyJob.ID = int64(jid)
|
|
|
|
tags, err := r.Job().Tags(ctx, &dummyJob)
|
2021-10-26 10:24:43 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
jobObj, err := r.Query().Job(ctx, job)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return tags, metricdata.UpdateTags(jobObj, tags)
|
|
|
|
}
|
|
|
|
|
2021-12-17 15:49:22 +01:00
|
|
|
func (r *mutationResolver) RemoveTagsFromJob(ctx context.Context, job string, tagIds []string) ([]*schema.Tag, error) {
|
2021-10-26 10:24:43 +02:00
|
|
|
jid, err := strconv.Atoi(job)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, tagId := range tagIds {
|
|
|
|
tid, err := strconv.Atoi(tagId)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, err := r.DB.Exec("DELETE FROM jobtag WHERE jobtag.job_id = $1 AND jobtag.tag_id = $2", jid, tid); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-17 15:49:22 +01:00
|
|
|
dummyJob := schema.Job{}
|
|
|
|
dummyJob.ID = int64(jid)
|
|
|
|
tags, err := r.Job().Tags(ctx, &dummyJob)
|
2021-10-26 10:24:43 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
jobObj, err := r.Query().Job(ctx, job)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return tags, metricdata.UpdateTags(jobObj, tags)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *mutationResolver) UpdateConfiguration(ctx context.Context, name string, value string) (*string, error) {
|
|
|
|
if err := config.UpdateConfig(name, value, ctx); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *queryResolver) Clusters(ctx context.Context) ([]*model.Cluster, error) {
|
2021-11-26 10:35:07 +01:00
|
|
|
return config.Clusters, nil
|
2021-10-26 10:24:43 +02:00
|
|
|
}
|
|
|
|
|
2021-12-17 15:49:22 +01:00
|
|
|
func (r *queryResolver) Tags(ctx context.Context) ([]*schema.Tag, error) {
|
|
|
|
sql, args, err := sq.Select("id", "tag_type", "tag_name").From("tag").ToSql()
|
2021-10-26 10:24:43 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-12-17 15:49:22 +01:00
|
|
|
tags := make([]*schema.Tag, 0)
|
|
|
|
if err := r.DB.Select(&tags, sql, args...); err != nil {
|
|
|
|
return nil, err
|
2021-10-26 10:24:43 +02:00
|
|
|
}
|
|
|
|
return tags, nil
|
|
|
|
}
|
|
|
|
|
2021-12-17 15:49:22 +01:00
|
|
|
func (r *queryResolver) Job(ctx context.Context, id string) (*schema.Job, error) {
|
2022-01-20 10:00:55 +01:00
|
|
|
// This query is very common (mostly called through other resolvers such as JobMetrics),
|
|
|
|
// so we use prepared statements here.
|
|
|
|
user := auth.GetUser(ctx)
|
2022-01-27 09:29:11 +01:00
|
|
|
if user == nil || user.HasRole(auth.RoleAdmin) {
|
2022-01-20 10:00:55 +01:00
|
|
|
return schema.ScanJob(r.findJobByIdStmt.QueryRowx(id))
|
2021-12-17 15:49:22 +01:00
|
|
|
}
|
|
|
|
|
2022-01-20 10:00:55 +01:00
|
|
|
return schema.ScanJob(r.findJobByIdWithUserStmt.QueryRowx(id, user.Username))
|
2021-10-26 10:24:43 +02:00
|
|
|
}
|
|
|
|
|
2021-12-20 10:48:58 +01:00
|
|
|
func (r *queryResolver) JobMetrics(ctx context.Context, id string, metrics []string, scopes []schema.MetricScope) ([]*model.JobMetricWithName, error) {
|
2021-10-26 10:24:43 +02:00
|
|
|
job, err := r.Query().Job(ctx, id)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2022-01-07 09:44:34 +01:00
|
|
|
data, err := metricdata.LoadData(job, metrics, scopes, ctx)
|
2021-10-26 10:24:43 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
res := []*model.JobMetricWithName{}
|
|
|
|
for name, md := range data {
|
2021-12-20 10:48:58 +01:00
|
|
|
for scope, metric := range md {
|
|
|
|
if metric.Scope != schema.MetricScope(scope) {
|
|
|
|
panic("WTF?")
|
|
|
|
}
|
|
|
|
|
|
|
|
res = append(res, &model.JobMetricWithName{
|
|
|
|
Name: name,
|
|
|
|
Metric: metric,
|
|
|
|
})
|
|
|
|
}
|
2021-10-26 10:24:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return res, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *queryResolver) JobsFootprints(ctx context.Context, filter []*model.JobFilter, metrics []string) ([]*model.MetricFootprints, error) {
|
|
|
|
return r.jobsFootprints(ctx, filter, metrics)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *queryResolver) Jobs(ctx context.Context, filter []*model.JobFilter, page *model.PageRequest, order *model.OrderByInput) (*model.JobResultList, error) {
|
2021-12-08 10:12:19 +01:00
|
|
|
jobs, count, err := r.queryJobs(ctx, filter, page, order)
|
2021-10-26 10:24:43 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &model.JobResultList{Items: jobs, Count: &count}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *queryResolver) JobsStatistics(ctx context.Context, filter []*model.JobFilter, groupBy *model.Aggregate) ([]*model.JobsStatistics, error) {
|
|
|
|
return r.jobsStatistics(ctx, filter, groupBy)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *queryResolver) RooflineHeatmap(ctx context.Context, filter []*model.JobFilter, rows int, cols int, minX float64, minY float64, maxX float64, maxY float64) ([][]float64, error) {
|
|
|
|
return r.rooflineHeatmap(ctx, filter, rows, cols, minX, minY, maxX, maxY)
|
|
|
|
}
|
|
|
|
|
2021-12-09 16:25:48 +01:00
|
|
|
func (r *queryResolver) NodeMetrics(ctx context.Context, cluster string, nodes []string, metrics []string, from time.Time, to time.Time) ([]*model.NodeMetrics, error) {
|
|
|
|
user := auth.GetUser(ctx)
|
2022-01-27 09:29:11 +01:00
|
|
|
if user != nil && !user.HasRole(auth.RoleAdmin) {
|
2021-12-09 16:25:48 +01:00
|
|
|
return nil, errors.New("you need to be an administrator for this query")
|
|
|
|
}
|
|
|
|
|
|
|
|
data, err := metricdata.LoadNodeData(cluster, metrics, nodes, from.Unix(), to.Unix(), ctx)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
res := make([]*model.NodeMetrics, 0, len(data))
|
|
|
|
for node, metrics := range data {
|
|
|
|
nodeMetrics := make([]*model.NodeMetric, 0, len(metrics))
|
|
|
|
for metric, data := range metrics {
|
|
|
|
nodeMetrics = append(nodeMetrics, &model.NodeMetric{
|
|
|
|
Name: metric,
|
|
|
|
Data: data,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
res = append(res, &model.NodeMetrics{
|
|
|
|
ID: node,
|
|
|
|
Metrics: nodeMetrics,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
return res, nil
|
|
|
|
}
|
|
|
|
|
2021-10-26 10:24:43 +02:00
|
|
|
// Job returns generated.JobResolver implementation.
|
|
|
|
func (r *Resolver) Job() generated.JobResolver { return &jobResolver{r} }
|
|
|
|
|
|
|
|
// Mutation returns generated.MutationResolver implementation.
|
|
|
|
func (r *Resolver) Mutation() generated.MutationResolver { return &mutationResolver{r} }
|
|
|
|
|
|
|
|
// Query returns generated.QueryResolver implementation.
|
|
|
|
func (r *Resolver) Query() generated.QueryResolver { return &queryResolver{r} }
|
|
|
|
|
|
|
|
type jobResolver struct{ *Resolver }
|
|
|
|
type mutationResolver struct{ *Resolver }
|
|
|
|
type queryResolver struct{ *Resolver }
|