Space updates (#299)

* updates for timestamps and storing meta for quick retrieval

* fix up head vis
This commit is contained in:
Dominic Wong
2021-12-09 16:25:34 +00:00
committed by GitHub
parent 395c190af3
commit dbdd3715a9
5 changed files with 239 additions and 87 deletions

View File

@@ -2,8 +2,6 @@ Infinite cloud storage
# Space Service # Space Service
Space for simple object storage. Put anything in the cloud Space for simple object storage. Put anything in the cloud forever. Objects can be public (readable by all via a public URL) or private.
forever. Objects can be public (readable by all via a public URL) or private.
Powered by S3 compatible storage API. Powered by S3 compatible storage API.

View File

@@ -14,6 +14,7 @@ import (
"github.com/micro/micro/v3/service/config" "github.com/micro/micro/v3/service/config"
"github.com/micro/micro/v3/service/errors" "github.com/micro/micro/v3/service/errors"
log "github.com/micro/micro/v3/service/logger" log "github.com/micro/micro/v3/service/logger"
"github.com/micro/micro/v3/service/store"
"github.com/micro/services/pkg/tenant" "github.com/micro/services/pkg/tenant"
pb "github.com/micro/services/space/proto" pb "github.com/micro/services/space/proto"
"github.com/minio/minio-go/v7/pkg/s3utils" "github.com/minio/minio-go/v7/pkg/s3utils"
@@ -34,6 +35,8 @@ const (
visibilityPrivate = "private" visibilityPrivate = "private"
visibilityPublic = "public" visibilityPublic = "public"
prefixByUser = "byUser"
) )
type Space struct { type Space struct {
@@ -51,6 +54,12 @@ type conf struct {
BaseURL string `json:"base_url"` BaseURL string `json:"base_url"`
} }
type meta struct {
Visibility string
CreateTime string
ModifiedTime string
}
func NewSpace(srv *service.Service) *Space { func NewSpace(srv *service.Service) *Space {
var c conf var c conf
val, err := config.Get("micro.space") val, err := config.Get("micro.space")
@@ -119,7 +128,7 @@ func (s Space) upsert(ctx context.Context, object []byte, name, visibility, meth
return "", errors.BadRequest(method, "Object already exists") return "", errors.BadRequest(method, "Object already exists")
} }
createTime := aws.String(fmt.Sprintf("%d", time.Now().Unix())) createTime := aws.String(time.Now().Format(time.RFC3339Nano))
if exists { if exists {
createTime = hoo.Metadata[mdCreated] createTime = hoo.Metadata[mdCreated]
} }
@@ -146,8 +155,19 @@ func (s Space) upsert(ctx context.Context, object []byte, name, visibility, meth
return "", errors.InternalServerError(method, "Error creating object") return "", errors.InternalServerError(method, "Error creating object")
} }
// TODO fix the url // store the metadata for easy retrieval for listing
return fmt.Sprintf("%s/%s", s.conf.BaseURL, objectName), nil if err := store.Write(store.NewRecord(
fmt.Sprintf("%s/%s", prefixByUser, objectName),
meta{Visibility: visibility, CreateTime: *createTime, ModifiedTime: time.Now().Format(time.RFC3339Nano)})); err != nil {
log.Errorf("Error writing object to store %s", err)
return "", errors.InternalServerError(method, "Error creating object")
}
retUrl := ""
if visibility == "public" {
retUrl = fmt.Sprintf("%s/%s", s.conf.BaseURL, objectName)
}
return retUrl, nil
} }
@@ -174,6 +194,10 @@ func (s Space) Delete(ctx context.Context, request *pb.DeleteRequest, response *
log.Errorf("Error deleting object %s", err) log.Errorf("Error deleting object %s", err)
return errors.InternalServerError(method, "Error deleting object") return errors.InternalServerError(method, "Error deleting object")
} }
if err := store.Delete(fmt.Sprintf("%s/%s", prefixByUser, objectName)); err != nil {
log.Errorf("Error deleting store record %s", err)
return errors.InternalServerError(method, "Error deleting object")
}
return nil return nil
} }
@@ -192,12 +216,38 @@ func (s Space) List(ctx context.Context, request *pb.ListRequest, response *pb.L
log.Errorf("Error listing objects %s", err) log.Errorf("Error listing objects %s", err)
return errors.InternalServerError(method, "Error listing objects") return errors.InternalServerError(method, "Error listing objects")
} }
recs, err := store.Read(fmt.Sprintf("%s/%s", prefixByUser, objectName), store.ReadPrefix())
if err != nil {
log.Errorf("Error listing objects %s", err)
return errors.InternalServerError(method, "Error listing objects")
}
md := map[string]meta{}
for _, r := range recs {
var m meta
if err := json.Unmarshal(r.Value, &m); err != nil {
log.Errorf("Error unmarshaling meta %s", err)
return errors.InternalServerError(method, "Error listing objects")
}
md[strings.TrimPrefix(r.Key, prefixByUser+"/")] = m
}
response.Objects = []*pb.ListObject{} response.Objects = []*pb.ListObject{}
for _, oi := range rsp.Contents { for _, oi := range rsp.Contents {
m, ok := md[*oi.Key]
if !ok {
// hack for now
m = meta{}
}
url := ""
if m.Visibility == "public" {
url = fmt.Sprintf("%s/%s", s.conf.BaseURL, *oi.Key)
}
response.Objects = append(response.Objects, &pb.ListObject{ response.Objects = append(response.Objects, &pb.ListObject{
Name: strings.TrimPrefix(*oi.Key, tnt+"/"), Name: strings.TrimPrefix(*oi.Key, tnt+"/"),
Modified: oi.LastModified.Unix(), Modified: oi.LastModified.Format(time.RFC3339Nano),
Url: fmt.Sprintf("%s/%s", s.conf.BaseURL, *oi.Key), Url: url,
Visibility: m.Visibility,
Created: m.CreateTime,
}) })
} }
return nil return nil
@@ -231,20 +281,31 @@ func (s Space) Head(ctx context.Context, request *pb.HeadRequest, response *pb.H
if md, ok := goo.Metadata[mdVisibility]; ok && len(*md) > 0 { if md, ok := goo.Metadata[mdVisibility]; ok && len(*md) > 0 {
vis = *md vis = *md
} }
var created int64 var created string
if md, ok := goo.Metadata[mdCreated]; ok && len(*md) > 0 { if md, ok := goo.Metadata[mdCreated]; ok && len(*md) > 0 {
created, err = strconv.ParseInt(*md, 10, 64) t, err := time.Parse(time.RFC3339Nano, *md)
if err != nil {
// try as unix ts
createdI, err := strconv.ParseInt(*md, 10, 64)
if err != nil { if err != nil {
log.Errorf("Error %s", err) log.Errorf("Error %s", err)
} else {
t = time.Unix(createdI, 0)
} }
} }
created = t.Format(time.RFC3339Nano)
}
url := ""
if vis == "public" {
url = fmt.Sprintf("%s/%s", s.conf.BaseURL, objectName)
}
response.Object = &pb.HeadObject{ response.Object = &pb.HeadObject{
Name: request.Name, Name: request.Name,
Modified: goo.LastModified.Unix(), Modified: goo.LastModified.Format(time.RFC3339Nano),
Created: created, Created: created,
Visibility: vis, Visibility: vis,
Url: fmt.Sprintf("%s/%s", s.conf.BaseURL, objectName), Url: url,
} }
return nil return nil

View File

@@ -11,6 +11,8 @@ import (
"github.com/aws/aws-sdk-go/service/s3/s3iface" "github.com/aws/aws-sdk-go/service/s3/s3iface"
"github.com/micro/micro/v3/service/auth" "github.com/micro/micro/v3/service/auth"
"github.com/micro/micro/v3/service/errors" "github.com/micro/micro/v3/service/errors"
"github.com/micro/micro/v3/service/store"
"github.com/micro/micro/v3/service/store/memory"
pb "github.com/micro/services/space/proto" pb "github.com/micro/services/space/proto"
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
@@ -96,7 +98,7 @@ func TestCreate(t *testing.T) {
{ {
name: "Simple case", name: "Simple case",
objName: "foo.jpg", objName: "foo.jpg",
url: "https://my-space.ams3.example.com/micro/123/foo.jpg", url: "",
head: func(input *sthree.HeadObjectInput) (*sthree.HeadObjectOutput, error) { head: func(input *sthree.HeadObjectInput) (*sthree.HeadObjectOutput, error) {
return nil, mockError{code: "NotFound"} return nil, mockError{code: "NotFound"}
}, },
@@ -140,6 +142,7 @@ func TestCreate(t *testing.T) {
}, },
}, },
} }
store.DefaultStore = memory.NewStore()
for _, tc := range tcs { for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
@@ -196,7 +199,7 @@ func TestUpdate(t *testing.T) {
{ {
name: "Does not exist", name: "Does not exist",
objName: "foo.jpg", objName: "foo.jpg",
url: "https://my-space.ams3.example.com/micro/123/foo.jpg", url: "",
head: func(input *sthree.HeadObjectInput) (*sthree.HeadObjectOutput, error) { head: func(input *sthree.HeadObjectInput) (*sthree.HeadObjectOutput, error) {
return nil, mockError{code: "NotFound"} return nil, mockError{code: "NotFound"}
}, },
@@ -250,7 +253,7 @@ func TestUpdate(t *testing.T) {
g.Expect(*input.Metadata[mdCreated]).To(Equal("1638541918")) g.Expect(*input.Metadata[mdCreated]).To(Equal("1638541918"))
return &sthree.PutObjectOutput{}, nil return &sthree.PutObjectOutput{}, nil
}, },
url: "https://my-space.ams3.example.com/micro/123/foo.jpg", url: "",
}, },
{ {
name: "Already exists public", name: "Already exists public",
@@ -277,6 +280,7 @@ func TestUpdate(t *testing.T) {
visibility: "public", visibility: "public",
}, },
} }
store.DefaultStore = memory.NewStore()
for _, tc := range tcs { for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
@@ -338,6 +342,7 @@ func TestDelete(t *testing.T) {
err: errors.BadRequest("space.Delete", "Missing name param"), err: errors.BadRequest("space.Delete", "Missing name param"),
}, },
} }
store.DefaultStore = memory.NewStore()
for _, tc := range tcs { for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
@@ -388,19 +393,35 @@ func TestList(t *testing.T) {
name string name string
prefix string prefix string
err error err error
list func(input *sthree.ListObjectsInput) (*sthree.ListObjectsInput, error) list func(input *sthree.ListObjectsInput) (*sthree.ListObjectsOutput, error)
visibility string
}{ }{
{ {
name: "Simple case", name: "Simple case",
prefix: "foo", prefix: "file",
}, },
{ {
name: "Empty prefix", name: "Empty prefix",
}, },
} }
store.DefaultStore = memory.NewStore()
for _, tc := range tcs { for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
store.Write(
store.NewRecord(fmt.Sprintf("%s/micro/123/file.jpg", prefixByUser),
meta{
Visibility: "public",
CreateTime: "2009-11-10T23:00:00Z",
ModifiedTime: "2009-11-10T23:00:00Z",
}))
store.Write(
store.NewRecord(fmt.Sprintf("%s/micro/123/file2.jpg", prefixByUser),
meta{
Visibility: "private",
CreateTime: "2009-11-10T23:00:01Z",
ModifiedTime: "2009-11-10T23:00:01Z",
}))
handler := Space{ handler := Space{
conf: conf{ conf: conf{
AccessKey: "access", AccessKey: "access",
@@ -415,7 +436,18 @@ func TestList(t *testing.T) {
list: func(input *sthree.ListObjectsInput) (*sthree.ListObjectsOutput, error) { list: func(input *sthree.ListObjectsInput) (*sthree.ListObjectsOutput, error) {
g.Expect(input.Bucket).To(Equal(aws.String("my-space"))) g.Expect(input.Bucket).To(Equal(aws.String("my-space")))
g.Expect(input.Prefix).To(Equal(aws.String("micro/123/" + tc.prefix))) g.Expect(input.Prefix).To(Equal(aws.String("micro/123/" + tc.prefix)))
return &sthree.ListObjectsOutput{}, nil return &sthree.ListObjectsOutput{
Contents: []*sthree.Object{
{
Key: aws.String("micro/123/file.jpg"),
LastModified: aws.Time(time.Unix(1257894000, 0)),
},
{
Key: aws.String("micro/123/file2.jpg"),
LastModified: aws.Time(time.Unix(1257894000, 0)),
},
},
}, nil
}}, }},
} }
ctx := context.Background() ctx := context.Background()
@@ -435,6 +467,11 @@ func TestList(t *testing.T) {
g.Expect(err).To(Equal(tc.err)) g.Expect(err).To(Equal(tc.err))
} else { } else {
g.Expect(err).To(BeNil()) g.Expect(err).To(BeNil())
g.Expect(rsp.Objects).To(HaveLen(2))
g.Expect(rsp.Objects[0].Name).To(Equal("file.jpg"))
g.Expect(rsp.Objects[0].Url).To(Equal("https://my-space.ams3.example.com/micro/123/file.jpg"))
g.Expect(rsp.Objects[1].Name).To(Equal("file2.jpg"))
g.Expect(rsp.Objects[1].Url).To(Equal(""))
} }
}) })
@@ -449,8 +486,8 @@ func TestHead(t *testing.T) {
objectName string objectName string
url string url string
visibility string visibility string
modified int64 modified string
created int64 created string
err error err error
head func(input *sthree.HeadObjectInput) (*sthree.HeadObjectOutput, error) head func(input *sthree.HeadObjectInput) (*sthree.HeadObjectOutput, error)
}{ }{
@@ -459,21 +496,41 @@ func TestHead(t *testing.T) {
objectName: "foo.jpg", objectName: "foo.jpg",
visibility: "public", visibility: "public",
url: "https://my-space.ams3.example.com/micro/123/foo.jpg", url: "https://my-space.ams3.example.com/micro/123/foo.jpg",
created: 1638547905, created: "2009-11-10T23:00:00Z",
modified: 1638547906, modified: "2009-11-10T23:00:00Z",
head: func(input *sthree.HeadObjectInput) (*sthree.HeadObjectOutput, error) { head: func(input *sthree.HeadObjectInput) (*sthree.HeadObjectOutput, error) {
g.Expect(*input.Bucket).To(Equal("my-space")) g.Expect(*input.Bucket).To(Equal("my-space"))
g.Expect(*input.Key).To(Equal("micro/123/foo.jpg")) g.Expect(*input.Key).To(Equal("micro/123/foo.jpg"))
return &sthree.HeadObjectOutput{ return &sthree.HeadObjectOutput{
LastModified: aws.Time(time.Unix(1638547906, 0)), LastModified: aws.Time(time.Unix(1257894000, 0)),
Metadata: map[string]*string{ Metadata: map[string]*string{
mdCreated: aws.String("1638547905"), mdCreated: aws.String("1257894000"),
mdVisibility: aws.String(visibilityPublic), mdVisibility: aws.String(visibilityPublic),
}, },
}, nil }, nil
}, },
}, },
{
name: "Simple case private",
objectName: "foo.jpg",
visibility: "private",
url: "",
created: "2009-11-10T23:00:00Z",
modified: "2009-11-10T23:00:00Z",
head: func(input *sthree.HeadObjectInput) (*sthree.HeadObjectOutput, error) {
g.Expect(*input.Bucket).To(Equal("my-space"))
g.Expect(*input.Key).To(Equal("micro/123/foo.jpg"))
return &sthree.HeadObjectOutput{
LastModified: aws.Time(time.Unix(1257894000, 0)),
Metadata: map[string]*string{
mdCreated: aws.String("2009-11-10T23:00:00Z"),
mdVisibility: aws.String("private"),
},
}, nil
},
},
{ {
name: "Empty prefix", name: "Empty prefix",
err: errors.BadRequest("space.Head", "Missing name param"), err: errors.BadRequest("space.Head", "Missing name param"),
@@ -487,6 +544,7 @@ func TestHead(t *testing.T) {
}, },
}, },
} }
store.DefaultStore = memory.NewStore()
for _, tc := range tcs { for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
@@ -522,7 +580,7 @@ func TestHead(t *testing.T) {
} else { } else {
g.Expect(err).To(BeNil()) g.Expect(err).To(BeNil())
g.Expect(rsp.Object.Name).To(Equal(tc.objectName)) g.Expect(rsp.Object.Name).To(Equal(tc.objectName))
g.Expect(rsp.Object.Url).To(Equal("https://my-space.ams3.example.com/micro/123/" + tc.objectName)) g.Expect(rsp.Object.Url).To(Equal(tc.url))
g.Expect(rsp.Object.Visibility).To(Equal(tc.visibility)) g.Expect(rsp.Object.Visibility).To(Equal(tc.visibility))
g.Expect(rsp.Object.Created).To(Equal(tc.created)) g.Expect(rsp.Object.Created).To(Equal(tc.created))
g.Expect(rsp.Object.Modified).To(Equal(tc.modified)) g.Expect(rsp.Object.Modified).To(Equal(tc.modified))

View File

@@ -444,8 +444,10 @@ type ListObject struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
// when was this last modified // when was this last modified
Modified int64 `protobuf:"varint,2,opt,name=modified,proto3" json:"modified,omitempty"` Modified string `protobuf:"bytes,2,opt,name=modified,proto3" json:"modified,omitempty"`
Url string `protobuf:"bytes,3,opt,name=url,proto3" json:"url,omitempty"` Url string `protobuf:"bytes,3,opt,name=url,proto3" json:"url,omitempty"`
Visibility string `protobuf:"bytes,4,opt,name=visibility,proto3" json:"visibility,omitempty"`
Created string `protobuf:"bytes,5,opt,name=created,proto3" json:"created,omitempty"`
} }
func (x *ListObject) Reset() { func (x *ListObject) Reset() {
@@ -487,11 +489,11 @@ func (x *ListObject) GetName() string {
return "" return ""
} }
func (x *ListObject) GetModified() int64 { func (x *ListObject) GetModified() string {
if x != nil { if x != nil {
return x.Modified return x.Modified
} }
return 0 return ""
} }
func (x *ListObject) GetUrl() string { func (x *ListObject) GetUrl() string {
@@ -501,12 +503,27 @@ func (x *ListObject) GetUrl() string {
return "" return ""
} }
func (x *ListObject) GetVisibility() string {
if x != nil {
return x.Visibility
}
return ""
}
func (x *ListObject) GetCreated() string {
if x != nil {
return x.Created
}
return ""
}
// Retrieve meta information about an object // Retrieve meta information about an object
type HeadRequest struct { type HeadRequest struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
// name of the object
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
} }
@@ -603,9 +620,9 @@ type HeadObject struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
// when was this last modified // when was this last modified
Modified int64 `protobuf:"varint,2,opt,name=modified,proto3" json:"modified,omitempty"` Modified string `protobuf:"bytes,2,opt,name=modified,proto3" json:"modified,omitempty"`
// when was this created // when was this created
Created int64 `protobuf:"varint,3,opt,name=created,proto3" json:"created,omitempty"` Created string `protobuf:"bytes,3,opt,name=created,proto3" json:"created,omitempty"`
// is this public or private // is this public or private
Visibility string `protobuf:"bytes,4,opt,name=visibility,proto3" json:"visibility,omitempty"` Visibility string `protobuf:"bytes,4,opt,name=visibility,proto3" json:"visibility,omitempty"`
// URL to access the object if it is public // URL to access the object if it is public
@@ -651,18 +668,18 @@ func (x *HeadObject) GetName() string {
return "" return ""
} }
func (x *HeadObject) GetModified() int64 { func (x *HeadObject) GetModified() string {
if x != nil { if x != nil {
return x.Modified return x.Modified
} }
return 0 return ""
} }
func (x *HeadObject) GetCreated() int64 { func (x *HeadObject) GetCreated() string {
if x != nil { if x != nil {
return x.Created return x.Created
} }
return 0 return ""
} }
func (x *HeadObject) GetVisibility() string { func (x *HeadObject) GetVisibility() string {
@@ -679,12 +696,13 @@ func (x *HeadObject) GetUrl() string {
return "" return ""
} }
// Read/download the object // Read an object in storage. Use for private objects.
type ReadRequest struct { type ReadRequest struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
// name of the object
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
} }
@@ -732,6 +750,9 @@ type ReadResponse struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
// Returns the response as raw data
Object string `protobuf:"bytes,1,opt,name=object,proto3" json:"object,omitempty"`
} }
func (x *ReadResponse) Reset() { func (x *ReadResponse) Reset() {
@@ -766,6 +787,13 @@ func (*ReadResponse) Descriptor() ([]byte, []int) {
return file_proto_space_proto_rawDescGZIP(), []int{13} return file_proto_space_proto_rawDescGZIP(), []int{13}
} }
func (x *ReadResponse) GetObject() string {
if x != nil {
return x.Object
}
return ""
}
var File_proto_space_proto protoreflect.FileDescriptor var File_proto_space_proto protoreflect.FileDescriptor
var file_proto_space_proto_rawDesc = []byte{ var file_proto_space_proto_rawDesc = []byte{
@@ -796,52 +824,57 @@ var file_proto_space_proto_rawDesc = []byte{
0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x07, 0x6f, 0x62, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x07, 0x6f, 0x62,
0x6a, 0x65, 0x63, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x73, 0x70, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x73, 0x70,
0x61, 0x63, 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x07, 0x61, 0x63, 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x07,
0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x22, 0x4e, 0x0a, 0x0a, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x22, 0x88, 0x01, 0x0a, 0x0a, 0x4c, 0x69, 0x73, 0x74,
0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,
0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x6f, 0x64, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x6f,
0x69, 0x66, 0x69, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x6d, 0x6f, 0x64, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x6f,
0x69, 0x66, 0x69, 0x65, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x03, 0x20,
0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x22, 0x21, 0x0a, 0x0b, 0x48, 0x65, 0x61, 0x64, 0x52, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x1e, 0x0a, 0x0a, 0x76, 0x69, 0x73, 0x69,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x76, 0x69,
0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x39, 0x0a, 0x0c, 0x48, 0x65, 0x73, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x72, 0x65, 0x61,
0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x29, 0x0a, 0x06, 0x6f, 0x62, 0x74, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74,
0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x73, 0x70, 0x61, 0x65, 0x64, 0x22, 0x21, 0x0a, 0x0b, 0x48, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x63, 0x65, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x06, 0x6f, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x62, 0x6a, 0x65, 0x63, 0x74, 0x22, 0x88, 0x01, 0x0a, 0x0a, 0x48, 0x65, 0x61, 0x64, 0x4f, 0x62, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x39, 0x0a, 0x0c, 0x48, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73,
0x6a, 0x65, 0x63, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x29, 0x0a, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18,
0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x6f, 0x64, 0x69, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2e, 0x48, 0x65,
0x66, 0x69, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x6d, 0x6f, 0x64, 0x69, 0x61, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74,
0x66, 0x69, 0x65, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x18, 0x22, 0x88, 0x01, 0x0a, 0x0a, 0x48, 0x65, 0x61, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12,
0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x12, 0x1e,
0x0a, 0x0a, 0x76, 0x69, 0x73, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x18, 0x04, 0x20, 0x01,
0x28, 0x09, 0x52, 0x0a, 0x76, 0x69, 0x73, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x10,
0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c,
0x22, 0x21, 0x0a, 0x0b, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e,
0x61, 0x6d, 0x65, 0x22, 0x0e, 0x0a, 0x0c, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x18,
0x6e, 0x73, 0x65, 0x32, 0xcb, 0x02, 0x0a, 0x05, 0x53, 0x70, 0x61, 0x63, 0x65, 0x12, 0x37, 0x0a, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x12,
0x06, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x14, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2e, 0x18, 0x0a, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x52, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x76, 0x69, 0x73,
0x73, 0x70, 0x61, 0x63, 0x65, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x76,
0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x37, 0x0a, 0x06, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x69, 0x73, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c,
0x12, 0x14, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x22, 0x21, 0x0a, 0x0b, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2e, 0x55, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61,
0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x26,
0x37, 0x0a, 0x06, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x14, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x0a, 0x0c, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16,
0x65, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0a, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
0x15, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x32, 0xcb, 0x02, 0x0a, 0x05, 0x53, 0x70, 0x61, 0x63, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x31, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x37, 0x0a, 0x06, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x14, 0x2e, 0x73, 0x70, 0x61,
0x12, 0x12, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x63, 0x65, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x1a, 0x15, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52,
0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x31, 0x0a, 0x04, 0x48, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x37, 0x0a, 0x06, 0x55, 0x70, 0x64,
0x65, 0x61, 0x64, 0x12, 0x12, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x61, 0x74, 0x65, 0x12, 0x14, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2e, 0x55, 0x70, 0x64, 0x61,
0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x73, 0x70, 0x61, 0x63,
0x65, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x22, 0x00, 0x12, 0x37, 0x0a, 0x06, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x14, 0x2e, 0x73,
0x70, 0x61, 0x63, 0x65, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x1a, 0x15, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74,
0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x31, 0x0a, 0x04, 0x4c,
0x69, 0x73, 0x74, 0x12, 0x12, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2e,
0x48, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x31, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x31,
0x0a, 0x04, 0x52, 0x65, 0x61, 0x64, 0x12, 0x12, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2e, 0x52, 0x0a, 0x04, 0x48, 0x65, 0x61, 0x64, 0x12, 0x12, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2e, 0x48,
0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x73, 0x70, 0x61, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x73, 0x70, 0x61,
0x63, 0x65, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x63, 0x65, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
0x00, 0x42, 0x0f, 0x5a, 0x0d, 0x2e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x3b, 0x73, 0x70, 0x61, 0x00, 0x12, 0x31, 0x0a, 0x04, 0x52, 0x65, 0x61, 0x64, 0x12, 0x12, 0x2e, 0x73, 0x70, 0x61, 0x63,
0x63, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x65, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e,
0x73, 0x70, 0x61, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x22, 0x00, 0x42, 0x0f, 0x5a, 0x0d, 0x2e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x3b,
0x73, 0x70, 0x61, 0x63, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
} }
var ( var (

View File

@@ -68,8 +68,10 @@ message ListResponse {
message ListObject { message ListObject {
string name = 1; string name = 1;
// when was this last modified // when was this last modified
int64 modified = 2; string modified = 2;
string url = 3; string url = 3;
string visibility = 4;
string created = 5;
} }
// Retrieve meta information about an object // Retrieve meta information about an object
@@ -85,9 +87,9 @@ message HeadResponse {
message HeadObject { message HeadObject {
string name = 1; string name = 1;
// when was this last modified // when was this last modified
int64 modified = 2; string modified = 2;
// when was this created // when was this created
int64 created = 3; string created = 3;
// is this public or private // is this public or private
string visibility = 4; string visibility = 4;
// URL to access the object if it is public // URL to access the object if it is public