mirror of
https://github.com/sourcegraph/sourcegraph.git
synced 2026-02-06 18:51:59 +00:00
insights: remove unused code for non-isolated settings migration (#43916)
This commit is contained in:
parent
69af2da113
commit
16ae2dac85
@ -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
|
||||
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -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{
|
||||
{
|
||||
|
||||
@ -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
|
||||
}
|
||||
@ -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})
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
@ -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}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
`
|
||||
|
||||
@ -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:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user