Skip to content

Commit d8b6da4

Browse files
committed
make use of lookup-subjects limit for pagination
1 parent 7d3bfb7 commit d8b6da4

File tree

7 files changed

+758
-74
lines changed

7 files changed

+758
-74
lines changed

.golangci.yaml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ linters:
1010
enable:
1111
- "bidichk"
1212
- "bodyclose"
13-
- "deadcode"
1413
- "errcheck"
1514
- "errname"
1615
- "errorlint"
@@ -20,7 +19,6 @@ linters:
2019
- "gosec"
2120
- "gosimple"
2221
- "govet"
23-
- "ifshort"
2422
- "importas"
2523
- "ineffassign"
2624
- "makezero"
@@ -30,12 +28,10 @@ linters:
3028
- "revive"
3129
- "rowserrcheck"
3230
- "staticcheck"
33-
- "structcheck"
3431
- "stylecheck"
3532
- "tenv"
3633
- "typecheck"
3734
- "unconvert"
3835
- "unused"
39-
- "varcheck"
4036
- "wastedassign"
4137
- "whitespace"

go.mod

Lines changed: 190 additions & 49 deletions
Large diffs are not rendered by default.

go.sum

Lines changed: 412 additions & 0 deletions
Large diffs are not rendered by default.

internal/commands/permission.go

