From 4690599354559e4a426dc0b013f87971861c2e77 Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Tue, 26 Apr 2022 15:46:18 -0400 Subject: [PATCH] Add headers for requesting and receiving the server version and other debug information --- pkg/requestmeta/requestmeta.go | 39 ++++++++++++++++++++++++++++++++ pkg/responsemeta/responsemeta.go | 37 ++++++++++++++++++++++++++---- 2 files changed, 71 insertions(+), 5 deletions(-) create mode 100644 pkg/requestmeta/requestmeta.go diff --git a/pkg/requestmeta/requestmeta.go b/pkg/requestmeta/requestmeta.go new file mode 100644 index 0000000..a859b9f --- /dev/null +++ b/pkg/requestmeta/requestmeta.go @@ -0,0 +1,39 @@ +package requestmeta + +import ( + "context" + + "google.golang.org/grpc/metadata" +) + +// RequestMetadataHeaderKey defines a key in the request metadata header. +type RequestMetadataHeaderKey string + +// BoolRequestMetadataHeaderKey defines a key for a boolean vlaue in the request metadata header. +type BoolRequestMetadataHeaderKey RequestMetadataHeaderKey + +const ( + // RequestServerVersion, if specified in a request header, asks SpiceDB to return its + // server version in the response header (if supported). + // Value: `1` + RequestServerVersion BoolRequestMetadataHeaderKey = "io.spicedb.requestversion" +) + +// AddRequestHeaders returns a new context with the given values as request headers. +func AddRequestHeaders(ctx context.Context, keys ...BoolRequestMetadataHeaderKey) context.Context { + values := make(map[RequestMetadataHeaderKey]string, len(keys)) + for _, key := range keys { + values[RequestMetadataHeaderKey(key)] = "1" + } + return SetRequestHeaders(ctx, values) +} + +// SetRequestHeaders returns a new context with the given values as request headers. +func SetRequestHeaders(ctx context.Context, values map[RequestMetadataHeaderKey]string) context.Context { + pairs := make([]string, 0, len(values)*2) + for key, value := range values { + pairs = append(pairs, string(key)) + pairs = append(pairs, value) + } + return metadata.NewOutgoingContext(ctx, metadata.Pairs(pairs...)) +} diff --git a/pkg/responsemeta/responsemeta.go b/pkg/responsemeta/responsemeta.go index 8e81204..b9616ec 100644 --- a/pkg/responsemeta/responsemeta.go +++ b/pkg/responsemeta/responsemeta.go @@ -15,6 +15,10 @@ type ResponseMetadataHeaderKey string const ( // RequestID is the key in the response header metadata for the request's tracking ID, if any. RequestID ResponseMetadataHeaderKey = "io.spicedb.respmeta.requestid" + + // ServerVersion is the key in the response header metadata holding the version of the server + // handling the API request, if requested via a request header. + ServerVersion ResponseMetadataHeaderKey = "io.spicedb.debug.version" ) // ResponseMetadataTrailerKey defines a key in the response metadata trailer. @@ -52,16 +56,39 @@ func SetResponseTrailerMetadata(ctx context.Context, values map[ResponseMetadata return grpc.SetTrailer(ctx, metadata.Pairs(pairs...)) } -// GetIntResponseTrailerMetadata retrieves an integer value for the given key in the trailer +// ListResponseTrailerMetadata retrieves the string value(s) for the given key in the trailer // metadata of a SpiceDB API response. -func GetIntResponseTrailerMetadata(trailer metadata.MD, key ResponseMetadataTrailerKey) (int, error) { +func ListResponseTrailerMetadata(trailer metadata.MD, key ResponseMetadataTrailerKey) ([]string, error) { values := trailer.Get(string(key)) if len(values) == 0 { - return 0, fmt.Errorf("key `%s` not found in trailer", key) + return []string{}, fmt.Errorf("key `%s` not found in trailer", key) + } + + return values, nil +} + +// GetResponseTrailerMetadata retrieves a string value for the given key in the trailer +// metadata of a SpiceDB API response. +func GetResponseTrailerMetadata(trailer metadata.MD, key ResponseMetadataTrailerKey) (string, error) { + values, err := ListResponseTrailerMetadata(trailer, key) + if err != nil { + return "", err } + if len(values) != 1 { - return 0, fmt.Errorf("key `%s` found multiple times in trailer", key) + return "", fmt.Errorf("key `%s` found multiple times in trailer", key) + } + + return values[0], nil +} + +// GetIntResponseTrailerMetadata retrieves an integer value for the given key in the trailer +// metadata of a SpiceDB API response. +func GetIntResponseTrailerMetadata(trailer metadata.MD, key ResponseMetadataTrailerKey) (int, error) { + found, err := GetResponseTrailerMetadata(trailer, key) + if err != nil { + return 0, err } - return strconv.Atoi(values[0]) + return strconv.Atoi(found) }