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

fix: Add MustPutInputFile function #144

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Open
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
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ main
/test.*
/config.yml


/testing_requests
/EduOJBackend
/backend
2 changes: 2 additions & 0 deletions app/controller/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"hash/fnv"
"html/template"
"io"
"io/ioutil"

Check failure on line 12 in app/controller/controller_test.go

View workflow job for this annotation

GitHub Actions / test (1.19.x, ubuntu-latest)

SA1019: "io/ioutil" has been deprecated since Go 1.19: As of Go 1.16, the same functionality is now provided by package io or package os, and those implementations should be preferred in new code. See the specific function documentation for details. (staticcheck)
"mime/multipart"
"net/http"
"net/http/httptest"
Expand Down Expand Up @@ -144,6 +144,8 @@
httpResp := makeResp(req)
resp := response.Response{}
mustJsonDecode(httpResp, &resp)
t.Logf("Expected Status Code: %d", test.statusCode)
t.Logf("Actual Status Code: %d", httpResp.StatusCode)
assert.Equal(t, test.statusCode, httpResp.StatusCode)
assert.Equal(t, test.resp, resp)
})
Expand Down
8 changes: 3 additions & 5 deletions app/controller/problem.go
Original file line number Diff line number Diff line change
Expand Up @@ -397,12 +397,11 @@ func CreateTestCase(c echo.Context) error {
InputFileName: inputFile.Filename,
OutputFileName: outputFile.Filename,
}

if err := base.DB.Model(&problem).Association("TestCases").Append(&testCase); err != nil {
panic(errors.Wrap(err, "could not create test case"))
}

utils.MustPutObject(inputFile, c.Request().Context(), "problems", fmt.Sprintf("%d/input/%d.in", problem.ID, testCase.ID))
// upload to minio
utils.MustPutInputFile(*req.Sanitize, inputFile, c.Request().Context(), "problems", fmt.Sprintf("%d/input/%d.in", problem.ID, testCase.ID))
utils.MustPutObject(outputFile, c.Request().Context(), "problems", fmt.Sprintf("%d/output/%d.out", problem.ID, testCase.ID))

