Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
57a9e8b
Add webhook event constants for file.stored, file.deleted, file.info_…
alxgsv Mar 11, 2026
fe55167
Add group.Delete() method for deleting group metadata
alxgsv Mar 11, 2026
97075ed
Add metadata package with file metadata CRUD operations
alxgsv Mar 11, 2026
015bc46
Add addon package for Addons API execution and status polling
alxgsv Mar 11, 2026
259b951
Return error for unhandled HTTP status codes in REST client
alxgsv Mar 11, 2026
0c6c9b4
Use assert in httptest handlers
alxgsv Mar 13, 2026
94eda94
Close resp.Body on every exit path in REST client
alxgsv Mar 13, 2026
e1f45e8
Add metadata key validation and dot-segment path escaping
alxgsv Mar 13, 2026
e42cb61
Implement phase 3 SDK enhancements
alxgsv Mar 14, 2026
d6b5cc1
Clarify retry wait semantics
alxgsv Mar 14, 2026
149184d
Fix upload API response decoding
alxgsv Mar 14, 2026
ae5c6f2
Assert multipart completion in upload test
alxgsv Mar 14, 2026
4ab8284
feat: close phase 4 sdk cli gaps
alxgsv Mar 14, 2026
87a4952
feat: automatic per-project CDN base URL
alxgsv Mar 16, 2026
5617b05
feat: add Project API support with bearer token authentication
alxgsv Mar 26, 2026
d0c6dc8
docs: mention how to obtain Project API tokens
alxgsv Mar 26, 2026
6590292
fix: use iterator-based pagination for Project API lists
alxgsv Mar 26, 2026
236f3b0
fix: handle cross-host pagination in bearer client
alxgsv Mar 26, 2026
2ba0666
fix: redact request headers from debug logs to prevent credential leaks
alxgsv Mar 27, 2026
d58ba67
Fix missing User-Agent header in Upload API client
alxgsv Apr 9, 2026
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
28 changes: 27 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,45 @@ BREAKING CHANGES:
* Remove `file.Copy()` method and `file.CopyParams` type — use `LocalCopy()` and `RemoteCopy()`
* Remove `file.OrderBySizeAsc` and `file.OrderBySizeDesc` constants (not supported in v0.7)
* Remove `APIv05` and `APIv06` constants
* Change `file.Service.Info()` signature to accept `*file.InfoParams` for optional `include` query parameters
* Change `webhook.Service.Delete()` to delete by webhook ID instead of target URL
* Minimum Go version is now 1.25
* Throttled requests no longer retry by default — automatic retries are now opt-in via `ucare.Config.Retry`

FEATURES:

* Add `projectapi` package for the Project API with bearer token authentication — manage projects, secret keys, and usage metrics
* Add `ucare.NewBearerClient()` for bearer token authentication used by the Project API
* Add `ucare.ProjectAPIError` type for Project API error responses
* Add `addon` package for Addons API execution and status polling
* Add typed addon params for Remove.bg and ClamAV requests
* Add `metadata` package with file metadata CRUD operations
* Add `group.Delete()` for deleting group metadata without deleting files
* Add webhook event constants for `file.stored`, `file.deleted`, `file.info_updated`, and deprecated `file.infected`
* Add `ucare.Config.Retry` and `RetryConfig` for configurable throttling retries
* Add `upload.Service.Upload()` for automatic direct-vs-multipart upload selection
* Add upload metadata support for direct, multipart, from-URL, and unified uploads
* Add `file.InfoParams.Include` and `file.ListParams.Include` for `include=appdata`
* Add `conversion.Params.SaveInGroup` for document conversions that should persist image output as a file group
* Add `conversion.BuildDocumentPath()` and `conversion.BuildVideoPath()` helpers for constructing conversion paths
* Export structured API error types: `APIError`, `AuthError`, `ThrottleError`, `ValidationError`, and `ForbiddenError`
* Automatic per-project CDN base URL derived from the public key (`ucare.Config.CDNBase`)

IMPROVEMENTS:

