Skip to content

Commit 4f5cee6

Browse files
committed
feat: add initial fileupload api client
1 parent 1714cd4 commit 4f5cee6

31 files changed

+3324
-0
lines changed

pkg/apiclients/fileupload/batch.go

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
package fileupload
2+
3+
import (
4+
"fmt"
5+
"iter"
6+
"os"
7+
"path/filepath"
8+
9+
"github.com/snyk/go-application-framework/pkg/apiclients/fileupload/uploadrevision"
10+
)
11+
12+
// uploadBatch manages a batch of files for upload.
13+
type uploadBatch struct {
14+
files []uploadrevision.UploadFile
15+
currentSize int64
16+
limits uploadrevision.Limits
17+
}
18+
19+
func newUploadBatch(limits uploadrevision.Limits) *uploadBatch {
20+
return &uploadBatch{
21+
files: make([]uploadrevision.UploadFile, 0, limits.FileCountLimit),
22+
limits: limits,
23+
}
24+
}
25+
26+
func (b *uploadBatch) addFile(file uploadrevision.UploadFile, fileSize int64) {
27+
b.files = append(b.files, file)
28+
b.currentSize += fileSize
29+
}
30+
31+
func (b *uploadBatch) wouldExceedLimits(fileSize int64) bool {
32+
wouldExceedCount := len(b.files) >= b.limits.FileCountLimit
33+
wouldExceedSize := b.currentSize+fileSize > b.limits.TotalPayloadSizeLimit
34+
return wouldExceedCount || wouldExceedSize
35+
}
36+
37+
func (b *uploadBatch) isEmpty() bool {
38+
return len(b.files) == 0
39+
}
40+
41+
func (b *uploadBatch) closeRemainingFiles() {
42+
for _, file := range b.files {
43+
file.File.Close()
44+
}
45+
}
46+
47+
type batchingResult struct {
48+
batch *uploadBatch
49+
filteredFiles []FilteredFile
50+
}
51+
52+
func batchPaths(rootPath string, paths <-chan string, limits uploadrevision.Limits, filters ...filter) iter.Seq2[*batchingResult, error] {
53+
return func(yield func(*batchingResult, error) bool) {
54+
batch := newUploadBatch(limits)
55+
filtered := []FilteredFile{}
56+
for path := range paths {
57+
relPath, err := filepath.Rel(rootPath, path)
58+
if err != nil {
59+
if !yield(nil, fmt.Errorf("failed to get relative path of file %s: %w", path, err)) {
60+
return
61+
}
62+
}
63+
64+
f, err := os.Open(path)
65+
if err != nil {
66+
f.Close()
67+
if !yield(nil, fmt.Errorf("failed to open file %s: %w", path, err)) {
68+
return
69+
}
70+
}
71+
72+
fstat, err := f.Stat()
73+
if err != nil {
74+
f.Close()
75+
if !yield(nil, fmt.Errorf("failed to stat file %s: %w", path, err)) {
76+
return
77+
}
78+
}
79+
80+
ff := applyFilters(fileToFilter{Path: relPath, Stat: fstat}, filters...)
81+
if ff != nil {
82+
f.Close()
83+
filtered = append(filtered, *ff)
84+
continue
85+
}
86+
87+
if batch.wouldExceedLimits(fstat.Size()) {
88+
if !yield(&batchingResult{batch: batch, filteredFiles: filtered}, nil) {
89+
return
90+
}
91+
batch = newUploadBatch(limits)
92+
filtered = []FilteredFile{}
93+
}
94+
95+
batch.addFile(uploadrevision.UploadFile{
96+
Path: relPath,
97+
File: f,
98+
}, fstat.Size())
99+
}
100+
101+
if !batch.isEmpty() || len(filtered) > 0 {
102+
yield(&batchingResult{batch: batch, filteredFiles: filtered}, nil)
103+
}
104+
}
105+
}
106+
107+
func applyFilters(ff fileToFilter, filters ...filter) *FilteredFile {
108+
for _, filter := range filters {
109+
if ff := filter(ff); ff != nil {
110+
return ff
111+
}
112+
}
113+
114+
return nil
115+
}

0 commit comments

Comments
 (0)