Skip to content

Commit

Permalink
feat: switch ContentType from options pattern to fluent
Browse files Browse the repository at this point in the history
  • Loading branch information
rvagg committed Sep 7, 2023
1 parent 7777929 commit d9f0719
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 63 deletions.
38 changes: 16 additions & 22 deletions http/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,37 +51,33 @@ func (ct ContentType) String() string {
return sb.String()
}

type ContentTypeOption func(ct *ContentType)
func (ct ContentType) WithOrder(order ContentTypeOrder) ContentType {
ct.Order = order
return ct
}

func WithContentTypeOrder(order ContentTypeOrder) ContentTypeOption {
return func(ct *ContentType) {
ct.Order = order
}
func (ct ContentType) WithDuplicates(duplicates bool) ContentType {
ct.Duplicates = duplicates
return ct
}

func WithContentTypeDuplicates(duplicates bool) ContentTypeOption {
return func(ct *ContentType) {
ct.Duplicates = duplicates
}
func (ct ContentType) WithQuality(quality float32) ContentType {
ct.Quality = quality
return ct
}

func WithContentTypeQuality(quality float32) ContentTypeOption {
return func(ct *ContentType) {
ct.Quality = quality
}
func (ct ContentType) WithMime(mime string) ContentType {
ct.Mime = mime
return ct
}

func NewContentType(opt ...ContentTypeOption) ContentType {
ct := ContentType{
func DefaultContentType() ContentType {
return ContentType{
Mime: MimeTypeCar,
Order: DefaultOrder,
Duplicates: DefaultIncludeDupes,
Quality: 1,
}
for _, o := range opt {
o(&ct)
}
return ct
}

// ResponseContentTypeHeader returns the value for the Content-Type header for a
Expand All @@ -90,9 +86,7 @@ func NewContentType(opt ...ContentTypeOption) ContentType {
//
// Deprecated: Use NewContentType().String() instead.
func ResponseContentTypeHeader(duplicates bool) string {
ct := NewContentType()
ct.Duplicates = duplicates
return ct.String()
return DefaultContentType().WithDuplicates(duplicates).String()
}

// RequestAcceptHeader returns the value for the Accept header for a Trustless
Expand Down
12 changes: 6 additions & 6 deletions http/constants_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ func TestContentType(t *testing.T) {
req.Equal("application/vnd.ipld.car;version=1;order=dfs;dups=n", trustlesshttp.ResponseContentTypeHeader(false))
req.Equal("application/vnd.ipld.car;version=1;order=dfs;dups=n", trustlesshttp.RequestAcceptHeader(false))

req.Equal("application/vnd.ipld.car;version=1;order=dfs;dups=y", trustlesshttp.NewContentType().String())
req.Equal("application/vnd.ipld.car;version=1;order=dfs;dups=y;q=0.8", trustlesshttp.NewContentType(trustlesshttp.WithContentTypeQuality(0.8)).String())
req.Equal("application/vnd.ipld.car;version=1;order=dfs;dups=y;q=0.333", trustlesshttp.NewContentType(trustlesshttp.WithContentTypeQuality(1.0/3.0)).String())
req.Equal("application/vnd.ipld.car;version=1;order=dfs;dups=y", trustlesshttp.NewContentType(trustlesshttp.WithContentTypeQuality(-1.0)).String())
req.Equal("application/vnd.ipld.car;version=1;order=dfs;dups=n", trustlesshttp.NewContentType(trustlesshttp.WithContentTypeDuplicates(false)).String())
req.Equal("application/vnd.ipld.car;version=1;order=unk;dups=n", trustlesshttp.NewContentType(trustlesshttp.WithContentTypeDuplicates(false), trustlesshttp.WithContentTypeOrder(trustlesshttp.ContentTypeOrderUnk)).String())
req.Equal("application/vnd.ipld.car;version=1;order=dfs;dups=y", trustlesshttp.DefaultContentType().String())
req.Equal("application/vnd.ipld.car;version=1;order=dfs;dups=y;q=0.8", trustlesshttp.DefaultContentType().WithQuality(0.8).String())
req.Equal("application/vnd.ipld.car;version=1;order=dfs;dups=y;q=0.333", trustlesshttp.DefaultContentType().WithQuality(1.0/3.0).String())
req.Equal("application/vnd.ipld.car;version=1;order=dfs;dups=y", trustlesshttp.DefaultContentType().WithQuality(-1.0).String())
req.Equal("application/vnd.ipld.car;version=1;order=dfs;dups=n", trustlesshttp.DefaultContentType().WithDuplicates(false).String())
req.Equal("application/vnd.ipld.car;version=1;order=unk;dups=n", trustlesshttp.DefaultContentType().WithDuplicates(false).WithOrder(trustlesshttp.ContentTypeOrderUnk).String())
}
5 changes: 2 additions & 3 deletions http/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ func CheckFormat(req *http.Request) (ContentType, error) {
}

if validFormat {
return NewContentType(), nil // default is acceptable in this case (no accept but format=car)
return DefaultContentType(), nil // default is acceptable in this case (no accept but format=car)
}

return ContentType{}, fmt.Errorf("neither a valid Accept header nor format parameter were provided")
Expand Down Expand Up @@ -140,8 +140,7 @@ func parseContentType(header string, strictType bool) (ContentType, bool) {
typeParts := strings.Split(header, ";")
mime := strings.TrimSpace(typeParts[0])
if mime == MimeTypeCar || (!strictType && (mime == "*/*" || mime == "application/*")) {
contentType := NewContentType()
contentType.Mime = mime
contentType := DefaultContentType().WithMime(mime)
// parse additional car attributes outlined in IPIP-412
// https://specs.ipfs.tech/http-gateways/trustless-gateway/
for _, nextPart := range typeParts[1:] {
Expand Down
64 changes: 32 additions & 32 deletions http/parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,15 +113,15 @@ func TestCheckFormat(t *testing.T) {
err string
}{
{"empty (err)", "", "", trustlesshttp.ContentType{}, "neither a valid Accept header nor format parameter were provided"},
{"format=bop (err)", "", "format=bop", trustlesshttp.NewContentType(), "invalid format parameter; unsupported: \"bop\""},
{"format=car", "", "format=car", trustlesshttp.NewContentType(), ""},
{"plain accept", "application/vnd.ipld.car", "", trustlesshttp.NewContentType(), ""},
{"accept dups", "application/vnd.ipld.car; dups=y", "", trustlesshttp.NewContentType(), ""},
{"accept no dups", "application/vnd.ipld.car; dups=n", "", trustlesshttp.NewContentType(trustlesshttp.WithContentTypeDuplicates(false)), ""},
{"accept no dups and cruft", "application/vnd.ipld.car; dups=n; bip; bop", "", trustlesshttp.NewContentType(trustlesshttp.WithContentTypeDuplicates(false)), ""},
{"valid accept but format=bop (err)", "application/vnd.ipld.car; dups=y", "format=bop", trustlesshttp.NewContentType(), "invalid format parameter; unsupported: \"bop\""},
{"valid accept but format=car", "application/vnd.ipld.car; dups=y", "format=car", trustlesshttp.NewContentType(), ""},
{"invalid accept but format=car", "application/vnd.ipld.car; dups=YES!", "format=car", trustlesshttp.NewContentType(trustlesshttp.WithContentTypeDuplicates(false)), "invalid Accept header; unsupported"},
{"format=bop (err)", "", "format=bop", trustlesshttp.DefaultContentType(), "invalid format parameter; unsupported: \"bop\""},
{"format=car", "", "format=car", trustlesshttp.DefaultContentType(), ""},
{"plain accept", "application/vnd.ipld.car", "", trustlesshttp.DefaultContentType(), ""},
{"accept dups", "application/vnd.ipld.car; dups=y", "", trustlesshttp.DefaultContentType(), ""},
{"accept no dups", "application/vnd.ipld.car; dups=n", "", trustlesshttp.DefaultContentType().WithDuplicates(false), ""},
{"accept no dups and cruft", "application/vnd.ipld.car; dups=n; bip; bop", "", trustlesshttp.DefaultContentType().WithDuplicates(false), ""},
{"valid accept but format=bop (err)", "application/vnd.ipld.car; dups=y", "format=bop", trustlesshttp.DefaultContentType(), "invalid format parameter; unsupported: \"bop\""},
{"valid accept but format=car", "application/vnd.ipld.car; dups=y", "format=car", trustlesshttp.DefaultContentType(), ""},
{"invalid accept but format=car", "application/vnd.ipld.car; dups=YES!", "format=car", trustlesshttp.DefaultContentType().WithDuplicates(false), "invalid Accept header; unsupported"},
} {
t.Run(tc.name, func(t *testing.T) {
req := &http.Request{}
Expand All @@ -143,32 +143,32 @@ func TestCheckFormat(t *testing.T) {

func TestParseContentType(t *testing.T) {
for _, tc := range []struct {
name string
accept string
expectValidContentType bool
expectDups bool
name string
accept string
expectValid bool
expectContentType trustlesshttp.ContentType
}{
{"empty (err)", "", false, false},
{"plain", "application/vnd.ipld.car", true, true},
{"*/*", "*/*", false, false},
{"application/*", "application/*", false, false},
{"dups", "application/vnd.ipld.car; dups=y", true, true},
{"no dups", "application/vnd.ipld.car; dups=n", true, false},
{"no dups and cruft", "application/vnd.ipld.car; dups=n; bip; bop", true, false},
{"version=1", "application/vnd.ipld.car; version=1; dups=n", true, false},
{"version=2", "application/vnd.ipld.car; version=2; dups=n", false, false},
{"order=dfs", "application/vnd.ipld.car; order=dfs; dups=n", true, false},
{"order=unk", "application/vnd.ipld.car; order=unk; dups=n", true, false},
{"order=bork", "application/vnd.ipld.car; order=bork; dups=y", false, false},
{"complete", "application/vnd.ipld.car; order=dfs; dups=y; version=1", true, true},
{"complete (squish)", "application/vnd.ipld.car;order=dfs;dups=y;version=1", true, true},
{"complete (shuffle)", "application/vnd.ipld.car;version=1;dups=y;order=dfs;", true, true},
{"complete (cruft)", "application/vnd.ipld.car;;version=1; bip ; dups=n ;bop;order=dfs;--", true, false},
{"empty (err)", "", false, trustlesshttp.ContentType{}},
{"plain", "application/vnd.ipld.car", true, trustlesshttp.DefaultContentType()},
{"*/*", "*/*", false, trustlesshttp.ContentType{}},
{"application/*", "application/*", false, trustlesshttp.ContentType{}},
{"dups", "application/vnd.ipld.car; dups=y", true, trustlesshttp.DefaultContentType()},
{"no dups", "application/vnd.ipld.car; dups=n", true, trustlesshttp.DefaultContentType().WithDuplicates(false)},
{"no dups and cruft", "application/vnd.ipld.car; dups=n; bip; bop", true, trustlesshttp.DefaultContentType().WithDuplicates(false)},
{"version=1", "application/vnd.ipld.car; version=1; dups=n", true, trustlesshttp.DefaultContentType().WithDuplicates(false)},
{"version=2", "application/vnd.ipld.car; version=2; dups=n", false, trustlesshttp.ContentType{}},
{"order=dfs", "application/vnd.ipld.car; order=dfs; dups=n", true, trustlesshttp.DefaultContentType().WithDuplicates(false)},
{"order=unk", "application/vnd.ipld.car; order=unk; dups=n", true, trustlesshttp.DefaultContentType().WithDuplicates(false).WithOrder(trustlesshttp.ContentTypeOrderUnk)},
{"order=bork", "application/vnd.ipld.car; order=bork; dups=y", false, trustlesshttp.ContentType{}},
{"complete", "application/vnd.ipld.car; order=dfs; dups=y; version=1", true, trustlesshttp.DefaultContentType()},
{"complete (squish)", "application/vnd.ipld.car;order=dfs;dups=y;version=1", true, trustlesshttp.DefaultContentType()},
{"complete (shuffle)", "application/vnd.ipld.car;version=1;dups=y;order=dfs;", true, trustlesshttp.DefaultContentType()},
{"complete (cruft)", "application/vnd.ipld.car;;version=1; bip ; dups=n ;bop;order=dfs;--", true, trustlesshttp.DefaultContentType().WithDuplicates(false)},
} {
t.Run(tc.name, func(t *testing.T) {
valid, dups := trustlesshttp.ParseContentType(tc.accept)
require.Equal(t, tc.expectValidContentType, valid)
require.Equal(t, tc.expectDups, dups)
ct, valid := trustlesshttp.ParseContentType(tc.accept)
require.Equal(t, tc.expectValid, valid)
require.Equal(t, tc.expectContentType, ct)
})
}
}
Expand Down

0 comments on commit d9f0719

Please sign in to comment.