* Add `UserAgent` field to `ucare.Config` for custom agent identification
* Extend form/query encoding to support Upload API metadata fields in `metadata[key]=value` bracket notation
* Replace `http.NewRequest` + `WithContext` with `http.NewRequestWithContext`
* Throttle retry loops now respect context cancellation
* REST throttling retries now honor `Retry-After` with configurable fail-fast limits and fallback exponential backoff
* Upload throttling retries now use configurable exponential backoff capping
* Error values now expose HTTP status details for caller inspection
* Replace `ioutil` usage with `io` equivalents
* Replace `go-env` dependency with `os.Getenv`
* Update `stretchr/testify` to v1.10.0
* Update CI: Go 1.25, modern GitHub Actions versions, remove deprecated golint
* Integration tests skip gracefully when credentials are not set
* Fix errors in package documentation examples
* Fix errors in package documentation examples and update public examples for the new `file.Info()` signature

## 1.2.1 (September 1, 2020)

Expand Down
54 changes: 51 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,13 @@ import (
"github.com/uploadcare/uploadcare-go/v2/group"
"github.com/uploadcare/uploadcare-go/v2/upload"
"github.com/uploadcare/uploadcare-go/v2/conversion"
"github.com/uploadcare/uploadcare-go/v2/projectapi"
)
```

## Configuration

Creating a client:
### REST & Upload API client

```go
creds := ucare.APICreds{
Expand All @@ -56,6 +57,20 @@ if err != nil {
}
```

### Project API client

The Project API uses bearer token authentication. Tokens can be obtained
via [Uploadcare Support](mailto:help@uploadcare.com).

```go
client, err := ucare.NewBearerClient("your-bearer-token", nil)
if err != nil {
log.Fatal("creating project API client: %s", err)
}

projectSvc := projectapi.NewService(client)
```

## Usage

For a comprehensive list of examples, check out the [API documentation](https://pkg.go.dev/github.com/uploadcare/uploadcare-go/v2/ucare).
Expand Down Expand Up @@ -92,7 +107,7 @@ Acquiring file-specific info:

```go
fileID := ids[0]
file, err := fileSvc.Info(context.Background(), fileID)
file, err := fileSvc.Info(context.Background(), fileID, nil)
if err != nil {
// handle error
}
Expand Down Expand Up @@ -125,12 +140,45 @@ if err != nil {
}
```

Managing projects via the Project API:

```go
client, err := ucare.NewBearerClient("your-bearer-token", nil)
if err != nil {
// handle error
}

projectSvc := projectapi.NewService(client)

// List all projects
projects, err := projectSvc.List(context.Background(), nil)
if err != nil {
// handle error
}

// Get project details
proj, err := projectSvc.Get(context.Background(), projects.Results[0].PubKey)
if err != nil {
// handle error
}
fmt.Printf("project: %s (%s)\n", proj.Name, proj.PubKey)

// Get usage metrics
usage, err := projectSvc.GetUsage(context.Background(), proj.PubKey, projectapi.UsageDateRange{
From: "2025-01-01",
To: "2025-01-31",
})
if err != nil {
// handle error
}
```

## Useful links

[Golang API client documentation](https://pkg.go.dev/github.com/uploadcare/uploadcare-go/v2/ucare)
[Uploadcare documentation](https://uploadcare.com/docs/?utm_source=github&utm_medium=referral&utm_campaign=uploadcare-go)
[Upload API reference](https://uploadcare.com/api-refs/upload-api/?utm_source=github&utm_medium=referral&utm_campaign=uploadcare-go)
[REST API reference](https://uploadcare.com/api-refs/rest-api/?utm_source=github&utm_medium=referral&utm_campaign=uploadcare-go)
[REST API reference](https://uploadcare.com/api-refs/rest-api/?utm_source=github&utm_medium=referral&utm_campaign=uploadcare-go)
[Changelog](https://github.com/uploadcare/uploadcare-go/blob/master/CHANGELOG.md)
[Contributing guide](https://github.com/uploadcare/.github/blob/master/CONTRIBUTING.md)
[Security policy](https://github.com/uploadcare/uploadcare-go/security/policy)
Expand Down
74 changes: 74 additions & 0 deletions addon/addon.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Package addon holds primitives and logic for the Uploadcare Addons API.
//
// Addons allow executing various processing tasks on uploaded files,
// such as background removal, virus scanning, and image recognition.
package addon

import "encoding/json"

// Addon name constants used in URL paths
const (
AddonRemoveBG = "remove_bg"
AddonClamAV = "uc_clamav_virus_scan"
AddonRekognitionLabels = "aws_rekognition_detect_labels"
AddonRekognitionModeration = "aws_rekognition_detect_moderation_labels"
)

// Execution status constants
const (
StatusInProgress = "in_progress"
StatusDone = "done"
StatusError = "error"
StatusUnknown = "unknown"
)

// ExecuteParams holds parameters for executing an addon
type ExecuteParams struct {
// Target is the file UUID to process
Target string `json:"target"`

// Params holds addon-specific parameters.
// Use RemoveBGParams, ClamAVParams, or nil depending on the addon.
Params interface{} `json:"params,omitempty"`
}

// ExecuteResult holds the response from an addon execution request
type ExecuteResult struct {
// RequestID is the unique identifier for this execution request
RequestID string `json:"request_id"`
}

// StatusParams holds parameters for checking addon execution status
type StatusParams struct {
// RequestID is the execution request ID returned by Execute
RequestID string `json:"request_id"`
}

// StatusResult holds the response from an addon status check
type StatusResult struct {
// Status is the current execution status
Status string `json:"status"`

// Result holds addon-specific result data.
// The structure varies per addon.
Result json.RawMessage `json:"result"`
}

// RemoveBGParams holds parameters for the remove.bg addon
type RemoveBGParams struct {
Crop *bool `json:"crop,omitempty"`
CropMargin *string `json:"crop_margin,omitempty"`
Scale *string `json:"scale,omitempty"`
AddShadow *bool `json:"add_shadow,omitempty"`
TypeLevel *string `json:"type_level,omitempty"`
Type *string `json:"type,omitempty"`
Semitransparency *bool `json:"semitransparency,omitempty"`
Channels *string `json:"channels,omitempty"`
ROI *string `json:"roi,omitempty"`
Position *string `json:"position,omitempty"`
}

// ClamAVParams holds parameters for the ClamAV virus scan addon
type ClamAVParams struct {
PurgeInfected *bool `json:"purge_infected,omitempty"`
}
20 changes: 20 additions & 0 deletions addon/log.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package addon

import (
"github.com/uploadcare/uploadcare-go/v2/uclog"
)

var log uclog.Logger

const subsystemTag = "ADON"

func init() { DisableLog() }

// DisableLog does what you expect
func DisableLog() { log = uclog.Disabled }

// EnableLog enables package scoped logging
func EnableLog(lvl uclog.Level) {
log = uclog.Backend.Logger(subsystemTag)
log.SetLevel(lvl)
}
76 changes: 76 additions & 0 deletions addon/service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package addon

import (
"context"
"fmt"
"net/http"

"github.com/uploadcare/uploadcare-go/v2/internal/codec"
"github.com/uploadcare/uploadcare-go/v2/internal/config"
"github.com/uploadcare/uploadcare-go/v2/internal/svc"
"github.com/uploadcare/uploadcare-go/v2/ucare"
)

// Service describes all addon related API
type Service interface {
// Execute starts an addon execution on a file
Execute(ctx context.Context, addonName string, params ExecuteParams) (ExecuteResult, error)

// Status checks the execution status of an addon request
Status(ctx context.Context, addonName string, requestID string) (StatusResult, error)
}

type service struct {
svc svc.Service
}

// NewService returns new instance of the Service
func NewService(client ucare.Client) Service {
return service{svc.New(config.RESTAPIEndpoint, client, log)}
}

// Execute starts an addon execution on a file
func (s service) Execute(
ctx context.Context,
addonName string,
params ExecuteParams,
) (data ExecuteResult, err error) {
err = s.svc.ResourceOp(
ctx,
http.MethodPost,
fmt.Sprintf("/addons/%s/execute/", addonName),
executeBody(params),
&data,
)
return
}

// Status checks the execution status of an addon request
func (s service) Status(
ctx context.Context,
addonName string,
requestID string,
) (data StatusResult, err error) {
err = s.svc.ResourceOp(
ctx,
http.MethodGet,
fmt.Sprintf("/addons/%s/execute/status/", addonName),
statusQuery{RequestID: requestID},
&data,
)
return
}

type executeBody ExecuteParams

func (p executeBody) EncodeReq(req *http.Request) error {
return codec.EncodeReqBody(p, req)
}

type statusQuery struct {
RequestID string `form:"request_id"`
}

func (p statusQuery) EncodeReq(req *http.Request) error {
return codec.EncodeReqQuery(&p, req)
}
Loading
Loading