Lines changed: 46 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ func RegisterPermissionCmd(rootCmd *cobra.Command) *cobra.Command {
106106
lookupSubjectsCmd.Flags().Bool("json", false, "output as JSON")
107107
lookupSubjectsCmd.Flags().String("revision", "", "optional revision at which to check")
108108
lookupSubjectsCmd.Flags().String("caveat-context", "", "the caveat context to send along with the lookup, in JSON form")
109+
lookupSubjectsCmd.Flags().Uint32("page-limit", 100, "limit of subject returned per page")
109110
registerConsistencyFlags(lookupSubjectsCmd.Flags())
110111

111112
return permissionCmd
@@ -188,7 +189,7 @@ func checkCmdFunc(cmd *cobra.Command, args []string) error {
188189
return err
189190
}
190191

191-
client, err := client.NewClient(cmd)
192+
c, err := client.NewClient(cmd)
192193
if err != nil {
193194
return err
194195
}
@@ -218,7 +219,7 @@ func checkCmdFunc(cmd *cobra.Command, args []string) error {
218219
}
219220

220221
var trailerMD metadata.MD
221-
resp, err := client.CheckPermission(ctx, request, grpc.Trailer(&trailerMD))
222+
resp, err := c.CheckPermission(ctx, request, grpc.Trailer(&trailerMD))
222223
if err != nil {
223224
var debugInfo *v1.DebugInformation
224225
if resp != nil {
@@ -370,7 +371,7 @@ func expandCmdFunc(cmd *cobra.Command, args []string) error {
370371
return err
371372
}
372373

373-
client, err := client.NewClient(cmd)
374+
c, err := client.NewClient(cmd)
374375
if err != nil {
375376
return err
376377
}
@@ -385,7 +386,7 @@ func expandCmdFunc(cmd *cobra.Command, args []string) error {
385386
}
386387
log.Trace().Interface("request", request).Send()
387388

388-
resp, err := client.ExpandPermissionTree(cmd.Context(), request)
389+
resp, err := c.ExpandPermissionTree(cmd.Context(), request)
389390
if err != nil {
390391
return err
391392
}
@@ -425,7 +426,7 @@ func lookupResourcesCmdFunc(cmd *cobra.Command, args []string) error {
425426
return err
426427
}
427428

428-
client, err := client.NewClient(cmd)
429+
c, err := client.NewClient(cmd)
429430
if err != nil {
430431
return err
431432
}
@@ -444,7 +445,7 @@ func lookupResourcesCmdFunc(cmd *cobra.Command, args []string) error {
444445
}
445446
log.Trace().Interface("request", request).Send()
446447

447-
respStream, err := client.LookupResources(cmd.Context(), request)
448+
respStream, err := c.LookupResources(cmd.Context(), request)
448449
if err != nil {
449450
return err
450451
}
@@ -492,7 +493,7 @@ func lookupSubjectsCmdFunc(cmd *cobra.Command, args []string) error {
492493
return err
493494
}
494495

495-
client, err := client.NewClient(cmd)
496+
c, err := client.NewClient(cmd)
496497
if err != nil {
497498
return err
498499
}
@@ -507,21 +508,37 @@ func lookupSubjectsCmdFunc(cmd *cobra.Command, args []string) error {
507508
Context: caveatContext,
508509
Consistency: consistency,
509510
}
510-
log.Trace().Interface("request", request).Send()
511-
512-
respStream, err := client.LookupSubjects(cmd.Context(), request)
513-
if err != nil {
514-
return err
515-
}
516511

512+
limit := cobrautil.MustGetUint32(cmd, "page-limit")
513+
request.OptionalConcreteLimit = limit
514+
log.Trace().Interface("request", request).Send()
515+
lastCursor := request.OptionalCursor
517516
for {
518-
resp, err := respStream.Recv()
519-
switch {
520-
case errors.Is(err, io.EOF):
521-
return nil
522-
case err != nil:
517+
request.OptionalCursor = lastCursor
518+
respStream, err := c.LookupSubjects(cmd.Context(), request)
519+
if err != nil {
523520
return err
524-
default:
521+
}
522+
523+
var cursorToken string
524+
if lastCursor != nil {
525+
cursorToken = lastCursor.Token
526+
}
527+
528+
log.Trace().Interface("request", request).Str("cursor", cursorToken).Msg("reading subjects page")
529+
var relCount uint32
530+
for {
531+
resp, err := respStream.Recv()
532+
if errors.Is(err, io.EOF) {
533+
break
534+
}
535+
if err != nil {
536+
return err
537+
}
538+
539+
lastCursor = resp.AfterResultCursor
540+
relCount++
541+
525542
if cobrautil.MustGetBool(cmd, "json") {
526543
prettyProto, err := PrettyProto(resp)
527544
if err != nil {
@@ -533,8 +550,16 @@ func lookupSubjectsCmdFunc(cmd *cobra.Command, args []string) error {
533550
console.Printf("%s:%s%s\n",
534551
subjectType,
535552
prettyLookupPermissionship(resp.Subject.SubjectObjectId, resp.Subject.Permissionship, resp.Subject.PartialCaveatInfo),
536-
excludedSubjectsString(resp.ExcludedSubjects),
537-
)
553+
excludedSubjectsString(resp.ExcludedSubjects))
554+
}
555+
556+
if relCount < limit || limit == 0 {
557+
return nil
558+
}
559+
560+
if relCount > limit {
561+
log.Warn().Uint32("limit-specified", limit).Uint32("relationships-received", relCount).Msg("page limit ignored, pagination may not be supported by the server, consider updating SpiceDB")
562+
return nil
538563
}
539564
}
540565
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package commands
2+
3+
import (
4+
"context"
5+
"fmt"
6+
v1 "github.com/authzed/authzed-go/proto/authzed/api/v1"
7+
"github.com/authzed/spicedb/pkg/tuple"
8+
"github.com/authzed/zed/internal/client"
9+
"github.com/authzed/zed/internal/console"
10+
zedtesting "github.com/authzed/zed/internal/testing"
11+
"github.com/stretchr/testify/require"
12+
"testing"
13+
)
14+
15+
func TestLookupSubjectsCmd(t *testing.T) {
16+
cmd := zedtesting.CreateTestCobraCommandWithFlagValue(t,
17+
zedtesting.BoolFlag{FlagName: "json"},
18+
zedtesting.StringFlag{FlagName: "caveat-context"},
19+
zedtesting.BoolFlag{FlagName: "consistency-full", FlagValue: true},
20+
zedtesting.StringFlag{FlagName: "consistency-at-least"},
21+
zedtesting.StringFlag{FlagName: "revision"},
22+
zedtesting.StringFlag{FlagName: "consistency-at-exactly"},
23+
zedtesting.UintFlag32{FlagName: "page-limit", FlagValue: 1})
24+
25+
ctx, cancel := context.WithCancel(context.Background())
26+
defer cancel()
27+
srv := zedtesting.NewTestServer(ctx, t)
28+
go func() {
29+
require.NoError(t, srv.Run(ctx))
30+
}()
31+
conn, err := srv.GRPCDialContext(ctx)
32+
require.NoError(t, err)
33+
34+
originalClient := client.NewClient
35+
defer func() {
36+
client.NewClient = originalClient
37+
}()
38+
39+
client.NewClient = zedtesting.ClientFromConn(conn)
40+
41+
c, err := zedtesting.ClientFromConn(conn)(cmd)
42+
require.NoError(t, err)
43+
44+
_, err = c.WriteSchema(ctx, &v1.WriteSchemaRequest{Schema: testSchema})
45+
require.NoError(t, err)
46+
47+
var updates []*v1.RelationshipUpdate
48+
for i := 0; i < 1000; i++ {
49+
updates = append(updates, &v1.RelationshipUpdate{
50+
Operation: v1.RelationshipUpdate_OPERATION_TOUCH,
51+
Relationship: tuple.ParseRel(fmt.Sprintf("test/resource:1#reader@test/user:%d", i)),
52+
})
53+
}
54+
wr := &v1.WriteRelationshipsRequest{
55+
Updates: updates,
56+
}
57+
_, err = c.WriteRelationships(ctx, wr)
58+
require.NoError(t, err)
59+
60+
originalFunc := console.Printf
61+
var results []string
62+
console.Printf = func(format string, a ...interface{}) {
63+
results = append(results, fmt.Sprintf(format, a...))
64+
}
65+
defer func() {
66+
console.Printf = originalFunc
67+
}()
68+
69+
err = lookupSubjectsCmdFunc(cmd, []string{"test/resource:1", "read", "test/user"})
70+
require.NoError(t, err)
71+
require.Len(t, results, 1000)
72+
}

internal/commands/relationship_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ import (
2525
const testSchema = `definition test/resource {
2626
relation reader: test/user
2727
relation writer: test/user
28+
29+
permission read = reader + write
30+
permission write = writer
2831
}
2932
3033
definition test/user {}`

pkg/wasm/client.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,41 @@ func (wc wasmClient) BulkCheckPermission(ctx context.Context, in *v1.BulkCheckPe
8282
return client.BulkCheckPermission(ctx, in, opts...)
8383
}
8484

85+
func (wc wasmClient) ExperimentalReflectSchema(ctx context.Context, in *v1.ExperimentalReflectSchemaRequest, opts ...grpc.CallOption) (*v1.ExperimentalReflectSchemaResponse, error) {
86+
client := v1.NewExperimentalServiceClient(wc.conn)
87+
return client.ExperimentalReflectSchema(ctx, in, opts...)
88+
}
89+
90+
func (wc wasmClient) ExperimentalComputablePermissions(ctx context.Context, in *v1.ExperimentalComputablePermissionsRequest, opts ...grpc.CallOption) (*v1.ExperimentalComputablePermissionsResponse, error) {
91+
client := v1.NewExperimentalServiceClient(wc.conn)
92+
return client.ExperimentalComputablePermissions(ctx, in, opts...)
93+
}
94+
95+
func (wc wasmClient) ExperimentalDependentRelations(ctx context.Context, in *v1.ExperimentalDependentRelationsRequest, opts ...grpc.CallOption) (*v1.ExperimentalDependentRelationsResponse, error) {
96+
client := v1.NewExperimentalServiceClient(wc.conn)
97+
return client.ExperimentalDependentRelations(ctx, in, opts...)
98+
}
99+
100+
func (wc wasmClient) ExperimentalDiffSchema(ctx context.Context, in *v1.ExperimentalDiffSchemaRequest, opts ...grpc.CallOption) (*v1.ExperimentalDiffSchemaResponse, error) {
101+
client := v1.NewExperimentalServiceClient(wc.conn)
102+
return client.ExperimentalDiffSchema(ctx, in, opts...)
103+
}
104+
105+
func (wc wasmClient) ExperimentalRegisterRelationshipCounter(ctx context.Context, in *v1.ExperimentalRegisterRelationshipCounterRequest, opts ...grpc.CallOption) (*v1.ExperimentalRegisterRelationshipCounterResponse, error) {
106+
client := v1.NewExperimentalServiceClient(wc.conn)
107+
return client.ExperimentalRegisterRelationshipCounter(ctx, in, opts...)
108+
}
109+
110+
func (wc wasmClient) ExperimentalCountRelationships(ctx context.Context, in *v1.ExperimentalCountRelationshipsRequest, opts ...grpc.CallOption) (*v1.ExperimentalCountRelationshipsResponse, error) {
111+
client := v1.NewExperimentalServiceClient(wc.conn)
112+
return client.ExperimentalCountRelationships(ctx, in, opts...)
113+
}
114+
115+
func (wc wasmClient) ExperimentalUnregisterRelationshipCounter(ctx context.Context, in *v1.ExperimentalUnregisterRelationshipCounterRequest, opts ...grpc.CallOption) (*v1.ExperimentalUnregisterRelationshipCounterResponse, error) {
116+
client := v1.NewExperimentalServiceClient(wc.conn)
117+
return client.ExperimentalUnregisterRelationshipCounter(ctx, in, opts...)
118+
}
119+
85120
func (wc wasmClient) Watch(ctx context.Context, in *v1.WatchRequest, opts ...grpc.CallOption) (v1.WatchService_WatchClient, error) {
86121
client := v1.NewWatchServiceClient(wc.conn)
87122
return client.Watch(ctx, in, opts...)

0 commit comments

Comments
 (0)