Skip to content

Commit 984d9cd

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

File tree

7 files changed

+179
-6
lines changed

7 files changed

+179
-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 := getBaseUrl(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: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package filters
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
"net/http"
8+
"net/http/httptest"
9+
"testing"
10+
11+
"github.com/google/uuid"
12+
"github.com/stretchr/testify/assert"
13+
"github.com/stretchr/testify/require"
14+
)
15+
16+
func Test_GetFilters(t *testing.T) {
17+
ctx := context.Background()
18+
orgID := uuid.New()
19+
expectedAllow := AllowList{
20+
ConfigFiles: []string{"package.json"},
21+
Extensions: []string{".ts", ".js"},
22+
}
23+
c, cleanup := setupTest(t, orgID, expectedAllow)
24+
defer cleanup()
25+
26+
allow, err := c.GetFilters(ctx, orgID)
27+
require.NoError(t, err)
28+
29+
assert.Equal(t, expectedAllow, allow)
30+
}
31+
32+
func setupTest(t *testing.T, orgID uuid.UUID, expectedAllow AllowList) (Client, func()) {
33+
t.Helper()
34+
35+
s := setupServer(t, orgID, expectedAllow)
36+
cleanup := func() {
37+
s.Close()
38+
}
39+
c := NewDeeproxyClient(Config{BaseURL: s.URL}, WithHTTPClient(s.Client()))
40+
41+
return c, cleanup
42+
}
43+
44+
func setupServer(t *testing.T, orgID uuid.UUID, expectedAllow AllowList) *httptest.Server {
45+
t.Helper()
46+
ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
47+
expectedURL := fmt.Sprintf("/hidden/orgs/%s/code/filters", orgID)
48+
assert.Equal(t, expectedURL, r.URL.Path)
49+
assert.Equal(t, orgID.String(), r.Header.Get("snyk-org-name"))
50+
w.Header().Set("Content-Type", "application/json")
51+
if err := json.NewEncoder(w).Encode(expectedAllow); err != nil {
52+
http.Error(w, "failed to encode response", http.StatusInternalServerError)
53+
return
54+
}
55+
}))
56+
ts.Start()
57+
return ts
58+
}

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 getBaseUrl(url string, orgID uuid.UUID, isFedRamp bool) string {
11+
if isFedRamp {
12+
return fmt.Sprintf("%s/hidden/orgs/%s/code/filters", url, orgID)
13+
} else {
14+
deeproxyUrl := strings.ReplaceAll(url, "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 := getBaseUrl("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 := getBaseUrl("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)