Skip to content

Commit 3227708

Browse files
Vipul Rawataryanmehrotra
andauthored
Add multipart form file bind and add basic zip file functionalities (#394)
--------- Co-authored-by: Aryan Mehrotra <[email protected]>
1 parent c81182a commit 3227708

File tree

18 files changed

+863
-16
lines changed

18 files changed

+863
-16
lines changed

examples/http-server-using-redis/main_test.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ func TestHTTPServerUsingRedis(t *testing.T) {
4343

4444
for i, tc := range tests {
4545
req, _ := http.NewRequest(tc.method, host+tc.path, bytes.NewBuffer(tc.body))
46+
req.Header.Set("content-type", "application/json")
4647
c := http.Client{}
4748
resp, err := c.Do(req)
4849

@@ -63,7 +64,7 @@ func TestRedisSetHandler(t *testing.T) {
6364
mock.ExpectSet("key", "value", 5*time.Minute).SetErr(testutil.CustomError{ErrorMessage: "redis get error"})
6465

6566
req, _ := http.NewRequestWithContext(context.Background(), http.MethodGet, "http://localhost:5000/handle", bytes.NewBuffer([]byte(`{"key":"value"}`)))
66-
67+
req.Header.Set("content-type", "application/json")
6768
gofrReq := gofrHTTP.NewRequest(req)
6869

6970
ctx := &gofr.Context{Context: context.Background(),
@@ -87,6 +88,7 @@ func TestRedisPipelineHandler(t *testing.T) {
8788
mock.ClearExpect()
8889

8990
req, _ := http.NewRequestWithContext(context.Background(), http.MethodGet, "http://localhost:5000/handle", bytes.NewBuffer([]byte(`{"key":"value"}`)))
91+
req.Header.Set("content-type", "application/json")
9092

9193
gofrReq := gofrHTTP.NewRequest(req)
9294

examples/http-server/main_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ func TestIntegration_SimpleAPIServer(t *testing.T) {
3939

4040
for i, tc := range tests {
4141
req, _ := http.NewRequest("GET", host+tc.path, nil)
42+
req.Header.Set("content-type", "application/json")
43+
4244
c := http.Client{}
4345
resp, err := c.Do(req)
4446

examples/using-custom-metrics/main_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ func TestIntegration(t *testing.T) {
1717
c := http.Client{}
1818

1919
req, _ := http.NewRequest("POST", host+"/transaction", nil)
20+
req.Header.Set("content-type", "application/json")
2021

2122
_, err := c.Do(req)
2223
if err != nil {
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
APP_NAME=file-upload-api
2+
HTTP_PORT=8300
3+
4+
LOG_LEVEL=DEBUG

examples/using-file-bind/main.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"io"
6+
"mime/multipart"
7+
"os"
8+
9+
"gofr.dev/pkg/gofr"
10+
"gofr.dev/pkg/gofr/file"
11+
)
12+
13+
func main() {
14+
app := gofr.New()
15+
16+
app.POST("/upload", UploadHandler)
17+
18+
app.Run()
19+
}
20+
21+
// Data is the struct that we are trying to bind files to
22+
type Data struct {
23+
// The Compressed field is of type zip,
24+
// the tag `upload` signifies the key for the form where the file is uploaded
25+
// if the tag is not present, the field name would be taken as a key.
26+
Compressed file.Zip `file:"upload"`
27+
28+
// The FileHeader determines the generic file format that we can get
29+
// from the multipart form that gets parsed by the incoming http request
30+
FileHeader *multipart.FileHeader `file:"a"`
31+
}
32+
33+
func UploadHandler(c *gofr.Context) (interface{}, error) {
34+
var d Data
35+
36+
// bind the multipart data into the variable d
37+
err := c.Bind(&d)
38+
if err != nil {
39+
return nil, err
40+
}
41+
42+
// create local copies of the zipped files in tmp folder
43+
err = d.Compressed.CreateLocalCopies("tmp")
44+
if err != nil {
45+
return nil, err
46+
}
47+
48+
defer os.RemoveAll("tmp")
49+
50+
f, err := d.FileHeader.Open()
51+
if err != nil {
52+
return nil, err
53+
}
54+
55+
defer f.Close()
56+
57+
// read the file content
58+
content, err := io.ReadAll(f)
59+
if err != nil {
60+
return false, err
61+
}
62+
63+
// return the number of compressed files recieved
64+
return fmt.Sprintf("zipped files: %d, len of file `a`: %d", len(d.Compressed.Files), len(content)), nil
65+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"github.com/stretchr/testify/assert"
6+
"io"
7+
"mime/multipart"
8+
"net/http"
9+
"os"
10+
"testing"
11+
"time"
12+
)
13+
14+
func TestMain_BindError(t *testing.T) {
15+
const host = "http://localhost:8300"
16+
go main()
17+
time.Sleep(time.Second * 1)
18+
19+
c := http.Client{}
20+
21+
req, _ := http.NewRequest(http.MethodPost, host+"/upload", http.NoBody)
22+
req.Header.Set("content-type", "multipart/form-data")
23+
resp, err := c.Do(req)
24+
25+
assert.Equal(t, 500, resp.StatusCode)
26+
assert.NoError(t, err)
27+
28+
buf, contentType := generateMultiPartBody(t)
29+
req, _ = http.NewRequest(http.MethodPost, host+"/upload", buf)
30+
req.Header.Set("content-type", contentType)
31+
32+
resp, err = c.Do(req)
33+
assert.Equal(t, 200, resp.StatusCode)
34+
}
35+
36+
func generateMultiPartBody(t *testing.T) (*bytes.Buffer, string) {
37+
var buf bytes.Buffer
38+
writer := multipart.NewWriter(&buf)
39+
40+
f, err := os.Open("../../pkg/gofr/testutil/test.zip")
41+
if err != nil {
42+
t.Fatalf("Failed to open test.zip: %v", err)
43+
}
44+
defer f.Close()
45+
46+
zipPart, err := writer.CreateFormFile("upload", "test.zip")
47+
if err != nil {
48+
t.Fatalf("Failed to create form file: %v", err)
49+
}
50+
51+
_, err = io.Copy(zipPart, f)
52+
if err != nil {
53+
t.Fatalf("Failed to write file to form: %v", err)
54+
}
55+
56+
fileHeader, err := writer.CreateFormFile("a", "hello.txt")
57+
if err != nil {
58+
t.Fatalf("Failed to create form file: %v", err)
59+
}
60+
61+
_, err = io.Copy(fileHeader, bytes.NewReader([]byte(`Test hello!`)))
62+
if err != nil {
63+
t.Fatalf("Failed to write file to form: %v", err)
64+
}
65+
66+
// Close the multipart writer
67+
writer.Close()
68+
69+
return &buf, writer.FormDataContentType()
70+
}

examples/using-migrations/main_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ func TestExampleMigration(t *testing.T) {
3434

3535
for i, tc := range tests {
3636
req, _ := http.NewRequest(tc.method, host+tc.path, bytes.NewBuffer(tc.body))
37+
req.Header.Set("content-type", "application/json")
3738
c := http.Client{}
3839
resp, err := c.Do(req)
3940

examples/using-publisher/main_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ func TestExamplePublisher(t *testing.T) {
5151

5252
for i, tc := range testCases {
5353
req, _ := http.NewRequest(http.MethodPost, host+tc.path, bytes.NewBuffer(tc.body))
54+
req.Header.Set("content-type", "application/json")
5455
resp, err := c.Do(req)
5556

5657
assert.Equal(t, tc.expectedStatusCode, resp.StatusCode, "TEST[%d], Failed.\n%s", i, tc.desc)
@@ -94,6 +95,7 @@ func TestExamplePublisherError(t *testing.T) {
9495

9596
for i, tc := range testCases {
9697
req, _ := http.NewRequest(http.MethodPost, host+tc.path, bytes.NewBuffer(tc.body))
98+
req.Header.Set("content-type", "application/json")
9799
resp, err := c.Do(req)
98100

99101
assert.Equal(t, tc.expectedStatusCode, resp.StatusCode, "TEST[%d], Failed.\n%s", i, tc.desc)

pkg/gofr/context_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import (
1616
func Test_newContextSuccess(t *testing.T) {
1717
httpRequest, err := http.NewRequestWithContext(context.Background(),
1818
http.MethodPost, "/test", bytes.NewBuffer([]byte(`{"key":"value"}`)))
19+
httpRequest.Header.Set("content-type", "application/json")
20+
1921
if err != nil {
2022
t.Fatalf("unable to create request with context %v", err)
2123
}

pkg/gofr/file/file.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package file
2+
3+
type file struct {
4+
name string
5+
content []byte
6+
size int64
7+
isDir bool
8+
}
9+
10+
func (f file) GetName() string {
11+
return f.name
12+
}
13+
14+
func (f file) GetSize() int64 {
15+
return f.size
16+
}
17+
18+
func (f file) Bytes() []byte {
19+
return f.content
20+
}
21+
22+
func (f file) IsDir() bool {
23+
return f.isDir
24+
}

0 commit comments

Comments
 (0)