Skip to content

Commit 83fac79

Browse files
Artem TitovArtem Titov
authored andcommitted
Merge branch 'main' into dev/storage
2 parents cafaf47 + d9f719b commit 83fac79

File tree

12 files changed

+317
-143
lines changed

12 files changed

+317
-143
lines changed

common/config/storage.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,16 @@ package config
22

33
type StorageConfig struct {
44
StoragePath string `yaml:"StoragePath"`
5+
6+
BlockSize uint `yaml:"BlockSize"`
57
}
68

79
func FillInStorageConfig(config *StorageConfig) {
810
if len(config.StoragePath) == 0 {
911
panic("No storage path specified")
1012
}
13+
14+
if config.BlockSize == 0 {
15+
config.BlockSize = 3
16+
}
1117
}

common/connectors/storageconn/connector.go

Lines changed: 120 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ package storageconn
22

33
import (
44
"fmt"
5+
"mime"
56
"os"
67
"path/filepath"
8+
"strconv"
79
"testing_system/common/config"
810
"testing_system/common/connectors"
911
"testing_system/common/constants/resource"
10-
11-
"github.com/go-resty/resty/v2"
1212
)
1313

1414
type Connector struct {
@@ -22,115 +22,171 @@ func NewConnector(connection *config.Connection) *Connector {
2222
return &Connector{connectors.NewConnectorBase(connection)}
2323
}
2424

25-
func (s *Connector) Download(request *Request) *ResponseFiles {
25+
func (s *Connector) Download(request *Request) *FileResponse {
26+
response := NewFileResponse(*request)
27+
2628
if err := os.MkdirAll(request.BaseFolder, 0775); err != nil {
27-
return &ResponseFiles{Response: Response{R: *request, Error: fmt.Errorf("failed to create base folder: %v", err)}}
29+
response.Error = fmt.Errorf("failed to create base folder: %v", err)
30+
return response
2831
}
2932

30-
path := fmt.Sprintf("/storage/get?id=%d&dataType=%s&filepath=%s",
31-
getIDForResource(request),
32-
getDataTypeForResource(request.Resource),
33-
request.Resource.String(),
34-
)
35-
33+
path := "/storage/get"
3634
r := s.connection.R()
37-
resp, err := r.Execute(resty.MethodGet, path)
35+
36+
params, err := getStorageParams(request)
37+
38+
if err != nil {
39+
response.Error = fmt.Errorf("failed to form storage request: %v", err)
40+
return response
41+
}
42+
43+
r.SetQueryParams(map[string]string{
44+
"id": params.id,
45+
"dataType": params.dataType,
46+
"filepath": params.filepath,
47+
})
48+
49+
resp, err := r.Get(path)
3850
if err != nil {
39-
return &ResponseFiles{Response: Response{R: *request, Error: fmt.Errorf("failed to send request: %v", err)}}
51+
response.Error = fmt.Errorf("failed to send request: %v", err)
52+
return response
4053
}
4154

4255
if resp.IsError() {
43-
return &ResponseFiles{Response: Response{R: *request, Error: fmt.Errorf("request failed with status: %v", resp.Status())}}
56+
response.Error = fmt.Errorf("get request failed with status: %v", resp.Status())
57+
return response
4458
}
4559

46-
filename := request.Resource.String()
60+
filename := ""
4761
if request.CustomFilename != "" {
4862
filename = request.CustomFilename
63+
} else {
64+
// Extract filename from Content-Disposition header
65+
contentDisposition := resp.Header().Get("Content-Disposition")
66+
if contentDisposition != "" {
67+
_, params, err := mime.ParseMediaType(contentDisposition)
68+
if err == nil && params["filename"] != "" {
69+
filename = params["filename"]
70+
}
71+
}
72+
}
73+
74+
if filename == "" {
75+
response.Error = fmt.Errorf("can't extract filename from CustomFilename or Content-Disposition header")
76+
return response
4977
}
5078

51-
filepath := filepath.Join(request.BaseFolder, filename)
52-
err = os.WriteFile(filepath, resp.Body(), 0644)
79+
filePath := filepath.Join(request.BaseFolder, filename)
80+
err = os.WriteFile(filePath, resp.Body(), 0644)
5381
if err != nil {
54-
return &ResponseFiles{Response: Response{R: *request, Error: fmt.Errorf("failed to write file: %v", err)}}
82+
response.Error = fmt.Errorf("failed to write file: %v", err)
83+
return response
5584
}
5685

57-
responseFiles := NewResponseFiles(*request)
58-
responseFiles.fileNames = []string{filename}
59-
responseFiles.Size = uint64(len(resp.Body()))
60-
return responseFiles
86+
response.Filename = filename
87+
response.BaseFolder = request.BaseFolder
88+
response.Size = uint64(len(resp.Body()))
89+
return response
6190
}
6291

6392
func (s *Connector) Upload(request *Request) *Response {
64-
if len(request.Files) == 0 {
65-
return &Response{R: *request, Error: fmt.Errorf("no files to upload")}
66-
}
93+
response := &Response{R: *request}
6794

68-
path := fmt.Sprintf("/storage/upload?id=%d&dataType=%s&filepath=%s",
69-
getIDForResource(request),
70-
getDataTypeForResource(request.Resource),
71-
request.Resource.String(),
72-
)
95+
if request.File == nil {
96+
response.Error = fmt.Errorf("file for upload is not specified")
97+
return response
98+
}
7399

100+
path := "/storage/upload"
74101
r := s.connection.R()
75-
for filename, reader := range request.Files {
76-
r.SetFileReader("file", filename, reader)
102+
103+
params, err := getStorageParams(request)
104+
105+
if err != nil {
106+
response.Error = fmt.Errorf("failed to form storage request: %v", err)
107+
return response
77108
}
78109

79-
resp, err := r.Execute(resty.MethodPost, path)
110+
r.SetFormData(map[string]string{
111+
"id": params.id,
112+
"dataType": params.dataType,
113+
"filepath": params.filepath,
114+
})
115+
116+
r.SetFileReader("file", request.Filename, request.File)
117+
118+
resp, err := r.Post(path)
80119
if err != nil {
81-
return &Response{R: *request, Error: fmt.Errorf("failed to send request: %v", err)}
120+
response.Error = fmt.Errorf("failed to send request: %v", err)
121+
return response
82122
}
83123

84124
if resp.IsError() {
85-
return &Response{R: *request, Error: fmt.Errorf("request failed with status: %s, body: %s", resp.Status(), resp.String())}
125+
response.Error = fmt.Errorf("upload failed with status: %v", resp.Status())
126+
return response
86127
}
87128

88-
return &Response{R: *request}
129+
return response
89130
}
90131

91132
func (s *Connector) Delete(request *Request) *Response {
92-
path := fmt.Sprintf("/storage/remove?id=%d&dataType=%s&filepath=%s",
93-
getIDForResource(request),
94-
getDataTypeForResource(request.Resource),
95-
request.Resource.String(),
96-
)
133+
response := &Response{R: *request}
97134

135+
path := "/storage/remove"
98136
r := s.connection.R()
99-
resp, err := r.Execute(resty.MethodDelete, path)
137+
138+
params, err := getStorageParams(request)
139+
100140
if err != nil {
101-
return &Response{R: *request, Error: fmt.Errorf("failed to send request: %v", err)}
141+
response.Error = fmt.Errorf("failed to form storage request: %v", err)
142+
return response
143+
}
144+
145+
r.SetFormData(map[string]string{
146+
"id": params.id,
147+
"dataType": params.dataType,
148+
"filepath": params.filepath,
149+
})
150+
151+
resp, err := r.Delete(path)
152+
if err != nil {
153+
response.Error = fmt.Errorf("failed to send request: %v", err)
154+
return response
102155
}
103156

104157
if resp.IsError() {
105-
return &Response{R: *request, Error: fmt.Errorf("request failed with status: %s, body: %s", resp.Status(), resp.String())}
158+
response.Error = fmt.Errorf("delete failed with status: %v", resp.Status())
159+
return response
106160
}
107161

108-
return &Response{R: *request}
162+
return response
109163
}
110164

111-
func getIDForResource(request *Request) uint64 {
112-
switch request.Resource {
113-
case resource.SourceCode, resource.CompiledBinary, resource.CompileOutput:
114-
return request.SubmitID
115-
case resource.Checker, resource.Interactor:
116-
return request.ProblemID
117-
case resource.Test:
118-
if request.TestID > 0 {
119-
return request.TestID
120-
}
121-
return request.ProblemID
122-
default:
123-
return 0
124-
}
165+
type storageParams struct {
166+
id string
167+
dataType string
168+
filepath string
125169
}
126170

127-
func getDataTypeForResource(resourceType resource.Type) string {
128-
switch resourceType {
171+
func getStorageParams(request *Request) (storageParams, error) {
172+
params := storageParams{}
173+
switch request.Resource {
174+
case resource.Checker, resource.Interactor:
175+
params.dataType = "problem"
176+
params.filepath = request.Resource.String()
177+
params.id = strconv.FormatUint(request.ProblemID, 10)
178+
return params, nil
129179
case resource.SourceCode, resource.CompiledBinary, resource.CompileOutput:
130-
return "submission"
131-
case resource.Checker, resource.Interactor, resource.Test:
132-
return "problem"
180+
params.dataType = "submission"
181+
params.filepath = request.Resource.String()
182+
params.id = strconv.FormatUint(request.SubmitID, 10)
183+
return params, nil
184+
case resource.Test:
185+
params.dataType = "problem"
186+
params.filepath = "tests/" + strconv.FormatUint(request.TestID, 10)
187+
params.id = strconv.FormatUint(request.ProblemID, 10)
188+
return params, nil
133189
default:
134-
return "unknown"
190+
return params, fmt.Errorf("unknown resource type: %s", request.Resource.String())
135191
}
136192
}

common/connectors/storageconn/structs.go

Lines changed: 24 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -25,54 +25,52 @@ type Request struct {
2525
// Specify a custom filename for the downloaded file
2626
CustomFilename string `json:"-"`
2727

28-
// For uploads, Files should be specified. Filename is key in map, file data should be read from value
29-
Files map[string]io.Reader `json:"-"`
28+
// For uploads, File should be specified
29+
File io.Reader `json:"-"`
30+
31+
// For uploads, Filename should be specified
32+
Filename string `json:"-"`
3033
}
3134

3235
type Response struct {
3336
R Request
3437
Error error
3538
}
3639

37-
type ResponseFiles struct {
40+
type FileResponse struct {
3841
Response
39-
fileNames []string
40-
Size uint64
42+
Filename string `json:"filename"`
43+
BaseFolder string `json:"basefolder"`
44+
Size uint64 `json:"size"`
4145
}
4246

43-
func NewResponseFiles(request Request) *ResponseFiles {
44-
return &ResponseFiles{
45-
Response: Response{R: request},
46-
fileNames: []string{},
47-
Size: 0,
47+
func NewFileResponse(request Request) *FileResponse {
48+
return &FileResponse{
49+
Response: Response{R: request, Error: nil},
50+
Filename: "",
51+
BaseFolder: "",
52+
Size: 0,
4853
}
4954
}
5055

51-
func (r *ResponseFiles) File() (string, bool) {
52-
if len(r.fileNames) == 0 {
56+
func (r *FileResponse) GetFilePath() (string, bool) {
57+
if r.BaseFolder == "" || r.Filename == "" {
5358
return "", false
5459
}
55-
return filepath.Join(r.R.BaseFolder, r.fileNames[0]), true
60+
return filepath.Join(r.BaseFolder, r.Filename), true
5661
}
5762

58-
func (r *ResponseFiles) Get(fileName string) (string, bool) {
59-
for _, f := range r.fileNames {
60-
if fileName == f {
61-
return filepath.Join(r.R.BaseFolder, f), true
62-
}
63-
}
64-
return "", false
65-
}
66-
67-
func (r *ResponseFiles) CleanUp() {
63+
func (r *FileResponse) CleanUp() {
6864
if r.Error != nil {
6965
return
7066
}
71-
if len(r.R.BaseFolder) == 0 {
67+
if r.BaseFolder == "" {
7268
return
7369
}
74-
err := os.RemoveAll(r.R.BaseFolder)
70+
71+
// CleanUp should be called after using all needed files
72+
err := os.RemoveAll(r.BaseFolder)
7573
if err != nil {
76-
logger.Panic("Can not remove resource folder, error: %s", err.Error())
74+
logger.Error("Cannot remove resource folder %s: %s", r.BaseFolder, err.Error())
7775
}
7876
}

invoker/handler.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@ package invoker
22

33
import (
44
"errors"
5-
"github.com/gin-gonic/gin"
6-
"gorm.io/gorm"
75
"net/http"
86
"testing_system/common/connectors/invokerconn"
97
"testing_system/common/db/models"
108
"testing_system/lib/connector"
119
"testing_system/lib/logger"
10+
11+
"github.com/gin-gonic/gin"
12+
"gorm.io/gorm"
1213
)
1314

1415
func (i *Invoker) HandleStatus(c *gin.Context) {

lib/connector/handler.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ package connector
22

33
import (
44
"fmt"
5-
"github.com/gin-gonic/gin"
65
"net/http"
6+
7+
"github.com/gin-gonic/gin"
78
)
89

910
type ErrResponse struct {

0 commit comments

Comments
 (0)