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

feat: add ability to unmarshal Upload from map[string]interface #3330

Closed
Closed
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
48 changes: 41 additions & 7 deletions graphql/upload.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
package graphql

import (
"encoding/json"
"fmt"
"io"
)

var (
invalidUpload = "%T is not an Upload"
)

type Upload struct {
File io.ReadSeeker
Filename string
Size int64
ContentType string
File io.ReadSeeker `json:"file"`
Filename string `json:"filename"`
Size int64 `json:"size"`
ContentType string `json:"contentType"`
}

func MarshalUpload(f Upload) Marshaler {
Expand All @@ -18,10 +23,39 @@ func MarshalUpload(f Upload) Marshaler {
})
}

// UnmarshalUpload reads an Upload from a JSON-encoded value.
func UnmarshalUpload(v any) (Upload, error) {
upload, ok := v.(Upload)
if !ok {
return Upload{}, fmt.Errorf("%T is not an Upload", v)
switch t := v.(type) {
case Upload:
return t, nil
case map[string]interface{}:
upload, err := unmarshalUploadMap(t)
if err != nil {
return Upload{}, fmt.Errorf(invalidUpload, v)
}

return upload, nil
default:
return Upload{}, fmt.Errorf(invalidUpload, v)
}
}

// unmarshalUploadMap reads an Upload from a map value and returns the struct if it's not empty
func unmarshalUploadMap(m map[string]interface{}) (Upload, error) {
var upload Upload

out, err := json.Marshal(m)
if err != nil {
return upload, err
}

// Unmarshal the JSON-encoded value into the Upload struct
// ignoring the error because we want to return the Upload struct if it's not empty
json.Unmarshal(out, &upload) // nolint: errcheck

if (Upload{}) == upload {
return Upload{}, fmt.Errorf(invalidUpload, m)
}

return upload, nil
}
81 changes: 81 additions & 0 deletions graphql/upload_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package graphql

import (
"io"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestUnmarshalUpload(t *testing.T) {
// Create a ReadSeeker with nil value to test the Upload struct
file := io.ReadSeeker(nil)

tests := []struct {
name string
input any
want Upload
wantErr bool
}{
{
name: "valid Upload struct",
input: Upload{
File: file,
Filename: "test.txt",
Size: 1234,
ContentType: "text/plain",
},
want: Upload{
File: file,
Filename: "test.txt",
Size: 1234,
ContentType: "text/plain",
},
wantErr: false,
},
{
name: "invalid type",
input: "invalid",
want: Upload{},
wantErr: true,
},
{
name: "valid JSON",
input: map[string]interface{}{
"file": file,
"filename": "test.txt",
"size": 1234,
"contentType": "text/plain",
},
want: Upload{
File: file,
Filename: "test.txt",
Size: 1234,
ContentType: "text/plain",
},
wantErr: false,
},
{
name: "invalid JSON",
input: map[string]interface{}{
"hello": "invalid",
},
want: Upload{},
wantErr: true,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := UnmarshalUpload(tt.input)
if tt.wantErr {
require.Error(t, err)
} else {
require.NoError(t, err)
}

assert.Equal(t, tt.want, got)
})
}
}