Skip to content
This repository was archived by the owner on Sep 30, 2024. It is now read-only.

Commit 31da9c2

Browse files
authored
fix/enterpriseportal: ListEnterpriseSubscriptions fixes (#63412)
Follow-ups to https://github.com/sourcegraph/sourcegraph/pull/63173 I'm running into while working on CORE-169: 1. Intersect permission-allowed subscriptions, instead of always overwriting. I think this is probably desired behaviour, to still allow filtering by subscriptions. If no subscriptions are explicitly listed, _then_ the allowed subscriptions become the set of subscriptions to list. 1. If a permissions filter is used and the resulting subscription set is empty, we fast-exit with empty result 2. If no subscription list is provided, list all subscriptions. This is safe because permission filter has special fast-path above 3. Fix "is archived" support ## Test plan Tests, and a manual check - with `sg start dotcom`, exchange for a token: ```sh sg sams create-client-token \ -sams https://accounts.sgdev.org \ -s 'enterprise_portal::subscription::read' \ -s 'enterprise_portal::codyaccess::read' ``` Query for subscriptions without filters: ```sh curl --header "Content-Type: application/json" --header 'authorization: bearer sams_at_...' --data '{}' http://localhost:6081/enterpriseportal.subscriptions.v1.SubscriptionsService/ListEnterpriseSubscriptions | jq ``` All subscriptions I have locally get returned ✅
1 parent b3fe6dc commit 31da9c2

File tree

9 files changed

+192
-65
lines changed

9 files changed

+192
-65
lines changed

cmd/enterprise-portal/internal/database/subscriptions.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,9 @@ type ListEnterpriseSubscriptionsOptions struct {
4242
IDs []string
4343
// InstanceDomains is a list of instance domains to filter by.
4444
InstanceDomains []string
45-
// OnlyArchived indicates whether to only list archived subscriptions.
46-
OnlyArchived bool
45+
// IsArchived indicates whether to only list archived subscriptions, or only
46+
// non-archived subscriptions.
47+
IsArchived bool
4748
// PageSize is the maximum number of subscriptions to return.
4849
PageSize int
4950
}

cmd/enterprise-portal/internal/dotcomdb/dotcomdb.go

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -464,17 +464,30 @@ type SubscriptionAttributes struct {
464464
ArchivedAt *time.Time
465465
}
466466

467+
type ListEnterpriseSubscriptionsOptions struct {
468+
SubscriptionIDs []string
469+
IsArchived bool
470+
}
471+
467472
// ListEnterpriseSubscriptions returns a list of enterprise subscription
468473
// attributes with the given IDs. It silently ignores any non-existent
469474
// subscription IDs. The caller should check the length of the returned slice to
470475
// ensure all requested subscriptions were found.
471-
func (r *Reader) ListEnterpriseSubscriptions(ctx context.Context, subscriptionIDs ...string) ([]*SubscriptionAttributes, error) {
472-
if len(subscriptionIDs) == 0 {
473-
return []*SubscriptionAttributes{}, nil
476+
//
477+
// If no IDs are given, it returns all subscriptions.
478+
func (r *Reader) ListEnterpriseSubscriptions(ctx context.Context, opts ListEnterpriseSubscriptionsOptions) ([]*SubscriptionAttributes, error) {
479+
query := `SELECT id, created_at, archived_at FROM product_subscriptions WHERE true`
480+
namedArgs := pgx.NamedArgs{}
481+
if len(opts.SubscriptionIDs) > 0 {
482+
query += "\nAND id = ANY(@ids)"
483+
namedArgs["ids"] = opts.SubscriptionIDs
484+
}
485+
if opts.IsArchived {
486+
query += "\nAND archived_at IS NOT NULL"
487+
} else {
488+
query += "\nAND archived_at IS NULL"
474489
}
475490

476-
query := `SELECT id, created_at, archived_at FROM product_subscriptions WHERE id = ANY(@ids)`
477-
namedArgs := pgx.NamedArgs{"ids": subscriptionIDs}
478491
rows, err := r.db.Query(ctx, query, namedArgs)
479492
if err != nil {
480493
return nil, errors.Wrap(err, "query subscription attributes")

cmd/enterprise-portal/internal/dotcomdb/dotcomdb_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ type mockedData struct {
7474
targetSubscriptionID string
7575
accessTokens []string
7676
createdLicenses int
77+
createdSubscriptions int
7778
archivedSubscriptions int
7879
}
7980

@@ -92,6 +93,7 @@ func setupDBAndInsertMockLicense(t *testing.T, dotcomdb database.DB, info licens
9293
require.NoError(t, err)
9394
sub, err := subscriptionsdb.Create(ctx, u.ID, u.Username)
9495
require.NoError(t, err)
96+
result.createdSubscriptions += 1
9597
_, err = licensesdb.Create(ctx, sub, t.Name()+"-barbaz", 2, license.Info{
9698
CreatedAt: info.CreatedAt,
9799
ExpiresAt: info.ExpiresAt,
@@ -108,6 +110,7 @@ func setupDBAndInsertMockLicense(t *testing.T, dotcomdb database.DB, info licens
108110
require.NoError(t, err)
109111
sub, err := subscriptionsdb.Create(ctx, u.ID, u.Username)
110112
require.NoError(t, err)
113+
result.createdSubscriptions += 1
111114
_, err = licensesdb.Create(ctx, sub, t.Name()+"-archived", 2, license.Info{
112115
CreatedAt: info.CreatedAt,
113116
ExpiresAt: info.ExpiresAt,
@@ -127,6 +130,7 @@ func setupDBAndInsertMockLicense(t *testing.T, dotcomdb database.DB, info licens
127130
require.NoError(t, err)
128131
sub, err := subscriptionsdb.Create(ctx, u.ID, u.Username)
129132
require.NoError(t, err)
133+
result.createdSubscriptions += 1
130134
_, err = licensesdb.Create(ctx, sub, t.Name()+"-not-dev", 2, license.Info{
131135
CreatedAt: info.CreatedAt,
132136
ExpiresAt: info.ExpiresAt,
@@ -140,6 +144,7 @@ func setupDBAndInsertMockLicense(t *testing.T, dotcomdb database.DB, info licens
140144
require.NoError(t, err)
141145
subid, err := subscriptionsdb.Create(ctx, u.ID, u.Username)
142146
require.NoError(t, err)
147+
result.createdSubscriptions += 1
143148
result.targetSubscriptionID = subid
144149
// Insert a rubbish license first, CreatedAt is not used (creation time is
145150
// inferred from insert time) so we need to do this first
@@ -171,6 +176,7 @@ func setupDBAndInsertMockLicense(t *testing.T, dotcomdb database.DB, info licens
171176
require.NoError(t, err)
172177
sub, err := subscriptionsdb.Create(ctx, u.ID, u.Username)
173178
require.NoError(t, err)
179+
result.createdSubscriptions += 1
174180
_, err = licensesdb.Create(ctx, sub, t.Name()+"-foobar", 2, license.Info{
175181
CreatedAt: info.CreatedAt,
176182
ExpiresAt: info.ExpiresAt,
@@ -458,3 +464,28 @@ func TestListEnterpriseSubscriptionLicenses(t *testing.T) {
458464
})
459465
}
460466
}
467+
468+
func TestListEnterpriseSubscriptions(t *testing.T) {
469+
db, dotcomreader := newTestDotcomReader(t)
470+
info := license.Info{
471+
ExpiresAt: time.Now().Add(30 * time.Minute),
472+
UserCount: 321,
473+
Tags: []string{licensing.PlanEnterprise1.Tag(), licensing.DevTag},
474+
}
475+
mock := setupDBAndInsertMockLicense(t, db, info, nil)
476+
477+
// Just a simple sanity test
478+
ss, err := dotcomreader.ListEnterpriseSubscriptions(
479+
context.Background(),
480+
dotcomdb.ListEnterpriseSubscriptionsOptions{})
481+
require.NoError(t, err)
482+
assert.Len(t, ss, mock.createdSubscriptions-mock.archivedSubscriptions)
483+
var found bool
484+
for _, s := range ss {
485+
if s.ID == mock.targetSubscriptionID {
486+
found = true
487+
break
488+
}
489+
}
490+
assert.True(t, found)
491+
}

cmd/enterprise-portal/internal/subscriptionsservice/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ go_library(
1717
"//cmd/enterprise-portal/internal/database",
1818
"//cmd/enterprise-portal/internal/dotcomdb",
1919
"//cmd/enterprise-portal/internal/samsm2m",
20+
"//internal/collections",
2021
"//internal/trace",
2122
"//lib/enterpriseportal/subscriptions/v1:subscriptions",
2223
"//lib/enterpriseportal/subscriptions/v1/v1connect",

cmd/enterprise-portal/internal/subscriptionsservice/adapters.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,12 @@ func convertLicenseAttrsToProto(attrs *dotcomdb.LicenseAttributes) *subscription
4747
}
4848

4949
func convertSubscriptionToProto(subscription *database.Subscription, attrs *dotcomdb.SubscriptionAttributes) *subscriptionsv1.EnterpriseSubscription {
50+
// Dotcom equivalent missing is surprising, but let's not panic just yet
51+
if attrs == nil {
52+
attrs = &dotcomdb.SubscriptionAttributes{
53+
ID: subscription.ID,
54+
}
55+
}
5056
conds := []*subscriptionsv1.EnterpriseSubscriptionCondition{
5157
{
5258
Status: subscriptionsv1.EnterpriseSubscriptionCondition_STATUS_CREATED,

cmd/enterprise-portal/internal/subscriptionsservice/mocks_test.go

Lines changed: 16 additions & 23 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)