Skip to content

Commit 0afa27d

Browse files
paulrosca-snykCalamarBicefalo
authored andcommitted
wip: filters client
1 parent 61ac7a4 commit 0afa27d

File tree

8 files changed

+221
-6
lines changed

8 files changed

+221
-6
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@
44
### Local development ###
55
# Local build system configuration
66
/local.mk
7+
.idea
78

89
node_modules

internal/fileupload/filters/client.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package filters
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
"net/http"
8+
9+
"github.com/google/uuid"
10+
)
11+
12+
// AllowList represents the response structure from the deeproxy filters API.
13+
type AllowList struct {
14+
ConfigFiles []string `json:"configFiles"`
15+
Extensions []string `json:"extensions"`
16+
}
17+
18+
type Client interface {
19+
GetFilters(ctx context.Context, orgID uuid.UUID) (AllowList, error)
20+
}
21+
22+
type deeproxyClient struct {
23+
httpClient *http.Client
24+
cfg Config
25+
}
26+
27+
type Config struct {
28+
BaseURL string
29+
IsFedRamp bool
30+
}
31+
32+
func NewDeeproxyClient(cfg Config, opts ...Opt) *deeproxyClient {
33+
c := &deeproxyClient{
34+
cfg: cfg,
35+
httpClient: http.DefaultClient,
36+
}
37+
38+
for _, opt := range opts {
39+
opt(c)
40+
}
41+
42+
return c
43+
}
44+
45+
func (c *deeproxyClient) GetFilters(ctx context.Context, orgID uuid.UUID) (AllowList, error) {
46+
var allowList AllowList
47+
48+
url := getFilterUrl(c.cfg.BaseURL, orgID, c.cfg.IsFedRamp)
49+
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, http.NoBody)
50+
if err != nil {
51+
return allowList, fmt.Errorf("failed to create deeproxy filters request: %w", err)
52+
}
53+
54+
req.Header.Set("snyk-org-name", orgID.String())
55+
56+
resp, err := c.httpClient.Do(req)
57+
if err != nil {
58+
return allowList, fmt.Errorf("error making deeproxy filters request: %w", err)
59+
}
60+
defer resp.Body.Close()
61+
62+
if resp.StatusCode < 200 || resp.StatusCode > 299 {
63+
return allowList, fmt.Errorf("unexpected response code: %s", resp.Status)
64+
}
65+
66+
if err := json.NewDecoder(resp.Body).Decode(&allowList); err != nil {
67+
return allowList, fmt.Errorf("failed to decode deeproxy filters response: %w", err)
68+
}
69+
70+
return allowList, nil
71+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package filters
2+
3+
import (
4+
"encoding/json"
5+
"net/http"
6+
"net/http/httptest"
7+
"testing"
8+
9+
"github.com/google/uuid"
10+
"github.com/stretchr/testify/assert"
11+
"github.com/stretchr/testify/require"
12+
)
13+
14+
func TestClients(t *testing.T) {
15+
tests := []struct {
16+
getClient func(t *testing.T, orgID uuid.UUID, expectedAllow AllowList) (Client, func())
17+
clientName string
18+
}{
19+
{
20+
clientName: "deeproxyClient",
21+
getClient: func(t *testing.T, orgID uuid.UUID, expectedAllow AllowList) (Client, func()) {
22+
t.Helper()
23+
24+
s := setupServer(t, orgID, expectedAllow)
25+
cleanup := func() {
26+
s.Close()
27+
}
28+
c := NewDeeproxyClient(Config{BaseURL: s.URL, IsFedRamp: true}, WithHTTPClient(s.Client()))
29+
30+
return c, cleanup
31+
},
32+
},
33+
{
34+
clientName: "fakeClient",
35+
getClient: func(t *testing.T, orgID uuid.UUID, expectedAllow AllowList) (Client, func()) {
36+
t.Helper()
37+
38+
c := NewFakeClient(expectedAllow, nil)
39+
40+
return c, func() {}
41+
},
42+
},
43+
}
44+
45+
for _, testData := range tests {
46+
t.Run(testData.clientName+": GetFilters", func(t *testing.T) {
47+
orgID := uuid.New()
48+
expectedAllow := AllowList{
49+
ConfigFiles: []string{"package.json"},
50+
Extensions: []string{".ts", ".js"},
51+
}
52+
client, cleanup := testData.getClient(t, orgID, expectedAllow)
53+
defer cleanup()
54+
55+
allow, err := client.GetFilters(t.Context(), orgID)
56+
require.NoError(t, err)
57+
58+
assert.Equal(t, expectedAllow, allow)
59+
})
60+
}
61+
}
62+
63+
func setupServer(t *testing.T, orgID uuid.UUID, expectedAllow AllowList) *httptest.Server {
64+
t.Helper()
65+
ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
66+
expectedURL := getFilterUrl("", orgID, true)
67+
assert.Equal(t, expectedURL, r.URL.Path)
68+
assert.Equal(t, orgID.String(), r.Header.Get("snyk-org-name"))
69+
w.Header().Set("Content-Type", "application/json")
70+
if err := json.NewEncoder(w).Encode(expectedAllow); err != nil {
71+
http.Error(w, "failed to encode response", http.StatusInternalServerError)
72+
return
73+
}
74+
}))
75+
ts.Start()
76+
return ts
77+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package filters
2+
3+
import (
4+
"context"
5+
"github.com/google/uuid"
6+
)
7+
8+
type fakeClient struct {
9+
getFilters func(ctx context.Context, orgID uuid.UUID) (AllowList, error)
10+
}
11+
12+
func NewFakeClient(allowList AllowList, err error) Client {
13+
return &fakeClient{
14+
getFilters: func(ctx context.Context, orgID uuid.UUID) (AllowList, error) {
15+
return allowList, err
16+
},
17+
}
18+
19+
}
20+
21+
func (f *fakeClient) GetFilters(ctx context.Context, orgID uuid.UUID) (AllowList, error) {
22+
return f.getFilters(ctx, orgID)
23+
}

