From f7be8ea1980689faed65f142ecf75d3784cb51f8 Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Thu, 16 May 2024 22:44:53 -0400 Subject: [PATCH] Implement a new, experimental variant of LookupResources as LookupResources2 This implementation should be much faster for intersections, exclusions and caveats due to early tree shearing and check hints --- internal/caveats/run.go | 236 ++- internal/caveats/run_test.go | 312 ++- internal/dispatch/caching/caching.go | 64 + .../dispatch/caching/cachingdispatch_test.go | 4 + internal/dispatch/caching/delegate.go | 4 + internal/dispatch/dispatch.go | 10 + internal/dispatch/graph/check_test.go | 358 ++++ internal/dispatch/graph/graph.go | 33 + .../dispatch/graph/lookupresources2_test.go | 661 +++++++ internal/dispatch/keys/computed.go | 13 + internal/dispatch/keys/computed_test.go | 260 +++ internal/dispatch/keys/keys.go | 14 + internal/dispatch/remote/cluster.go | 51 + .../dispatch/singleflight/singleflight.go | 4 + .../singleflight/singleflight_test.go | 4 + internal/graph/check.go | 125 +- internal/graph/computed/computecheck.go | 4 +- internal/graph/computed/computecheck_test.go | 4 +- internal/graph/lookupresources2.go | 783 ++++++++ internal/graph/resourcesubjectsmap.go | 5 +- internal/graph/resourcesubjectsmap2.go | 197 ++ internal/graph/resourcesubjectsmap2_test.go | 185 ++ internal/services/dispatch/v1/acl.go | 8 + .../integrationtesting/consistency_test.go | 19 +- .../consistencytestutil/clusteranddata.go | 9 +- .../testconfigs/caveatarrow.yaml | 2 +- internal/services/v1/debug_test.go | 4 +- internal/services/v1/permissions.go | 141 ++ internal/services/v1/permissions_test.go | 100 +- internal/services/v1/relationships.go | 4 + internal/testserver/cluster.go | 7 +- internal/testserver/server.go | 19 +- pkg/cmd/serve.go | 1 + pkg/cmd/server/server.go | 24 +- pkg/cmd/server/zz_generated.options.go | 9 + pkg/proto/core/v1/core.pb.go | 561 +++--- pkg/proto/core/v1/core.pb.validate.go | 2 + pkg/proto/core/v1/core_vtproto.pb.go | 47 + pkg/proto/dispatch/v1/00_zerolog.go | 9 + pkg/proto/dispatch/v1/dispatch.pb.go | 1356 ++++++++----- pkg/proto/dispatch/v1/dispatch.pb.validate.go | 663 +++++++ pkg/proto/dispatch/v1/dispatch_grpc.pb.go | 64 + pkg/proto/dispatch/v1/dispatch_vtproto.pb.go | 1722 +++++++++++++++-- pkg/typesystem/checkhints.go | 128 ++ pkg/typesystem/checkhints_test.go | 144 ++ pkg/typesystem/reachabilitygraph.go | 27 + pkg/typesystem/reachabilitygraphbuilder.go | 16 +- proto/internal/core/v1/core.proto | 6 + proto/internal/dispatch/v1/dispatch.proto | 38 + 49 files changed, 7391 insertions(+), 1070 deletions(-) create mode 100644 internal/dispatch/graph/lookupresources2_test.go create mode 100644 internal/graph/lookupresources2.go create mode 100644 internal/graph/resourcesubjectsmap2.go create mode 100644 internal/graph/resourcesubjectsmap2_test.go create mode 100644 pkg/typesystem/checkhints.go create mode 100644 pkg/typesystem/checkhints_test.go diff --git a/internal/caveats/run.go b/internal/caveats/run.go index 23339f6171..f57e29f960 100644 --- a/internal/caveats/run.go +++ b/internal/caveats/run.go @@ -63,9 +63,10 @@ type ExpressionResult interface { } type syntheticResult struct { - value bool - contextValues map[string]any - exprString string + value bool + contextValues map[string]any + exprString string + missingContextParams []string } func (sr syntheticResult) Value() bool { @@ -73,11 +74,15 @@ func (sr syntheticResult) Value() bool { } func (sr syntheticResult) IsPartial() bool { - return false + return len(sr.missingContextParams) > 0 } func (sr syntheticResult) MissingVarNames() ([]string, error) { - return nil, fmt.Errorf("not a partial value") + if len(sr.missingContextParams) == 0 { + return nil, fmt.Errorf("not a partial value") + } + + return sr.missingContextParams, nil } func (sr syntheticResult) ContextValues() map[string]any { @@ -156,6 +161,14 @@ func (lc loadedCaveats) Get(caveatDefName string) (*core.CaveatDefinition, *cave return caveat, justDeserialized, nil } +func isFalseResult(resuilt ExpressionResult) bool { + return !resuilt.Value() && !resuilt.IsPartial() +} + +func isTrueResult(resuilt ExpressionResult) bool { + return resuilt.Value() && !resuilt.IsPartial() +} + func runExpressionWithCaveats( ctx context.Context, env *caveats.Environment, @@ -203,15 +216,30 @@ func runExpressionWithCaveats( } cop := expr.GetOperation() - boolResult := false - if cop.Op == core.CaveatOperation_AND { - boolResult = true - } var contextValues map[string]any var exprStringPieces []string + var currentResult ExpressionResult = syntheticResult{ + value: false, + contextValues: map[string]any{}, + exprString: "", + missingContextParams: []string{}, + } + if cop.Op == core.CaveatOperation_AND { + currentResult = syntheticResult{ + value: true, + contextValues: map[string]any{}, + exprString: "", + missingContextParams: []string{}, + } + } + buildExprString := func() (string, error) { + if debugOption != RunCaveatExpressionWithDebugInformation { + return "", nil + } + switch cop.Op { case core.CaveatOperation_AND: return strings.Join(exprStringPieces, " && "), nil @@ -227,90 +255,174 @@ func runExpressionWithCaveats( } } - for _, child := range cop.Children { - childResult, err := runExpressionWithCaveats(ctx, env, child, context, loadedCaveats, debugOption) - if err != nil { + addDebugInfo := func(found ExpressionResult) error { + if debugOption == RunCaveatExpressionWithDebugInformation { + contextValues = combineMaps(contextValues, found.ContextValues()) + exprString, err := found.ExpressionString() + if err != nil { + return err + } + + exprStringPieces = append(exprStringPieces, exprString) + } + + return nil + } + + and := func(existing ExpressionResult, found ExpressionResult) (ExpressionResult, error) { + if err := addDebugInfo(found); err != nil { return nil, err } - if childResult.IsPartial() { - return childResult, nil + var missingContextParams []string + if existing.IsPartial() { + params, err := existing.MissingVarNames() + if err != nil { + return nil, err + } + + missingContextParams = params + } else if !existing.Value() { + return existing, nil } - switch cop.Op { - case core.CaveatOperation_AND: - boolResult = boolResult && childResult.Value() + if found.IsPartial() { + params, err := found.MissingVarNames() + if err != nil { + return nil, err + } - if debugOption == RunCaveatExpressionWithDebugInformation { - contextValues = combineMaps(contextValues, childResult.ContextValues()) - exprString, err := childResult.ExpressionString() - if err != nil { - return nil, err - } + missingContextParams = append(missingContextParams, params...) + } else if !found.Value() { + return found, nil + } + + exprString, err := buildExprString() + if err != nil { + return nil, err + } - exprStringPieces = append(exprStringPieces, exprString) + return syntheticResult{ + value: existing.Value() && found.Value(), + contextValues: contextValues, + exprString: exprString, + missingContextParams: missingContextParams, + }, nil + } + + or := func(existing ExpressionResult, found ExpressionResult) (ExpressionResult, error) { + if err := addDebugInfo(found); err != nil { + return nil, err + } + + var missingContextParams []string + if existing.IsPartial() { + params, err := existing.MissingVarNames() + if err != nil { + return nil, err } - if !boolResult { - built, err := buildExprString() - if err != nil { - return nil, err - } + missingContextParams = params + } else if existing.Value() { + return found, nil + } - return syntheticResult{false, contextValues, built}, nil + if found.IsPartial() { + params, err := found.MissingVarNames() + if err != nil { + return nil, err } - case core.CaveatOperation_OR: - boolResult = boolResult || childResult.Value() + missingContextParams = append(missingContextParams, params...) + } else if found.Value() { + return found, nil + } - if debugOption == RunCaveatExpressionWithDebugInformation { - contextValues = combineMaps(contextValues, childResult.ContextValues()) - exprString, err := childResult.ExpressionString() - if err != nil { - return nil, err - } + exprString, err := buildExprString() + if err != nil { + return nil, err + } + + return syntheticResult{ + value: existing.Value() || found.Value(), + contextValues: contextValues, + exprString: exprString, + missingContextParams: missingContextParams, + }, nil + } - exprStringPieces = append(exprStringPieces, exprString) + invert := func(existing ExpressionResult) (ExpressionResult, error) { + if debugOption == RunCaveatExpressionWithDebugInformation { + contextValues = combineMaps(contextValues, existing.ContextValues()) + exprString, err := existing.ExpressionString() + if err != nil { + return nil, err } - if boolResult { - built, err := buildExprString() - if err != nil { - return nil, err - } + exprStringPieces = append(exprStringPieces, "!("+exprString+")") + } + + value := !existing.Value() + + var missingContextParams []string + if existing.IsPartial() { + value = false // partials always have a value of false. + missingContextParams, _ = existing.MissingVarNames() + } + + exprString, err := buildExprString() + if err != nil { + return nil, err + } + + return syntheticResult{ + value: value, + contextValues: contextValues, + exprString: exprString, + missingContextParams: missingContextParams, + }, nil + } - return syntheticResult{true, contextValues, built}, nil + for _, child := range cop.Children { + childResult, err := runExpressionWithCaveats(ctx, env, child, context, loadedCaveats, debugOption) + if err != nil { + return nil, err + } + + switch cop.Op { + case core.CaveatOperation_AND: + cr, err := and(currentResult, childResult) + if err != nil { + return nil, err } - case core.CaveatOperation_NOT: - if debugOption == RunCaveatExpressionWithDebugInformation { - contextValues = combineMaps(contextValues, childResult.ContextValues()) - exprString, err := childResult.ExpressionString() - if err != nil { - return nil, err - } - - exprStringPieces = append(exprStringPieces, "!("+exprString+")") + currentResult = cr + + if isFalseResult(currentResult) { + return currentResult, nil } - built, err := buildExprString() + case core.CaveatOperation_OR: + cr, err := or(currentResult, childResult) if err != nil { return nil, err } - return syntheticResult{!childResult.Value(), contextValues, built}, nil + currentResult = cr + + if isTrueResult(currentResult) { + return currentResult, nil + } + + case core.CaveatOperation_NOT: + return invert(childResult) default: return nil, spiceerrors.MustBugf("unknown caveat operation: %v", cop.Op) } } - built, err := buildExprString() - if err != nil { - return nil, err - } - - return syntheticResult{boolResult, contextValues, built}, nil + return currentResult, nil } func combineMaps(first map[string]any, second map[string]any) map[string]any { diff --git a/internal/caveats/run_test.go b/internal/caveats/run_test.go index dbabd97c3b..0bb718e759 100644 --- a/internal/caveats/run_test.go +++ b/internal/caveats/run_test.go @@ -23,10 +23,12 @@ var ( func TestRunCaveatExpressions(t *testing.T) { tcs := []struct { - name string - expression *core.CaveatExpression - context map[string]any - expectedValue bool + name string + expression *core.CaveatExpression + context map[string]any + expectedValue bool + expectedMissingContext []string + expectedExprString string }{ { "basic", @@ -35,6 +37,8 @@ func TestRunCaveatExpressions(t *testing.T) { "first": "42", }, true, + nil, + "first == 42", }, { "another basic", @@ -43,6 +47,8 @@ func TestRunCaveatExpressions(t *testing.T) { "first": "12", }, false, + nil, + "first == 42", }, { "short circuited or", @@ -54,6 +60,8 @@ func TestRunCaveatExpressions(t *testing.T) { "first": "42", }, true, + nil, + "first == 42", }, { "non-short circuited or", @@ -66,6 +74,8 @@ func TestRunCaveatExpressions(t *testing.T) { "second": "hello", }, true, + nil, + `second == "hello"`, }, { "false or", @@ -78,6 +88,8 @@ func TestRunCaveatExpressions(t *testing.T) { "second": "hi", }, false, + nil, + `first == 42 || second == "hello"`, }, { "short circuited and", @@ -89,6 +101,8 @@ func TestRunCaveatExpressions(t *testing.T) { "first": "12", }, false, + nil, + "first == 42", }, { "non-short circuited and", @@ -101,6 +115,8 @@ func TestRunCaveatExpressions(t *testing.T) { "second": "hello", }, false, + nil, + "first == 42", }, { "false or", @@ -113,6 +129,8 @@ func TestRunCaveatExpressions(t *testing.T) { "second": "hi", }, false, + nil, + `second == "hello"`, }, { "inversion", @@ -123,6 +141,8 @@ func TestRunCaveatExpressions(t *testing.T) { "first": "12", }, true, + nil, + `!(first == 42)`, }, { "nested", @@ -141,6 +161,8 @@ func TestRunCaveatExpressions(t *testing.T) { "third": false, }, false, + nil, + `first == 42 || second == "hello"`, }, { "nested true", @@ -159,6 +181,264 @@ func TestRunCaveatExpressions(t *testing.T) { "third": false, }, true, + nil, + `first == 42 && !(third)`, + }, + { + "missing context on left side of and branch, right branch is true", + caveatAnd( + caveatexpr("firstCaveat"), + caveatexpr("secondCaveat"), + ), + map[string]any{ + "second": "hello", + }, + false, + []string{"first"}, + `first == 42 && second == "hello"`, + }, + { + "missing context on right side of and branch, left branch is true", + caveatAnd( + caveatexpr("firstCaveat"), + caveatexpr("secondCaveat"), + ), + map[string]any{ + "first": "42", + }, + false, + []string{"second"}, + `first == 42 && second == "hello"`, + }, + { + "missing context on left side of or branch, right branch is true", + caveatOr( + caveatexpr("firstCaveat"), + caveatexpr("secondCaveat"), + ), + map[string]any{ + "second": "hello", + }, + true, + nil, + `second == "hello"`, + }, + { + "missing context on right side of or branch, left branch is true", + caveatOr( + caveatexpr("firstCaveat"), + caveatexpr("secondCaveat"), + ), + map[string]any{ + "first": "42", + }, + true, + nil, + `first == 42`, + }, + { + "missing context on left side of and branch, right branch is false", + caveatAnd( + caveatexpr("firstCaveat"), + caveatexpr("secondCaveat"), + ), + map[string]any{ + "second": "hi", + }, + false, + nil, + `second == "hello"`, + }, + { + "missing context on right side of and branch, left branch is false", + caveatAnd( + caveatexpr("firstCaveat"), + caveatexpr("secondCaveat"), + ), + map[string]any{ + "first": "41", + }, + false, + nil, + `first == 42`, + }, + { + "missing context on left side of or branch, right branch is false", + caveatOr( + caveatexpr("firstCaveat"), + caveatexpr("secondCaveat"), + ), + map[string]any{ + "second": "hi", + }, + false, + []string{"first"}, + `first == 42 || second == "hello"`, + }, + { + "missing context on right side of or branch, left branch is false", + caveatOr( + caveatexpr("firstCaveat"), + caveatexpr("secondCaveat"), + ), + map[string]any{ + "first": "41", + }, + false, + []string{"second"}, + `first == 42 || second == "hello"`, + }, + { + "missing context on both sides of and branch", + caveatAnd( + caveatexpr("firstCaveat"), + caveatexpr("secondCaveat"), + ), + map[string]any{}, + false, + []string{"first", "second"}, + `first == 42 && second == "hello"`, + }, + { + "missing context on both sides of or branch", + caveatOr( + caveatexpr("firstCaveat"), + caveatexpr("secondCaveat"), + ), + map[string]any{}, + false, + []string{"first", "second"}, + `first == 42 || second == "hello"`, + }, + { + "missing context under invert", + caveatInvert( + caveatexpr("firstCaveat"), + ), + map[string]any{}, + false, + []string{"first"}, + `!(first == 42)`, + }, + { + "missing context with invert under and with true right branch", + caveatAnd( + caveatInvert( + caveatexpr("firstCaveat"), + ), + caveatexpr("secondCaveat"), + ), + map[string]any{ + "second": "hello", + }, + false, + []string{"first"}, + `!(first == 42) && second == "hello"`, + }, + { + "missing context with invert under and with true left branch", + caveatAnd( + caveatexpr("secondCaveat"), + caveatInvert( + caveatexpr("firstCaveat"), + ), + ), + map[string]any{ + "second": "hello", + }, + false, + []string{"first"}, + `second == "hello" && !(first == 42)`, + }, + { + "missing context with invert under and with false right branch", + caveatAnd( + caveatInvert( + caveatexpr("firstCaveat"), + ), + caveatexpr("secondCaveat"), + ), + map[string]any{ + "second": "hi", + }, + false, + nil, + `second == "hello"`, + }, + { + "missing context with invert under and with false left branch", + caveatAnd( + caveatexpr("secondCaveat"), + caveatInvert( + caveatexpr("firstCaveat"), + ), + ), + map[string]any{ + "second": "hi", + }, + false, + nil, + `second == "hello"`, + }, + { + "missing context with invert under or with true right branch", + caveatOr( + caveatInvert( + caveatexpr("firstCaveat"), + ), + caveatexpr("secondCaveat"), + ), + map[string]any{ + "second": "hello", + }, + true, + nil, + `second == "hello"`, + }, + { + "missing context with invert under or with true left branch", + caveatOr( + caveatexpr("secondCaveat"), + caveatInvert( + caveatexpr("firstCaveat"), + ), + ), + map[string]any{ + "second": "hello", + }, + true, + nil, + `second == "hello"`, + }, + { + "missing context with invert under or with false right branch", + caveatOr( + caveatInvert( + caveatexpr("firstCaveat"), + ), + caveatexpr("secondCaveat"), + ), + map[string]any{ + "second": "hi", + }, + false, + []string{"first"}, + `!(first == 42) || second == "hello"`, + }, + { + "missing context with invert under or with false left branch", + caveatOr( + caveatexpr("secondCaveat"), + caveatInvert( + caveatexpr("firstCaveat"), + ), + ), + map[string]any{ + "second": "hi", + }, + false, + []string{"first"}, + `second == "hello" || !(first == 42)`, }, } @@ -199,6 +479,30 @@ func TestRunCaveatExpressions(t *testing.T) { result, err := caveats.RunCaveatExpression(context.Background(), tc.expression, tc.context, reader, debugOption) req.NoError(err) req.Equal(tc.expectedValue, result.Value()) + + if len(tc.expectedMissingContext) > 0 { + require.False(t, result.Value()) + require.True(t, result.IsPartial()) + + missingFields, err := result.MissingVarNames() + require.NoError(t, err) + require.ElementsMatch(t, tc.expectedMissingContext, missingFields) + } else { + require.False(t, result.IsPartial()) + } + + _, err = result.ContextStruct() + require.NoError(t, err) + + _, err = result.ExpressionString() + require.NoError(t, err) + + if debugOption == caveats.RunCaveatExpressionWithDebugInformation { + exprString, err := result.ExpressionString() + require.NoError(t, err) + require.NotEmpty(t, exprString) + require.Equal(t, tc.expectedExprString, exprString) + } }) } }) diff --git a/internal/dispatch/caching/caching.go b/internal/dispatch/caching/caching.go index 49cfd7bff4..5428502190 100644 --- a/internal/dispatch/caching/caching.go +++ b/internal/dispatch/caching/caching.go @@ -292,6 +292,70 @@ func sliceSize(xs []byte) int64 { return int64(int(unsafe.Sizeof(xs)) + len(xs)) } +func (cd *Dispatcher) DispatchLookupResources2(req *v1.DispatchLookupResources2Request, stream dispatch.LookupResources2Stream) error { + cd.lookupResourcesTotalCounter.Inc() + + requestKey, err := cd.keyHandler.LookupResources2CacheKey(stream.Context(), req) + if err != nil { + return err + } + + if cachedResultRaw, found := cd.c.Get(requestKey); found { + cd.lookupResourcesFromCacheCounter.Inc() + for _, slice := range cachedResultRaw.([][]byte) { + var response v1.DispatchLookupResources2Response + if err := response.UnmarshalVT(slice); err != nil { + return err + } + if err := stream.Publish(&response); err != nil { + // don't wrap error with additional context, as it may be a grpc status.Status. + // status.FromError() is unable to unwrap status.Status values, and as a consequence + // the Dispatcher wouldn't properly propagate the gRPC error code + return err + } + } + return nil + } + + var ( + mu sync.Mutex + toCacheResults [][]byte + ) + wrapped := &dispatch.WrappedDispatchStream[*v1.DispatchLookupResources2Response]{ + Stream: stream, + Ctx: stream.Context(), + Processor: func(result *v1.DispatchLookupResources2Response) (*v1.DispatchLookupResources2Response, bool, error) { + adjustedResult := result.CloneVT() + adjustedResult.Metadata.CachedDispatchCount = adjustedResult.Metadata.DispatchCount + adjustedResult.Metadata.DispatchCount = 0 + adjustedResult.Metadata.DebugInfo = nil + + adjustedBytes, err := adjustedResult.MarshalVT() + if err != nil { + return &v1.DispatchLookupResources2Response{Metadata: &v1.ResponseMeta{}}, false, err + } + + mu.Lock() + toCacheResults = append(toCacheResults, adjustedBytes) + mu.Unlock() + + return result, true, nil + }, + } + + if err := cd.d.DispatchLookupResources2(req, wrapped); err != nil { + return err + } + + var size int64 + for _, slice := range toCacheResults { + size += sliceSize(slice) + } + + cd.c.Set(requestKey, toCacheResults, size) + return nil +} + // DispatchLookupResources implements dispatch.LookupResources interface. func (cd *Dispatcher) DispatchLookupResources(req *v1.DispatchLookupResourcesRequest, stream dispatch.LookupResourcesStream) error { cd.lookupResourcesTotalCounter.Inc() diff --git a/internal/dispatch/caching/cachingdispatch_test.go b/internal/dispatch/caching/cachingdispatch_test.go index 1d4066c531..8eee3d5ee5 100644 --- a/internal/dispatch/caching/cachingdispatch_test.go +++ b/internal/dispatch/caching/cachingdispatch_test.go @@ -167,6 +167,10 @@ func (ddm delegateDispatchMock) DispatchLookupResources(_ *v1.DispatchLookupReso return nil } +func (ddm delegateDispatchMock) DispatchLookupResources2(_ *v1.DispatchLookupResources2Request, _ dispatch.LookupResources2Stream) error { + return nil +} + func (ddm delegateDispatchMock) DispatchLookupSubjects(_ *v1.DispatchLookupSubjectsRequest, _ dispatch.LookupSubjectsStream) error { return nil } diff --git a/internal/dispatch/caching/delegate.go b/internal/dispatch/caching/delegate.go index 7c42529688..399742c4e5 100644 --- a/internal/dispatch/caching/delegate.go +++ b/internal/dispatch/caching/delegate.go @@ -36,6 +36,10 @@ func (fd fakeDelegate) DispatchLookupResources(_ *v1.DispatchLookupResourcesRequ return spiceerrors.MustBugf(errMessage) } +func (fd fakeDelegate) DispatchLookupResources2(_ *v1.DispatchLookupResources2Request, _ dispatch.LookupResources2Stream) error { + return spiceerrors.MustBugf(errMessage) +} + func (fd fakeDelegate) DispatchLookupSubjects(_ *v1.DispatchLookupSubjectsRequest, _ dispatch.LookupSubjectsStream) error { return spiceerrors.MustBugf(errMessage) } diff --git a/internal/dispatch/dispatch.go b/internal/dispatch/dispatch.go index 8ed2ee4b9a..b997aaa4f1 100644 --- a/internal/dispatch/dispatch.go +++ b/internal/dispatch/dispatch.go @@ -26,6 +26,7 @@ type Dispatcher interface { ReachableResources LookupResources LookupSubjects + LookupResources2 // Close closes the dispatcher. Close() error @@ -70,6 +71,15 @@ type LookupResources interface { ) error } +type LookupResources2Stream = Stream[*v1.DispatchLookupResources2Response] + +type LookupResources2 interface { + DispatchLookupResources2( + req *v1.DispatchLookupResources2Request, + stream LookupResources2Stream, + ) error +} + // LookupSubjectsStream is an alias for the stream to which found subjects will be written. type LookupSubjectsStream = Stream[*v1.DispatchLookupSubjectsResponse] diff --git a/internal/dispatch/graph/check_test.go b/internal/dispatch/graph/check_test.go index e5ba8b0039..f44b204529 100644 --- a/internal/dispatch/graph/check_test.go +++ b/internal/dispatch/graph/check_test.go @@ -24,6 +24,7 @@ import ( v1 "github.com/authzed/spicedb/pkg/proto/dispatch/v1" "github.com/authzed/spicedb/pkg/testutil" "github.com/authzed/spicedb/pkg/tuple" + "github.com/authzed/spicedb/pkg/typesystem" ) var ONR = tuple.ObjectAndRelation @@ -1372,6 +1373,363 @@ func TestCheckDebugging(t *testing.T) { } } +func TestCheckWithHints(t *testing.T) { + testCases := []struct { + name string + schema string + relationships []*core.RelationTuple + resource *core.ObjectAndRelation + subject *core.ObjectAndRelation + hints map[string]bool + expectedPermissionship bool + }{ + { + "no relationships", + `definition user {} + + definition document { + relation editor: user + relation viewer: user + permission view = viewer + editor + }`, + []*core.RelationTuple{}, + ONR("document", "somedoc", "view"), + ONR("user", "tom", graph.Ellipsis), + map[string]bool{}, + false, + }, + { + "no relationships with matching check hint", + `definition user {} + + definition document { + relation editor: user + relation viewer: user + permission view = viewer + editor + }`, + []*core.RelationTuple{}, + ONR("document", "somedoc", "view"), + ONR("user", "tom", graph.Ellipsis), + map[string]bool{ + typesystem.CheckHint( + typesystem.ResourceCheckHintForRelation("document", "somedoc", "viewer"), + ONR("user", "tom", graph.Ellipsis), + ): true, + }, + true, + }, + { + "no relationships with non check hint", + `definition user {} + + definition document { + relation editor: user + relation viewer: user + permission view = viewer + editor + }`, + []*core.RelationTuple{}, + ONR("document", "somedoc", "view"), + ONR("user", "tom", graph.Ellipsis), + map[string]bool{ + typesystem.CheckHint( + typesystem.ResourceCheckHintForRelation("document", "anotherdoc", "viewer"), + ONR("user", "tom", graph.Ellipsis), + ): true, + }, + false, + }, + { + "no relationships with non check hint due to subject", + `definition user {} + + definition document { + relation editor: user + relation viewer: user + permission view = viewer + editor + }`, + []*core.RelationTuple{}, + ONR("document", "somedoc", "view"), + ONR("user", "tom", graph.Ellipsis), + map[string]bool{ + typesystem.CheckHint( + typesystem.ResourceCheckHintForRelation("document", "somedoc", "viewer"), + ONR("user", "anotheruser", graph.Ellipsis), + ): true, + }, + false, + }, + { + "no relationships with matching arrow check hint", + `definition user {} + + definition organization { + relation member: user + } + + definition document { + relation org: organization + permission view = org->member + }`, + []*core.RelationTuple{}, + ONR("document", "somedoc", "view"), + ONR("user", "tom", graph.Ellipsis), + map[string]bool{ + typesystem.CheckHint( + typesystem.ResourceCheckHintForArrow("document", "somedoc", "org", "member"), + ONR("user", "tom", graph.Ellipsis), + ): true, + }, + true, + }, + { + "no relationships with non matching tupleset arrow check hint", + `definition user {} + + definition organization { + relation member: user + } + + definition document { + relation org: organization + permission view = org->member + }`, + []*core.RelationTuple{}, + ONR("document", "somedoc", "view"), + ONR("user", "tom", graph.Ellipsis), + map[string]bool{ + typesystem.CheckHint( + typesystem.ResourceCheckHintForArrow("document", "somedoc", "anotherrel", "member"), + ONR("user", "tom", graph.Ellipsis), + ): true, + }, + false, + }, + { + "no relationships with non matching computed userset arrow check hint", + `definition user {} + + definition organization { + relation member: user + } + + definition document { + relation org: organization + permission view = org->member + }`, + []*core.RelationTuple{}, + ONR("document", "somedoc", "view"), + ONR("user", "tom", graph.Ellipsis), + map[string]bool{ + typesystem.CheckHint( + typesystem.ResourceCheckHintForArrow("document", "somedoc", "org", "membersssssss"), + ONR("user", "tom", graph.Ellipsis), + ): true, + }, + false, + }, + { + "check hint over part of an intersection but missing other branch in rels", + `definition user {} + + definition document { + relation editor: user + relation viewer: user + permission view = viewer & editor + }`, + []*core.RelationTuple{}, + ONR("document", "somedoc", "view"), + ONR("user", "tom", graph.Ellipsis), + map[string]bool{ + typesystem.CheckHint( + typesystem.ResourceCheckHintForRelation("document", "somedoc", "viewer"), + ONR("user", "tom", graph.Ellipsis), + ): true, + }, + false, + }, + { + "check hint over part of an intersection with other branch in rels", + `definition user {} + + definition document { + relation editor: user + relation viewer: user + permission view = viewer & editor + }`, + []*core.RelationTuple{ + tuple.MustParse("document:somedoc#editor@user:tom"), + }, + ONR("document", "somedoc", "view"), + ONR("user", "tom", graph.Ellipsis), + map[string]bool{ + typesystem.CheckHint( + typesystem.ResourceCheckHintForRelation("document", "somedoc", "viewer"), + ONR("user", "tom", graph.Ellipsis), + ): true, + }, + true, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + require := require.New(t) + + dispatcher := NewLocalOnlyDispatcher(10) + + ds, err := memdb.NewMemdbDatastore(0, 0, memdb.DisableGC) + require.NoError(err) + + ds, revision := testfixtures.DatastoreFromSchemaAndTestRelationships(ds, tc.schema, tc.relationships, require) + + ctx := datastoremw.ContextWithHandle(context.Background()) + require.NoError(datastoremw.SetInContext(ctx, ds)) + + checkHints := make(map[string]*v1.ResourceCheckResult, len(tc.hints)) + for hint, value := range tc.hints { + if value { + checkHints[hint] = &v1.ResourceCheckResult{ + Membership: v1.ResourceCheckResult_MEMBER, + } + } else { + checkHints[hint] = &v1.ResourceCheckResult{ + Membership: v1.ResourceCheckResult_NOT_MEMBER, + } + } + } + + resp, err := dispatcher.DispatchCheck(ctx, &v1.DispatchCheckRequest{ + ResourceRelation: RR(tc.resource.Namespace, tc.resource.Relation), + ResourceIds: []string{tc.resource.ObjectId}, + Subject: tc.subject, + ResultsSetting: v1.DispatchCheckRequest_ALLOW_SINGLE_RESULT, + Metadata: &v1.ResolverMeta{ + AtRevision: revision.String(), + DepthRemaining: 50, + }, + CheckHints: checkHints, + }) + require.NoError(err) + + _, ok := resp.ResultsByResourceId[tc.resource.ObjectId] + if tc.expectedPermissionship { + require.True(ok) + require.Equal(v1.ResourceCheckResult_MEMBER, resp.ResultsByResourceId[tc.resource.ObjectId].Membership) + } else { + if ok { + require.Equal(v1.ResourceCheckResult_NOT_MEMBER, resp.ResultsByResourceId[tc.resource.ObjectId].Membership) + } + } + }) + } +} + +func TestCheckHintsPartialApplication(t *testing.T) { + require := require.New(t) + + dispatcher := NewLocalOnlyDispatcher(10) + + ds, err := memdb.NewMemdbDatastore(0, 0, memdb.DisableGC) + require.NoError(err) + + ds, revision := testfixtures.DatastoreFromSchemaAndTestRelationships(ds, ` + definition user {} + + definition document { + relation viewer: user + permission view = viewer + } + + `, []*core.RelationTuple{ + tuple.MustParse("document:somedoc#viewer@user:tom"), + }, require) + + ctx := datastoremw.ContextWithHandle(context.Background()) + require.NoError(datastoremw.SetInContext(ctx, ds)) + + checkHints := map[string]*v1.ResourceCheckResult{ + typesystem.CheckHint( + typesystem.ResourceCheckHintForRelation("document", "anotherdoc", "viewer"), + ONR("user", "tom", graph.Ellipsis), + ): { + Membership: v1.ResourceCheckResult_MEMBER, + }, + } + + resp, err := dispatcher.DispatchCheck(ctx, &v1.DispatchCheckRequest{ + ResourceRelation: RR("document", "view"), + ResourceIds: []string{"somedoc", "anotherdoc", "thirddoc"}, + Subject: ONR("user", "tom", graph.Ellipsis), + ResultsSetting: v1.DispatchCheckRequest_REQUIRE_ALL_RESULTS, + Metadata: &v1.ResolverMeta{ + AtRevision: revision.String(), + DepthRemaining: 50, + }, + CheckHints: checkHints, + }) + require.NoError(err) + + require.Len(resp.ResultsByResourceId, 2) + require.Equal(v1.ResourceCheckResult_MEMBER, resp.ResultsByResourceId["somedoc"].Membership) + require.Equal(v1.ResourceCheckResult_MEMBER, resp.ResultsByResourceId["anotherdoc"].Membership) +} + +func TestCheckHintsPartialApplicationOverArrow(t *testing.T) { + require := require.New(t) + + dispatcher := NewLocalOnlyDispatcher(10) + + ds, err := memdb.NewMemdbDatastore(0, 0, memdb.DisableGC) + require.NoError(err) + + ds, revision := testfixtures.DatastoreFromSchemaAndTestRelationships(ds, ` + definition user {} + + definition organization { + relation member: user + } + + definition document { + relation org: organization + permission view = org->member + } + + `, []*core.RelationTuple{ + tuple.MustParse("document:somedoc#org@organization:someorg"), + tuple.MustParse("organization:someorg#member@user:tom"), + }, require) + + ctx := datastoremw.ContextWithHandle(context.Background()) + require.NoError(datastoremw.SetInContext(ctx, ds)) + + checkHints := map[string]*v1.ResourceCheckResult{ + typesystem.CheckHint( + typesystem.ResourceCheckHintForArrow("document", "anotherdoc", "org", "member"), + ONR("user", "tom", graph.Ellipsis), + ): { + Membership: v1.ResourceCheckResult_MEMBER, + }, + } + + resp, err := dispatcher.DispatchCheck(ctx, &v1.DispatchCheckRequest{ + ResourceRelation: RR("document", "view"), + ResourceIds: []string{"somedoc", "anotherdoc", "thirddoc"}, + Subject: ONR("user", "tom", graph.Ellipsis), + ResultsSetting: v1.DispatchCheckRequest_REQUIRE_ALL_RESULTS, + Metadata: &v1.ResolverMeta{ + AtRevision: revision.String(), + DepthRemaining: 50, + }, + CheckHints: checkHints, + }) + require.NoError(err) + + require.Len(resp.ResultsByResourceId, 2) + require.Equal(v1.ResourceCheckResult_MEMBER, resp.ResultsByResourceId["somedoc"].Membership) + require.Equal(v1.ResourceCheckResult_MEMBER, resp.ResultsByResourceId["anotherdoc"].Membership) +} + func newLocalDispatcherWithConcurrencyLimit(t testing.TB, concurrencyLimit uint16) (context.Context, dispatch.Dispatcher, datastore.Revision) { rawDS, err := memdb.NewMemdbDatastore(0, 0, memdb.DisableGC) require.NoError(t, err) diff --git a/internal/dispatch/graph/graph.go b/internal/dispatch/graph/graph.go index 72a8d9093a..c44dd93ffb 100644 --- a/internal/dispatch/graph/graph.go +++ b/internal/dispatch/graph/graph.go @@ -94,6 +94,7 @@ func NewLocalOnlyDispatcherWithLimits(concurrencyLimits ConcurrencyLimits) dispa d.reachableResourcesHandler = graph.NewCursoredReachableResources(d, concurrencyLimits.ReachableResources) d.lookupResourcesHandler = graph.NewCursoredLookupResources(d, d, concurrencyLimits.LookupResources) d.lookupSubjectsHandler = graph.NewConcurrentLookupSubjects(d, concurrencyLimits.LookupSubjects) + d.lookupResourcesHandler2 = graph.NewCursoredLookupResources2(d, d, concurrencyLimits.LookupResources) return d } @@ -108,6 +109,7 @@ func NewDispatcher(redispatcher dispatch.Dispatcher, concurrencyLimits Concurren reachableResourcesHandler := graph.NewCursoredReachableResources(redispatcher, concurrencyLimits.ReachableResources) lookupResourcesHandler := graph.NewCursoredLookupResources(redispatcher, redispatcher, concurrencyLimits.LookupResources) lookupSubjectsHandler := graph.NewConcurrentLookupSubjects(redispatcher, concurrencyLimits.LookupSubjects) + lookupResourcesHandler2 := graph.NewCursoredLookupResources2(redispatcher, redispatcher, concurrencyLimits.LookupResources) return &localDispatcher{ checker: checker, @@ -115,6 +117,7 @@ func NewDispatcher(redispatcher dispatch.Dispatcher, concurrencyLimits Concurren reachableResourcesHandler: reachableResourcesHandler, lookupResourcesHandler: lookupResourcesHandler, lookupSubjectsHandler: lookupSubjectsHandler, + lookupResourcesHandler2: lookupResourcesHandler2, } } @@ -124,6 +127,7 @@ type localDispatcher struct { reachableResourcesHandler *graph.CursoredReachableResources lookupResourcesHandler *graph.CursoredLookupResources lookupSubjectsHandler *graph.ConcurrentLookupSubjects + lookupResourcesHandler2 *graph.CursoredLookupResources2 } func (ld *localDispatcher) loadNamespace(ctx context.Context, nsName string, revision datastore.Revision) (*core.NamespaceDefinition, error) { @@ -228,6 +232,7 @@ func (ld *localDispatcher) DispatchCheck(ctx context.Context, req *v1.DispatchCh Subject: req.Subject, Metadata: req.Metadata, Debug: req.Debug, + CheckHints: req.CheckHints, }, Revision: revision, } @@ -337,6 +342,34 @@ func (ld *localDispatcher) DispatchLookupResources( ) } +func (ld *localDispatcher) DispatchLookupResources2( + req *v1.DispatchLookupResources2Request, + stream dispatch.LookupResources2Stream, +) error { + ctx, span := tracer.Start(stream.Context(), "DispatchLookupResources", trace.WithAttributes( + attribute.String("resource-type", tuple.StringRR(req.ResourceRelation)), + attribute.String("subject", tuple.StringONR(req.TerminalSubject)), + )) + defer span.End() + + if err := dispatch.CheckDepth(ctx, req); err != nil { + return err + } + + revision, err := ld.parseRevision(ctx, req.Metadata.AtRevision) + if err != nil { + return err + } + + return ld.lookupResourcesHandler2.LookupResources2( + graph.ValidatedLookupResources2Request{ + DispatchLookupResources2Request: req, + Revision: revision, + }, + dispatch.StreamWithContext(ctx, stream), + ) +} + // DispatchLookupSubjects implements dispatch.LookupSubjects interface func (ld *localDispatcher) DispatchLookupSubjects( req *v1.DispatchLookupSubjectsRequest, diff --git a/internal/dispatch/graph/lookupresources2_test.go b/internal/dispatch/graph/lookupresources2_test.go new file mode 100644 index 0000000000..a467e52190 --- /dev/null +++ b/internal/dispatch/graph/lookupresources2_test.go @@ -0,0 +1,661 @@ +package graph + +import ( + "context" + "fmt" + "slices" + "testing" + "time" + + "github.com/stretchr/testify/require" + "go.uber.org/goleak" + + "github.com/authzed/spicedb/internal/datastore/memdb" + "github.com/authzed/spicedb/internal/dispatch" + datastoremw "github.com/authzed/spicedb/internal/middleware/datastore" + "github.com/authzed/spicedb/internal/testfixtures" + "github.com/authzed/spicedb/pkg/genutil/mapz" + core "github.com/authzed/spicedb/pkg/proto/core/v1" + v1 "github.com/authzed/spicedb/pkg/proto/dispatch/v1" + "github.com/authzed/spicedb/pkg/tuple" +) + +func TestSimpleLookupResources2(t *testing.T) { + defer goleak.VerifyNone(t, goleakIgnores...) + + testCases := []struct { + start *core.RelationReference + target *core.ObjectAndRelation + expectedResources []*v1.PossibleResource + expectedDispatchCount uint32 + expectedDepthRequired uint32 + }{ + { + RR("document", "view"), + ONR("user", "unknown", "..."), + []*v1.PossibleResource{}, + 0, + 0, + }, + { + RR("document", "view"), + ONR("user", "eng_lead", "..."), + []*v1.PossibleResource{ + possibleRes("masterplan"), + }, + 2, + 1, + }, + { + RR("document", "owner"), + ONR("user", "product_manager", "..."), + []*v1.PossibleResource{ + possibleRes("masterplan"), + }, + 2, + 0, + }, + { + RR("document", "view"), + ONR("user", "legal", "..."), + []*v1.PossibleResource{ + possibleRes("companyplan"), + possibleRes("masterplan"), + }, + 6, + 3, + }, + { + RR("document", "view_and_edit"), + ONR("user", "multiroleguy", "..."), + []*v1.PossibleResource{ + possibleRes("specialplan"), + }, + 7, + 5, + }, + { + RR("folder", "view"), + ONR("user", "owner", "..."), + []*v1.PossibleResource{ + possibleRes("strategy"), + possibleRes("company"), + }, + 8, + 4, + }, + } + + for _, tc := range testCases { + name := fmt.Sprintf( + "%s#%s->%s", + tc.start.Namespace, + tc.start.Relation, + tuple.StringONR(tc.target), + ) + + tc := tc + t.Run(name, func(t *testing.T) { + defer goleak.VerifyNone(t, goleak.IgnoreCurrent()) + + require := require.New(t) + ctx, dispatcher, revision := newLocalDispatcher(t) + defer dispatcher.Close() + + stream := dispatch.NewCollectingDispatchStream[*v1.DispatchLookupResources2Response](ctx) + err := dispatcher.DispatchLookupResources2(&v1.DispatchLookupResources2Request{ + ResourceRelation: tc.start, + SubjectRelation: RR(tc.target.Namespace, tc.target.Relation), + SubjectIds: []string{tc.target.ObjectId}, + TerminalSubject: tc.target, + Metadata: &v1.ResolverMeta{ + AtRevision: revision.String(), + DepthRemaining: 50, + }, + OptionalLimit: veryLargeLimit, + }, stream) + + require.NoError(err) + + foundResources, maxDepthRequired, maxDispatchCount, maxCachedDispatchCount := processResults2(stream) + require.ElementsMatch(tc.expectedResources, foundResources, "Found: %v, Expected: %v", foundResources, tc.expectedResources) + require.Equal(tc.expectedDepthRequired, maxDepthRequired, "Depth required mismatch") + require.LessOrEqual(maxDispatchCount, tc.expectedDispatchCount, "Found dispatch count greater than expected") + require.Equal(uint32(0), maxCachedDispatchCount) + + // We have to sleep a while to let the cache converge: + // https://github.com/outcaste-io/ristretto/blob/01b9f37dd0fd453225e042d6f3a27cd14f252cd0/cache_test.go#L17 + time.Sleep(10 * time.Millisecond) + + // Run again with the cache available. + stream = dispatch.NewCollectingDispatchStream[*v1.DispatchLookupResources2Response](ctx) + err = dispatcher.DispatchLookupResources2(&v1.DispatchLookupResources2Request{ + ResourceRelation: tc.start, + SubjectRelation: RR(tc.target.Namespace, tc.target.Relation), + SubjectIds: []string{tc.target.ObjectId}, + TerminalSubject: tc.target, + Metadata: &v1.ResolverMeta{ + AtRevision: revision.String(), + DepthRemaining: 50, + }, + OptionalLimit: veryLargeLimit, + }, stream) + dispatcher.Close() + + require.NoError(err) + + foundResources, maxDepthRequired, maxDispatchCount, maxCachedDispatchCount = processResults2(stream) + require.ElementsMatch(tc.expectedResources, foundResources, "Found: %v, Expected: %v", foundResources, tc.expectedResources) + require.Equal(tc.expectedDepthRequired, maxDepthRequired, "Depth required mismatch") + require.LessOrEqual(maxCachedDispatchCount, tc.expectedDispatchCount, "Found dispatch count greater than expected") + require.Equal(uint32(0), maxDispatchCount) + }) + } +} + +func TestSimpleLookupResourcesWithCursor2(t *testing.T) { + defer goleak.VerifyNone(t, goleakIgnores...) + + for _, tc := range []struct { + subject string + expectedFirst []string + expectedSecond []string + }{ + { + subject: "owner", + expectedFirst: []string{"ownerplan"}, + expectedSecond: []string{"companyplan", "masterplan", "ownerplan"}, + }, + { + subject: "chief_financial_officer", + expectedFirst: []string{"healthplan"}, + expectedSecond: []string{"healthplan", "masterplan"}, + }, + { + subject: "auditor", + expectedFirst: []string{"companyplan"}, + expectedSecond: []string{"companyplan", "masterplan"}, + }, + } { + tc := tc + t.Run(tc.subject, func(t *testing.T) { + require := require.New(t) + ctx, dispatcher, revision := newLocalDispatcher(t) + defer dispatcher.Close() + + found := mapz.NewSet[string]() + + stream := dispatch.NewCollectingDispatchStream[*v1.DispatchLookupResources2Response](ctx) + err := dispatcher.DispatchLookupResources2(&v1.DispatchLookupResources2Request{ + ResourceRelation: RR("document", "view"), + SubjectRelation: RR("user", "..."), + SubjectIds: []string{tc.subject}, + TerminalSubject: ONR("user", tc.subject, "..."), + Metadata: &v1.ResolverMeta{ + AtRevision: revision.String(), + DepthRemaining: 50, + }, + OptionalLimit: 1, + }, stream) + + require.NoError(err) + + require.Equal(1, len(stream.Results())) + + found.Insert(stream.Results()[0].Resource.ResourceId) + require.Equal(tc.expectedFirst, found.AsSlice()) + + cursor := stream.Results()[0].AfterResponseCursor + require.NotNil(cursor) + + stream = dispatch.NewCollectingDispatchStream[*v1.DispatchLookupResources2Response](ctx) + err = dispatcher.DispatchLookupResources2(&v1.DispatchLookupResources2Request{ + ResourceRelation: RR("document", "view"), + SubjectRelation: RR("user", "..."), + SubjectIds: []string{tc.subject}, + TerminalSubject: ONR("user", tc.subject, "..."), + Metadata: &v1.ResolverMeta{ + AtRevision: revision.String(), + DepthRemaining: 50, + }, + OptionalCursor: cursor, + OptionalLimit: 2, + }, stream) + + require.NoError(err) + + for _, result := range stream.Results() { + found.Insert(result.Resource.ResourceId) + } + + foundResults := found.AsSlice() + slices.Sort(foundResults) + + require.Equal(tc.expectedSecond, foundResults) + }) + } +} + +func TestLookupResourcesCursorStability2(t *testing.T) { + defer goleak.VerifyNone(t, goleakIgnores...) + + require := require.New(t) + ctx, dispatcher, revision := newLocalDispatcher(t) + defer dispatcher.Close() + + stream := dispatch.NewCollectingDispatchStream[*v1.DispatchLookupResources2Response](ctx) + + // Make the first first request. + err := dispatcher.DispatchLookupResources2(&v1.DispatchLookupResources2Request{ + ResourceRelation: RR("document", "view"), + SubjectRelation: RR("user", "..."), + SubjectIds: []string{"owner"}, + TerminalSubject: ONR("user", "owner", "..."), + Metadata: &v1.ResolverMeta{ + AtRevision: revision.String(), + DepthRemaining: 50, + }, + OptionalLimit: 2, + }, stream) + + require.NoError(err) + require.Equal(2, len(stream.Results())) + + cursor := stream.Results()[1].AfterResponseCursor + require.NotNil(cursor) + + // Make the same request and ensure the cursor has not changed. + stream = dispatch.NewCollectingDispatchStream[*v1.DispatchLookupResources2Response](ctx) + err = dispatcher.DispatchLookupResources2(&v1.DispatchLookupResources2Request{ + ResourceRelation: RR("document", "view"), + SubjectRelation: RR("user", "..."), + SubjectIds: []string{"owner"}, + TerminalSubject: ONR("user", "owner", "..."), + Metadata: &v1.ResolverMeta{ + AtRevision: revision.String(), + DepthRemaining: 50, + }, + OptionalLimit: 2, + }, stream) + + require.NoError(err) + + require.NoError(err) + require.Equal(2, len(stream.Results())) + + cursorAgain := stream.Results()[1].AfterResponseCursor + require.NotNil(cursor) + require.Equal(cursor, cursorAgain) +} + +func processResults2(stream *dispatch.CollectingDispatchStream[*v1.DispatchLookupResources2Response]) ([]*v1.PossibleResource, uint32, uint32, uint32) { + foundResources := []*v1.PossibleResource{} + var maxDepthRequired uint32 + var maxDispatchCount uint32 + var maxCachedDispatchCount uint32 + for _, result := range stream.Results() { + result.Resource.ForSubjectIds = nil + foundResources = append(foundResources, result.Resource) + maxDepthRequired = max(maxDepthRequired, result.Metadata.DepthRequired) + maxDispatchCount = max(maxDispatchCount, result.Metadata.DispatchCount) + maxCachedDispatchCount = max(maxCachedDispatchCount, result.Metadata.CachedDispatchCount) + } + return foundResources, maxDepthRequired, maxDispatchCount, maxCachedDispatchCount +} + +func TestMaxDepthLookup2(t *testing.T) { + require := require.New(t) + + rawDS, err := memdb.NewMemdbDatastore(0, 0, memdb.DisableGC) + require.NoError(err) + + ds, revision := testfixtures.StandardDatastoreWithData(rawDS, require) + + dispatcher := NewLocalOnlyDispatcher(10) + defer dispatcher.Close() + + ctx := datastoremw.ContextWithHandle(context.Background()) + require.NoError(datastoremw.SetInContext(ctx, ds)) + stream := dispatch.NewCollectingDispatchStream[*v1.DispatchLookupResources2Response](ctx) + + err = dispatcher.DispatchLookupResources2(&v1.DispatchLookupResources2Request{ + ResourceRelation: RR("document", "view"), + SubjectRelation: RR("user", "..."), + SubjectIds: []string{"legal"}, + TerminalSubject: ONR("user", "legal", "..."), + Metadata: &v1.ResolverMeta{ + AtRevision: revision.String(), + DepthRemaining: 0, + }, + }, stream) + + require.Error(err) +} + +func TestLookupResources2OverSchemaWithCursors(t *testing.T) { + testCases := []struct { + name string + schema string + relationships []*core.RelationTuple + permission *core.RelationReference + subject *core.ObjectAndRelation + expectedResourceIDs []string + }{ + { + "basic union", + `definition user {} + + definition document { + relation editor: user + relation viewer: user + permission view = viewer + editor + }`, + joinTuples( + genTuples("document", "viewer", "user", "tom", 1510), + genTuples("document", "editor", "user", "tom", 1510), + ), + RR("document", "view"), + ONR("user", "tom", "..."), + genResourceIds("document", 1510), + }, + { + "basic exclusion", + `definition user {} + + definition document { + relation banned: user + relation viewer: user + permission view = viewer - banned + }`, + genTuples("document", "viewer", "user", "tom", 1010), + RR("document", "view"), + ONR("user", "tom", "..."), + genResourceIds("document", 1010), + }, + { + "basic intersection", + `definition user {} + + definition document { + relation editor: user + relation viewer: user + permission view = viewer & editor + }`, + joinTuples( + genTuples("document", "viewer", "user", "tom", 510), + genTuples("document", "editor", "user", "tom", 510), + ), + RR("document", "view"), + ONR("user", "tom", "..."), + genResourceIds("document", 510), + }, + { + "union and excluded union", + `definition user {} + + definition document { + relation editor: user + relation viewer: user + relation banned: user + permission can_view = viewer - banned + permission view = can_view + editor + }`, + joinTuples( + genTuples("document", "viewer", "user", "tom", 1310), + genTuplesWithOffset("document", "editor", "user", "tom", 1250, 1200), + ), + RR("document", "view"), + ONR("user", "tom", "..."), + genResourceIds("document", 2450), + }, + { + "basic caveats", + `definition user {} + + caveat somecaveat(somecondition int) { + somecondition == 42 + } + + definition document { + relation viewer: user with somecaveat + permission view = viewer + }`, + genTuplesWithCaveat("document", "viewer", "user", "tom", "somecaveat", map[string]any{"somecondition": 42}, 0, 2450), + RR("document", "view"), + ONR("user", "tom", "..."), + genResourceIds("document", 2450), + }, + { + "excluded items", + `definition user {} + + definition document { + relation banned: user + relation viewer: user + permission view = viewer - banned + }`, + joinTuples( + genTuples("document", "viewer", "user", "tom", 1310), + genTuplesWithOffset("document", "banned", "user", "tom", 1210, 100), + ), + RR("document", "view"), + ONR("user", "tom", "..."), + genResourceIds("document", 1210), + }, + { + "basic caveats with missing field", + `definition user {} + + caveat somecaveat(somecondition int) { + somecondition == 42 + } + + definition document { + relation viewer: user with somecaveat + permission view = viewer + }`, + genTuplesWithCaveat("document", "viewer", "user", "tom", "somecaveat", map[string]any{}, 0, 2450), + RR("document", "view"), + ONR("user", "tom", "..."), + genResourceIds("document", 2450), + }, + { + "larger arrow dispatch", + `definition user {} + + definition folder { + relation viewer: user + } + + definition document { + relation folder: folder + permission view = folder->viewer + }`, + joinTuples( + genTuples("folder", "viewer", "user", "tom", 150), + genSubjectTuples("document", "folder", "folder", "...", 150), + ), + RR("document", "view"), + ONR("user", "tom", "..."), + genResourceIds("document", 150), + }, + { + "big", + `definition user {} + + definition document { + relation editor: user + relation viewer: user + permission view = viewer + editor + }`, + joinTuples( + genTuples("document", "viewer", "user", "tom", 15100), + genTuples("document", "editor", "user", "tom", 15100), + ), + RR("document", "view"), + ONR("user", "tom", "..."), + genResourceIds("document", 15100), + }, + { + "arrow under intersection", + `definition user {} + + definition organization { + relation member: user + } + + definition document { + relation org: organization + relation viewer: user + permission view = org->member & viewer + }`, + joinTuples( + genTuples("document", "viewer", "user", "tom", 510), + joinTuples( + genTuples("document", "org", "organization", "someorg", 510), + []*core.RelationTuple{ + tuple.MustParse("organization:someorg#member@user:tom"), + }, + ), + ), + RR("document", "view"), + ONR("user", "tom", "..."), + genResourceIds("document", 510), + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + for _, pageSize := range []int{0, 104, 1023} { + pageSize := pageSize + t.Run(fmt.Sprintf("ps-%d_", pageSize), func(t *testing.T) { + require := require.New(t) + + dispatcher := NewLocalOnlyDispatcher(10) + + ds, err := memdb.NewMemdbDatastore(0, 0, memdb.DisableGC) + require.NoError(err) + + ds, revision := testfixtures.DatastoreFromSchemaAndTestRelationships(ds, tc.schema, tc.relationships, require) + + ctx := datastoremw.ContextWithHandle(context.Background()) + require.NoError(datastoremw.SetInContext(ctx, ds)) + + var currentCursor *v1.Cursor + foundResourceIDs := mapz.NewSet[string]() + for { + stream := dispatch.NewCollectingDispatchStream[*v1.DispatchLookupResources2Response](ctx) + err = dispatcher.DispatchLookupResources2(&v1.DispatchLookupResources2Request{ + ResourceRelation: tc.permission, + SubjectRelation: RR(tc.subject.Namespace, "..."), + SubjectIds: []string{tc.subject.ObjectId}, + TerminalSubject: tc.subject, + Metadata: &v1.ResolverMeta{ + AtRevision: revision.String(), + DepthRemaining: 50, + }, + OptionalLimit: uint32(pageSize), + OptionalCursor: currentCursor, + }, stream) + require.NoError(err) + + if pageSize > 0 { + require.LessOrEqual(len(stream.Results()), pageSize) + } + + for _, result := range stream.Results() { + foundResourceIDs.Insert(result.Resource.ResourceId) + currentCursor = result.AfterResponseCursor + } + + if pageSize == 0 || len(stream.Results()) < pageSize { + break + } + } + + foundResourceIDsSlice := foundResourceIDs.AsSlice() + slices.Sort(foundResourceIDsSlice) + slices.Sort(tc.expectedResourceIDs) + + require.Equal(tc.expectedResourceIDs, foundResourceIDsSlice) + }) + } + }) + } +} + +func TestLookupResources2ImmediateTimeout(t *testing.T) { + defer goleak.VerifyNone(t, goleak.IgnoreCurrent()) + + require := require.New(t) + + rawDS, err := memdb.NewMemdbDatastore(0, 0, memdb.DisableGC) + require.NoError(err) + + ds, revision := testfixtures.StandardDatastoreWithData(rawDS, require) + + dispatcher := NewLocalOnlyDispatcher(10) + defer dispatcher.Close() + + ctx := datastoremw.ContextWithHandle(context.Background()) + cctx, cancel := context.WithTimeout(ctx, 1*time.Nanosecond) + defer cancel() + + require.NoError(datastoremw.SetInContext(cctx, ds)) + stream := dispatch.NewCollectingDispatchStream[*v1.DispatchLookupResources2Response](cctx) + + err = dispatcher.DispatchLookupResources2(&v1.DispatchLookupResources2Request{ + ResourceRelation: RR("document", "view"), + SubjectRelation: RR("user", "..."), + SubjectIds: []string{"legal"}, + TerminalSubject: ONR("user", "legal", "..."), + Metadata: &v1.ResolverMeta{ + AtRevision: revision.String(), + DepthRemaining: 10, + }, + }, stream) + + require.ErrorIs(err, context.DeadlineExceeded) + require.ErrorContains(err, "context deadline exceeded") +} + +func TestLookupResources2WithError(t *testing.T) { + defer goleak.VerifyNone(t, goleak.IgnoreCurrent()) + + require := require.New(t) + + rawDS, err := memdb.NewMemdbDatastore(0, 0, memdb.DisableGC) + require.NoError(err) + + ds, revision := testfixtures.StandardDatastoreWithData(rawDS, require) + + dispatcher := NewLocalOnlyDispatcher(10) + defer dispatcher.Close() + + ctx := datastoremw.ContextWithHandle(context.Background()) + cctx, cancel := context.WithTimeout(ctx, 1*time.Nanosecond) + defer cancel() + + require.NoError(datastoremw.SetInContext(cctx, ds)) + stream := dispatch.NewCollectingDispatchStream[*v1.DispatchLookupResources2Response](cctx) + + err = dispatcher.DispatchLookupResources2(&v1.DispatchLookupResources2Request{ + ResourceRelation: RR("document", "view"), + SubjectRelation: RR("user", "..."), + SubjectIds: []string{"legal"}, + TerminalSubject: ONR("user", "legal", "..."), + Metadata: &v1.ResolverMeta{ + AtRevision: revision.String(), + DepthRemaining: 1, // Set depth 1 to cause an error within reachable resources + }, + }, stream) + + require.Error(err) +} + +func possibleRes(resourceID string) *v1.PossibleResource { + return &v1.PossibleResource{ + ResourceId: resourceID, + } +} diff --git a/internal/dispatch/keys/computed.go b/internal/dispatch/keys/computed.go index 38c9a3a732..a9794d2f22 100644 --- a/internal/dispatch/keys/computed.go +++ b/internal/dispatch/keys/computed.go @@ -95,6 +95,19 @@ func lookupResourcesRequestToKey(req *v1.DispatchLookupResourcesRequest, option ) } +// lookupResourcesRequest2ToKey converts a lookup request into a cache key +func lookupResourcesRequest2ToKey(req *v1.DispatchLookupResources2Request, option dispatchCacheKeyHashComputeOption) DispatchCacheKey { + return dispatchCacheKeyHash(lookupPrefix, req.Metadata.AtRevision, option, + hashableRelationReference{req.ResourceRelation}, + hashableRelationReference{req.SubjectRelation}, + hashableIds(req.SubjectIds), + hashableOnr{req.TerminalSubject}, + hashableContext{HashableContext: caveats.HashableContext{Struct: req.Context}}, // NOTE: context is included here because lookup does a single dispatch + hashableCursor{req.OptionalCursor}, + hashableLimit(req.OptionalLimit), + ) +} + // lookupSubjectsRequestToKey converts a lookup subjects request into a cache key func lookupSubjectsRequestToKey(req *v1.DispatchLookupSubjectsRequest, option dispatchCacheKeyHashComputeOption) DispatchCacheKey { return dispatchCacheKeyHash(lookupSubjectsPrefix, req.Metadata.AtRevision, option, diff --git a/internal/dispatch/keys/computed_test.go b/internal/dispatch/keys/computed_test.go index 8af141e99f..9069fecfe2 100644 --- a/internal/dispatch/keys/computed_test.go +++ b/internal/dispatch/keys/computed_test.go @@ -405,6 +405,266 @@ func TestStableCacheKeys(t *testing.T) { }, "d699c5b5d3a6dfade601", }, + { + "lookup resources 2", + func() DispatchCacheKey { + return lookupResourcesRequest2ToKey(&v1.DispatchLookupResources2Request{ + ResourceRelation: RR("document", "view"), + SubjectRelation: RR("user", "..."), + SubjectIds: []string{"mariah"}, + TerminalSubject: ONR("user", "mariah", "..."), + Metadata: &v1.ResolverMeta{ + AtRevision: "1234", + }, + }, computeBothHashes) + }, + "f49fbafef9c489abe601", + }, + { + "lookup resources 2 with zero limit", + func() DispatchCacheKey { + return lookupResourcesRequest2ToKey(&v1.DispatchLookupResources2Request{ + ResourceRelation: RR("document", "view"), + SubjectRelation: RR("user", "..."), + SubjectIds: []string{"mariah"}, + TerminalSubject: ONR("user", "mariah", "..."), + Metadata: &v1.ResolverMeta{ + AtRevision: "1234", + }, + OptionalLimit: 0, + }, computeBothHashes) + }, + "f49fbafef9c489abe601", + }, + { + "lookup resources 2 with non-zero limit", + func() DispatchCacheKey { + return lookupResourcesRequest2ToKey(&v1.DispatchLookupResources2Request{ + ResourceRelation: RR("document", "view"), + SubjectRelation: RR("user", "..."), + SubjectIds: []string{"mariah"}, + TerminalSubject: ONR("user", "mariah", "..."), + Metadata: &v1.ResolverMeta{ + AtRevision: "1234", + }, + OptionalLimit: 42, + }, computeBothHashes) + }, + "ea88adb1c1dfa6ebab01", + }, + { + "lookup resources 2 with nil context", + func() DispatchCacheKey { + return lookupResourcesRequest2ToKey(&v1.DispatchLookupResources2Request{ + ResourceRelation: RR("document", "view"), + SubjectRelation: RR("user", "..."), + SubjectIds: []string{"mariah"}, + TerminalSubject: ONR("user", "mariah", "..."), + Metadata: &v1.ResolverMeta{ + AtRevision: "1234", + }, + Context: nil, + }, computeBothHashes) + }, + "f49fbafef9c489abe601", + }, + { + "lookup resources 2 with empty context", + func() DispatchCacheKey { + return lookupResourcesRequest2ToKey(&v1.DispatchLookupResources2Request{ + ResourceRelation: RR("document", "view"), + SubjectRelation: RR("user", "..."), + SubjectIds: []string{"mariah"}, + TerminalSubject: ONR("user", "mariah", "..."), + Metadata: &v1.ResolverMeta{ + AtRevision: "1234", + }, + Context: func() *structpb.Struct { + v, _ := structpb.NewStruct(map[string]any{}) + return v + }(), + }, computeBothHashes) + }, + "f49fbafef9c489abe601", + }, + { + "lookup resources 2 with context", + func() DispatchCacheKey { + return lookupResourcesRequest2ToKey(&v1.DispatchLookupResources2Request{ + ResourceRelation: RR("document", "view"), + SubjectRelation: RR("user", "..."), + SubjectIds: []string{"mariah"}, + TerminalSubject: ONR("user", "mariah", "..."), + Metadata: &v1.ResolverMeta{ + AtRevision: "1234", + }, + Context: func() *structpb.Struct { + v, _ := structpb.NewStruct(map[string]any{ + "foo": 1, + "bar": true, + }) + return v + }(), + }, computeBothHashes) + }, + "bbd78884eff9edbebd01", + }, + { + "lookup resources 2 with different context", + func() DispatchCacheKey { + return lookupResourcesRequest2ToKey(&v1.DispatchLookupResources2Request{ + ResourceRelation: RR("document", "view"), + SubjectRelation: RR("user", "..."), + SubjectIds: []string{"mariah"}, + TerminalSubject: ONR("user", "mariah", "..."), + Metadata: &v1.ResolverMeta{ + AtRevision: "1234", + }, + Context: func() *structpb.Struct { + v, _ := structpb.NewStruct(map[string]any{ + "foo": 2, + "bar": true, + }) + return v + }(), + }, computeBothHashes) + }, + "94d0faa5feaaa4dc17", + }, + { + "lookup resources 2 with escaped string", + func() DispatchCacheKey { + return lookupResourcesRequest2ToKey(&v1.DispatchLookupResources2Request{ + ResourceRelation: RR("document", "view"), + SubjectRelation: RR("user", "..."), + SubjectIds: []string{"mariah"}, + TerminalSubject: ONR("user", "mariah", "..."), + Metadata: &v1.ResolverMeta{ + AtRevision: "1234", + }, + Context: func() *structpb.Struct { + v, _ := structpb.NewStruct(map[string]any{ + "foo": "this is an `escaped` string\nhi", + }) + return v + }(), + }, computeBothHashes) + }, + "cdffc895cb9299b9da01", + }, + { + "lookup resources 2 with nested context", + func() DispatchCacheKey { + return lookupResourcesRequest2ToKey(&v1.DispatchLookupResources2Request{ + ResourceRelation: RR("document", "view"), + SubjectRelation: RR("user", "..."), + SubjectIds: []string{"mariah"}, + TerminalSubject: ONR("user", "mariah", "..."), + Metadata: &v1.ResolverMeta{ + AtRevision: "1234", + }, + Context: func() *structpb.Struct { + v, _ := structpb.NewStruct(map[string]any{ + "foo": 1, + "bar": map[string]any{ + "meh": "hiya", + "baz": "yo", + }, + }) + return v + }(), + }, computeBothHashes) + }, + "84d8d38ff9fadbb36d", + }, + { + "lookup resources 2 with empty cursor", + func() DispatchCacheKey { + return lookupResourcesRequest2ToKey(&v1.DispatchLookupResources2Request{ + ResourceRelation: RR("document", "view"), + SubjectRelation: RR("user", "..."), + SubjectIds: []string{"mariah"}, + TerminalSubject: ONR("user", "mariah", "..."), + Metadata: &v1.ResolverMeta{ + AtRevision: "1234", + }, + OptionalCursor: &v1.Cursor{}, + }, computeBothHashes) + }, + "f49fbafef9c489abe601", + }, + { + "lookup resources 2 with non-empty cursor", + func() DispatchCacheKey { + return lookupResourcesRequest2ToKey(&v1.DispatchLookupResources2Request{ + ResourceRelation: RR("document", "view"), + SubjectRelation: RR("user", "..."), + SubjectIds: []string{"mariah"}, + TerminalSubject: ONR("user", "mariah", "..."), + Metadata: &v1.ResolverMeta{ + AtRevision: "1234", + }, + OptionalCursor: &v1.Cursor{ + Sections: []string{"foo"}, + }, + }, computeBothHashes) + }, + "f09894c0d9a0b8ff7f", + }, + { + "lookup resources 2 with different cursor", + func() DispatchCacheKey { + return lookupResourcesRequest2ToKey(&v1.DispatchLookupResources2Request{ + ResourceRelation: RR("document", "view"), + SubjectRelation: RR("user", "..."), + SubjectIds: []string{"mariah"}, + TerminalSubject: ONR("user", "mariah", "..."), + Metadata: &v1.ResolverMeta{ + AtRevision: "1234", + }, + OptionalCursor: &v1.Cursor{ + Sections: []string{"foo", "bar"}, + }, + }, computeBothHashes) + }, + "badfd7c59cdbcef671", + }, + { + "lookup resources 2 with different terminal subject", + func() DispatchCacheKey { + return lookupResourcesRequest2ToKey(&v1.DispatchLookupResources2Request{ + ResourceRelation: RR("document", "view"), + SubjectRelation: RR("user", "..."), + SubjectIds: []string{"mariah"}, + TerminalSubject: ONR("user", "sarah", "..."), + Metadata: &v1.ResolverMeta{ + AtRevision: "1234", + }, + OptionalCursor: &v1.Cursor{ + Sections: []string{"foo", "bar"}, + }, + }, computeBothHashes) + }, + "8787b685e9cea993a901", + }, + { + "lookup resources 2 with different subject IDs", + func() DispatchCacheKey { + return lookupResourcesRequest2ToKey(&v1.DispatchLookupResources2Request{ + ResourceRelation: RR("document", "view"), + SubjectRelation: RR("user", "..."), + SubjectIds: []string{"mariah", "tom"}, + TerminalSubject: ONR("user", "sarah", "..."), + Metadata: &v1.ResolverMeta{ + AtRevision: "1234", + }, + OptionalCursor: &v1.Cursor{ + Sections: []string{"foo", "bar"}, + }, + }, computeBothHashes) + }, + "f4d6d884eaa5e4b4ed01", + }, } for _, tc := range tcs { diff --git a/internal/dispatch/keys/keys.go b/internal/dispatch/keys/keys.go index fa384eb626..7c45846c92 100644 --- a/internal/dispatch/keys/keys.go +++ b/internal/dispatch/keys/keys.go @@ -16,6 +16,9 @@ type Handler interface { // LookupResourcesCacheKey computes the caching key for a LookupResources operation. LookupResourcesCacheKey(ctx context.Context, req *v1.DispatchLookupResourcesRequest) (DispatchCacheKey, error) + // LookupResources2CacheKey computes the caching key for a LookupResources2 operation. + LookupResources2CacheKey(ctx context.Context, req *v1.DispatchLookupResources2Request) (DispatchCacheKey, error) + // LookupSubjectsCacheKey computes the caching key for a LookupSubjects operation. LookupSubjectsCacheKey(ctx context.Context, req *v1.DispatchLookupSubjectsRequest) (DispatchCacheKey, error) @@ -31,6 +34,9 @@ type Handler interface { // LookupResourcesDispatchKey computes the dispatch key for a LookupResources operation. LookupResourcesDispatchKey(ctx context.Context, req *v1.DispatchLookupResourcesRequest) ([]byte, error) + // LookupResources2DispatchKey computes the dispatch key for a LookupResources2 operation. + LookupResources2DispatchKey(ctx context.Context, req *v1.DispatchLookupResources2Request) ([]byte, error) + // LookupSubjectsDispatchKey computes the key for a LookupSubjects operation. LookupSubjectsDispatchKey(ctx context.Context, req *v1.DispatchLookupSubjectsRequest) ([]byte, error) @@ -47,6 +53,10 @@ func (b baseKeyHandler) LookupResourcesCacheKey(_ context.Context, req *v1.Dispa return lookupResourcesRequestToKey(req, computeBothHashes), nil } +func (b baseKeyHandler) LookupResources2CacheKey(_ context.Context, req *v1.DispatchLookupResources2Request) (DispatchCacheKey, error) { + return lookupResourcesRequest2ToKey(req, computeBothHashes), nil +} + func (b baseKeyHandler) LookupSubjectsCacheKey(_ context.Context, req *v1.DispatchLookupSubjectsRequest) (DispatchCacheKey, error) { return lookupSubjectsRequestToKey(req, computeBothHashes), nil } @@ -67,6 +77,10 @@ func (b baseKeyHandler) LookupResourcesDispatchKey(_ context.Context, req *v1.Di return lookupResourcesRequestToKey(req, computeOnlyStableHash).StableSumAsBytes(), nil } +func (b baseKeyHandler) LookupResources2DispatchKey(_ context.Context, req *v1.DispatchLookupResources2Request) ([]byte, error) { + return lookupResourcesRequest2ToKey(req, computeOnlyStableHash).StableSumAsBytes(), nil +} + func (b baseKeyHandler) LookupSubjectsDispatchKey(_ context.Context, req *v1.DispatchLookupSubjectsRequest) ([]byte, error) { return lookupSubjectsRequestToKey(req, computeOnlyStableHash).StableSumAsBytes(), nil } diff --git a/internal/dispatch/remote/cluster.go b/internal/dispatch/remote/cluster.go index 5cee5f57c7..52305a1a11 100644 --- a/internal/dispatch/remote/cluster.go +++ b/internal/dispatch/remote/cluster.go @@ -38,6 +38,7 @@ type ClusterClient interface { DispatchExpand(ctx context.Context, req *v1.DispatchExpandRequest, opts ...grpc.CallOption) (*v1.DispatchExpandResponse, error) DispatchReachableResources(ctx context.Context, in *v1.DispatchReachableResourcesRequest, opts ...grpc.CallOption) (v1.DispatchService_DispatchReachableResourcesClient, error) DispatchLookupResources(ctx context.Context, in *v1.DispatchLookupResourcesRequest, opts ...grpc.CallOption) (v1.DispatchService_DispatchLookupResourcesClient, error) + DispatchLookupResources2(ctx context.Context, in *v1.DispatchLookupResources2Request, opts ...grpc.CallOption) (v1.DispatchService_DispatchLookupResources2Client, error) DispatchLookupSubjects(ctx context.Context, in *v1.DispatchLookupSubjectsRequest, opts ...grpc.CallOption) (v1.DispatchService_DispatchLookupSubjectsClient, error) } @@ -353,6 +354,56 @@ func (cr *clusterDispatcher) DispatchLookupResources( } } +func (cr *clusterDispatcher) DispatchLookupResources2( + req *v1.DispatchLookupResources2Request, + stream dispatch.LookupResources2Stream, +) error { + requestKey, err := cr.keyHandler.LookupResources2DispatchKey(stream.Context(), req) + if err != nil { + return err + } + + ctx := context.WithValue(stream.Context(), consistent.CtxKey, requestKey) + stream = dispatch.StreamWithContext(ctx, stream) + + if err := dispatch.CheckDepth(ctx, req); err != nil { + return err + } + + withTimeout, cancelFn := context.WithTimeout(ctx, cr.dispatchOverallTimeout) + defer cancelFn() + + client, err := cr.clusterClient.DispatchLookupResources2(withTimeout, req) + if err != nil { + return err + } + + for { + select { + case <-withTimeout.Done(): + return withTimeout.Err() + + default: + result, err := client.Recv() + if errors.Is(err, io.EOF) { + return nil + } else if err != nil { + return err + } + + merr := adjustMetadataForDispatch(result.Metadata) + if merr != nil { + return merr + } + + serr := stream.Publish(result) + if serr != nil { + return serr + } + } + } +} + func (cr *clusterDispatcher) DispatchLookupSubjects( req *v1.DispatchLookupSubjectsRequest, stream dispatch.LookupSubjectsStream, diff --git a/internal/dispatch/singleflight/singleflight.go b/internal/dispatch/singleflight/singleflight.go index 39fc8ea5c8..ad98dd31c7 100644 --- a/internal/dispatch/singleflight/singleflight.go +++ b/internal/dispatch/singleflight/singleflight.go @@ -143,6 +143,10 @@ func (d *Dispatcher) DispatchLookupResources(req *v1.DispatchLookupResourcesRequ return d.delegate.DispatchLookupResources(req, stream) } +func (d *Dispatcher) DispatchLookupResources2(req *v1.DispatchLookupResources2Request, stream dispatch.LookupResources2Stream) error { + return d.delegate.DispatchLookupResources2(req, stream) +} + func (d *Dispatcher) DispatchLookupSubjects(req *v1.DispatchLookupSubjectsRequest, stream dispatch.LookupSubjectsStream) error { return d.delegate.DispatchLookupSubjects(req, stream) } diff --git a/internal/dispatch/singleflight/singleflight_test.go b/internal/dispatch/singleflight/singleflight_test.go index 55f1e4095d..6612b42806 100644 --- a/internal/dispatch/singleflight/singleflight_test.go +++ b/internal/dispatch/singleflight/singleflight_test.go @@ -373,6 +373,10 @@ func (m mockDispatcher) DispatchLookupResources(_ *v1.DispatchLookupResourcesReq return nil } +func (m mockDispatcher) DispatchLookupResources2(_ *v1.DispatchLookupResources2Request, _ dispatch.LookupResources2Stream) error { + return nil +} + func (m mockDispatcher) DispatchLookupSubjects(_ *v1.DispatchLookupSubjectsRequest, _ dispatch.LookupSubjectsStream) error { return nil } diff --git a/internal/graph/check.go b/internal/graph/check.go index 7432512a8f..4887043632 100644 --- a/internal/graph/check.go +++ b/internal/graph/check.go @@ -26,6 +26,7 @@ import ( iv1 "github.com/authzed/spicedb/pkg/proto/impl/v1" "github.com/authzed/spicedb/pkg/spiceerrors" "github.com/authzed/spicedb/pkg/tuple" + "github.com/authzed/spicedb/pkg/typesystem" ) var tracer = otel.Tracer("spicedb/internal/graph/check") @@ -173,8 +174,29 @@ func (cc *ConcurrentChecker) checkInternal(ctx context.Context, req ValidatedChe return checkResultsForMembership(membershipSet, emptyMetadata) } + // Filter for check hints, if any. + if len(req.CheckHints) > 0 { + filteredResourcesIdsSet := mapz.NewSet(filteredResourcesIds...) + for _, resourceID := range filteredResourcesIds { + checkHintKey := typesystem.CheckHint( + typesystem.ResourceCheckHintForRelation( + req.ResourceRelation.Namespace, + resourceID, + req.ResourceRelation.Relation, + ), + req.Subject, + ) + + if _, ok := req.CheckHints[checkHintKey]; ok { + filteredResourcesIdsSet.Delete(resourceID) + } + } + + filteredResourcesIds = filteredResourcesIdsSet.AsSlice() + } + if len(filteredResourcesIds) == 0 { - return noMembers() + return combineWithCheckHints(combineResultWithFoundResources(noMembers(), membershipSet), req) } // NOTE: We can always allow a single result if we're only trying to find the results for a @@ -196,10 +218,66 @@ func (cc *ConcurrentChecker) checkInternal(ctx context.Context, req ValidatedChe } if relation.UsersetRewrite == nil { - return combineResultWithFoundResources(cc.checkDirect(ctx, crc, relation), membershipSet) + return combineWithCheckHints(combineResultWithFoundResources(cc.checkDirect(ctx, crc, relation), membershipSet), req) + } + + return combineWithCheckHints(combineResultWithFoundResources(cc.checkUsersetRewrite(ctx, crc, relation.UsersetRewrite), membershipSet), req) +} + +func combineWithComputedHints(result CheckResult, hints map[string]*v1.ResourceCheckResult) CheckResult { + if len(hints) == 0 { + return result + } + + for resourceID, hint := range hints { + if _, ok := result.Resp.ResultsByResourceId[resourceID]; ok { + return checkResultError( + spiceerrors.MustBugf("check hint for resource ID %q, which already exists", resourceID), + emptyMetadata, + ) + } + + if result.Resp.ResultsByResourceId == nil { + result.Resp.ResultsByResourceId = make(map[string]*v1.ResourceCheckResult) + } + result.Resp.ResultsByResourceId[resourceID] = hint + } + + return result +} + +func combineWithCheckHints(result CheckResult, req ValidatedCheckRequest) CheckResult { + if len(req.CheckHints) == 0 { + return result } - return combineResultWithFoundResources(cc.checkUsersetRewrite(ctx, crc, relation.UsersetRewrite), membershipSet) + for checkHintKey, checkHintValue := range req.CheckHints { + parsed, err := typesystem.ParseCheckHint(checkHintKey) + if err != nil { + return checkResultError(err, emptyMetadata) + } + + if parsed.Type == typesystem.CheckHintTypeRelation { + if parsed.Resource.Namespace == req.ResourceRelation.Namespace && + parsed.Resource.Relation == req.ResourceRelation.Relation && + parsed.Subject.EqualVT(req.Subject) { + if result.Resp.ResultsByResourceId == nil { + result.Resp.ResultsByResourceId = make(map[string]*v1.ResourceCheckResult) + } + + if _, ok := result.Resp.ResultsByResourceId[parsed.Resource.ObjectId]; ok { + return checkResultError( + spiceerrors.MustBugf("check hint for resource ID %q, which already exists", parsed.Resource.ObjectId), + emptyMetadata, + ) + } + + result.Resp.ResultsByResourceId[parsed.Resource.ObjectId] = checkHintValue + } + } + } + + return result } type directDispatch struct { @@ -408,8 +486,9 @@ func (cc *ConcurrentChecker) checkDirect(ctx context.Context, crc currentRequest Subject: crc.parentReq.Subject, ResultsSetting: crc.resultsSetting, - Metadata: decrementDepth(crc.parentReq.Metadata), - Debug: crc.parentReq.Debug, + Metadata: decrementDepth(crc.parentReq.Metadata), + Debug: crc.parentReq.Debug, + CheckHints: crc.parentReq.CheckHints, }, crc.parentReq.Revision, }) @@ -560,6 +639,7 @@ func (cc *ConcurrentChecker) checkComputedUserset(ctx context.Context, crc curre ResultsSetting: crc.resultsSetting, Metadata: decrementDepth(crc.parentReq.Metadata), Debug: crc.parentReq.Debug, + CheckHints: crc.parentReq.CheckHints, }, crc.parentReq.Revision, }) @@ -609,6 +689,7 @@ func checkIntersectionTupleToUserset( crc currentRequestContext, ttu *core.FunctionedTupleToUserset, ) CheckResult { + // TODO(jschorr): use check hints here ctx, span := tracer.Start(ctx, ttu.GetTupleset().GetRelation()+"-(all)->"+ttu.GetComputedUserset().Relation) defer span.End() @@ -767,6 +848,34 @@ func checkTupleToUserset[T relation]( crc currentRequestContext, ttu ttu[T], ) CheckResult { + filteredResourceIDs := crc.filteredResourceIDs + hintsToReturn := make(map[string]*v1.ResourceCheckResult, len(crc.parentReq.CheckHints)) + if len(crc.parentReq.CheckHints) > 0 { + filteredResourceIDs = make([]string, 0, len(crc.filteredResourceIDs)) + + for _, resourceID := range crc.filteredResourceIDs { + checkHintKey := typesystem.CheckHint( + typesystem.ResourceCheckHintForArrow( + crc.parentReq.ResourceRelation.Namespace, + resourceID, + ttu.GetTupleset().GetRelation(), + ttu.GetComputedUserset().Relation, + ), + crc.parentReq.Subject, + ) + + if hint, ok := crc.parentReq.CheckHints[checkHintKey]; ok { + hintsToReturn[resourceID] = hint + } else { + filteredResourceIDs = append(filteredResourceIDs, resourceID) + } + } + } + + if len(filteredResourceIDs) == 0 { + return combineWithComputedHints(noMembers(), hintsToReturn) + } + ctx, span := tracer.Start(ctx, ttu.GetTupleset().GetRelation()+"->"+ttu.GetComputedUserset().Relation) defer span.End() @@ -774,7 +883,7 @@ func checkTupleToUserset[T relation]( ds := datastoremw.MustFromContext(ctx).SnapshotReader(crc.parentReq.Revision) it, err := ds.QueryRelationships(ctx, datastore.RelationshipsFilter{ OptionalResourceType: crc.parentReq.ResourceRelation.Namespace, - OptionalResourceIds: crc.filteredResourceIDs, + OptionalResourceIds: filteredResourceIDs, OptionalResourceRelation: ttu.GetTupleset().GetRelation(), }) if err != nil { @@ -811,7 +920,7 @@ func checkTupleToUserset[T relation]( dispatchChunkCountHistogram.Observe(chunkCount) }) - return union( + return combineWithComputedHints(union( ctx, crc, toDispatch, @@ -824,7 +933,7 @@ func checkTupleToUserset[T relation]( return mapFoundResources(childResult, dd.resourceType, relationshipsBySubjectONR) }, cc.concurrencyLimit, - ) + ), hintsToReturn) } func withDistinctMetadata(result CheckResult) CheckResult { diff --git a/internal/graph/computed/computecheck.go b/internal/graph/computed/computecheck.go index 2f6c9cbebd..38975e8735 100644 --- a/internal/graph/computed/computecheck.go +++ b/internal/graph/computed/computecheck.go @@ -46,6 +46,7 @@ type CheckParameters struct { AtRevision datastore.Revision MaximumDepth uint32 DebugOption DebugOption + CheckHints map[string]*v1.ResourceCheckResult } // ComputeCheck computes a check result for the given resource and subject, computing any @@ -118,7 +119,8 @@ func computeCheck(ctx context.Context, DepthRemaining: params.MaximumDepth, TraversalBloom: bf, }, - Debug: debugging, + Debug: debugging, + CheckHints: params.CheckHints, }) if len(resourceIDs) == 1 { diff --git a/internal/graph/computed/computecheck_test.go b/internal/graph/computed/computecheck_test.go index 36623db76b..557cb96218 100644 --- a/internal/graph/computed/computecheck_test.go +++ b/internal/graph/computed/computecheck_test.go @@ -87,7 +87,7 @@ func TestComputeCheckWithCaveats(t *testing.T) { "document:foo#view@user:sarah", nil, v1.ResourceCheckResult_CAVEATED_MEMBER, - []string{"anothercondition"}, + []string{"anothercondition", "somecondition", "somebool"}, "", }, { @@ -96,7 +96,7 @@ func TestComputeCheckWithCaveats(t *testing.T) { "somecondition": "42", }, v1.ResourceCheckResult_CAVEATED_MEMBER, - []string{"anothercondition"}, + []string{"anothercondition", "somebool"}, "", }, { diff --git a/internal/graph/lookupresources2.go b/internal/graph/lookupresources2.go new file mode 100644 index 0000000000..7d8e796478 --- /dev/null +++ b/internal/graph/lookupresources2.go @@ -0,0 +1,783 @@ +package graph + +import ( + "context" + "slices" + "sort" + + "github.com/authzed/spicedb/internal/caveats" + "github.com/authzed/spicedb/internal/dispatch" + "github.com/authzed/spicedb/internal/graph/computed" + datastoremw "github.com/authzed/spicedb/internal/middleware/datastore" + "github.com/authzed/spicedb/pkg/datastore" + "github.com/authzed/spicedb/pkg/datastore/options" + "github.com/authzed/spicedb/pkg/genutil/mapz" + core "github.com/authzed/spicedb/pkg/proto/core/v1" + v1 "github.com/authzed/spicedb/pkg/proto/dispatch/v1" + "github.com/authzed/spicedb/pkg/spiceerrors" + "github.com/authzed/spicedb/pkg/tuple" + "github.com/authzed/spicedb/pkg/typesystem" +) + +func NewCursoredLookupResources2(dl dispatch.LookupResources2, dc dispatch.Check, concurrencyLimit uint16) *CursoredLookupResources2 { + return &CursoredLookupResources2{dl, dc, concurrencyLimit} +} + +type CursoredLookupResources2 struct { + dl dispatch.LookupResources2 + dc dispatch.Check + concurrencyLimit uint16 +} + +type ValidatedLookupResources2Request struct { + *v1.DispatchLookupResources2Request + Revision datastore.Revision +} + +func (crr *CursoredLookupResources2) LookupResources2( + req ValidatedLookupResources2Request, + stream dispatch.LookupResources2Stream, +) error { + if req.TerminalSubject == nil { + return spiceerrors.MustBugf("no terminal subject given to lookup resources dispatch") + } + + if slices.Contains(req.SubjectIds, tuple.PublicWildcard) { + return NewWildcardNotAllowedErr("cannot perform lookup resources on wildcard", "subject_id") + } + + if len(req.SubjectIds) == 0 { + return spiceerrors.MustBugf("no subjects ids given to lookup resources dispatch") + } + + // Sort for stability. + sort.Strings(req.SubjectIds) + + ctx := stream.Context() + limits := newLimitTracker(req.OptionalLimit) + ci, err := newCursorInformation(req.OptionalCursor, limits, dispatchVersion) + if err != nil { + return err + } + + return withSubsetInCursor(ci, + func(currentOffset int, nextCursorWith afterResponseCursor) error { + // If the resource type matches the subject type, yield directly as a one-to-one result + // for each subjectID. + if req.SubjectRelation.Namespace == req.ResourceRelation.Namespace && + req.SubjectRelation.Relation == req.ResourceRelation.Relation { + for index, subjectID := range req.SubjectIds { + if index < currentOffset { + continue + } + + if !ci.limits.prepareForPublishing() { + return nil + } + + err := stream.Publish(&v1.DispatchLookupResources2Response{ + Resource: &v1.PossibleResource{ + ResourceId: subjectID, + ForSubjectIds: []string{subjectID}, + }, + Metadata: emptyMetadata, + AfterResponseCursor: nextCursorWith(index + 1), + }) + if err != nil { + return err + } + } + } + return nil + }, func(ci cursorInformation) error { + // Once done checking for the matching subject type, yield by dispatching over entrypoints. + return crr.afterSameType(ctx, ci, req, stream) + }) +} + +func (crr *CursoredLookupResources2) afterSameType( + ctx context.Context, + ci cursorInformation, + req ValidatedLookupResources2Request, + parentStream dispatch.LookupResources2Stream, +) error { + dispatched := &syncONRSet{} + + // Load the type system and reachability graph to find the entrypoints for the reachability. + ds := datastoremw.MustFromContext(ctx) + reader := ds.SnapshotReader(req.Revision) + _, typeSystem, err := typesystem.ReadNamespaceAndTypes(ctx, req.ResourceRelation.Namespace, reader) + if err != nil { + return err + } + + rg := typesystem.ReachabilityGraphFor(typeSystem) + entrypoints, err := rg.OptimizedEntrypointsForSubjectToResource(ctx, &core.RelationReference{ + Namespace: req.SubjectRelation.Namespace, + Relation: req.SubjectRelation.Relation, + }, req.ResourceRelation) + if err != nil { + return err + } + + // For each entrypoint, load the necessary data and re-dispatch if a subproblem was found. + return withParallelizedStreamingIterableInCursor(ctx, ci, entrypoints, parentStream, crr.concurrencyLimit, + func(ctx context.Context, ci cursorInformation, entrypoint typesystem.ReachabilityEntrypoint, stream dispatch.LookupResources2Stream) error { + switch entrypoint.EntrypointKind() { + case core.ReachabilityEntrypoint_RELATION_ENTRYPOINT: + return crr.lookupRelationEntrypoint(ctx, ci, entrypoint, rg, reader, req, stream, dispatched) + + case core.ReachabilityEntrypoint_COMPUTED_USERSET_ENTRYPOINT: + containingRelation := entrypoint.ContainingRelationOrPermission() + rewrittenSubjectRelation := &core.RelationReference{ + Namespace: containingRelation.Namespace, + Relation: containingRelation.Relation, + } + + rsm := subjectIDsToResourcesMap2(rewrittenSubjectRelation, req.SubjectIds) + drsm := rsm.asReadOnly() + + return crr.redispatchOrReport( + ctx, + ci, + rewrittenSubjectRelation, + drsm, + rg, + entrypoint, + stream, + req, + dispatched, + ) + + case core.ReachabilityEntrypoint_TUPLESET_TO_USERSET_ENTRYPOINT: + return crr.lookupTTUEntrypoint(ctx, ci, entrypoint, rg, reader, req, stream, dispatched) + + default: + return spiceerrors.MustBugf("Unknown kind of entrypoint: %v", entrypoint.EntrypointKind()) + } + }) +} + +func (crr *CursoredLookupResources2) lookupRelationEntrypoint( + ctx context.Context, + ci cursorInformation, + entrypoint typesystem.ReachabilityEntrypoint, + rg *typesystem.ReachabilityGraph, + reader datastore.Reader, + req ValidatedLookupResources2Request, + stream dispatch.LookupResources2Stream, + dispatched *syncONRSet, +) error { + relationReference, err := entrypoint.DirectRelation() + if err != nil { + return err + } + + _, relTypeSystem, err := typesystem.ReadNamespaceAndTypes(ctx, relationReference.Namespace, reader) + if err != nil { + return err + } + + // Build the list of subjects to lookup based on the type information available. + isDirectAllowed, err := relTypeSystem.IsAllowedDirectRelation( + relationReference.Relation, + req.SubjectRelation.Namespace, + req.SubjectRelation.Relation, + ) + if err != nil { + return err + } + + subjectIds := make([]string, 0, len(req.SubjectIds)+1) + if isDirectAllowed == typesystem.DirectRelationValid { + subjectIds = append(subjectIds, req.SubjectIds...) + } + + if req.SubjectRelation.Relation == tuple.Ellipsis { + isWildcardAllowed, err := relTypeSystem.IsAllowedPublicNamespace(relationReference.Relation, req.SubjectRelation.Namespace) + if err != nil { + return err + } + + if isWildcardAllowed == typesystem.PublicSubjectAllowed { + subjectIds = append(subjectIds, "*") + } + } + + // Lookup the subjects and then redispatch/report results. + relationFilter := datastore.SubjectRelationFilter{ + NonEllipsisRelation: req.SubjectRelation.Relation, + } + + if req.SubjectRelation.Relation == tuple.Ellipsis { + relationFilter = datastore.SubjectRelationFilter{ + IncludeEllipsisRelation: true, + } + } + + subjectsFilter := datastore.SubjectsFilter{ + SubjectType: req.SubjectRelation.Namespace, + OptionalSubjectIds: subjectIds, + RelationFilter: relationFilter, + } + + return crr.redispatchOrReportOverDatabaseQuery( + ctx, + redispatchOverDatabaseConfig2{ + ci: ci, + reader: reader, + subjectsFilter: subjectsFilter, + sourceResourceType: relationReference, + foundResourceType: relationReference, + entrypoint: entrypoint, + rg: rg, + concurrencyLimit: crr.concurrencyLimit, + parentStream: stream, + parentRequest: req, + dispatched: dispatched, + }, + ) +} + +type redispatchOverDatabaseConfig2 struct { + ci cursorInformation + + reader datastore.Reader + + subjectsFilter datastore.SubjectsFilter + sourceResourceType *core.RelationReference + foundResourceType *core.RelationReference + + entrypoint typesystem.ReachabilityEntrypoint + rg *typesystem.ReachabilityGraph + + concurrencyLimit uint16 + parentStream dispatch.LookupResources2Stream + parentRequest ValidatedLookupResources2Request + dispatched *syncONRSet +} + +func (crr *CursoredLookupResources2) redispatchOrReportOverDatabaseQuery( + ctx context.Context, + config redispatchOverDatabaseConfig2, +) error { + return withDatastoreCursorInCursor(ctx, config.ci, config.parentStream, config.concurrencyLimit, + // Find the target resources for the subject. + func(queryCursor options.Cursor) ([]itemAndPostCursor[dispatchableResourcesSubjectMap2], error) { + it, err := config.reader.ReverseQueryRelationships( + ctx, + config.subjectsFilter, + options.WithResRelation(&options.ResourceRelation{ + Namespace: config.sourceResourceType.Namespace, + Relation: config.sourceResourceType.Relation, + }), + options.WithSortForReverse(options.BySubject), + options.WithAfterForReverse(queryCursor), + ) + if err != nil { + return nil, err + } + defer it.Close() + + // Chunk based on the FilterMaximumIDCount, to ensure we never send more than that amount of + // results to a downstream dispatch. + rsm := newResourcesSubjectMap2WithCapacity(config.sourceResourceType, uint32(datastore.FilterMaximumIDCount)) + toBeHandled := make([]itemAndPostCursor[dispatchableResourcesSubjectMap2], 0) + currentCursor := queryCursor + + for tpl := it.Next(); tpl != nil; tpl = it.Next() { + if it.Err() != nil { + return nil, it.Err() + } + + var missingContextParameters []string + + // If a caveat exists on the relationship, run it and filter the results, marking those that have missing context. + if tpl.Caveat != nil && tpl.Caveat.CaveatName != "" { + caveatExpr := caveats.CaveatAsExpr(tpl.Caveat) + runResult, err := caveats.RunCaveatExpression(ctx, caveatExpr, config.parentRequest.Context.AsMap(), config.reader, caveats.RunCaveatExpressionNoDebugging) + if err != nil { + return nil, err + } + + // If a partial result is returned, collect the missing context parameters. + if runResult.IsPartial() { + missingNames, err := runResult.MissingVarNames() + if err != nil { + return nil, err + } + + missingContextParameters = missingNames + } else if !runResult.Value() { + // If the run result shows the caveat does not apply, skip. This shears the tree of results early. + continue + } + } + + if err := rsm.addRelationship(tpl, missingContextParameters); err != nil { + return nil, err + } + + if rsm.len() == int(datastore.FilterMaximumIDCount) { + toBeHandled = append(toBeHandled, itemAndPostCursor[dispatchableResourcesSubjectMap2]{ + item: rsm.asReadOnly(), + cursor: currentCursor, + }) + rsm = newResourcesSubjectMap2WithCapacity(config.sourceResourceType, uint32(datastore.FilterMaximumIDCount)) + currentCursor = tpl + } + } + it.Close() + + if rsm.len() > 0 { + toBeHandled = append(toBeHandled, itemAndPostCursor[dispatchableResourcesSubjectMap2]{ + item: rsm.asReadOnly(), + cursor: currentCursor, + }) + } + + return toBeHandled, nil + }, + + // Redispatch or report the results. + func( + ctx context.Context, + ci cursorInformation, + drsm dispatchableResourcesSubjectMap2, + currentStream dispatch.LookupResources2Stream, + ) error { + return crr.redispatchOrReport( + ctx, + ci, + config.foundResourceType, + drsm, + config.rg, + config.entrypoint, + currentStream, + config.parentRequest, + config.dispatched, + ) + }, + ) +} + +func (crr *CursoredLookupResources2) lookupTTUEntrypoint(ctx context.Context, + ci cursorInformation, + entrypoint typesystem.ReachabilityEntrypoint, + rg *typesystem.ReachabilityGraph, + reader datastore.Reader, + req ValidatedLookupResources2Request, + stream dispatch.LookupResources2Stream, + dispatched *syncONRSet, +) error { + containingRelation := entrypoint.ContainingRelationOrPermission() + + _, ttuTypeSystem, err := typesystem.ReadNamespaceAndTypes(ctx, containingRelation.Namespace, reader) + if err != nil { + return err + } + + tuplesetRelation, err := entrypoint.TuplesetRelation() + if err != nil { + return err + } + + // Determine whether this TTU should be followed, which will be the case if the subject relation's namespace + // is allowed in any form on the relation; since arrows ignore the subject's relation (if any), we check + // for the subject namespace as a whole. + allowedRelations, err := ttuTypeSystem.GetAllowedDirectNamespaceSubjectRelations(tuplesetRelation, req.SubjectRelation.Namespace) + if err != nil { + return err + } + + if allowedRelations == nil { + return nil + } + + // Search for the resolved subjects in the tupleset of the TTU. + subjectsFilter := datastore.SubjectsFilter{ + SubjectType: req.SubjectRelation.Namespace, + OptionalSubjectIds: req.SubjectIds, + } + + // Optimization: if there is a single allowed relation, pass it as a subject relation filter to make things faster + // on querying. + if allowedRelations.Len() == 1 { + allowedRelationName := allowedRelations.AsSlice()[0] + subjectsFilter.RelationFilter = datastore.SubjectRelationFilter{}.WithRelation(allowedRelationName) + } + + tuplesetRelationReference := &core.RelationReference{ + Namespace: containingRelation.Namespace, + Relation: tuplesetRelation, + } + + return crr.redispatchOrReportOverDatabaseQuery( + ctx, + redispatchOverDatabaseConfig2{ + ci: ci, + reader: reader, + subjectsFilter: subjectsFilter, + sourceResourceType: tuplesetRelationReference, + foundResourceType: containingRelation, + entrypoint: entrypoint, + rg: rg, + parentStream: stream, + parentRequest: req, + dispatched: dispatched, + }, + ) +} + +func hintString(resourceID string, entrypoint typesystem.ReachabilityEntrypoint, terminalSubject *core.ObjectAndRelation) (string, error) { + resourceKey, err := entrypoint.CheckHintForResource(resourceID) + if err != nil { + return "", err + } + + return typesystem.CheckHint(resourceKey, terminalSubject), nil +} + +// redispatchOrReport checks if further redispatching is necessary for the found resource +// type. If not, and the found resource type+relation matches the target resource type+relation, +// the resource is reported to the parent stream. +func (crr *CursoredLookupResources2) redispatchOrReport( + ctx context.Context, + ci cursorInformation, + foundResourceType *core.RelationReference, + foundResources dispatchableResourcesSubjectMap2, + rg *typesystem.ReachabilityGraph, + entrypoint typesystem.ReachabilityEntrypoint, + parentStream dispatch.LookupResources2Stream, + parentRequest ValidatedLookupResources2Request, + dispatched *syncONRSet, +) error { + if foundResources.isEmpty() { + // Nothing more to do. + return nil + } + + // Check for entrypoints for the new found resource type. + hasResourceEntrypoints, err := rg.HasOptimizedEntrypointsForSubjectToResource(ctx, foundResourceType, parentRequest.ResourceRelation) + if err != nil { + return err + } + + return withSubsetInCursor(ci, + func(currentOffset int, nextCursorWith afterResponseCursor) error { + if !hasResourceEntrypoints { + // If the found resource matches the target resource type and relation, potentially yield the resource. + if foundResourceType.Namespace == parentRequest.ResourceRelation.Namespace && foundResourceType.Relation == parentRequest.ResourceRelation.Relation { + resources := foundResources.asPossibleResources() + if len(resources) == 0 { + return nil + } + + if currentOffset >= len(resources) { + return nil + } + + offsetted := resources[currentOffset:] + if len(offsetted) == 0 { + return nil + } + + filtered := offsetted + metadata := emptyMetadata + + // If the entrypoint is not a direct result, issue a check to further filter the results on the intersection or exclusion. + if !entrypoint.IsDirectResult() { + resourceIDs := make([]string, 0, len(offsetted)) + checkHints := make(map[string]*v1.ResourceCheckResult, len(offsetted)) + for _, resource := range offsetted { + resourceIDs = append(resourceIDs, resource.ResourceId) + hintKey, err := hintString(resource.ResourceId, entrypoint, parentRequest.TerminalSubject) + if err != nil { + return err + } + + checkHints[hintKey] = &v1.ResourceCheckResult{ + Membership: v1.ResourceCheckResult_MEMBER, + } + } + + resultsByResourceID, checkMetadata, err := computed.ComputeBulkCheck(ctx, crr.dc, computed.CheckParameters{ + ResourceType: parentRequest.ResourceRelation, + Subject: parentRequest.TerminalSubject, + CaveatContext: parentRequest.Context.AsMap(), + AtRevision: parentRequest.Revision, + MaximumDepth: parentRequest.Metadata.DepthRemaining - 1, + DebugOption: computed.NoDebugging, + CheckHints: checkHints, + }, resourceIDs) + if err != nil { + return err + } + + metadata = addCallToResponseMetadata(checkMetadata) + + filtered = make([]*v1.PossibleResource, 0, len(offsetted)) + for _, resource := range offsetted { + result, ok := resultsByResourceID[resource.ResourceId] + if !ok { + continue + } + + switch result.Membership { + case v1.ResourceCheckResult_MEMBER: + filtered = append(filtered, resource) + + case v1.ResourceCheckResult_CAVEATED_MEMBER: + missingContextParams := mapz.NewSet(result.MissingExprFields...) + missingContextParams.Extend(resource.MissingContextParams) + + filtered = append(filtered, &v1.PossibleResource{ + ResourceId: resource.ResourceId, + ForSubjectIds: resource.ForSubjectIds, + MissingContextParams: missingContextParams.AsSlice(), + }) + + case v1.ResourceCheckResult_NOT_MEMBER: + // Skip. + + default: + return spiceerrors.MustBugf("unexpected result from check: %v", result.Membership) + } + } + } + + for index, resource := range filtered { + if !ci.limits.prepareForPublishing() { + return nil + } + + err := parentStream.Publish(&v1.DispatchLookupResources2Response{ + Resource: resource, + Metadata: metadata, + AfterResponseCursor: nextCursorWith(currentOffset + index + 1), + }) + if err != nil { + return err + } + + metadata = emptyMetadata + } + return nil + } + } + return nil + }, func(ci cursorInformation) error { + if !hasResourceEntrypoints { + return nil + } + + // The new subject type for dispatching was the found type of the *resource*. + newSubjectType := foundResourceType + + // To avoid duplicate work, remove any subjects already dispatched. + filteredSubjectIDs := foundResources.filterSubjectIDsToDispatch(dispatched, newSubjectType) + if len(filteredSubjectIDs) == 0 { + return nil + } + + // The stream that collects the results of the dispatch will add metadata to the response, + // map the results found based on the mapping data in the results and, if the entrypoint is not + // direct, issue a check to further filter the results. + stream, completed := lookupResourcesDispatchStreamForEntrypoint(ctx, foundResources, parentStream, entrypoint, ci, parentRequest, crr.dc) + + // Dispatch the found resources as the subjects for the next call, to continue the + // resolution. + err = crr.dl.DispatchLookupResources2(&v1.DispatchLookupResources2Request{ + ResourceRelation: parentRequest.ResourceRelation, + SubjectRelation: newSubjectType, + SubjectIds: filteredSubjectIDs, + TerminalSubject: parentRequest.TerminalSubject, + Metadata: &v1.ResolverMeta{ + AtRevision: parentRequest.Revision.String(), + DepthRemaining: parentRequest.Metadata.DepthRemaining - 1, + }, + OptionalCursor: ci.currentCursor, + OptionalLimit: ci.limits.currentLimit, + }, stream) + if err != nil { + return err + } + + return completed() + }) +} + +func lookupResourcesDispatchStreamForEntrypoint( + ctx context.Context, + foundResources dispatchableResourcesSubjectMap2, + parentStream dispatch.LookupResources2Stream, + entrypoint typesystem.ReachabilityEntrypoint, + ci cursorInformation, + parentRequest ValidatedLookupResources2Request, + dc dispatch.Check, +) (dispatch.LookupResources2Stream, func() error) { + // Branch the context so that the dispatch can be canceled without canceling the parent + // call. + sctx, cancelDispatch := branchContext(ctx) + + needsCallAddedToMetadata := true + resultsToCheck := make([]*v1.DispatchLookupResources2Response, 0, int(datastore.FilterMaximumIDCount)) + + publishResultToParentStream := func( + result *v1.DispatchLookupResources2Response, + additionalMissingContext []string, + additionalMetadata *v1.ResponseMeta, + ) error { + // If we've exhausted the limit of resources to be returned, nothing more to do. + if ci.limits.hasExhaustedLimit() { + cancelDispatch(errCanceledBecauseLimitReached) + return nil + } + + // Map the found resources via the subject+resources used for dispatching, to determine + // if any need to be made conditional due to caveats. + mappedResource, err := foundResources.mapPossibleResource(result.Resource) + if err != nil { + return err + } + + if !ci.limits.prepareForPublishing() { + cancelDispatch(errCanceledBecauseLimitReached) + return nil + } + + // The cursor for the response is that of the parent response + the cursor from the result itself. + afterResponseCursor, err := combineCursors( + ci.responsePartialCursor(), + result.AfterResponseCursor, + ) + if err != nil { + return err + } + + metadata := combineResponseMetadata(result.Metadata, additionalMetadata) + + // Only the first dispatched result gets the call added to it. This is to prevent overcounting + // of the batched dispatch. + if needsCallAddedToMetadata { + metadata = addCallToResponseMetadata(metadata) + needsCallAddedToMetadata = false + } else { + metadata = addAdditionalDepthRequired(metadata) + } + + missingContextParameters := mapz.NewSet[string](mappedResource.MissingContextParams...) + missingContextParameters.Extend(result.Resource.MissingContextParams) + missingContextParameters.Extend(additionalMissingContext) + + mappedResource.MissingContextParams = missingContextParameters.AsSlice() + + resp := &v1.DispatchLookupResources2Response{ + Resource: mappedResource, + Metadata: metadata, + AfterResponseCursor: afterResponseCursor, + } + + return parentStream.Publish(resp) + } + + batchCheckAndPublishIfNecessary := func(result *v1.DispatchLookupResources2Response) error { + // Add the result to the list of results to check. If nil, this is the final call to check+publish. + if result != nil { + resultsToCheck = append(resultsToCheck, result) + } + + // If we have not yet reached the maximum number of results to check and this is not the final + // call, return early. + if len(resultsToCheck) < int(datastore.FilterMaximumIDCount) && result != nil { + return nil + } + + // Ensure there are items left to check. + if len(resultsToCheck) == 0 { + return nil + } + + // Build the set of resource IDs to check and the hints to short circuit the check on the current entrypoint. + checkHints := make(map[string]*v1.ResourceCheckResult, len(resultsToCheck)) + resourceIDsToCheck := make([]string, 0, len(resultsToCheck)) + for _, resource := range resultsToCheck { + hintKey, err := hintString(resource.Resource.ResourceId, entrypoint, parentRequest.TerminalSubject) + if err != nil { + return err + } + + resourceIDsToCheck = append(resourceIDsToCheck, resource.Resource.ResourceId) + + checkHints[hintKey] = &v1.ResourceCheckResult{ + Membership: v1.ResourceCheckResult_MEMBER, + } + } + + // Batch check the results to filter to those visible and then publish just the visible resources. + resultsByResourceID, checkMetadata, err := computed.ComputeBulkCheck(ctx, dc, computed.CheckParameters{ + ResourceType: parentRequest.ResourceRelation, + Subject: parentRequest.TerminalSubject, + CaveatContext: parentRequest.Context.AsMap(), + AtRevision: parentRequest.Revision, + MaximumDepth: parentRequest.Metadata.DepthRemaining - 1, + DebugOption: computed.NoDebugging, + CheckHints: checkHints, + }, resourceIDsToCheck) + if err != nil { + return err + } + + metadata := checkMetadata + for _, resource := range resultsToCheck { + result, ok := resultsByResourceID[resource.Resource.ResourceId] + if !ok { + continue + } + + switch result.Membership { + case v1.ResourceCheckResult_MEMBER: + fallthrough + + case v1.ResourceCheckResult_CAVEATED_MEMBER: + if err := publishResultToParentStream(resource, result.MissingExprFields, metadata); err != nil { + return err + } + metadata = emptyMetadata + + case v1.ResourceCheckResult_NOT_MEMBER: + // Skip. + continue + + default: + return spiceerrors.MustBugf("unexpected result from check: %v", result.Membership) + } + } + + resultsToCheck = make([]*v1.DispatchLookupResources2Response, 0, int(datastore.FilterMaximumIDCount)) + return nil + } + + wrappedStream := dispatch.NewHandlingDispatchStream(sctx, func(result *v1.DispatchLookupResources2Response) error { + select { + case <-ctx.Done(): + return ctx.Err() + + default: + } + + // If the entrypoint is a direct result, simply publish the found resource. + if entrypoint.IsDirectResult() { + return publishResultToParentStream(result, nil, emptyMetadata) + } + + // Otherwise, queue the result for checking and publishing if the check succeeds. + return batchCheckAndPublishIfNecessary(result) + }) + + return wrappedStream, func() error { + defer cancelDispatch(nil) + + return batchCheckAndPublishIfNecessary(nil) + } +} diff --git a/internal/graph/resourcesubjectsmap.go b/internal/graph/resourcesubjectsmap.go index 62a3076098..667a249c77 100644 --- a/internal/graph/resourcesubjectsmap.go +++ b/internal/graph/resourcesubjectsmap.go @@ -31,7 +31,8 @@ type resourcesSubjectMap struct { // subjectInfo is the information about a subject contained in a resourcesSubjectMap. type subjectInfo struct { - subjectID string + subjectID string + isCaveated bool } @@ -142,6 +143,7 @@ func (rsm dispatchableResourcesSubjectMap) asReachableResources(isDirectEntrypoi for _, info := range subjectInfos { subjectIDs = append(subjectIDs, info.subjectID) + if !info.isCaveated { allCaveated = false nonCaveatedSubjectIDs = append(nonCaveatedSubjectIDs, info.subjectID) @@ -197,6 +199,7 @@ func (rsm dispatchableResourcesSubjectMap) mapFoundResource(foundResource *v1.Re for _, info := range infos { forSubjectIDs.Insert(info.subjectID) + if !info.isCaveated { nonCaveatedSubjectIDs.Insert(info.subjectID) } diff --git a/internal/graph/resourcesubjectsmap2.go b/internal/graph/resourcesubjectsmap2.go new file mode 100644 index 0000000000..bde1b85d02 --- /dev/null +++ b/internal/graph/resourcesubjectsmap2.go @@ -0,0 +1,197 @@ +package graph + +import ( + "sort" + + "github.com/authzed/spicedb/pkg/genutil/mapz" + core "github.com/authzed/spicedb/pkg/proto/core/v1" + v1 "github.com/authzed/spicedb/pkg/proto/dispatch/v1" + "github.com/authzed/spicedb/pkg/spiceerrors" +) + +// resourcesSubjectMap2 is a multimap which tracks mappings from found resource IDs +// to the subject IDs (may be more than one) for each, as well as whether the mapping +// is conditional due to the use of a caveat on the relationship which formed the mapping. +type resourcesSubjectMap2 struct { + resourceType *core.RelationReference + resourcesAndSubjects *mapz.MultiMap[string, subjectInfo2] +} + +// subjectInfo2 is the information about a subject contained in a resourcesSubjectMap2. +type subjectInfo2 struct { + subjectID string + missingContextParameters []string +} + +func newResourcesSubjectMap2(resourceType *core.RelationReference) resourcesSubjectMap2 { + return resourcesSubjectMap2{ + resourceType: resourceType, + resourcesAndSubjects: mapz.NewMultiMap[string, subjectInfo2](), + } +} + +func newResourcesSubjectMap2WithCapacity(resourceType *core.RelationReference, capacity uint32) resourcesSubjectMap2 { + return resourcesSubjectMap2{ + resourceType: resourceType, + resourcesAndSubjects: mapz.NewMultiMapWithCap[string, subjectInfo2](capacity), + } +} + +func subjectIDsToResourcesMap2(resourceType *core.RelationReference, subjectIDs []string) resourcesSubjectMap2 { + rsm := newResourcesSubjectMap2(resourceType) + for _, subjectID := range subjectIDs { + rsm.addSubjectIDAsFoundResourceID(subjectID) + } + return rsm +} + +// addRelationship adds the relationship to the resource subject map, recording a mapping from +// the resource of the relationship to the subject, as well as whether the relationship was caveated. +func (rsm resourcesSubjectMap2) addRelationship(rel *core.RelationTuple, missingContextParameters []string) error { + if rel.ResourceAndRelation.Namespace != rsm.resourceType.Namespace || + rel.ResourceAndRelation.Relation != rsm.resourceType.Relation { + return spiceerrors.MustBugf("invalid relationship for addRelationship. expected: %v, found: %v", rsm.resourceType, rel.ResourceAndRelation) + } + + if len(missingContextParameters) > 0 && rel.Caveat == nil { + return spiceerrors.MustBugf("missing caveat for caveated relationship") + } + + rsm.resourcesAndSubjects.Add(rel.ResourceAndRelation.ObjectId, subjectInfo2{rel.Subject.ObjectId, missingContextParameters}) + return nil +} + +// addSubjectIDAsFoundResourceID adds a subject ID directly as a found subject for itself as the resource, +// with no associated caveat. +func (rsm resourcesSubjectMap2) addSubjectIDAsFoundResourceID(subjectID string) { + rsm.resourcesAndSubjects.Add(subjectID, subjectInfo2{subjectID, nil}) +} + +// asReadOnly returns a read-only dispatchableResourcesSubjectMap2 for dispatching for the +// resources in this map (if any). +func (rsm resourcesSubjectMap2) asReadOnly() dispatchableResourcesSubjectMap2 { + return dispatchableResourcesSubjectMap2{rsm.resourceType, rsm.resourcesAndSubjects.AsReadOnly()} +} + +func (rsm resourcesSubjectMap2) len() int { + return rsm.resourcesAndSubjects.Len() +} + +// dispatchableResourcesSubjectMap2 is a read-only, frozen version of the resourcesSubjectMap2 that +// can be used for mapping conditionals once calls have been dispatched. This is read-only due to +// its use by concurrent callers. +type dispatchableResourcesSubjectMap2 struct { + resourceType *core.RelationReference + resourcesAndSubjects mapz.ReadOnlyMultimap[string, subjectInfo2] +} + +func (rsm dispatchableResourcesSubjectMap2) isEmpty() bool { + return rsm.resourcesAndSubjects.IsEmpty() +} + +func (rsm dispatchableResourcesSubjectMap2) resourceIDs() []string { + return rsm.resourcesAndSubjects.Keys() +} + +// filterSubjectIDsToDispatch returns the set of subject IDs that have not yet been +// dispatched, by adding them to the dispatched set. +func (rsm dispatchableResourcesSubjectMap2) filterSubjectIDsToDispatch(dispatched *syncONRSet, dispatchSubjectType *core.RelationReference) []string { + resourceIDs := rsm.resourceIDs() + filtered := make([]string, 0, len(resourceIDs)) + for _, resourceID := range resourceIDs { + if dispatched.Add(&core.ObjectAndRelation{ + Namespace: dispatchSubjectType.Namespace, + ObjectId: resourceID, + Relation: dispatchSubjectType.Relation, + }) { + filtered = append(filtered, resourceID) + } + } + + return filtered +} + +func (rsm dispatchableResourcesSubjectMap2) asPossibleResources() []*v1.PossibleResource { + resources := make([]*v1.PossibleResource, 0, rsm.resourcesAndSubjects.Len()) + + // Sort for stability. + sortedResourceIds := rsm.resourcesAndSubjects.Keys() + sort.Strings(sortedResourceIds) + + for _, resourceID := range sortedResourceIds { + subjectInfo2s, _ := rsm.resourcesAndSubjects.Get(resourceID) + subjectIDs := make([]string, 0, len(subjectInfo2s)) + allCaveated := true + nonCaveatedSubjectIDs := make([]string, 0, len(subjectInfo2s)) + missingContextParameters := mapz.NewSet[string]() + + for _, info := range subjectInfo2s { + subjectIDs = append(subjectIDs, info.subjectID) + if len(info.missingContextParameters) == 0 { + allCaveated = false + nonCaveatedSubjectIDs = append(nonCaveatedSubjectIDs, info.subjectID) + } else { + missingContextParameters.Extend(info.missingContextParameters) + } + } + + // Sort for stability. + sort.Strings(subjectIDs) + + // If all the incoming edges are caveated, then the entire status has to be marked as a check + // is required. Otherwise, if there is at least *one* non-caveated incoming edge, then we can + // return the existing status as a short-circuit for those non-caveated found subjects. + if allCaveated { + resources = append(resources, &v1.PossibleResource{ + ResourceId: resourceID, + ForSubjectIds: subjectIDs, + MissingContextParams: missingContextParameters.AsSlice(), + }) + } else { + resources = append(resources, &v1.PossibleResource{ + ResourceId: resourceID, + ForSubjectIds: nonCaveatedSubjectIDs, + }) + } + } + return resources +} + +func (rsm dispatchableResourcesSubjectMap2) mapPossibleResource(foundResource *v1.PossibleResource) (*v1.PossibleResource, error) { + forSubjectIDs := mapz.NewSet[string]() + nonCaveatedSubjectIDs := mapz.NewSet[string]() + missingContextParameters := mapz.NewSet[string]() + + for _, forSubjectID := range foundResource.ForSubjectIds { + // Map from the incoming subject ID to the subject ID(s) that caused the dispatch. + infos, ok := rsm.resourcesAndSubjects.Get(forSubjectID) + if !ok { + return nil, spiceerrors.MustBugf("missing for subject ID") + } + + for _, info := range infos { + forSubjectIDs.Insert(info.subjectID) + if len(info.missingContextParameters) == 0 { + nonCaveatedSubjectIDs.Insert(info.subjectID) + } else { + missingContextParameters.Extend(info.missingContextParameters) + } + } + } + + // If there are some non-caveated IDs, return those and mark as the parent status. + if nonCaveatedSubjectIDs.Len() > 0 { + return &v1.PossibleResource{ + ResourceId: foundResource.ResourceId, + ForSubjectIds: nonCaveatedSubjectIDs.AsSlice(), + }, nil + } + + // Otherwise, everything is caveated, so return the full set of subject IDs and mark + // as a check is required. + return &v1.PossibleResource{ + ResourceId: foundResource.ResourceId, + ForSubjectIds: forSubjectIDs.AsSlice(), + MissingContextParams: missingContextParameters.AsSlice(), + }, nil +} diff --git a/internal/graph/resourcesubjectsmap2_test.go b/internal/graph/resourcesubjectsmap2_test.go new file mode 100644 index 0000000000..7f670c7db5 --- /dev/null +++ b/internal/graph/resourcesubjectsmap2_test.go @@ -0,0 +1,185 @@ +package graph + +import ( + "sort" + "strings" + "testing" + + "github.com/stretchr/testify/require" + + core "github.com/authzed/spicedb/pkg/proto/core/v1" + v1 "github.com/authzed/spicedb/pkg/proto/dispatch/v1" + "github.com/authzed/spicedb/pkg/testutil" + "github.com/authzed/spicedb/pkg/tuple" +) + +func TestResourcesSubjectsMap2Basic(t *testing.T) { + rsm := newResourcesSubjectMap2(&core.RelationReference{ + Namespace: "document", + Relation: "view", + }) + + require.Equal(t, rsm.resourceType.Namespace, "document") + require.Equal(t, rsm.resourceType.Relation, "view") + require.Equal(t, 0, rsm.len()) + + rsm.addSubjectIDAsFoundResourceID("first") + require.Equal(t, 1, rsm.len()) + + rsm.addSubjectIDAsFoundResourceID("second") + require.Equal(t, 2, rsm.len()) + + err := rsm.addRelationship(tuple.MustParse("document:third#view@user:tom"), nil) + require.NoError(t, err) + require.Equal(t, 3, rsm.len()) + + err = rsm.addRelationship(tuple.MustParse("document:fourth#view@user:sarah[somecaveat]"), []string{"somecontext"}) + require.NoError(t, err) + require.Equal(t, 4, rsm.len()) + + locked := rsm.asReadOnly() + require.False(t, locked.isEmpty()) + + directAsResources := locked.asPossibleResources() + testutil.RequireProtoSlicesEqual(t, []*v1.PossibleResource{ + { + ResourceId: "first", + ForSubjectIds: []string{"first"}, + }, + { + ResourceId: "fourth", + ForSubjectIds: []string{"sarah"}, + MissingContextParams: []string{"somecontext"}, + }, + { + ResourceId: "second", + ForSubjectIds: []string{"second"}, + }, + { + ResourceId: "third", + ForSubjectIds: []string{"tom"}, + }, + }, directAsResources, nil, "different resources") +} + +type relAndMissingContext struct { + rel *core.RelationTuple + missingContext []string +} + +func TestResourcesSubjectsMap2MapFoundResources(t *testing.T) { + tcs := []struct { + name string + rels []relAndMissingContext + foundResources []*v1.PossibleResource + expected []*v1.PossibleResource + }{ + { + "empty", + []relAndMissingContext{}, + []*v1.PossibleResource{}, + []*v1.PossibleResource{}, + }, + { + "basic no caveats", + []relAndMissingContext{ + {rel: tuple.MustParse("group:firstgroup#member@organization:foo")}, + {rel: tuple.MustParse("group:firstgroup#member@organization:bar")}, + }, + []*v1.PossibleResource{ + { + ResourceId: "first", + ForSubjectIds: []string{"firstgroup"}, + }, + }, + []*v1.PossibleResource{ + { + ResourceId: "first", + ForSubjectIds: []string{"foo", "bar"}, + }, + }, + }, + { + "caveated all found", + []relAndMissingContext{ + {rel: tuple.MustParse("group:firstgroup#member@organization:foo[somecaveat]")}, + {rel: tuple.MustParse("group:firstgroup#member@organization:bar[somecvaeat]")}, + }, + []*v1.PossibleResource{ + { + ResourceId: "first", + ForSubjectIds: []string{"firstgroup"}, + }, + }, + []*v1.PossibleResource{ + { + ResourceId: "first", + ForSubjectIds: []string{"bar", "foo"}, + }, + }, + }, + { + "multi-input all caveated", + []relAndMissingContext{ + {rel: tuple.MustParse("group:firstgroup#member@organization:bar[anothercaveat]"), missingContext: []string{"somecontext"}}, + {rel: tuple.MustParse("group:secondgroup#member@organization:foo[somecaveat]"), missingContext: []string{"anothercaveat"}}, + }, + []*v1.PossibleResource{ + { + ResourceId: "somedoc", + ForSubjectIds: []string{"firstgroup", "secondgroup"}, + MissingContextParams: []string{"somecontext"}, + }, + }, + []*v1.PossibleResource{ + { + ResourceId: "somedoc", + ForSubjectIds: []string{"bar", "foo"}, + MissingContextParams: []string{"anothercaveat", "somecontext"}, + }, + }, + }, + } + + for _, tc := range tcs { + tc := tc + t.Run(tc.name, func(t *testing.T) { + rsm := newResourcesSubjectMap2(&core.RelationReference{ + Namespace: "group", + Relation: "member", + }) + + for _, rel := range tc.rels { + err := rsm.addRelationship(rel.rel, rel.missingContext) + require.NoError(t, err) + } + + expected := make([]*v1.PossibleResource, 0, len(tc.expected)) + for _, expectedResource := range tc.expected { + cloned := expectedResource.CloneVT() + sort.Strings(cloned.ForSubjectIds) + expected = append(expected, cloned) + } + + locked := rsm.asReadOnly() + + resources := make([]*v1.PossibleResource, 0, len(tc.foundResources)) + for _, resource := range tc.foundResources { + r, err := locked.mapPossibleResource(resource) + require.NoError(t, err) + resources = append(resources, r) + } + + for _, r := range resources { + sort.Strings(r.ForSubjectIds) + sort.Strings(r.MissingContextParams) + } + + testutil.RequireProtoSlicesEqual(t, expected, resources, sortPossibleByResource, "different resources") + }) + } +} + +func sortPossibleByResource(first *v1.PossibleResource, second *v1.PossibleResource) int { + return strings.Compare(first.ResourceId, second.ResourceId) +} diff --git a/internal/services/dispatch/v1/acl.go b/internal/services/dispatch/v1/acl.go index 64dbe071c6..68d2740929 100644 --- a/internal/services/dispatch/v1/acl.go +++ b/internal/services/dispatch/v1/acl.go @@ -69,6 +69,14 @@ func (ds *dispatchServer) DispatchLookupResources( dispatch.WrapGRPCStream[*dispatchv1.DispatchLookupResourcesResponse](resp)) } +func (ds *dispatchServer) DispatchLookupResources2( + req *dispatchv1.DispatchLookupResources2Request, + resp dispatchv1.DispatchService_DispatchLookupResources2Server, +) error { + return ds.localDispatch.DispatchLookupResources2(req, + dispatch.WrapGRPCStream[*dispatchv1.DispatchLookupResources2Response](resp)) +} + func (ds *dispatchServer) DispatchLookupSubjects( req *dispatchv1.DispatchLookupSubjectsRequest, resp dispatchv1.DispatchService_DispatchLookupSubjectsServer, diff --git a/internal/services/integrationtesting/consistency_test.go b/internal/services/integrationtesting/consistency_test.go index 0316140731..2ca87c9c4f 100644 --- a/internal/services/integrationtesting/consistency_test.go +++ b/internal/services/integrationtesting/consistency_test.go @@ -23,6 +23,7 @@ import ( "github.com/authzed/spicedb/internal/dispatch" "github.com/authzed/spicedb/internal/graph" "github.com/authzed/spicedb/internal/services/integrationtesting/consistencytestutil" + "github.com/authzed/spicedb/pkg/cmd/server" "github.com/authzed/spicedb/pkg/datastore" "github.com/authzed/spicedb/pkg/development" "github.com/authzed/spicedb/pkg/genutil/mapz" @@ -62,16 +63,26 @@ func TestConsistency(t *testing.T) { dispatcherKind := dispatcherKind t.Run(dispatcherKind, func(t *testing.T) { - t.Parallel() - runConsistencyTestSuiteForFile(t, filePath, dispatcherKind == "caching") + for _, useLRV2 := range []bool{false, true} { + useLRV2 := useLRV2 + t.Run(fmt.Sprintf("lrv2-%t", useLRV2), func(t *testing.T) { + t.Parallel() + runConsistencyTestSuiteForFile(t, filePath, dispatcherKind == "caching", useLRV2) + }) + } }) } }) } } -func runConsistencyTestSuiteForFile(t *testing.T, filePath string, useCachingDispatcher bool) { - cad := consistencytestutil.LoadDataAndCreateClusterForTesting(t, filePath, testTimedelta) +func runConsistencyTestSuiteForFile(t *testing.T, filePath string, useCachingDispatcher bool, useLRV2 bool) { + options := []server.ConfigOption{} + if useLRV2 { + options = append(options, server.WithEnableExperimentalLookupResources(true)) + } + + cad := consistencytestutil.LoadDataAndCreateClusterForTesting(t, filePath, testTimedelta, options...) // Validate the type system for each namespace. headRevision, err := cad.DataStore.HeadRevision(cad.Ctx) diff --git a/internal/services/integrationtesting/consistencytestutil/clusteranddata.go b/internal/services/integrationtesting/consistencytestutil/clusteranddata.go index 637d5ead9c..eae495a202 100644 --- a/internal/services/integrationtesting/consistencytestutil/clusteranddata.go +++ b/internal/services/integrationtesting/consistencytestutil/clusteranddata.go @@ -16,6 +16,7 @@ import ( "github.com/authzed/spicedb/internal/dispatch/keys" datastoremw "github.com/authzed/spicedb/internal/middleware/datastore" "github.com/authzed/spicedb/internal/testserver" + "github.com/authzed/spicedb/pkg/cmd/server" "github.com/authzed/spicedb/pkg/datastore" "github.com/authzed/spicedb/pkg/typesystem" "github.com/authzed/spicedb/pkg/validationfile" @@ -34,24 +35,24 @@ type ConsistencyClusterAndData struct { // LoadDataAndCreateClusterForTesting loads the data found in a consistency test file, // builds a cluster for it, and returns both the data and cluster. -func LoadDataAndCreateClusterForTesting(t *testing.T, consistencyTestFilePath string, revisionDelta time.Duration) ConsistencyClusterAndData { +func LoadDataAndCreateClusterForTesting(t *testing.T, consistencyTestFilePath string, revisionDelta time.Duration, additionalServerOptions ...server.ConfigOption) ConsistencyClusterAndData { require := require.New(t) ds, err := memdb.NewMemdbDatastore(0, revisionDelta, memdb.DisableGC) require.NoError(err) - return BuildDataAndCreateClusterForTesting(t, consistencyTestFilePath, ds) + return BuildDataAndCreateClusterForTesting(t, consistencyTestFilePath, ds, additionalServerOptions...) } // BuildDataAndCreateClusterForTesting loads the data found in a consistency test file, // builds a cluster for it, and returns both the data and cluster. -func BuildDataAndCreateClusterForTesting(t *testing.T, consistencyTestFilePath string, ds datastore.Datastore) ConsistencyClusterAndData { +func BuildDataAndCreateClusterForTesting(t *testing.T, consistencyTestFilePath string, ds datastore.Datastore, additionalServerOptions ...server.ConfigOption) ConsistencyClusterAndData { require := require.New(t) populated, revision, err := validationfile.PopulateFromFiles(context.Background(), ds, []string{consistencyTestFilePath}) require.NoError(err) - connections, cleanup := testserver.TestClusterWithDispatch(t, 1, ds) + connections, cleanup := testserver.TestClusterWithDispatch(t, 1, ds, additionalServerOptions...) t.Cleanup(cleanup) dsCtx := datastoremw.ContextWithHandle(context.Background()) diff --git a/internal/services/integrationtesting/testconfigs/caveatarrow.yaml b/internal/services/integrationtesting/testconfigs/caveatarrow.yaml index e59ce8e7fd..4b742138b5 100644 --- a/internal/services/integrationtesting/testconfigs/caveatarrow.yaml +++ b/internal/services/integrationtesting/testconfigs/caveatarrow.yaml @@ -51,11 +51,11 @@ assertions: - "document:caveatedorgdoc#view@user:tom" - "document:staticorgdoc#view@user:sarah" - "document:caveatedorgdoc#view@user:sarah" - - "document:caveatedorgdoc#view@user:fred" - 'document:caveatedorgdoc#view@user:sarah with {"anothercondition": "hello world"}' assertFalse: - "document:directorgdoc#view@user:fred" - "document:staticnoorgdoc#view@user:tom" - "document:staticorgdoc#view@user:fred" + - "document:caveatedorgdoc#view@user:fred" - 'document:caveatedorgdoc#view@user:tom with {"anothercondition": "nope"}' - 'document:caveatedorgdoc#view@user:sarah with {"somecondition": 41, "anothercondition": "hello world"}' diff --git a/internal/services/v1/debug_test.go b/internal/services/v1/debug_test.go index dea12bb669..0b68f95295 100644 --- a/internal/services/v1/debug_test.go +++ b/internal/services/v1/debug_test.go @@ -402,7 +402,7 @@ func TestCheckPermissionWithDebug(t *testing.T) { }, v1.CheckPermissionResponse_PERMISSIONSHIP_NO_PERMISSION, 1, - []rda{expectDebugFrames("member"), expectCaveat(`anothercondition == "hello world" && somecondition == 42`)}, + []rda{expectDebugFrames("member"), expectCaveat(`somecondition == 42`)}, }, { "sarah as partially conditional viewer", @@ -433,7 +433,7 @@ func TestCheckPermissionWithDebug(t *testing.T) { 1, []rda{ expectDebugFrames("member"), - expectMissingContext("anothercondition"), + expectMissingContext("anothercondition", "somecondition"), }, }, }, diff --git a/internal/services/v1/permissions.go b/internal/services/v1/permissions.go index 97c4a08b60..b19f23a925 100644 --- a/internal/services/v1/permissions.go +++ b/internal/services/v1/permissions.go @@ -376,6 +376,14 @@ func TranslateExpansionTree(node *core.RelationTupleTreeNode) *v1.PermissionRela } func (ps *permissionServer) LookupResources(req *v1.LookupResourcesRequest, resp v1.PermissionsService_LookupResourcesServer) error { + if ps.config.UseExperimentalLookupResources2 { + return ps.lookupResources2(req, resp) + } + + return ps.lookupResources1(req, resp) +} + +func (ps *permissionServer) lookupResources1(req *v1.LookupResourcesRequest, resp v1.PermissionsService_LookupResourcesServer) error { if req.OptionalLimit > 0 && req.OptionalLimit > ps.config.MaxLookupResourcesLimit { return ps.rewriteError(resp.Context(), NewExceedsMaximumLimitErr(uint64(req.OptionalLimit), uint64(ps.config.MaxLookupResourcesLimit))) } @@ -503,6 +511,139 @@ func (ps *permissionServer) LookupResources(req *v1.LookupResourcesRequest, resp return nil } +func (ps *permissionServer) lookupResources2(req *v1.LookupResourcesRequest, resp v1.PermissionsService_LookupResourcesServer) error { + if req.OptionalLimit > 0 && req.OptionalLimit > ps.config.MaxLookupResourcesLimit { + return ps.rewriteError(resp.Context(), NewExceedsMaximumLimitErr(uint64(req.OptionalLimit), uint64(ps.config.MaxLookupResourcesLimit))) + } + + ctx := resp.Context() + + atRevision, revisionReadAt, err := consistency.RevisionFromContext(ctx) + if err != nil { + return ps.rewriteError(ctx, err) + } + + ds := datastoremw.MustFromContext(ctx).SnapshotReader(atRevision) + + if err := namespace.CheckNamespaceAndRelations(ctx, + []namespace.TypeAndRelationToCheck{ + { + NamespaceName: req.ResourceObjectType, + RelationName: req.Permission, + AllowEllipsis: false, + }, + { + NamespaceName: req.Subject.Object.ObjectType, + RelationName: normalizeSubjectRelation(req.Subject), + AllowEllipsis: true, + }, + }, ds); err != nil { + return ps.rewriteError(ctx, err) + } + + respMetadata := &dispatch.ResponseMeta{ + DispatchCount: 1, + CachedDispatchCount: 0, + DepthRequired: 1, + DebugInfo: nil, + } + usagemetrics.SetInContext(ctx, respMetadata) + + var currentCursor *dispatch.Cursor + + lrRequestHash, err := computeLRRequestHash(req) + if err != nil { + return ps.rewriteError(ctx, err) + } + + if req.OptionalCursor != nil { + decodedCursor, err := cursor.DecodeToDispatchCursor(req.OptionalCursor, lrRequestHash) + if err != nil { + return ps.rewriteError(ctx, err) + } + currentCursor = decodedCursor + } + + alreadyPublishedPermissionedResourceIds := map[string]struct{}{} + + stream := dispatchpkg.NewHandlingDispatchStream(ctx, func(result *dispatch.DispatchLookupResources2Response) error { + found := result.Resource + + dispatchpkg.AddResponseMetadata(respMetadata, result.Metadata) + currentCursor = result.AfterResponseCursor + + var partial *v1.PartialCaveatInfo + permissionship := v1.LookupPermissionship_LOOKUP_PERMISSIONSHIP_HAS_PERMISSION + if len(found.MissingContextParams) > 0 { + permissionship = v1.LookupPermissionship_LOOKUP_PERMISSIONSHIP_CONDITIONAL_PERMISSION + partial = &v1.PartialCaveatInfo{ + MissingRequiredContext: found.MissingContextParams, + } + } else if req.OptionalLimit == 0 { + if _, ok := alreadyPublishedPermissionedResourceIds[found.ResourceId]; ok { + // Skip publishing the duplicate. + return nil + } + + alreadyPublishedPermissionedResourceIds[found.ResourceId] = struct{}{} + } + + encodedCursor, err := cursor.EncodeFromDispatchCursor(result.AfterResponseCursor, lrRequestHash, atRevision) + if err != nil { + return ps.rewriteError(ctx, err) + } + + err = resp.Send(&v1.LookupResourcesResponse{ + LookedUpAt: revisionReadAt, + ResourceObjectId: found.ResourceId, + Permissionship: permissionship, + PartialCaveatInfo: partial, + AfterResultCursor: encodedCursor, + }) + if err != nil { + return err + } + return nil + }) + + bf, err := dispatch.NewTraversalBloomFilter(uint(ps.config.MaximumAPIDepth)) + if err != nil { + return err + } + + err = ps.dispatch.DispatchLookupResources2( + &dispatch.DispatchLookupResources2Request{ + Metadata: &dispatch.ResolverMeta{ + AtRevision: atRevision.String(), + DepthRemaining: ps.config.MaximumAPIDepth, + TraversalBloom: bf, + }, + ResourceRelation: &core.RelationReference{ + Namespace: req.ResourceObjectType, + Relation: req.Permission, + }, + SubjectRelation: &core.RelationReference{ + Namespace: req.Subject.Object.ObjectType, + Relation: normalizeSubjectRelation(req.Subject), + }, + SubjectIds: []string{req.Subject.Object.ObjectId}, + TerminalSubject: &core.ObjectAndRelation{ + Namespace: req.Subject.Object.ObjectType, + ObjectId: req.Subject.Object.ObjectId, + Relation: normalizeSubjectRelation(req.Subject), + }, + Context: req.Context, + OptionalCursor: currentCursor, + OptionalLimit: req.OptionalLimit, + }, + stream) + if err != nil { + return ps.rewriteError(ctx, err) + } + + return nil +} + func (ps *permissionServer) LookupSubjects(req *v1.LookupSubjectsRequest, resp v1.PermissionsService_LookupSubjectsServer) error { ctx := resp.Context() diff --git a/internal/services/v1/permissions_test.go b/internal/services/v1/permissions_test.go index 4702cd7127..e04445044d 100644 --- a/internal/services/v1/permissions_test.go +++ b/internal/services/v1/permissions_test.go @@ -556,7 +556,7 @@ func TestLookupResources(t *testing.T) { []string{"specialplan"}, codes.OK, 6, - 7, + 8, }, { "document", "view_and_edit", @@ -622,53 +622,71 @@ func TestLookupResources(t *testing.T) { for _, tc := range testCases { tc := tc t.Run(fmt.Sprintf("%s::%s from %s:%s#%s", tc.objectType, tc.permission, tc.subject.Object.ObjectType, tc.subject.Object.ObjectId, tc.subject.OptionalRelation), func(t *testing.T) { - require := require.New(t) - conn, cleanup, _, revision := testserver.NewTestServer(require, delta, memdb.DisableGC, true, tf.StandardDatastoreWithData) - client := v1.NewPermissionsServiceClient(conn) - t.Cleanup(func() { - goleak.VerifyNone(t, goleak.IgnoreCurrent()) - }) - t.Cleanup(cleanup) - - var trailer metadata.MD - lookupClient, err := client.LookupResources(context.Background(), &v1.LookupResourcesRequest{ - ResourceObjectType: tc.objectType, - Permission: tc.permission, - Subject: tc.subject, - Consistency: &v1.Consistency{ - Requirement: &v1.Consistency_AtLeastAsFresh{ - AtLeastAsFresh: zedtoken.MustNewFromRevision(revision), - }, - }, - }, grpc.Trailer(&trailer)) + for _, useV2 := range []bool{false, true} { + useV2 := useV2 + t.Run(fmt.Sprintf("v2:%v", useV2), func(t *testing.T) { + require := require.New(t) + conn, cleanup, _, revision := testserver.NewTestServerWithConfig( + require, + delta, + memdb.DisableGC, + true, + testserver.ServerConfig{ + MaxUpdatesPerWrite: 1000, + MaxPreconditionsCount: 1000, + StreamingAPITimeout: 30 * time.Second, + MaxRelationshipContextSize: 25000, + UseExperimentalLookupResources2: useV2, + }, + tf.StandardDatastoreWithData, + ) + client := v1.NewPermissionsServiceClient(conn) + t.Cleanup(func() { + goleak.VerifyNone(t, goleak.IgnoreCurrent()) + }) + t.Cleanup(cleanup) - require.NoError(err) - if tc.expectedErrorCode == codes.OK { - var resolvedObjectIds []string - for { - resp, err := lookupClient.Recv() - if errors.Is(err, io.EOF) { - break - } + var trailer metadata.MD + lookupClient, err := client.LookupResources(context.Background(), &v1.LookupResourcesRequest{ + ResourceObjectType: tc.objectType, + Permission: tc.permission, + Subject: tc.subject, + Consistency: &v1.Consistency{ + Requirement: &v1.Consistency_AtLeastAsFresh{ + AtLeastAsFresh: zedtoken.MustNewFromRevision(revision), + }, + }, + }, grpc.Trailer(&trailer)) require.NoError(err) + if tc.expectedErrorCode == codes.OK { + var resolvedObjectIds []string + for { + resp, err := lookupClient.Recv() + if errors.Is(err, io.EOF) { + break + } - resolvedObjectIds = append(resolvedObjectIds, resp.ResourceObjectId) - } + require.NoError(err) - slices.Sort(tc.expectedObjectIds) - slices.Sort(resolvedObjectIds) + resolvedObjectIds = append(resolvedObjectIds, resp.ResourceObjectId) + } - require.Equal(tc.expectedObjectIds, resolvedObjectIds) + slices.Sort(tc.expectedObjectIds) + slices.Sort(resolvedObjectIds) - dispatchCount, err := responsemeta.GetIntResponseTrailerMetadata(trailer, responsemeta.DispatchedOperationsCount) - require.NoError(err) - require.GreaterOrEqual(dispatchCount, 0) - require.LessOrEqual(dispatchCount, tc.maximumDispatchCount) - require.GreaterOrEqual(dispatchCount, tc.minimumDispatchCount) - } else { - _, err := lookupClient.Recv() - grpcutil.RequireStatus(t, tc.expectedErrorCode, err) + require.Equal(tc.expectedObjectIds, resolvedObjectIds) + + dispatchCount, err := responsemeta.GetIntResponseTrailerMetadata(trailer, responsemeta.DispatchedOperationsCount) + require.NoError(err) + require.GreaterOrEqual(dispatchCount, 0) + require.LessOrEqual(dispatchCount, tc.maximumDispatchCount) + require.GreaterOrEqual(dispatchCount, tc.minimumDispatchCount) + } else { + _, err := lookupClient.Recv() + grpcutil.RequireStatus(t, tc.expectedErrorCode, err) + } + }) } }) } diff --git a/internal/services/v1/relationships.go b/internal/services/v1/relationships.go index 4f77c0fa11..0be7131154 100644 --- a/internal/services/v1/relationships.go +++ b/internal/services/v1/relationships.go @@ -89,6 +89,9 @@ type PermissionsServerConfig struct { // MaxBulkExportRelationshipsLimit defines the maximum number of relationships that can be // exported in a single BulkExportRelationships call. MaxBulkExportRelationshipsLimit uint32 + + // UseExperimentalLookupResources2 enables the experimental LookupResources2 API. + UseExperimentalLookupResources2 bool } // NewPermissionsServer creates a PermissionsServiceServer instance. @@ -108,6 +111,7 @@ func NewPermissionsServer( MaxDeleteRelationshipsLimit: defaultIfZero(config.MaxDeleteRelationshipsLimit, 1_000), MaxLookupResourcesLimit: defaultIfZero(config.MaxLookupResourcesLimit, 1_000), MaxBulkExportRelationshipsLimit: defaultIfZero(config.MaxBulkExportRelationshipsLimit, 100_000), + UseExperimentalLookupResources2: config.UseExperimentalLookupResources2, } return &permissionServer{ diff --git a/internal/testserver/cluster.go b/internal/testserver/cluster.go index 49265c944f..da08cfc628 100644 --- a/internal/testserver/cluster.go +++ b/internal/testserver/cluster.go @@ -137,12 +137,12 @@ func (r *SafeManualResolver) Close() {} // TestClusterWithDispatch creates a cluster with `size` nodes // The cluster has a real dispatch stack that uses bufconn grpc connections -func TestClusterWithDispatch(t testing.TB, size uint, ds datastore.Datastore) ([]*grpc.ClientConn, func()) { - return TestClusterWithDispatchAndCacheConfig(t, size, ds) +func TestClusterWithDispatch(t testing.TB, size uint, ds datastore.Datastore, additionalServerOptions ...server.ConfigOption) ([]*grpc.ClientConn, func()) { + return TestClusterWithDispatchAndCacheConfig(t, size, ds, additionalServerOptions...) } // TestClusterWithDispatchAndCacheConfig creates a cluster with `size` nodes and with cache toggled. -func TestClusterWithDispatchAndCacheConfig(t testing.TB, size uint, ds datastore.Datastore) ([]*grpc.ClientConn, func()) { +func TestClusterWithDispatchAndCacheConfig(t testing.TB, size uint, ds datastore.Datastore, additionalServerOptions ...server.ConfigOption) ([]*grpc.ClientConn, func()) { // each cluster gets a unique prefix since grpc resolution is process-global prefix := getPrefix(t) @@ -212,6 +212,7 @@ func TestClusterWithDispatchAndCacheConfig(t testing.TB, size uint, ds datastore }), server.WithDispatchClusterMetricsPrefix(fmt.Sprintf("%s_%d_dispatch", prefix, i)), } + serverOptions = append(serverOptions, additionalServerOptions...) ctx, cancel := context.WithCancel(context.Background()) srv, err := server.NewConfigWithOptions(serverOptions...).Complete(ctx) diff --git a/internal/testserver/server.go b/internal/testserver/server.go index 5ac971353b..547288e2f3 100644 --- a/internal/testserver/server.go +++ b/internal/testserver/server.go @@ -21,17 +21,19 @@ import ( // ServerConfig is configuration for the test server. type ServerConfig struct { - MaxUpdatesPerWrite uint16 - MaxPreconditionsCount uint16 - MaxRelationshipContextSize int - StreamingAPITimeout time.Duration + MaxUpdatesPerWrite uint16 + MaxPreconditionsCount uint16 + MaxRelationshipContextSize int + StreamingAPITimeout time.Duration + UseExperimentalLookupResources2 bool } var DefaultTestServerConfig = ServerConfig{ - MaxUpdatesPerWrite: 1000, - MaxPreconditionsCount: 1000, - StreamingAPITimeout: 30 * time.Second, - MaxRelationshipContextSize: 25000, + MaxUpdatesPerWrite: 1000, + MaxPreconditionsCount: 1000, + StreamingAPITimeout: 30 * time.Second, + MaxRelationshipContextSize: 25000, + UseExperimentalLookupResources2: false, } // NewTestServer creates a new test server, using defaults for the config. @@ -90,6 +92,7 @@ func NewTestServerWithConfigAndDatastore(require *require.Assertions, server.WithHTTPGateway(util.HTTPServerConfig{HTTPEnabled: false}), server.WithMetricsAPI(util.HTTPServerConfig{HTTPEnabled: false}), server.WithDispatchServer(util.GRPCServerConfig{Enabled: false}), + server.WithEnableExperimentalLookupResources(config.UseExperimentalLookupResources2), server.SetUnaryMiddlewareModification([]server.MiddlewareModification[grpc.UnaryServerInterceptor]{ { Operation: server.OperationReplaceAllUnsafe, diff --git a/pkg/cmd/serve.go b/pkg/cmd/serve.go index f0dbc2cc5d..bfc705260c 100644 --- a/pkg/cmd/serve.go +++ b/pkg/cmd/serve.go @@ -137,6 +137,7 @@ func RegisterServeFlags(cmd *cobra.Command, config *server.Config) error { cmd.Flags().Uint32Var(&config.MaxDeleteRelationshipsLimit, "max-delete-relationships-limit", 1000, "maximum number of relationships that can be deleted in a single request") cmd.Flags().Uint32Var(&config.MaxLookupResourcesLimit, "max-lookup-resources-limit", 1000, "maximum number of resources that can be looked up in a single request") cmd.Flags().Uint32Var(&config.MaxBulkExportRelationshipsLimit, "max-bulk-export-relationships-limit", 10_000, "maximum number of relationships that can be exported in a single request") + cmd.Flags().BoolVar(&config.EnableExperimentalLookupResources, "enable-experimental-lookup-resources", false, "enables the experimental version of the lookup resources API") cmd.Flags().BoolVar(&config.V1SchemaAdditiveOnly, "testing-only-schema-additive-writes", false, "append new definitions to the existing schema, rather than overwriting it") if err := cmd.Flags().MarkHidden("testing-only-schema-additive-writes"); err != nil { diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index c471592676..3abf6049f0 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -104,17 +104,18 @@ type Config struct { ClusterDispatchCacheConfig CacheConfig `debugmap:"visible"` // API Behavior - DisableV1SchemaAPI bool `debugmap:"visible"` - V1SchemaAdditiveOnly bool `debugmap:"visible"` - MaximumUpdatesPerWrite uint16 `debugmap:"visible"` - MaximumPreconditionCount uint16 `debugmap:"visible"` - MaxDatastoreReadPageSize uint64 `debugmap:"visible"` - StreamingAPITimeout time.Duration `debugmap:"visible"` - WatchHeartbeat time.Duration `debugmap:"visible"` - MaxReadRelationshipsLimit uint32 `debugmap:"visible"` - MaxDeleteRelationshipsLimit uint32 `debugmap:"visible"` - MaxLookupResourcesLimit uint32 `debugmap:"visible"` - MaxBulkExportRelationshipsLimit uint32 `debugmap:"visible"` + DisableV1SchemaAPI bool `debugmap:"visible"` + V1SchemaAdditiveOnly bool `debugmap:"visible"` + MaximumUpdatesPerWrite uint16 `debugmap:"visible"` + MaximumPreconditionCount uint16 `debugmap:"visible"` + MaxDatastoreReadPageSize uint64 `debugmap:"visible"` + StreamingAPITimeout time.Duration `debugmap:"visible"` + WatchHeartbeat time.Duration `debugmap:"visible"` + MaxReadRelationshipsLimit uint32 `debugmap:"visible"` + MaxDeleteRelationshipsLimit uint32 `debugmap:"visible"` + MaxLookupResourcesLimit uint32 `debugmap:"visible"` + MaxBulkExportRelationshipsLimit uint32 `debugmap:"visible"` + EnableExperimentalLookupResources bool `debugmap:"visible"` // Additional Services MetricsAPI util.HTTPServerConfig `debugmap:"visible"` @@ -417,6 +418,7 @@ func (c *Config) Complete(ctx context.Context) (RunnableServer, error) { MaxDeleteRelationshipsLimit: c.MaxDeleteRelationshipsLimit, MaxLookupResourcesLimit: c.MaxLookupResourcesLimit, MaxBulkExportRelationshipsLimit: c.MaxBulkExportRelationshipsLimit, + UseExperimentalLookupResources2: c.EnableExperimentalLookupResources, } healthManager := health.NewHealthManager(dispatcher, ds) diff --git a/pkg/cmd/server/zz_generated.options.go b/pkg/cmd/server/zz_generated.options.go index 801978c731..b367c97fc2 100644 --- a/pkg/cmd/server/zz_generated.options.go +++ b/pkg/cmd/server/zz_generated.options.go @@ -85,6 +85,7 @@ func (c *Config) ToOption() ConfigOption { to.MaxDeleteRelationshipsLimit = c.MaxDeleteRelationshipsLimit to.MaxLookupResourcesLimit = c.MaxLookupResourcesLimit to.MaxBulkExportRelationshipsLimit = c.MaxBulkExportRelationshipsLimit + to.EnableExperimentalLookupResources = c.EnableExperimentalLookupResources to.MetricsAPI = c.MetricsAPI to.UnaryMiddlewareModification = c.UnaryMiddlewareModification to.StreamingMiddlewareModification = c.StreamingMiddlewareModification @@ -150,6 +151,7 @@ func (c Config) DebugMap() map[string]any { debugMap["MaxDeleteRelationshipsLimit"] = helpers.DebugValue(c.MaxDeleteRelationshipsLimit, false) debugMap["MaxLookupResourcesLimit"] = helpers.DebugValue(c.MaxLookupResourcesLimit, false) debugMap["MaxBulkExportRelationshipsLimit"] = helpers.DebugValue(c.MaxBulkExportRelationshipsLimit, false) + debugMap["EnableExperimentalLookupResources"] = helpers.DebugValue(c.EnableExperimentalLookupResources, false) debugMap["MetricsAPI"] = helpers.DebugValue(c.MetricsAPI, false) debugMap["SilentlyDisableTelemetry"] = helpers.DebugValue(c.SilentlyDisableTelemetry, false) debugMap["TelemetryCAOverridePath"] = helpers.DebugValue(c.TelemetryCAOverridePath, false) @@ -534,6 +536,13 @@ func WithMaxBulkExportRelationshipsLimit(maxBulkExportRelationshipsLimit uint32) } } +// WithEnableExperimentalLookupResources returns an option that can set EnableExperimentalLookupResources on a Config +func WithEnableExperimentalLookupResources(enableExperimentalLookupResources bool) ConfigOption { + return func(c *Config) { + c.EnableExperimentalLookupResources = enableExperimentalLookupResources + } +} + // WithMetricsAPI returns an option that can set MetricsAPI on a Config func WithMetricsAPI(metricsAPI util.HTTPServerConfig) ConfigOption { return func(c *Config) { diff --git a/pkg/proto/core/v1/core.pb.go b/pkg/proto/core/v1/core.pb.go index 2ea1d3550c..35f49d65be 100644 --- a/pkg/proto/core/v1/core.pb.go +++ b/pkg/proto/core/v1/core.pb.go @@ -1549,6 +1549,10 @@ type ReachabilityEntrypoint struct { // tupleset_relation is the name of the tupleset relation on the TupleToUserset this entrypoint // represents, if applicable. TuplesetRelation string `protobuf:"bytes,5,opt,name=tupleset_relation,json=tuplesetRelation,proto3" json:"tupleset_relation,omitempty"` + // * + // computed_userset_relation is the name of the computed userset relation on the ComputedUserset + // this entrypoint represents, if applicable. + ComputedUsersetRelation string `protobuf:"bytes,6,opt,name=computed_userset_relation,json=computedUsersetRelation,proto3" json:"computed_userset_relation,omitempty"` } func (x *ReachabilityEntrypoint) Reset() { @@ -1611,6 +1615,13 @@ func (x *ReachabilityEntrypoint) GetTuplesetRelation() string { return "" } +func (x *ReachabilityEntrypoint) GetComputedUsersetRelation() string { + if x != nil { + return x.ComputedUsersetRelation + } + return "" +} + // * // TypeInformation defines the allowed types for a relation. type TypeInformation struct { @@ -3197,7 +3208,7 @@ var file_core_v1_core_proto_rawDesc = []byte{ 0x74, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x0f, 0x73, 0x75, - 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x92, 0x04, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xce, 0x04, 0x0a, 0x16, 0x52, 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x4e, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x3a, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, @@ -3217,284 +3228,288 @@ var file_core_v1_core_proto_rawDesc = []byte{ 0x73, 0x75, 0x6c, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x74, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x65, 0x74, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x74, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x65, 0x74, 0x52, - 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x7a, 0x0a, 0x1a, 0x52, 0x65, 0x61, 0x63, 0x68, - 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x70, 0x6f, 0x69, 0x6e, - 0x74, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x17, 0x0a, 0x13, 0x52, 0x45, 0x4c, 0x41, 0x54, 0x49, 0x4f, - 0x4e, 0x5f, 0x45, 0x4e, 0x54, 0x52, 0x59, 0x50, 0x4f, 0x49, 0x4e, 0x54, 0x10, 0x00, 0x12, 0x1f, - 0x0a, 0x1b, 0x43, 0x4f, 0x4d, 0x50, 0x55, 0x54, 0x45, 0x44, 0x5f, 0x55, 0x53, 0x45, 0x52, 0x53, - 0x45, 0x54, 0x5f, 0x45, 0x4e, 0x54, 0x52, 0x59, 0x50, 0x4f, 0x49, 0x4e, 0x54, 0x10, 0x01, 0x12, - 0x22, 0x0a, 0x1e, 0x54, 0x55, 0x50, 0x4c, 0x45, 0x53, 0x45, 0x54, 0x5f, 0x54, 0x4f, 0x5f, 0x55, - 0x53, 0x45, 0x52, 0x53, 0x45, 0x54, 0x5f, 0x45, 0x4e, 0x54, 0x52, 0x59, 0x50, 0x4f, 0x49, 0x4e, - 0x54, 0x10, 0x02, 0x22, 0x57, 0x0a, 0x16, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x70, 0x6f, 0x69, 0x6e, - 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x20, 0x0a, - 0x1c, 0x52, 0x45, 0x41, 0x43, 0x48, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x44, 0x49, - 0x54, 0x49, 0x4f, 0x4e, 0x41, 0x4c, 0x5f, 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x10, 0x00, 0x12, - 0x1b, 0x0a, 0x17, 0x44, 0x49, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, - 0x49, 0x4f, 0x4e, 0x5f, 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x10, 0x01, 0x4a, 0x04, 0x08, 0x03, - 0x10, 0x04, 0x22, 0x65, 0x0a, 0x0f, 0x54, 0x79, 0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x52, 0x0a, 0x18, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, - 0x5f, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x52, 0x16, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, - 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xca, 0x03, 0x0a, 0x0f, 0x41, 0x6c, - 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x66, 0x0a, - 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x42, 0x48, 0xfa, 0x42, 0x45, 0x72, 0x43, 0x28, 0x80, 0x01, 0x32, 0x3e, 0x5e, 0x28, 0x5b, 0x61, - 0x2d, 0x7a, 0x5d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5f, 0x5d, 0x7b, 0x31, 0x2c, 0x36, - 0x31, 0x7d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x2f, 0x29, 0x2a, 0x5b, 0x61, 0x2d, - 0x7a, 0x5d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5f, 0x5d, 0x7b, 0x31, 0x2c, 0x36, 0x32, - 0x7d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x24, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x4e, 0x0a, 0x08, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x30, 0xfa, 0x42, 0x2d, 0x72, 0x2b, 0x28, 0x40, - 0x32, 0x27, 0x5e, 0x28, 0x5c, 0x2e, 0x5c, 0x2e, 0x5c, 0x2e, 0x7c, 0x5b, 0x61, 0x2d, 0x7a, 0x5d, - 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5f, 0x5d, 0x7b, 0x31, 0x2c, 0x36, 0x32, 0x7d, 0x5b, - 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x24, 0x48, 0x00, 0x52, 0x08, 0x72, 0x65, 0x6c, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x52, 0x0a, 0x0f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, - 0x77, 0x69, 0x6c, 0x64, 0x63, 0x61, 0x72, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, - 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, - 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x57, - 0x69, 0x6c, 0x64, 0x63, 0x61, 0x72, 0x64, 0x48, 0x00, 0x52, 0x0e, 0x70, 0x75, 0x62, 0x6c, 0x69, - 0x63, 0x57, 0x69, 0x6c, 0x64, 0x63, 0x61, 0x72, 0x64, 0x12, 0x40, 0x0a, 0x0f, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3f, 0x0a, 0x0f, 0x72, - 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x5f, 0x63, 0x61, 0x76, 0x65, 0x61, 0x74, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, - 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x43, 0x61, 0x76, 0x65, 0x61, 0x74, 0x52, 0x0e, 0x72, 0x65, - 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x43, 0x61, 0x76, 0x65, 0x61, 0x74, 0x1a, 0x10, 0x0a, 0x0e, - 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x57, 0x69, 0x6c, 0x64, 0x63, 0x61, 0x72, 0x64, 0x42, 0x16, - 0x0a, 0x14, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6f, 0x72, 0x5f, 0x77, 0x69, - 0x6c, 0x64, 0x63, 0x61, 0x72, 0x64, 0x22, 0x30, 0x0a, 0x0d, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, - 0x64, 0x43, 0x61, 0x76, 0x65, 0x61, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x61, 0x76, 0x65, 0x61, - 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x61, - 0x76, 0x65, 0x61, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0xad, 0x02, 0x0a, 0x0e, 0x55, 0x73, 0x65, - 0x72, 0x73, 0x65, 0x74, 0x52, 0x65, 0x77, 0x72, 0x69, 0x74, 0x65, 0x12, 0x37, 0x0a, 0x05, 0x75, - 0x6e, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x63, 0x6f, 0x72, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x48, 0x00, 0x52, 0x05, 0x75, - 0x6e, 0x69, 0x6f, 0x6e, 0x12, 0x45, 0x0a, 0x0c, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x65, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x63, 0x6f, 0x72, + 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3a, 0x0a, 0x19, 0x63, 0x6f, 0x6d, 0x70, 0x75, + 0x74, 0x65, 0x64, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x5f, 0x72, 0x65, 0x6c, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x17, 0x63, 0x6f, 0x6d, 0x70, + 0x75, 0x74, 0x65, 0x64, 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x52, 0x65, 0x6c, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x22, 0x7a, 0x0a, 0x1a, 0x52, 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, 0x69, 0x6c, + 0x69, 0x74, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x4b, 0x69, 0x6e, + 0x64, 0x12, 0x17, 0x0a, 0x13, 0x52, 0x45, 0x4c, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x45, 0x4e, + 0x54, 0x52, 0x59, 0x50, 0x4f, 0x49, 0x4e, 0x54, 0x10, 0x00, 0x12, 0x1f, 0x0a, 0x1b, 0x43, 0x4f, + 0x4d, 0x50, 0x55, 0x54, 0x45, 0x44, 0x5f, 0x55, 0x53, 0x45, 0x52, 0x53, 0x45, 0x54, 0x5f, 0x45, + 0x4e, 0x54, 0x52, 0x59, 0x50, 0x4f, 0x49, 0x4e, 0x54, 0x10, 0x01, 0x12, 0x22, 0x0a, 0x1e, 0x54, + 0x55, 0x50, 0x4c, 0x45, 0x53, 0x45, 0x54, 0x5f, 0x54, 0x4f, 0x5f, 0x55, 0x53, 0x45, 0x52, 0x53, + 0x45, 0x54, 0x5f, 0x45, 0x4e, 0x54, 0x52, 0x59, 0x50, 0x4f, 0x49, 0x4e, 0x54, 0x10, 0x02, 0x22, + 0x57, 0x0a, 0x16, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x20, 0x0a, 0x1c, 0x52, 0x45, 0x41, + 0x43, 0x48, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x44, 0x49, 0x54, 0x49, 0x4f, 0x4e, + 0x41, 0x4c, 0x5f, 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x44, + 0x49, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, + 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x10, 0x01, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x22, 0x65, + 0x0a, 0x0f, 0x54, 0x79, 0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x52, 0x0a, 0x18, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x5f, 0x64, 0x69, 0x72, + 0x65, 0x63, 0x74, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x6c, + 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x16, 0x61, + 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x52, 0x65, 0x6c, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xca, 0x03, 0x0a, 0x0f, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, + 0x64, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x66, 0x0a, 0x09, 0x6e, 0x61, 0x6d, + 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x48, 0xfa, 0x42, + 0x45, 0x72, 0x43, 0x28, 0x80, 0x01, 0x32, 0x3e, 0x5e, 0x28, 0x5b, 0x61, 0x2d, 0x7a, 0x5d, 0x5b, + 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5f, 0x5d, 0x7b, 0x31, 0x2c, 0x36, 0x31, 0x7d, 0x5b, 0x61, + 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x2f, 0x29, 0x2a, 0x5b, 0x61, 0x2d, 0x7a, 0x5d, 0x5b, 0x61, + 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5f, 0x5d, 0x7b, 0x31, 0x2c, 0x36, 0x32, 0x7d, 0x5b, 0x61, 0x2d, + 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x24, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x12, 0x4e, 0x0a, 0x08, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x42, 0x30, 0xfa, 0x42, 0x2d, 0x72, 0x2b, 0x28, 0x40, 0x32, 0x27, 0x5e, 0x28, + 0x5c, 0x2e, 0x5c, 0x2e, 0x5c, 0x2e, 0x7c, 0x5b, 0x61, 0x2d, 0x7a, 0x5d, 0x5b, 0x61, 0x2d, 0x7a, + 0x30, 0x2d, 0x39, 0x5f, 0x5d, 0x7b, 0x31, 0x2c, 0x36, 0x32, 0x7d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, + 0x2d, 0x39, 0x5d, 0x29, 0x24, 0x48, 0x00, 0x52, 0x08, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x52, 0x0a, 0x0f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x77, 0x69, 0x6c, 0x64, + 0x63, 0x61, 0x72, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x63, 0x6f, 0x72, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x52, 0x65, 0x6c, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x57, 0x69, 0x6c, 0x64, 0x63, + 0x61, 0x72, 0x64, 0x48, 0x00, 0x52, 0x0e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x57, 0x69, 0x6c, + 0x64, 0x63, 0x61, 0x72, 0x64, 0x12, 0x40, 0x0a, 0x0f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, + 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, + 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, + 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, + 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3f, 0x0a, 0x0f, 0x72, 0x65, 0x71, 0x75, 0x69, + 0x72, 0x65, 0x64, 0x5f, 0x63, 0x61, 0x76, 0x65, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x16, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x77, + 0x65, 0x64, 0x43, 0x61, 0x76, 0x65, 0x61, 0x74, 0x52, 0x0e, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, + 0x65, 0x64, 0x43, 0x61, 0x76, 0x65, 0x61, 0x74, 0x1a, 0x10, 0x0a, 0x0e, 0x50, 0x75, 0x62, 0x6c, + 0x69, 0x63, 0x57, 0x69, 0x6c, 0x64, 0x63, 0x61, 0x72, 0x64, 0x42, 0x16, 0x0a, 0x14, 0x72, 0x65, + 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6f, 0x72, 0x5f, 0x77, 0x69, 0x6c, 0x64, 0x63, 0x61, + 0x72, 0x64, 0x22, 0x30, 0x0a, 0x0d, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x43, 0x61, 0x76, + 0x65, 0x61, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x61, 0x76, 0x65, 0x61, 0x74, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x61, 0x76, 0x65, 0x61, 0x74, + 0x4e, 0x61, 0x6d, 0x65, 0x22, 0xad, 0x02, 0x0a, 0x0e, 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, + 0x52, 0x65, 0x77, 0x72, 0x69, 0x74, 0x65, 0x12, 0x37, 0x0a, 0x05, 0x75, 0x6e, 0x69, 0x6f, 0x6e, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x53, 0x65, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x08, 0xfa, + 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x48, 0x00, 0x52, 0x05, 0x75, 0x6e, 0x69, 0x6f, 0x6e, + 0x12, 0x45, 0x0a, 0x0c, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x53, 0x65, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x08, 0xfa, + 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x48, 0x00, 0x52, 0x0c, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x73, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3f, 0x0a, 0x09, 0x65, 0x78, 0x63, 0x6c, 0x75, + 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x48, 0x00, 0x52, 0x0c, 0x69, - 0x6e, 0x74, 0x65, 0x72, 0x73, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3f, 0x0a, 0x09, 0x65, - 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, - 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x4f, 0x70, 0x65, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x48, - 0x00, 0x52, 0x09, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x40, 0x0a, 0x0f, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, + 0x6e, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x48, 0x00, 0x52, 0x09, 0x65, + 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x40, 0x0a, 0x0f, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x18, 0x0a, 0x11, 0x72, 0x65, + 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x03, 0xf8, 0x42, 0x01, 0x22, 0xb2, 0x05, 0x0a, 0x0c, 0x53, 0x65, 0x74, 0x4f, 0x70, 0x65, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x42, 0x0a, 0x05, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, + 0x65, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x68, 0x69, 0x6c, + 0x64, 0x42, 0x0f, 0xfa, 0x42, 0x0c, 0x92, 0x01, 0x09, 0x08, 0x01, 0x22, 0x05, 0x8a, 0x01, 0x02, + 0x10, 0x01, 0x52, 0x05, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x1a, 0xdd, 0x04, 0x0a, 0x05, 0x43, 0x68, + 0x69, 0x6c, 0x64, 0x12, 0x37, 0x0a, 0x05, 0x5f, 0x74, 0x68, 0x69, 0x73, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, + 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x68, 0x69, 0x6c, 0x64, 0x2e, + 0x54, 0x68, 0x69, 0x73, 0x48, 0x00, 0x52, 0x04, 0x54, 0x68, 0x69, 0x73, 0x12, 0x4f, 0x0a, 0x10, + 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, + 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x48, 0x00, 0x52, 0x0f, 0x63, 0x6f, + 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x12, 0x4d, 0x0a, + 0x10, 0x74, 0x75, 0x70, 0x6c, 0x65, 0x5f, 0x74, 0x6f, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x73, 0x65, + 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x54, 0x6f, 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, + 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x48, 0x00, 0x52, 0x0e, 0x74, 0x75, + 0x70, 0x6c, 0x65, 0x54, 0x6f, 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x12, 0x4c, 0x0a, 0x0f, + 0x75, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x5f, 0x72, 0x65, 0x77, 0x72, 0x69, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, - 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0e, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x18, - 0x0a, 0x11, 0x72, 0x65, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x03, 0xf8, 0x42, 0x01, 0x22, 0xb2, 0x05, 0x0a, 0x0c, 0x53, 0x65, 0x74, - 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x42, 0x0a, 0x05, 0x63, 0x68, 0x69, - 0x6c, 0x64, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, - 0x43, 0x68, 0x69, 0x6c, 0x64, 0x42, 0x0f, 0xfa, 0x42, 0x0c, 0x92, 0x01, 0x09, 0x08, 0x01, 0x22, - 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x05, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x1a, 0xdd, 0x04, - 0x0a, 0x05, 0x43, 0x68, 0x69, 0x6c, 0x64, 0x12, 0x37, 0x0a, 0x05, 0x5f, 0x74, 0x68, 0x69, 0x73, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, + 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x52, 0x65, 0x77, 0x72, 0x69, 0x74, 0x65, 0x42, 0x08, + 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x48, 0x00, 0x52, 0x0e, 0x75, 0x73, 0x65, 0x72, + 0x73, 0x65, 0x74, 0x52, 0x65, 0x77, 0x72, 0x69, 0x74, 0x65, 0x12, 0x6c, 0x0a, 0x1b, 0x66, 0x75, + 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x64, 0x5f, 0x74, 0x75, 0x70, 0x6c, 0x65, 0x5f, 0x74, + 0x6f, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x21, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x65, 0x64, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x54, 0x6f, 0x55, 0x73, 0x65, 0x72, 0x73, + 0x65, 0x74, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x48, 0x00, 0x52, 0x18, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x64, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x54, + 0x6f, 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x12, 0x34, 0x0a, 0x04, 0x5f, 0x6e, 0x69, 0x6c, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x68, - 0x69, 0x6c, 0x64, 0x2e, 0x54, 0x68, 0x69, 0x73, 0x48, 0x00, 0x52, 0x04, 0x54, 0x68, 0x69, 0x73, - 0x12, 0x4f, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x5f, 0x75, 0x73, 0x65, - 0x72, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x63, 0x6f, 0x72, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x55, 0x73, 0x65, - 0x72, 0x73, 0x65, 0x74, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x48, 0x00, - 0x52, 0x0f, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, - 0x74, 0x12, 0x4d, 0x0a, 0x10, 0x74, 0x75, 0x70, 0x6c, 0x65, 0x5f, 0x74, 0x6f, 0x5f, 0x75, 0x73, - 0x65, 0x72, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6f, - 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x54, 0x6f, 0x55, 0x73, 0x65, - 0x72, 0x73, 0x65, 0x74, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x48, 0x00, - 0x52, 0x0e, 0x74, 0x75, 0x70, 0x6c, 0x65, 0x54, 0x6f, 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, - 0x12, 0x4c, 0x0a, 0x0f, 0x75, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x5f, 0x72, 0x65, 0x77, 0x72, - 0x69, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x72, 0x65, - 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x52, 0x65, 0x77, 0x72, 0x69, - 0x74, 0x65, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x48, 0x00, 0x52, 0x0e, - 0x75, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x52, 0x65, 0x77, 0x72, 0x69, 0x74, 0x65, 0x12, 0x6c, - 0x0a, 0x1b, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x64, 0x5f, 0x74, 0x75, 0x70, - 0x6c, 0x65, 0x5f, 0x74, 0x6f, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x18, 0x08, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x75, - 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x64, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x54, 0x6f, 0x55, - 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, - 0x48, 0x00, 0x52, 0x18, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x64, 0x54, 0x75, - 0x70, 0x6c, 0x65, 0x54, 0x6f, 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x12, 0x34, 0x0a, 0x04, - 0x5f, 0x6e, 0x69, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x63, 0x6f, 0x72, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x2e, 0x43, 0x68, 0x69, 0x6c, 0x64, 0x2e, 0x4e, 0x69, 0x6c, 0x48, 0x00, 0x52, 0x03, 0x4e, - 0x69, 0x6c, 0x12, 0x40, 0x0a, 0x0f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x70, 0x6f, 0x73, - 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6f, - 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x73, 0x69, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x73, 0x69, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0d, 0x6f, 0x70, - 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x74, 0x68, 0x1a, 0x06, 0x0a, 0x04, 0x54, - 0x68, 0x69, 0x73, 0x1a, 0x05, 0x0a, 0x03, 0x4e, 0x69, 0x6c, 0x42, 0x11, 0x0a, 0x0a, 0x63, 0x68, - 0x69, 0x6c, 0x64, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x12, 0x03, 0xf8, 0x42, 0x01, 0x22, 0xba, 0x02, - 0x0a, 0x0e, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x54, 0x6f, 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, - 0x12, 0x46, 0x0a, 0x08, 0x74, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x75, 0x70, - 0x6c, 0x65, 0x54, 0x6f, 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x2e, 0x54, 0x75, 0x70, 0x6c, - 0x65, 0x73, 0x65, 0x74, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x08, - 0x74, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x65, 0x74, 0x12, 0x4d, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x70, - 0x75, 0x74, 0x65, 0x64, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, - 0x70, 0x75, 0x74, 0x65, 0x64, 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x42, 0x08, 0xfa, 0x42, - 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x0f, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, - 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x12, 0x40, 0x0a, 0x0f, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x4f, 0x0a, 0x08, 0x54, 0x75, 0x70, - 0x6c, 0x65, 0x73, 0x65, 0x74, 0x12, 0x43, 0x0a, 0x08, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x27, 0xfa, 0x42, 0x24, 0x72, 0x22, 0x28, 0x40, - 0x32, 0x1e, 0x5e, 0x5b, 0x61, 0x2d, 0x7a, 0x5d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5f, - 0x5d, 0x7b, 0x31, 0x2c, 0x36, 0x32, 0x7d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x24, - 0x52, 0x08, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xec, 0x03, 0x0a, 0x18, 0x46, - 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x64, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x54, 0x6f, - 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x12, 0x52, 0x0a, 0x08, 0x66, 0x75, 0x6e, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x72, 0x65, + 0x69, 0x6c, 0x64, 0x2e, 0x4e, 0x69, 0x6c, 0x48, 0x00, 0x52, 0x03, 0x4e, 0x69, 0x6c, 0x12, 0x40, + 0x0a, 0x0f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x25, 0x0a, 0x0e, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x61, + 0x74, 0x68, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0d, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x74, 0x68, 0x1a, 0x06, 0x0a, 0x04, 0x54, 0x68, 0x69, 0x73, 0x1a, + 0x05, 0x0a, 0x03, 0x4e, 0x69, 0x6c, 0x42, 0x11, 0x0a, 0x0a, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x12, 0x03, 0xf8, 0x42, 0x01, 0x22, 0xba, 0x02, 0x0a, 0x0e, 0x54, 0x75, + 0x70, 0x6c, 0x65, 0x54, 0x6f, 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x12, 0x46, 0x0a, 0x08, + 0x74, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, + 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x54, 0x6f, + 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x2e, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x65, 0x74, + 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x08, 0x74, 0x75, 0x70, 0x6c, + 0x65, 0x73, 0x65, 0x74, 0x12, 0x4d, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, + 0x5f, 0x75, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, + 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, + 0x64, 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, + 0x10, 0x01, 0x52, 0x0f, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x55, 0x73, 0x65, 0x72, + 0x73, 0x65, 0x74, 0x12, 0x40, 0x0a, 0x0f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x70, 0x6f, + 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, + 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x73, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x73, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x4f, 0x0a, 0x08, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x65, + 0x74, 0x12, 0x43, 0x0a, 0x08, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x42, 0x27, 0xfa, 0x42, 0x24, 0x72, 0x22, 0x28, 0x40, 0x32, 0x1e, 0x5e, 0x5b, + 0x61, 0x2d, 0x7a, 0x5d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5f, 0x5d, 0x7b, 0x31, 0x2c, + 0x36, 0x32, 0x7d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x24, 0x52, 0x08, 0x72, 0x65, + 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xec, 0x03, 0x0a, 0x18, 0x46, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x65, 0x64, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x54, 0x6f, 0x55, 0x73, 0x65, 0x72, + 0x73, 0x65, 0x74, 0x12, 0x52, 0x0a, 0x08, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, + 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x64, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x54, + 0x6f, 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x2e, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x42, 0x0a, 0xfa, 0x42, 0x07, 0x82, 0x01, 0x04, 0x10, 0x01, 0x20, 0x00, 0x52, 0x08, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x50, 0x0a, 0x08, 0x74, 0x75, 0x70, 0x6c, 0x65, + 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x64, 0x54, 0x75, - 0x70, 0x6c, 0x65, 0x54, 0x6f, 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x2e, 0x46, 0x75, 0x6e, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x0a, 0xfa, 0x42, 0x07, 0x82, 0x01, 0x04, 0x10, 0x01, 0x20, - 0x00, 0x52, 0x08, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x50, 0x0a, 0x08, 0x74, - 0x75, 0x70, 0x6c, 0x65, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, - 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x65, 0x64, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x54, 0x6f, 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, - 0x2e, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x65, 0x74, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, - 0x02, 0x10, 0x01, 0x52, 0x08, 0x74, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x65, 0x74, 0x12, 0x4d, 0x0a, - 0x10, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x73, 0x65, - 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, - 0x74, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x0f, 0x63, 0x6f, 0x6d, - 0x70, 0x75, 0x74, 0x65, 0x64, 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x12, 0x40, 0x0a, 0x0f, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, - 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0e, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x4f, - 0x0a, 0x08, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x65, 0x74, 0x12, 0x43, 0x0a, 0x08, 0x72, 0x65, - 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x27, 0xfa, 0x42, - 0x24, 0x72, 0x22, 0x28, 0x40, 0x32, 0x1e, 0x5e, 0x5b, 0x61, 0x2d, 0x7a, 0x5d, 0x5b, 0x61, 0x2d, + 0x70, 0x6c, 0x65, 0x54, 0x6f, 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x2e, 0x54, 0x75, 0x70, + 0x6c, 0x65, 0x73, 0x65, 0x74, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, + 0x08, 0x74, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x65, 0x74, 0x12, 0x4d, 0x0a, 0x10, 0x63, 0x6f, 0x6d, + 0x70, 0x75, 0x74, 0x65, 0x64, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, + 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x42, 0x08, 0xfa, + 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x0f, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, + 0x64, 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x12, 0x40, 0x0a, 0x0f, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x4f, 0x0a, 0x08, 0x54, 0x75, + 0x70, 0x6c, 0x65, 0x73, 0x65, 0x74, 0x12, 0x43, 0x0a, 0x08, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x27, 0xfa, 0x42, 0x24, 0x72, 0x22, 0x28, + 0x40, 0x32, 0x1e, 0x5e, 0x5b, 0x61, 0x2d, 0x7a, 0x5d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, + 0x5f, 0x5d, 0x7b, 0x31, 0x2c, 0x36, 0x32, 0x7d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, + 0x24, 0x52, 0x08, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x48, 0x0a, 0x08, 0x46, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x14, 0x46, 0x55, 0x4e, 0x43, 0x54, + 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, + 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x46, 0x55, 0x4e, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x41, 0x4e, + 0x59, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x46, 0x55, 0x4e, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, + 0x41, 0x4c, 0x4c, 0x10, 0x02, 0x22, 0x91, 0x02, 0x0a, 0x0f, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, + 0x65, 0x64, 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x12, 0x41, 0x0a, 0x06, 0x6f, 0x62, 0x6a, + 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x63, 0x6f, 0x72, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x55, 0x73, 0x65, 0x72, + 0x73, 0x65, 0x74, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x82, + 0x01, 0x02, 0x10, 0x01, 0x52, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x43, 0x0a, 0x08, + 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x27, + 0xfa, 0x42, 0x24, 0x72, 0x22, 0x28, 0x40, 0x32, 0x1e, 0x5e, 0x5b, 0x61, 0x2d, 0x7a, 0x5d, 0x5b, + 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5f, 0x5d, 0x7b, 0x31, 0x2c, 0x36, 0x32, 0x7d, 0x5b, 0x61, + 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x24, 0x52, 0x08, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x40, 0x0a, 0x0f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x70, 0x6f, 0x73, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x72, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x22, 0x34, 0x0a, 0x06, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x10, 0x0a, + 0x0c, 0x54, 0x55, 0x50, 0x4c, 0x45, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x10, 0x00, 0x12, + 0x18, 0x0a, 0x14, 0x54, 0x55, 0x50, 0x4c, 0x45, 0x5f, 0x55, 0x53, 0x45, 0x52, 0x53, 0x45, 0x54, + 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x10, 0x01, 0x22, 0x8a, 0x01, 0x0a, 0x0e, 0x53, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x37, 0x0a, 0x18, + 0x7a, 0x65, 0x72, 0x6f, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x64, 0x5f, 0x6c, 0x69, 0x6e, + 0x65, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x15, + 0x7a, 0x65, 0x72, 0x6f, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x64, 0x4c, 0x69, 0x6e, 0x65, 0x4e, + 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x3f, 0x0a, 0x1c, 0x7a, 0x65, 0x72, 0x6f, 0x5f, 0x69, 0x6e, + 0x64, 0x65, 0x78, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x5f, 0x70, 0x6f, 0x73, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x19, 0x7a, 0x65, 0x72, + 0x6f, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x64, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x50, 0x6f, + 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x9c, 0x01, 0x0a, 0x10, 0x43, 0x61, 0x76, 0x65, 0x61, + 0x74, 0x45, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x38, 0x0a, 0x09, 0x6f, + 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, + 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x76, 0x65, 0x61, 0x74, 0x4f, + 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x09, 0x6f, 0x70, 0x65, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x37, 0x0a, 0x06, 0x63, 0x61, 0x76, 0x65, 0x61, 0x74, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, + 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x75, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x43, 0x61, + 0x76, 0x65, 0x61, 0x74, 0x48, 0x00, 0x52, 0x06, 0x63, 0x61, 0x76, 0x65, 0x61, 0x74, 0x42, 0x15, + 0x0a, 0x13, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6f, 0x72, 0x5f, 0x63, + 0x61, 0x76, 0x65, 0x61, 0x74, 0x22, 0xb0, 0x01, 0x0a, 0x0f, 0x43, 0x61, 0x76, 0x65, 0x61, 0x74, + 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x32, 0x0a, 0x02, 0x6f, 0x70, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, + 0x43, 0x61, 0x76, 0x65, 0x61, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, + 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x02, 0x6f, 0x70, 0x12, 0x35, 0x0a, + 0x08, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x72, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x19, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x76, 0x65, 0x61, 0x74, + 0x45, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x63, 0x68, 0x69, 0x6c, + 0x64, 0x72, 0x65, 0x6e, 0x22, 0x32, 0x0a, 0x09, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x06, + 0x0a, 0x02, 0x4f, 0x52, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x4e, 0x44, 0x10, 0x02, 0x12, + 0x07, 0x0a, 0x03, 0x4e, 0x4f, 0x54, 0x10, 0x03, 0x22, 0xee, 0x03, 0x0a, 0x12, 0x52, 0x65, 0x6c, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x68, 0x69, 0x70, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, + 0x70, 0x0a, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x4b, 0xfa, 0x42, 0x48, 0x72, 0x46, 0x28, 0x80, 0x01, + 0x32, 0x41, 0x5e, 0x28, 0x28, 0x5b, 0x61, 0x2d, 0x7a, 0x5d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, + 0x39, 0x5f, 0x5d, 0x7b, 0x31, 0x2c, 0x36, 0x31, 0x7d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, + 0x5d, 0x2f, 0x29, 0x2a, 0x5b, 0x61, 0x2d, 0x7a, 0x5d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, + 0x5f, 0x5d, 0x7b, 0x31, 0x2c, 0x36, 0x32, 0x7d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, + 0x29, 0x3f, 0x24, 0x52, 0x0c, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x79, 0x70, + 0x65, 0x12, 0x57, 0x0a, 0x14, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, + 0x25, 0xfa, 0x42, 0x22, 0x72, 0x20, 0x28, 0x80, 0x08, 0x32, 0x1b, 0x5e, 0x28, 0x5b, 0x61, 0x2d, + 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x2f, 0x5f, 0x7c, 0x5c, 0x2d, 0x3d, 0x2b, 0x5d, 0x7b, + 0x31, 0x2c, 0x7d, 0x29, 0x3f, 0x24, 0x52, 0x12, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x64, 0x0a, 0x1b, 0x6f, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, + 0x69, 0x64, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, + 0x25, 0xfa, 0x42, 0x22, 0x72, 0x20, 0x28, 0x80, 0x08, 0x32, 0x1b, 0x5e, 0x28, 0x5b, 0x61, 0x2d, + 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x2f, 0x5f, 0x7c, 0x5c, 0x2d, 0x3d, 0x2b, 0x5d, 0x7b, + 0x31, 0x2c, 0x7d, 0x29, 0x3f, 0x24, 0x52, 0x18, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, + 0x12, 0x57, 0x0a, 0x11, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x72, 0x65, 0x6c, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x2a, 0xfa, 0x42, 0x27, + 0x72, 0x25, 0x28, 0x40, 0x32, 0x21, 0x5e, 0x28, 0x5b, 0x61, 0x2d, 0x7a, 0x5d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5f, 0x5d, 0x7b, 0x31, 0x2c, 0x36, 0x32, 0x7d, 0x5b, 0x61, 0x2d, 0x7a, - 0x30, 0x2d, 0x39, 0x5d, 0x24, 0x52, 0x08, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, - 0x48, 0x0a, 0x08, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x14, 0x46, - 0x55, 0x4e, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, - 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x46, 0x55, 0x4e, 0x43, 0x54, 0x49, 0x4f, - 0x4e, 0x5f, 0x41, 0x4e, 0x59, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x46, 0x55, 0x4e, 0x43, 0x54, - 0x49, 0x4f, 0x4e, 0x5f, 0x41, 0x4c, 0x4c, 0x10, 0x02, 0x22, 0x91, 0x02, 0x0a, 0x0f, 0x43, 0x6f, - 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x12, 0x41, 0x0a, - 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, - 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, - 0x55, 0x73, 0x65, 0x72, 0x73, 0x65, 0x74, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x42, 0x08, - 0xfa, 0x42, 0x05, 0x82, 0x01, 0x02, 0x10, 0x01, 0x52, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, - 0x12, 0x43, 0x0a, 0x08, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x42, 0x27, 0xfa, 0x42, 0x24, 0x72, 0x22, 0x28, 0x40, 0x32, 0x1e, 0x5e, 0x5b, 0x61, + 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x3f, 0x24, 0x52, 0x10, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, + 0x6c, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x4e, 0x0a, 0x17, 0x6f, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x66, 0x69, + 0x6c, 0x74, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x6f, 0x72, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x46, 0x69, 0x6c, 0x74, + 0x65, 0x72, 0x52, 0x15, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x53, 0x75, 0x62, 0x6a, + 0x65, 0x63, 0x74, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x22, 0x86, 0x03, 0x0a, 0x0d, 0x53, 0x75, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x6b, 0x0a, 0x0c, 0x73, + 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x42, 0x48, 0xfa, 0x42, 0x45, 0x72, 0x43, 0x28, 0x80, 0x01, 0x32, 0x3e, 0x5e, 0x28, 0x5b, + 0x61, 0x2d, 0x7a, 0x5d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5f, 0x5d, 0x7b, 0x31, 0x2c, + 0x36, 0x31, 0x7d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x2f, 0x29, 0x2a, 0x5b, 0x61, 0x2d, 0x7a, 0x5d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5f, 0x5d, 0x7b, 0x31, 0x2c, 0x36, - 0x32, 0x7d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x24, 0x52, 0x08, 0x72, 0x65, 0x6c, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x40, 0x0a, 0x0f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, - 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, - 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, - 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, - 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x34, 0x0a, 0x06, 0x4f, 0x62, 0x6a, 0x65, 0x63, - 0x74, 0x12, 0x10, 0x0a, 0x0c, 0x54, 0x55, 0x50, 0x4c, 0x45, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, - 0x54, 0x10, 0x00, 0x12, 0x18, 0x0a, 0x14, 0x54, 0x55, 0x50, 0x4c, 0x45, 0x5f, 0x55, 0x53, 0x45, - 0x52, 0x53, 0x45, 0x54, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x10, 0x01, 0x22, 0x8a, 0x01, - 0x0a, 0x0e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x37, 0x0a, 0x18, 0x7a, 0x65, 0x72, 0x6f, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x64, - 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x15, 0x7a, 0x65, 0x72, 0x6f, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x64, 0x4c, - 0x69, 0x6e, 0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x3f, 0x0a, 0x1c, 0x7a, 0x65, 0x72, - 0x6f, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, - 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x19, 0x7a, 0x65, 0x72, 0x6f, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x64, 0x43, 0x6f, 0x6c, 0x75, - 0x6d, 0x6e, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x9c, 0x01, 0x0a, 0x10, 0x43, - 0x61, 0x76, 0x65, 0x61, 0x74, 0x45, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, - 0x38, 0x0a, 0x09, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x76, - 0x65, 0x61, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x09, - 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x37, 0x0a, 0x06, 0x63, 0x61, 0x76, - 0x65, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x63, 0x6f, 0x72, 0x65, - 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x75, 0x61, 0x6c, 0x69, 0x7a, - 0x65, 0x64, 0x43, 0x61, 0x76, 0x65, 0x61, 0x74, 0x48, 0x00, 0x52, 0x06, 0x63, 0x61, 0x76, 0x65, - 0x61, 0x74, 0x42, 0x15, 0x0a, 0x13, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, - 0x6f, 0x72, 0x5f, 0x63, 0x61, 0x76, 0x65, 0x61, 0x74, 0x22, 0xb0, 0x01, 0x0a, 0x0f, 0x43, 0x61, - 0x76, 0x65, 0x61, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x32, 0x0a, - 0x02, 0x6f, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x63, 0x6f, 0x72, 0x65, - 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x76, 0x65, 0x61, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x02, 0x6f, - 0x70, 0x12, 0x35, 0x0a, 0x08, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x72, 0x65, 0x6e, 0x18, 0x02, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, - 0x76, 0x65, 0x61, 0x74, 0x45, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x08, - 0x63, 0x68, 0x69, 0x6c, 0x64, 0x72, 0x65, 0x6e, 0x22, 0x32, 0x0a, 0x09, 0x4f, 0x70, 0x65, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, - 0x10, 0x00, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x52, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x4e, - 0x44, 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x4e, 0x4f, 0x54, 0x10, 0x03, 0x22, 0xee, 0x03, 0x0a, - 0x12, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x68, 0x69, 0x70, 0x46, 0x69, 0x6c, - 0x74, 0x65, 0x72, 0x12, 0x70, 0x0a, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, - 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x4b, 0xfa, 0x42, 0x48, 0x72, - 0x46, 0x28, 0x80, 0x01, 0x32, 0x41, 0x5e, 0x28, 0x28, 0x5b, 0x61, 0x2d, 0x7a, 0x5d, 0x5b, 0x61, - 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5f, 0x5d, 0x7b, 0x31, 0x2c, 0x36, 0x31, 0x7d, 0x5b, 0x61, 0x2d, - 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x2f, 0x29, 0x2a, 0x5b, 0x61, 0x2d, 0x7a, 0x5d, 0x5b, 0x61, 0x2d, - 0x7a, 0x30, 0x2d, 0x39, 0x5f, 0x5d, 0x7b, 0x31, 0x2c, 0x36, 0x32, 0x7d, 0x5b, 0x61, 0x2d, 0x7a, - 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x3f, 0x24, 0x52, 0x0c, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x57, 0x0a, 0x14, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, - 0x6c, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x42, 0x25, 0xfa, 0x42, 0x22, 0x72, 0x20, 0x28, 0x80, 0x08, 0x32, 0x1b, 0x5e, - 0x28, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x2f, 0x5f, 0x7c, 0x5c, 0x2d, - 0x3d, 0x2b, 0x5d, 0x7b, 0x31, 0x2c, 0x7d, 0x29, 0x3f, 0x24, 0x52, 0x12, 0x6f, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x64, - 0x0a, 0x1b, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x09, 0x42, 0x25, 0xfa, 0x42, 0x22, 0x72, 0x20, 0x28, 0x80, 0x08, 0x32, 0x1b, 0x5e, - 0x28, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x2f, 0x5f, 0x7c, 0x5c, 0x2d, - 0x3d, 0x2b, 0x5d, 0x7b, 0x31, 0x2c, 0x7d, 0x29, 0x3f, 0x24, 0x52, 0x18, 0x6f, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x50, 0x72, - 0x65, 0x66, 0x69, 0x78, 0x12, 0x57, 0x0a, 0x11, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, - 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, - 0x2a, 0xfa, 0x42, 0x27, 0x72, 0x25, 0x28, 0x40, 0x32, 0x21, 0x5e, 0x28, 0x5b, 0x61, 0x2d, 0x7a, - 0x5d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5f, 0x5d, 0x7b, 0x31, 0x2c, 0x36, 0x32, 0x7d, - 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x3f, 0x24, 0x52, 0x10, 0x6f, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x4e, 0x0a, - 0x17, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, - 0x74, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, - 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, - 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x15, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, - 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x22, 0x86, 0x03, - 0x0a, 0x0d, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, - 0x6b, 0x0a, 0x0c, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x48, 0xfa, 0x42, 0x45, 0x72, 0x43, 0x28, 0x80, 0x01, 0x32, - 0x3e, 0x5e, 0x28, 0x5b, 0x61, 0x2d, 0x7a, 0x5d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5f, - 0x5d, 0x7b, 0x31, 0x2c, 0x36, 0x31, 0x7d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x2f, - 0x29, 0x2a, 0x5b, 0x61, 0x2d, 0x7a, 0x5d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5f, 0x5d, - 0x7b, 0x31, 0x2c, 0x36, 0x32, 0x7d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x24, 0x52, - 0x0b, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x5a, 0x0a, 0x13, - 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, - 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x2a, 0xfa, 0x42, 0x27, 0x72, 0x25, - 0x28, 0x80, 0x08, 0x32, 0x20, 0x5e, 0x28, 0x28, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, - 0x2d, 0x39, 0x2f, 0x5f, 0x7c, 0x5c, 0x2d, 0x3d, 0x2b, 0x5d, 0x7b, 0x31, 0x2c, 0x7d, 0x29, 0x7c, - 0x5c, 0x2a, 0x29, 0x3f, 0x24, 0x52, 0x11, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x53, - 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x12, 0x52, 0x0a, 0x11, 0x6f, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, - 0x62, 0x6a, 0x65, 0x63, 0x74, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x6c, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x10, 0x6f, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x61, 0x6c, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x58, 0x0a, 0x0e, - 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x46, - 0x0a, 0x08, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x42, 0x2a, 0xfa, 0x42, 0x27, 0x72, 0x25, 0x28, 0x40, 0x32, 0x21, 0x5e, 0x28, 0x5b, 0x61, 0x2d, - 0x7a, 0x5d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5f, 0x5d, 0x7b, 0x31, 0x2c, 0x36, 0x32, - 0x7d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x3f, 0x24, 0x52, 0x08, 0x72, 0x65, - 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x8a, 0x01, 0x0a, 0x0b, 0x63, 0x6f, 0x6d, 0x2e, 0x63, - 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x42, 0x09, 0x43, 0x6f, 0x72, 0x65, 0x50, 0x72, 0x6f, 0x74, - 0x6f, 0x50, 0x01, 0x5a, 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x61, 0x75, 0x74, 0x68, 0x7a, 0x65, 0x64, 0x2f, 0x73, 0x70, 0x69, 0x63, 0x65, 0x64, 0x62, 0x2f, - 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76, - 0x31, 0x3b, 0x63, 0x6f, 0x72, 0x65, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x43, 0x58, 0x58, 0xaa, 0x02, - 0x07, 0x43, 0x6f, 0x72, 0x65, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x07, 0x43, 0x6f, 0x72, 0x65, 0x5c, - 0x56, 0x31, 0xe2, 0x02, 0x13, 0x43, 0x6f, 0x72, 0x65, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, - 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x08, 0x43, 0x6f, 0x72, 0x65, 0x3a, - 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x32, 0x7d, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x24, 0x52, 0x0b, 0x73, 0x75, 0x62, + 0x6a, 0x65, 0x63, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x5a, 0x0a, 0x13, 0x6f, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x2a, 0xfa, 0x42, 0x27, 0x72, 0x25, 0x28, 0x80, 0x08, 0x32, + 0x20, 0x5e, 0x28, 0x28, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x2f, 0x5f, + 0x7c, 0x5c, 0x2d, 0x3d, 0x2b, 0x5d, 0x7b, 0x31, 0x2c, 0x7d, 0x29, 0x7c, 0x5c, 0x2a, 0x29, 0x3f, + 0x24, 0x52, 0x11, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x53, 0x75, 0x62, 0x6a, 0x65, + 0x63, 0x74, 0x49, 0x64, 0x12, 0x52, 0x0a, 0x11, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, + 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x25, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, + 0x74, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x10, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, + 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x58, 0x0a, 0x0e, 0x52, 0x65, 0x6c, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x46, 0x0a, 0x08, 0x72, 0x65, + 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x2a, 0xfa, 0x42, + 0x27, 0x72, 0x25, 0x28, 0x40, 0x32, 0x21, 0x5e, 0x28, 0x5b, 0x61, 0x2d, 0x7a, 0x5d, 0x5b, 0x61, + 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5f, 0x5d, 0x7b, 0x31, 0x2c, 0x36, 0x32, 0x7d, 0x5b, 0x61, 0x2d, + 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x3f, 0x24, 0x52, 0x08, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x42, 0x8a, 0x01, 0x0a, 0x0b, 0x63, 0x6f, 0x6d, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x76, 0x31, 0x42, 0x09, 0x43, 0x6f, 0x72, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, + 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x75, 0x74, 0x68, + 0x7a, 0x65, 0x64, 0x2f, 0x73, 0x70, 0x69, 0x63, 0x65, 0x64, 0x62, 0x2f, 0x70, 0x6b, 0x67, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x63, 0x6f, + 0x72, 0x65, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x43, 0x58, 0x58, 0xaa, 0x02, 0x07, 0x43, 0x6f, 0x72, + 0x65, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x07, 0x43, 0x6f, 0x72, 0x65, 0x5c, 0x56, 0x31, 0xe2, 0x02, + 0x13, 0x43, 0x6f, 0x72, 0x65, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, + 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x08, 0x43, 0x6f, 0x72, 0x65, 0x3a, 0x3a, 0x56, 0x31, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/pkg/proto/core/v1/core.pb.validate.go b/pkg/proto/core/v1/core.pb.validate.go index 66bdbdd411..1c5d9896ed 100644 --- a/pkg/proto/core/v1/core.pb.validate.go +++ b/pkg/proto/core/v1/core.pb.validate.go @@ -3086,6 +3086,8 @@ func (m *ReachabilityEntrypoint) validate(all bool) error { // no validation rules for TuplesetRelation + // no validation rules for ComputedUsersetRelation + if len(errors) > 0 { return ReachabilityEntrypointMultiError(errors) } diff --git a/pkg/proto/core/v1/core_vtproto.pb.go b/pkg/proto/core/v1/core_vtproto.pb.go index b3b90c601c..cb4c57d1d6 100644 --- a/pkg/proto/core/v1/core_vtproto.pb.go +++ b/pkg/proto/core/v1/core_vtproto.pb.go @@ -429,6 +429,7 @@ func (m *ReachabilityEntrypoint) CloneVT() *ReachabilityEntrypoint { r.TargetRelation = m.TargetRelation.CloneVT() r.ResultStatus = m.ResultStatus r.TuplesetRelation = m.TuplesetRelation + r.ComputedUsersetRelation = m.ComputedUsersetRelation if len(m.unknownFields) > 0 { r.unknownFields = make([]byte, len(m.unknownFields)) copy(r.unknownFields, m.unknownFields) @@ -1572,6 +1573,9 @@ func (this *ReachabilityEntrypoint) EqualVT(that *ReachabilityEntrypoint) bool { if this.TuplesetRelation != that.TuplesetRelation { return false } + if this.ComputedUsersetRelation != that.ComputedUsersetRelation { + return false + } return string(this.unknownFields) == string(that.unknownFields) } @@ -3488,6 +3492,13 @@ func (m *ReachabilityEntrypoint) MarshalToSizedBufferVT(dAtA []byte) (int, error i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } + if len(m.ComputedUsersetRelation) > 0 { + i -= len(m.ComputedUsersetRelation) + copy(dAtA[i:], m.ComputedUsersetRelation) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ComputedUsersetRelation))) + i-- + dAtA[i] = 0x32 + } if len(m.TuplesetRelation) > 0 { i -= len(m.TuplesetRelation) copy(dAtA[i:], m.TuplesetRelation) @@ -5181,6 +5192,10 @@ func (m *ReachabilityEntrypoint) SizeVT() (n int) { if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } + l = len(m.ComputedUsersetRelation) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } n += len(m.unknownFields) return n } @@ -8534,6 +8549,38 @@ func (m *ReachabilityEntrypoint) UnmarshalVT(dAtA []byte) error { } m.TuplesetRelation = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ComputedUsersetRelation", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ComputedUsersetRelation = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) diff --git a/pkg/proto/dispatch/v1/00_zerolog.go b/pkg/proto/dispatch/v1/00_zerolog.go index be0041f0b9..cbd71671a2 100644 --- a/pkg/proto/dispatch/v1/00_zerolog.go +++ b/pkg/proto/dispatch/v1/00_zerolog.go @@ -45,6 +45,15 @@ func (lr *DispatchLookupResourcesRequest) MarshalZerologObject(e *zerolog.Event) e.Interface("context", lr.Context) } +// MarshalZerologObject implements zerolog object marshalling. +func (lr *DispatchLookupResources2Request) MarshalZerologObject(e *zerolog.Event) { + e.Object("metadata", lr.Metadata) + e.Str("resource", tuple.StringRR(lr.ResourceRelation)) + e.Str("subject", tuple.StringRR(lr.SubjectRelation)) + e.Array("subject-ids", strArray(lr.SubjectIds)) + e.Interface("context", lr.Context) +} + // MarshalZerologObject implements zerolog object marshalling. func (lr *DispatchReachableResourcesRequest) MarshalZerologObject(e *zerolog.Event) { e.Object("metadata", lr.Metadata) diff --git a/pkg/proto/dispatch/v1/dispatch.pb.go b/pkg/proto/dispatch/v1/dispatch.pb.go index 4b7ac5bddd..884dea7a4f 100644 --- a/pkg/proto/dispatch/v1/dispatch.pb.go +++ b/pkg/proto/dispatch/v1/dispatch.pb.go @@ -266,7 +266,7 @@ func (x ReachableResource_ResultStatus) Number() protoreflect.EnumNumber { // Deprecated: Use ReachableResource_ResultStatus.Descriptor instead. func (ReachableResource_ResultStatus) EnumDescriptor() ([]byte, []int) { - return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{7, 0} + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{10, 0} } type ResolvedResource_Permissionship int32 @@ -315,7 +315,7 @@ func (x ResolvedResource_Permissionship) Number() protoreflect.EnumNumber { // Deprecated: Use ResolvedResource_Permissionship.Descriptor instead. func (ResolvedResource_Permissionship) EnumDescriptor() ([]byte, []int) { - return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{10, 0} + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{13, 0} } type CheckDebugTrace_RelationType int32 @@ -364,7 +364,7 @@ func (x CheckDebugTrace_RelationType) Number() protoreflect.EnumNumber { // Deprecated: Use CheckDebugTrace_RelationType.Descriptor instead. func (CheckDebugTrace_RelationType) EnumDescriptor() ([]byte, []int) { - return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{19, 0} + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{22, 0} } type DispatchCheckRequest struct { @@ -378,6 +378,13 @@ type DispatchCheckRequest struct { Subject *v1.ObjectAndRelation `protobuf:"bytes,4,opt,name=subject,proto3" json:"subject,omitempty"` ResultsSetting DispatchCheckRequest_ResultsSetting `protobuf:"varint,5,opt,name=results_setting,json=resultsSetting,proto3,enum=dispatch.v1.DispatchCheckRequest_ResultsSetting" json:"results_setting,omitempty"` Debug DispatchCheckRequest_DebugSetting `protobuf:"varint,6,opt,name=debug,proto3,enum=dispatch.v1.DispatchCheckRequest_DebugSetting" json:"debug,omitempty"` + // * + // check_hints are hints provided to the check call to help the resolver optimize the check + // by skipping calculations for the provided checks. The string key is the fully qualified + // "relationtuple"-string for the problem, e.g. `document:example#relation@user:someuser`. + // It is up to the caller to *ensure* that the hints provided are correct; if incorrect, + // the resolver may return incorrect results which will in turn be cached. + CheckHints map[string]*ResourceCheckResult `protobuf:"bytes,7,rep,name=check_hints,json=checkHints,proto3" json:"check_hints,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } func (x *DispatchCheckRequest) Reset() { @@ -454,6 +461,13 @@ func (x *DispatchCheckRequest) GetDebug() DispatchCheckRequest_DebugSetting { return DispatchCheckRequest_NO_DEBUG } +func (x *DispatchCheckRequest) GetCheckHints() map[string]*ResourceCheckResult { + if x != nil { + return x.CheckHints + } + return nil +} + type DispatchCheckResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -745,6 +759,235 @@ func (x *Cursor) GetDispatchVersion() uint32 { return 0 } +type DispatchLookupResources2Request struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Metadata *ResolverMeta `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` + ResourceRelation *v1.RelationReference `protobuf:"bytes,2,opt,name=resource_relation,json=resourceRelation,proto3" json:"resource_relation,omitempty"` + SubjectRelation *v1.RelationReference `protobuf:"bytes,3,opt,name=subject_relation,json=subjectRelation,proto3" json:"subject_relation,omitempty"` + SubjectIds []string `protobuf:"bytes,4,rep,name=subject_ids,json=subjectIds,proto3" json:"subject_ids,omitempty"` + TerminalSubject *v1.ObjectAndRelation `protobuf:"bytes,5,opt,name=terminal_subject,json=terminalSubject,proto3" json:"terminal_subject,omitempty"` + Context *structpb.Struct `protobuf:"bytes,6,opt,name=context,proto3" json:"context,omitempty"` + OptionalCursor *Cursor `protobuf:"bytes,7,opt,name=optional_cursor,json=optionalCursor,proto3" json:"optional_cursor,omitempty"` + OptionalLimit uint32 `protobuf:"varint,8,opt,name=optional_limit,json=optionalLimit,proto3" json:"optional_limit,omitempty"` +} + +func (x *DispatchLookupResources2Request) Reset() { + *x = DispatchLookupResources2Request{} + if protoimpl.UnsafeEnabled { + mi := &file_dispatch_v1_dispatch_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DispatchLookupResources2Request) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DispatchLookupResources2Request) ProtoMessage() {} + +func (x *DispatchLookupResources2Request) ProtoReflect() protoreflect.Message { + mi := &file_dispatch_v1_dispatch_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DispatchLookupResources2Request.ProtoReflect.Descriptor instead. +func (*DispatchLookupResources2Request) Descriptor() ([]byte, []int) { + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{6} +} + +func (x *DispatchLookupResources2Request) GetMetadata() *ResolverMeta { + if x != nil { + return x.Metadata + } + return nil +} + +func (x *DispatchLookupResources2Request) GetResourceRelation() *v1.RelationReference { + if x != nil { + return x.ResourceRelation + } + return nil +} + +func (x *DispatchLookupResources2Request) GetSubjectRelation() *v1.RelationReference { + if x != nil { + return x.SubjectRelation + } + return nil +} + +func (x *DispatchLookupResources2Request) GetSubjectIds() []string { + if x != nil { + return x.SubjectIds + } + return nil +} + +func (x *DispatchLookupResources2Request) GetTerminalSubject() *v1.ObjectAndRelation { + if x != nil { + return x.TerminalSubject + } + return nil +} + +func (x *DispatchLookupResources2Request) GetContext() *structpb.Struct { + if x != nil { + return x.Context + } + return nil +} + +func (x *DispatchLookupResources2Request) GetOptionalCursor() *Cursor { + if x != nil { + return x.OptionalCursor + } + return nil +} + +func (x *DispatchLookupResources2Request) GetOptionalLimit() uint32 { + if x != nil { + return x.OptionalLimit + } + return 0 +} + +type PossibleResource struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ResourceId string `protobuf:"bytes,1,opt,name=resource_id,json=resourceId,proto3" json:"resource_id,omitempty"` + ForSubjectIds []string `protobuf:"bytes,2,rep,name=for_subject_ids,json=forSubjectIds,proto3" json:"for_subject_ids,omitempty"` + MissingContextParams []string `protobuf:"bytes,3,rep,name=missing_context_params,json=missingContextParams,proto3" json:"missing_context_params,omitempty"` +} + +func (x *PossibleResource) Reset() { + *x = PossibleResource{} + if protoimpl.UnsafeEnabled { + mi := &file_dispatch_v1_dispatch_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PossibleResource) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PossibleResource) ProtoMessage() {} + +func (x *PossibleResource) ProtoReflect() protoreflect.Message { + mi := &file_dispatch_v1_dispatch_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PossibleResource.ProtoReflect.Descriptor instead. +func (*PossibleResource) Descriptor() ([]byte, []int) { + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{7} +} + +func (x *PossibleResource) GetResourceId() string { + if x != nil { + return x.ResourceId + } + return "" +} + +func (x *PossibleResource) GetForSubjectIds() []string { + if x != nil { + return x.ForSubjectIds + } + return nil +} + +func (x *PossibleResource) GetMissingContextParams() []string { + if x != nil { + return x.MissingContextParams + } + return nil +} + +type DispatchLookupResources2Response struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Resource *PossibleResource `protobuf:"bytes,1,opt,name=resource,proto3" json:"resource,omitempty"` + Metadata *ResponseMeta `protobuf:"bytes,2,opt,name=metadata,proto3" json:"metadata,omitempty"` + AfterResponseCursor *Cursor `protobuf:"bytes,3,opt,name=after_response_cursor,json=afterResponseCursor,proto3" json:"after_response_cursor,omitempty"` +} + +func (x *DispatchLookupResources2Response) Reset() { + *x = DispatchLookupResources2Response{} + if protoimpl.UnsafeEnabled { + mi := &file_dispatch_v1_dispatch_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DispatchLookupResources2Response) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DispatchLookupResources2Response) ProtoMessage() {} + +func (x *DispatchLookupResources2Response) ProtoReflect() protoreflect.Message { + mi := &file_dispatch_v1_dispatch_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DispatchLookupResources2Response.ProtoReflect.Descriptor instead. +func (*DispatchLookupResources2Response) Descriptor() ([]byte, []int) { + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{8} +} + +func (x *DispatchLookupResources2Response) GetResource() *PossibleResource { + if x != nil { + return x.Resource + } + return nil +} + +func (x *DispatchLookupResources2Response) GetMetadata() *ResponseMeta { + if x != nil { + return x.Metadata + } + return nil +} + +func (x *DispatchLookupResources2Response) GetAfterResponseCursor() *Cursor { + if x != nil { + return x.AfterResponseCursor + } + return nil +} + type DispatchReachableResourcesRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -764,7 +1007,7 @@ type DispatchReachableResourcesRequest struct { func (x *DispatchReachableResourcesRequest) Reset() { *x = DispatchReachableResourcesRequest{} if protoimpl.UnsafeEnabled { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[6] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -777,7 +1020,7 @@ func (x *DispatchReachableResourcesRequest) String() string { func (*DispatchReachableResourcesRequest) ProtoMessage() {} func (x *DispatchReachableResourcesRequest) ProtoReflect() protoreflect.Message { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[6] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -790,7 +1033,7 @@ func (x *DispatchReachableResourcesRequest) ProtoReflect() protoreflect.Message // Deprecated: Use DispatchReachableResourcesRequest.ProtoReflect.Descriptor instead. func (*DispatchReachableResourcesRequest) Descriptor() ([]byte, []int) { - return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{6} + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{9} } func (x *DispatchReachableResourcesRequest) GetMetadata() *ResolverMeta { @@ -848,7 +1091,7 @@ type ReachableResource struct { func (x *ReachableResource) Reset() { *x = ReachableResource{} if protoimpl.UnsafeEnabled { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[7] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -861,7 +1104,7 @@ func (x *ReachableResource) String() string { func (*ReachableResource) ProtoMessage() {} func (x *ReachableResource) ProtoReflect() protoreflect.Message { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[7] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -874,7 +1117,7 @@ func (x *ReachableResource) ProtoReflect() protoreflect.Message { // Deprecated: Use ReachableResource.ProtoReflect.Descriptor instead. func (*ReachableResource) Descriptor() ([]byte, []int) { - return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{7} + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{10} } func (x *ReachableResource) GetResourceId() string { @@ -911,7 +1154,7 @@ type DispatchReachableResourcesResponse struct { func (x *DispatchReachableResourcesResponse) Reset() { *x = DispatchReachableResourcesResponse{} if protoimpl.UnsafeEnabled { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[8] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -924,7 +1167,7 @@ func (x *DispatchReachableResourcesResponse) String() string { func (*DispatchReachableResourcesResponse) ProtoMessage() {} func (x *DispatchReachableResourcesResponse) ProtoReflect() protoreflect.Message { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[8] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -937,7 +1180,7 @@ func (x *DispatchReachableResourcesResponse) ProtoReflect() protoreflect.Message // Deprecated: Use DispatchReachableResourcesResponse.ProtoReflect.Descriptor instead. func (*DispatchReachableResourcesResponse) Descriptor() ([]byte, []int) { - return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{8} + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{11} } func (x *DispatchReachableResourcesResponse) GetResource() *ReachableResource { @@ -980,7 +1223,7 @@ type DispatchLookupResourcesRequest struct { func (x *DispatchLookupResourcesRequest) Reset() { *x = DispatchLookupResourcesRequest{} if protoimpl.UnsafeEnabled { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[9] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -993,7 +1236,7 @@ func (x *DispatchLookupResourcesRequest) String() string { func (*DispatchLookupResourcesRequest) ProtoMessage() {} func (x *DispatchLookupResourcesRequest) ProtoReflect() protoreflect.Message { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[9] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1006,7 +1249,7 @@ func (x *DispatchLookupResourcesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DispatchLookupResourcesRequest.ProtoReflect.Descriptor instead. func (*DispatchLookupResourcesRequest) Descriptor() ([]byte, []int) { - return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{9} + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{12} } func (x *DispatchLookupResourcesRequest) GetMetadata() *ResolverMeta { @@ -1064,7 +1307,7 @@ type ResolvedResource struct { func (x *ResolvedResource) Reset() { *x = ResolvedResource{} if protoimpl.UnsafeEnabled { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[10] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1077,7 +1320,7 @@ func (x *ResolvedResource) String() string { func (*ResolvedResource) ProtoMessage() {} func (x *ResolvedResource) ProtoReflect() protoreflect.Message { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[10] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1090,7 +1333,7 @@ func (x *ResolvedResource) ProtoReflect() protoreflect.Message { // Deprecated: Use ResolvedResource.ProtoReflect.Descriptor instead. func (*ResolvedResource) Descriptor() ([]byte, []int) { - return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{10} + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{13} } func (x *ResolvedResource) GetResourceId() string { @@ -1127,7 +1370,7 @@ type DispatchLookupResourcesResponse struct { func (x *DispatchLookupResourcesResponse) Reset() { *x = DispatchLookupResourcesResponse{} if protoimpl.UnsafeEnabled { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[11] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1140,7 +1383,7 @@ func (x *DispatchLookupResourcesResponse) String() string { func (*DispatchLookupResourcesResponse) ProtoMessage() {} func (x *DispatchLookupResourcesResponse) ProtoReflect() protoreflect.Message { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[11] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1153,7 +1396,7 @@ func (x *DispatchLookupResourcesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DispatchLookupResourcesResponse.ProtoReflect.Descriptor instead. func (*DispatchLookupResourcesResponse) Descriptor() ([]byte, []int) { - return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{11} + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{14} } func (x *DispatchLookupResourcesResponse) GetMetadata() *ResponseMeta { @@ -1191,7 +1434,7 @@ type DispatchLookupSubjectsRequest struct { func (x *DispatchLookupSubjectsRequest) Reset() { *x = DispatchLookupSubjectsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[12] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1204,7 +1447,7 @@ func (x *DispatchLookupSubjectsRequest) String() string { func (*DispatchLookupSubjectsRequest) ProtoMessage() {} func (x *DispatchLookupSubjectsRequest) ProtoReflect() protoreflect.Message { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[12] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1217,7 +1460,7 @@ func (x *DispatchLookupSubjectsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DispatchLookupSubjectsRequest.ProtoReflect.Descriptor instead. func (*DispatchLookupSubjectsRequest) Descriptor() ([]byte, []int) { - return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{12} + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{15} } func (x *DispatchLookupSubjectsRequest) GetMetadata() *ResolverMeta { @@ -1261,7 +1504,7 @@ type FoundSubject struct { func (x *FoundSubject) Reset() { *x = FoundSubject{} if protoimpl.UnsafeEnabled { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[13] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1274,7 +1517,7 @@ func (x *FoundSubject) String() string { func (*FoundSubject) ProtoMessage() {} func (x *FoundSubject) ProtoReflect() protoreflect.Message { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[13] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1287,7 +1530,7 @@ func (x *FoundSubject) ProtoReflect() protoreflect.Message { // Deprecated: Use FoundSubject.ProtoReflect.Descriptor instead. func (*FoundSubject) Descriptor() ([]byte, []int) { - return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{13} + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{16} } func (x *FoundSubject) GetSubjectId() string { @@ -1322,7 +1565,7 @@ type FoundSubjects struct { func (x *FoundSubjects) Reset() { *x = FoundSubjects{} if protoimpl.UnsafeEnabled { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[14] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1335,7 +1578,7 @@ func (x *FoundSubjects) String() string { func (*FoundSubjects) ProtoMessage() {} func (x *FoundSubjects) ProtoReflect() protoreflect.Message { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[14] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1348,7 +1591,7 @@ func (x *FoundSubjects) ProtoReflect() protoreflect.Message { // Deprecated: Use FoundSubjects.ProtoReflect.Descriptor instead. func (*FoundSubjects) Descriptor() ([]byte, []int) { - return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{14} + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{17} } func (x *FoundSubjects) GetFoundSubjects() []*FoundSubject { @@ -1370,7 +1613,7 @@ type DispatchLookupSubjectsResponse struct { func (x *DispatchLookupSubjectsResponse) Reset() { *x = DispatchLookupSubjectsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[15] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1383,7 +1626,7 @@ func (x *DispatchLookupSubjectsResponse) String() string { func (*DispatchLookupSubjectsResponse) ProtoMessage() {} func (x *DispatchLookupSubjectsResponse) ProtoReflect() protoreflect.Message { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[15] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[18] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1396,7 +1639,7 @@ func (x *DispatchLookupSubjectsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DispatchLookupSubjectsResponse.ProtoReflect.Descriptor instead. func (*DispatchLookupSubjectsResponse) Descriptor() ([]byte, []int) { - return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{15} + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{18} } func (x *DispatchLookupSubjectsResponse) GetFoundSubjectsByResourceId() map[string]*FoundSubjects { @@ -1428,7 +1671,7 @@ type ResolverMeta struct { func (x *ResolverMeta) Reset() { *x = ResolverMeta{} if protoimpl.UnsafeEnabled { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[16] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1441,7 +1684,7 @@ func (x *ResolverMeta) String() string { func (*ResolverMeta) ProtoMessage() {} func (x *ResolverMeta) ProtoReflect() protoreflect.Message { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[16] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[19] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1454,7 +1697,7 @@ func (x *ResolverMeta) ProtoReflect() protoreflect.Message { // Deprecated: Use ResolverMeta.ProtoReflect.Descriptor instead. func (*ResolverMeta) Descriptor() ([]byte, []int) { - return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{16} + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{19} } func (x *ResolverMeta) GetAtRevision() string { @@ -1500,7 +1743,7 @@ type ResponseMeta struct { func (x *ResponseMeta) Reset() { *x = ResponseMeta{} if protoimpl.UnsafeEnabled { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[17] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1513,7 +1756,7 @@ func (x *ResponseMeta) String() string { func (*ResponseMeta) ProtoMessage() {} func (x *ResponseMeta) ProtoReflect() protoreflect.Message { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[17] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[20] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1526,7 +1769,7 @@ func (x *ResponseMeta) ProtoReflect() protoreflect.Message { // Deprecated: Use ResponseMeta.ProtoReflect.Descriptor instead. func (*ResponseMeta) Descriptor() ([]byte, []int) { - return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{17} + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{20} } func (x *ResponseMeta) GetDispatchCount() uint32 { @@ -1568,7 +1811,7 @@ type DebugInformation struct { func (x *DebugInformation) Reset() { *x = DebugInformation{} if protoimpl.UnsafeEnabled { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[18] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1581,7 +1824,7 @@ func (x *DebugInformation) String() string { func (*DebugInformation) ProtoMessage() {} func (x *DebugInformation) ProtoReflect() protoreflect.Message { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[18] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[21] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1594,7 +1837,7 @@ func (x *DebugInformation) ProtoReflect() protoreflect.Message { // Deprecated: Use DebugInformation.ProtoReflect.Descriptor instead. func (*DebugInformation) Descriptor() ([]byte, []int) { - return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{18} + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{21} } func (x *DebugInformation) GetCheck() *CheckDebugTrace { @@ -1620,7 +1863,7 @@ type CheckDebugTrace struct { func (x *CheckDebugTrace) Reset() { *x = CheckDebugTrace{} if protoimpl.UnsafeEnabled { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[19] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1633,7 +1876,7 @@ func (x *CheckDebugTrace) String() string { func (*CheckDebugTrace) ProtoMessage() {} func (x *CheckDebugTrace) ProtoReflect() protoreflect.Message { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[19] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[22] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1646,7 +1889,7 @@ func (x *CheckDebugTrace) ProtoReflect() protoreflect.Message { // Deprecated: Use CheckDebugTrace.ProtoReflect.Descriptor instead. func (*CheckDebugTrace) Descriptor() ([]byte, []int) { - return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{19} + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{22} } func (x *CheckDebugTrace) GetRequest() *DispatchCheckRequest { @@ -1703,7 +1946,7 @@ var file_dispatch_v1_dispatch_proto_rawDesc = []byte{ 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x17, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc8, 0x04, 0x0a, 0x14, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, + 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xfd, 0x05, 0x0a, 0x14, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3f, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, @@ -1730,368 +1973,444 @@ var file_dispatch_v1_dispatch_proto_rawDesc = []byte{ 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x05, - 0x64, 0x65, 0x62, 0x75, 0x67, 0x22, 0x54, 0x0a, 0x0c, 0x44, 0x65, 0x62, 0x75, 0x67, 0x53, 0x65, - 0x74, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x0c, 0x0a, 0x08, 0x4e, 0x4f, 0x5f, 0x44, 0x45, 0x42, 0x55, - 0x47, 0x10, 0x00, 0x12, 0x1a, 0x0a, 0x16, 0x45, 0x4e, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x42, 0x41, - 0x53, 0x49, 0x43, 0x5f, 0x44, 0x45, 0x42, 0x55, 0x47, 0x47, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, - 0x1a, 0x0a, 0x16, 0x45, 0x4e, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x54, 0x52, 0x41, 0x43, 0x45, 0x5f, - 0x44, 0x45, 0x42, 0x55, 0x47, 0x47, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x22, 0x42, 0x0a, 0x0e, 0x52, - 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x17, 0x0a, - 0x13, 0x52, 0x45, 0x51, 0x55, 0x49, 0x52, 0x45, 0x5f, 0x41, 0x4c, 0x4c, 0x5f, 0x52, 0x45, 0x53, - 0x55, 0x4c, 0x54, 0x53, 0x10, 0x00, 0x12, 0x17, 0x0a, 0x13, 0x41, 0x4c, 0x4c, 0x4f, 0x57, 0x5f, - 0x53, 0x49, 0x4e, 0x47, 0x4c, 0x45, 0x5f, 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x10, 0x01, 0x22, - 0xaa, 0x02, 0x0a, 0x15, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x43, 0x68, 0x65, 0x63, - 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x08, 0x6d, 0x65, 0x74, - 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x64, 0x69, - 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0x12, 0x70, 0x0a, 0x16, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x5f, 0x62, 0x79, 0x5f, 0x72, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x3b, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, - 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x42, 0x79, 0x52, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x13, 0x72, - 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x42, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x49, 0x64, 0x1a, 0x68, 0x0a, 0x18, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x42, 0x79, 0x52, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, - 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, - 0x12, 0x36, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x20, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x75, 0x6c, - 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x99, 0x02, 0x0a, - 0x13, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, - 0x73, 0x75, 0x6c, 0x74, 0x12, 0x4b, 0x0a, 0x0a, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, - 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2b, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, - 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, - 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x4d, 0x65, 0x6d, 0x62, 0x65, - 0x72, 0x73, 0x68, 0x69, 0x70, 0x52, 0x0a, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, 0x69, - 0x70, 0x12, 0x39, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, - 0x43, 0x61, 0x76, 0x65, 0x61, 0x74, 0x45, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, - 0x52, 0x0a, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2e, 0x0a, 0x13, - 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x65, 0x78, 0x70, 0x72, 0x5f, 0x66, 0x69, 0x65, - 0x6c, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x11, 0x6d, 0x69, 0x73, 0x73, 0x69, - 0x6e, 0x67, 0x45, 0x78, 0x70, 0x72, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x22, 0x4a, 0x0a, 0x0a, - 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, - 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x4e, 0x4f, 0x54, 0x5f, 0x4d, - 0x45, 0x4d, 0x42, 0x45, 0x52, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x4d, 0x45, 0x4d, 0x42, 0x45, - 0x52, 0x10, 0x02, 0x12, 0x13, 0x0a, 0x0f, 0x43, 0x41, 0x56, 0x45, 0x41, 0x54, 0x45, 0x44, 0x5f, - 0x4d, 0x45, 0x4d, 0x42, 0x45, 0x52, 0x10, 0x03, 0x22, 0xb8, 0x02, 0x0a, 0x15, 0x44, 0x69, 0x73, - 0x70, 0x61, 0x74, 0x63, 0x68, 0x45, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x64, 0x65, 0x62, 0x75, 0x67, 0x12, 0x52, 0x0a, 0x0b, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x68, + 0x69, 0x6e, 0x74, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x64, 0x69, 0x73, + 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, + 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x43, 0x68, + 0x65, 0x63, 0x6b, 0x48, 0x69, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x63, + 0x68, 0x65, 0x63, 0x6b, 0x48, 0x69, 0x6e, 0x74, 0x73, 0x1a, 0x5f, 0x0a, 0x0f, 0x43, 0x68, 0x65, + 0x63, 0x6b, 0x48, 0x69, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x36, + 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, + 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x54, 0x0a, 0x0c, 0x44, 0x65, + 0x62, 0x75, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x0c, 0x0a, 0x08, 0x4e, 0x4f, + 0x5f, 0x44, 0x45, 0x42, 0x55, 0x47, 0x10, 0x00, 0x12, 0x1a, 0x0a, 0x16, 0x45, 0x4e, 0x41, 0x42, + 0x4c, 0x45, 0x5f, 0x42, 0x41, 0x53, 0x49, 0x43, 0x5f, 0x44, 0x45, 0x42, 0x55, 0x47, 0x47, 0x49, + 0x4e, 0x47, 0x10, 0x01, 0x12, 0x1a, 0x0a, 0x16, 0x45, 0x4e, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x54, + 0x52, 0x41, 0x43, 0x45, 0x5f, 0x44, 0x45, 0x42, 0x55, 0x47, 0x47, 0x49, 0x4e, 0x47, 0x10, 0x02, + 0x22, 0x42, 0x0a, 0x0e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x53, 0x65, 0x74, 0x74, 0x69, + 0x6e, 0x67, 0x12, 0x17, 0x0a, 0x13, 0x52, 0x45, 0x51, 0x55, 0x49, 0x52, 0x45, 0x5f, 0x41, 0x4c, + 0x4c, 0x5f, 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x53, 0x10, 0x00, 0x12, 0x17, 0x0a, 0x13, 0x41, + 0x4c, 0x4c, 0x4f, 0x57, 0x5f, 0x53, 0x49, 0x4e, 0x47, 0x4c, 0x45, 0x5f, 0x52, 0x45, 0x53, 0x55, + 0x4c, 0x54, 0x10, 0x01, 0x22, 0xaa, 0x02, 0x0a, 0x15, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, + 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, + 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x19, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x70, 0x0a, 0x16, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, + 0x5f, 0x62, 0x79, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, + 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, + 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x43, 0x68, 0x65, 0x63, + 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x73, 0x42, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x52, 0x13, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x42, 0x79, 0x52, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x1a, 0x68, 0x0a, 0x18, 0x52, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x73, 0x42, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x36, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, + 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, + 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, + 0x01, 0x22, 0x99, 0x02, 0x0a, 0x13, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, + 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x4b, 0x0a, 0x0a, 0x6d, 0x65, 0x6d, + 0x62, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2b, 0x2e, + 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, + 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x52, 0x0a, 0x6d, 0x65, 0x6d, 0x62, + 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x12, 0x39, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x63, 0x6f, 0x72, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x76, 0x65, 0x61, 0x74, 0x45, 0x78, 0x70, 0x72, 0x65, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x12, 0x2e, 0x0a, 0x13, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x65, 0x78, 0x70, + 0x72, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x11, + 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x45, 0x78, 0x70, 0x72, 0x46, 0x69, 0x65, 0x6c, 0x64, + 0x73, 0x22, 0x4a, 0x0a, 0x0a, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x12, + 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, + 0x4e, 0x4f, 0x54, 0x5f, 0x4d, 0x45, 0x4d, 0x42, 0x45, 0x52, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, + 0x4d, 0x45, 0x4d, 0x42, 0x45, 0x52, 0x10, 0x02, 0x12, 0x13, 0x0a, 0x0f, 0x43, 0x41, 0x56, 0x45, + 0x41, 0x54, 0x45, 0x44, 0x5f, 0x4d, 0x45, 0x4d, 0x42, 0x45, 0x52, 0x10, 0x03, 0x22, 0xb8, 0x02, + 0x0a, 0x15, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x45, 0x78, 0x70, 0x61, 0x6e, 0x64, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3f, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, + 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x64, 0x69, 0x73, 0x70, + 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, + 0x4d, 0x65, 0x74, 0x61, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x08, + 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x58, 0x0a, 0x15, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x41, 0x6e, 0x64, 0x52, 0x65, 0x6c, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x13, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, 0x6e, 0x64, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x57, 0x0a, 0x0e, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x5f, + 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x30, 0x2e, 0x64, 0x69, 0x73, + 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, + 0x68, 0x45, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x45, + 0x78, 0x70, 0x61, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x0d, 0x65, 0x78, + 0x70, 0x61, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x22, 0x2b, 0x0a, 0x0d, 0x45, + 0x78, 0x70, 0x61, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x0b, 0x0a, 0x07, + 0x53, 0x48, 0x41, 0x4c, 0x4c, 0x4f, 0x57, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x52, 0x45, 0x43, + 0x55, 0x52, 0x53, 0x49, 0x56, 0x45, 0x10, 0x01, 0x22, 0x8c, 0x01, 0x0a, 0x16, 0x44, 0x69, 0x73, + 0x70, 0x61, 0x74, 0x63, 0x68, 0x45, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, + 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, + 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x3b, 0x0a, 0x09, 0x74, 0x72, + 0x65, 0x65, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, + 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x54, 0x75, 0x70, 0x6c, 0x65, 0x54, 0x72, 0x65, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x08, 0x74, + 0x72, 0x65, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x22, 0x55, 0x0a, 0x06, 0x43, 0x75, 0x72, 0x73, 0x6f, + 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x08, 0x73, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x29, 0x0a, + 0x10, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, + 0x68, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x22, 0x90, + 0x04, 0x0a, 0x1f, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, + 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x32, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3f, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, - 0x61, 0x74, 0x61, 0x12, 0x58, 0x0a, 0x15, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, - 0x61, 0x6e, 0x64, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, + 0x61, 0x74, 0x61, 0x12, 0x51, 0x0a, 0x11, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, + 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, + 0x01, 0x02, 0x10, 0x01, 0x52, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, + 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x4f, 0x0a, 0x10, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, + 0x74, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x42, 0x08, 0xfa, 0x42, + 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x0f, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, + 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x75, 0x62, 0x6a, 0x65, + 0x63, 0x74, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x75, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x73, 0x12, 0x4f, 0x0a, 0x10, 0x74, 0x65, 0x72, 0x6d, + 0x69, 0x6e, 0x61, 0x6c, 0x5f, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x41, 0x6e, 0x64, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x08, - 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x13, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x41, 0x6e, 0x64, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x57, 0x0a, - 0x0e, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x30, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, - 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x45, 0x78, 0x70, 0x61, - 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x45, 0x78, 0x70, 0x61, 0x6e, 0x73, - 0x69, 0x6f, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x0d, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x73, 0x69, - 0x6f, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x22, 0x2b, 0x0a, 0x0d, 0x45, 0x78, 0x70, 0x61, 0x6e, 0x73, - 0x69, 0x6f, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x48, 0x41, 0x4c, 0x4c, - 0x4f, 0x57, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x52, 0x45, 0x43, 0x55, 0x52, 0x53, 0x49, 0x56, - 0x45, 0x10, 0x01, 0x22, 0x8c, 0x01, 0x0a, 0x16, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, - 0x45, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, - 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x19, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, - 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x3b, 0x0a, 0x09, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x6e, 0x6f, - 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x75, 0x70, 0x6c, 0x65, - 0x54, 0x72, 0x65, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x08, 0x74, 0x72, 0x65, 0x65, 0x4e, 0x6f, - 0x64, 0x65, 0x22, 0x55, 0x0a, 0x06, 0x43, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x12, 0x1a, 0x0a, 0x08, - 0x73, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, - 0x73, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x69, 0x73, 0x70, - 0x61, 0x74, 0x63, 0x68, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x0f, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x56, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x22, 0x8e, 0x03, 0x0a, 0x21, 0x44, 0x69, - 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, 0x6c, 0x65, 0x52, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x3f, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x19, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, - 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x42, 0x08, 0xfa, 0x42, - 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0x12, 0x51, 0x0a, 0x11, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x72, 0x65, 0x6c, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x63, 0x6f, - 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, - 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, - 0x01, 0x52, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x6c, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x4f, 0x0a, 0x10, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x72, - 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, - 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, - 0x02, 0x10, 0x01, 0x52, 0x0f, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x6c, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, - 0x69, 0x64, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x75, 0x62, 0x6a, 0x65, - 0x63, 0x74, 0x49, 0x64, 0x73, 0x12, 0x3c, 0x0a, 0x0f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, - 0x6c, 0x5f, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, - 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x75, 0x72, - 0x73, 0x6f, 0x72, 0x52, 0x0e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x43, 0x75, 0x72, - 0x73, 0x6f, 0x72, 0x12, 0x25, 0x0a, 0x0e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, - 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x6f, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x22, 0xe6, 0x01, 0x0a, 0x11, 0x52, - 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, - 0x64, 0x12, 0x50, 0x0a, 0x0d, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2b, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, - 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, 0x6c, 0x65, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x53, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x0c, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x53, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x66, 0x6f, 0x72, 0x5f, 0x73, 0x75, 0x62, 0x6a, 0x65, - 0x63, 0x74, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x66, 0x6f, - 0x72, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x73, 0x22, 0x36, 0x0a, 0x0c, 0x52, - 0x65, 0x73, 0x75, 0x6c, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x12, 0x0a, 0x0e, 0x52, - 0x45, 0x51, 0x55, 0x49, 0x52, 0x45, 0x53, 0x5f, 0x43, 0x48, 0x45, 0x43, 0x4b, 0x10, 0x00, 0x12, - 0x12, 0x0a, 0x0e, 0x48, 0x41, 0x53, 0x5f, 0x50, 0x45, 0x52, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4f, - 0x4e, 0x10, 0x01, 0x22, 0xe0, 0x01, 0x0a, 0x22, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, - 0x52, 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x08, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x64, - 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x61, 0x63, 0x68, - 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x08, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x35, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, - 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, - 0x65, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x47, 0x0a, - 0x15, 0x61, 0x66, 0x74, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, - 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x64, - 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x75, 0x72, 0x73, 0x6f, - 0x72, 0x52, 0x13, 0x61, 0x66, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x43, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x22, 0x88, 0x03, 0x0a, 0x1e, 0x44, 0x69, 0x73, 0x70, 0x61, - 0x74, 0x63, 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3f, 0x0a, 0x08, 0x6d, 0x65, 0x74, - 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x64, 0x69, - 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, - 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, - 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x4d, 0x0a, 0x0f, 0x6f, 0x62, - 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, - 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x42, - 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x0e, 0x6f, 0x62, 0x6a, 0x65, 0x63, - 0x74, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3e, 0x0a, 0x07, 0x73, 0x75, 0x62, - 0x6a, 0x65, 0x63, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x72, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x41, 0x6e, 0x64, 0x52, 0x65, - 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, - 0x52, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x31, 0x0a, 0x07, 0x63, 0x6f, 0x6e, - 0x74, 0x65, 0x78, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, + 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x0f, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, + 0x61, 0x6c, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x31, 0x0a, 0x07, 0x63, 0x6f, 0x6e, + 0x74, 0x65, 0x78, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, - 0x75, 0x63, 0x74, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x25, 0x0a, 0x0e, - 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x4c, 0x69, - 0x6d, 0x69, 0x74, 0x12, 0x3c, 0x0a, 0x0f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, - 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x64, - 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x75, 0x72, 0x73, 0x6f, - 0x72, 0x52, 0x0e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x43, 0x75, 0x72, 0x73, 0x6f, - 0x72, 0x22, 0x98, 0x02, 0x0a, 0x10, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x52, 0x65, + 0x75, 0x63, 0x74, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x3c, 0x0a, 0x0f, + 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, + 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x52, 0x0e, 0x6f, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x61, 0x6c, 0x43, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x12, 0x25, 0x0a, 0x0e, 0x6f, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x08, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x0d, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x4c, 0x69, 0x6d, 0x69, + 0x74, 0x22, 0x91, 0x01, 0x0a, 0x10, 0x50, 0x6f, 0x73, 0x73, 0x69, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x54, 0x0a, 0x0e, 0x70, 0x65, 0x72, 0x6d, 0x69, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x68, 0x69, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, - 0x2c, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, - 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x50, - 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x68, 0x69, 0x70, 0x52, 0x0e, 0x70, - 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x68, 0x69, 0x70, 0x12, 0x38, 0x0a, - 0x18, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, - 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x16, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, - 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x53, 0x0a, 0x0e, 0x50, 0x65, 0x72, 0x6d, 0x69, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x68, 0x69, 0x70, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, - 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x48, 0x41, 0x53, 0x5f, 0x50, 0x45, - 0x52, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x01, 0x12, 0x20, 0x0a, 0x1c, 0x43, 0x4f, - 0x4e, 0x44, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x41, 0x4c, 0x4c, 0x59, 0x5f, 0x48, 0x41, 0x53, 0x5f, - 0x50, 0x45, 0x52, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x02, 0x22, 0xed, 0x01, 0x0a, - 0x1f, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x52, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x35, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, - 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x08, 0x6d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x4a, 0x0a, 0x11, 0x72, 0x65, 0x73, 0x6f, 0x6c, - 0x76, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, - 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x52, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x12, 0x47, 0x0a, 0x15, 0x61, 0x66, 0x74, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, - 0x2e, 0x43, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x52, 0x13, 0x61, 0x66, 0x74, 0x65, 0x72, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x43, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x22, 0xa7, 0x02, 0x0a, - 0x1d, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x53, - 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3f, - 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x19, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, - 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x42, 0x08, 0xfa, 0x42, 0x05, - 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, - 0x51, 0x0a, 0x11, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x72, 0x65, 0x6c, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x72, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x66, - 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, - 0x52, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, - 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x49, 0x64, 0x73, 0x12, 0x4f, 0x0a, 0x10, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, - 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x26, 0x0a, 0x0f, 0x66, 0x6f, 0x72, 0x5f, 0x73, + 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x0d, 0x66, 0x6f, 0x72, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x73, 0x12, + 0x34, 0x0a, 0x16, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, + 0x78, 0x74, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x14, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x50, + 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0xdd, 0x01, 0x0a, 0x20, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, + 0x63, 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x73, 0x32, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x08, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x64, + 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6f, 0x73, 0x73, 0x69, + 0x62, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x08, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x35, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, + 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, + 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x47, 0x0a, 0x15, + 0x61, 0x66, 0x74, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x63, + 0x75, 0x72, 0x73, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x64, 0x69, + 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x75, 0x72, 0x73, 0x6f, 0x72, + 0x52, 0x13, 0x61, 0x66, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x43, + 0x75, 0x72, 0x73, 0x6f, 0x72, 0x22, 0x8e, 0x03, 0x0a, 0x21, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, + 0x63, 0x68, 0x52, 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3f, 0x0a, 0x08, 0x6d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, + 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, + 0x6c, 0x76, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, + 0x10, 0x01, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x51, 0x0a, 0x11, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, + 0x6e, 0x63, 0x65, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x10, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x4f, 0x0a, 0x10, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x72, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x66, 0x65, + 0x72, 0x65, 0x6e, 0x63, 0x65, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, + 0x0f, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x73, 0x18, + 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, + 0x73, 0x12, 0x3c, 0x0a, 0x0f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x63, 0x75, + 0x72, 0x73, 0x6f, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x64, 0x69, 0x73, + 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x52, + 0x0e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x43, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x12, + 0x25, 0x0a, 0x0e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x6c, 0x69, 0x6d, 0x69, + 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, + 0x6c, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x22, 0xe6, 0x01, 0x0a, 0x11, 0x52, 0x65, 0x61, 0x63, 0x68, + 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x1f, 0x0a, 0x0b, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x50, 0x0a, + 0x0d, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2b, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, + 0x76, 0x31, 0x2e, 0x52, 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x52, 0x0c, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, + 0x26, 0x0a, 0x0f, 0x66, 0x6f, 0x72, 0x5f, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, + 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x66, 0x6f, 0x72, 0x53, 0x75, 0x62, + 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x73, 0x22, 0x36, 0x0a, 0x0c, 0x52, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x12, 0x0a, 0x0e, 0x52, 0x45, 0x51, 0x55, 0x49, + 0x52, 0x45, 0x53, 0x5f, 0x43, 0x48, 0x45, 0x43, 0x4b, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x48, + 0x41, 0x53, 0x5f, 0x50, 0x45, 0x52, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x01, 0x22, + 0xe0, 0x01, 0x0a, 0x22, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x61, 0x63, + 0x68, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, + 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, 0x6c, 0x65, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x12, 0x35, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, + 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x52, + 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x47, 0x0a, 0x15, 0x61, 0x66, 0x74, + 0x65, 0x72, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x63, 0x75, 0x72, 0x73, + 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, + 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x52, 0x13, 0x61, + 0x66, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x43, 0x75, 0x72, 0x73, + 0x6f, 0x72, 0x22, 0x88, 0x03, 0x0a, 0x1e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x4c, + 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3f, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, + 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x4d, 0x65, + 0x74, 0x61, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x08, 0x6d, 0x65, + 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x4d, 0x0a, 0x0f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x42, 0x08, 0xfa, 0x42, 0x05, - 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x0f, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, - 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xbd, 0x01, 0x0a, 0x0c, 0x46, 0x6f, 0x75, 0x6e, 0x64, - 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x75, 0x62, 0x6a, 0x65, - 0x63, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x75, 0x62, - 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x12, 0x46, 0x0a, 0x11, 0x63, 0x61, 0x76, 0x65, 0x61, 0x74, - 0x5f, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x19, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x76, 0x65, - 0x61, 0x74, 0x45, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x10, 0x63, 0x61, - 0x76, 0x65, 0x61, 0x74, 0x45, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x46, - 0x0a, 0x11, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x5f, 0x73, 0x75, 0x62, 0x6a, 0x65, - 0x63, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x64, 0x69, 0x73, 0x70, - 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x75, 0x62, - 0x6a, 0x65, 0x63, 0x74, 0x52, 0x10, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x53, 0x75, - 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x22, 0x51, 0x0a, 0x0d, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x53, - 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x40, 0x0a, 0x0e, 0x66, 0x6f, 0x75, 0x6e, 0x64, - 0x5f, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x19, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, - 0x75, 0x6e, 0x64, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x0d, 0x66, 0x6f, 0x75, 0x6e, - 0x64, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x22, 0xd0, 0x02, 0x0a, 0x1e, 0x44, 0x69, - 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x53, 0x75, 0x62, 0x6a, - 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x8c, 0x01, 0x0a, - 0x1d, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x5f, - 0x62, 0x79, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x4a, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, - 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, - 0x70, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x2e, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x42, - 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x52, 0x19, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x42, - 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x35, 0x0a, 0x08, 0x6d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, - 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0x1a, 0x68, 0x0a, 0x1e, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x75, 0x62, 0x6a, 0x65, - 0x63, 0x74, 0x73, 0x42, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, + 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x0e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x6c, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3e, 0x0a, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x41, 0x6e, 0x64, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x07, 0x73, 0x75, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x31, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, + 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x6f, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x0d, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, + 0x3c, 0x0a, 0x0f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x63, 0x75, 0x72, 0x73, + 0x6f, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, + 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x52, 0x0e, 0x6f, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x43, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x22, 0x98, 0x02, + 0x0a, 0x10, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x49, 0x64, 0x12, 0x54, 0x0a, 0x0e, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x73, 0x68, 0x69, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2c, 0x2e, 0x64, 0x69, + 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, + 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x50, 0x65, 0x72, 0x6d, 0x69, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x68, 0x69, 0x70, 0x52, 0x0e, 0x70, 0x65, 0x72, 0x6d, 0x69, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x68, 0x69, 0x70, 0x12, 0x38, 0x0a, 0x18, 0x6d, 0x69, 0x73, + 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x5f, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x16, 0x6d, 0x69, 0x73, + 0x73, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x74, + 0x65, 0x78, 0x74, 0x22, 0x53, 0x0a, 0x0e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x73, 0x68, 0x69, 0x70, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, + 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x48, 0x41, 0x53, 0x5f, 0x50, 0x45, 0x52, 0x4d, 0x49, 0x53, + 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x01, 0x12, 0x20, 0x0a, 0x1c, 0x43, 0x4f, 0x4e, 0x44, 0x49, 0x54, + 0x49, 0x4f, 0x4e, 0x41, 0x4c, 0x4c, 0x59, 0x5f, 0x48, 0x41, 0x53, 0x5f, 0x50, 0x45, 0x52, 0x4d, + 0x49, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x02, 0x22, 0xed, 0x01, 0x0a, 0x1f, 0x44, 0x69, 0x73, + 0x70, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x08, + 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, + 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, + 0x61, 0x74, 0x61, 0x12, 0x4a, 0x0a, 0x11, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x5f, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, + 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, + 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x10, 0x72, + 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, + 0x47, 0x0a, 0x15, 0x61, 0x66, 0x74, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x5f, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, + 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x75, 0x72, + 0x73, 0x6f, 0x72, 0x52, 0x13, 0x61, 0x66, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x43, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x22, 0xa7, 0x02, 0x0a, 0x1d, 0x44, 0x69, 0x73, + 0x70, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x53, 0x75, 0x62, 0x6a, 0x65, + 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3f, 0x0a, 0x08, 0x6d, 0x65, + 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x64, + 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, + 0x76, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, + 0x01, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x51, 0x0a, 0x11, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, + 0x63, 0x65, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x10, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x21, + 0x0a, 0x0c, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, + 0x73, 0x12, 0x4f, 0x0a, 0x10, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x72, 0x65, 0x6c, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x63, 0x6f, + 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, + 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, + 0x01, 0x52, 0x0f, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x22, 0xbd, 0x01, 0x0a, 0x0c, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x75, 0x62, 0x6a, + 0x65, 0x63, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x49, 0x64, 0x12, 0x46, 0x0a, 0x11, 0x63, 0x61, 0x76, 0x65, 0x61, 0x74, 0x5f, 0x65, 0x78, 0x70, + 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, + 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x76, 0x65, 0x61, 0x74, 0x45, 0x78, + 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x10, 0x63, 0x61, 0x76, 0x65, 0x61, 0x74, + 0x45, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x46, 0x0a, 0x11, 0x65, 0x78, + 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x5f, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x18, + 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, - 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xc1, 0x01, 0x0a, - 0x0c, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x29, 0x0a, - 0x0b, 0x61, 0x74, 0x5f, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x72, 0x03, 0x28, 0x80, 0x08, 0x52, 0x0a, 0x61, 0x74, - 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x30, 0x0a, 0x0f, 0x64, 0x65, 0x70, 0x74, - 0x68, 0x5f, 0x72, 0x65, 0x6d, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0d, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x2a, 0x02, 0x20, 0x00, 0x52, 0x0e, 0x64, 0x65, 0x70, 0x74, - 0x68, 0x52, 0x65, 0x6d, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x12, 0x21, 0x0a, 0x0a, 0x72, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x02, - 0x18, 0x01, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x31, 0x0a, - 0x0f, 0x74, 0x72, 0x61, 0x76, 0x65, 0x72, 0x73, 0x61, 0x6c, 0x5f, 0x62, 0x6c, 0x6f, 0x6f, 0x6d, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x7a, 0x03, 0x18, 0x80, 0x08, - 0x52, 0x0e, 0x74, 0x72, 0x61, 0x76, 0x65, 0x72, 0x73, 0x61, 0x6c, 0x42, 0x6c, 0x6f, 0x6f, 0x6d, - 0x22, 0xda, 0x01, 0x0a, 0x0c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x74, - 0x61, 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x64, 0x69, 0x73, 0x70, 0x61, - 0x74, 0x63, 0x68, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x65, 0x70, 0x74, - 0x68, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x0d, 0x64, 0x65, 0x70, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, - 0x32, 0x0a, 0x15, 0x63, 0x61, 0x63, 0x68, 0x65, 0x64, 0x5f, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, - 0x63, 0x68, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x13, - 0x63, 0x61, 0x63, 0x68, 0x65, 0x64, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x43, 0x6f, - 0x75, 0x6e, 0x74, 0x12, 0x3c, 0x0a, 0x0a, 0x64, 0x65, 0x62, 0x75, 0x67, 0x5f, 0x69, 0x6e, 0x66, - 0x6f, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, - 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x72, - 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x64, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x66, - 0x6f, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x4a, 0x04, 0x08, 0x05, 0x10, 0x06, 0x22, 0x46, 0x0a, - 0x10, 0x44, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x12, 0x32, 0x0a, 0x05, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1c, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x43, - 0x68, 0x65, 0x63, 0x6b, 0x44, 0x65, 0x62, 0x75, 0x67, 0x54, 0x72, 0x61, 0x63, 0x65, 0x52, 0x05, - 0x63, 0x68, 0x65, 0x63, 0x6b, 0x22, 0xaf, 0x04, 0x0a, 0x0f, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x44, - 0x65, 0x62, 0x75, 0x67, 0x54, 0x72, 0x61, 0x63, 0x65, 0x12, 0x3b, 0x0a, 0x07, 0x72, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x64, 0x69, 0x73, - 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, - 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x72, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x5f, 0x0a, 0x16, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x29, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, - 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x44, 0x65, 0x62, 0x75, 0x67, 0x54, - 0x72, 0x61, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, - 0x65, 0x52, 0x14, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x6c, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x43, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, - 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, - 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x44, 0x65, 0x62, 0x75, - 0x67, 0x54, 0x72, 0x61, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x52, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x28, 0x0a, 0x10, - 0x69, 0x73, 0x5f, 0x63, 0x61, 0x63, 0x68, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x69, 0x73, 0x43, 0x61, 0x63, 0x68, 0x65, 0x64, - 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x3f, 0x0a, 0x0c, 0x73, 0x75, 0x62, 0x5f, 0x70, 0x72, - 0x6f, 0x62, 0x6c, 0x65, 0x6d, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x64, + 0x52, 0x10, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, + 0x74, 0x73, 0x22, 0x51, 0x0a, 0x0d, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x75, 0x62, 0x6a, 0x65, + 0x63, 0x74, 0x73, 0x12, 0x40, 0x0a, 0x0e, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x73, 0x75, 0x62, + 0x6a, 0x65, 0x63, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x64, 0x69, + 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x53, + 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x0d, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x75, 0x62, + 0x6a, 0x65, 0x63, 0x74, 0x73, 0x22, 0xd0, 0x02, 0x0a, 0x1e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, + 0x63, 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x8c, 0x01, 0x0a, 0x1d, 0x66, 0x6f, 0x75, + 0x6e, 0x64, 0x5f, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x5f, 0x62, 0x79, 0x5f, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x4a, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, + 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x53, 0x75, 0x62, + 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x46, 0x6f, + 0x75, 0x6e, 0x64, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x42, 0x79, 0x52, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x19, 0x66, 0x6f, + 0x75, 0x6e, 0x64, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x42, 0x79, 0x52, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x35, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, + 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x64, 0x69, 0x73, 0x70, + 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x4d, 0x65, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x1a, 0x68, + 0x0a, 0x1e, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x42, + 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, + 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xc1, 0x01, 0x0a, 0x0c, 0x52, 0x65, 0x73, + 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x29, 0x0a, 0x0b, 0x61, 0x74, 0x5f, + 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, + 0xfa, 0x42, 0x05, 0x72, 0x03, 0x28, 0x80, 0x08, 0x52, 0x0a, 0x61, 0x74, 0x52, 0x65, 0x76, 0x69, + 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x30, 0x0a, 0x0f, 0x64, 0x65, 0x70, 0x74, 0x68, 0x5f, 0x72, 0x65, + 0x6d, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x07, 0xfa, + 0x42, 0x04, 0x2a, 0x02, 0x20, 0x00, 0x52, 0x0e, 0x64, 0x65, 0x70, 0x74, 0x68, 0x52, 0x65, 0x6d, + 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x12, 0x21, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x09, + 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x31, 0x0a, 0x0f, 0x74, 0x72, 0x61, + 0x76, 0x65, 0x72, 0x73, 0x61, 0x6c, 0x5f, 0x62, 0x6c, 0x6f, 0x6f, 0x6d, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0c, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x7a, 0x03, 0x18, 0x80, 0x08, 0x52, 0x0e, 0x74, 0x72, + 0x61, 0x76, 0x65, 0x72, 0x73, 0x61, 0x6c, 0x42, 0x6c, 0x6f, 0x6f, 0x6d, 0x22, 0xda, 0x01, 0x0a, + 0x0c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x25, 0x0a, + 0x0e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x43, + 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x65, 0x70, 0x74, 0x68, 0x5f, 0x72, 0x65, + 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x64, 0x65, + 0x70, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0x32, 0x0a, 0x15, 0x63, + 0x61, 0x63, 0x68, 0x65, 0x64, 0x5f, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x13, 0x63, 0x61, 0x63, 0x68, + 0x65, 0x64, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, + 0x3c, 0x0a, 0x0a, 0x64, 0x65, 0x62, 0x75, 0x67, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, + 0x31, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x09, 0x64, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x4a, 0x04, 0x08, + 0x04, 0x10, 0x05, 0x4a, 0x04, 0x08, 0x05, 0x10, 0x06, 0x22, 0x46, 0x0a, 0x10, 0x44, 0x65, 0x62, + 0x75, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x32, 0x0a, + 0x05, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, - 0x44, 0x65, 0x62, 0x75, 0x67, 0x54, 0x72, 0x61, 0x63, 0x65, 0x52, 0x0b, 0x73, 0x75, 0x62, 0x50, - 0x72, 0x6f, 0x62, 0x6c, 0x65, 0x6d, 0x73, 0x12, 0x35, 0x0a, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x5c, - 0x0a, 0x0c, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, - 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, - 0x12, 0x36, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x20, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x75, 0x6c, - 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x39, 0x0a, 0x0c, - 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, - 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x45, 0x4c, - 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x50, 0x45, 0x52, 0x4d, 0x49, - 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x02, 0x32, 0xbd, 0x04, 0x0a, 0x0f, 0x44, 0x69, 0x73, 0x70, - 0x61, 0x74, 0x63, 0x68, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x58, 0x0a, 0x0d, 0x44, - 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x21, 0x2e, 0x64, - 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, - 0x74, 0x63, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x22, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, - 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5b, 0x0a, 0x0e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, - 0x68, 0x45, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x12, 0x22, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, - 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x45, 0x78, - 0x70, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x64, 0x69, + 0x44, 0x65, 0x62, 0x75, 0x67, 0x54, 0x72, 0x61, 0x63, 0x65, 0x52, 0x05, 0x63, 0x68, 0x65, 0x63, + 0x6b, 0x22, 0xaf, 0x04, 0x0a, 0x0f, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x44, 0x65, 0x62, 0x75, 0x67, + 0x54, 0x72, 0x61, 0x63, 0x65, 0x12, 0x3b, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, + 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x43, 0x68, 0x65, + 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x5f, 0x0a, 0x16, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x72, + 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x29, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, + 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x44, 0x65, 0x62, 0x75, 0x67, 0x54, 0x72, 0x61, 0x63, 0x65, + 0x2e, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x14, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, + 0x79, 0x70, 0x65, 0x12, 0x43, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x03, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, + 0x76, 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x44, 0x65, 0x62, 0x75, 0x67, 0x54, 0x72, 0x61, + 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x69, 0x73, 0x5f, 0x63, + 0x61, 0x63, 0x68, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0e, 0x69, 0x73, 0x43, 0x61, 0x63, 0x68, 0x65, 0x64, 0x52, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x12, 0x3f, 0x0a, 0x0c, 0x73, 0x75, 0x62, 0x5f, 0x70, 0x72, 0x6f, 0x62, 0x6c, 0x65, + 0x6d, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, + 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x44, 0x65, 0x62, 0x75, + 0x67, 0x54, 0x72, 0x61, 0x63, 0x65, 0x52, 0x0b, 0x73, 0x75, 0x62, 0x50, 0x72, 0x6f, 0x62, 0x6c, + 0x65, 0x6d, 0x73, 0x12, 0x35, 0x0a, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x5c, 0x0a, 0x0c, 0x52, 0x65, + 0x73, 0x75, 0x6c, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x36, 0x0a, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x64, 0x69, + 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x39, 0x0a, 0x0c, 0x52, 0x65, 0x6c, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, + 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x45, 0x4c, 0x41, 0x54, 0x49, 0x4f, + 0x4e, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x50, 0x45, 0x52, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4f, + 0x4e, 0x10, 0x02, 0x32, 0xba, 0x05, 0x0a, 0x0f, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, + 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x58, 0x0a, 0x0d, 0x44, 0x69, 0x73, 0x70, 0x61, + 0x74, 0x63, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x21, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, + 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x43, + 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, - 0x63, 0x68, 0x45, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x12, 0x81, 0x01, 0x0a, 0x1a, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x52, - 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x73, 0x12, 0x2e, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, - 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, 0x6c, - 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x2f, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, - 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, 0x6c, - 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x78, 0x0a, 0x17, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, - 0x63, 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x73, 0x12, 0x2b, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, - 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x52, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, - 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, - 0x70, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, - 0x12, 0x75, 0x0a, 0x16, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x6f, 0x6f, 0x6b, - 0x75, 0x70, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x2a, 0x2e, 0x64, 0x69, 0x73, + 0x63, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x12, 0x5b, 0x0a, 0x0e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x45, 0x78, 0x70, + 0x61, 0x6e, 0x64, 0x12, 0x22, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, + 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x45, 0x78, 0x70, 0x61, 0x6e, 0x64, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, + 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x45, 0x78, + 0x70, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x81, + 0x01, 0x0a, 0x1a, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x61, 0x63, 0x68, + 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x2e, 0x2e, + 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, + 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, + 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, + 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, + 0x30, 0x01, 0x12, 0x78, 0x0a, 0x17, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x6f, + 0x6f, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x2b, 0x2e, + 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, + 0x61, 0x74, 0x63, 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, - 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, + 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x75, 0x0a, 0x16, + 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x53, 0x75, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x2a, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x6f, 0x6f, - 0x6b, 0x75, 0x70, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x42, 0xaa, 0x01, 0x0a, 0x0f, 0x63, 0x6f, 0x6d, 0x2e, - 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x42, 0x0d, 0x44, 0x69, 0x73, - 0x70, 0x61, 0x74, 0x63, 0x68, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3b, 0x67, 0x69, - 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x75, 0x74, 0x68, 0x7a, 0x65, 0x64, - 0x2f, 0x73, 0x70, 0x69, 0x63, 0x65, 0x64, 0x62, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x2f, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2f, 0x76, 0x31, 0x3b, 0x64, - 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x44, 0x58, 0x58, 0xaa, - 0x02, 0x0b, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0b, - 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x17, 0x44, 0x69, - 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, - 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0c, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, - 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6b, 0x75, 0x70, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, + 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x53, + 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x30, 0x01, 0x12, 0x7b, 0x0a, 0x18, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x4c, + 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x32, 0x12, + 0x2c, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, + 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x73, 0x32, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, + 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, + 0x61, 0x74, 0x63, 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x73, 0x32, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, + 0x42, 0xaa, 0x01, 0x0a, 0x0f, 0x63, 0x6f, 0x6d, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, + 0x68, 0x2e, 0x76, 0x31, 0x42, 0x0d, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x50, 0x72, + 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x61, 0x75, 0x74, 0x68, 0x7a, 0x65, 0x64, 0x2f, 0x73, 0x70, 0x69, 0x63, 0x65, 0x64, + 0x62, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x64, 0x69, 0x73, 0x70, + 0x61, 0x74, 0x63, 0x68, 0x2f, 0x76, 0x31, 0x3b, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, + 0x76, 0x31, 0xa2, 0x02, 0x03, 0x44, 0x58, 0x58, 0xaa, 0x02, 0x0b, 0x44, 0x69, 0x73, 0x70, 0x61, + 0x74, 0x63, 0x68, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0b, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, + 0x68, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x17, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x5c, + 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, + 0x0c, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -2107,7 +2426,7 @@ func file_dispatch_v1_dispatch_proto_rawDescGZIP() []byte { } var file_dispatch_v1_dispatch_proto_enumTypes = make([]protoimpl.EnumInfo, 7) -var file_dispatch_v1_dispatch_proto_msgTypes = make([]protoimpl.MessageInfo, 23) +var file_dispatch_v1_dispatch_proto_msgTypes = make([]protoimpl.MessageInfo, 27) var file_dispatch_v1_dispatch_proto_goTypes = []interface{}{ (DispatchCheckRequest_DebugSetting)(0), // 0: dispatch.v1.DispatchCheckRequest.DebugSetting (DispatchCheckRequest_ResultsSetting)(0), // 1: dispatch.v1.DispatchCheckRequest.ResultsSetting @@ -2122,95 +2441,112 @@ var file_dispatch_v1_dispatch_proto_goTypes = []interface{}{ (*DispatchExpandRequest)(nil), // 10: dispatch.v1.DispatchExpandRequest (*DispatchExpandResponse)(nil), // 11: dispatch.v1.DispatchExpandResponse (*Cursor)(nil), // 12: dispatch.v1.Cursor - (*DispatchReachableResourcesRequest)(nil), // 13: dispatch.v1.DispatchReachableResourcesRequest - (*ReachableResource)(nil), // 14: dispatch.v1.ReachableResource - (*DispatchReachableResourcesResponse)(nil), // 15: dispatch.v1.DispatchReachableResourcesResponse - (*DispatchLookupResourcesRequest)(nil), // 16: dispatch.v1.DispatchLookupResourcesRequest - (*ResolvedResource)(nil), // 17: dispatch.v1.ResolvedResource - (*DispatchLookupResourcesResponse)(nil), // 18: dispatch.v1.DispatchLookupResourcesResponse - (*DispatchLookupSubjectsRequest)(nil), // 19: dispatch.v1.DispatchLookupSubjectsRequest - (*FoundSubject)(nil), // 20: dispatch.v1.FoundSubject - (*FoundSubjects)(nil), // 21: dispatch.v1.FoundSubjects - (*DispatchLookupSubjectsResponse)(nil), // 22: dispatch.v1.DispatchLookupSubjectsResponse - (*ResolverMeta)(nil), // 23: dispatch.v1.ResolverMeta - (*ResponseMeta)(nil), // 24: dispatch.v1.ResponseMeta - (*DebugInformation)(nil), // 25: dispatch.v1.DebugInformation - (*CheckDebugTrace)(nil), // 26: dispatch.v1.CheckDebugTrace - nil, // 27: dispatch.v1.DispatchCheckResponse.ResultsByResourceIdEntry - nil, // 28: dispatch.v1.DispatchLookupSubjectsResponse.FoundSubjectsByResourceIdEntry - nil, // 29: dispatch.v1.CheckDebugTrace.ResultsEntry - (*v1.RelationReference)(nil), // 30: core.v1.RelationReference - (*v1.ObjectAndRelation)(nil), // 31: core.v1.ObjectAndRelation - (*v1.CaveatExpression)(nil), // 32: core.v1.CaveatExpression - (*v1.RelationTupleTreeNode)(nil), // 33: core.v1.RelationTupleTreeNode - (*structpb.Struct)(nil), // 34: google.protobuf.Struct - (*durationpb.Duration)(nil), // 35: google.protobuf.Duration + (*DispatchLookupResources2Request)(nil), // 13: dispatch.v1.DispatchLookupResources2Request + (*PossibleResource)(nil), // 14: dispatch.v1.PossibleResource + (*DispatchLookupResources2Response)(nil), // 15: dispatch.v1.DispatchLookupResources2Response + (*DispatchReachableResourcesRequest)(nil), // 16: dispatch.v1.DispatchReachableResourcesRequest + (*ReachableResource)(nil), // 17: dispatch.v1.ReachableResource + (*DispatchReachableResourcesResponse)(nil), // 18: dispatch.v1.DispatchReachableResourcesResponse + (*DispatchLookupResourcesRequest)(nil), // 19: dispatch.v1.DispatchLookupResourcesRequest + (*ResolvedResource)(nil), // 20: dispatch.v1.ResolvedResource + (*DispatchLookupResourcesResponse)(nil), // 21: dispatch.v1.DispatchLookupResourcesResponse + (*DispatchLookupSubjectsRequest)(nil), // 22: dispatch.v1.DispatchLookupSubjectsRequest + (*FoundSubject)(nil), // 23: dispatch.v1.FoundSubject + (*FoundSubjects)(nil), // 24: dispatch.v1.FoundSubjects + (*DispatchLookupSubjectsResponse)(nil), // 25: dispatch.v1.DispatchLookupSubjectsResponse + (*ResolverMeta)(nil), // 26: dispatch.v1.ResolverMeta + (*ResponseMeta)(nil), // 27: dispatch.v1.ResponseMeta + (*DebugInformation)(nil), // 28: dispatch.v1.DebugInformation + (*CheckDebugTrace)(nil), // 29: dispatch.v1.CheckDebugTrace + nil, // 30: dispatch.v1.DispatchCheckRequest.CheckHintsEntry + nil, // 31: dispatch.v1.DispatchCheckResponse.ResultsByResourceIdEntry + nil, // 32: dispatch.v1.DispatchLookupSubjectsResponse.FoundSubjectsByResourceIdEntry + nil, // 33: dispatch.v1.CheckDebugTrace.ResultsEntry + (*v1.RelationReference)(nil), // 34: core.v1.RelationReference + (*v1.ObjectAndRelation)(nil), // 35: core.v1.ObjectAndRelation + (*v1.CaveatExpression)(nil), // 36: core.v1.CaveatExpression + (*v1.RelationTupleTreeNode)(nil), // 37: core.v1.RelationTupleTreeNode + (*structpb.Struct)(nil), // 38: google.protobuf.Struct + (*durationpb.Duration)(nil), // 39: google.protobuf.Duration } var file_dispatch_v1_dispatch_proto_depIdxs = []int32{ - 23, // 0: dispatch.v1.DispatchCheckRequest.metadata:type_name -> dispatch.v1.ResolverMeta - 30, // 1: dispatch.v1.DispatchCheckRequest.resource_relation:type_name -> core.v1.RelationReference - 31, // 2: dispatch.v1.DispatchCheckRequest.subject:type_name -> core.v1.ObjectAndRelation + 26, // 0: dispatch.v1.DispatchCheckRequest.metadata:type_name -> dispatch.v1.ResolverMeta + 34, // 1: dispatch.v1.DispatchCheckRequest.resource_relation:type_name -> core.v1.RelationReference + 35, // 2: dispatch.v1.DispatchCheckRequest.subject:type_name -> core.v1.ObjectAndRelation 1, // 3: dispatch.v1.DispatchCheckRequest.results_setting:type_name -> dispatch.v1.DispatchCheckRequest.ResultsSetting 0, // 4: dispatch.v1.DispatchCheckRequest.debug:type_name -> dispatch.v1.DispatchCheckRequest.DebugSetting - 24, // 5: dispatch.v1.DispatchCheckResponse.metadata:type_name -> dispatch.v1.ResponseMeta - 27, // 6: dispatch.v1.DispatchCheckResponse.results_by_resource_id:type_name -> dispatch.v1.DispatchCheckResponse.ResultsByResourceIdEntry - 2, // 7: dispatch.v1.ResourceCheckResult.membership:type_name -> dispatch.v1.ResourceCheckResult.Membership - 32, // 8: dispatch.v1.ResourceCheckResult.expression:type_name -> core.v1.CaveatExpression - 23, // 9: dispatch.v1.DispatchExpandRequest.metadata:type_name -> dispatch.v1.ResolverMeta - 31, // 10: dispatch.v1.DispatchExpandRequest.resource_and_relation:type_name -> core.v1.ObjectAndRelation - 3, // 11: dispatch.v1.DispatchExpandRequest.expansion_mode:type_name -> dispatch.v1.DispatchExpandRequest.ExpansionMode - 24, // 12: dispatch.v1.DispatchExpandResponse.metadata:type_name -> dispatch.v1.ResponseMeta - 33, // 13: dispatch.v1.DispatchExpandResponse.tree_node:type_name -> core.v1.RelationTupleTreeNode - 23, // 14: dispatch.v1.DispatchReachableResourcesRequest.metadata:type_name -> dispatch.v1.ResolverMeta - 30, // 15: dispatch.v1.DispatchReachableResourcesRequest.resource_relation:type_name -> core.v1.RelationReference - 30, // 16: dispatch.v1.DispatchReachableResourcesRequest.subject_relation:type_name -> core.v1.RelationReference - 12, // 17: dispatch.v1.DispatchReachableResourcesRequest.optional_cursor:type_name -> dispatch.v1.Cursor - 4, // 18: dispatch.v1.ReachableResource.result_status:type_name -> dispatch.v1.ReachableResource.ResultStatus - 14, // 19: dispatch.v1.DispatchReachableResourcesResponse.resource:type_name -> dispatch.v1.ReachableResource - 24, // 20: dispatch.v1.DispatchReachableResourcesResponse.metadata:type_name -> dispatch.v1.ResponseMeta - 12, // 21: dispatch.v1.DispatchReachableResourcesResponse.after_response_cursor:type_name -> dispatch.v1.Cursor - 23, // 22: dispatch.v1.DispatchLookupResourcesRequest.metadata:type_name -> dispatch.v1.ResolverMeta - 30, // 23: dispatch.v1.DispatchLookupResourcesRequest.object_relation:type_name -> core.v1.RelationReference - 31, // 24: dispatch.v1.DispatchLookupResourcesRequest.subject:type_name -> core.v1.ObjectAndRelation - 34, // 25: dispatch.v1.DispatchLookupResourcesRequest.context:type_name -> google.protobuf.Struct - 12, // 26: dispatch.v1.DispatchLookupResourcesRequest.optional_cursor:type_name -> dispatch.v1.Cursor - 5, // 27: dispatch.v1.ResolvedResource.permissionship:type_name -> dispatch.v1.ResolvedResource.Permissionship - 24, // 28: dispatch.v1.DispatchLookupResourcesResponse.metadata:type_name -> dispatch.v1.ResponseMeta - 17, // 29: dispatch.v1.DispatchLookupResourcesResponse.resolved_resource:type_name -> dispatch.v1.ResolvedResource - 12, // 30: dispatch.v1.DispatchLookupResourcesResponse.after_response_cursor:type_name -> dispatch.v1.Cursor - 23, // 31: dispatch.v1.DispatchLookupSubjectsRequest.metadata:type_name -> dispatch.v1.ResolverMeta - 30, // 32: dispatch.v1.DispatchLookupSubjectsRequest.resource_relation:type_name -> core.v1.RelationReference - 30, // 33: dispatch.v1.DispatchLookupSubjectsRequest.subject_relation:type_name -> core.v1.RelationReference - 32, // 34: dispatch.v1.FoundSubject.caveat_expression:type_name -> core.v1.CaveatExpression - 20, // 35: dispatch.v1.FoundSubject.excluded_subjects:type_name -> dispatch.v1.FoundSubject - 20, // 36: dispatch.v1.FoundSubjects.found_subjects:type_name -> dispatch.v1.FoundSubject - 28, // 37: dispatch.v1.DispatchLookupSubjectsResponse.found_subjects_by_resource_id:type_name -> dispatch.v1.DispatchLookupSubjectsResponse.FoundSubjectsByResourceIdEntry - 24, // 38: dispatch.v1.DispatchLookupSubjectsResponse.metadata:type_name -> dispatch.v1.ResponseMeta - 25, // 39: dispatch.v1.ResponseMeta.debug_info:type_name -> dispatch.v1.DebugInformation - 26, // 40: dispatch.v1.DebugInformation.check:type_name -> dispatch.v1.CheckDebugTrace - 7, // 41: dispatch.v1.CheckDebugTrace.request:type_name -> dispatch.v1.DispatchCheckRequest - 6, // 42: dispatch.v1.CheckDebugTrace.resource_relation_type:type_name -> dispatch.v1.CheckDebugTrace.RelationType - 29, // 43: dispatch.v1.CheckDebugTrace.results:type_name -> dispatch.v1.CheckDebugTrace.ResultsEntry - 26, // 44: dispatch.v1.CheckDebugTrace.sub_problems:type_name -> dispatch.v1.CheckDebugTrace - 35, // 45: dispatch.v1.CheckDebugTrace.duration:type_name -> google.protobuf.Duration - 9, // 46: dispatch.v1.DispatchCheckResponse.ResultsByResourceIdEntry.value:type_name -> dispatch.v1.ResourceCheckResult - 21, // 47: dispatch.v1.DispatchLookupSubjectsResponse.FoundSubjectsByResourceIdEntry.value:type_name -> dispatch.v1.FoundSubjects - 9, // 48: dispatch.v1.CheckDebugTrace.ResultsEntry.value:type_name -> dispatch.v1.ResourceCheckResult - 7, // 49: dispatch.v1.DispatchService.DispatchCheck:input_type -> dispatch.v1.DispatchCheckRequest - 10, // 50: dispatch.v1.DispatchService.DispatchExpand:input_type -> dispatch.v1.DispatchExpandRequest - 13, // 51: dispatch.v1.DispatchService.DispatchReachableResources:input_type -> dispatch.v1.DispatchReachableResourcesRequest - 16, // 52: dispatch.v1.DispatchService.DispatchLookupResources:input_type -> dispatch.v1.DispatchLookupResourcesRequest - 19, // 53: dispatch.v1.DispatchService.DispatchLookupSubjects:input_type -> dispatch.v1.DispatchLookupSubjectsRequest - 8, // 54: dispatch.v1.DispatchService.DispatchCheck:output_type -> dispatch.v1.DispatchCheckResponse - 11, // 55: dispatch.v1.DispatchService.DispatchExpand:output_type -> dispatch.v1.DispatchExpandResponse - 15, // 56: dispatch.v1.DispatchService.DispatchReachableResources:output_type -> dispatch.v1.DispatchReachableResourcesResponse - 18, // 57: dispatch.v1.DispatchService.DispatchLookupResources:output_type -> dispatch.v1.DispatchLookupResourcesResponse - 22, // 58: dispatch.v1.DispatchService.DispatchLookupSubjects:output_type -> dispatch.v1.DispatchLookupSubjectsResponse - 54, // [54:59] is the sub-list for method output_type - 49, // [49:54] is the sub-list for method input_type - 49, // [49:49] is the sub-list for extension type_name - 49, // [49:49] is the sub-list for extension extendee - 0, // [0:49] is the sub-list for field type_name + 30, // 5: dispatch.v1.DispatchCheckRequest.check_hints:type_name -> dispatch.v1.DispatchCheckRequest.CheckHintsEntry + 27, // 6: dispatch.v1.DispatchCheckResponse.metadata:type_name -> dispatch.v1.ResponseMeta + 31, // 7: dispatch.v1.DispatchCheckResponse.results_by_resource_id:type_name -> dispatch.v1.DispatchCheckResponse.ResultsByResourceIdEntry + 2, // 8: dispatch.v1.ResourceCheckResult.membership:type_name -> dispatch.v1.ResourceCheckResult.Membership + 36, // 9: dispatch.v1.ResourceCheckResult.expression:type_name -> core.v1.CaveatExpression + 26, // 10: dispatch.v1.DispatchExpandRequest.metadata:type_name -> dispatch.v1.ResolverMeta + 35, // 11: dispatch.v1.DispatchExpandRequest.resource_and_relation:type_name -> core.v1.ObjectAndRelation + 3, // 12: dispatch.v1.DispatchExpandRequest.expansion_mode:type_name -> dispatch.v1.DispatchExpandRequest.ExpansionMode + 27, // 13: dispatch.v1.DispatchExpandResponse.metadata:type_name -> dispatch.v1.ResponseMeta + 37, // 14: dispatch.v1.DispatchExpandResponse.tree_node:type_name -> core.v1.RelationTupleTreeNode + 26, // 15: dispatch.v1.DispatchLookupResources2Request.metadata:type_name -> dispatch.v1.ResolverMeta + 34, // 16: dispatch.v1.DispatchLookupResources2Request.resource_relation:type_name -> core.v1.RelationReference + 34, // 17: dispatch.v1.DispatchLookupResources2Request.subject_relation:type_name -> core.v1.RelationReference + 35, // 18: dispatch.v1.DispatchLookupResources2Request.terminal_subject:type_name -> core.v1.ObjectAndRelation + 38, // 19: dispatch.v1.DispatchLookupResources2Request.context:type_name -> google.protobuf.Struct + 12, // 20: dispatch.v1.DispatchLookupResources2Request.optional_cursor:type_name -> dispatch.v1.Cursor + 14, // 21: dispatch.v1.DispatchLookupResources2Response.resource:type_name -> dispatch.v1.PossibleResource + 27, // 22: dispatch.v1.DispatchLookupResources2Response.metadata:type_name -> dispatch.v1.ResponseMeta + 12, // 23: dispatch.v1.DispatchLookupResources2Response.after_response_cursor:type_name -> dispatch.v1.Cursor + 26, // 24: dispatch.v1.DispatchReachableResourcesRequest.metadata:type_name -> dispatch.v1.ResolverMeta + 34, // 25: dispatch.v1.DispatchReachableResourcesRequest.resource_relation:type_name -> core.v1.RelationReference + 34, // 26: dispatch.v1.DispatchReachableResourcesRequest.subject_relation:type_name -> core.v1.RelationReference + 12, // 27: dispatch.v1.DispatchReachableResourcesRequest.optional_cursor:type_name -> dispatch.v1.Cursor + 4, // 28: dispatch.v1.ReachableResource.result_status:type_name -> dispatch.v1.ReachableResource.ResultStatus + 17, // 29: dispatch.v1.DispatchReachableResourcesResponse.resource:type_name -> dispatch.v1.ReachableResource + 27, // 30: dispatch.v1.DispatchReachableResourcesResponse.metadata:type_name -> dispatch.v1.ResponseMeta + 12, // 31: dispatch.v1.DispatchReachableResourcesResponse.after_response_cursor:type_name -> dispatch.v1.Cursor + 26, // 32: dispatch.v1.DispatchLookupResourcesRequest.metadata:type_name -> dispatch.v1.ResolverMeta + 34, // 33: dispatch.v1.DispatchLookupResourcesRequest.object_relation:type_name -> core.v1.RelationReference + 35, // 34: dispatch.v1.DispatchLookupResourcesRequest.subject:type_name -> core.v1.ObjectAndRelation + 38, // 35: dispatch.v1.DispatchLookupResourcesRequest.context:type_name -> google.protobuf.Struct + 12, // 36: dispatch.v1.DispatchLookupResourcesRequest.optional_cursor:type_name -> dispatch.v1.Cursor + 5, // 37: dispatch.v1.ResolvedResource.permissionship:type_name -> dispatch.v1.ResolvedResource.Permissionship + 27, // 38: dispatch.v1.DispatchLookupResourcesResponse.metadata:type_name -> dispatch.v1.ResponseMeta + 20, // 39: dispatch.v1.DispatchLookupResourcesResponse.resolved_resource:type_name -> dispatch.v1.ResolvedResource + 12, // 40: dispatch.v1.DispatchLookupResourcesResponse.after_response_cursor:type_name -> dispatch.v1.Cursor + 26, // 41: dispatch.v1.DispatchLookupSubjectsRequest.metadata:type_name -> dispatch.v1.ResolverMeta + 34, // 42: dispatch.v1.DispatchLookupSubjectsRequest.resource_relation:type_name -> core.v1.RelationReference + 34, // 43: dispatch.v1.DispatchLookupSubjectsRequest.subject_relation:type_name -> core.v1.RelationReference + 36, // 44: dispatch.v1.FoundSubject.caveat_expression:type_name -> core.v1.CaveatExpression + 23, // 45: dispatch.v1.FoundSubject.excluded_subjects:type_name -> dispatch.v1.FoundSubject + 23, // 46: dispatch.v1.FoundSubjects.found_subjects:type_name -> dispatch.v1.FoundSubject + 32, // 47: dispatch.v1.DispatchLookupSubjectsResponse.found_subjects_by_resource_id:type_name -> dispatch.v1.DispatchLookupSubjectsResponse.FoundSubjectsByResourceIdEntry + 27, // 48: dispatch.v1.DispatchLookupSubjectsResponse.metadata:type_name -> dispatch.v1.ResponseMeta + 28, // 49: dispatch.v1.ResponseMeta.debug_info:type_name -> dispatch.v1.DebugInformation + 29, // 50: dispatch.v1.DebugInformation.check:type_name -> dispatch.v1.CheckDebugTrace + 7, // 51: dispatch.v1.CheckDebugTrace.request:type_name -> dispatch.v1.DispatchCheckRequest + 6, // 52: dispatch.v1.CheckDebugTrace.resource_relation_type:type_name -> dispatch.v1.CheckDebugTrace.RelationType + 33, // 53: dispatch.v1.CheckDebugTrace.results:type_name -> dispatch.v1.CheckDebugTrace.ResultsEntry + 29, // 54: dispatch.v1.CheckDebugTrace.sub_problems:type_name -> dispatch.v1.CheckDebugTrace + 39, // 55: dispatch.v1.CheckDebugTrace.duration:type_name -> google.protobuf.Duration + 9, // 56: dispatch.v1.DispatchCheckRequest.CheckHintsEntry.value:type_name -> dispatch.v1.ResourceCheckResult + 9, // 57: dispatch.v1.DispatchCheckResponse.ResultsByResourceIdEntry.value:type_name -> dispatch.v1.ResourceCheckResult + 24, // 58: dispatch.v1.DispatchLookupSubjectsResponse.FoundSubjectsByResourceIdEntry.value:type_name -> dispatch.v1.FoundSubjects + 9, // 59: dispatch.v1.CheckDebugTrace.ResultsEntry.value:type_name -> dispatch.v1.ResourceCheckResult + 7, // 60: dispatch.v1.DispatchService.DispatchCheck:input_type -> dispatch.v1.DispatchCheckRequest + 10, // 61: dispatch.v1.DispatchService.DispatchExpand:input_type -> dispatch.v1.DispatchExpandRequest + 16, // 62: dispatch.v1.DispatchService.DispatchReachableResources:input_type -> dispatch.v1.DispatchReachableResourcesRequest + 19, // 63: dispatch.v1.DispatchService.DispatchLookupResources:input_type -> dispatch.v1.DispatchLookupResourcesRequest + 22, // 64: dispatch.v1.DispatchService.DispatchLookupSubjects:input_type -> dispatch.v1.DispatchLookupSubjectsRequest + 13, // 65: dispatch.v1.DispatchService.DispatchLookupResources2:input_type -> dispatch.v1.DispatchLookupResources2Request + 8, // 66: dispatch.v1.DispatchService.DispatchCheck:output_type -> dispatch.v1.DispatchCheckResponse + 11, // 67: dispatch.v1.DispatchService.DispatchExpand:output_type -> dispatch.v1.DispatchExpandResponse + 18, // 68: dispatch.v1.DispatchService.DispatchReachableResources:output_type -> dispatch.v1.DispatchReachableResourcesResponse + 21, // 69: dispatch.v1.DispatchService.DispatchLookupResources:output_type -> dispatch.v1.DispatchLookupResourcesResponse + 25, // 70: dispatch.v1.DispatchService.DispatchLookupSubjects:output_type -> dispatch.v1.DispatchLookupSubjectsResponse + 15, // 71: dispatch.v1.DispatchService.DispatchLookupResources2:output_type -> dispatch.v1.DispatchLookupResources2Response + 66, // [66:72] is the sub-list for method output_type + 60, // [60:66] is the sub-list for method input_type + 60, // [60:60] is the sub-list for extension type_name + 60, // [60:60] is the sub-list for extension extendee + 0, // [0:60] is the sub-list for field type_name } func init() { file_dispatch_v1_dispatch_proto_init() } @@ -2292,7 +2628,7 @@ func file_dispatch_v1_dispatch_proto_init() { } } file_dispatch_v1_dispatch_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DispatchReachableResourcesRequest); i { + switch v := v.(*DispatchLookupResources2Request); i { case 0: return &v.state case 1: @@ -2304,7 +2640,7 @@ func file_dispatch_v1_dispatch_proto_init() { } } file_dispatch_v1_dispatch_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ReachableResource); i { + switch v := v.(*PossibleResource); i { case 0: return &v.state case 1: @@ -2316,7 +2652,7 @@ func file_dispatch_v1_dispatch_proto_init() { } } file_dispatch_v1_dispatch_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DispatchReachableResourcesResponse); i { + switch v := v.(*DispatchLookupResources2Response); i { case 0: return &v.state case 1: @@ -2328,7 +2664,7 @@ func file_dispatch_v1_dispatch_proto_init() { } } file_dispatch_v1_dispatch_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DispatchLookupResourcesRequest); i { + switch v := v.(*DispatchReachableResourcesRequest); i { case 0: return &v.state case 1: @@ -2340,7 +2676,7 @@ func file_dispatch_v1_dispatch_proto_init() { } } file_dispatch_v1_dispatch_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ResolvedResource); i { + switch v := v.(*ReachableResource); i { case 0: return &v.state case 1: @@ -2352,7 +2688,7 @@ func file_dispatch_v1_dispatch_proto_init() { } } file_dispatch_v1_dispatch_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DispatchLookupResourcesResponse); i { + switch v := v.(*DispatchReachableResourcesResponse); i { case 0: return &v.state case 1: @@ -2364,7 +2700,7 @@ func file_dispatch_v1_dispatch_proto_init() { } } file_dispatch_v1_dispatch_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DispatchLookupSubjectsRequest); i { + switch v := v.(*DispatchLookupResourcesRequest); i { case 0: return &v.state case 1: @@ -2376,7 +2712,7 @@ func file_dispatch_v1_dispatch_proto_init() { } } file_dispatch_v1_dispatch_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FoundSubject); i { + switch v := v.(*ResolvedResource); i { case 0: return &v.state case 1: @@ -2388,7 +2724,7 @@ func file_dispatch_v1_dispatch_proto_init() { } } file_dispatch_v1_dispatch_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FoundSubjects); i { + switch v := v.(*DispatchLookupResourcesResponse); i { case 0: return &v.state case 1: @@ -2400,7 +2736,7 @@ func file_dispatch_v1_dispatch_proto_init() { } } file_dispatch_v1_dispatch_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DispatchLookupSubjectsResponse); i { + switch v := v.(*DispatchLookupSubjectsRequest); i { case 0: return &v.state case 1: @@ -2412,7 +2748,7 @@ func file_dispatch_v1_dispatch_proto_init() { } } file_dispatch_v1_dispatch_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ResolverMeta); i { + switch v := v.(*FoundSubject); i { case 0: return &v.state case 1: @@ -2424,7 +2760,7 @@ func file_dispatch_v1_dispatch_proto_init() { } } file_dispatch_v1_dispatch_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ResponseMeta); i { + switch v := v.(*FoundSubjects); i { case 0: return &v.state case 1: @@ -2436,7 +2772,7 @@ func file_dispatch_v1_dispatch_proto_init() { } } file_dispatch_v1_dispatch_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DebugInformation); i { + switch v := v.(*DispatchLookupSubjectsResponse); i { case 0: return &v.state case 1: @@ -2448,6 +2784,42 @@ func file_dispatch_v1_dispatch_proto_init() { } } file_dispatch_v1_dispatch_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ResolverMeta); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_dispatch_v1_dispatch_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ResponseMeta); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_dispatch_v1_dispatch_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DebugInformation); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_dispatch_v1_dispatch_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CheckDebugTrace); i { case 0: return &v.state @@ -2466,7 +2838,7 @@ func file_dispatch_v1_dispatch_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_dispatch_v1_dispatch_proto_rawDesc, NumEnums: 7, - NumMessages: 23, + NumMessages: 27, NumExtensions: 0, NumServices: 1, }, diff --git a/pkg/proto/dispatch/v1/dispatch.pb.validate.go b/pkg/proto/dispatch/v1/dispatch.pb.validate.go index bfe4243241..e27c737bc9 100644 --- a/pkg/proto/dispatch/v1/dispatch.pb.validate.go +++ b/pkg/proto/dispatch/v1/dispatch.pb.validate.go @@ -181,6 +181,52 @@ func (m *DispatchCheckRequest) validate(all bool) error { // no validation rules for Debug + { + sorted_keys := make([]string, len(m.GetCheckHints())) + i := 0 + for key := range m.GetCheckHints() { + sorted_keys[i] = key + i++ + } + sort.Slice(sorted_keys, func(i, j int) bool { return sorted_keys[i] < sorted_keys[j] }) + for _, key := range sorted_keys { + val := m.GetCheckHints()[key] + _ = val + + // no validation rules for CheckHints[key] + + if all { + switch v := interface{}(val).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, DispatchCheckRequestValidationError{ + field: fmt.Sprintf("CheckHints[%v]", key), + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, DispatchCheckRequestValidationError{ + field: fmt.Sprintf("CheckHints[%v]", key), + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(val).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return DispatchCheckRequestValidationError{ + field: fmt.Sprintf("CheckHints[%v]", key), + reason: "embedded message failed validation", + cause: err, + } + } + } + + } + } + if len(errors) > 0 { return DispatchCheckRequestMultiError(errors) } @@ -1015,6 +1061,623 @@ var _ interface { ErrorName() string } = CursorValidationError{} +// Validate checks the field values on DispatchLookupResources2Request with the +// rules defined in the proto definition for this message. If any rules are +// violated, the first error encountered is returned, or nil if there are no violations. +func (m *DispatchLookupResources2Request) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on DispatchLookupResources2Request with +// the rules defined in the proto definition for this message. If any rules +// are violated, the result is a list of violation errors wrapped in +// DispatchLookupResources2RequestMultiError, or nil if none found. +func (m *DispatchLookupResources2Request) ValidateAll() error { + return m.validate(true) +} + +func (m *DispatchLookupResources2Request) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + if m.GetMetadata() == nil { + err := DispatchLookupResources2RequestValidationError{ + field: "Metadata", + reason: "value is required", + } + if !all { + return err + } + errors = append(errors, err) + } + + if all { + switch v := interface{}(m.GetMetadata()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, DispatchLookupResources2RequestValidationError{ + field: "Metadata", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, DispatchLookupResources2RequestValidationError{ + field: "Metadata", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetMetadata()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return DispatchLookupResources2RequestValidationError{ + field: "Metadata", + reason: "embedded message failed validation", + cause: err, + } + } + } + + if m.GetResourceRelation() == nil { + err := DispatchLookupResources2RequestValidationError{ + field: "ResourceRelation", + reason: "value is required", + } + if !all { + return err + } + errors = append(errors, err) + } + + if all { + switch v := interface{}(m.GetResourceRelation()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, DispatchLookupResources2RequestValidationError{ + field: "ResourceRelation", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, DispatchLookupResources2RequestValidationError{ + field: "ResourceRelation", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetResourceRelation()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return DispatchLookupResources2RequestValidationError{ + field: "ResourceRelation", + reason: "embedded message failed validation", + cause: err, + } + } + } + + if m.GetSubjectRelation() == nil { + err := DispatchLookupResources2RequestValidationError{ + field: "SubjectRelation", + reason: "value is required", + } + if !all { + return err + } + errors = append(errors, err) + } + + if all { + switch v := interface{}(m.GetSubjectRelation()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, DispatchLookupResources2RequestValidationError{ + field: "SubjectRelation", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, DispatchLookupResources2RequestValidationError{ + field: "SubjectRelation", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetSubjectRelation()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return DispatchLookupResources2RequestValidationError{ + field: "SubjectRelation", + reason: "embedded message failed validation", + cause: err, + } + } + } + + if m.GetTerminalSubject() == nil { + err := DispatchLookupResources2RequestValidationError{ + field: "TerminalSubject", + reason: "value is required", + } + if !all { + return err + } + errors = append(errors, err) + } + + if all { + switch v := interface{}(m.GetTerminalSubject()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, DispatchLookupResources2RequestValidationError{ + field: "TerminalSubject", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, DispatchLookupResources2RequestValidationError{ + field: "TerminalSubject", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetTerminalSubject()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return DispatchLookupResources2RequestValidationError{ + field: "TerminalSubject", + reason: "embedded message failed validation", + cause: err, + } + } + } + + if all { + switch v := interface{}(m.GetContext()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, DispatchLookupResources2RequestValidationError{ + field: "Context", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, DispatchLookupResources2RequestValidationError{ + field: "Context", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetContext()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return DispatchLookupResources2RequestValidationError{ + field: "Context", + reason: "embedded message failed validation", + cause: err, + } + } + } + + if all { + switch v := interface{}(m.GetOptionalCursor()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, DispatchLookupResources2RequestValidationError{ + field: "OptionalCursor", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, DispatchLookupResources2RequestValidationError{ + field: "OptionalCursor", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetOptionalCursor()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return DispatchLookupResources2RequestValidationError{ + field: "OptionalCursor", + reason: "embedded message failed validation", + cause: err, + } + } + } + + // no validation rules for OptionalLimit + + if len(errors) > 0 { + return DispatchLookupResources2RequestMultiError(errors) + } + + return nil +} + +// DispatchLookupResources2RequestMultiError is an error wrapping multiple +// validation errors returned by DispatchLookupResources2Request.ValidateAll() +// if the designated constraints aren't met. +type DispatchLookupResources2RequestMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m DispatchLookupResources2RequestMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m DispatchLookupResources2RequestMultiError) AllErrors() []error { return m } + +// DispatchLookupResources2RequestValidationError is the validation error +// returned by DispatchLookupResources2Request.Validate if the designated +// constraints aren't met. +type DispatchLookupResources2RequestValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e DispatchLookupResources2RequestValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e DispatchLookupResources2RequestValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e DispatchLookupResources2RequestValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e DispatchLookupResources2RequestValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e DispatchLookupResources2RequestValidationError) ErrorName() string { + return "DispatchLookupResources2RequestValidationError" +} + +// Error satisfies the builtin error interface +func (e DispatchLookupResources2RequestValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sDispatchLookupResources2Request.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = DispatchLookupResources2RequestValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = DispatchLookupResources2RequestValidationError{} + +// Validate checks the field values on PossibleResource with the rules defined +// in the proto definition for this message. If any rules are violated, the +// first error encountered is returned, or nil if there are no violations. +func (m *PossibleResource) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on PossibleResource with the rules +// defined in the proto definition for this message. If any rules are +// violated, the result is a list of violation errors wrapped in +// PossibleResourceMultiError, or nil if none found. +func (m *PossibleResource) ValidateAll() error { + return m.validate(true) +} + +func (m *PossibleResource) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + // no validation rules for ResourceId + + if len(errors) > 0 { + return PossibleResourceMultiError(errors) + } + + return nil +} + +// PossibleResourceMultiError is an error wrapping multiple validation errors +// returned by PossibleResource.ValidateAll() if the designated constraints +// aren't met. +type PossibleResourceMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m PossibleResourceMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m PossibleResourceMultiError) AllErrors() []error { return m } + +// PossibleResourceValidationError is the validation error returned by +// PossibleResource.Validate if the designated constraints aren't met. +type PossibleResourceValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e PossibleResourceValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e PossibleResourceValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e PossibleResourceValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e PossibleResourceValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e PossibleResourceValidationError) ErrorName() string { return "PossibleResourceValidationError" } + +// Error satisfies the builtin error interface +func (e PossibleResourceValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sPossibleResource.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = PossibleResourceValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = PossibleResourceValidationError{} + +// Validate checks the field values on DispatchLookupResources2Response with +// the rules defined in the proto definition for this message. If any rules +// are violated, the first error encountered is returned, or nil if there are +// no violations. +func (m *DispatchLookupResources2Response) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on DispatchLookupResources2Response with +// the rules defined in the proto definition for this message. If any rules +// are violated, the result is a list of violation errors wrapped in +// DispatchLookupResources2ResponseMultiError, or nil if none found. +func (m *DispatchLookupResources2Response) ValidateAll() error { + return m.validate(true) +} + +func (m *DispatchLookupResources2Response) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + if all { + switch v := interface{}(m.GetResource()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, DispatchLookupResources2ResponseValidationError{ + field: "Resource", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, DispatchLookupResources2ResponseValidationError{ + field: "Resource", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetResource()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return DispatchLookupResources2ResponseValidationError{ + field: "Resource", + reason: "embedded message failed validation", + cause: err, + } + } + } + + if all { + switch v := interface{}(m.GetMetadata()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, DispatchLookupResources2ResponseValidationError{ + field: "Metadata", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, DispatchLookupResources2ResponseValidationError{ + field: "Metadata", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetMetadata()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return DispatchLookupResources2ResponseValidationError{ + field: "Metadata", + reason: "embedded message failed validation", + cause: err, + } + } + } + + if all { + switch v := interface{}(m.GetAfterResponseCursor()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, DispatchLookupResources2ResponseValidationError{ + field: "AfterResponseCursor", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, DispatchLookupResources2ResponseValidationError{ + field: "AfterResponseCursor", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetAfterResponseCursor()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return DispatchLookupResources2ResponseValidationError{ + field: "AfterResponseCursor", + reason: "embedded message failed validation", + cause: err, + } + } + } + + if len(errors) > 0 { + return DispatchLookupResources2ResponseMultiError(errors) + } + + return nil +} + +// DispatchLookupResources2ResponseMultiError is an error wrapping multiple +// validation errors returned by +// DispatchLookupResources2Response.ValidateAll() if the designated +// constraints aren't met. +type DispatchLookupResources2ResponseMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m DispatchLookupResources2ResponseMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m DispatchLookupResources2ResponseMultiError) AllErrors() []error { return m } + +// DispatchLookupResources2ResponseValidationError is the validation error +// returned by DispatchLookupResources2Response.Validate if the designated +// constraints aren't met. +type DispatchLookupResources2ResponseValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e DispatchLookupResources2ResponseValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e DispatchLookupResources2ResponseValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e DispatchLookupResources2ResponseValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e DispatchLookupResources2ResponseValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e DispatchLookupResources2ResponseValidationError) ErrorName() string { + return "DispatchLookupResources2ResponseValidationError" +} + +// Error satisfies the builtin error interface +func (e DispatchLookupResources2ResponseValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sDispatchLookupResources2Response.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = DispatchLookupResources2ResponseValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = DispatchLookupResources2ResponseValidationError{} + // Validate checks the field values on DispatchReachableResourcesRequest with // the rules defined in the proto definition for this message. If any rules // are violated, the first error encountered is returned, or nil if there are diff --git a/pkg/proto/dispatch/v1/dispatch_grpc.pb.go b/pkg/proto/dispatch/v1/dispatch_grpc.pb.go index 14a476ee31..3d6a8ce734 100644 --- a/pkg/proto/dispatch/v1/dispatch_grpc.pb.go +++ b/pkg/proto/dispatch/v1/dispatch_grpc.pb.go @@ -24,6 +24,7 @@ const ( DispatchService_DispatchReachableResources_FullMethodName = "/dispatch.v1.DispatchService/DispatchReachableResources" DispatchService_DispatchLookupResources_FullMethodName = "/dispatch.v1.DispatchService/DispatchLookupResources" DispatchService_DispatchLookupSubjects_FullMethodName = "/dispatch.v1.DispatchService/DispatchLookupSubjects" + DispatchService_DispatchLookupResources2_FullMethodName = "/dispatch.v1.DispatchService/DispatchLookupResources2" ) // DispatchServiceClient is the client API for DispatchService service. @@ -35,6 +36,7 @@ type DispatchServiceClient interface { DispatchReachableResources(ctx context.Context, in *DispatchReachableResourcesRequest, opts ...grpc.CallOption) (DispatchService_DispatchReachableResourcesClient, error) DispatchLookupResources(ctx context.Context, in *DispatchLookupResourcesRequest, opts ...grpc.CallOption) (DispatchService_DispatchLookupResourcesClient, error) DispatchLookupSubjects(ctx context.Context, in *DispatchLookupSubjectsRequest, opts ...grpc.CallOption) (DispatchService_DispatchLookupSubjectsClient, error) + DispatchLookupResources2(ctx context.Context, in *DispatchLookupResources2Request, opts ...grpc.CallOption) (DispatchService_DispatchLookupResources2Client, error) } type dispatchServiceClient struct { @@ -159,6 +161,38 @@ func (x *dispatchServiceDispatchLookupSubjectsClient) Recv() (*DispatchLookupSub return m, nil } +func (c *dispatchServiceClient) DispatchLookupResources2(ctx context.Context, in *DispatchLookupResources2Request, opts ...grpc.CallOption) (DispatchService_DispatchLookupResources2Client, error) { + stream, err := c.cc.NewStream(ctx, &DispatchService_ServiceDesc.Streams[3], DispatchService_DispatchLookupResources2_FullMethodName, opts...) + if err != nil { + return nil, err + } + x := &dispatchServiceDispatchLookupResources2Client{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type DispatchService_DispatchLookupResources2Client interface { + Recv() (*DispatchLookupResources2Response, error) + grpc.ClientStream +} + +type dispatchServiceDispatchLookupResources2Client struct { + grpc.ClientStream +} + +func (x *dispatchServiceDispatchLookupResources2Client) Recv() (*DispatchLookupResources2Response, error) { + m := new(DispatchLookupResources2Response) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + // DispatchServiceServer is the server API for DispatchService service. // All implementations must embed UnimplementedDispatchServiceServer // for forward compatibility @@ -168,6 +202,7 @@ type DispatchServiceServer interface { DispatchReachableResources(*DispatchReachableResourcesRequest, DispatchService_DispatchReachableResourcesServer) error DispatchLookupResources(*DispatchLookupResourcesRequest, DispatchService_DispatchLookupResourcesServer) error DispatchLookupSubjects(*DispatchLookupSubjectsRequest, DispatchService_DispatchLookupSubjectsServer) error + DispatchLookupResources2(*DispatchLookupResources2Request, DispatchService_DispatchLookupResources2Server) error mustEmbedUnimplementedDispatchServiceServer() } @@ -190,6 +225,9 @@ func (UnimplementedDispatchServiceServer) DispatchLookupResources(*DispatchLooku func (UnimplementedDispatchServiceServer) DispatchLookupSubjects(*DispatchLookupSubjectsRequest, DispatchService_DispatchLookupSubjectsServer) error { return status.Errorf(codes.Unimplemented, "method DispatchLookupSubjects not implemented") } +func (UnimplementedDispatchServiceServer) DispatchLookupResources2(*DispatchLookupResources2Request, DispatchService_DispatchLookupResources2Server) error { + return status.Errorf(codes.Unimplemented, "method DispatchLookupResources2 not implemented") +} func (UnimplementedDispatchServiceServer) mustEmbedUnimplementedDispatchServiceServer() {} // UnsafeDispatchServiceServer may be embedded to opt out of forward compatibility for this service. @@ -302,6 +340,27 @@ func (x *dispatchServiceDispatchLookupSubjectsServer) Send(m *DispatchLookupSubj return x.ServerStream.SendMsg(m) } +func _DispatchService_DispatchLookupResources2_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(DispatchLookupResources2Request) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(DispatchServiceServer).DispatchLookupResources2(m, &dispatchServiceDispatchLookupResources2Server{stream}) +} + +type DispatchService_DispatchLookupResources2Server interface { + Send(*DispatchLookupResources2Response) error + grpc.ServerStream +} + +type dispatchServiceDispatchLookupResources2Server struct { + grpc.ServerStream +} + +func (x *dispatchServiceDispatchLookupResources2Server) Send(m *DispatchLookupResources2Response) error { + return x.ServerStream.SendMsg(m) +} + // DispatchService_ServiceDesc is the grpc.ServiceDesc for DispatchService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -334,6 +393,11 @@ var DispatchService_ServiceDesc = grpc.ServiceDesc{ Handler: _DispatchService_DispatchLookupSubjects_Handler, ServerStreams: true, }, + { + StreamName: "DispatchLookupResources2", + Handler: _DispatchService_DispatchLookupResources2_Handler, + ServerStreams: true, + }, }, Metadata: "dispatch/v1/dispatch.proto", } diff --git a/pkg/proto/dispatch/v1/dispatch_vtproto.pb.go b/pkg/proto/dispatch/v1/dispatch_vtproto.pb.go index 78aac4308f..80543e28d6 100644 --- a/pkg/proto/dispatch/v1/dispatch_vtproto.pb.go +++ b/pkg/proto/dispatch/v1/dispatch_vtproto.pb.go @@ -51,6 +51,13 @@ func (m *DispatchCheckRequest) CloneVT() *DispatchCheckRequest { r.Subject = proto.Clone(rhs).(*v1.ObjectAndRelation) } } + if rhs := m.CheckHints; rhs != nil { + tmpContainer := make(map[string]*ResourceCheckResult, len(rhs)) + for k, v := range rhs { + tmpContainer[k] = v.CloneVT() + } + r.CheckHints = tmpContainer + } if len(m.unknownFields) > 0 { r.unknownFields = make([]byte, len(m.unknownFields)) copy(r.unknownFields, m.unknownFields) @@ -188,6 +195,98 @@ func (m *Cursor) CloneMessageVT() proto.Message { return m.CloneVT() } +func (m *DispatchLookupResources2Request) CloneVT() *DispatchLookupResources2Request { + if m == nil { + return (*DispatchLookupResources2Request)(nil) + } + r := new(DispatchLookupResources2Request) + r.Metadata = m.Metadata.CloneVT() + r.Context = (*structpb.Struct)((*structpb1.Struct)(m.Context).CloneVT()) + r.OptionalCursor = m.OptionalCursor.CloneVT() + r.OptionalLimit = m.OptionalLimit + if rhs := m.ResourceRelation; rhs != nil { + if vtpb, ok := interface{}(rhs).(interface{ CloneVT() *v1.RelationReference }); ok { + r.ResourceRelation = vtpb.CloneVT() + } else { + r.ResourceRelation = proto.Clone(rhs).(*v1.RelationReference) + } + } + if rhs := m.SubjectRelation; rhs != nil { + if vtpb, ok := interface{}(rhs).(interface{ CloneVT() *v1.RelationReference }); ok { + r.SubjectRelation = vtpb.CloneVT() + } else { + r.SubjectRelation = proto.Clone(rhs).(*v1.RelationReference) + } + } + if rhs := m.SubjectIds; rhs != nil { + tmpContainer := make([]string, len(rhs)) + copy(tmpContainer, rhs) + r.SubjectIds = tmpContainer + } + if rhs := m.TerminalSubject; rhs != nil { + if vtpb, ok := interface{}(rhs).(interface{ CloneVT() *v1.ObjectAndRelation }); ok { + r.TerminalSubject = vtpb.CloneVT() + } else { + r.TerminalSubject = proto.Clone(rhs).(*v1.ObjectAndRelation) + } + } + if len(m.unknownFields) > 0 { + r.unknownFields = make([]byte, len(m.unknownFields)) + copy(r.unknownFields, m.unknownFields) + } + return r +} + +func (m *DispatchLookupResources2Request) CloneMessageVT() proto.Message { + return m.CloneVT() +} + +func (m *PossibleResource) CloneVT() *PossibleResource { + if m == nil { + return (*PossibleResource)(nil) + } + r := new(PossibleResource) + r.ResourceId = m.ResourceId + if rhs := m.ForSubjectIds; rhs != nil { + tmpContainer := make([]string, len(rhs)) + copy(tmpContainer, rhs) + r.ForSubjectIds = tmpContainer + } + if rhs := m.MissingContextParams; rhs != nil { + tmpContainer := make([]string, len(rhs)) + copy(tmpContainer, rhs) + r.MissingContextParams = tmpContainer + } + if len(m.unknownFields) > 0 { + r.unknownFields = make([]byte, len(m.unknownFields)) + copy(r.unknownFields, m.unknownFields) + } + return r +} + +func (m *PossibleResource) CloneMessageVT() proto.Message { + return m.CloneVT() +} + +func (m *DispatchLookupResources2Response) CloneVT() *DispatchLookupResources2Response { + if m == nil { + return (*DispatchLookupResources2Response)(nil) + } + r := new(DispatchLookupResources2Response) + r.Resource = m.Resource.CloneVT() + r.Metadata = m.Metadata.CloneVT() + r.AfterResponseCursor = m.AfterResponseCursor.CloneVT() + if len(m.unknownFields) > 0 { + r.unknownFields = make([]byte, len(m.unknownFields)) + copy(r.unknownFields, m.unknownFields) + } + return r +} + +func (m *DispatchLookupResources2Response) CloneMessageVT() proto.Message { + return m.CloneVT() +} + func (m *DispatchReachableResourcesRequest) CloneVT() *DispatchReachableResourcesRequest { if m == nil { return (*DispatchReachableResourcesRequest)(nil) @@ -595,6 +694,26 @@ func (this *DispatchCheckRequest) EqualVT(that *DispatchCheckRequest) bool { if this.Debug != that.Debug { return false } + if len(this.CheckHints) != len(that.CheckHints) { + return false + } + for i, vx := range this.CheckHints { + vy, ok := that.CheckHints[i] + if !ok { + return false + } + if p, q := vx, vy; p != q { + if p == nil { + p = &ResourceCheckResult{} + } + if q == nil { + q = &ResourceCheckResult{} + } + if !p.EqualVT(q) { + return false + } + } + } return string(this.unknownFields) == string(that.unknownFields) } @@ -768,6 +887,132 @@ func (this *Cursor) EqualMessageVT(thatMsg proto.Message) bool { } return this.EqualVT(that) } +func (this *DispatchLookupResources2Request) EqualVT(that *DispatchLookupResources2Request) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if !this.Metadata.EqualVT(that.Metadata) { + return false + } + if equal, ok := interface{}(this.ResourceRelation).(interface { + EqualVT(*v1.RelationReference) bool + }); ok { + if !equal.EqualVT(that.ResourceRelation) { + return false + } + } else if !proto.Equal(this.ResourceRelation, that.ResourceRelation) { + return false + } + if equal, ok := interface{}(this.SubjectRelation).(interface { + EqualVT(*v1.RelationReference) bool + }); ok { + if !equal.EqualVT(that.SubjectRelation) { + return false + } + } else if !proto.Equal(this.SubjectRelation, that.SubjectRelation) { + return false + } + if len(this.SubjectIds) != len(that.SubjectIds) { + return false + } + for i, vx := range this.SubjectIds { + vy := that.SubjectIds[i] + if vx != vy { + return false + } + } + if equal, ok := interface{}(this.TerminalSubject).(interface { + EqualVT(*v1.ObjectAndRelation) bool + }); ok { + if !equal.EqualVT(that.TerminalSubject) { + return false + } + } else if !proto.Equal(this.TerminalSubject, that.TerminalSubject) { + return false + } + if !(*structpb1.Struct)(this.Context).EqualVT((*structpb1.Struct)(that.Context)) { + return false + } + if !this.OptionalCursor.EqualVT(that.OptionalCursor) { + return false + } + if this.OptionalLimit != that.OptionalLimit { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *DispatchLookupResources2Request) EqualMessageVT(thatMsg proto.Message) bool { + that, ok := thatMsg.(*DispatchLookupResources2Request) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *PossibleResource) EqualVT(that *PossibleResource) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.ResourceId != that.ResourceId { + return false + } + if len(this.ForSubjectIds) != len(that.ForSubjectIds) { + return false + } + for i, vx := range this.ForSubjectIds { + vy := that.ForSubjectIds[i] + if vx != vy { + return false + } + } + if len(this.MissingContextParams) != len(that.MissingContextParams) { + return false + } + for i, vx := range this.MissingContextParams { + vy := that.MissingContextParams[i] + if vx != vy { + return false + } + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *PossibleResource) EqualMessageVT(thatMsg proto.Message) bool { + that, ok := thatMsg.(*PossibleResource) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *DispatchLookupResources2Response) EqualVT(that *DispatchLookupResources2Response) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if !this.Resource.EqualVT(that.Resource) { + return false + } + if !this.Metadata.EqualVT(that.Metadata) { + return false + } + if !this.AfterResponseCursor.EqualVT(that.AfterResponseCursor) { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *DispatchLookupResources2Response) EqualMessageVT(thatMsg proto.Message) bool { + that, ok := thatMsg.(*DispatchLookupResources2Response) + if !ok { + return false + } + return this.EqualVT(that) +} func (this *DispatchReachableResourcesRequest) EqualVT(that *DispatchReachableResourcesRequest) bool { if this == that { return true @@ -1311,6 +1556,28 @@ func (m *DispatchCheckRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } + if len(m.CheckHints) > 0 { + for k := range m.CheckHints { + v := m.CheckHints[k] + baseI := i + size, err := v.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = protohelpers.EncodeVarint(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x3a + } + } if m.Debug != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Debug)) i-- @@ -1703,7 +1970,7 @@ func (m *Cursor) MarshalToSizedBufferVT(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *DispatchReachableResourcesRequest) MarshalVT() (dAtA []byte, err error) { +func (m *DispatchLookupResources2Request) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } @@ -1716,12 +1983,12 @@ func (m *DispatchReachableResourcesRequest) MarshalVT() (dAtA []byte, err error) return dAtA[:n], nil } -func (m *DispatchReachableResourcesRequest) MarshalToVT(dAtA []byte) (int, error) { +func (m *DispatchLookupResources2Request) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } -func (m *DispatchReachableResourcesRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { +func (m *DispatchLookupResources2Request) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } @@ -1736,7 +2003,7 @@ func (m *DispatchReachableResourcesRequest) MarshalToSizedBufferVT(dAtA []byte) if m.OptionalLimit != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.OptionalLimit)) i-- - dAtA[i] = 0x30 + dAtA[i] = 0x40 } if m.OptionalCursor != nil { size, err := m.OptionalCursor.MarshalToSizedBufferVT(dAtA[:i]) @@ -1746,19 +2013,20 @@ func (m *DispatchReachableResourcesRequest) MarshalToSizedBufferVT(dAtA []byte) i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- - dAtA[i] = 0x2a + dAtA[i] = 0x3a } - if len(m.SubjectIds) > 0 { - for iNdEx := len(m.SubjectIds) - 1; iNdEx >= 0; iNdEx-- { - i -= len(m.SubjectIds[iNdEx]) - copy(dAtA[i:], m.SubjectIds[iNdEx]) - i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.SubjectIds[iNdEx]))) - i-- - dAtA[i] = 0x22 + if m.Context != nil { + size, err := (*structpb1.Struct)(m.Context).MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x32 } - if m.SubjectRelation != nil { - if vtmsg, ok := interface{}(m.SubjectRelation).(interface { + if m.TerminalSubject != nil { + if vtmsg, ok := interface{}(m.TerminalSubject).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) @@ -1768,7 +2036,7 @@ func (m *DispatchReachableResourcesRequest) MarshalToSizedBufferVT(dAtA []byte) i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { - encoded, err := proto.Marshal(m.SubjectRelation) + encoded, err := proto.Marshal(m.TerminalSubject) if err != nil { return 0, err } @@ -1777,13 +2045,44 @@ func (m *DispatchReachableResourcesRequest) MarshalToSizedBufferVT(dAtA []byte) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- - dAtA[i] = 0x1a + dAtA[i] = 0x2a } - if m.ResourceRelation != nil { - if vtmsg, ok := interface{}(m.ResourceRelation).(interface { - MarshalToSizedBufferVT([]byte) (int, error) - }); ok { - size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) + if len(m.SubjectIds) > 0 { + for iNdEx := len(m.SubjectIds) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.SubjectIds[iNdEx]) + copy(dAtA[i:], m.SubjectIds[iNdEx]) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.SubjectIds[iNdEx]))) + i-- + dAtA[i] = 0x22 + } + } + if m.SubjectRelation != nil { + if vtmsg, ok := interface{}(m.SubjectRelation).(interface { + MarshalToSizedBufferVT([]byte) (int, error) + }); ok { + size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + } else { + encoded, err := proto.Marshal(m.SubjectRelation) + if err != nil { + return 0, err + } + i -= len(encoded) + copy(dAtA[i:], encoded) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) + } + i-- + dAtA[i] = 0x1a + } + if m.ResourceRelation != nil { + if vtmsg, ok := interface{}(m.ResourceRelation).(interface { + MarshalToSizedBufferVT([]byte) (int, error) + }); ok { + size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } @@ -1814,7 +2113,7 @@ func (m *DispatchReachableResourcesRequest) MarshalToSizedBufferVT(dAtA []byte) return len(dAtA) - i, nil } -func (m *ReachableResource) MarshalVT() (dAtA []byte, err error) { +func (m *PossibleResource) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } @@ -1827,12 +2126,12 @@ func (m *ReachableResource) MarshalVT() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *ReachableResource) MarshalToVT(dAtA []byte) (int, error) { +func (m *PossibleResource) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } -func (m *ReachableResource) MarshalToSizedBufferVT(dAtA []byte) (int, error) { +func (m *PossibleResource) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } @@ -1844,20 +2143,24 @@ func (m *ReachableResource) MarshalToSizedBufferVT(dAtA []byte) (int, error) { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } + if len(m.MissingContextParams) > 0 { + for iNdEx := len(m.MissingContextParams) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.MissingContextParams[iNdEx]) + copy(dAtA[i:], m.MissingContextParams[iNdEx]) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.MissingContextParams[iNdEx]))) + i-- + dAtA[i] = 0x1a + } + } if len(m.ForSubjectIds) > 0 { for iNdEx := len(m.ForSubjectIds) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.ForSubjectIds[iNdEx]) copy(dAtA[i:], m.ForSubjectIds[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ForSubjectIds[iNdEx]))) i-- - dAtA[i] = 0x1a + dAtA[i] = 0x12 } } - if m.ResultStatus != 0 { - i = protohelpers.EncodeVarint(dAtA, i, uint64(m.ResultStatus)) - i-- - dAtA[i] = 0x10 - } if len(m.ResourceId) > 0 { i -= len(m.ResourceId) copy(dAtA[i:], m.ResourceId) @@ -1868,7 +2171,7 @@ func (m *ReachableResource) MarshalToSizedBufferVT(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *DispatchReachableResourcesResponse) MarshalVT() (dAtA []byte, err error) { +func (m *DispatchLookupResources2Response) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } @@ -1881,12 +2184,12 @@ func (m *DispatchReachableResourcesResponse) MarshalVT() (dAtA []byte, err error return dAtA[:n], nil } -func (m *DispatchReachableResourcesResponse) MarshalToVT(dAtA []byte) (int, error) { +func (m *DispatchLookupResources2Response) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } -func (m *DispatchReachableResourcesResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { +func (m *DispatchLookupResources2Response) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } @@ -1931,7 +2234,7 @@ func (m *DispatchReachableResourcesResponse) MarshalToSizedBufferVT(dAtA []byte) return len(dAtA) - i, nil } -func (m *DispatchLookupResourcesRequest) MarshalVT() (dAtA []byte, err error) { +func (m *DispatchReachableResourcesRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } @@ -1944,12 +2247,12 @@ func (m *DispatchLookupResourcesRequest) MarshalVT() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *DispatchLookupResourcesRequest) MarshalToVT(dAtA []byte) (int, error) { +func (m *DispatchReachableResourcesRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } -func (m *DispatchLookupResourcesRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { +func (m *DispatchReachableResourcesRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } @@ -1961,18 +2264,13 @@ func (m *DispatchLookupResourcesRequest) MarshalToSizedBufferVT(dAtA []byte) (in i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } - if m.OptionalCursor != nil { - size, err := m.OptionalCursor.MarshalToSizedBufferVT(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + if m.OptionalLimit != 0 { + i = protohelpers.EncodeVarint(dAtA, i, uint64(m.OptionalLimit)) i-- - dAtA[i] = 0x32 + dAtA[i] = 0x30 } - if m.Context != nil { - size, err := (*structpb1.Struct)(m.Context).MarshalToSizedBufferVT(dAtA[:i]) + if m.OptionalCursor != nil { + size, err := m.OptionalCursor.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } @@ -1981,13 +2279,17 @@ func (m *DispatchLookupResourcesRequest) MarshalToSizedBufferVT(dAtA []byte) (in i-- dAtA[i] = 0x2a } - if m.OptionalLimit != 0 { - i = protohelpers.EncodeVarint(dAtA, i, uint64(m.OptionalLimit)) - i-- - dAtA[i] = 0x20 + if len(m.SubjectIds) > 0 { + for iNdEx := len(m.SubjectIds) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.SubjectIds[iNdEx]) + copy(dAtA[i:], m.SubjectIds[iNdEx]) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.SubjectIds[iNdEx]))) + i-- + dAtA[i] = 0x22 + } } - if m.Subject != nil { - if vtmsg, ok := interface{}(m.Subject).(interface { + if m.SubjectRelation != nil { + if vtmsg, ok := interface{}(m.SubjectRelation).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) @@ -1997,7 +2299,7 @@ func (m *DispatchLookupResourcesRequest) MarshalToSizedBufferVT(dAtA []byte) (in i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { - encoded, err := proto.Marshal(m.Subject) + encoded, err := proto.Marshal(m.SubjectRelation) if err != nil { return 0, err } @@ -2008,8 +2310,8 @@ func (m *DispatchLookupResourcesRequest) MarshalToSizedBufferVT(dAtA []byte) (in i-- dAtA[i] = 0x1a } - if m.ObjectRelation != nil { - if vtmsg, ok := interface{}(m.ObjectRelation).(interface { + if m.ResourceRelation != nil { + if vtmsg, ok := interface{}(m.ResourceRelation).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) @@ -2019,7 +2321,7 @@ func (m *DispatchLookupResourcesRequest) MarshalToSizedBufferVT(dAtA []byte) (in i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { - encoded, err := proto.Marshal(m.ObjectRelation) + encoded, err := proto.Marshal(m.ResourceRelation) if err != nil { return 0, err } @@ -2043,7 +2345,7 @@ func (m *DispatchLookupResourcesRequest) MarshalToSizedBufferVT(dAtA []byte) (in return len(dAtA) - i, nil } -func (m *ResolvedResource) MarshalVT() (dAtA []byte, err error) { +func (m *ReachableResource) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } @@ -2056,12 +2358,12 @@ func (m *ResolvedResource) MarshalVT() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *ResolvedResource) MarshalToVT(dAtA []byte) (int, error) { +func (m *ReachableResource) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } -func (m *ResolvedResource) MarshalToSizedBufferVT(dAtA []byte) (int, error) { +func (m *ReachableResource) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } @@ -2073,17 +2375,17 @@ func (m *ResolvedResource) MarshalToSizedBufferVT(dAtA []byte) (int, error) { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } - if len(m.MissingRequiredContext) > 0 { - for iNdEx := len(m.MissingRequiredContext) - 1; iNdEx >= 0; iNdEx-- { - i -= len(m.MissingRequiredContext[iNdEx]) - copy(dAtA[i:], m.MissingRequiredContext[iNdEx]) - i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.MissingRequiredContext[iNdEx]))) + if len(m.ForSubjectIds) > 0 { + for iNdEx := len(m.ForSubjectIds) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.ForSubjectIds[iNdEx]) + copy(dAtA[i:], m.ForSubjectIds[iNdEx]) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ForSubjectIds[iNdEx]))) i-- dAtA[i] = 0x1a } } - if m.Permissionship != 0 { - i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Permissionship)) + if m.ResultStatus != 0 { + i = protohelpers.EncodeVarint(dAtA, i, uint64(m.ResultStatus)) i-- dAtA[i] = 0x10 } @@ -2097,7 +2399,7 @@ func (m *ResolvedResource) MarshalToSizedBufferVT(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *DispatchLookupResourcesResponse) MarshalVT() (dAtA []byte, err error) { +func (m *DispatchReachableResourcesResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } @@ -2110,12 +2412,12 @@ func (m *DispatchLookupResourcesResponse) MarshalVT() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *DispatchLookupResourcesResponse) MarshalToVT(dAtA []byte) (int, error) { +func (m *DispatchReachableResourcesResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } -func (m *DispatchLookupResourcesResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { +func (m *DispatchReachableResourcesResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } @@ -2137,8 +2439,8 @@ func (m *DispatchLookupResourcesResponse) MarshalToSizedBufferVT(dAtA []byte) (i i-- dAtA[i] = 0x1a } - if m.ResolvedResource != nil { - size, err := m.ResolvedResource.MarshalToSizedBufferVT(dAtA[:i]) + if m.Metadata != nil { + size, err := m.Metadata.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } @@ -2147,8 +2449,8 @@ func (m *DispatchLookupResourcesResponse) MarshalToSizedBufferVT(dAtA []byte) (i i-- dAtA[i] = 0x12 } - if m.Metadata != nil { - size, err := m.Metadata.MarshalToSizedBufferVT(dAtA[:i]) + if m.Resource != nil { + size, err := m.Resource.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } @@ -2160,7 +2462,7 @@ func (m *DispatchLookupResourcesResponse) MarshalToSizedBufferVT(dAtA []byte) (i return len(dAtA) - i, nil } -func (m *DispatchLookupSubjectsRequest) MarshalVT() (dAtA []byte, err error) { +func (m *DispatchLookupResourcesRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } @@ -2173,12 +2475,12 @@ func (m *DispatchLookupSubjectsRequest) MarshalVT() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *DispatchLookupSubjectsRequest) MarshalToVT(dAtA []byte) (int, error) { +func (m *DispatchLookupResourcesRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } -func (m *DispatchLookupSubjectsRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { +func (m *DispatchLookupResourcesRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } @@ -2190,8 +2492,33 @@ func (m *DispatchLookupSubjectsRequest) MarshalToSizedBufferVT(dAtA []byte) (int i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } - if m.SubjectRelation != nil { - if vtmsg, ok := interface{}(m.SubjectRelation).(interface { + if m.OptionalCursor != nil { + size, err := m.OptionalCursor.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x32 + } + if m.Context != nil { + size, err := (*structpb1.Struct)(m.Context).MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x2a + } + if m.OptionalLimit != 0 { + i = protohelpers.EncodeVarint(dAtA, i, uint64(m.OptionalLimit)) + i-- + dAtA[i] = 0x20 + } + if m.Subject != nil { + if vtmsg, ok := interface{}(m.Subject).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) @@ -2201,7 +2528,7 @@ func (m *DispatchLookupSubjectsRequest) MarshalToSizedBufferVT(dAtA []byte) (int i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { - encoded, err := proto.Marshal(m.SubjectRelation) + encoded, err := proto.Marshal(m.Subject) if err != nil { return 0, err } @@ -2210,19 +2537,10 @@ func (m *DispatchLookupSubjectsRequest) MarshalToSizedBufferVT(dAtA []byte) (int i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- - dAtA[i] = 0x22 - } - if len(m.ResourceIds) > 0 { - for iNdEx := len(m.ResourceIds) - 1; iNdEx >= 0; iNdEx-- { - i -= len(m.ResourceIds[iNdEx]) - copy(dAtA[i:], m.ResourceIds[iNdEx]) - i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ResourceIds[iNdEx]))) - i-- - dAtA[i] = 0x1a - } + dAtA[i] = 0x1a } - if m.ResourceRelation != nil { - if vtmsg, ok := interface{}(m.ResourceRelation).(interface { + if m.ObjectRelation != nil { + if vtmsg, ok := interface{}(m.ObjectRelation).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) @@ -2232,7 +2550,7 @@ func (m *DispatchLookupSubjectsRequest) MarshalToSizedBufferVT(dAtA []byte) (int i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { - encoded, err := proto.Marshal(m.ResourceRelation) + encoded, err := proto.Marshal(m.ObjectRelation) if err != nil { return 0, err } @@ -2256,7 +2574,7 @@ func (m *DispatchLookupSubjectsRequest) MarshalToSizedBufferVT(dAtA []byte) (int return len(dAtA) - i, nil } -func (m *FoundSubject) MarshalVT() (dAtA []byte, err error) { +func (m *ResolvedResource) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } @@ -2269,12 +2587,12 @@ func (m *FoundSubject) MarshalVT() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *FoundSubject) MarshalToVT(dAtA []byte) (int, error) { +func (m *ResolvedResource) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } -func (m *FoundSubject) MarshalToSizedBufferVT(dAtA []byte) (int, error) { +func (m *ResolvedResource) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } @@ -2286,51 +2604,31 @@ func (m *FoundSubject) MarshalToSizedBufferVT(dAtA []byte) (int, error) { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } - if len(m.ExcludedSubjects) > 0 { - for iNdEx := len(m.ExcludedSubjects) - 1; iNdEx >= 0; iNdEx-- { - size, err := m.ExcludedSubjects[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + if len(m.MissingRequiredContext) > 0 { + for iNdEx := len(m.MissingRequiredContext) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.MissingRequiredContext[iNdEx]) + copy(dAtA[i:], m.MissingRequiredContext[iNdEx]) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.MissingRequiredContext[iNdEx]))) i-- dAtA[i] = 0x1a } } - if m.CaveatExpression != nil { - if vtmsg, ok := interface{}(m.CaveatExpression).(interface { - MarshalToSizedBufferVT([]byte) (int, error) - }); ok { - size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) - } else { - encoded, err := proto.Marshal(m.CaveatExpression) - if err != nil { - return 0, err - } - i -= len(encoded) - copy(dAtA[i:], encoded) - i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) - } + if m.Permissionship != 0 { + i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Permissionship)) i-- - dAtA[i] = 0x12 + dAtA[i] = 0x10 } - if len(m.SubjectId) > 0 { - i -= len(m.SubjectId) - copy(dAtA[i:], m.SubjectId) - i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.SubjectId))) + if len(m.ResourceId) > 0 { + i -= len(m.ResourceId) + copy(dAtA[i:], m.ResourceId) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ResourceId))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } -func (m *FoundSubjects) MarshalVT() (dAtA []byte, err error) { +func (m *DispatchLookupResourcesResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } @@ -2343,12 +2641,12 @@ func (m *FoundSubjects) MarshalVT() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *FoundSubjects) MarshalToVT(dAtA []byte) (int, error) { +func (m *DispatchLookupResourcesResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } -func (m *FoundSubjects) MarshalToSizedBufferVT(dAtA []byte) (int, error) { +func (m *DispatchLookupResourcesResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } @@ -2360,16 +2658,249 @@ func (m *FoundSubjects) MarshalToSizedBufferVT(dAtA []byte) (int, error) { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } - if len(m.FoundSubjects) > 0 { - for iNdEx := len(m.FoundSubjects) - 1; iNdEx >= 0; iNdEx-- { - size, err := m.FoundSubjects[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) - i-- - dAtA[i] = 0xa + if m.AfterResponseCursor != nil { + size, err := m.AfterResponseCursor.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x1a + } + if m.ResolvedResource != nil { + size, err := m.ResolvedResource.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 + } + if m.Metadata != nil { + size, err := m.Metadata.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *DispatchLookupSubjectsRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *DispatchLookupSubjectsRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *DispatchLookupSubjectsRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.SubjectRelation != nil { + if vtmsg, ok := interface{}(m.SubjectRelation).(interface { + MarshalToSizedBufferVT([]byte) (int, error) + }); ok { + size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + } else { + encoded, err := proto.Marshal(m.SubjectRelation) + if err != nil { + return 0, err + } + i -= len(encoded) + copy(dAtA[i:], encoded) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) + } + i-- + dAtA[i] = 0x22 + } + if len(m.ResourceIds) > 0 { + for iNdEx := len(m.ResourceIds) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.ResourceIds[iNdEx]) + copy(dAtA[i:], m.ResourceIds[iNdEx]) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ResourceIds[iNdEx]))) + i-- + dAtA[i] = 0x1a + } + } + if m.ResourceRelation != nil { + if vtmsg, ok := interface{}(m.ResourceRelation).(interface { + MarshalToSizedBufferVT([]byte) (int, error) + }); ok { + size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + } else { + encoded, err := proto.Marshal(m.ResourceRelation) + if err != nil { + return 0, err + } + i -= len(encoded) + copy(dAtA[i:], encoded) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) + } + i-- + dAtA[i] = 0x12 + } + if m.Metadata != nil { + size, err := m.Metadata.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *FoundSubject) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *FoundSubject) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *FoundSubject) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.ExcludedSubjects) > 0 { + for iNdEx := len(m.ExcludedSubjects) - 1; iNdEx >= 0; iNdEx-- { + size, err := m.ExcludedSubjects[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x1a + } + } + if m.CaveatExpression != nil { + if vtmsg, ok := interface{}(m.CaveatExpression).(interface { + MarshalToSizedBufferVT([]byte) (int, error) + }); ok { + size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + } else { + encoded, err := proto.Marshal(m.CaveatExpression) + if err != nil { + return 0, err + } + i -= len(encoded) + copy(dAtA[i:], encoded) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) + } + i-- + dAtA[i] = 0x12 + } + if len(m.SubjectId) > 0 { + i -= len(m.SubjectId) + copy(dAtA[i:], m.SubjectId) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.SubjectId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *FoundSubjects) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *FoundSubjects) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *FoundSubjects) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.FoundSubjects) > 0 { + for iNdEx := len(m.FoundSubjects) - 1; iNdEx >= 0; iNdEx-- { + size, err := m.FoundSubjects[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa } } return len(dAtA) - i, nil @@ -2744,6 +3275,19 @@ func (m *DispatchCheckRequest) SizeVT() (n int) { if m.Debug != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Debug)) } + if len(m.CheckHints) > 0 { + for k, v := range m.CheckHints { + _ = k + _ = v + l = 0 + if v != nil { + l = v.SizeVT() + } + l += 1 + protohelpers.SizeOfVarint(uint64(l)) + mapEntrySize := 1 + len(k) + protohelpers.SizeOfVarint(uint64(len(k))) + l + n += mapEntrySize + 1 + protohelpers.SizeOfVarint(uint64(mapEntrySize)) + } + } n += len(m.unknownFields) return n } @@ -2874,7 +3418,7 @@ func (m *Cursor) SizeVT() (n int) { return n } -func (m *DispatchReachableResourcesRequest) SizeVT() (n int) { +func (m *DispatchLookupResources2Request) SizeVT() (n int) { if m == nil { return 0 } @@ -2910,11 +3454,120 @@ func (m *DispatchReachableResourcesRequest) SizeVT() (n int) { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } - if m.OptionalCursor != nil { - l = m.OptionalCursor.SizeVT() - n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) - } - if m.OptionalLimit != 0 { + if m.TerminalSubject != nil { + if size, ok := interface{}(m.TerminalSubject).(interface { + SizeVT() int + }); ok { + l = size.SizeVT() + } else { + l = proto.Size(m.TerminalSubject) + } + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.Context != nil { + l = (*structpb1.Struct)(m.Context).SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.OptionalCursor != nil { + l = m.OptionalCursor.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.OptionalLimit != 0 { + n += 1 + protohelpers.SizeOfVarint(uint64(m.OptionalLimit)) + } + n += len(m.unknownFields) + return n +} + +func (m *PossibleResource) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ResourceId) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if len(m.ForSubjectIds) > 0 { + for _, s := range m.ForSubjectIds { + l = len(s) + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + } + if len(m.MissingContextParams) > 0 { + for _, s := range m.MissingContextParams { + l = len(s) + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + } + n += len(m.unknownFields) + return n +} + +func (m *DispatchLookupResources2Response) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Resource != nil { + l = m.Resource.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.Metadata != nil { + l = m.Metadata.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.AfterResponseCursor != nil { + l = m.AfterResponseCursor.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *DispatchReachableResourcesRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Metadata != nil { + l = m.Metadata.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.ResourceRelation != nil { + if size, ok := interface{}(m.ResourceRelation).(interface { + SizeVT() int + }); ok { + l = size.SizeVT() + } else { + l = proto.Size(m.ResourceRelation) + } + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.SubjectRelation != nil { + if size, ok := interface{}(m.SubjectRelation).(interface { + SizeVT() int + }); ok { + l = size.SizeVT() + } else { + l = proto.Size(m.SubjectRelation) + } + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if len(m.SubjectIds) > 0 { + for _, s := range m.SubjectIds { + l = len(s) + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + } + if m.OptionalCursor != nil { + l = m.OptionalCursor.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.OptionalLimit != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.OptionalLimit)) } n += len(m.unknownFields) @@ -3497,6 +4150,135 @@ func (m *DispatchCheckRequest) UnmarshalVT(dAtA []byte) error { break } } + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CheckHints", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.CheckHints == nil { + m.CheckHints = make(map[string]*ResourceCheckResult) + } + var mapkey string + var mapvalue *ResourceCheckResult + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return protohelpers.ErrInvalidLength + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return protohelpers.ErrInvalidLength + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var mapmsglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + mapmsglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if mapmsglen < 0 { + return protohelpers.ErrInvalidLength + } + postmsgIndex := iNdEx + mapmsglen + if postmsgIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postmsgIndex > l { + return io.ErrUnexpectedEOF + } + mapvalue = &ResourceCheckResult{} + if err := mapvalue.UnmarshalVT(dAtA[iNdEx:postmsgIndex]); err != nil { + return err + } + iNdEx = postmsgIndex + } else { + iNdEx = entryPreIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.CheckHints[mapkey] = mapvalue + iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) @@ -4264,6 +5046,654 @@ func (m *Cursor) UnmarshalVT(dAtA []byte) error { } return nil } +func (m *DispatchLookupResources2Request) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: DispatchLookupResources2Request: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DispatchLookupResources2Request: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Metadata == nil { + m.Metadata = &ResolverMeta{} + } + if err := m.Metadata.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ResourceRelation", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ResourceRelation == nil { + m.ResourceRelation = &v1.RelationReference{} + } + if unmarshal, ok := interface{}(m.ResourceRelation).(interface { + UnmarshalVT([]byte) error + }); ok { + if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + } else { + if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.ResourceRelation); err != nil { + return err + } + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SubjectRelation", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.SubjectRelation == nil { + m.SubjectRelation = &v1.RelationReference{} + } + if unmarshal, ok := interface{}(m.SubjectRelation).(interface { + UnmarshalVT([]byte) error + }); ok { + if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + } else { + if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.SubjectRelation); err != nil { + return err + } + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SubjectIds", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SubjectIds = append(m.SubjectIds, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TerminalSubject", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.TerminalSubject == nil { + m.TerminalSubject = &v1.ObjectAndRelation{} + } + if unmarshal, ok := interface{}(m.TerminalSubject).(interface { + UnmarshalVT([]byte) error + }); ok { + if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + } else { + if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.TerminalSubject); err != nil { + return err + } + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Context", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Context == nil { + m.Context = &structpb.Struct{} + } + if err := (*structpb1.Struct)(m.Context).UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field OptionalCursor", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.OptionalCursor == nil { + m.OptionalCursor = &Cursor{} + } + if err := m.OptionalCursor.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 8: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field OptionalLimit", wireType) + } + m.OptionalLimit = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.OptionalLimit |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PossibleResource) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PossibleResource: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PossibleResource: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ResourceId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ResourceId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ForSubjectIds", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ForSubjectIds = append(m.ForSubjectIds, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MissingContextParams", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.MissingContextParams = append(m.MissingContextParams, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *DispatchLookupResources2Response) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: DispatchLookupResources2Response: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DispatchLookupResources2Response: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Resource", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Resource == nil { + m.Resource = &PossibleResource{} + } + if err := m.Resource.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Metadata == nil { + m.Metadata = &ResponseMeta{} + } + if err := m.Metadata.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AfterResponseCursor", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.AfterResponseCursor == nil { + m.AfterResponseCursor = &Cursor{} + } + if err := m.AfterResponseCursor.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *DispatchReachableResourcesRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/pkg/typesystem/checkhints.go b/pkg/typesystem/checkhints.go new file mode 100644 index 0000000000..b1a5ccaadd --- /dev/null +++ b/pkg/typesystem/checkhints.go @@ -0,0 +1,128 @@ +package typesystem + +import ( + "fmt" + "strings" + + core "github.com/authzed/spicedb/pkg/proto/core/v1" + "github.com/authzed/spicedb/pkg/tuple" +) + +// ResourceCheckHintForRelation returns the resource portion of a check for a specific relation. +func ResourceCheckHintForRelation(resourceType string, resourceID string, relation string) string { + return tuple.StringONR(&core.ObjectAndRelation{ + Namespace: resourceType, + ObjectId: resourceID, + Relation: relation, + }) +} + +// ResourceCheckHintForArrow returns the resource portion of a check for a specific arrow (tupleset -> userset). +func ResourceCheckHintForArrow(resourceType string, resourceID string, tuplesetRelation string, computedUsersetRelation string) string { + return ResourceCheckHintForRelation(resourceType, resourceID, tuplesetRelation) + "->" + computedUsersetRelation +} + +// CheckHint returns a string representation of a check hint for a resource and subject. This is used in the CheckRequest to +// provide hints to the dispatcher about a subproblem whose result has already been computed, allowing the check to skip +// the evaluation of that subproblem. +func CheckHint(resourceHint string, subject *core.ObjectAndRelation) string { + return resourceHint + "@" + tuple.StringONR(subject) +} + +// CheckHintType is an enum for the type of check hint. +type CheckHintType int + +const ( + CheckHintTypeUnknown CheckHintType = iota + + // CheckHintTypeRelation is a hint for a specific relation. + CheckHintTypeRelation + + // CheckHintTypeArrow is a hint for a specific arrow (tupleset -> userset). + CheckHintTypeArrow +) + +// ParsedCheckHint is a parsed check hint. +type ParsedCheckHint struct { + // Type is the type of check hint. + Type CheckHintType + + // Resource is the resource portion of the check hint. + Resource *core.ObjectAndRelation + + // Subject is the subject portion of the check hint. + Subject *core.ObjectAndRelation + + // ArrowComputedUsersetRelation is the relation of the computed userset in an arrow hint. Only + // valid if Type is CheckHintTypeArrow. + ArrowComputedUsersetRelation string +} + +// AsHintString returns the parsed check hint as a string. +func (pch ParsedCheckHint) AsHintString() string { + if pch.Type == CheckHintTypeArrow { + return CheckHint(ResourceCheckHintForArrow(pch.Resource.Namespace, pch.Resource.ObjectId, pch.Resource.Relation, pch.ArrowComputedUsersetRelation), pch.Subject) + } + + return CheckHint(ResourceCheckHintForRelation(pch.Resource.Namespace, pch.Resource.ObjectId, pch.Resource.Relation), pch.Subject) +} + +// ParseCheckHint parses a check hint string into a ParsedCheckHint or returns an error if the hint is invalid. +func ParseCheckHint(checkHint string) (*ParsedCheckHint, error) { + // If the check hint contains an arrow, it is an arrow hint. + if strings.Contains(checkHint, "->") { + resourceAndSubject := strings.Split(checkHint, "@") + if len(resourceAndSubject) != 2 { + return nil, fmt.Errorf("invalid number of elements in check hint: %q", checkHint) + } + + resourceAndArrow := resourceAndSubject[0] + subject := resourceAndSubject[1] + + resourceAndArrowSplit := strings.Split(resourceAndArrow, "->") + if len(resourceAndArrowSplit) != 2 { + return nil, fmt.Errorf("invalid number of resources in hint: %q", checkHint) + } + + resource := tuple.ParseONR(resourceAndArrowSplit[0]) + if resource == nil { + return nil, fmt.Errorf("could not parse portion %q of check hint: %q", resourceAndArrowSplit[0], checkHint) + } + + if err := resource.Validate(); err != nil { + return nil, fmt.Errorf("invalid resource in check hint: %w", err) + } + + parsedSubject := tuple.ParseSubjectONR(subject) + if parsedSubject == nil { + return nil, fmt.Errorf("could not parse portion %q of check hint: %q", subject, checkHint) + } + + if err := parsedSubject.Validate(); err != nil { + return nil, fmt.Errorf("invalid subject in check hint: %w", err) + } + + return &ParsedCheckHint{ + Type: CheckHintTypeArrow, + Resource: resource, + Subject: parsedSubject, + ArrowComputedUsersetRelation: resourceAndArrowSplit[1], + }, nil + } + + // Otherwise, it is a relation hint, represented as a single relationship string. + parsed := tuple.Parse(checkHint) + if parsed == nil { + return nil, fmt.Errorf("could not parse check hint: %q", checkHint) + } + + if err := parsed.Validate(); err != nil { + return nil, fmt.Errorf("invalid check hint: %w", err) + } + + return &ParsedCheckHint{ + Type: CheckHintTypeRelation, + Resource: parsed.ResourceAndRelation, + Subject: parsed.Subject, + }, nil +} diff --git a/pkg/typesystem/checkhints_test.go b/pkg/typesystem/checkhints_test.go new file mode 100644 index 0000000000..ba5c894630 --- /dev/null +++ b/pkg/typesystem/checkhints_test.go @@ -0,0 +1,144 @@ +package typesystem + +import ( + "testing" + + "github.com/stretchr/testify/require" + + core "github.com/authzed/spicedb/pkg/proto/core/v1" +) + +func TestCheckHints(t *testing.T) { + tcs := []struct { + name string + hintString string + expectedParsedHint *ParsedCheckHint + expectedError string + }{ + { + name: "empty", + hintString: "", + expectedError: "could not parse check hint: \"\"", + }, + { + name: "invalid", + hintString: "invalid", + expectedError: "could not parse check hint: \"invalid\"", + }, + { + name: "invalid resource", + hintString: "invalid:#viewer@user:fred", + expectedError: "could not parse check hint: \"invalid:#viewer@user:fred\"", + }, + { + name: "invalid subject", + hintString: ResourceCheckHintForRelation("type", "id", "relation") + "@" + "invalid", + expectedError: "could not parse check hint: \"type:id#relation@invalid\"", + }, + { + name: "valid relation terminal subject", + hintString: ResourceCheckHintForRelation("type", "id", "relation") + "@user:fred", + expectedParsedHint: &ParsedCheckHint{ + Type: CheckHintTypeRelation, + Resource: &core.ObjectAndRelation{ + Namespace: "type", + ObjectId: "id", + Relation: "relation", + }, + Subject: &core.ObjectAndRelation{ + Namespace: "user", + ObjectId: "fred", + Relation: "...", + }, + }, + }, + { + name: "valid relation non terminal subject", + hintString: ResourceCheckHintForRelation("type", "id", "relation") + "@user:fred#viewer", + expectedParsedHint: &ParsedCheckHint{ + Type: CheckHintTypeRelation, + Resource: &core.ObjectAndRelation{ + Namespace: "type", + ObjectId: "id", + Relation: "relation", + }, + Subject: &core.ObjectAndRelation{ + Namespace: "user", + ObjectId: "fred", + Relation: "viewer", + }, + }, + }, + { + name: "valid arrow terminal subject", + hintString: ResourceCheckHintForArrow("type", "id", "relation", "computed") + "@user:fred", + expectedParsedHint: &ParsedCheckHint{ + Type: CheckHintTypeArrow, + Resource: &core.ObjectAndRelation{ + Namespace: "type", + ObjectId: "id", + Relation: "relation", + }, + Subject: &core.ObjectAndRelation{ + Namespace: "user", + ObjectId: "fred", + Relation: "...", + }, + ArrowComputedUsersetRelation: "computed", + }, + }, + { + name: "valid arrow non terminal subject", + hintString: ResourceCheckHintForArrow("type", "id", "relation", "computed") + "@user:fred#viewer", + expectedParsedHint: &ParsedCheckHint{ + Type: CheckHintTypeArrow, + Resource: &core.ObjectAndRelation{ + Namespace: "type", + ObjectId: "id", + Relation: "relation", + }, + Subject: &core.ObjectAndRelation{ + Namespace: "user", + ObjectId: "fred", + Relation: "viewer", + }, + ArrowComputedUsersetRelation: "computed", + }, + }, + { + name: "invalid arrow", + hintString: ResourceCheckHintForArrow("type", "id", "relation", "computed") + "->bar@user:fred", + expectedError: "invalid number of resources in hint: \"type:id#relation->computed->bar@user:fred\"", + }, + { + name: "invalid arrow resource", + hintString: ResourceCheckHintForArrow("type", "id$", "relation", "computed") + "@user:fred", + expectedError: "could not parse portion \"type:id$#relation\" of check hint: \"type:id$#relation->computed@user:fred\"", + }, + { + name: "invalid arrow relation", + hintString: ResourceCheckHintForArrow("type", "id", "relation$", "computed") + "@user:fred", + expectedError: "could not parse portion \"type:id#relation$\" of check hint: \"type:id#relation$->computed@user:fred\"", + }, + { + name: "invalid resource ID", + hintString: ResourceCheckHintForRelation("type", "id$", "relation") + "@user:fred", + expectedError: "could not parse check hint: \"type:id$#relation@user:fred\"", + }, + } + + for _, tc := range tcs { + tc := tc + t.Run(tc.name, func(t *testing.T) { + parsedHint, err := ParseCheckHint(tc.hintString) + if tc.expectedError != "" { + require.EqualError(t, err, tc.expectedError) + return + } + + require.NoError(t, err) + require.Equal(t, tc.expectedParsedHint, parsedHint) + require.Equal(t, tc.hintString, parsedHint.AsHintString()) + }) + } +} diff --git a/pkg/typesystem/reachabilitygraph.go b/pkg/typesystem/reachabilitygraph.go index 828dee20c1..83fe2a4caf 100644 --- a/pkg/typesystem/reachabilitygraph.go +++ b/pkg/typesystem/reachabilitygraph.go @@ -67,6 +67,15 @@ func (re ReachabilityEntrypoint) EntrypointKind() core.ReachabilityEntrypoint_Re return re.re.Kind } +// ComputedUsersetRelation returns the tupleset relation of the computed userset, if a TUPLESET_TO_USERSET_ENTRYPOINT. +func (re ReachabilityEntrypoint) ComputedUsersetRelation() (string, error) { + if re.EntrypointKind() != core.ReachabilityEntrypoint_COMPUTED_USERSET_ENTRYPOINT { + return "", fmt.Errorf("cannot call ComputedUsersetRelation for kind %v", re.EntrypointKind()) + } + + return re.re.ComputedUsersetRelation, nil +} + // TuplesetRelation returns the tupleset relation of the TTU, if a TUPLESET_TO_USERSET_ENTRYPOINT. func (re ReachabilityEntrypoint) TuplesetRelation() (string, error) { if re.EntrypointKind() != core.ReachabilityEntrypoint_TUPLESET_TO_USERSET_ENTRYPOINT { @@ -85,6 +94,24 @@ func (re ReachabilityEntrypoint) DirectRelation() (*core.RelationReference, erro return re.re.TargetRelation, nil } +// CheckHintForResource returns the key that can be used for this entrypoint as the resource in a check hint +// when this entrypoint has been computed. +func (re ReachabilityEntrypoint) CheckHintForResource(resourceID string) (string, error) { + switch re.EntrypointKind() { + case core.ReachabilityEntrypoint_RELATION_ENTRYPOINT: + return "", spiceerrors.MustBugf("cannot call CheckHintResourceKey for kind %v", re.EntrypointKind()) + + case core.ReachabilityEntrypoint_TUPLESET_TO_USERSET_ENTRYPOINT: + return ResourceCheckHintForArrow(re.re.TargetRelation.Namespace, resourceID, re.re.TuplesetRelation, re.re.ComputedUsersetRelation), nil + + case core.ReachabilityEntrypoint_COMPUTED_USERSET_ENTRYPOINT: + return ResourceCheckHintForRelation(re.re.TargetRelation.Namespace, resourceID, re.re.ComputedUsersetRelation), nil + + default: + return "", spiceerrors.MustBugf("unknown relation entrypoint kind") + } +} + // ContainingRelationOrPermission is the relation or permission containing this entrypoint. func (re ReachabilityEntrypoint) ContainingRelationOrPermission() *core.RelationReference { return re.parentRelation diff --git a/pkg/typesystem/reachabilitygraphbuilder.go b/pkg/typesystem/reachabilitygraphbuilder.go index 1abbf5297f..208c432585 100644 --- a/pkg/typesystem/reachabilitygraphbuilder.go +++ b/pkg/typesystem/reachabilitygraphbuilder.go @@ -82,9 +82,10 @@ func computeRewriteOpReachability(ctx context.Context, children []*core.SetOpera case *core.SetOperation_Child_ComputedUserset: // A computed userset adds an entrypoint indicating that the relation is rewritten. err := addSubjectEntrypoint(graph, ts.nsDef.Name, child.ComputedUserset.Relation, &core.ReachabilityEntrypoint{ - Kind: core.ReachabilityEntrypoint_COMPUTED_USERSET_ENTRYPOINT, - TargetRelation: rr, - ResultStatus: operationResultState, + Kind: core.ReachabilityEntrypoint_COMPUTED_USERSET_ENTRYPOINT, + TargetRelation: rr, + ComputedUsersetRelation: child.ComputedUserset.Relation, + ResultStatus: operationResultState, }) if err != nil { return err @@ -187,10 +188,11 @@ func computeTTUReachability( if relTypeSystem.HasRelation(computedUsersetRelation) { err := addSubjectEntrypoint(graph, allowedRelationType.Namespace, computedUsersetRelation, &core.ReachabilityEntrypoint{ - Kind: core.ReachabilityEntrypoint_TUPLESET_TO_USERSET_ENTRYPOINT, - TargetRelation: rr, - ResultStatus: operationResultState, - TuplesetRelation: tuplesetRelation, + Kind: core.ReachabilityEntrypoint_TUPLESET_TO_USERSET_ENTRYPOINT, + TargetRelation: rr, + ResultStatus: operationResultState, + ComputedUsersetRelation: computedUsersetRelation, + TuplesetRelation: tuplesetRelation, }) if err != nil { return err diff --git a/proto/internal/core/v1/core.proto b/proto/internal/core/v1/core.proto index 5fa55ea94f..1b8a57ab59 100644 --- a/proto/internal/core/v1/core.proto +++ b/proto/internal/core/v1/core.proto @@ -348,6 +348,12 @@ message ReachabilityEntrypoint { * represents, if applicable. */ string tupleset_relation = 5; + + /** + * computed_userset_relation is the name of the computed userset relation on the ComputedUserset + * this entrypoint represents, if applicable. + */ + string computed_userset_relation = 6; } /** diff --git a/proto/internal/dispatch/v1/dispatch.proto b/proto/internal/dispatch/v1/dispatch.proto index 92c78a1a2e..788660679c 100644 --- a/proto/internal/dispatch/v1/dispatch.proto +++ b/proto/internal/dispatch/v1/dispatch.proto @@ -16,6 +16,9 @@ service DispatchService { rpc DispatchLookupResources(DispatchLookupResourcesRequest) returns (stream DispatchLookupResourcesResponse) {} rpc DispatchLookupSubjects(DispatchLookupSubjectsRequest) returns (stream DispatchLookupSubjectsResponse) {} + + + rpc DispatchLookupResources2(DispatchLookupResources2Request) returns (stream DispatchLookupResources2Response) {} } message DispatchCheckRequest { @@ -40,6 +43,15 @@ message DispatchCheckRequest { ResultsSetting results_setting = 5; DebugSetting debug = 6; + + /** + * check_hints are hints provided to the check call to help the resolver optimize the check + * by skipping calculations for the provided checks. The string key is the fully qualified + * "relationtuple"-string for the problem, e.g. `document:example#relation@user:someuser`. + * It is up to the caller to *ensure* that the hints provided are correct; if incorrect, + * the resolver may return incorrect results which will in turn be cached. + */ + map check_hints = 7; } message DispatchCheckResponse { @@ -83,6 +95,32 @@ message Cursor { uint32 dispatch_version = 3; } +message DispatchLookupResources2Request { + ResolverMeta metadata = 1 [(validate.rules).message.required = true]; + + core.v1.RelationReference resource_relation = 2 [(validate.rules).message.required = true]; + core.v1.RelationReference subject_relation = 3 [(validate.rules).message.required = true]; + repeated string subject_ids = 4; + + core.v1.ObjectAndRelation terminal_subject = 5 [(validate.rules).message.required = true]; + google.protobuf.Struct context = 6; + + Cursor optional_cursor = 7; + uint32 optional_limit = 8; +} + +message PossibleResource { + string resource_id = 1; + repeated string for_subject_ids = 2; + repeated string missing_context_params = 3; +} + +message DispatchLookupResources2Response { + PossibleResource resource = 1; + ResponseMeta metadata = 2; + Cursor after_response_cursor = 3; +} + message DispatchReachableResourcesRequest { ResolverMeta metadata = 1 [(validate.rules).message.required = true];