diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index e7f743b..f092f9b 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -11,11 +11,10 @@ jobs: fail-fast: false matrix: go-version: - - 1.20.x - - 1.21.x - - 1.22.x - - 1.23.x - 1.24.x + - 1.25.x + - 1.26.x + - 1.27.x steps: - name: ๐Ÿ”„ Checkout Repository @@ -36,7 +35,7 @@ jobs: echo "cloud_name: $(echo $CLOUDINARY_URL | cut -d'@' -f2)" - name: ๐Ÿงฐ Install gotestsum - run: go install gotest.tools/gotestsum@v1.11.0 + run: go install gotest.tools/gotestsum@v1.13.0 - name: ๐Ÿงช Run Tests run: | diff --git a/README.md b/README.md index f3904dd..64cca86 100644 --- a/README.md +++ b/README.md @@ -32,11 +32,11 @@ For the complete documentation, see the [Go SDK Guide](https://cloudinary.com/do ## Version Support -| **SDK Version** | **Go 1.13 - 1.19** | **Go 1.20** | **Go 1.21** | **Go 1.22** | **Go 1.23** | -|-----------------|--------------------|-------------|-------------|-------------|-------------| -| **2.8 & Up** | โŒ | โœ”๏ธ | โœ”๏ธ | โœ”๏ธ | โœ”๏ธ | -| **2.7** | โœ”๏ธ | โœ”๏ธ | โœ”๏ธ | โœ”๏ธ | โœ”๏ธ | -| **1.x** | โœ”๏ธ | โœ”๏ธ | โœ”๏ธ | โœ”๏ธ | โœ”๏ธ | +| **SDK Version** | **Go 1.13 - 1.19** | **Go 1.20 - 1.23** | **Go 1.24 - 1.27** | +|-----------------|--------------------|--------------------|--------------------| +| **2.8 & Up** | โŒ | โœ”๏ธ | โœ”๏ธ | +| **2.7** | โœ”๏ธ | โœ”๏ธ | โœ”๏ธ | +| **1.x** | โœ”๏ธ | โœ”๏ธ | โœ”๏ธ | ## Installation diff --git a/api/admin/analysis.go b/api/admin/analysis.go index 416b567..84b66a1 100644 --- a/api/admin/analysis.go +++ b/api/admin/analysis.go @@ -5,6 +5,10 @@ import ( "github.com/cloudinary/cloudinary-go/v2/api" ) +type contextKey string + +const apiVersionKey contextKey = "api_version" + const ( analysis api.EndPoint = "analysis" analyze api.EndPoint = "analyze" @@ -46,7 +50,7 @@ Currently supports the following analysis options: * Custom */ func (a *API) Analyze(ctx context.Context, params AnalyzeParams) (*AnalyzeResult, error) { - v2APICtx := context.WithValue(ctx, "api_version", "2") + v2APICtx := context.WithValue(ctx, apiVersionKey, "2") res := &AnalyzeResult{} _, err := a.post(v2APICtx, api.BuildPath(analysis, analyze, uri), params, res) diff --git a/api/admin/api.go b/api/admin/api.go index 82c278d..9a7e08e 100644 --- a/api/admin/api.go +++ b/api/admin/api.go @@ -9,7 +9,6 @@ import ( "encoding/json" "fmt" "io" - "io/ioutil" "mime/multipart" "net/http" "os" @@ -168,7 +167,7 @@ func (a *API) postFile(ctx context.Context, path interface{}, file interface{}, func (a *API) executeRequest(ctx context.Context, method string, path interface{}, body io.Reader, queryParams string, headers map[string]string, result interface{}) (*http.Response, error) { apiVersion := "" - if apiVersionRaw := ctx.Value("api_version"); apiVersionRaw != nil { + if apiVersionRaw := ctx.Value(apiVersionKey); apiVersionRaw != nil { apiVersion = apiVersionRaw.(string) } @@ -205,7 +204,7 @@ func (a *API) executeRequest(ctx context.Context, method string, path interface{ defer api.DeferredClose(resp.Body) - bodyBytes, err := ioutil.ReadAll(resp.Body) + bodyBytes, err := io.ReadAll(resp.Body) if err != nil { return nil, err } diff --git a/api/admin/asset.go b/api/admin/asset.go index 615c5a7..6137399 100644 --- a/api/admin/asset.go +++ b/api/admin/asset.go @@ -221,7 +221,7 @@ type UpdateAssetParams struct { BackgroundRemoval string `json:"background_removal,omitempty"` QualityOverride int `json:"quality_override,omitempty"` NotificationURL string `json:"notification_url,omitempty"` - Tags api.CldAPIArray `json:"tags,omitempty,omitempty"` + Tags api.CldAPIArray `json:"tags,omitempty"` Context api.CldAPIMap `json:"context,omitempty"` AdminContext []api.AdminContext `json:"admin_context,omitempty"` FaceCoordinates api.Coordinates `json:"face_coordinates,omitempty"` diff --git a/api/api.go b/api/api.go index 0c7843e..e1dc2b0 100644 --- a/api/api.go +++ b/api/api.go @@ -63,7 +63,7 @@ func BaseURL(uploadPrefix string, apiVer string) string { } // base64DataRegex is the regular expression for detecting base64 encoded strings. -var base64DataRegex = regexp.MustCompile("^data:([\\w-]+/[\\w\\-+.]+)?(;[\\w-]+=[\\w-]+)*;base64,([a-zA-Z0-9/+\\n=]+)$") +var base64DataRegex = regexp.MustCompile(`^data:([\w-]+/[\w\-+.]+)?(;[\w-]+=[\w-]+)*;base64,([a-zA-Z0-9/+\n=]+)$`) // AssetType is the type of the asset. type AssetType string diff --git a/api/uploader/upload.go b/api/uploader/upload.go index 835bbc9..f4c084a 100644 --- a/api/uploader/upload.go +++ b/api/uploader/upload.go @@ -12,7 +12,6 @@ import ( "errors" "fmt" "io" - "io/ioutil" "log" "mime/multipart" "net/http" @@ -358,7 +357,7 @@ func (u *API) postBody(ctx context.Context, urlPath interface{}, bodyReader io.R defer api.DeferredClose(resp.Body) - return ioutil.ReadAll(resp.Body) + return io.ReadAll(resp.Body) } func setAuth(u *API, req *http.Request) { diff --git a/api/uploader/upload_acceptance_test.go b/api/uploader/upload_acceptance_test.go index 38a6de2..1305c65 100644 --- a/api/uploader/upload_acceptance_test.go +++ b/api/uploader/upload_acceptance_test.go @@ -190,6 +190,8 @@ func getAutoVideoDetailsTestCases() []UploadAPIAcceptanceTestCase { AutoVideoDetails: &api.AutoVideoDetails{ Fields: []string{"title", "description"}, }, + Unsigned: api.Bool(true), + Timestamp: 123456789, }) }, ResponseTest: func(response interface{}, t *testing.T) {}, @@ -292,6 +294,7 @@ func TestUploadAPI_Acceptance(t *testing.T) { testUploadAPIByTestCases(getAuthorizationTestCases(), t) testUploadAPIByTestCases(getFolderDecouplingTestCases(), t) testUploadAPIByTestCases(getAutoTranscriptionTestCases(), t) + testUploadAPIByTestCases(getAutoVideoDetailsTestCases(), t) testUploadAPIByTestCases(getBooleanValuesTestCases(), t) testUploadAPIByTestCases(getVariousValuesTestCases(), t) testUploadAPIByTestCases(getUploadConfigTestCases(), t) diff --git a/api/uploader/upload_asset.go b/api/uploader/upload_asset.go index 18e138a..5d57cbf 100644 --- a/api/uploader/upload_asset.go +++ b/api/uploader/upload_asset.go @@ -128,7 +128,7 @@ func (u *API) Upload(ctx context.Context, file interface{}, uploadParams UploadP } err = api.HandleRawResponse(body, result) - return result, nil + return result, err } // Eager contains information about eagerly transformed derived assets. diff --git a/api/uploader/upload_asset_test.go b/api/uploader/upload_asset_test.go index 772d32d..78174fb 100644 --- a/api/uploader/upload_asset_test.go +++ b/api/uploader/upload_asset_test.go @@ -4,11 +4,11 @@ import ( "context" "fmt" "io" - "io/ioutil" "log" "os" "runtime" "strings" + "sync/atomic" "testing" "time" @@ -310,7 +310,7 @@ func TestUploader_UploadWithResponsiveBreakpoints(t *testing.T) { } if eResp == nil { - t.Error(resp) + t.Fatal(resp) } assert.Len(t, eResp.ResponsiveBreakpoints, 2) @@ -364,7 +364,7 @@ func populateLargeImage() string { "\xFC\x00\x00\x00\x00\x00\x00\x00\x00fff\xFC\x00\x00\x00\x00\x00\x00\x00\x00\xC4\xF5(\xFF\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - tmpFile, err := ioutil.TempFile(cldtest.TestDataDir(), largeImagePublicID+".*.bmp") + tmpFile, err := os.CreateTemp(cldtest.TestDataDir(), largeImagePublicID+".*.bmp") if err != nil { log.Fatal(err) } @@ -390,24 +390,29 @@ func readMemoryAlloc() uint64 { } func watchMemoryAllocation() func() uint64 { - var maxAlloc uint64 - proceed := true + var maxAlloc atomic.Uint64 + var proceed atomic.Bool + proceed.Store(true) initialRead := readMemoryAlloc() go func() { for i := 0; i < 10000; i++ { - if !proceed { + if !proceed.Load() { return } - read := readMemoryAlloc() - if read-initialRead > maxAlloc { - maxAlloc = read - initialRead + // Alloc is the live heap, which can drop below the baseline once the + // GC collects. Only consider growth, otherwise the unsigned + // subtraction underflows. + if read := readMemoryAlloc(); read > initialRead { + if grown := read - initialRead; grown > maxAlloc.Load() { + maxAlloc.Store(grown) + } } time.Sleep(time.Millisecond * 500) } }() return func() uint64 { - proceed = false - return maxAlloc + proceed.Store(false) + return maxAlloc.Load() } } diff --git a/asset/auth_token_test.go b/asset/auth_token_test.go index 6edf6ed..7017b2b 100644 --- a/asset/auth_token_test.go +++ b/asset/auth_token_test.go @@ -8,14 +8,11 @@ import ( ) const authTokenKey = "00112233FF99" -const authTokenAltKey = "CCBB2233FF00" const duration = 300 const startTime = 11111111 const authTokenTestImage = "sample.jpg" -const authTokenTestConfigACL = "/*/t_foobar" -const authTokenTestPath = "http://res.cloudinary.com/test123/image/upload/v1486020273/sample.jpg" var authTokenConfig = config.AuthToken{ Duration: duration, diff --git a/cloudinary_test.go b/cloudinary_test.go index 86111e6..3887bd8 100644 --- a/cloudinary_test.go +++ b/cloudinary_test.go @@ -26,7 +26,7 @@ func TestCloudinary_CreateInstance(t *testing.T) { t.Error("Failed creating Cloudinary instance from Cloudinary URL.") } - c, err := NewFromURL("") + _, err := NewFromURL("") if err == nil || err.Error() != "must provide CLOUDINARY_URL" { t.Error("Error expected, got: ", err) } diff --git a/config/configuration_test.go b/config/configuration_test.go index 222aba1..6b78a13 100644 --- a/config/configuration_test.go +++ b/config/configuration_test.go @@ -27,7 +27,7 @@ func TestConfiguration_CreateInstance(t *testing.T) { assert.Equal(t, cldtest.CloudName, c.Cloud.CloudName) assert.Equal(t, signature.SHA256, c.Cloud.SignatureAlgorithm) - c, err = config.NewFromURL("") + _, err = config.NewFromURL("") if err == nil || err.Error() != "must provide CLOUDINARY_URL" { t.Error("Error expected, got: ", err) } diff --git a/gen/generate_setters/template.go b/gen/generate_setters/template.go index 0abfc8c..ba138fc 100644 --- a/gen/generate_setters/template.go +++ b/gen/generate_setters/template.go @@ -7,6 +7,7 @@ import ( "io" "strings" "text/template" + "unicode" ) // Generate template for setters file @@ -31,7 +32,7 @@ func ({{ .Receiver}} *{{ .StructName}}) <>(<