From ac8548a950b5bb5694297c8dd12553dde9bc146c Mon Sep 17 00:00:00 2001 From: Christoph Kluge Date: Tue, 13 Dec 2022 09:53:37 +0100 Subject: [PATCH 01/11] Initial orientation --- cmd/cc-backend/main.go | 4 ++-- internal/repository/job.go | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/cmd/cc-backend/main.go b/cmd/cc-backend/main.go index daf3ca9..8d39a25 100644 --- a/cmd/cc-backend/main.go +++ b/cmd/cc-backend/main.go @@ -295,12 +295,12 @@ func main() { } secured.Handle("/query", graphQLEndpoint) - // Send a searchId and then reply with a redirect to a user or job. + // Send a searchId and then reply with a redirect to a user or job. // IMPROVE HERE secured.HandleFunc("/search", func(rw http.ResponseWriter, r *http.Request) { if search := r.URL.Query().Get("searchId"); search != "" { job, username, err := api.JobRepository.FindJobOrUser(r.Context(), search) if err == repository.ErrNotFound { - http.Redirect(rw, r, "/monitoring/jobs/?jobId="+url.QueryEscape(search), http.StatusTemporaryRedirect) + http.Redirect(rw, r, "/monitoring/jobs/?jobId="+url.QueryEscape(search), http.StatusTemporaryRedirect) // Directly to table! -> Running Jobs probably return } else if err != nil { http.Error(rw, err.Error(), http.StatusInternalServerError) diff --git a/internal/repository/job.go b/internal/repository/job.go index d1b2af2..73b3517 100644 --- a/internal/repository/job.go +++ b/internal/repository/job.go @@ -360,9 +360,14 @@ func (r *JobRepository) Archive( var ErrNotFound = errors.New("no such job or user") -// FindJobOrUser returns a job database ID or a username if a job or user machtes the search term. +// FindJobOrUser returns a job database ID or a username if a job or user matches the search term. // As 0 is a valid job id, check if username is "" instead in order to check what machted. // If nothing matches the search, `ErrNotFound` is returned. + +// TO BE IMPROVED; SHOW ALL MATCHES (eg: multiple clusters with matching jobId) + Search by JobNAME +// Plan: Parent-Method: Solves Searchtag or, if no searchtag, tries to find by hierarical searchId +// Plan: Nested methods for jobid, jobname (?), username, project, tag [dependent on roles of user, do not forget api!] + func (r *JobRepository) FindJobOrUser(ctx context.Context, searchterm string) (job int64, username string, err error) { user := auth.GetUser(ctx) if id, err := strconv.Atoi(searchterm); err == nil { From c9ee8b552d7d80d21d2a23c032459dab7ff6f2e5 Mon Sep 17 00:00:00 2001 From: Christoph Kluge Date: Wed, 14 Dec 2022 10:02:22 +0100 Subject: [PATCH 02/11] Rework search to show jobId matches in table, add projectId search --- cmd/cc-backend/main.go | 17 ++++++++++------- internal/repository/job.go | 33 +++++++++++++++++++++------------ web/frontend/src/Header.svelte | 2 +- 3 files changed, 32 insertions(+), 20 deletions(-) diff --git a/cmd/cc-backend/main.go b/cmd/cc-backend/main.go index 8d39a25..bc6a265 100644 --- a/cmd/cc-backend/main.go +++ b/cmd/cc-backend/main.go @@ -295,14 +295,13 @@ func main() { } secured.Handle("/query", graphQLEndpoint) - // Send a searchId and then reply with a redirect to a user or job. // IMPROVE HERE + // Send a searchId and then reply with a redirect to a user, or directly send query to job table for jobid and project. secured.HandleFunc("/search", func(rw http.ResponseWriter, r *http.Request) { if search := r.URL.Query().Get("searchId"); search != "" { - job, username, err := api.JobRepository.FindJobOrUser(r.Context(), search) - if err == repository.ErrNotFound { - http.Redirect(rw, r, "/monitoring/jobs/?jobId="+url.QueryEscape(search), http.StatusTemporaryRedirect) // Directly to table! -> Running Jobs probably - return - } else if err != nil { + + _, username, project, err := api.JobRepository.FindJobOrUserOrProject(r.Context(), search) + + if err != nil { http.Error(rw, err.Error(), http.StatusInternalServerError) return } @@ -310,10 +309,14 @@ func main() { if username != "" { http.Redirect(rw, r, "/monitoring/user/"+username, http.StatusTemporaryRedirect) return + } else if (project != "") { + http.Redirect(rw, r, "/monitoring/jobs/?projectMatch=eq&project="+project, http.StatusTemporaryRedirect) // Directly to table! + return } else { - http.Redirect(rw, r, fmt.Sprintf("/monitoring/job/%d", job), http.StatusTemporaryRedirect) + http.Redirect(rw, r, "/monitoring/jobs/?jobId="+url.QueryEscape(search), http.StatusTemporaryRedirect) // Directly to table! return } + } else { http.Error(rw, "'searchId' query parameter missing", http.StatusBadRequest) } diff --git a/internal/repository/job.go b/internal/repository/job.go index 73b3517..7902ce0 100644 --- a/internal/repository/job.go +++ b/internal/repository/job.go @@ -358,17 +358,15 @@ func (r *JobRepository) Archive( return nil } -var ErrNotFound = errors.New("no such job or user") +var ErrNotFound = errors.New("no such job, project or user") -// FindJobOrUser returns a job database ID or a username if a job or user matches the search term. -// As 0 is a valid job id, check if username is "" instead in order to check what machted. +// FindJobOrUserOrProject returns a job database ID or a username or a projectId if a job or user or project matches the search term. +// As 0 is a valid job id, check if username/projectId is "" instead in order to check what matched. // If nothing matches the search, `ErrNotFound` is returned. -// TO BE IMPROVED; SHOW ALL MATCHES (eg: multiple clusters with matching jobId) + Search by JobNAME -// Plan: Parent-Method: Solves Searchtag or, if no searchtag, tries to find by hierarical searchId -// Plan: Nested methods for jobid, jobname (?), username, project, tag [dependent on roles of user, do not forget api!] +// TO BE IMPROVED; Search by JobNAME -func (r *JobRepository) FindJobOrUser(ctx context.Context, searchterm string) (job int64, username string, err error) { +func (r *JobRepository) FindJobOrUserOrProject(ctx context.Context, searchterm string) (job int64, username string, project string, err error) { user := auth.GetUser(ctx) if id, err := strconv.Atoi(searchterm); err == nil { qb := sq.Select("job.id").From("job").Where("job.job_id = ?", id) @@ -378,9 +376,9 @@ func (r *JobRepository) FindJobOrUser(ctx context.Context, searchterm string) (j err := qb.RunWith(r.stmtCache).QueryRow().Scan(&job) if err != nil && err != sql.ErrNoRows { - return 0, "", err + return 0, "", "", err } else if err == nil { - return job, "", nil + return job, "", "", nil } } @@ -389,13 +387,24 @@ func (r *JobRepository) FindJobOrUser(ctx context.Context, searchterm string) (j Where("job.user = ?", searchterm). RunWith(r.stmtCache).QueryRow().Scan(&username) if err != nil && err != sql.ErrNoRows { - return 0, "", err + return 0, "", "", err } else if err == nil { - return 0, username, nil + return 0, username, "", nil } } - return 0, "", ErrNotFound + if user == nil || user.HasRole(auth.RoleAdmin) || user.HasRole(auth.RoleSupport) { + err := sq.Select("job.project").Distinct().From("job"). + Where("job.project = ?", searchterm). + RunWith(r.stmtCache).QueryRow().Scan(&project) + if err != nil && err != sql.ErrNoRows { + return 0, "", "", err + } else if err == nil { + return 0, "", project, nil + } + } + + return 0, "", "", ErrNotFound } func (r *JobRepository) Partitions(cluster string) ([]string, error) { diff --git a/web/frontend/src/Header.svelte b/web/frontend/src/Header.svelte index 95675f4..b541b2d 100644 --- a/web/frontend/src/Header.svelte +++ b/web/frontend/src/Header.svelte @@ -55,7 +55,7 @@
- +
From a642e9cc7ced07b74a91608fe860b6b6f628acd0 Mon Sep 17 00:00:00 2001 From: Christoph Kluge Date: Wed, 11 Jan 2023 16:25:02 +0100 Subject: [PATCH 03/11] Add working, but manually entered jobName url query --- api/schema.graphqls | 2 + internal/graph/generated/generated.go | 84 ++++++++++++++++++++++++- internal/graph/model/models_gen.go | 1 + internal/graph/schema.resolvers.go | 5 ++ internal/repository/job.go | 39 +++++++++++- internal/repository/query.go | 3 + internal/routerConfig/routes.go | 3 + web/frontend/src/filters/Filters.svelte | 7 ++- web/frontend/src/joblist/JobList.svelte | 2 +- 9 files changed, 141 insertions(+), 5 deletions(-) diff --git a/api/schema.graphqls b/api/schema.graphqls index 82c9488..957a9f5 100644 --- a/api/schema.graphqls +++ b/api/schema.graphqls @@ -10,6 +10,7 @@ type Job { jobId: Int! user: String! project: String! + jobName: String cluster: String! subCluster: String! startTime: Time! @@ -201,6 +202,7 @@ input JobFilter { arrayJobId: Int user: StringInput project: StringInput + jobName: StringInput cluster: StringInput partition: StringInput duration: IntRange diff --git a/internal/graph/generated/generated.go b/internal/graph/generated/generated.go index a83998b..edae306 100644 --- a/internal/graph/generated/generated.go +++ b/internal/graph/generated/generated.go @@ -88,6 +88,7 @@ type ComplexityRoot struct { Exclusive func(childComplexity int) int ID func(childComplexity int) int JobID func(childComplexity int) int + JobName func(childComplexity int) int MetaData func(childComplexity int) int MonitoringStatus func(childComplexity int) int NumAcc func(childComplexity int) int @@ -262,6 +263,8 @@ type ClusterResolver interface { Partitions(ctx context.Context, obj *schema.Cluster) ([]string, error) } type JobResolver interface { + JobName(ctx context.Context, obj *schema.Job) (*string, error) + Tags(ctx context.Context, obj *schema.Job) ([]*schema.Tag, error) MetaData(ctx context.Context, obj *schema.Job) (interface{}, error) @@ -451,6 +454,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Job.JobID(childComplexity), true + case "Job.jobName": + if e.complexity.Job.JobName == nil { + break + } + + return e.complexity.Job.JobName(childComplexity), true + case "Job.metaData": if e.complexity.Job.MetaData == nil { break @@ -1383,6 +1393,7 @@ type Job { jobId: Int! user: String! project: String! + jobName: String cluster: String! subCluster: String! startTime: Time! @@ -1574,6 +1585,7 @@ input JobFilter { arrayJobId: Int user: StringInput project: StringInput + jobName: StringInput cluster: StringInput partition: StringInput duration: IntRange @@ -3029,6 +3041,47 @@ func (ec *executionContext) fieldContext_Job_project(ctx context.Context, field return fc, nil } +func (ec *executionContext) _Job_jobName(ctx context.Context, field graphql.CollectedField, obj *schema.Job) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Job_jobName(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Job().JobName(rctx, obj) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Job_jobName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Job", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) _Job_cluster(ctx context.Context, field graphql.CollectedField, obj *schema.Job) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Job_cluster(ctx, field) if err != nil { @@ -4220,6 +4273,8 @@ func (ec *executionContext) fieldContext_JobResultList_items(ctx context.Context return ec.fieldContext_Job_user(ctx, field) case "project": return ec.fieldContext_Job_project(ctx, field) + case "jobName": + return ec.fieldContext_Job_jobName(ctx, field) case "cluster": return ec.fieldContext_Job_cluster(ctx, field) case "subCluster": @@ -6065,6 +6120,8 @@ func (ec *executionContext) fieldContext_Query_job(ctx context.Context, field gr return ec.fieldContext_Job_user(ctx, field) case "project": return ec.fieldContext_Job_project(ctx, field) + case "jobName": + return ec.fieldContext_Job_jobName(ctx, field) case "cluster": return ec.fieldContext_Job_cluster(ctx, field) case "subCluster": @@ -10335,7 +10392,7 @@ func (ec *executionContext) unmarshalInputJobFilter(ctx context.Context, obj int asMap[k] = v } - fieldsInOrder := [...]string{"tags", "jobId", "arrayJobId", "user", "project", "cluster", "partition", "duration", "minRunningFor", "numNodes", "numAccelerators", "numHWThreads", "startTime", "state", "flopsAnyAvg", "memBwAvg", "loadAvg", "memUsedMax"} + fieldsInOrder := [...]string{"tags", "jobId", "arrayJobId", "user", "project", "jobName", "cluster", "partition", "duration", "minRunningFor", "numNodes", "numAccelerators", "numHWThreads", "startTime", "state", "flopsAnyAvg", "memBwAvg", "loadAvg", "memUsedMax"} for _, k := range fieldsInOrder { v, ok := asMap[k] if !ok { @@ -10382,6 +10439,14 @@ func (ec *executionContext) unmarshalInputJobFilter(ctx context.Context, obj int if err != nil { return it, err } + case "jobName": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("jobName")) + it.JobName, err = ec.unmarshalOStringInput2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐStringInput(ctx, v) + if err != nil { + return it, err + } case "cluster": var err error @@ -10946,6 +11011,23 @@ func (ec *executionContext) _Job(ctx context.Context, sel ast.SelectionSet, obj if out.Values[i] == graphql.Null { atomic.AddUint32(&invalids, 1) } + case "jobName": + field := field + + innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Job_jobName(ctx, field, obj) + return res + } + + out.Concurrently(i, func() graphql.Marshaler { + return innerFunc(ctx) + + }) case "cluster": out.Values[i] = ec._Job_cluster(ctx, field, obj) diff --git a/internal/graph/model/models_gen.go b/internal/graph/model/models_gen.go index d27d517..9d2f9ea 100644 --- a/internal/graph/model/models_gen.go +++ b/internal/graph/model/models_gen.go @@ -42,6 +42,7 @@ type JobFilter struct { ArrayJobID *int `json:"arrayJobId"` User *StringInput `json:"user"` Project *StringInput `json:"project"` + JobName *StringInput `json:"jobName"` Cluster *StringInput `json:"cluster"` Partition *StringInput `json:"partition"` Duration *schema.IntRange `json:"duration"` diff --git a/internal/graph/schema.resolvers.go b/internal/graph/schema.resolvers.go index 1aa8a04..da227c7 100644 --- a/internal/graph/schema.resolvers.go +++ b/internal/graph/schema.resolvers.go @@ -24,6 +24,11 @@ func (r *clusterResolver) Partitions(ctx context.Context, obj *schema.Cluster) ( return r.Repo.Partitions(obj.Name) } +// JobName is the resolver for the jobName field. +func (r *jobResolver) JobName(ctx context.Context, obj *schema.Job) (*string, error) { + return r.Repo.FetchJobName(obj) +} + // Tags is the resolver for the tags field. func (r *jobResolver) Tags(ctx context.Context, obj *schema.Job) ([]*schema.Tag, error) { return r.Repo.GetTags(&obj.ID) diff --git a/internal/repository/job.go b/internal/repository/job.go index 7902ce0..99db9d8 100644 --- a/internal/repository/job.go +++ b/internal/repository/job.go @@ -52,7 +52,7 @@ func GetJobRepository() *JobRepository { var jobColumns []string = []string{ "job.id", "job.job_id", "job.user", "job.project", "job.cluster", "job.subcluster", "job.start_time", "job.partition", "job.array_job_id", "job.num_nodes", "job.num_hwthreads", "job.num_acc", "job.exclusive", "job.monitoring_status", "job.smt", "job.job_state", - "job.duration", "job.walltime", "job.resources", // "job.meta_data", + "job.duration", "job.walltime", "job.resources", "job.meta_data", } func scanJob(row interface{ Scan(...interface{}) error }) (*schema.Job, error) { @@ -60,7 +60,7 @@ func scanJob(row interface{ Scan(...interface{}) error }) (*schema.Job, error) { if err := row.Scan( &job.ID, &job.JobID, &job.User, &job.Project, &job.Cluster, &job.SubCluster, &job.StartTimeUnix, &job.Partition, &job.ArrayJobId, &job.NumNodes, &job.NumHWThreads, &job.NumAcc, &job.Exclusive, &job.MonitoringStatus, &job.SMT, &job.State, - &job.Duration, &job.Walltime, &job.RawResources /*&job.MetaData*/); err != nil { + &job.Duration, &job.Walltime, &job.RawResources, &job.RawMetaData); err != nil { return nil, err } @@ -68,6 +68,10 @@ func scanJob(row interface{ Scan(...interface{}) error }) (*schema.Job, error) { return nil, err } + if err := json.Unmarshal(job.RawMetaData, &job.MetaData); err != nil { + return nil, err + } + job.StartTime = time.Unix(job.StartTimeUnix, 0) if job.Duration == 0 && job.State == schema.JobStateRunning { job.Duration = int32(time.Since(job.StartTime).Seconds()) @@ -77,6 +81,37 @@ func scanJob(row interface{ Scan(...interface{}) error }) (*schema.Job, error) { return job, nil } +func (r *JobRepository) FetchJobName(job *schema.Job) (*string, error) { + cachekey := fmt.Sprintf("metadata:%d", job.ID) + if cached := r.cache.Get(cachekey, nil); cached != nil { + job.MetaData = cached.(map[string]string) + if jobName := job.MetaData["jobName"]; jobName != "" { + return &jobName, nil + } + } + + if err := sq.Select("job.meta_data").From("job").Where("job.id = ?", job.ID). + RunWith(r.stmtCache).QueryRow().Scan(&job.RawMetaData); err != nil { + return nil, err + } + + if len(job.RawMetaData) == 0 { + return nil, nil + } + + if err := json.Unmarshal(job.RawMetaData, &job.MetaData); err != nil { + return nil, err + } + + r.cache.Put(cachekey, job.MetaData, len(job.RawMetaData), 24*time.Hour) + + if jobName := job.MetaData["jobName"]; jobName != "" { + return &jobName, nil + } else { + return new(string), nil + } +} + func (r *JobRepository) FetchMetadata(job *schema.Job) (map[string]string, error) { cachekey := fmt.Sprintf("metadata:%d", job.ID) if cached := r.cache.Get(cachekey, nil); cached != nil { diff --git a/internal/repository/query.go b/internal/repository/query.go index fad6091..68f3320 100644 --- a/internal/repository/query.go +++ b/internal/repository/query.go @@ -118,6 +118,9 @@ func BuildWhereClause(filter *model.JobFilter, query sq.SelectBuilder) sq.Select if filter.Project != nil { query = buildStringCondition("job.project", filter.Project, query) } + if filter.JobName != nil { + query = buildStringCondition("job.meta_data", filter.JobName, query) + } if filter.Cluster != nil { query = buildStringCondition("job.cluster", filter.Cluster, query) } diff --git a/internal/routerConfig/routes.go b/internal/routerConfig/routes.go index 9424df7..ccceb58 100644 --- a/internal/routerConfig/routes.go +++ b/internal/routerConfig/routes.go @@ -180,6 +180,9 @@ func buildFilterPresets(query url.Values) map[string]interface{} { filterPresets["project"] = query.Get("project") filterPresets["projectMatch"] = "eq" } + if query.Get("jobName") != "" { + filterPresets["jobName"] = query.Get("jobName") + } if query.Get("user") != "" { filterPresets["user"] = query.Get("user") filterPresets["userMatch"] = "eq" diff --git a/web/frontend/src/filters/Filters.svelte b/web/frontend/src/filters/Filters.svelte index 410f445..e8f6d46 100644 --- a/web/frontend/src/filters/Filters.svelte +++ b/web/frontend/src/filters/Filters.svelte @@ -45,6 +45,7 @@ arrayJobId: filterPresets.arrayJobId || null, user: filterPresets.user || '', project: filterPresets.project || '', + jobName: filterPresets.jobName || '', numNodes: filterPresets.numNodes || { from: null, to: null }, numHWThreads: filterPresets.numHWThreads || { from: null, to: null }, @@ -94,6 +95,8 @@ items.push({ user: { [filters.userMatch]: filters.user } }) if (filters.project) items.push({ project: { [filters.projectMatch]: filters.project } }) + if (filters.jobName) + items.push({ jobName: { contains: filters.jobName } }) for (let stat of filters.stats) items.push({ [stat.field]: { from: stat.from, to: stat.to } }) @@ -115,7 +118,7 @@ opts.push(`state=${state}`) if (filters.startTime.from && filters.startTime.to) opts.push(`startTime=${dateToUnixEpoch(filters.startTime.from)}-${dateToUnixEpoch(filters.startTime.to)}`) - for (let tag of filters.tags) + for (let tag of filters.tags) opts.push(`tag=${tag}`) if (filters.duration.from && filters.duration.to) opts.push(`duration=${filters.duration.from}-${filters.duration.to}`) @@ -129,6 +132,8 @@ opts.push(`userMatch=${filters.userMatch}`) if (filters.project) opts.push(`project=${filters.project}`) + if (filters.jobName) + opts.push(`jobName=${filters.jobName}`) if (filters.projectMatch != 'contains') opts.push(`projectMatch=${filters.projectMatch}`) diff --git a/web/frontend/src/joblist/JobList.svelte b/web/frontend/src/joblist/JobList.svelte index 8cdca26..b316727 100644 --- a/web/frontend/src/joblist/JobList.svelte +++ b/web/frontend/src/joblist/JobList.svelte @@ -32,7 +32,7 @@ query($filter: [JobFilter!]!, $sorting: OrderByInput!, $paging: PageRequest! ){ jobs(filter: $filter, order: $sorting, page: $paging) { items { - id, jobId, user, project, cluster, subCluster, startTime, + id, jobId, user, project, jobName, cluster, subCluster, startTime, duration, numNodes, numHWThreads, numAcc, walltime, SMT, exclusive, partition, arrayJobId, monitoringStatus, state, From a5298a363086332ae14285d16bd2f43b4ba2fb1a Mon Sep 17 00:00:00 2001 From: Christoph Kluge Date: Wed, 11 Jan 2023 16:26:08 +0100 Subject: [PATCH 04/11] updated gofiles --- go.mod | 8 +++----- go.sum | 49 +++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 42 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index 7fb1ac4..a892297 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,9 @@ require ( github.com/influxdata/influxdb-client-go/v2 v2.10.0 github.com/jmoiron/sqlx v1.3.5 github.com/mattn/go-sqlite3 v1.14.15 - github.com/santhosh-tekuri/jsonschema v1.2.4 + github.com/santhosh-tekuri/jsonschema/v5 v5.0.0 + github.com/swaggo/http-swagger v1.3.3 + github.com/swaggo/swag v1.8.5 github.com/vektah/gqlparser/v2 v2.5.0 golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 ) @@ -45,10 +47,7 @@ require ( github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/santhosh-tekuri/jsonschema/v5 v5.0.0 // indirect github.com/swaggo/files v0.0.0-20220728132757-551d4a08d97a // indirect - github.com/swaggo/http-swagger v1.3.3 // indirect - github.com/swaggo/swag v1.8.5 // indirect github.com/urfave/cli/v2 v2.8.1 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect @@ -56,7 +55,6 @@ require ( golang.org/x/sys v0.0.0-20220913175220-63ea55921009 // indirect golang.org/x/text v0.3.7 // indirect golang.org/x/tools v0.1.12 // indirect - golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 7df1b7e..234d533 100644 --- a/go.sum +++ b/go.sum @@ -2,11 +2,15 @@ github.com/99designs/gqlgen v0.17.16 h1:tTIw/cQ/uvf3iXIb2I6YSkdaDkmHmH2W2eZkVe0I github.com/99designs/gqlgen v0.17.16/go.mod h1:dnJdUkgfh8iw8CEx2hhTdgTQO/GvVWKLcm/kult5gwI= github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e h1:NeAW1fUYUEWhft7pkxDf6WoUvEZJ/uOKsvtpjLnn8MU= github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= github.com/Masterminds/squirrel v1.5.3 h1:YPpoceAcxuzIljlr5iWpNKaql7hLeG1KLSrhvdHpkZc= github.com/Masterminds/squirrel v1.5.3/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/agiledragon/gomonkey/v2 v2.3.1/go.mod h1:ap1AmDzcVOAz1YpeJ3TCzIgstoaWLA6jbbgxfB4w2iY= github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8= github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo= @@ -14,6 +18,7 @@ github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNg github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU= github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= @@ -49,8 +54,11 @@ github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiU github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA= github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= +github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= +github.com/go-openapi/spec v0.20.6/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= github.com/go-openapi/spec v0.20.7 h1:1Rlu/ZrOCCob0n+JKKJAWhNWMPW8bOZRg8FJaY+0SKI= github.com/go-openapi/spec v0.20.7/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= @@ -84,6 +92,7 @@ github.com/google/gops v0.3.25 h1:Pf6uw+cO6pDhc7HJ71NiG0x8dyQTeQcmg3HQFF39qVw= github.com/google/gops v0.3.25/go.mod h1:8A7ebAm0id9K3H0uOggeRVGxszSvnlURun9mg3GdYDw= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= @@ -107,6 +116,7 @@ github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8Hm github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/kevinmbeaulieu/eq-go v1.0.0/go.mod h1:G3S8ajA56gKBZm4UB9AOyoOS37JO3roToPzKNM8dtdM= github.com/keybase/go-ps v0.0.0-20190827175125-91aafc93ba19/go.mod h1:hY+WOq6m2FpbvyrI93sMaypsttvaIL5nhVR92dTMUcQ= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -166,6 +176,11 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/otiai10/copy v1.7.0/go.mod h1:rmRl6QPdJj6EiUqXQ/4Nn2lLXoNQjFCQbbNrxgc/t3U= +github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= +github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= +github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= +github.com/otiai10/mint v1.3.3/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -176,16 +191,19 @@ github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:Om github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/santhosh-tekuri/jsonschema v1.2.4 h1:hNhW8e7t+H1vgY+1QeEQpveR6D4+OwKPXCfD2aieJis= -github.com/santhosh-tekuri/jsonschema v1.2.4/go.mod h1:TEAUOeZSmIxTTuHatJzrvARHiuO9LYd+cIxzgEHCQI4= github.com/santhosh-tekuri/jsonschema/v5 v5.0.0 h1:TToq11gyfNlrMFZiYujSekIsPd9AmsA2Bj/iv+s4JHE= github.com/santhosh-tekuri/jsonschema/v5 v5.0.0/go.mod h1:FKdcjfQW6rpZSnxxUvEA5H/cDPdvJ/SZJQLWWXWGrZ0= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shirou/gopsutil/v3 v3.22.4/go.mod h1:D01hZJ4pVHPpCTZ3m3T2+wDF2YAGfd+H4ifUguaQzHM= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -193,13 +211,15 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/swaggo/files v0.0.0-20220610200504-28940afbdbfe/go.mod h1:lKJPbtWzJ9JhsTN1k1gZgleJWY/cqq0psdoMmaThG3w= github.com/swaggo/files v0.0.0-20220728132757-551d4a08d97a h1:kAe4YSu0O0UFn1DowNo2MY5p6xzqtJ/wQ7LZynSvGaY= github.com/swaggo/files v0.0.0-20220728132757-551d4a08d97a/go.mod h1:lKJPbtWzJ9JhsTN1k1gZgleJWY/cqq0psdoMmaThG3w= github.com/swaggo/http-swagger v1.3.3 h1:Hu5Z0L9ssyBLofaama21iYaF2VbWyA8jdohaaCGpHsc= github.com/swaggo/http-swagger v1.3.3/go.mod h1:sE+4PjD89IxMPm77FnkDz0sdO+p5lbXzrVWT6OTVVGo= +github.com/swaggo/swag v1.8.1/go.mod h1:ugemnJsPZm/kRwFUnzBlbHRd0JY9zE1M4F+uy2pAaPQ= github.com/swaggo/swag v1.8.5 h1:7NgtfXsXE+jrcOwRyiftGKW7Ppydj7tZiVenuRf1fE4= github.com/swaggo/swag v1.8.5/go.mod h1:jMLeXOOmYyjk8PvHTsXBdrubsNd9gUJTTCzL5iBnseg= github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk= @@ -208,6 +228,7 @@ github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVM github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= +github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= github.com/urfave/cli/v2 v2.8.1 h1:CGuYNZF9IKZY/rfBe3lJpccSoIY1ytfvmgQT90cNOl4= github.com/urfave/cli/v2 v2.8.1/go.mod h1:Z41J9TPoffeoqP0Iza0YbAhGvymRdZAd2uPmZ5JxRdY= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= @@ -218,9 +239,12 @@ github.com/vektah/gqlparser/v2 v2.5.0/go.mod h1:mPgqFBu/woKTVYWyNk8cO3kh4S/f4aRF github.com/xlab/treeprint v1.1.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= +github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= @@ -232,25 +256,28 @@ golang.org/x/crypto v0.0.0-20220513210258-46612604a0f9/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM= golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220513224357-95641704303c/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220906165146-f3363e06e74c h1:yKufUcDwucU5urd+50/Opbt4AYpqthk7wHpHok8f1lo= -golang.org/x/net v0.0.0-20220906165146-f3363e06e74c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220909164309-bea034e7d591 h1:D0B/7al0LLrVC8aWF4+oxpv/m8bc7ViFfVS8/gXGdqI= golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -264,10 +291,12 @@ golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -275,9 +304,8 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220513210249-45d2b4557a2a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220907062415-87db552b00fd h1:AZeIEzg+8RCELJYq8w+ODLVxFgLMMigSwO/ffKPEd9U= -golang.org/x/sys v0.0.0-20220907062415-87db552b00fd/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220913175220-63ea55921009 h1:PuvuRMeLWqsf/ZdT1UUZz0syhioyv1mzuFZsXs4fvhw= golang.org/x/sys v0.0.0-20220913175220-63ea55921009/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= @@ -294,9 +322,10 @@ golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220411224347-583f2d630306/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20= +golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= @@ -304,7 +333,6 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f h1:GGU+dLjvlC3qDwqYgL6UgRmHXhOOgns0bZu2Ty5mm6U= golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= @@ -317,6 +345,7 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= From bad3f9ea95b0f0e54892fc645907ff2b8cf1c32f Mon Sep 17 00:00:00 2001 From: Christoph Kluge Date: Thu, 12 Jan 2023 11:26:01 +0100 Subject: [PATCH 05/11] Makeover of searchbar, add "not found" results-redirects - Tagged Search has priority - Best guess search if no tag provided - "Hard" errors only on searchtag-malformed or searchtag-unknown, or if best guess search fails --- cmd/cc-backend/main.go | 65 ++++++++++++++++++++++------- internal/repository/job.go | 76 +++++++++++++++++++++++++--------- web/frontend/src/Header.svelte | 5 ++- 3 files changed, 111 insertions(+), 35 deletions(-) diff --git a/cmd/cc-backend/main.go b/cmd/cc-backend/main.go index bc6a265..f52f8a3 100644 --- a/cmd/cc-backend/main.go +++ b/cmd/cc-backend/main.go @@ -298,27 +298,64 @@ func main() { // Send a searchId and then reply with a redirect to a user, or directly send query to job table for jobid and project. secured.HandleFunc("/search", func(rw http.ResponseWriter, r *http.Request) { if search := r.URL.Query().Get("searchId"); search != "" { + splitSearch := strings.Split(search, ":") - _, username, project, err := api.JobRepository.FindJobOrUserOrProject(r.Context(), search) + if (len(splitSearch) == 2) { + switch splitSearch[0] { + case "jobId": + http.Redirect(rw, r, "/monitoring/jobs/?jobId="+url.QueryEscape(splitSearch[1]), http.StatusTemporaryRedirect) // All Users: Redirect to Tablequery + return + case "jobName": + http.Redirect(rw, r, "/monitoring/jobs/?jobName="+url.QueryEscape(splitSearch[1]), http.StatusTemporaryRedirect) // All Users: Redirect to Tablequery + return + case "projectId": + project, _ := api.JobRepository.FindProject(r.Context(), splitSearch[1]) // Restricted: projectId + if project != "" { + http.Redirect(rw, r, "/monitoring/jobs/?projectMatch=eq&project="+url.QueryEscape(project), http.StatusTemporaryRedirect) + return + } else { + http.Redirect(rw, r, "/monitoring/jobs/?jobId=NotFound", http.StatusTemporaryRedirect) // Workaround to display correctly empty table + } + case "username": + username, _ := api.JobRepository.FindUser(r.Context(), splitSearch[1]) // Restricted: username + if username != "" { + http.Redirect(rw, r, "/monitoring/user/"+username, http.StatusTemporaryRedirect) + return + } else { + http.Redirect(rw, r, "/monitoring/jobs/?jobId=NotFound", http.StatusTemporaryRedirect) // Workaround to display correctly empty table + } + default: + http.Error(rw, "'searchId' type parameter unknown", http.StatusBadRequest) + } - if err != nil { - http.Error(rw, err.Error(), http.StatusInternalServerError) - return - } + } else if (len(splitSearch) == 1) { + jobname, username, project, err := api.JobRepository.FindJobnameOrUserOrProject(r.Context(), search) // Determine Access within + + if err != nil { + http.Error(rw, err.Error(), http.StatusInternalServerError) + return + } + + if username != "" { + http.Redirect(rw, r, "/monitoring/user/"+username, http.StatusTemporaryRedirect) // User: Redirect to user page + return + } else if (project != "") { + http.Redirect(rw, r, "/monitoring/jobs/?projectMatch=eq&project="+url.QueryEscape(search), http.StatusTemporaryRedirect) // projectId (equal) + return + } else if (jobname != "") { + http.Redirect(rw, r, "/monitoring/jobs/?jobName="+url.QueryEscape(search), http.StatusTemporaryRedirect) // JobName (contains) + return + } else { + http.Redirect(rw, r, "/monitoring/jobs/?jobId="+url.QueryEscape(search), http.StatusTemporaryRedirect) // No Result: Probably jobId + return + } - if username != "" { - http.Redirect(rw, r, "/monitoring/user/"+username, http.StatusTemporaryRedirect) - return - } else if (project != "") { - http.Redirect(rw, r, "/monitoring/jobs/?projectMatch=eq&project="+project, http.StatusTemporaryRedirect) // Directly to table! - return } else { - http.Redirect(rw, r, "/monitoring/jobs/?jobId="+url.QueryEscape(search), http.StatusTemporaryRedirect) // Directly to table! - return + http.Error(rw, "'searchId' query parameter malformed", http.StatusBadRequest) } } else { - http.Error(rw, "'searchId' query parameter missing", http.StatusBadRequest) + http.Redirect(rw, r, "/monitoring/jobs/?", http.StatusTemporaryRedirect) } }) diff --git a/internal/repository/job.go b/internal/repository/job.go index 99db9d8..7cf62c7 100644 --- a/internal/repository/job.go +++ b/internal/repository/job.go @@ -393,53 +393,91 @@ func (r *JobRepository) Archive( return nil } -var ErrNotFound = errors.New("no such job, project or user") +var ErrNotFound = errors.New("no such jobname, project or user") +var ErrForbidden = errors.New("not authorized") // FindJobOrUserOrProject returns a job database ID or a username or a projectId if a job or user or project matches the search term. // As 0 is a valid job id, check if username/projectId is "" instead in order to check what matched. // If nothing matches the search, `ErrNotFound` is returned. -// TO BE IMPROVED; Search by JobNAME - -func (r *JobRepository) FindJobOrUserOrProject(ctx context.Context, searchterm string) (job int64, username string, project string, err error) { +func (r *JobRepository) FindJobnameOrUserOrProject(ctx context.Context, searchterm string) (metasnip string, username string, project string, err error) { user := auth.GetUser(ctx) - if id, err := strconv.Atoi(searchterm); err == nil { - qb := sq.Select("job.id").From("job").Where("job.job_id = ?", id) - if user != nil && !user.HasRole(auth.RoleAdmin) && !user.HasRole(auth.RoleSupport) { - qb = qb.Where("job.user = ?", user.Username) + if _, err := strconv.Atoi(searchterm); err == nil { // Return empty on successful conversion: parent method will redirect for integer jobId + return "", "", "", nil + } else { // has to have letters + + if user == nil || user.HasRole(auth.RoleAdmin) || user.HasRole(auth.RoleSupport) { + err := sq.Select("job.user").Distinct().From("job"). + Where("job.user = ?", searchterm). + RunWith(r.stmtCache).QueryRow().Scan(&username) + if err != nil && err != sql.ErrNoRows { + return "", "", "", err + } else if err == nil { + return "", username, "", nil + } } - err := qb.RunWith(r.stmtCache).QueryRow().Scan(&job) + if user == nil || user.HasRole(auth.RoleAdmin) || user.HasRole(auth.RoleSupport) { + err := sq.Select("job.project").Distinct().From("job"). + Where("job.project = ?", searchterm). + RunWith(r.stmtCache).QueryRow().Scan(&project) + if err != nil && err != sql.ErrNoRows { + return "", "", "", err + } else if err == nil { + return "", "", project, nil + } + } + + // All Authorizations: If unlabeled query not username or projectId, try for jobname: Match Metadata, on hit, parent method redirects to jobName GQL query + err := sq.Select("job.cluster").Distinct().From("job"). + Where("job.meta_data LIKE ?", "%" + searchterm + "%"). + RunWith(r.stmtCache).QueryRow().Scan(&metasnip) if err != nil && err != sql.ErrNoRows { - return 0, "", "", err + return "", "", "", err } else if err == nil { - return job, "", "", nil + return metasnip[0:1], "", "", nil } - } + return "", "", "", ErrNotFound + } +} + +func (r *JobRepository) FindUser(ctx context.Context, searchterm string) (username string, err error) { + user := auth.GetUser(ctx) if user == nil || user.HasRole(auth.RoleAdmin) || user.HasRole(auth.RoleSupport) { err := sq.Select("job.user").Distinct().From("job"). Where("job.user = ?", searchterm). RunWith(r.stmtCache).QueryRow().Scan(&username) if err != nil && err != sql.ErrNoRows { - return 0, "", "", err + return "", err } else if err == nil { - return 0, username, "", nil + return username, nil } - } + return "", ErrNotFound + } else { + log.Infof("Non-Admin User %s : Requested Query Username -> %s: Forbidden", user, username) + return "", ErrForbidden + } +} + +func (r *JobRepository) FindProject(ctx context.Context, searchterm string) (project string, err error) { + user := auth.GetUser(ctx) if user == nil || user.HasRole(auth.RoleAdmin) || user.HasRole(auth.RoleSupport) { err := sq.Select("job.project").Distinct().From("job"). Where("job.project = ?", searchterm). RunWith(r.stmtCache).QueryRow().Scan(&project) if err != nil && err != sql.ErrNoRows { - return 0, "", "", err + return "", err } else if err == nil { - return 0, "", project, nil + return project, nil } - } + return "", ErrNotFound - return 0, "", "", ErrNotFound + } else { + log.Infof("Non-Admin User %s : Requested Query Project -> %s: Forbidden", user, project) + return "", ErrForbidden + } } func (r *JobRepository) Partitions(cluster string) ([]string, error) { diff --git a/web/frontend/src/Header.svelte b/web/frontend/src/Header.svelte index b541b2d..96a5f06 100644 --- a/web/frontend/src/Header.svelte +++ b/web/frontend/src/Header.svelte @@ -1,7 +1,7 @@