return c.JSON(http.StatusCreated, response.CreateTestCaseResponse{
Expand Down Expand Up @@ -478,7 +477,6 @@ func UpdateTestCase(c echo.Context) error {
if err, ok := utils.BindAndValidate(&req, c); !ok {
return err
}

inputFile, err := c.FormFile("input_file")
if err != nil && err != http.ErrMissingFile && err.Error() != "request Content-Type isn't multipart/form-data" {
panic(errors.Wrap(err, "could not read input file"))
Expand All @@ -489,7 +487,7 @@ func UpdateTestCase(c echo.Context) error {
}

if inputFile != nil {
utils.MustPutObject(inputFile, c.Request().Context(), "problems", fmt.Sprintf("%d/input/%d.in", problem.ID, testCase.ID))
utils.MustPutInputFile(*req.Sanitize, inputFile, c.Request().Context(), "problems", fmt.Sprintf("%d/input/%d.in", problem.ID, testCase.ID))
testCase.InputFileName = inputFile.Filename
}
if outputFile != nil {
Expand Down
57 changes: 36 additions & 21 deletions app/controller/problem_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1036,6 +1036,7 @@ func TestCreateProblem(t *testing.T) {
assert.Equal(t, test.req.TimeLimit, databaseProblem.TimeLimit)
assert.Equal(t, strings.Split(test.req.LanguageAllowed, ","), []string(databaseProblem.LanguageAllowed))
assert.Equal(t, test.req.CompareScriptName, databaseProblem.CompareScriptName)
//assert.Equal(t, *test.req.Sanitize, databaseProblem.Sanitize)
assert.Equal(t, *test.req.Public, databaseProblem.Public)
assert.Equal(t, *test.req.Privacy, databaseProblem.Privacy)
// response == database
Expand All @@ -1055,6 +1056,8 @@ func TestCreateProblem(t *testing.T) {
storageContent := getObjectContent(t, "problems", fmt.Sprintf("%d/attachment", databaseProblem.ID))
expectedContent, err := ioutil.ReadAll(test.attachment.reader)
assert.NoError(t, err)
t.Logf("Expected Content: %+v", expectedContent)
t.Logf("Storage Content: %+v", storageContent)
assert.Equal(t, expectedContent, storageContent)
assert.Equal(t, test.attachment.fileName, databaseProblem.AttachmentFileName)
} else {
Expand Down Expand Up @@ -1734,8 +1737,9 @@ func TestCreateTestCase(t *testing.T) {
newFileContent("input_file", "test_create_test_case_non_existing_problem.in", inputTextBase64),
newFileContent("output_file", "test_create_test_case_non_existing_problem.out", outputTextBase64),
}, map[string]string{
"score": "100",
"sample": "true",
"score": "100",
"sample": "true",
"sanitize": "false",
}),
reqOptions: []reqOption{
applyAdminUser,
Expand All @@ -1750,8 +1754,9 @@ func TestCreateTestCase(t *testing.T) {
req: addFieldContentSlice([]reqContent{
newFileContent("output_file", "test_create_test_case_lack_input_file.out", outputTextBase64),
}, map[string]string{
"score": "100",
"sample": "true",
"score": "100",
"sample": "true",
"sanitize": "false",
}),
reqOptions: []reqOption{
headerOption{
Expand All @@ -1768,8 +1773,9 @@ func TestCreateTestCase(t *testing.T) {
req: addFieldContentSlice([]reqContent{
newFileContent("input_file", "test_create_test_case_lack_output_file.in", inputTextBase64),
}, map[string]string{
"score": "100",
"sample": "true",
"score": "100",
"sample": "true",
"sanitize": "false",
}),
reqOptions: []reqOption{
headerOption{
Expand All @@ -1784,8 +1790,9 @@ func TestCreateTestCase(t *testing.T) {
method: "POST",
path: base.Echo.Reverse("problem.createTestCase", problem.ID),
req: addFieldContentSlice([]reqContent{}, map[string]string{
"score": "100",
"sample": "true",
"score": "100",
"sample": "true",
"sanitize": "false",
}),
reqOptions: []reqOption{
headerOption{
Expand All @@ -1803,8 +1810,9 @@ func TestCreateTestCase(t *testing.T) {
newFileContent("input_file", "test_create_test_case_permission_denied.in", inputTextBase64),
newFileContent("output_file", "test_create_test_case_permission_denied.out", outputTextBase64),
}, map[string]string{
"score": "100",
"sample": "true",
"score": "100",
"sample": "true",
"sanitize": "false",
}),
reqOptions: []reqOption{
applyNormalUser,
Expand All @@ -1822,8 +1830,9 @@ func TestCreateTestCase(t *testing.T) {
newFileContent("input_file", "test_create_test_case_success.in", inputTextBase64),
newFileContent("output_file", "test_create_test_case_success.out", outputTextBase64),
}, map[string]string{
"score": "100",
"sample": "true",
"score": "100",
"sample": "true",
"sanitize": "false",
}), headerOption{
"Set-User-For-Test": {fmt.Sprintf("%d", user.ID)},
})
Expand Down Expand Up @@ -2066,14 +2075,16 @@ func TestUpdateTestCase(t *testing.T) {
problem := createProblemForTest(t, "update_test_case", 0, nil, user)
boolTrue := true

boolFalse := false
failTests := []failTest{
{
name: "NonExistingProblem",
method: "PUT",
path: base.Echo.Reverse("problem.updateTestCase", -1, 1),
req: request.UpdateTestCaseRequest{
Score: 100,
Sample: &boolTrue,
Score: 100,
Sample: &boolTrue,
Sanitize: &boolFalse,
},
reqOptions: []reqOption{
applyAdminUser,
Expand All @@ -2086,8 +2097,9 @@ func TestUpdateTestCase(t *testing.T) {
method: "PUT",
path: base.Echo.Reverse("problem.updateTestCase", problem.ID, -1),
req: request.UpdateTestCaseRequest{
Score: 100,
Sample: &boolTrue,
Score: 100,
Sample: &boolTrue,
Sanitize: &boolFalse,
},
reqOptions: []reqOption{
headerOption{
Expand All @@ -2106,8 +2118,9 @@ func TestUpdateTestCase(t *testing.T) {
method: "PUT",
path: base.Echo.Reverse("problem.updateTestCase", problem.ID, 1),
req: request.UpdateTestCaseRequest{
Score: 100,
Sample: &boolTrue,
Score: 100,
Sample: &boolTrue,
Sanitize: &boolFalse,
},
reqOptions: []reqOption{
applyNormalUser,
Expand All @@ -2128,7 +2141,7 @@ func TestUpdateTestCase(t *testing.T) {
{
name: "SuccessWithoutUpdatingFile",
originalData: testCaseData{
Score: 0,
Score: 100,
Sample: false,
InputFile: newFileContent("input_file", "test_update_test_case_1.in", inputTextBase64),
OutputFile: newFileContent("output_file", "test_update_test_case_1.out", outputTextBase64),
Expand Down Expand Up @@ -2229,10 +2242,12 @@ func TestUpdateTestCase(t *testing.T) {
if test.updatedData.OutputFile != nil {
reqContentSlice = append(reqContentSlice, test.updatedData.OutputFile)
}
sanitizeValue := false
req := makeReq(t, "PUT", base.Echo.Reverse("problem.updateTestCase", problem.ID, testCase.ID), addFieldContentSlice(
reqContentSlice, map[string]string{
"score": fmt.Sprintf("%d", test.updatedData.Score),
"sample": fmt.Sprintf("%t", test.updatedData.Sample),
"score": fmt.Sprintf("%d", test.updatedData.Score),
"sample": fmt.Sprintf("%t", test.updatedData.Sample),
"sanitize": fmt.Sprintf("%t", sanitizeValue),
}), headerOption{
"Set-User-For-Test": {fmt.Sprintf("%d", user.ID)},
})
Expand Down
12 changes: 8 additions & 4 deletions app/request/problem.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ type CreateProblemRequest struct {
Public *bool `json:"public" form:"public" query:"public" validate:"required"`
Privacy *bool `json:"privacy" form:"privacy" query:"privacy" validate:"required"`

//Sanitize *bool `json:"sanitize" form:"sanitize" query:"sanitize" validate:"required"`

MemoryLimit uint64 `json:"memory_limit" form:"memory_limit" query:"memory_limit" validate:"required"` // Byte
TimeLimit uint `json:"time_limit" form:"time_limit" query:"time_limit" validate:"required"` // ms
LanguageAllowed string `json:"language_allowed" form:"language_allowed" query:"language_allowed" validate:"required,max=255"` // E.g. cpp,c,java,python
Expand Down Expand Up @@ -36,8 +38,9 @@ type DeleteProblemRequest struct {
}

type CreateTestCaseRequest struct {
Score uint `json:"score" form:"score" query:"score"` // 0 for 平均分配
Sample *bool `json:"sample" form:"sample" query:"sample" validate:"required"`
Score uint `json:"score" form:"score" query:"score"`
Sample *bool `json:"sample" form:"sample" query:"sample" validate:"required"`
Sanitize *bool `json:"sanitize" form:"sanitize" query:"sanitize" validate:"required"`
// input_file(required)
// output_file(required)
}
Expand All @@ -49,8 +52,9 @@ type GetTestCaseOutputFileRequest struct {
}

type UpdateTestCaseRequest struct {
Score uint `json:"score" form:"score" query:"score"` // 0 for 平均分配
Sample *bool `json:"sample" form:"sample" query:"sample" validate:"required"`
Score uint `json:"score" form:"score" query:"score"`
Sample *bool `json:"sample" form:"sample" query:"sample" validate:"required"`
Sanitize *bool `json:"sanitize" form:"sanitize" query:"sanitize" validate:"required"`
// input_file(optional)
// output_file(optional)
}
Expand Down
59 changes: 59 additions & 0 deletions base/utils/helpers.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package utils

import (
"bufio"
"context"
"fmt"
"mime/multipart"
"net/http"
"os"
"strings"

"github.com/EduOJ/backend/app/response"
"github.com/EduOJ/backend/base"
Expand Down Expand Up @@ -58,6 +62,61 @@ func MustPutObject(object *multipart.FileHeader, ctx context.Context, bucket str
}
}

func MustPutInputFile(sanitize bool, object *multipart.FileHeader, ctx context.Context, bucket string, path string) {
src, err := object.Open()
if err != nil {
panic(err)
}
defer src.Close()

var fileSize int64

if sanitize {
scanner := bufio.NewScanner(src)
tempFile, err := os.CreateTemp("", "tempFile*.txt")
if err != nil {
panic(err)
}
writer := bufio.NewWriter(tempFile)
for scanner.Scan() {
line := strings.ReplaceAll(scanner.Text(), "\r\n", "\n") // replace '\r\n' to '\n'

if !strings.HasSuffix(line, "\n") {
line += "\n"
}
_, err := fmt.Fprint(writer, line)
if err != nil {
panic(err)
}
}
if err := scanner.Err(); err != nil {
panic(err)
}
if err := writer.Flush(); err != nil {
panic(err)
}

fileInfo, err := tempFile.Stat()
if err != nil {
panic(err)
}
fileSize = fileInfo.Size()

src, err = os.Open(tempFile.Name())
if err != nil {
panic(err)
}
defer src.Close()
} else {
fileSize = object.Size
}

_, err = base.Storage.PutObject(ctx, bucket, path, src, fileSize, minio.PutObjectOptions{})
if err != nil {
panic(errors.Wrap(err, "could write file to s3 storage."))
}
}

func MustGetObject(c echo.Context, bucket string, path string) *minio.Object {
object, err := base.Storage.GetObject(c.Request().Context(), bucket, path, minio.GetObjectOptions{})
if err != nil {
Expand Down
1 change: 1 addition & 0 deletions base/validator/translations/zh/zh.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ var FieldTranslations = map[string]string{
"Tried": "选取尝试过题目",
"Passed": "选取通过题目",
"Token": "验证码",
"Sanitize": "是否清洗数据",
}

// RegisterDefaultTranslations registers a set of default translations
Expand Down
Loading