Skip to content

Commit

Permalink
Add a separate bucket ref type for signed upload URLs. (#1716)
Browse files Browse the repository at this point in the history
  • Loading branch information
erikcarlsson authored Jan 17, 2025
1 parent d41bbc1 commit a389ac0
Show file tree
Hide file tree
Showing 7 changed files with 40 additions and 11 deletions.
16 changes: 16 additions & 0 deletions runtimes/go/storage/objects/refs.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,22 @@ type Uploader interface {
// Upload begins uploading an object to the bucket.
Upload(ctx context.Context, object string, options ...UploadOption) *Writer

perms()
}

// SignedUploader is the interface for creating external URLs to upload objects
// to a bucket. It can be used in conjunction with [BucketRef] to declare
// a reference that can generate upload URLs to the bucket.
//
// For example:
//
// var MyBucket = objects.NewBucket(...)
// var ref = objects.BucketRef[objects.SignedUploader](MyBucket)
//
// The ref object can then be used to generate upload URLs and can be
// passed around freely within the service, without being subject
// to Encore's static analysis restrictions that apply to MyBucket.
type SignedUploader interface {
// SignedUploadURL returns a signed URL that can be used to upload directly to
// storage, without any other authentication.
SignedUploadURL(ctx context.Context, object string, options ...UploadURLOption) (string, error)
Expand Down
4 changes: 2 additions & 2 deletions runtimes/js/encore.dev/storage/objects/bucket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { getCurrentRequest } from "../../internal/reqtrack/mod";
import * as runtime from "../../internal/runtime/mod";
import { StringLiteral } from "../../internal/utils/constraints";
import { unwrapErr } from "./error";
import { BucketPerms, Uploader, Downloader, Attrser, Lister, Remover, PublicUrler } from "./refs";
import { BucketPerms, Uploader, SignedUploader, Downloader, Attrser, Lister, Remover, PublicUrler } from "./refs";

export interface BucketConfig {
/**
Expand All @@ -21,7 +21,7 @@ export interface BucketConfig {
/**
* Defines a new Object Storage bucket infrastructure resource.
*/
export class Bucket extends BucketPerms implements Uploader, Downloader, Attrser, Lister, Remover, PublicUrler {
export class Bucket extends BucketPerms implements Uploader, SignedUploader, Downloader, Attrser, Lister, Remover, PublicUrler {
impl: runtime.Bucket;

/**
Expand Down
2 changes: 1 addition & 1 deletion runtimes/js/encore.dev/storage/objects/mod.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export { Bucket } from "./bucket";
export type { BucketConfig, ObjectAttrs, UploadOptions } from "./bucket";
export { ObjectsError, ObjectNotFound, PreconditionFailed } from "./error";
export type { BucketPerms, Uploader, Downloader, Attrser, Lister, ReadWriter, PublicUrler } from "./refs";
export type { BucketPerms, Uploader, SignedUploader, Downloader, Attrser, Lister, ReadWriter, PublicUrler } from "./refs";
8 changes: 7 additions & 1 deletion runtimes/js/encore.dev/storage/objects/refs.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { AttrsOptions, DeleteOptions, DownloadOptions, ExistsOptions, ListEntry, ListOptions, ObjectAttrs, UploadOptions } from "./bucket";
import type { AttrsOptions, DeleteOptions, DownloadOptions, ExistsOptions, ListEntry,
ListOptions, ObjectAttrs, SignedUploadUrl, UploadOptions, UploadUrlOptions } from "./bucket";

export abstract class BucketPerms {
private bucketPerms(): void { };
Expand All @@ -8,6 +9,10 @@ export abstract class Uploader extends BucketPerms {
abstract upload(name: string, data: Buffer, options?: UploadOptions): Promise<ObjectAttrs>;
}

export abstract class SignedUploader extends BucketPerms {
abstract signedUploadUrl(name: string, options?: UploadUrlOptions): Promise<SignedUploadUrl>;
}

export abstract class Downloader extends BucketPerms {
abstract download(name: string, options?: DownloadOptions): Promise<Buffer>;
}
Expand All @@ -31,6 +36,7 @@ export abstract class PublicUrler extends BucketPerms {

export type ReadWriter =
& Uploader
& SignedUploader
& Downloader
& Attrser
& Lister
Expand Down
5 changes: 4 additions & 1 deletion tsparser/src/parser/resources/infra/objects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,10 @@ fn parse_bucket_ref(
let ops = match named.obj.name.as_deref() {
Some("Lister") => vec![Operation::ListObjects],
Some("Attrser") => vec![Operation::GetObjectMetadata],
Some("Uploader") => vec![Operation::WriteObject, Operation::SignedUploadUrl],
Some("Uploader") => vec![Operation::WriteObject],
Some("SignedUploader") => {
vec![Operation::WriteObject, Operation::SignedUploadUrl]
}
Some("Downloader") => vec![Operation::ReadObjectContents],
Some("Remover") => vec![Operation::DeleteObject],
Some("PublicUrler") => vec![Operation::GetPublicUrl],
Expand Down
8 changes: 6 additions & 2 deletions v2/parser/infra/objects/usage.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,8 @@ func parseBucketRef(errs *perr.List, expr *usage.FuncArg) usage.Usage {
for _, typ := range types {
switch {
case isNamed(typ, "Uploader"):
perms = append(perms, WriteObject)
case isNamed(typ, "SignedUploader"):
perms = append(perms, WriteObject, SignedUploadURL)
case isNamed(typ, "Downloader"):
perms = append(perms, ReadObjectContents)
Expand All @@ -141,15 +143,17 @@ func parseBucketRef(errs *perr.List, expr *usage.FuncArg) usage.Usage {
case isNamed(typ, "PublicURLer"):
perms = append(perms, GetPublicURL)
case isNamed(typ, "ReadWriter"):
perms = append(perms, WriteObject, ReadObjectContents, ListObjects, DeleteObject, GetObjectMetadata, SignedUploadURL, UpdateObjectMetadata)
perms = append(perms,
WriteObject, ReadObjectContents, ListObjects, DeleteObject,
GetObjectMetadata, SignedUploadURL, UpdateObjectMetadata)
default:
return nil, false
}
}

// Sort and de-dup the perms.
slices.Sort(perms)
slices.Compact(perms)
perms = slices.Compact(perms)

return &RefUsage{
Base: usage.Base{
Expand Down
8 changes: 4 additions & 4 deletions v2/parser/infra/objects/usage_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ var bkt = objects.NewBucket("bucket", objects.BucketConfig{})
var ref = objects.BucketRef[objects.Uploader](bkt)
`,
Want: []usage.Usage{&objects.RefUsage{
Perms: []objects.Perm{objects.SignedUploadURL, objects.WriteObject},
Perms: []objects.Perm{objects.WriteObject},
}},
},
{
Expand Down Expand Up @@ -78,7 +78,7 @@ type MyRef = objects.Uploader
var ref = objects.BucketRef[MyRef](bkt)
`,
Want: []usage.Usage{&objects.RefUsage{
Perms: []objects.Perm{objects.SignedUploadURL, objects.WriteObject},
Perms: []objects.Perm{objects.WriteObject},
}},
},
{
Expand All @@ -91,15 +91,15 @@ type MyRef interface { objects.Uploader }
var ref = objects.BucketRef[MyRef](bkt)
`,
Want: []usage.Usage{&objects.RefUsage{
Perms: []objects.Perm{objects.SignedUploadURL, objects.WriteObject},
Perms: []objects.Perm{objects.WriteObject},
}},
},
{
Name: "custom_ref_interface_multi",
Code: `
var bkt = objects.NewBucket("bucket", objects.BucketConfig{})
type MyRef interface { objects.Uploader; objects.Downloader }
type MyRef interface { objects.Uploader; objects.Downloader; objects.SignedUploader }
var ref = objects.BucketRef[MyRef](bkt)
`,
Expand Down

0 comments on commit a389ac0

Please sign in to comment.