insights: remove unused code for non-isolated settings migration (#43916)

This commit is contained in:
coury-clark 2022-11-04 09:13:04 -07:00 committed by GitHub
parent 69af2da113
commit 16ae2dac85
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 5 additions and 1054 deletions

View File

@ -37,6 +37,7 @@ All notable changes to Sourcegraph are documented in this file.
- Remove the older `log.gitserver.accessLogs` site config setting. The setting is succeeded by `log.auditLog.gitserverAccess`. [#43174](https://github.com/sourcegraph/sourcegraph/pull/43174)
- Remove `LOG_ALL_GRAPHQL_REQUESTS` env var. The setting is succeeded by `log.auditLog.graphQL`. [#43181](https://github.com/sourcegraph/sourcegraph/pull/43181)
- Removed support for setting `SRC_ENDPOINTS_CONSISTENT_HASH`. This was an environment variable to support the transition to a new consistent hashing scheme introduced in 3.31.0. [#43528](https://github.com/sourcegraph/sourcegraph/pull/43528)
- Removed legacy environment variable `ENABLE_CODE_INSIGHTS_SETTINGS_STORAGE` used in old versions of Code Insights to fall back to JSON settings based storage. All data was previously migrated in version 3.35 and this is no longer supported.
## 4.1.2

View File

@ -6,9 +6,10 @@ import (
"strconv"
"time"
"github.com/sourcegraph/log"
"go.opentelemetry.io/otel"
"github.com/sourcegraph/log"
"github.com/prometheus/client_golang/prometheus"
"github.com/sourcegraph/sourcegraph/cmd/frontend/envvar"
@ -112,13 +113,6 @@ func GetBackgroundJobs(ctx context.Context, logger log.Logger, mainAppDB databas
}
// this flag will allow users to ENABLE the settings sync job. This is a last resort option if for some reason the new GraphQL API does not work. This
// should not be published as an option externally, and will be deprecated as soon as possible.
enableSync, _ := strconv.ParseBool(os.Getenv("ENABLE_CODE_INSIGHTS_SETTINGS_STORAGE"))
if enableSync {
observationContext.Logger.Info("Enabling Code Insights Settings Storage - This is a deprecated functionality!")
routines = append(routines, discovery.NewMigrateSettingInsightsJob(ctx, mainAppDB, insightsDB))
}
routines = append(
routines,
pings.NewInsightsPingEmitterJob(ctx, mainAppDB, insightsDB),

View File

@ -7,12 +7,12 @@ import (
"time"
"github.com/hexops/autogold"
"github.com/sourcegraph/log/logtest"
"golang.org/x/time/rate"
"github.com/sourcegraph/log/logtest"
"github.com/sourcegraph/sourcegraph/enterprise/internal/insights/background/queryrunner"
"github.com/sourcegraph/sourcegraph/enterprise/internal/insights/compression"
"github.com/sourcegraph/sourcegraph/enterprise/internal/insights/discovery"
"github.com/sourcegraph/sourcegraph/enterprise/internal/insights/store"
itypes "github.com/sourcegraph/sourcegraph/enterprise/internal/insights/types"
"github.com/sourcegraph/sourcegraph/internal/api"
@ -61,11 +61,6 @@ func testHistoricalEnqueuer(t *testing.T, p *testParams) *testResults {
featureFlagStore.GetFeatureFlagFunc.SetDefaultReturn(&mockedFeatureFlag, nil)
settingStore := discovery.NewMockSettingStore()
if p.settings != nil {
settingStore.GetLatestFunc.SetDefaultReturn(p.settings, nil)
}
dataSeriesStore := store.NewMockDataSeriesStore()
dataSeriesStore.GetDataSeriesFunc.SetDefaultReturn([]itypes.InsightSeries{
{

View File

@ -1,508 +0,0 @@
package discovery
import (
"context"
"time"
"github.com/inconshreveable/log15"
"github.com/segmentio/ksuid"
edb "github.com/sourcegraph/sourcegraph/enterprise/internal/database"
"github.com/sourcegraph/sourcegraph/enterprise/internal/insights/store"
"github.com/sourcegraph/sourcegraph/enterprise/internal/insights/types"
"github.com/sourcegraph/sourcegraph/internal/api"
"github.com/sourcegraph/sourcegraph/internal/database"
"github.com/sourcegraph/sourcegraph/internal/database/dbutil"
"github.com/sourcegraph/sourcegraph/internal/goroutine"
"github.com/sourcegraph/sourcegraph/internal/insights"
"github.com/sourcegraph/sourcegraph/internal/jsonc"
"github.com/sourcegraph/sourcegraph/lib/errors"
"github.com/sourcegraph/sourcegraph/schema"
)
// SettingStore is a subset of the API exposed by the database.Settings() store.
type SettingStore interface {
GetLatest(context.Context, api.SettingsSubject) (*api.Settings, error)
GetLatestSchemaSettings(context.Context, api.SettingsSubject) (*schema.Settings, error)
}
// InsightFilterArgs contains arguments that will filter out insights when discovered if matched.
type InsightFilterArgs struct {
Ids []string
}
// Discover uses the given settings store to look for insights in the global user settings.
func Discover(ctx context.Context, settingStore SettingStore, loader insights.Loader, args InsightFilterArgs) ([]insights.SearchInsight, error) {
discovered, err := discoverAll(ctx, settingStore, loader)
if err != nil {
return []insights.SearchInsight{}, err
}
return applyFilters(discovered, args), nil
}
// discoverIntegrated will load any insights that are integrated (meaning backend capable) from the extensions settings
func discoverIntegrated(ctx context.Context, loader insights.Loader) ([]insights.SearchInsight, error) {
return loader.LoadAll(ctx)
}
func discoverAll(ctx context.Context, settingStore SettingStore, loader insights.Loader) ([]insights.SearchInsight, error) {
// Get latest Global user settings.
subject := api.SettingsSubject{Site: true}
globalSettingsRaw, err := settingStore.GetLatest(ctx, subject)
if err != nil {
return nil, err
}
globalSettings, err := parseUserSettings(globalSettingsRaw)
if err != nil {
return nil, err
}
results := convertFromBackendInsight(globalSettings.Insights)
integrated, err := discoverIntegrated(ctx, loader)
if err != nil {
return nil, err
}
return append(results, integrated...), nil
}
// convertFromBackendInsight is an adapter method that will transform the 'backend' insight schema to the schema that is
// used by the extensions on the frontend, and will be used in the future. As soon as the backend and frontend are fully integrated these
// 'backend' insights will be deprecated.
func convertFromBackendInsight(backendInsights []*schema.Insight) []insights.SearchInsight {
converted := make([]insights.SearchInsight, 0)
for _, backendInsight := range backendInsights {
var temp insights.SearchInsight
temp.Title = backendInsight.Title
temp.Description = backendInsight.Description
for _, series := range backendInsight.Series {
temp.Series = append(temp.Series, insights.TimeSeries{
Name: series.Label,
Query: series.Search,
})
}
temp.ID = backendInsight.Id
converted = append(converted, temp)
}
return converted
}
func parseUserSettings(settings *api.Settings) (*schema.Settings, error) {
if settings == nil {
// Settings have never been saved for this subject; equivalent to `{}`.
return &schema.Settings{}, nil
}
var v schema.Settings
if err := jsonc.Unmarshal(settings.Contents, &v); err != nil {
return nil, err
}
return &v, nil
}
// applyFilters will apply any filters defined as arguments serially and return the intersection.
func applyFilters(total []insights.SearchInsight, args InsightFilterArgs) []insights.SearchInsight {
filtered := total
if len(args.Ids) > 0 {
filtered = filterByIds(args.Ids, total)
}
return filtered
}
func filterByIds(ids []string, insight []insights.SearchInsight) []insights.SearchInsight {
filtered := make([]insights.SearchInsight, 0)
keys := make(map[string]bool)
for _, id := range ids {
keys[id] = true
}
for _, searchInsight := range insight {
if _, ok := keys[searchInsight.ID]; ok {
filtered = append(filtered, searchInsight)
}
}
return filtered
}
type settingMigrator struct {
base database.DB
insights edb.InsightsDB
}
// NewMigrateSettingInsightsJob will migrate insights from settings into the database. This is a job that will be
// deprecated as soon as this functionality is available over an API.
func NewMigrateSettingInsightsJob(ctx context.Context, base database.DB, insights edb.InsightsDB) goroutine.BackgroundRoutine {
interval := time.Minute * 10
m := settingMigrator{
base: base,
insights: insights,
}
return goroutine.NewPeriodicGoroutine(ctx, interval,
goroutine.NewHandlerWithErrorMessage("insight_setting_migrator", m.migrate))
}
func (m *settingMigrator) migrate(ctx context.Context) error {
loader := insights.NewLoader(m.base)
dashboardStore := store.NewDashboardStore(m.insights)
discovered, err := discoverIntegrated(ctx, loader)
if err != nil {
return err
}
justInTimeInsights, err := insights.GetSearchInsights(ctx, m.base, insights.All)
if err != nil {
return errors.Wrap(err, "failed to fetch just-in-time insights from all settings")
}
langStatsInsights, err := insights.GetLangStatsInsights(ctx, m.base, insights.All)
if err != nil {
return errors.Wrap(err, "failed to fetch lang stats insights from all settings")
}
log15.Info("insights migration: migrating backend insights")
m.migrateInsights(ctx, discovered, backend)
log15.Info("insights migration: migrating frontend search insights")
m.migrateInsights(ctx, justInTimeInsights, frontend)
log15.Info("insights migration: migrating frontend lang stats insights")
m.migrateLangStatsInsights(ctx, langStatsInsights)
log15.Info("insights migration: migrating dashboards")
dashboards, err := loader.LoadDashboards(ctx)
if err != nil {
return err
}
err = clearDashboards(ctx, m.insights)
if err != nil {
return errors.Wrap(err, "clearDashboards")
}
for _, dashboard := range dashboards {
err := migrateDashboard(ctx, dashboardStore, dashboard)
if err != nil {
log15.Info("insights migration: error while migrating dashboard", "error", err)
continue
}
}
err = purgeOrphanFrontendSeries(ctx, m.insights)
if err != nil {
return errors.Wrap(err, "failed to purge orphaned frontend series")
}
return nil
}
type migrationBatch string
const (
backend migrationBatch = "backend"
frontend migrationBatch = "frontend"
)
func (m *settingMigrator) migrateInsights(ctx context.Context, toMigrate []insights.SearchInsight, batch migrationBatch) {
insightStore := store.NewInsightStore(m.insights)
var count, skipped, errorCount int
for _, d := range toMigrate {
if d.ID == "" {
// we need a unique ID, and if for some reason this insight doesn't have one, it can't be migrated.
skipped++
continue
}
err := insightStore.DeleteViewByUniqueID(ctx, d.ID)
log15.Info("insights migration: deleting insight view", "unique_id", d.ID)
if err != nil {
// if we fail here there isn't much we can do in this migration, so continue
skipped++
continue
}
err = migrateSeries(ctx, insightStore, d, batch)
if err != nil {
// we can't do anything about errors, so we will just skip it and log it
errorCount++
log15.Error("insights migration: error while migrating insight", "error", err)
}
count++
}
log15.Info("insights settings migration batch complete", "batch", batch, "count", count, "skipped", skipped, "errors", errorCount)
}
func (m *settingMigrator) migrateLangStatsInsights(ctx context.Context, toMigrate []insights.LangStatsInsight) {
insightStore := store.NewInsightStore(m.insights)
tx, err := insightStore.Transact(ctx)
if err != nil {
log15.Info("insights migration: problem connecting to store, aborting lang stats migration")
return
}
defer func() { err = tx.Store.Done(err) }()
var count, skipped, errorCount int
for _, d := range toMigrate {
if d.ID == "" {
// we need a unique ID, and if for some reason this insight doesn't have one, it can't be migrated.
skipped++
continue
}
err := insightStore.DeleteViewByUniqueID(ctx, d.ID)
log15.Info("insights migration: deleting insight view", "unique_id", d.ID)
if err != nil {
// if we fail here there isn't much we can do in this migration, so continue
skipped++
continue
}
err = migrateLangStatSeries(ctx, insightStore, d)
if err != nil {
// we can't do anything about errors, so we will just skip it and log it
errorCount++
log15.Error("insights migration: error while migrating insight", "error", err)
}
count++
}
log15.Info("insights settings migration batch complete", "batch", "langStats", "count", count, "skipped", skipped, "errors", errorCount)
}
func migrateDashboard(ctx context.Context, dashboardStore *store.DBDashboardStore, from insights.SettingDashboard) (err error) {
tx, err := dashboardStore.Transact(ctx)
if err != nil {
return err
}
defer func() { err = tx.Store.Done(err) }()
dashboard := types.Dashboard{
Title: from.Title,
InsightIDs: from.InsightIds,
}
log15.Info("insights migration: migrating dashboard", "settings_unique_id", from.ID)
var grants []store.DashboardGrant
if from.UserID != nil {
grants = []store.DashboardGrant{store.UserDashboardGrant(int(*from.UserID))}
} else if from.OrgID != nil {
grants = []store.DashboardGrant{store.OrgDashboardGrant(int(*from.OrgID))}
} else {
grants = []store.DashboardGrant{store.GlobalDashboardGrant()}
}
_, err = dashboardStore.CreateDashboard(ctx, store.CreateDashboardArgs{Dashboard: dashboard, Grants: grants})
if err != nil {
return err
}
return nil
}
// clearDashboards will delete all dashboards. This should be deprecated as soon as possible, and is only useful to ensure a smooth migration from settings to database.
func clearDashboards(ctx context.Context, db dbutil.DB) error {
_, err := db.ExecContext(ctx, deleteAllDashboardsSql)
if err != nil {
return err
}
return nil
}
const deleteAllDashboardsSql = `
delete from dashboard where save != true;
`
func purgeOrphanFrontendSeries(ctx context.Context, db dbutil.DB) error {
_, err := db.ExecContext(ctx, purgeOrphanedFrontendSeries)
if err != nil {
return err
}
return nil
}
const purgeOrphanedFrontendSeries = `
with distinct_series_ids as (select distinct ivs.insight_series_id from insight_view_series ivs)
delete from insight_series
where id not in (select * from distinct_series_ids);
`
// migrateSeries will attempt to take an insight defined in Sourcegraph settings and migrate it to the database.
func migrateSeries(ctx context.Context, insightStore *store.InsightStore, from insights.SearchInsight, batch migrationBatch) (err error) {
tx, err := insightStore.Transact(ctx)
if err != nil {
return err
}
defer func() { err = tx.Store.Done(err) }()
log15.Info("insights migration: attempting to migrate insight", "unique_id", from.ID)
dataSeries := make([]types.InsightSeries, len(from.Series))
metadata := make([]types.InsightViewSeriesMetadata, len(from.Series))
for i, timeSeries := range from.Series {
temp := types.InsightSeries{
Query: timeSeries.Query,
}
if batch == frontend {
temp.Repositories = from.Repositories
if temp.Repositories == nil {
// this shouldn't be possible, but if for some reason we get here there is a malformed schema
return errors.New("invalid schema for frontend insight, missing repositories")
}
interval := parseTimeInterval(from)
temp.SampleIntervalUnit = string(interval.unit)
temp.SampleIntervalValue = interval.value
temp.SeriesID = ksuid.New().String() // this will cause some orphan records, but we can't use the query to match because of repo / time scope. We will purge orphan records at the end of this job.
temp.JustInTime = true
temp.GenerationMethod = types.Search
} else if batch == backend {
temp.SampleIntervalUnit = string(types.Month)
temp.SampleIntervalValue = 1
temp.NextRecordingAfter = insights.NextRecording(time.Now())
temp.NextSnapshotAfter = insights.NextSnapshot(time.Now())
temp.SeriesID = Encode(timeSeries)
temp.JustInTime = false
temp.GenerationMethod = types.Search
} else {
// not a real possibility
return errors.Newf("invalid batch %v", batch)
}
var series types.InsightSeries
// first check if this data series already exists (somebody already created an insight of this query), in which case we just need to attach the view to this data series
existing, err := tx.GetDataSeries(ctx, store.GetDataSeriesArgs{SeriesID: temp.SeriesID})
if err != nil {
return errors.Wrapf(err, "unable to migrate insight unique_id: %s series_id: %s", from.ID, temp.SeriesID)
} else if len(existing) > 0 {
series = existing[0]
log15.Info("insights migration: existing data series identified, attempting to construct and attach new view", "series_id", series.SeriesID, "unique_id", from.ID)
} else {
series, err = tx.CreateSeries(ctx, temp)
if err != nil {
return errors.Wrapf(err, "unable to migrate insight unique_id: %s series_id: %s", from.ID, temp.SeriesID)
}
}
dataSeries[i] = series
metadata[i] = types.InsightViewSeriesMetadata{
Label: timeSeries.Name,
Stroke: timeSeries.Stroke,
}
}
view := types.InsightView{
Title: from.Title,
Description: from.Description,
UniqueID: from.ID,
PresentationType: types.Line,
}
if from.Filters != nil {
view.Filters = types.InsightViewFilters{
IncludeRepoRegex: from.Filters.IncludeRepoRegexp,
ExcludeRepoRegex: from.Filters.ExcludeRepoRegexp,
}
}
var grants []store.InsightViewGrant
if from.UserID != nil {
grants = []store.InsightViewGrant{store.UserGrant(int(*from.UserID))}
} else if from.OrgID != nil {
grants = []store.InsightViewGrant{store.OrgGrant(int(*from.OrgID))}
} else {
grants = []store.InsightViewGrant{store.GlobalGrant()}
}
view, err = tx.CreateView(ctx, view, grants)
if err != nil {
return errors.Wrapf(err, "unable to migrate insight unique_id: %s", from.ID)
}
for i, insightSeries := range dataSeries {
err := tx.AttachSeriesToView(ctx, insightSeries, view, metadata[i])
if err != nil {
return errors.Wrapf(err, "unable to migrate insight unique_id: %s", from.ID)
}
}
return nil
}
func migrateLangStatSeries(ctx context.Context, insightStore *store.InsightStore, from insights.LangStatsInsight) (err error) {
tx, err := insightStore.Transact(ctx)
if err != nil {
return err
}
defer func() { err = tx.Store.Done(err) }()
log15.Info("insights migration: attempting to migrate insight", "unique_id", from.ID)
view := types.InsightView{
Title: from.Title,
UniqueID: from.ID,
OtherThreshold: &from.OtherThreshold,
PresentationType: types.Pie,
}
series := types.InsightSeries{
SeriesID: ksuid.New().String(),
Repositories: []string{from.Repository},
SampleIntervalUnit: string(types.Month),
}
var grants []store.InsightViewGrant
if from.UserID != nil {
grants = []store.InsightViewGrant{store.UserGrant(int(*from.UserID))}
} else if from.OrgID != nil {
grants = []store.InsightViewGrant{store.OrgGrant(int(*from.OrgID))}
} else {
grants = []store.InsightViewGrant{store.GlobalGrant()}
}
view, err = tx.CreateView(ctx, view, grants)
if err != nil {
return errors.Wrapf(err, "unable to migrate insight unique_id: %s", from.ID)
}
series, err = tx.CreateSeries(ctx, series)
if err != nil {
return errors.Wrapf(err, "unable to migrate insight unique_id: %s", from.ID)
}
err = tx.AttachSeriesToView(ctx, series, view, types.InsightViewSeriesMetadata{})
if err != nil {
return errors.Wrapf(err, "unable to migrate insight unique_id: %s", from.ID)
}
return nil
}
// there seems to be some global insights with possibly old schema that have a step field
func parseTimeInterval(insight insights.SearchInsight) timeInterval {
if insight.Step.Days != nil {
return timeInterval{
unit: types.Day,
value: *insight.Step.Days,
}
} else if insight.Step.Hours != nil {
return timeInterval{
unit: types.Hour,
value: *insight.Step.Hours,
}
} else if insight.Step.Weeks != nil {
return timeInterval{
unit: types.Week,
value: *insight.Step.Weeks,
}
} else if insight.Step.Months != nil {
return timeInterval{
unit: types.Month,
value: *insight.Step.Months,
}
} else if insight.Step.Years != nil {
return timeInterval{
unit: types.Year,
value: *insight.Step.Years,
}
} else {
return timeInterval{
unit: types.Month,
value: 1,
}
}
}
type timeInterval struct {
unit types.IntervalUnit
value int
}

View File

@ -1,222 +0,0 @@
package discovery
import (
"context"
"testing"
"github.com/sourcegraph/sourcegraph/internal/insights"
"github.com/hexops/autogold"
"github.com/sourcegraph/sourcegraph/internal/api"
"github.com/sourcegraph/sourcegraph/schema"
)
var settingsExample = &api.Settings{ID: 1, Contents: `{
"insights": [
{
"title": "fmt usage",
"description": "errors.Errorf/fmt.Printf usage",
"id": "1",
"series": [
{
"label": "errors.Errorf",
"search": "errorf",
},
{
"label": "printf",
"search": "fmt.Printf",
}
]
},
{
"title": "gitserver usage",
"description": "gitserver exec & close usage",
"id": "5",
"series": [
{
"label": "exec",
"search": "gitserver.Exec",
},
{
"label": "close",
"search": "gitserver.Close",
}
]
}
]
}
`}
func TestDiscover(t *testing.T) {
settingStore := NewMockSettingStore()
settingStore.GetLatestFunc.SetDefaultHook(func(ctx context.Context, subject api.SettingsSubject) (*api.Settings, error) {
if !subject.Site { // TODO: future: site is an extremely poor name for "global settings", we should change this.
t.Fatal("expected only to request settings from global user settings")
}
return settingsExample, nil
})
ctx := context.Background()
loader := insights.NewMockLoader()
t.Run("test_with_no_id_filter", func(t *testing.T) {
discovered, err := Discover(ctx, settingStore, loader, InsightFilterArgs{})
if err != nil {
t.Fatal(err)
}
autogold.Want("discovered", []insights.SearchInsight{
{
ID: "1",
Title: "fmt usage",
Description: "errors.Errorf/fmt.Printf usage",
Series: []insights.TimeSeries{
{
Name: "errors.Errorf",
Query: "errorf",
},
{
Name: "printf",
Query: "fmt.Printf",
},
},
},
{
ID: "5",
Title: "gitserver usage",
Description: "gitserver exec & close usage",
Series: []insights.TimeSeries{
{
Name: "exec",
Query: "gitserver.Exec",
},
{
Name: "close",
Query: "gitserver.Close",
},
},
},
}).Equal(t, discovered)
})
t.Run("test_with_id_filter", func(t *testing.T) {
discovered, err := Discover(ctx, settingStore, loader, InsightFilterArgs{Ids: []string{"1"}})
if err != nil {
t.Fatal(err)
}
autogold.Want("discovered_id_filter", []insights.SearchInsight{
{
ID: "1",
Title: "fmt usage",
Description: "errors.Errorf/fmt.Printf usage",
Series: []insights.TimeSeries{
{
Name: "errors.Errorf",
Query: "errorf",
},
{
Name: "printf",
Query: "fmt.Printf",
},
},
},
}).Equal(t, discovered)
})
t.Run("test_with_loader", func(t *testing.T) {
integrated := []insights.SearchInsight{{
ID: "1234",
Title: "my insight",
Description: "woooo!!!!",
}}
loader.LoadAllFunc.SetDefaultReturn(integrated, nil)
discovered, err := Discover(ctx, settingStore, loader, InsightFilterArgs{Ids: []string{"1"}})
if err != nil {
t.Fatal(err)
}
autogold.Want("discovered_with_loader", []insights.SearchInsight{{
ID: "1",
Title: "fmt usage",
Description: "errors.Errorf/fmt.Printf usage",
Series: []insights.TimeSeries{
{
Name: "errors.Errorf",
Query: "errorf",
},
{
Name: "printf",
Query: "fmt.Printf",
},
},
}}).Equal(t, discovered)
})
}
func Test_parseUserSettings(t *testing.T) {
tests := []struct {
name string
input *api.Settings
want autogold.Value
}{
{
name: "nil",
input: nil,
want: autogold.Want("nil", [2]any{&schema.Settings{}, nil}),
},
{
name: "empty",
input: &api.Settings{
Contents: "{}",
},
want: autogold.Want("empty", [2]any{&schema.Settings{}, nil}),
},
{
name: "real",
input: settingsExample,
want: autogold.Want("real", [2]any{
&schema.Settings{Insights: []*schema.Insight{
{
Description: "errors.Errorf/fmt.Printf usage",
Id: "1",
Series: []*schema.InsightSeries{
{
Label: "errors.Errorf",
Search: "errorf",
},
{
Label: "printf",
Search: "fmt.Printf",
},
},
Title: "fmt usage",
},
{
Description: "gitserver exec & close usage",
Id: "5",
Series: []*schema.InsightSeries{
{
Label: "exec",
Search: "gitserver.Exec",
},
{
Label: "close",
Search: "gitserver.Close",
},
},
Title: "gitserver usage",
},
}},
nil,
}),
},
}
for _, tst := range tests {
t.Run(tst.name, func(t *testing.T) {
got, err := parseUserSettings(tst.input)
tst.want.Equal(t, [2]any{got, err})
})
}
}

View File

@ -10,10 +10,8 @@ import (
"context"
"sync"
api "github.com/sourcegraph/sourcegraph/internal/api"
database "github.com/sourcegraph/sourcegraph/internal/database"
types "github.com/sourcegraph/sourcegraph/internal/types"
schema "github.com/sourcegraph/sourcegraph/schema"
)
// MockIndexableReposLister is a mock implementation of the
@ -318,283 +316,3 @@ func (c RepoStoreListFuncCall) Args() []interface{} {
func (c RepoStoreListFuncCall) Results() []interface{} {
return []interface{}{c.Result0, c.Result1}
}
// MockSettingStore is a mock implementation of the SettingStore interface
// (from the package
// github.com/sourcegraph/sourcegraph/enterprise/internal/insights/discovery)
// used for unit testing.
type MockSettingStore struct {
// GetLatestFunc is an instance of a mock function object controlling
// the behavior of the method GetLatest.
GetLatestFunc *SettingStoreGetLatestFunc
// GetLatestSchemaSettingsFunc is an instance of a mock function object
// controlling the behavior of the method GetLatestSchemaSettings.
GetLatestSchemaSettingsFunc *SettingStoreGetLatestSchemaSettingsFunc
}
// NewMockSettingStore creates a new mock of the SettingStore interface. All
// methods return zero values for all results, unless overwritten.
func NewMockSettingStore() *MockSettingStore {
return &MockSettingStore{
GetLatestFunc: &SettingStoreGetLatestFunc{
defaultHook: func(context.Context, api.SettingsSubject) (r0 *api.Settings, r1 error) {
return
},
},
GetLatestSchemaSettingsFunc: &SettingStoreGetLatestSchemaSettingsFunc{
defaultHook: func(context.Context, api.SettingsSubject) (r0 *schema.Settings, r1 error) {
return
},
},
}
}
// NewStrictMockSettingStore creates a new mock of the SettingStore
// interface. All methods panic on invocation, unless overwritten.
func NewStrictMockSettingStore() *MockSettingStore {
return &MockSettingStore{
GetLatestFunc: &SettingStoreGetLatestFunc{
defaultHook: func(context.Context, api.SettingsSubject) (*api.Settings, error) {
panic("unexpected invocation of MockSettingStore.GetLatest")
},
},
GetLatestSchemaSettingsFunc: &SettingStoreGetLatestSchemaSettingsFunc{
defaultHook: func(context.Context, api.SettingsSubject) (*schema.Settings, error) {
panic("unexpected invocation of MockSettingStore.GetLatestSchemaSettings")
},
},
}
}
// NewMockSettingStoreFrom creates a new mock of the MockSettingStore
// interface. All methods delegate to the given implementation, unless
// overwritten.
func NewMockSettingStoreFrom(i SettingStore) *MockSettingStore {
return &MockSettingStore{
GetLatestFunc: &SettingStoreGetLatestFunc{
defaultHook: i.GetLatest,
},
GetLatestSchemaSettingsFunc: &SettingStoreGetLatestSchemaSettingsFunc{
defaultHook: i.GetLatestSchemaSettings,
},
}
}
// SettingStoreGetLatestFunc describes the behavior when the GetLatest
// method of the parent MockSettingStore instance is invoked.
type SettingStoreGetLatestFunc struct {
defaultHook func(context.Context, api.SettingsSubject) (*api.Settings, error)
hooks []func(context.Context, api.SettingsSubject) (*api.Settings, error)
history []SettingStoreGetLatestFuncCall
mutex sync.Mutex
}
// GetLatest delegates to the next hook function in the queue and stores the
// parameter and result values of this invocation.
func (m *MockSettingStore) GetLatest(v0 context.Context, v1 api.SettingsSubject) (*api.Settings, error) {
r0, r1 := m.GetLatestFunc.nextHook()(v0, v1)
m.GetLatestFunc.appendCall(SettingStoreGetLatestFuncCall{v0, v1, r0, r1})
return r0, r1
}
// SetDefaultHook sets function that is called when the GetLatest method of
// the parent MockSettingStore instance is invoked and the hook queue is
// empty.
func (f *SettingStoreGetLatestFunc) SetDefaultHook(hook func(context.Context, api.SettingsSubject) (*api.Settings, error)) {
f.defaultHook = hook
}
// PushHook adds a function to the end of hook queue. Each invocation of the
// GetLatest method of the parent MockSettingStore instance invokes the hook
// at the front of the queue and discards it. After the queue is empty, the
// default hook function is invoked for any future action.
func (f *SettingStoreGetLatestFunc) PushHook(hook func(context.Context, api.SettingsSubject) (*api.Settings, error)) {
f.mutex.Lock()
f.hooks = append(f.hooks, hook)
f.mutex.Unlock()
}
// SetDefaultReturn calls SetDefaultHook with a function that returns the
// given values.
func (f *SettingStoreGetLatestFunc) SetDefaultReturn(r0 *api.Settings, r1 error) {
f.SetDefaultHook(func(context.Context, api.SettingsSubject) (*api.Settings, error) {
return r0, r1
})
}
// PushReturn calls PushHook with a function that returns the given values.
func (f *SettingStoreGetLatestFunc) PushReturn(r0 *api.Settings, r1 error) {
f.PushHook(func(context.Context, api.SettingsSubject) (*api.Settings, error) {
return r0, r1
})
}
func (f *SettingStoreGetLatestFunc) nextHook() func(context.Context, api.SettingsSubject) (*api.Settings, error) {
f.mutex.Lock()
defer f.mutex.Unlock()
if len(f.hooks) == 0 {
return f.defaultHook
}
hook := f.hooks[0]
f.hooks = f.hooks[1:]
return hook
}
func (f *SettingStoreGetLatestFunc) appendCall(r0 SettingStoreGetLatestFuncCall) {
f.mutex.Lock()
f.history = append(f.history, r0)
f.mutex.Unlock()
}
// History returns a sequence of SettingStoreGetLatestFuncCall objects
// describing the invocations of this function.
func (f *SettingStoreGetLatestFunc) History() []SettingStoreGetLatestFuncCall {
f.mutex.Lock()
history := make([]SettingStoreGetLatestFuncCall, len(f.history))
copy(history, f.history)
f.mutex.Unlock()
return history
}
// SettingStoreGetLatestFuncCall is an object that describes an invocation
// of method GetLatest on an instance of MockSettingStore.
type SettingStoreGetLatestFuncCall struct {
// Arg0 is the value of the 1st argument passed to this method
// invocation.
Arg0 context.Context
// Arg1 is the value of the 2nd argument passed to this method
// invocation.
Arg1 api.SettingsSubject
// Result0 is the value of the 1st result returned from this method
// invocation.
Result0 *api.Settings
// Result1 is the value of the 2nd result returned from this method
// invocation.
Result1 error
}
// Args returns an interface slice containing the arguments of this
// invocation.
func (c SettingStoreGetLatestFuncCall) Args() []interface{} {
return []interface{}{c.Arg0, c.Arg1}
}
// Results returns an interface slice containing the results of this
// invocation.
func (c SettingStoreGetLatestFuncCall) Results() []interface{} {
return []interface{}{c.Result0, c.Result1}
}
// SettingStoreGetLatestSchemaSettingsFunc describes the behavior when the
// GetLatestSchemaSettings method of the parent MockSettingStore instance is
// invoked.
type SettingStoreGetLatestSchemaSettingsFunc struct {
defaultHook func(context.Context, api.SettingsSubject) (*schema.Settings, error)
hooks []func(context.Context, api.SettingsSubject) (*schema.Settings, error)
history []SettingStoreGetLatestSchemaSettingsFuncCall
mutex sync.Mutex
}
// GetLatestSchemaSettings delegates to the next hook function in the queue
// and stores the parameter and result values of this invocation.
func (m *MockSettingStore) GetLatestSchemaSettings(v0 context.Context, v1 api.SettingsSubject) (*schema.Settings, error) {
r0, r1 := m.GetLatestSchemaSettingsFunc.nextHook()(v0, v1)
m.GetLatestSchemaSettingsFunc.appendCall(SettingStoreGetLatestSchemaSettingsFuncCall{v0, v1, r0, r1})
return r0, r1
}
// SetDefaultHook sets function that is called when the
// GetLatestSchemaSettings method of the parent MockSettingStore instance is
// invoked and the hook queue is empty.
func (f *SettingStoreGetLatestSchemaSettingsFunc) SetDefaultHook(hook func(context.Context, api.SettingsSubject) (*schema.Settings, error)) {
f.defaultHook = hook
}
// PushHook adds a function to the end of hook queue. Each invocation of the
// GetLatestSchemaSettings method of the parent MockSettingStore instance
// invokes the hook at the front of the queue and discards it. After the
// queue is empty, the default hook function is invoked for any future
// action.
func (f *SettingStoreGetLatestSchemaSettingsFunc) PushHook(hook func(context.Context, api.SettingsSubject) (*schema.Settings, error)) {
f.mutex.Lock()
f.hooks = append(f.hooks, hook)
f.mutex.Unlock()
}
// SetDefaultReturn calls SetDefaultHook with a function that returns the
// given values.
func (f *SettingStoreGetLatestSchemaSettingsFunc) SetDefaultReturn(r0 *schema.Settings, r1 error) {
f.SetDefaultHook(func(context.Context, api.SettingsSubject) (*schema.Settings, error) {
return r0, r1
})
}
// PushReturn calls PushHook with a function that returns the given values.
func (f *SettingStoreGetLatestSchemaSettingsFunc) PushReturn(r0 *schema.Settings, r1 error) {
f.PushHook(func(context.Context, api.SettingsSubject) (*schema.Settings, error) {
return r0, r1
})
}
func (f *SettingStoreGetLatestSchemaSettingsFunc) nextHook() func(context.Context, api.SettingsSubject) (*schema.Settings, error) {
f.mutex.Lock()
defer f.mutex.Unlock()
if len(f.hooks) == 0 {
return f.defaultHook
}
hook := f.hooks[0]
f.hooks = f.hooks[1:]
return hook
}
func (f *SettingStoreGetLatestSchemaSettingsFunc) appendCall(r0 SettingStoreGetLatestSchemaSettingsFuncCall) {
f.mutex.Lock()
f.history = append(f.history, r0)
f.mutex.Unlock()
}
// History returns a sequence of SettingStoreGetLatestSchemaSettingsFuncCall
// objects describing the invocations of this function.
func (f *SettingStoreGetLatestSchemaSettingsFunc) History() []SettingStoreGetLatestSchemaSettingsFuncCall {
f.mutex.Lock()
history := make([]SettingStoreGetLatestSchemaSettingsFuncCall, len(f.history))
copy(history, f.history)
f.mutex.Unlock()
return history
}
// SettingStoreGetLatestSchemaSettingsFuncCall is an object that describes
// an invocation of method GetLatestSchemaSettings on an instance of
// MockSettingStore.
type SettingStoreGetLatestSchemaSettingsFuncCall struct {
// Arg0 is the value of the 1st argument passed to this method
// invocation.
Arg0 context.Context
// Arg1 is the value of the 2nd argument passed to this method
// invocation.
Arg1 api.SettingsSubject
// Result0 is the value of the 1st result returned from this method
// invocation.
Result0 *schema.Settings
// Result1 is the value of the 2nd result returned from this method
// invocation.
Result1 error
}
// Args returns an interface slice containing the arguments of this
// invocation.
func (c SettingStoreGetLatestSchemaSettingsFuncCall) Args() []interface{} {
return []interface{}{c.Arg0, c.Arg1}
}
// Results returns an interface slice containing the results of this
// invocation.
func (c SettingStoreGetLatestSchemaSettingsFuncCall) Results() []interface{} {
return []interface{}{c.Result0, c.Result1}
}

View File

@ -12,7 +12,6 @@ import (
edb "github.com/sourcegraph/sourcegraph/enterprise/internal/database"
"github.com/sourcegraph/sourcegraph/enterprise/internal/insights/types"
"github.com/sourcegraph/sourcegraph/internal/database/basestore"
"github.com/sourcegraph/sourcegraph/internal/insights"
"github.com/sourcegraph/sourcegraph/lib/errors"
)
@ -441,28 +440,3 @@ type DashboardStore interface {
RestoreDashboard(ctx context.Context, id int) error
HasDashboardPermission(ctx context.Context, dashboardId []int, userIds []int, orgIds []int) (bool, error)
}
// This is only used for the oob migration. Can be removed when that is deprecated.
func (s *DBDashboardStore) DashboardExists(ctx context.Context, dashboard insights.SettingDashboard) (bool, error) {
var grantsQuery *sqlf.Query
if dashboard.UserID != nil {
grantsQuery = sqlf.Sprintf("dg.user_id = %s", *dashboard.UserID)
} else if dashboard.OrgID != nil {
grantsQuery = sqlf.Sprintf("dg.org_id = %s", *dashboard.OrgID)
} else {
grantsQuery = sqlf.Sprintf("dg.global IS TRUE")
}
count, _, err := basestore.ScanFirstInt(s.Query(ctx, sqlf.Sprintf(dashboardExistsSql, dashboard.Title, grantsQuery)))
if err != nil {
return false, err
}
return count != 0, nil
}
const dashboardExistsSql = `
SELECT COUNT(*) from dashboard
JOIN dashboard_grants dg ON dashboard.id = dg.dashboard_id
WHERE dashboard.title = %s AND %s;
`

View File

@ -22,7 +22,6 @@
interfaces:
- IndexableReposLister
- RepoStore
- SettingStore
- filename: enterprise/internal/insights/store/mocks_temp.go
path: github.com/sourcegraph/sourcegraph/enterprise/internal/insights/store
interfaces: