-
Notifications
You must be signed in to change notification settings - Fork 9
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
Optional configs for setting custom headers / metadata for REST / gRPC operations #18
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good addition overall. Might want to consider some breaking changes pre-1.0 to clean up some of the options in the constructors.
pinecone/client.go
Outdated
} | ||
|
||
func (c *Client) IndexWithAdditionalMetadata(host string, namespace string, additionalMetadata map[string]string) (*IndexConnection, error) { | ||
idx, err := newIndexConnection(c.apiKey, host, namespace, c.sourceTag, additionalMetadata) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The newIndexConnection
input is getting a bit long. Consider refactoring this into a breaking change where it takes a struct similar to other constructors.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed, will go ahead and take a pass on that now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See newIndexParameters
struct type which newIndexConnection
should consume.
pinecone/client.go
Outdated
apiKeyProvider, err := securityprovider.NewSecurityProviderApiKey("header", "Api-Key", in.ApiKey) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
clientOptions = append(clientOptions, control.WithRequestEditorFn(apiKeyProvider.Intercept)) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is an API Key now optional with the addition of custom headers? Still seems required here.
If a caller was attempting to use a JWT Bearer token instead of an API Key for auth, would this now work? Seems I'd need to pass in an API Key (which gets added to the headers) and a custom header with the Bearer token. Also guessing that breaks the request (two auth schemes in the same request headers)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, I didn't change anything regarding API key being optional vs. required so I think it still expects it to be populated.
The point about JWT Bearer is intersting, I'd maybe need to defer to @jhamon regarding specifically what we need to support here, but maybe I should look at removing the API key if they've passed a header for auth, for example. 🤔
pinecone/client_test.go
Outdated
@@ -66,8 +68,11 @@ func (ts *ClientTests) TestNewClientParamsSet() { | |||
if client.sourceTag != "" { | |||
ts.FailNow(fmt.Sprintf("Expected client to have empty sourceTag, but got '%s'", client.sourceTag)) | |||
} | |||
if client.headers != nil { | |||
ts.FailNow(fmt.Sprintf("Expected client to have nil headers, but got '%v'", client.headers)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Total nit, but when I read this, I ask myself "what are nil headers?" because nil
sounds like a quantity. Maybe rephrase this stmt to be "Expected client headers to be nil"
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(B/c at least if I'm understanding stuff about Go correctly online, nil
isn't really a quantity, it's sorta just a pointer that indicates there's no object or or no value/concrete type, right?)
ts.FailNow(err.Error()) | ||
} | ||
if client.apiKey != apiKey { | ||
ts.FailNow(fmt.Sprintf("Expected client to have apiKey '%s', but got '%s'", apiKey, client.apiKey)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this potentially dangerous b/c someone could reveal an API key through this error msg?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(I mean, I know it's a mocked API key in the test, but if this function outputs this error msg IRL, that could be a dangerous vulnerability for users, no?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is test code and thus not compiled into the actual built package, as far as I understand. Given that and the usage of a fake key in the tests where we're doing this, I feel like this is probably safe. Users would need to be developing the client themselves locally or in CI for it to be an issue I think.
pinecone/index_connection_test.go
Outdated
@@ -93,19 +106,47 @@ func (ts *IndexConnectionTests) TestNewIndexConnection() { | |||
if idxConn.Namespace != "" { | |||
ts.FailNow(fmt.Sprintf("Expected idxConn to have empty namespace, but got '%s'", idxConn.Namespace)) | |||
} | |||
if idxConn.additionalMetadata != nil { | |||
ts.FailNow(fmt.Sprintf("Expected idxConn to have nil additionalMetadata, but got '%+v'", idxConn.additionalMetadata)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment re: the usage of nil
here, as w/my previous comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just lurking :) Giving an approval so I don't block!
…ta plane requests, update tests, add verbose to just test, update NewClientParams to take in a custom rest client to allow for mocking in unit tests
8293e2b
to
505a4ff
Compare
…f both Api-Key and Authorization header have been provided, add unit tests
67b7090
to
59a6918
Compare
@@ -3,7 +3,7 @@ test: | |||
set -o allexport | |||
source .env | |||
set +o allexport | |||
go test -count=1 ./pinecone | |||
go test -count=1 -v ./pinecone |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the additional logging output when running these locally, etc.
pinecone/client.go
Outdated
for key, value := range in.Headers { | ||
headerProvider := provider.NewHeaderProvider(key, value) | ||
|
||
if strings.Contains(key, "Authorization") { | ||
hasAuthorizationHeader = true | ||
} | ||
|
||
clientOptions = append(clientOptions, control.WithRequestEditorFn(headerProvider.Intercept)) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are there any headers we'd want to prevent them from setting? I'm not sure there, just asking the question.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At the moment I don't think there are, good question though I'm also not sure.
type newIndexParameters struct { | ||
apiKey string | ||
host string | ||
namespace string | ||
sourceTag string | ||
additionalMetadata map[string]string | ||
} | ||
|
||
func newIndexConnection(in newIndexParameters) (*IndexConnection, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, I like this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Props to @haruska for the suggestion.
type Metadata = structpb.Struct | ||
type Metadata = structpb.Struct |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
accidental change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it was just auto-formatting removing the line at the end of the file.
pinecone/client.go
Outdated
} | ||
|
||
type NewClientParams struct { | ||
ApiKey string | ||
SourceTag string // optional | ||
ApiKey string // optional unless no Authorization header provided |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggest rephrasing this to "required unless Authorization header provided"
pinecone/client.go
Outdated
if strings.Contains(key, "Authorization") { | ||
hasAuthorizationHeader = true | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
http headers are typically case in-sensitive so we probably want to relax this check
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated to use strings.ToLower(key), "authorization")
, thanks!
️✅ There are no secrets present in this pull request anymore.If these secrets were true positive and are still valid, we highly recommend you to revoke them. 🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good improvements. The creation of the index connection still seems a bit clunky but I think that's just an artifact of the public constructor(s) being off Client. Overall, the PR is much better than the current impl in main.
Problem
Currently, the Go SDK does not support specifying custom headers or metadata for control & data plane operations (REST and gRPC). This is a useful feature to aid in debugging or tracking a specific request, and we'd like to enable this functionality in the Go SDK.
Solution
NewClientParams
andClient
structs to supportheaders
, also allow passing a customRestClient
to the new client. This is primarily for allowing mocking in unit tests, but we have a similar approach in other clients allowing users to customize the HTTP module.buildClientOptions
function.NewClient
to support appendingHeaders
andRestClient
toclientOptions
.ApiKey
without anyAuthorization
header provided. If the user passes both, avoid applying theApi-Key
header in favor of theAuthorization
header.Client.IndexWithAdditionalMetadata
constructor.IndexConnection
struct to includeadditionalMetadata
, and updatenewIndexConnection
to acceptadditionalMetadata
as an argument.IndexConnection.akCtx
to handle appending theapi-key
and anyadditionalMetadata
to requests.mocks
package andmock_transport
to facilitate validating requests made viaClient
including.I spent some time trying to figure out how to mock / test
IndexConnection
but I think it'll be a bit trickier thanClient
, so somewhat saving for a future PR atm.Type of Change
Test Plan
just test
to run the test suite locally. Validate tests pass in CI.