mirror of
https://github.com/sourcegraph/sourcegraph.git
synced 2026-02-06 19:51:50 +00:00
Insights: support filtering insightViews graphql query based on a search context embedded query pattern (#33686)
This commit is contained in:
parent
ca8deb2895
commit
1c290f11a1
@ -337,6 +337,7 @@ type InsightViewControlsInput struct {
|
||||
type InsightViewFiltersInput struct {
|
||||
IncludeRepoRegex *string
|
||||
ExcludeRepoRegex *string
|
||||
SearchContexts *[]string
|
||||
}
|
||||
|
||||
type LineChartSearchInsightDataSeriesInput struct {
|
||||
|
||||
@ -622,6 +622,11 @@ input InsightViewFiltersInput {
|
||||
A regex string for which to exclude repositories in a filter.
|
||||
"""
|
||||
excludeRepoRegex: String
|
||||
|
||||
"""
|
||||
A list of query based search contexts to include in the filters for the view.
|
||||
"""
|
||||
searchContexts: [String!]
|
||||
}
|
||||
|
||||
"""
|
||||
|
||||
@ -4,6 +4,16 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/sourcegraph/sourcegraph/internal/database/dbutil"
|
||||
|
||||
sctypes "github.com/sourcegraph/sourcegraph/internal/types"
|
||||
|
||||
"github.com/sourcegraph/sourcegraph/internal/search/searchcontexts"
|
||||
|
||||
"github.com/sourcegraph/sourcegraph/lib/errors"
|
||||
|
||||
"github.com/sourcegraph/sourcegraph/internal/database"
|
||||
|
||||
"github.com/sourcegraph/sourcegraph/enterprise/internal/insights/timeseries"
|
||||
|
||||
"github.com/sourcegraph/sourcegraph/enterprise/internal/insights/query"
|
||||
@ -14,6 +24,8 @@ import (
|
||||
"github.com/sourcegraph/sourcegraph/enterprise/internal/insights/background/queryrunner"
|
||||
"github.com/sourcegraph/sourcegraph/enterprise/internal/insights/store"
|
||||
"github.com/sourcegraph/sourcegraph/internal/database/basestore"
|
||||
|
||||
searchquery "github.com/sourcegraph/sourcegraph/internal/search/query"
|
||||
)
|
||||
|
||||
var _ graphqlbackend.InsightSeriesResolver = &insightSeriesResolver{}
|
||||
@ -60,21 +72,36 @@ func (r *insightSeriesResolver) Points(ctx context.Context, args *graphqlbackend
|
||||
opts.To = &args.To.Time
|
||||
}
|
||||
|
||||
includeRepo := func(regex ...string) {
|
||||
opts.IncludeRepoRegex = append(opts.IncludeRepoRegex, regex...)
|
||||
}
|
||||
excludeRepo := func(regex ...string) {
|
||||
opts.ExcludeRepoRegex = append(opts.ExcludeRepoRegex, regex...)
|
||||
}
|
||||
|
||||
// to preserve backwards compatibility, we are going to keep the arguments on this resolver for now. Ideally
|
||||
// we would deprecate these in favor of passing arguments from a higher level resolver (insight view) to match
|
||||
// the model of how we want default filters to work at the insight view level. That said, we will only inherit
|
||||
// higher resolver filters if provided filter arguments are nil.
|
||||
if args.IncludeRepoRegex != nil {
|
||||
opts.IncludeRepoRegex = *args.IncludeRepoRegex
|
||||
includeRepo(*args.IncludeRepoRegex)
|
||||
} else if r.filters.IncludeRepoRegex != nil {
|
||||
opts.IncludeRepoRegex = *r.filters.IncludeRepoRegex
|
||||
includeRepo(*r.filters.IncludeRepoRegex)
|
||||
}
|
||||
if args.ExcludeRepoRegex != nil {
|
||||
opts.ExcludeRepoRegex = *args.ExcludeRepoRegex
|
||||
excludeRepo(*args.ExcludeRepoRegex)
|
||||
} else if r.filters.ExcludeRepoRegex != nil {
|
||||
opts.ExcludeRepoRegex = *r.filters.ExcludeRepoRegex
|
||||
excludeRepo(*r.filters.ExcludeRepoRegex)
|
||||
}
|
||||
|
||||
scLoader := &scLoader{primary: r.workerBaseStore.Handle().DB()}
|
||||
inc, exc, err := unwrapSearchContexts(ctx, scLoader, r.filters.SearchContexts)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unwrapSearchContexts")
|
||||
}
|
||||
includeRepo(inc...)
|
||||
excludeRepo(exc...)
|
||||
|
||||
points, err := r.insightsStore.SeriesPoints(ctx, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -86,6 +113,47 @@ func (r *insightSeriesResolver) Points(ctx context.Context, args *graphqlbackend
|
||||
return resolvers, nil
|
||||
}
|
||||
|
||||
// SearchContextLoader loads search contexts just from the full name of the
|
||||
// context. This will not verify that the calling context owns the context, it
|
||||
// will load regardless of the current user.
|
||||
type SearchContextLoader interface {
|
||||
GetByName(ctx context.Context, name string) (*sctypes.SearchContext, error)
|
||||
}
|
||||
|
||||
type scLoader struct {
|
||||
primary dbutil.DB
|
||||
}
|
||||
|
||||
func (l *scLoader) GetByName(ctx context.Context, name string) (*sctypes.SearchContext, error) {
|
||||
db := database.NewDB(l.primary)
|
||||
return searchcontexts.ResolveSearchContextSpec(ctx, db, name)
|
||||
}
|
||||
|
||||
func unwrapSearchContexts(ctx context.Context, loader SearchContextLoader, rawContexts []string) ([]string, []string, error) {
|
||||
var include []string
|
||||
var exclude []string
|
||||
|
||||
for _, rawContext := range rawContexts {
|
||||
searchContext, err := loader.GetByName(ctx, rawContext)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if searchContext.Query != "" {
|
||||
var plan searchquery.Plan
|
||||
plan, err := searchquery.Pipeline(
|
||||
searchquery.Init(searchContext.Query, searchquery.SearchTypeRegex),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrapf(err, "failed to parse search query for search context: %s", rawContext)
|
||||
}
|
||||
inc, exc := plan.ToParseTree().Repositories()
|
||||
include = append(include, inc...)
|
||||
exclude = append(exclude, exc...)
|
||||
}
|
||||
}
|
||||
return include, exclude, nil
|
||||
}
|
||||
|
||||
func (r *insightSeriesResolver) Status(ctx context.Context) (graphqlbackend.InsightStatusResolver, error) {
|
||||
seriesID := r.series.SeriesID
|
||||
|
||||
|
||||
@ -101,7 +101,7 @@ func TestResolver_InsightSeries(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
autogold.Want("insights[0][0].Points store opts", `{"SeriesID":"1234567","RepoID":null,"Excluded":null,"Included":null,"IncludeRepoRegex":"","ExcludeRepoRegex":"","From":"2006-01-02T15:04:05Z","To":"2006-01-03T15:04:05Z","Limit":0}`).Equal(t, string(json))
|
||||
autogold.Want("insights[0][0].Points store opts", `{"SeriesID":"1234567","RepoID":null,"Excluded":null,"Included":null,"IncludeRepoRegex":null,"ExcludeRepoRegex":null,"From":"2006-01-02T15:04:05Z","To":"2006-01-03T15:04:05Z","Limit":0}`).Equal(t, string(json))
|
||||
return []store.SeriesPoint{
|
||||
{Time: args.From.Time, Value: 1},
|
||||
{Time: args.From.Time, Value: 2},
|
||||
|
||||
@ -175,12 +175,27 @@ func expandCaptureGroupSeriesRecorded(ctx context.Context, definition types.Insi
|
||||
}
|
||||
opts.From = &oldest
|
||||
|
||||
includeRepo := func(regex ...string) {
|
||||
opts.IncludeRepoRegex = append(opts.IncludeRepoRegex, regex...)
|
||||
}
|
||||
excludeRepo := func(regex ...string) {
|
||||
opts.ExcludeRepoRegex = append(opts.ExcludeRepoRegex, regex...)
|
||||
}
|
||||
|
||||
if filters.IncludeRepoRegex != nil {
|
||||
opts.IncludeRepoRegex = *filters.IncludeRepoRegex
|
||||
includeRepo(*filters.IncludeRepoRegex)
|
||||
}
|
||||
if filters.ExcludeRepoRegex != nil {
|
||||
opts.ExcludeRepoRegex = *filters.ExcludeRepoRegex
|
||||
excludeRepo(*filters.ExcludeRepoRegex)
|
||||
}
|
||||
scLoader := &scLoader{primary: r.workerBaseStore.Handle().DB()}
|
||||
inc, exc, err := unwrapSearchContexts(ctx, scLoader, filters.SearchContexts)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unwrapSearchContexts")
|
||||
}
|
||||
includeRepo(inc...)
|
||||
excludeRepo(exc...)
|
||||
|
||||
groupedByCapture := make(map[string][]store.SeriesPoint)
|
||||
allPoints, err := r.timeSeriesStore.SeriesPoints(ctx, opts)
|
||||
if err != nil {
|
||||
@ -797,6 +812,7 @@ type InsightViewQueryConnectionResolver struct {
|
||||
|
||||
func (d *InsightViewQueryConnectionResolver) Nodes(ctx context.Context) ([]graphqlbackend.InsightViewResolver, error) {
|
||||
resolvers := make([]graphqlbackend.InsightViewResolver, 0)
|
||||
var scs []string
|
||||
|
||||
views, _, err := d.computeViews(ctx)
|
||||
if err != nil {
|
||||
@ -805,9 +821,13 @@ func (d *InsightViewQueryConnectionResolver) Nodes(ctx context.Context) ([]graph
|
||||
for i := range views {
|
||||
resolver := &insightViewResolver{view: &views[i], baseInsightResolver: d.baseInsightResolver}
|
||||
if d.args.Filters != nil {
|
||||
if d.args.Filters.SearchContexts != nil {
|
||||
scs = *d.args.Filters.SearchContexts
|
||||
}
|
||||
resolver.overrideFilters = &types.InsightViewFilters{
|
||||
IncludeRepoRegex: d.args.Filters.IncludeRepoRegex,
|
||||
ExcludeRepoRegex: d.args.Filters.ExcludeRepoRegex,
|
||||
SearchContexts: scs,
|
||||
}
|
||||
}
|
||||
resolvers = append(resolvers, resolver)
|
||||
|
||||
@ -107,8 +107,8 @@ type SeriesPointsOpts struct {
|
||||
// TODO(slimsag): Add ability to filter based on repo name, original name.
|
||||
// TODO(slimsag): Add ability to do limited filtering based on metadata.
|
||||
|
||||
IncludeRepoRegex string
|
||||
ExcludeRepoRegex string
|
||||
IncludeRepoRegex []string
|
||||
ExcludeRepoRegex []string
|
||||
|
||||
// Time ranges to query from/to, if non-nil, in UTC.
|
||||
From, To *time.Time
|
||||
@ -246,10 +246,20 @@ func seriesPointsQuery(opts SeriesPointsOpts) *sqlf.Query {
|
||||
preds = append(preds, sqlf.Sprintf(s))
|
||||
}
|
||||
if len(opts.IncludeRepoRegex) > 0 {
|
||||
preds = append(preds, sqlf.Sprintf("rn.name ~ %s", opts.IncludeRepoRegex))
|
||||
for _, regex := range opts.IncludeRepoRegex {
|
||||
if len(regex) == 0 {
|
||||
continue
|
||||
}
|
||||
preds = append(preds, sqlf.Sprintf("rn.name ~ %s", regex))
|
||||
}
|
||||
}
|
||||
if len(opts.ExcludeRepoRegex) > 0 {
|
||||
preds = append(preds, sqlf.Sprintf("rn.name !~ %s", opts.ExcludeRepoRegex))
|
||||
for _, regex := range opts.ExcludeRepoRegex {
|
||||
if len(regex) == 0 {
|
||||
continue
|
||||
}
|
||||
preds = append(preds, sqlf.Sprintf("rn.name !~ %s", regex))
|
||||
}
|
||||
}
|
||||
|
||||
if len(preds) == 0 {
|
||||
|
||||
@ -51,6 +51,7 @@ type Insight struct {
|
||||
type InsightViewFilters struct {
|
||||
IncludeRepoRegex *string
|
||||
ExcludeRepoRegex *string
|
||||
SearchContexts []string
|
||||
}
|
||||
|
||||
// InsightViewSeriesMetadata contains metadata about a viewable insight series such as render properties.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user