Skip to content

Commit

Permalink
campaigns: Support changeset filtering (#8848)
Browse files Browse the repository at this point in the history
* campaigns: Add columns and update graphql to allow changeset filtering

* campaigns: Update changeset external state fields

When we sync a changeset or receive a webhook

* campaigns: Changeset filtering implemented via graphql

* campaigns: Include filters when counting changesets

* campaigns: Add filtering tests

* campaigns: Remove custom column types for changeset states

* campaigns: Remove "unknown" changeset states

* migrations: Move in preparation for merge

* Add filter UI to changesets

* campaigns: Handle null strings for changeset external state in db

* migrations: Set defaults for changeset external state

* dev: Run code generation

* campaigns: Handle nullability of changeset external state columns

* campaigns: Remove err check as value is always nil

* graphql: Add enum values that are in Go code

* campaigns: Use stronger types in filter args

* campaigns: Use changset and events when computing state

* campaigns: Fix lint warning

* campaigns: Handle upsert of check runs and suites

* campaigns: Fetch all events on webhook receive

In order to properly compute new state

* campaigns: Move changeset update so that it is included in tx

* campaigns: Improve comment

* campaigns: Use consistent argument types

* campaigns: Rename updateExternalState to SetDerivedState

And make it a method on Changeset

* campaigns: Add derived state on creation of changeset

* campaigns: Set derived state when creating a new changeset

* changelog: Add changeset filtering

* campaigns: Recalculate metadata if changeset already exists

* campaigns: Reorder code for clarity

* Update internal/campaigns/types.go

Co-Authored-By: Thorsten Ball <mrnugget@gmail.com>

* Update internal/campaigns/types.go

Co-Authored-By: Thorsten Ball <mrnugget@gmail.com>

* campaigns: Order results to be deterministic

* Fix snapshot

Co-authored-by: Erik Seliger <erikseliger@me.com>
Co-authored-by: Thorsten Ball <mrnugget@gmail.com>
  • Loading branch information
3 people committed Mar 12, 2020
1 parent d2a15e1 commit 6aa97aa
Show file tree
Hide file tree
Showing 25 changed files with 837 additions and 190 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -15,6 +15,7 @@ All notable changes to Sourcegraph are documented in this file.

- Site-Admin/Instrumentation is now available in the Kubernetes cluster deployment [8805](https://github.com/sourcegraph/sourcegraph/pull/8805).
- Extensions can now specify a `baseUri` in the `DocumentFilter` when registering providers.
- Campaign changesets can be filtered by State, Review State and Check State [8848](https://github.com/sourcegraph/sourcegraph/pull/8848)

### Changed

Expand Down
3 changes: 3 additions & 0 deletions cmd/frontend/db/schema.md
Expand Up @@ -180,6 +180,9 @@ Foreign-key constraints:
external_deleted_at | timestamp with time zone |
external_branch | text |
external_updated_at | timestamp with time zone |
external_state | text |
external_review_state | text |
external_check_state | text |
Indexes:
"changesets_pkey" PRIMARY KEY, btree (id)
"changesets_repo_external_id_unique" UNIQUE CONSTRAINT, btree (repo_id, external_id)
Expand Down
13 changes: 10 additions & 3 deletions cmd/frontend/graphqlbackend/campaigns.go
Expand Up @@ -99,7 +99,7 @@ type CampaignsResolver interface {

CreateChangesets(ctx context.Context, args *CreateChangesetsArgs) ([]ExternalChangesetResolver, error)
ChangesetByID(ctx context.Context, id graphql.ID) (ExternalChangesetResolver, error)
Changesets(ctx context.Context, args *graphqlutil.ConnectionArgs) (ExternalChangesetsConnectionResolver, error)
Changesets(ctx context.Context, args *ListChangesetsArgs) (ExternalChangesetsConnectionResolver, error)

AddChangesetsToCampaign(ctx context.Context, args *AddChangesetsToCampaignArgs) (CampaignResolver, error)

Expand Down Expand Up @@ -157,7 +157,7 @@ func (defaultCampaignsResolver) ChangesetByID(ctx context.Context, id graphql.ID
return nil, campaignsOnlyInEnterprise
}

func (defaultCampaignsResolver) Changesets(ctx context.Context, args *graphqlutil.ConnectionArgs) (ExternalChangesetsConnectionResolver, error) {
func (defaultCampaignsResolver) Changesets(ctx context.Context, args *ListChangesetsArgs) (ExternalChangesetsConnectionResolver, error) {
return nil, campaignsOnlyInEnterprise
}

Expand All @@ -182,6 +182,13 @@ type ChangesetCountsArgs struct {
To *DateTime
}

type ListChangesetsArgs struct {
First *int32
State *campaigns.ChangesetState
ReviewState *campaigns.ChangesetReviewState
CheckState *campaigns.ChangesetCheckState
}

type CampaignResolver interface {
ID() graphql.ID
Name() string
Expand All @@ -193,7 +200,7 @@ type CampaignResolver interface {
Namespace(ctx context.Context) (n NamespaceResolver, err error)
CreatedAt() DateTime
UpdatedAt() DateTime
Changesets(ctx context.Context, args struct{ graphqlutil.ConnectionArgs }) ExternalChangesetsConnectionResolver
Changesets(ctx context.Context, args *ListChangesetsArgs) (ExternalChangesetsConnectionResolver, error)
ChangesetCountsOverTime(ctx context.Context, args *ChangesetCountsArgs) ([]ChangesetCountsResolver, error)
RepositoryDiffs(ctx context.Context, args *graphqlutil.ConnectionArgs) (RepositoryComparisonConnectionResolver, error)
Plan(ctx context.Context) (CampaignPlanResolver, error)
Expand Down
12 changes: 11 additions & 1 deletion cmd/frontend/graphqlbackend/schema.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 11 additions & 1 deletion cmd/frontend/graphqlbackend/schema.graphql
Expand Up @@ -590,7 +590,15 @@ type Campaign implements Node {
repositoryDiffs(first: Int): RepositoryComparisonConnection!

# The changesets in this campaign, already created on the code host.
changesets(first: Int): ExternalChangesetConnection!
changesets(
first: Int
# Only include changesets with the given state
state: ChangesetState
# Only include changesets with the given review state
reviewState: ChangesetReviewState
# Only include changesets with the given check state
checkState: ChangesetCheckState
): ExternalChangesetConnection!

# The changeset counts over time, in 1 day intervals backwards from the point in time given in 'to'.
changesetCountsOverTime(
Expand Down Expand Up @@ -667,6 +675,8 @@ enum ChangesetReviewState {
APPROVED
CHANGES_REQUESTED
PENDING
COMMENTED
DISMISSED
}

# The state of continuous integration checks on a changeset
Expand Down
19 changes: 11 additions & 8 deletions enterprise/internal/campaigns/resolvers/campaigns.go
Expand Up @@ -154,16 +154,19 @@ func (r *campaignResolver) PublishedAt(ctx context.Context) (*graphqlbackend.Dat
return &graphqlbackend.DateTime{Time: createdAt}, nil
}

func (r *campaignResolver) Changesets(ctx context.Context, args struct {
graphqlutil.ConnectionArgs
}) graphqlbackend.ExternalChangesetsConnectionResolver {
func (r *campaignResolver) Changesets(
ctx context.Context,
args *graphqlbackend.ListChangesetsArgs,
) (graphqlbackend.ExternalChangesetsConnectionResolver, error) {
opts, err := listChangesetOptsFromArgs(args)
if err != nil {
return nil, err
}
opts.CampaignID = r.Campaign.ID
return &changesetsConnectionResolver{
store: r.store,
opts: ee.ListChangesetsOpts{
CampaignID: r.Campaign.ID,
Limit: int(args.ConnectionArgs.GetFirst()),
},
}
opts: opts,
}, nil
}

func (r *campaignResolver) ChangesetPlans(
Expand Down
12 changes: 7 additions & 5 deletions enterprise/internal/campaigns/resolvers/changesets.go
Expand Up @@ -59,7 +59,12 @@ func (r *changesetsConnectionResolver) Nodes(ctx context.Context) ([]graphqlback
}

func (r *changesetsConnectionResolver) TotalCount(ctx context.Context) (int32, error) {
opts := ee.CountChangesetsOpts{CampaignID: r.opts.CampaignID}
opts := ee.CountChangesetsOpts{
CampaignID: r.opts.CampaignID,
ExternalState: r.opts.ExternalState,
ExternalCheckState: r.opts.ExternalCheckState,
ExternalReviewState: r.opts.ExternalReviewState,
}
count, err := r.store.CountChangesets(ctx, opts)
return int32(count), err
}
Expand Down Expand Up @@ -230,10 +235,7 @@ func (r *changesetResolver) CheckState(ctx context.Context) (*campaigns.Changese
if err != nil {
return nil, err
}
state, err := campaigns.ComputeCheckState(r.Changeset, events), nil
if err != nil {
return nil, err
}
state := campaigns.ComputeCheckState(r.Changeset, events)
if state == campaigns.ChangesetCheckStateUnknown {
return nil, nil
}
Expand Down
43 changes: 38 additions & 5 deletions enterprise/internal/campaigns/resolvers/resolver.go
Expand Up @@ -14,7 +14,6 @@ import (
"github.com/sourcegraph/sourcegraph/cmd/frontend/backend"
"github.com/sourcegraph/sourcegraph/cmd/frontend/db"
"github.com/sourcegraph/sourcegraph/cmd/frontend/graphqlbackend"
"github.com/sourcegraph/sourcegraph/cmd/frontend/graphqlbackend/graphqlutil"
"github.com/sourcegraph/sourcegraph/cmd/repo-updater/repos"
ee "github.com/sourcegraph/sourcegraph/enterprise/internal/campaigns"
"github.com/sourcegraph/sourcegraph/internal/api"
Expand Down Expand Up @@ -481,19 +480,53 @@ func (r *Resolver) CreateChangesets(ctx context.Context, args *graphqlbackend.Cr
return csr, nil
}

func (r *Resolver) Changesets(ctx context.Context, args *graphqlutil.ConnectionArgs) (graphqlbackend.ExternalChangesetsConnectionResolver, error) {
func (r *Resolver) Changesets(ctx context.Context, args *graphqlbackend.ListChangesetsArgs) (graphqlbackend.ExternalChangesetsConnectionResolver, error) {
// 🚨 SECURITY: Only site admins or users when read-access is enabled may access changesets.
if err := allowReadAccess(ctx); err != nil {
return nil, err
}
opts, err := listChangesetOptsFromArgs(args)
if err != nil {
return nil, err
}
return &changesetsConnectionResolver{
store: r.store,
opts: ee.ListChangesetsOpts{
Limit: int(args.GetFirst()),
},
opts: opts,
}, nil
}

func listChangesetOptsFromArgs(args *graphqlbackend.ListChangesetsArgs) (ee.ListChangesetsOpts, error) {
var opts ee.ListChangesetsOpts
if args == nil {
return opts, nil
}
if args.First != nil {
opts.Limit = int(*args.First)
}
if args.State != nil {
state := campaigns.ChangesetState(*args.State)
if !state.Valid() {
return opts, errors.New("changeset state not valid")
}
opts.ExternalState = &state
}
if args.ReviewState != nil {
state := campaigns.ChangesetReviewState(*args.ReviewState)
if !state.Valid() {
return opts, errors.New("changeset review state not valid")
}
opts.ExternalReviewState = &state
}
if args.CheckState != nil {
state := campaigns.ChangesetCheckState(*args.CheckState)
if !state.Valid() {
return opts, errors.New("changeset check state not valid")
}
opts.ExternalCheckState = &state
}
return opts, nil
}

func (r *Resolver) CreateCampaignPlanFromPatches(ctx context.Context, args graphqlbackend.CreateCampaignPlanFromPatchesArgs) (graphqlbackend.CampaignPlanResolver, error) {
var err error
tr, ctx := trace.New(ctx, "Resolver.CreateCampaignPlanFromPatches", "")
Expand Down
6 changes: 5 additions & 1 deletion enterprise/internal/campaigns/service.go
Expand Up @@ -398,6 +398,8 @@ func RunChangesetJob(
// We keep a clone because CreateChangesets might overwrite the changeset
// with outdated metadata.
clone := cs.Changeset.Clone()
events := clone.Events()
clone.SetDerivedState(events)
if err = store.CreateChangesets(ctx, clone); err != nil {
if _, ok := err.(AlreadyExistError); !ok {
return err
Expand All @@ -412,12 +414,14 @@ func RunChangesetJob(
if err := clone.SetMetadata(cs.Changeset.Metadata); err != nil {
return errors.Wrap(err, "setting changeset metadata")
}
events = clone.Events()
clone.SetDerivedState(events)
if err = store.UpdateChangesets(ctx, clone); err != nil {
return err
}
}

if err := store.UpsertChangesetEvents(ctx, clone.Events()...); err != nil {
if err := store.UpsertChangesetEvents(ctx, events...); err != nil {
log15.Error("UpsertChangesetEvents", "err", err)
return err
}
Expand Down

0 comments on commit 6aa97aa

Please sign in to comment.