Skip to content
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

Add config option to skip non existent keys #13

Merged
merged 3 commits into from
Mar 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
[![Go Reference](https://pkg.go.dev/badge/github.com/adelowo/gulter.svg)](https://pkg.go.dev/github.com/adelowo/gulter)
[![Go Report Card](https://goreportcard.com/badge/github.com/adelowo/gulter)](https://goreportcard.com/report/github.com/adelowo/gulter)


Gulter is a Go HTTP middleware designed to simplify the process of uploading files
for your web apps. It follows the standard
`http.Handler` and `http.HandlerFunc` interfaces so you can
Expand Down Expand Up @@ -53,7 +52,8 @@ To create a new Gulter instance, you can do something like this:
The `handler` is really just a HTTP middleware with the following signature
`Upload(keys ...string) func(next http.Handler) http.Handler`. `keys` here
are the input names from the HTML form, so you can chain this into almost any HTTP
router,
router.


### Standard HTTP router

Expand Down Expand Up @@ -175,6 +175,13 @@ Gulter also ships with two storage implementations at the moment:
- `DiskStore`: uses a local filesystem backed store to upload files
- `CloudinaryStore`: uploads file to cloudinary

## Ignoring non existent keys in the multipart Request

Sometimes, the keys you have configured the middleware might get dropped from the
frontend for some reason, ideally the middleware fails if it cannot find a
configured key in the request. To disable this behavior and ignore the missing
key, you can make use of the `WithIgnoreNonExistentKey(true)` option to prevent the
middleware from causing an error when such keys do not exists

## Writing your custom validator logic

Expand Down
6 changes: 6 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,9 @@ func WithNameFuncGenerator(nameFunc NameGeneratorFunc) Option {
g.nameFuncGenerator = nameFunc
}
}

func WithIgnoreNonExistentKey(ignore bool) Option {
return func(g *Gulter) {
g.ignoreNonExistentKeys = ignore
}
}
15 changes: 13 additions & 2 deletions handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,15 @@ type ErrResponseHandler func(error) http.HandlerFunc
type NameGeneratorFunc func(s string) string

type Gulter struct {
storage Storage
maxSize int64
storage Storage
maxSize int64

// when you configure the middleware, you usually provide a list of
// keys to retrieve the files from. If any of these keys do not exists,
// the handler fails.
// If this option is set to true, the value is just skipped instead
ignoreNonExistentKeys bool

validationFunc ValidationFunc
nameFuncGenerator NameGeneratorFunc
errorResponseHandler ErrResponseHandler
Expand Down Expand Up @@ -106,6 +113,10 @@ func (h *Gulter) Upload(keys ...string) func(next http.Handler) http.Handler {
wg.Go(func() error {
f, header, err := r.FormFile(key)
if err != nil {
if h.ignoreNonExistentKeys {
return nil
}

return err
}

Expand Down
40 changes: 36 additions & 4 deletions handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ func TestGulter(t *testing.T) {
// ignoreFormField instructs the test to not add the
// multipar form data part to the request
ignoreFormField bool

useIgnoreSkipOpt bool
}{
{
name: "uploading succeeds",
Expand Down Expand Up @@ -80,6 +82,24 @@ func TestGulter(t *testing.T) {
validMimeTypes: []string{"image/png", "application/pdf"},
ignoreFormField: true,
},
{
// this test case will use the WithIgnore option
name: "upload middleware succeeds even if the form field does not exist",
maxFileSize: 1024,
fn: func(store *mocks.MockStorage, size int64) {
store.EXPECT().
Upload(gomock.Any(), gomock.Any(), gomock.Any()).
Return(&gulter.UploadedFileMetadata{
Size: size,
}, errors.New("could not upload file")).
Times(0) // make sure this is never called
},
expectedStatusCode: http.StatusAccepted,
pathToFile: "gulter.md",
validMimeTypes: []string{"image/png", "application/pdf"},
ignoreFormField: true,
useIgnoreSkipOpt: true,
},
{
name: "upload fails because of mimetype validation constraints",
maxFileSize: 1024,
Expand Down Expand Up @@ -134,11 +154,17 @@ func TestGulter(t *testing.T) {

storage := mocks.NewMockStorage(ctrl)

handler, err := gulter.New(gulter.WithMaxFileSize(v.maxFileSize),
opts := []gulter.Option{
gulter.WithMaxFileSize(v.maxFileSize),
gulter.WithStorage(storage),
gulter.WithValidationFunc(gulter.MimeTypeValidator(v.validMimeTypes...)),
)
}

if v.useIgnoreSkipOpt {
opts = append(opts, gulter.WithIgnoreNonExistentKey(true))
}

handler, err := gulter.New(opts...)
require.NoError(t, err)

buffer := bytes.NewBuffer(nil)
Expand All @@ -164,19 +190,25 @@ func TestGulter(t *testing.T) {
require.NoError(t, multipartWriter.Close())

recorder := httptest.NewRecorder()
r := httptest.NewRequest(http.MethodPatch, "/", buffer)

r := httptest.NewRequest(http.MethodPatch, "/", buffer)
r.Header.Set("Content-Type", multipartWriter.FormDataContentType())

handler.Upload("form-field")(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if v.useIgnoreSkipOpt {
w.WriteHeader(http.StatusAccepted)
fmt.Fprintf(w, "skipping check since we did not upload any file")
return
}

file, err := gulter.FileFromContext(r, "form-field")

require.NoError(t, err)

require.Equal(t, v.pathToFile, file.OriginalName)

w.WriteHeader(http.StatusAccepted)
fmt.Fprintf(w, "successfully uploade the file")
fmt.Fprintf(w, "successfully uploaded the file")
})).ServeHTTP(recorder, r)

require.Equal(t, v.expectedStatusCode, recorder.Result().StatusCode)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
skipping check since we did not upload any file
2 changes: 1 addition & 1 deletion testdata/golden/TestGulter/uploading_succeeds.golden
Original file line number Diff line number Diff line change
@@ -1 +1 @@
successfully uploade the file
successfully uploaded the file
Loading