Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add headers for requesting and receiving the server version #57

Merged
merged 1 commit into from Apr 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
39 changes: 39 additions & 0 deletions 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 {
jzelinskie marked this conversation as resolved.
Show resolved Hide resolved
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...))
}
37 changes: 32 additions & 5 deletions pkg/responsemeta/responsemeta.go
Expand Up @@ -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.
Expand Down Expand Up @@ -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)
}