From e651b1950ed8b0cd5e71111eb82423056d365b87 Mon Sep 17 00:00:00 2001 From: JolisaBrownHashiCorp Date: Tue, 6 Dec 2022 15:09:41 -0500 Subject: [PATCH 01/18] add integration test for middleware --- httpclient/httpclient_test.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/httpclient/httpclient_test.go b/httpclient/httpclient_test.go index 083065f2..7bce6971 100644 --- a/httpclient/httpclient_test.go +++ b/httpclient/httpclient_test.go @@ -8,6 +8,7 @@ import ( "net/http" "net/http/httptest" "net/url" + "os" "sync/atomic" "testing" @@ -151,4 +152,27 @@ func TestNew(t *testing.T) { // just skip all the assertions! require.Equal(t, uint32(2), atomic.LoadUint32(&numRequests)) }) + +} + +//Must have integration environment set and client credentials +func TestProfileIntegration(t *testing.T) { + // Create a config that calls the test server + + hcpConfig, err := config.NewHCPConfig( + config.WithClientCredentials(os.Getenv("HCP_CLIENT_ID"), os.Getenv("HCP_CLIENT_SECRET")), + config.WithProfile(&profile.UserProfile{OrganizationID: os.Getenv("HCP_ORGANIZATION_ID"), ProjectID: os.Getenv("HCP_PROJECT_ID")}), + config.WithAuth(os.Getenv("HCP_AUTH_URL"), nil), + config.WithAPI(os.Getenv("HCP_API_HOST"), nil), + ) + + cl, err := New(Config{ + HCPConfig: hcpConfig, + }) + require.NoError(t, err) + consulClient := consul.New(cl, nil) + listParams := consul.NewListParams() + _, err = consulClient.List(listParams, nil) + require.NoError(t, err) + } From 700a8e820f6fc7491587c878140712bae3065e98 Mon Sep 17 00:00:00 2001 From: Paras Prajapati Date: Tue, 6 Dec 2022 16:05:18 -0500 Subject: [PATCH 02/18] WIP try to edit URL --- httpclient/httpclient.go | 26 ++++++++++++++++++++++++++ httpclient/httpclient_test.go | 2 +- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/httpclient/httpclient.go b/httpclient/httpclient.go index 00b5cc62..055b93c7 100644 --- a/httpclient/httpclient.go +++ b/httpclient/httpclient.go @@ -61,6 +61,23 @@ func (rt *roundTripperWithSourceChannel) RoundTrip(req *http.Request) (*http.Res return rt.OriginalRoundTripper.RoundTrip(req) } +type roundTripperWithProfile struct { + OriginalRoundTripper http.RoundTripper + OrganizationID string + ProjectID string +} + +func (rt *roundTripperWithProfile) RoundTrip(req *http.Request) (*http.Response, error) { + fmt.Printf("REQUEST URL BEFORE: %v", req.URL.Path) + url := req.URL.Path + strings.Replace(url, "organizations//", fmt.Sprintf("organizations/%s/", rt.OrganizationID), 1) + strings.Replace(url, "projects//", fmt.Sprintf("projects/%s/", rt.ProjectID), 1) + // TODO check that source channel didn't get overwritten + req.URL.Path = url + fmt.Printf("REQUEST URL AFTER: %v", req.URL.Path) + return rt.OriginalRoundTripper.RoundTrip(req) +} + // New creates a client with the right base path to connect to any HCP API func New(cfg Config) (runtime *httptransport.Runtime, err error) { // Populate default values where possible. @@ -84,6 +101,15 @@ func New(cfg Config) (runtime *httptransport.Runtime, err error) { transport = &roundTripperWithSourceChannel{OriginalRoundTripper: transport, SourceChannel: sourceChannel} } + if cfg.Profile().OrganizationID != "" || cfg.Profile().ProjectID != "" { + // Use custom transport in order to set the organization and project IDs if missing + transport = &roundTripperWithProfile{ + OriginalRoundTripper: transport, + OrganizationID: cfg.Profile().OrganizationID, + ProjectID: cfg.Profile().ProjectID, + } + } + // Set the scheme based on the TLS configuration. scheme := "https" if cfg.APITLSConfig() == nil { diff --git a/httpclient/httpclient_test.go b/httpclient/httpclient_test.go index 7bce6971..cc98a228 100644 --- a/httpclient/httpclient_test.go +++ b/httpclient/httpclient_test.go @@ -155,7 +155,7 @@ func TestNew(t *testing.T) { } -//Must have integration environment set and client credentials +// Must have integration environment set and client credentials func TestProfileIntegration(t *testing.T) { // Create a config that calls the test server From 0832b7f9a2e556dd89d1485ae1865f4bc2540b2e Mon Sep 17 00:00:00 2001 From: Brenna Hewer-Darroch <21015366+bcmdarroch@users.noreply.github.com> Date: Tue, 6 Dec 2022 18:20:46 -0500 Subject: [PATCH 03/18] roundtripper updates URL with profile org/proj IDs --- httpclient/httpclient.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/httpclient/httpclient.go b/httpclient/httpclient.go index 055b93c7..2e4a63ac 100644 --- a/httpclient/httpclient.go +++ b/httpclient/httpclient.go @@ -68,13 +68,12 @@ type roundTripperWithProfile struct { } func (rt *roundTripperWithProfile) RoundTrip(req *http.Request) (*http.Response, error) { - fmt.Printf("REQUEST URL BEFORE: %v", req.URL.Path) - url := req.URL.Path - strings.Replace(url, "organizations//", fmt.Sprintf("organizations/%s/", rt.OrganizationID), 1) - strings.Replace(url, "projects//", fmt.Sprintf("projects/%s/", rt.ProjectID), 1) + path := req.URL.Path + path = strings.Replace(path, "organizations//", fmt.Sprintf("organizations/%s/", rt.OrganizationID), 1) + path = strings.Replace(path, "projects//", fmt.Sprintf("projects/%s/", rt.ProjectID), 1) + req.URL.Path = path + // TODO check that source channel didn't get overwritten - req.URL.Path = url - fmt.Printf("REQUEST URL AFTER: %v", req.URL.Path) return rt.OriginalRoundTripper.RoundTrip(req) } From 05a2436dd3e60ab189c3779166db81979e66ec15 Mon Sep 17 00:00:00 2001 From: JolisaBrownHashiCorp Date: Fri, 9 Dec 2022 13:12:17 -0500 Subject: [PATCH 04/18] tweak middleware test --- httpclient/httpclient_test.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/httpclient/httpclient_test.go b/httpclient/httpclient_test.go index cc98a228..7edd3d67 100644 --- a/httpclient/httpclient_test.go +++ b/httpclient/httpclient_test.go @@ -155,21 +155,20 @@ func TestNew(t *testing.T) { } -// Must have integration environment set and client credentials +// Defaults to production environment and client credentials func TestProfileIntegration(t *testing.T) { // Create a config that calls the test server hcpConfig, err := config.NewHCPConfig( config.WithClientCredentials(os.Getenv("HCP_CLIENT_ID"), os.Getenv("HCP_CLIENT_SECRET")), config.WithProfile(&profile.UserProfile{OrganizationID: os.Getenv("HCP_ORGANIZATION_ID"), ProjectID: os.Getenv("HCP_PROJECT_ID")}), - config.WithAuth(os.Getenv("HCP_AUTH_URL"), nil), - config.WithAPI(os.Getenv("HCP_API_HOST"), nil), ) cl, err := New(Config{ HCPConfig: hcpConfig, }) require.NoError(t, err) + consulClient := consul.New(cl, nil) listParams := consul.NewListParams() _, err = consulClient.List(listParams, nil) From e1b2b9747e025e34e6318275f718b7d84728da5a Mon Sep 17 00:00:00 2001 From: JolisaBrownHashiCorp Date: Fri, 9 Dec 2022 13:19:40 -0500 Subject: [PATCH 05/18] fix linter --- httpclient/httpclient_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/httpclient/httpclient_test.go b/httpclient/httpclient_test.go index 7edd3d67..0d08dd15 100644 --- a/httpclient/httpclient_test.go +++ b/httpclient/httpclient_test.go @@ -163,6 +163,7 @@ func TestProfileIntegration(t *testing.T) { config.WithClientCredentials(os.Getenv("HCP_CLIENT_ID"), os.Getenv("HCP_CLIENT_SECRET")), config.WithProfile(&profile.UserProfile{OrganizationID: os.Getenv("HCP_ORGANIZATION_ID"), ProjectID: os.Getenv("HCP_PROJECT_ID")}), ) + require.NoError(t, err) cl, err := New(Config{ HCPConfig: hcpConfig, From 85cb2002fc60d117dc8bf3bb6aa7f381624f2371 Mon Sep 17 00:00:00 2001 From: JolisaBrownHashiCorp Date: Mon, 12 Dec 2022 09:55:05 -0500 Subject: [PATCH 06/18] refactor roundtripper, make profile integration test local only --- httpclient/httpclient.go | 21 +++++++-------------- httpclient/httpclient_test.go | 5 ++++- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/httpclient/httpclient.go b/httpclient/httpclient.go index 2e4a63ac..3b4872df 100644 --- a/httpclient/httpclient.go +++ b/httpclient/httpclient.go @@ -51,23 +51,16 @@ type Config struct { // Deprecated: HCPConfig should be used instead Client *http.Client } -type roundTripperWithSourceChannel struct { - OriginalRoundTripper http.RoundTripper - SourceChannel string -} -func (rt *roundTripperWithSourceChannel) RoundTrip(req *http.Request) (*http.Response, error) { - req.Header.Set("X-HCP-Source-Channel", rt.SourceChannel) - return rt.OriginalRoundTripper.RoundTrip(req) -} - -type roundTripperWithProfile struct { +type roundTripperWithMiddleware struct { OriginalRoundTripper http.RoundTripper + SourceChannel string OrganizationID string ProjectID string } -func (rt *roundTripperWithProfile) RoundTrip(req *http.Request) (*http.Response, error) { +func (rt *roundTripperWithMiddleware) RoundTrip(req *http.Request) (*http.Response, error) { + req.Header.Set("X-HCP-Source-Channel", rt.SourceChannel) path := req.URL.Path path = strings.Replace(path, "organizations//", fmt.Sprintf("organizations/%s/", rt.OrganizationID), 1) path = strings.Replace(path, "projects//", fmt.Sprintf("projects/%s/", rt.ProjectID), 1) @@ -97,12 +90,12 @@ func New(cfg Config) (runtime *httptransport.Runtime, err error) { if cfg.SourceChannel != "" { // Use custom transport in order to set the source channel header when it is present. sourceChannel := fmt.Sprintf("%s hcp-go-sdk/%s", cfg.SourceChannel, version.Version) - transport = &roundTripperWithSourceChannel{OriginalRoundTripper: transport, SourceChannel: sourceChannel} + transport = &roundTripperWithMiddleware{OriginalRoundTripper: transport, SourceChannel: sourceChannel} } - if cfg.Profile().OrganizationID != "" || cfg.Profile().ProjectID != "" { + if cfg.Profile().OrganizationID != "" && cfg.Profile().ProjectID != "" { // Use custom transport in order to set the organization and project IDs if missing - transport = &roundTripperWithProfile{ + transport = &roundTripperWithMiddleware{ OriginalRoundTripper: transport, OrganizationID: cfg.Profile().OrganizationID, ProjectID: cfg.Profile().ProjectID, diff --git a/httpclient/httpclient_test.go b/httpclient/httpclient_test.go index 0d08dd15..52682cc6 100644 --- a/httpclient/httpclient_test.go +++ b/httpclient/httpclient_test.go @@ -157,8 +157,11 @@ func TestNew(t *testing.T) { // Defaults to production environment and client credentials func TestProfileIntegration(t *testing.T) { - // Create a config that calls the test server + if testing.Short() { + t.Skip("skipping test in short mode (CI).") + } + // Create a config that calls the test server hcpConfig, err := config.NewHCPConfig( config.WithClientCredentials(os.Getenv("HCP_CLIENT_ID"), os.Getenv("HCP_CLIENT_SECRET")), config.WithProfile(&profile.UserProfile{OrganizationID: os.Getenv("HCP_ORGANIZATION_ID"), ProjectID: os.Getenv("HCP_PROJECT_ID")}), From 928b96e0d4fd8396ddd4c3230b6f2515df217727 Mon Sep 17 00:00:00 2001 From: JolisaBrownHashiCorp Date: Mon, 12 Dec 2022 10:00:32 -0500 Subject: [PATCH 07/18] update changelog --- .changelog/142.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/142.txt diff --git a/.changelog/142.txt b/.changelog/142.txt new file mode 100644 index 00000000..eb455433 --- /dev/null +++ b/.changelog/142.txt @@ -0,0 +1,3 @@ +```release-note:improvement +Insert middleware to allow organization and project id on requests +``` \ No newline at end of file From 4e052c59b299880e65a5eedc20156faa7e7d0737 Mon Sep 17 00:00:00 2001 From: JolisaBrownHashiCorp Date: Tue, 13 Dec 2022 14:47:49 -0500 Subject: [PATCH 08/18] refactor tests to cover profile and source channel middlewares --- httpclient/httpclient.go | 48 ++++++++++++++++++------------ httpclient/httpclient_test.go | 55 +++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 19 deletions(-) diff --git a/httpclient/httpclient.go b/httpclient/httpclient.go index 3b4872df..8d3d6e68 100644 --- a/httpclient/httpclient.go +++ b/httpclient/httpclient.go @@ -59,15 +59,25 @@ type roundTripperWithMiddleware struct { ProjectID string } +func addMiddlewareToRequest(req *http.Request, sourceChannel string, orgID string, projID string) (*http.Request, error) { + if sourceChannel != "" { + req.Header.Set("X-HCP-Source-Channel", sourceChannel) + } + if orgID != "" && projID != "" { + path := req.URL.Path + fmt.Printf("url request path is %v: ", path) + path = strings.Replace(path, "organizations//", fmt.Sprintf("organizations/%s/", orgID), 1) + path = strings.Replace(path, "projects//", fmt.Sprintf("projects/%s/", projID), 1) + req.URL.Path = path + } + return req, nil +} + func (rt *roundTripperWithMiddleware) RoundTrip(req *http.Request) (*http.Response, error) { - req.Header.Set("X-HCP-Source-Channel", rt.SourceChannel) - path := req.URL.Path - path = strings.Replace(path, "organizations//", fmt.Sprintf("organizations/%s/", rt.OrganizationID), 1) - path = strings.Replace(path, "projects//", fmt.Sprintf("projects/%s/", rt.ProjectID), 1) - req.URL.Path = path + request, _ := addMiddlewareToRequest(req, rt.SourceChannel, rt.OrganizationID, rt.ProjectID) // TODO check that source channel didn't get overwritten - return rt.OriginalRoundTripper.RoundTrip(req) + return rt.OriginalRoundTripper.RoundTrip(request) } // New creates a client with the right base path to connect to any HCP API @@ -87,20 +97,20 @@ func New(cfg Config) (runtime *httptransport.Runtime, err error) { Source: cfg, } - if cfg.SourceChannel != "" { - // Use custom transport in order to set the source channel header when it is present. - sourceChannel := fmt.Sprintf("%s hcp-go-sdk/%s", cfg.SourceChannel, version.Version) - transport = &roundTripperWithMiddleware{OriginalRoundTripper: transport, SourceChannel: sourceChannel} - } - - if cfg.Profile().OrganizationID != "" && cfg.Profile().ProjectID != "" { - // Use custom transport in order to set the organization and project IDs if missing - transport = &roundTripperWithMiddleware{ - OriginalRoundTripper: transport, - OrganizationID: cfg.Profile().OrganizationID, - ProjectID: cfg.Profile().ProjectID, - } + //if cfg.SourceChannel != "" { + // Use custom transport in order to set the source channel header when it is present. + sourceChannel := fmt.Sprintf("%s hcp-go-sdk/%s", cfg.SourceChannel, version.Version) + transport = &roundTripperWithMiddleware{OriginalRoundTripper: transport, SourceChannel: sourceChannel} + //} + + //if cfg.Profile().OrganizationID != "" && cfg.Profile().ProjectID != "" { + // Use custom transport in order to set the organization and project IDs if missing + transport = &roundTripperWithMiddleware{ + OriginalRoundTripper: transport, + OrganizationID: cfg.Profile().OrganizationID, + ProjectID: cfg.Profile().ProjectID, } + //} // Set the scheme based on the TLS configuration. scheme := "https" diff --git a/httpclient/httpclient_test.go b/httpclient/httpclient_test.go index 52682cc6..3c0ba3d3 100644 --- a/httpclient/httpclient_test.go +++ b/httpclient/httpclient_test.go @@ -105,6 +105,39 @@ func TestNew(t *testing.T) { require.Equal(t, uint32(2), atomic.LoadUint32(&numRequests)) }) + t.Run("legacy configuration, no source channel", func(t *testing.T) { + numRequests = 0 + + cfg := Config{ + HostPath: ts.URL, + AuthURL: ts.URL, + ClientID: clientID, + ClientSecret: clientSecret, + // Override the base http.Client so that we trust the test server's TLS + Client: ts.Client(), + } + + // The test's important assertions occur in the test server handler above. + // When an http client is initialized, it tries to obtain an access token using the configured token URL (from an auth provider), client ID, and client secret. + // The token request embedded in this client initializer hits the mock auth provider handler above ('/oauth/token'). + cl, err := New(cfg) + require.NoError(t, err) + + consulClient := consul.New(cl, nil) + listParams := consul.NewListParams() + listParams.LocationOrganizationID = orgID + listParams.LocationProjectID = projID + + // This SDK request hits the mock Consul List API handler above ('/consul/2021-02-04/organizations...'). + // The handler verifies that the expected bearer token is provided. + _, err = consulClient.List(listParams, nil) + require.NoError(t, err) + + // Make sure we actually handled both the auth and the GET request and didn't + // just skip all the assertions! + require.Equal(t, uint32(2), atomic.LoadUint32(&numRequests)) + }) + t.Run("HCPConfig", func(t *testing.T) { numRequests = 0 @@ -176,6 +209,28 @@ func TestProfileIntegration(t *testing.T) { consulClient := consul.New(cl, nil) listParams := consul.NewListParams() _, err = consulClient.List(listParams, nil) + require.Error(t, err) + +} + +// Defaults to production environment and client credentials +func TestSourceChannelIntegration(t *testing.T) { + if testing.Short() { + t.Skip("skipping test in short mode (CI).") + } + + request, err := http.NewRequest("GET", "api.cloud.hashicorp.com/consul/2021-02-04/organizations//projects//clusters", httptest.NewRecorder().Body) require.NoError(t, err) + expectedSourceChannel := "source_channel_foo" + request, err = addMiddlewareToRequest(request, expectedSourceChannel, "", "") + require.NoError(t, err) + expectedOrgID := "org_id_77" + expectedProjID := "proj_id_123" + + request, err = addMiddlewareToRequest(request, "", expectedOrgID, expectedProjID) + require.NoError(t, err) + require.Equal(t, request.Header.Get("X-HCP-Source-Channel"), expectedSourceChannel) + require.Contains(t, request.URL.Path, expectedOrgID) + require.Contains(t, request.URL.Path, expectedProjID) } From b906dc47cd6ea03d29a2dca44741ce0348516715 Mon Sep 17 00:00:00 2001 From: JolisaBrownHashiCorp Date: Tue, 13 Dec 2022 15:24:09 -0500 Subject: [PATCH 09/18] WIP - adding middleware function signature as struct field --- httpclient/httpclient.go | 59 +++++++++++++++++++++++++++++----------- 1 file changed, 43 insertions(+), 16 deletions(-) diff --git a/httpclient/httpclient.go b/httpclient/httpclient.go index 8d3d6e68..4f06d5ca 100644 --- a/httpclient/httpclient.go +++ b/httpclient/httpclient.go @@ -52,11 +52,35 @@ type Config struct { Client *http.Client } +// HCPConfigOption are functions that modify the hcpConfig struct. They can be +// passed to NewHCPConfig. +type MiddlewareOption = func(req *http.Request) error + type roundTripperWithMiddleware struct { OriginalRoundTripper http.RoundTripper - SourceChannel string - OrganizationID string - ProjectID string + + MiddlewareFunctions []MiddlewareOption +} + +func (rt *roundTripperWithMiddleware) withSourceChannel(sourceChannel string, req *http.Request) MiddlewareOption { + + return func(req *http.Request) error { + req.Header.Set("X-HCP-Source-Channel", sourceChannel) + return nil + } + +} + +func (rt *roundTripperWithMiddleware) updateProjAndOrgID(req *http.Request) error { + if rt.OrganizationID != "" && rt.ProjectID != "" { + path := req.URL.Path + fmt.Printf("url request path is %v: ", path) + path = strings.Replace(path, "organizations//", fmt.Sprintf("organizations/%s/", rt.OrganizationID), 1) + path = strings.Replace(path, "projects//", fmt.Sprintf("projects/%s/", rt.ProjectID), 1) + req.URL.Path = path + } + + return nil } func addMiddlewareToRequest(req *http.Request, sourceChannel string, orgID string, projID string) (*http.Request, error) { @@ -96,21 +120,24 @@ func New(cfg Config) (runtime *httptransport.Runtime, err error) { Base: tlsTransport, Source: cfg, } + transport = &roundTripperWithMiddleware{OriginalRoundTripper: transport} + if cfg.SourceChannel != "" { + // Use custom transport in order to set the source channel header when it is present. + sourceChannel := fmt.Sprintf("%s hcp-go-sdk/%s", cfg.SourceChannel, version.Version) + + transport.MiddlewareFunctions = append(transport.MiddlewareFunctions, withSourceChannel()) + //transport = &roundTripperWithMiddleware{OriginalRoundTripper: transport, SourceChannel: sourceChannel} - //if cfg.SourceChannel != "" { - // Use custom transport in order to set the source channel header when it is present. - sourceChannel := fmt.Sprintf("%s hcp-go-sdk/%s", cfg.SourceChannel, version.Version) - transport = &roundTripperWithMiddleware{OriginalRoundTripper: transport, SourceChannel: sourceChannel} - //} - - //if cfg.Profile().OrganizationID != "" && cfg.Profile().ProjectID != "" { - // Use custom transport in order to set the organization and project IDs if missing - transport = &roundTripperWithMiddleware{ - OriginalRoundTripper: transport, - OrganizationID: cfg.Profile().OrganizationID, - ProjectID: cfg.Profile().ProjectID, } - //} + + if cfg.Profile().OrganizationID != "" && cfg.Profile().ProjectID != "" { + // Use custom transport in order to set the organization and project IDs if missing + transport = &roundTripperWithMiddleware{ + OriginalRoundTripper: transport, + OrganizationID: cfg.Profile().OrganizationID, + ProjectID: cfg.Profile().ProjectID, + } + } // Set the scheme based on the TLS configuration. scheme := "https" From 17ec85706674963efa95cfbbb96b19e529917509 Mon Sep 17 00:00:00 2001 From: Brenna Hewer-Darroch <21015366+bcmdarroch@users.noreply.github.com> Date: Tue, 13 Dec 2022 18:50:45 -0500 Subject: [PATCH 10/18] (wip) working middleware funcs implementation --- httpclient/httpclient.go | 75 +++++++++++++++++++---------------- httpclient/httpclient_test.go | 27 ++++++++++--- 2 files changed, 61 insertions(+), 41 deletions(-) diff --git a/httpclient/httpclient.go b/httpclient/httpclient.go index 4f06d5ca..5650a1b9 100644 --- a/httpclient/httpclient.go +++ b/httpclient/httpclient.go @@ -54,54 +54,43 @@ type Config struct { // HCPConfigOption are functions that modify the hcpConfig struct. They can be // passed to NewHCPConfig. -type MiddlewareOption = func(req *http.Request) error +type MiddlewareFunc = func(req *http.Request) error type roundTripperWithMiddleware struct { OriginalRoundTripper http.RoundTripper - - MiddlewareFunctions []MiddlewareOption + SourceChannel MiddlewareFunc + Profile MiddlewareFunc } -func (rt *roundTripperWithMiddleware) withSourceChannel(sourceChannel string, req *http.Request) MiddlewareOption { - +func withSourceChannel(sourceChannel string) MiddlewareFunc { return func(req *http.Request) error { req.Header.Set("X-HCP-Source-Channel", sourceChannel) return nil } - -} - -func (rt *roundTripperWithMiddleware) updateProjAndOrgID(req *http.Request) error { - if rt.OrganizationID != "" && rt.ProjectID != "" { - path := req.URL.Path - fmt.Printf("url request path is %v: ", path) - path = strings.Replace(path, "organizations//", fmt.Sprintf("organizations/%s/", rt.OrganizationID), 1) - path = strings.Replace(path, "projects//", fmt.Sprintf("projects/%s/", rt.ProjectID), 1) - req.URL.Path = path - } - - return nil } -func addMiddlewareToRequest(req *http.Request, sourceChannel string, orgID string, projID string) (*http.Request, error) { - if sourceChannel != "" { - req.Header.Set("X-HCP-Source-Channel", sourceChannel) - } - if orgID != "" && projID != "" { +func withProfile(orgID, projID string) MiddlewareFunc { + return func(req *http.Request) error { path := req.URL.Path - fmt.Printf("url request path is %v: ", path) path = strings.Replace(path, "organizations//", fmt.Sprintf("organizations/%s/", orgID), 1) path = strings.Replace(path, "projects//", fmt.Sprintf("projects/%s/", projID), 1) req.URL.Path = path + return nil } - return req, nil } func (rt *roundTripperWithMiddleware) RoundTrip(req *http.Request) (*http.Response, error) { - request, _ := addMiddlewareToRequest(req, rt.SourceChannel, rt.OrganizationID, rt.ProjectID) - // TODO check that source channel didn't get overwritten - return rt.OriginalRoundTripper.RoundTrip(request) + // Panics if middleware func not set + if rt.SourceChannel != nil { + rt.SourceChannel(req) + } + + if rt.Profile != nil { + rt.Profile(req) + } + + return rt.OriginalRoundTripper.RoundTrip(req) } // New creates a client with the right base path to connect to any HCP API @@ -120,22 +109,38 @@ func New(cfg Config) (runtime *httptransport.Runtime, err error) { Base: tlsTransport, Source: cfg, } - transport = &roundTripperWithMiddleware{OriginalRoundTripper: transport} + if cfg.SourceChannel != "" { // Use custom transport in order to set the source channel header when it is present. - sourceChannel := fmt.Sprintf("%s hcp-go-sdk/%s", cfg.SourceChannel, version.Version) + sc := fmt.Sprintf("%s hcp-go-sdk/%s", cfg.SourceChannel, version.Version) + + // TODO: cleanup these comments, just for learning + // 1. Original implementation: With sourceChannel in struct state + // transport = &roundTripperWithMiddleware{OriginalRoundTripper: transport, SourceChannel: sourceChannel} + + // 2. With sourceChannel function in struct state's function array + // transport.MiddlewareFuncs = append(transport.MiddlewareFuncs, transport.withSourceChannel(sc)) + // can't append MiddlewareFuncs - transport is still considered an HTTP transport - transport.MiddlewareFunctions = append(transport.MiddlewareFunctions, withSourceChannel()) - //transport = &roundTripperWithMiddleware{OriginalRoundTripper: transport, SourceChannel: sourceChannel} + // 3. this works, but would be overwritten when we do the same thing in profile conditional branch + // transport = &roundTripperWithMiddleware{ + // OriginalRoundTripper: transport, + // MiddlewareFuncs: []]MiddlewareFunc{ + // withSourceChannel(sc), + // }, + // } + transport = &roundTripperWithMiddleware{ + OriginalRoundTripper: transport, + SourceChannel: withSourceChannel(sc), + } } if cfg.Profile().OrganizationID != "" && cfg.Profile().ProjectID != "" { - // Use custom transport in order to set the organization and project IDs if missing + // Use custom transport in order to set the organization and project IDs if set in user profile. transport = &roundTripperWithMiddleware{ OriginalRoundTripper: transport, - OrganizationID: cfg.Profile().OrganizationID, - ProjectID: cfg.Profile().ProjectID, + Profile: withProfile(cfg.Profile().OrganizationID, cfg.Profile().ProjectID), } } diff --git a/httpclient/httpclient_test.go b/httpclient/httpclient_test.go index 3c0ba3d3..f4ccfb9d 100644 --- a/httpclient/httpclient_test.go +++ b/httpclient/httpclient_test.go @@ -12,6 +12,7 @@ import ( "sync/atomic" "testing" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" consul "github.com/hashicorp/hcp-sdk-go/clients/cloud-consul-service/stable/2021-02-04/client/consul_service" @@ -188,6 +189,12 @@ func TestNew(t *testing.T) { } +// TODO: merge the tests below into TestMiddleware with table-driven tests with different HCP client configs +// one with no profile, no source channel +// one with profile +// one with source channel +// one with profile and source channel + // Defaults to production environment and client credentials func TestProfileIntegration(t *testing.T) { if testing.Short() { @@ -209,7 +216,7 @@ func TestProfileIntegration(t *testing.T) { consulClient := consul.New(cl, nil) listParams := consul.NewListParams() _, err = consulClient.List(listParams, nil) - require.Error(t, err) + require.NoError(t, err) } @@ -222,15 +229,23 @@ func TestSourceChannelIntegration(t *testing.T) { request, err := http.NewRequest("GET", "api.cloud.hashicorp.com/consul/2021-02-04/organizations//projects//clusters", httptest.NewRecorder().Body) require.NoError(t, err) + require.Equal(t, request.Header.Get("X-HCP-Source-Channel"), "") + expectedSourceChannel := "source_channel_foo" - request, err = addMiddlewareToRequest(request, expectedSourceChannel, "", "") + // TODO: since this is an integration test, we shouldn't be calling this function directly. Instead, we want to see that, + // if the HCP client is configured with source channel and profile, the request is properly mutated. + + // request, err = addMiddlewareToRequest(request, expectedSourceChannel, "", "") require.NoError(t, err) expectedOrgID := "org_id_77" expectedProjID := "proj_id_123" + assert.Equal(t, request.Header.Get("X-HCP-Source-Channel"), expectedSourceChannel) + assert.NotContains(t, request.URL.Path, expectedOrgID) + assert.NotContains(t, request.URL.Path, expectedProjID) - request, err = addMiddlewareToRequest(request, "", expectedOrgID, expectedProjID) + // request, err = addMiddlewareToRequest(request, "", expectedOrgID, expectedProjID) require.NoError(t, err) - require.Equal(t, request.Header.Get("X-HCP-Source-Channel"), expectedSourceChannel) - require.Contains(t, request.URL.Path, expectedOrgID) - require.Contains(t, request.URL.Path, expectedProjID) + assert.Contains(t, request.URL.Path, expectedOrgID) + assert.Contains(t, request.URL.Path, expectedProjID) + assert.Equal(t, request.Header.Get("X-HCP-Source-Channel"), expectedSourceChannel) } From 32af614c0677ce439ce79e3490c0d3fdbb18d1d9 Mon Sep 17 00:00:00 2001 From: Brenna Hewer-Darroch <21015366+bcmdarroch@users.noreply.github.com> Date: Wed, 14 Dec 2022 14:33:29 -0500 Subject: [PATCH 11/18] roundtripper now has array of Middleware functions --- httpclient/httpclient.go | 51 ++++++++++++++-------------------------- 1 file changed, 17 insertions(+), 34 deletions(-) diff --git a/httpclient/httpclient.go b/httpclient/httpclient.go index 5650a1b9..8b914c56 100644 --- a/httpclient/httpclient.go +++ b/httpclient/httpclient.go @@ -58,8 +58,7 @@ type MiddlewareFunc = func(req *http.Request) error type roundTripperWithMiddleware struct { OriginalRoundTripper http.RoundTripper - SourceChannel MiddlewareFunc - Profile MiddlewareFunc + MiddlewareFuncs []MiddlewareFunc } func withSourceChannel(sourceChannel string) MiddlewareFunc { @@ -81,13 +80,12 @@ func withProfile(orgID, projID string) MiddlewareFunc { func (rt *roundTripperWithMiddleware) RoundTrip(req *http.Request) (*http.Response, error) { - // Panics if middleware func not set - if rt.SourceChannel != nil { - rt.SourceChannel(req) - } - - if rt.Profile != nil { - rt.Profile(req) + for _, mw := range rt.MiddlewareFuncs { + if err := mw(req); err != nil { + // Failure to apply middleware should not fail the request + fmt.Printf("failed to apply middleware: %#v", mw(req)) + continue + } } return rt.OriginalRoundTripper.RoundTrip(req) @@ -110,38 +108,23 @@ func New(cfg Config) (runtime *httptransport.Runtime, err error) { Source: cfg, } + var mwFuncs []MiddlewareFunc + if cfg.SourceChannel != "" { // Use custom transport in order to set the source channel header when it is present. sc := fmt.Sprintf("%s hcp-go-sdk/%s", cfg.SourceChannel, version.Version) - // TODO: cleanup these comments, just for learning - // 1. Original implementation: With sourceChannel in struct state - // transport = &roundTripperWithMiddleware{OriginalRoundTripper: transport, SourceChannel: sourceChannel} - - // 2. With sourceChannel function in struct state's function array - // transport.MiddlewareFuncs = append(transport.MiddlewareFuncs, transport.withSourceChannel(sc)) - // can't append MiddlewareFuncs - transport is still considered an HTTP transport - - // 3. this works, but would be overwritten when we do the same thing in profile conditional branch - // transport = &roundTripperWithMiddleware{ - // OriginalRoundTripper: transport, - // MiddlewareFuncs: []]MiddlewareFunc{ - // withSourceChannel(sc), - // }, - // } - - transport = &roundTripperWithMiddleware{ - OriginalRoundTripper: transport, - SourceChannel: withSourceChannel(sc), - } + mwFuncs = append(mwFuncs, withSourceChannel(sc)) } if cfg.Profile().OrganizationID != "" && cfg.Profile().ProjectID != "" { - // Use custom transport in order to set the organization and project IDs if set in user profile. - transport = &roundTripperWithMiddleware{ - OriginalRoundTripper: transport, - Profile: withProfile(cfg.Profile().OrganizationID, cfg.Profile().ProjectID), - } + + mwFuncs = append(mwFuncs, withProfile(cfg.Profile().OrganizationID, cfg.Profile().ProjectID)) + } + + transport = &roundTripperWithMiddleware{ + OriginalRoundTripper: transport, + MiddlewareFuncs: mwFuncs, } // Set the scheme based on the TLS configuration. From 01013930f7de8577d682bb20b0f32933cceae9b3 Mon Sep 17 00:00:00 2001 From: Brenna Hewer-Darroch <21015366+bcmdarroch@users.noreply.github.com> Date: Wed, 14 Dec 2022 14:42:25 -0500 Subject: [PATCH 12/18] add proj ID/org ID validation --- config/hcp.go | 6 ++++++ config/new_test.go | 20 ++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/config/hcp.go b/config/hcp.go index f5ff5a1d..a984df2e 100644 --- a/config/hcp.go +++ b/config/hcp.go @@ -138,6 +138,12 @@ func (c *hcpConfig) validate() error { return fmt.Errorf("either client credentials or oauth2 client ID must be provided") } + // Ensure profile contains both org ID and project ID + if (c.profile.OrganizationID == "" && c.profile.ProjectID != "") || + (c.profile.OrganizationID != "" && c.profile.ProjectID == "") { + return fmt.Errorf("when setting a user profile, both organization ID and project ID must be provided") + } + // Ensure the auth URL is valid if c.authURL.Host == "" { return fmt.Errorf("the auth URL has to be non-empty") diff --git a/config/new_test.go b/config/new_test.go index 98ab0563..e0eac192 100644 --- a/config/new_test.go +++ b/config/new_test.go @@ -111,6 +111,26 @@ func TestNew_Invalid(t *testing.T) { }, expectedError: "the configuration is not valid: the SCADA address has to be non-empty", }, + { + name: "empty project ID with populated org ID", + options: []HCPConfigOption{ + WithClientCredentials("my-client-id", "my-client-secret"), + WithProfile(&profile.UserProfile{ + OrganizationID: "abc123", + }), + }, + expectedError: "the configuration is not valid: when setting a user profile, both organization ID and project ID must be provided", + }, + { + name: "empty org ID with populated project ID", + options: []HCPConfigOption{ + WithClientCredentials("my-client-id", "my-client-secret"), + WithProfile(&profile.UserProfile{ + ProjectID: "abc123", + }), + }, + expectedError: "the configuration is not valid: when setting a user profile, both organization ID and project ID must be provided", + }, } for _, testCase := range testCases { From bd592c98843ddd478b0595181bb66e941aae2ca7 Mon Sep 17 00:00:00 2001 From: Brenna Hewer-Darroch <21015366+bcmdarroch@users.noreply.github.com> Date: Wed, 14 Dec 2022 18:36:59 -0500 Subject: [PATCH 13/18] update middleware test --- httpclient/httpclient_test.go | 61 +++++++++++------------------------ 1 file changed, 19 insertions(+), 42 deletions(-) diff --git a/httpclient/httpclient_test.go b/httpclient/httpclient_test.go index f4ccfb9d..5ee233e3 100644 --- a/httpclient/httpclient_test.go +++ b/httpclient/httpclient_test.go @@ -8,7 +8,6 @@ import ( "net/http" "net/http/httptest" "net/url" - "os" "sync/atomic" "testing" @@ -189,62 +188,40 @@ func TestNew(t *testing.T) { } -// TODO: merge the tests below into TestMiddleware with table-driven tests with different HCP client configs -// one with no profile, no source channel -// one with profile -// one with source channel -// one with profile and source channel - -// Defaults to production environment and client credentials -func TestProfileIntegration(t *testing.T) { - if testing.Short() { - t.Skip("skipping test in short mode (CI).") - } - - // Create a config that calls the test server - hcpConfig, err := config.NewHCPConfig( - config.WithClientCredentials(os.Getenv("HCP_CLIENT_ID"), os.Getenv("HCP_CLIENT_SECRET")), - config.WithProfile(&profile.UserProfile{OrganizationID: os.Getenv("HCP_ORGANIZATION_ID"), ProjectID: os.Getenv("HCP_PROJECT_ID")}), - ) - require.NoError(t, err) - - cl, err := New(Config{ - HCPConfig: hcpConfig, - }) - require.NoError(t, err) - - consulClient := consul.New(cl, nil) - listParams := consul.NewListParams() - _, err = consulClient.List(listParams, nil) - require.NoError(t, err) - -} - -// Defaults to production environment and client credentials -func TestSourceChannelIntegration(t *testing.T) { - if testing.Short() { - t.Skip("skipping test in short mode (CI).") - } +func TestMiddleware(t *testing.T) { + // Start with a plain request. request, err := http.NewRequest("GET", "api.cloud.hashicorp.com/consul/2021-02-04/organizations//projects//clusters", httptest.NewRecorder().Body) require.NoError(t, err) + // Prepare header is unset. require.Equal(t, request.Header.Get("X-HCP-Source-Channel"), "") + // Prepare middleware function. expectedSourceChannel := "source_channel_foo" - // TODO: since this is an integration test, we shouldn't be calling this function directly. Instead, we want to see that, - // if the HCP client is configured with source channel and profile, the request is properly mutated. + sourceChannelMiddleware := withSourceChannel(expectedSourceChannel) - // request, err = addMiddlewareToRequest(request, expectedSourceChannel, "", "") + // Apply middleware function. + err = sourceChannelMiddleware(request) require.NoError(t, err) + + // Assert request is modified as expected. + assert.Equal(t, request.Header.Get("X-HCP-Source-Channel"), expectedSourceChannel) + + // Assert path is unmodified. expectedOrgID := "org_id_77" expectedProjID := "proj_id_123" - assert.Equal(t, request.Header.Get("X-HCP-Source-Channel"), expectedSourceChannel) assert.NotContains(t, request.URL.Path, expectedOrgID) assert.NotContains(t, request.URL.Path, expectedProjID) - // request, err = addMiddlewareToRequest(request, "", expectedOrgID, expectedProjID) + // Prepare middleware function. + profileMiddleware := withProfile(expectedOrgID, expectedProjID) + + // Apply middleware function. + err = profileMiddleware(request) require.NoError(t, err) + + // Assert request is modified as expected. assert.Contains(t, request.URL.Path, expectedOrgID) assert.Contains(t, request.URL.Path, expectedProjID) assert.Equal(t, request.Header.Get("X-HCP-Source-Channel"), expectedSourceChannel) From 1ad9e50197005e4b848a20622d63ae5a15e6bb6e Mon Sep 17 00:00:00 2001 From: Brenna Hewer-Darroch <21015366+bcmdarroch@users.noreply.github.com> Date: Fri, 16 Dec 2022 11:40:34 -0500 Subject: [PATCH 14/18] remove unnecessary httpclient test --- httpclient/httpclient_test.go | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/httpclient/httpclient_test.go b/httpclient/httpclient_test.go index 5ee233e3..b2eaa53e 100644 --- a/httpclient/httpclient_test.go +++ b/httpclient/httpclient_test.go @@ -105,39 +105,6 @@ func TestNew(t *testing.T) { require.Equal(t, uint32(2), atomic.LoadUint32(&numRequests)) }) - t.Run("legacy configuration, no source channel", func(t *testing.T) { - numRequests = 0 - - cfg := Config{ - HostPath: ts.URL, - AuthURL: ts.URL, - ClientID: clientID, - ClientSecret: clientSecret, - // Override the base http.Client so that we trust the test server's TLS - Client: ts.Client(), - } - - // The test's important assertions occur in the test server handler above. - // When an http client is initialized, it tries to obtain an access token using the configured token URL (from an auth provider), client ID, and client secret. - // The token request embedded in this client initializer hits the mock auth provider handler above ('/oauth/token'). - cl, err := New(cfg) - require.NoError(t, err) - - consulClient := consul.New(cl, nil) - listParams := consul.NewListParams() - listParams.LocationOrganizationID = orgID - listParams.LocationProjectID = projID - - // This SDK request hits the mock Consul List API handler above ('/consul/2021-02-04/organizations...'). - // The handler verifies that the expected bearer token is provided. - _, err = consulClient.List(listParams, nil) - require.NoError(t, err) - - // Make sure we actually handled both the auth and the GET request and didn't - // just skip all the assertions! - require.Equal(t, uint32(2), atomic.LoadUint32(&numRequests)) - }) - t.Run("HCPConfig", func(t *testing.T) { numRequests = 0 From 4323b43762d8acc0ea48e59e0b4109b26abf1605 Mon Sep 17 00:00:00 2001 From: Brenna Hewer-Darroch <21015366+bcmdarroch@users.noreply.github.com> Date: Fri, 16 Dec 2022 11:54:54 -0500 Subject: [PATCH 15/18] move middleware into its own file for organization --- httpclient/httpclient.go | 47 ++++---------------------------------- httpclient/middleware.go | 49 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 43 deletions(-) create mode 100644 httpclient/middleware.go diff --git a/httpclient/httpclient.go b/httpclient/httpclient.go index 8b914c56..4abe97d5 100644 --- a/httpclient/httpclient.go +++ b/httpclient/httpclient.go @@ -52,45 +52,6 @@ type Config struct { Client *http.Client } -// HCPConfigOption are functions that modify the hcpConfig struct. They can be -// passed to NewHCPConfig. -type MiddlewareFunc = func(req *http.Request) error - -type roundTripperWithMiddleware struct { - OriginalRoundTripper http.RoundTripper - MiddlewareFuncs []MiddlewareFunc -} - -func withSourceChannel(sourceChannel string) MiddlewareFunc { - return func(req *http.Request) error { - req.Header.Set("X-HCP-Source-Channel", sourceChannel) - return nil - } -} - -func withProfile(orgID, projID string) MiddlewareFunc { - return func(req *http.Request) error { - path := req.URL.Path - path = strings.Replace(path, "organizations//", fmt.Sprintf("organizations/%s/", orgID), 1) - path = strings.Replace(path, "projects//", fmt.Sprintf("projects/%s/", projID), 1) - req.URL.Path = path - return nil - } -} - -func (rt *roundTripperWithMiddleware) RoundTrip(req *http.Request) (*http.Response, error) { - - for _, mw := range rt.MiddlewareFuncs { - if err := mw(req); err != nil { - // Failure to apply middleware should not fail the request - fmt.Printf("failed to apply middleware: %#v", mw(req)) - continue - } - } - - return rt.OriginalRoundTripper.RoundTrip(req) -} - // New creates a client with the right base path to connect to any HCP API func New(cfg Config) (runtime *httptransport.Runtime, err error) { // Populate default values where possible. @@ -108,23 +69,23 @@ func New(cfg Config) (runtime *httptransport.Runtime, err error) { Source: cfg, } - var mwFuncs []MiddlewareFunc + var opts []MiddlewareOption if cfg.SourceChannel != "" { // Use custom transport in order to set the source channel header when it is present. sc := fmt.Sprintf("%s hcp-go-sdk/%s", cfg.SourceChannel, version.Version) - mwFuncs = append(mwFuncs, withSourceChannel(sc)) + opts = append(opts, withSourceChannel(sc)) } if cfg.Profile().OrganizationID != "" && cfg.Profile().ProjectID != "" { - mwFuncs = append(mwFuncs, withProfile(cfg.Profile().OrganizationID, cfg.Profile().ProjectID)) + opts = append(opts, withProfile(cfg.Profile().OrganizationID, cfg.Profile().ProjectID)) } transport = &roundTripperWithMiddleware{ OriginalRoundTripper: transport, - MiddlewareFuncs: mwFuncs, + MiddlewareOptions: opts, } // Set the scheme based on the TLS configuration. diff --git a/httpclient/middleware.go b/httpclient/middleware.go new file mode 100644 index 00000000..ccd5eb62 --- /dev/null +++ b/httpclient/middleware.go @@ -0,0 +1,49 @@ +package httpclient + +import ( + "fmt" + "net/http" + "strings" +) + +// MiddlewareOption is a function that modifies an HTTP request. +type MiddlewareOption = func(req *http.Request) error + +// roundTripperWithMiddleware takes a plain Roundtripper and an array of MiddlewareOptions to apply to the Roundtripper's request. +type roundTripperWithMiddleware struct { + OriginalRoundTripper http.RoundTripper + MiddlewareOptions []MiddlewareOption +} + +// withSourceChannel updates the request header to include the HCP Go SDK source channel stamp. +func withSourceChannel(sourceChannel string) MiddlewareOption { + return func(req *http.Request) error { + req.Header.Set("X-HCP-Source-Channel", sourceChannel) + return nil + } +} + +// withProfile takes the user profile's org ID and project ID and sets them in the request path if needed. +func withProfile(orgID, projID string) MiddlewareOption { + return func(req *http.Request) error { + path := req.URL.Path + path = strings.Replace(path, "organizations//", fmt.Sprintf("organizations/%s/", orgID), 1) + path = strings.Replace(path, "projects//", fmt.Sprintf("projects/%s/", projID), 1) + req.URL.Path = path + return nil + } +} + +// RoundTrip attaches MiddlewareOption modifications to the request before sending along. +func (rt *roundTripperWithMiddleware) RoundTrip(req *http.Request) (*http.Response, error) { + + for _, mw := range rt.MiddlewareOptions { + if err := mw(req); err != nil { + // Failure to apply middleware should not fail the request + fmt.Printf("failed to apply middleware: %#v", mw(req)) + continue + } + } + + return rt.OriginalRoundTripper.RoundTrip(req) +} From 0888ac43bba9978242c7ae958978fb576d176ef7 Mon Sep 17 00:00:00 2001 From: Brenna Hewer-Darroch <21015366+bcmdarroch@users.noreply.github.com> Date: Fri, 16 Dec 2022 11:55:59 -0500 Subject: [PATCH 16/18] withProfile -> withOrgAndProjectIDs --- httpclient/httpclient.go | 2 +- httpclient/httpclient_test.go | 2 +- httpclient/middleware.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/httpclient/httpclient.go b/httpclient/httpclient.go index 4abe97d5..202a73b0 100644 --- a/httpclient/httpclient.go +++ b/httpclient/httpclient.go @@ -80,7 +80,7 @@ func New(cfg Config) (runtime *httptransport.Runtime, err error) { if cfg.Profile().OrganizationID != "" && cfg.Profile().ProjectID != "" { - opts = append(opts, withProfile(cfg.Profile().OrganizationID, cfg.Profile().ProjectID)) + opts = append(opts, withOrgAndProjectIDs(cfg.Profile().OrganizationID, cfg.Profile().ProjectID)) } transport = &roundTripperWithMiddleware{ diff --git a/httpclient/httpclient_test.go b/httpclient/httpclient_test.go index b2eaa53e..9938d793 100644 --- a/httpclient/httpclient_test.go +++ b/httpclient/httpclient_test.go @@ -182,7 +182,7 @@ func TestMiddleware(t *testing.T) { assert.NotContains(t, request.URL.Path, expectedProjID) // Prepare middleware function. - profileMiddleware := withProfile(expectedOrgID, expectedProjID) + profileMiddleware := withOrgAndProjectIDs(expectedOrgID, expectedProjID) // Apply middleware function. err = profileMiddleware(request) diff --git a/httpclient/middleware.go b/httpclient/middleware.go index ccd5eb62..d3150fb4 100644 --- a/httpclient/middleware.go +++ b/httpclient/middleware.go @@ -24,7 +24,7 @@ func withSourceChannel(sourceChannel string) MiddlewareOption { } // withProfile takes the user profile's org ID and project ID and sets them in the request path if needed. -func withProfile(orgID, projID string) MiddlewareOption { +func withOrgAndProjectIDs(orgID, projID string) MiddlewareOption { return func(req *http.Request) error { path := req.URL.Path path = strings.Replace(path, "organizations//", fmt.Sprintf("organizations/%s/", orgID), 1) From 523af9ab046ea9ba0bd50611b6554f3882230d1c Mon Sep 17 00:00:00 2001 From: Brenna Hewer-Darroch <21015366+bcmdarroch@users.noreply.github.com> Date: Fri, 16 Dec 2022 11:56:12 -0500 Subject: [PATCH 17/18] update changelog entry --- .changelog/142.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.changelog/142.txt b/.changelog/142.txt index eb455433..d44c048c 100644 --- a/.changelog/142.txt +++ b/.changelog/142.txt @@ -1,3 +1,7 @@ ```release-note:improvement -Insert middleware to allow organization and project id on requests +Add middleware package +``` + +```release-note:improvement +Add middleware that gets org ID and project ID from user profile and sets on request ``` \ No newline at end of file From 9920cbca3b6b52719662f58b0b12eded32d9644c Mon Sep 17 00:00:00 2001 From: Brenna Hewer-Darroch <21015366+bcmdarroch@users.noreply.github.com> Date: Fri, 16 Dec 2022 11:57:44 -0500 Subject: [PATCH 18/18] update changelog (again) --- .changelog/142.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.changelog/142.txt b/.changelog/142.txt index d44c048c..43daa409 100644 --- a/.changelog/142.txt +++ b/.changelog/142.txt @@ -1,7 +1,7 @@ ```release-note:improvement -Add middleware package +Add middleware support to httpclient package ``` ```release-note:improvement Add middleware that gets org ID and project ID from user profile and sets on request -``` \ No newline at end of file +```