internal/fileupload/filters/opts.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package filters
2+
3+
import "net/http"
4+
5+
// Opt is a function that configures an deeproxyClient instance.
6+
type Opt func(*deeproxyClient)
7+
8+
// WithHTTPClient sets a custom HTTP client for the filters client.
9+
func WithHTTPClient(httpClient *http.Client) Opt {
10+
return func(c *deeproxyClient) {
11+
c.httpClient = httpClient
12+
}
13+
}

internal/fileupload/filters/utils.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package filters
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
7+
"github.com/google/uuid"
8+
)
9+
10+
func getFilterUrl(baseUrl string, orgID uuid.UUID, isFedRamp bool) string {
11+
if isFedRamp {
12+
return fmt.Sprintf("%s/hidden/orgs/%s/code/filters", baseUrl, orgID)
13+
} else {
14+
deeproxyUrl := strings.ReplaceAll(baseUrl, "api", "deeproxy")
15+
return fmt.Sprintf("%s/filters", deeproxyUrl)
16+
}
17+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package filters
2+
3+
import (
4+
"github.com/google/uuid"
5+
"github.com/stretchr/testify/assert"
6+
"testing"
7+
)
8+
9+
var orgId = uuid.MustParse("738ef92e-21cc-4a11-8c13-388d89272f4b")
10+
11+
func Test_getBaseUrl_notFedramp(t *testing.T) {
12+
actualUrl := getFilterUrl("https://api.snyk.io", orgId, false)
13+
assert.Equal(t, "https://deeproxy.snyk.io/filters", actualUrl)
14+
}
15+
16+
func Test_getBaseUrl_fedramp(t *testing.T) {
17+
actualUrl := getFilterUrl("https://api.snyk.io", orgId, true)
18+
assert.Equal(t, "https://api.snyk.io/hidden/orgs/738ef92e-21cc-4a11-8c13-388d89272f4b/code/filters", actualUrl)
19+
}

internal/fileupload/types.go

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,6 @@ type Filters struct {
2222
initErr error
2323
}
2424

25-
// FiltersResponse represents the response structure from the deeproxy filters API.
26-
type FiltersResponse struct {
27-
ConfigFiles []string `json:"configFiles"`
28-
Extensions []string `json:"extensions"`
29-
}
30-
3125
// UploadOptions configures the behavior of file upload operations.
3226
type UploadOptions struct {
3327
SkipFiltering bool

0 commit comments

Comments
 (0)