diff --git a/gifs/README.md b/gifs/README.md index 22fd6cc..a3fa966 100644 --- a/gifs/README.md +++ b/gifs/README.md @@ -4,7 +4,6 @@ Quick and simple GIF search Add GIFs to your project with keyword search and results in multiple sizes and formats. - ![Powered by GIPHY](https://raw.githubusercontent.com/micro/services/master/gifs/assets/PoweredBy_200px-White_HorizLogo.png) When integrating this API please follow GIPHY's attribution guidelines [here](https://developers.giphy.com/docs/sdk/#design-guidelines). diff --git a/space/examples.json b/space/examples.json index b8f4ee9..4dd212c 100644 --- a/space/examples.json +++ b/space/examples.json @@ -109,5 +109,17 @@ "url": "https://example.com/foo/bar/images/file.jpg" } } + ], + "upload": [ + { + "title": "Upload an object", + "run_check": false, + "request": { + "name": "images/file.jpg" + }, + "response": { + "url": "https://example.com/foo/bar/images/file.jpg" + } + } ] } diff --git a/space/handler/space.go b/space/handler/space.go index 8ad6792..6aeb8cb 100644 --- a/space/handler/space.go +++ b/space/handler/space.go @@ -6,7 +6,6 @@ import ( "encoding/json" "fmt" "io/ioutil" - "strconv" "strings" "time" @@ -29,10 +28,8 @@ import ( ) const ( - mdACL = "X-Amz-Acl" - mdACLPublic = "public-read" - mdCreated = "Micro-Created" - mdVisibility = "Micro-Visibility" + mdACL = "X-Amz-Acl" + mdACLPublic = "public-read" visibilityPrivate = "private" visibilityPublic = "public" @@ -115,7 +112,7 @@ func (s Space) upsert(ctx context.Context, object []byte, name, visibility, meth } exists := false - hoo, err := s.client.HeadObject(&sthree.HeadObjectInput{ + _, err := s.client.HeadObject(&sthree.HeadObjectInput{ Bucket: aws.String(s.conf.SpaceName), Key: aws.String(objectName), }) @@ -132,24 +129,30 @@ func (s Space) upsert(ctx context.Context, object []byte, name, visibility, meth return "", errors.BadRequest(method, "Object already exists") } - createTime := aws.String(time.Now().Format(time.RFC3339Nano)) - if exists { - createTime = hoo.Metadata[mdCreated] - } - if len(visibility) == 0 { visibility = visibilityPrivate } + + now := time.Now().Format(time.RFC3339Nano) + md := meta{ + CreateTime: now, + ModifiedTime: now, + Visibility: visibility, + } + if exists { + m, err := s.objectMeta(objectName) + if err != nil { + log.Errorf("Error reading object meta %s", err) + return "", errors.BadRequest(method, "Error creating object") + } + md.CreateTime = m.CreateTime + } + putInput := &sthree.PutObjectInput{ Body: bytes.NewReader(object), Key: aws.String(objectName), Bucket: aws.String(s.conf.SpaceName), - Metadata: map[string]*string{ - mdVisibility: aws.String(visibility), - mdCreated: createTime, - }, } - // TODO flesh out options - might want to do content-type for better serving of object if visibility == visibilityPublic { putInput.ACL = aws.String(mdACLPublic) } @@ -160,14 +163,12 @@ func (s Space) upsert(ctx context.Context, object []byte, name, visibility, meth } // store the metadata for easy retrieval for listing - 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 { + if err := store.Write(store.NewRecord(fmt.Sprintf("%s/%s", prefixByUser, objectName), md)); err != nil { log.Errorf("Error writing object to store %s", err) return "", errors.InternalServerError(method, "Error creating object") } retUrl := "" - if visibility == "public" { + if visibility == visibilityPublic { retUrl = fmt.Sprintf("%s/%s", s.conf.BaseURL, objectName) } @@ -281,40 +282,39 @@ func (s Space) Head(ctx context.Context, request *pb.HeadRequest, response *pb.H return errors.InternalServerError(method, "Error reading object") } - vis := visibilityPrivate - if md, ok := goo.Metadata[mdVisibility]; ok && len(*md) > 0 { - vis = *md - } - var created string - if md, ok := goo.Metadata[mdCreated]; ok && len(*md) > 0 { - t, err := time.Parse(time.RFC3339Nano, *md) - if err != nil { - // try as unix ts - createdI, err := strconv.ParseInt(*md, 10, 64) - if err != nil { - log.Errorf("Error %s", err) - } else { - t = time.Unix(createdI, 0) - } - } - created = t.Format(time.RFC3339Nano) + md, err := s.objectMeta(objectName) + if err != nil { + log.Errorf("Error reading object meta %s", err) + return errors.InternalServerError(method, "Error reading object") } url := "" - if vis == "public" { + if md.Visibility == visibilityPublic { url = fmt.Sprintf("%s/%s", s.conf.BaseURL, objectName) } response.Object = &pb.HeadObject{ Name: request.Name, Modified: goo.LastModified.Format(time.RFC3339Nano), - Created: created, - Visibility: vis, + Created: md.CreateTime, + Visibility: md.Visibility, Url: url, } return nil } +func (s *Space) objectMeta(objName string) (*meta, error) { + recs, err := store.Read(fmt.Sprintf("%s/%s", prefixByUser, objName)) + if err != nil { + return nil, err + } + var me meta + if err := json.Unmarshal(recs[0].Value, &me); err != nil { + return nil, err + } + return &me, nil +} + func (s *Space) Read(ctx context.Context, req *pb.ReadRequest, rsp *pb.ReadResponse) error { method := "space.Read" tnt, ok := tenant.FromContext(ctx) @@ -330,55 +330,35 @@ func (s *Space) Read(ctx context.Context, req *pb.ReadRequest, rsp *pb.ReadRespo objectName := fmt.Sprintf("%s/%s", tnt, name) - goo, err := s.client.HeadObject(&sthree.HeadObjectInput{ + goo, err := s.client.GetObject(&sthree.GetObjectInput{ Bucket: aws.String(s.conf.SpaceName), Key: aws.String(objectName), }) if err != nil { aerr, ok := err.(awserr.Error) - if ok && aerr.Code() == "NotFound" { + if ok && aerr.Code() == "NotSuchKey" { return errors.BadRequest(method, "Object not found") } log.Errorf("Error s3 %s", err) return errors.InternalServerError(method, "Error reading object") } - _, gooreq := s.client.GetObjectRequest(&sthree.GetObjectInput{ - Bucket: aws.String(s.conf.SpaceName), - Key: aws.String(objectName), - }) - - - vis := visibilityPrivate - if md, ok := goo.Metadata[mdVisibility]; ok && len(*md) > 0 { - vis = *md - } - - var created string - if md, ok := goo.Metadata[mdCreated]; ok && len(*md) > 0 { - t, err := time.Parse(time.RFC3339Nano, *md) - if err != nil { - // try as unix ts - createdI, err := strconv.ParseInt(*md, 10, 64) - if err != nil { - log.Errorf("Error %s", err) - } else { - t = time.Unix(createdI, 0) - } - } - created = t.Format(time.RFC3339Nano) + md, err := s.objectMeta(objectName) + if err != nil { + log.Errorf("Error reading meta %s", err) + return errors.InternalServerError(method, "Error reading object") } url := "" - if vis == "public" { + if md.Visibility == visibilityPublic { url = fmt.Sprintf("%s/%s", s.conf.BaseURL, objectName) } - if *gooreq.ContentLength > maxReadSize { + if *goo.ContentLength > maxReadSize { return errors.BadRequest(method, "Exceeds max read size: %v bytes", maxReadSize) } - b, err := ioutil.ReadAll(gooreq.Body) + b, err := ioutil.ReadAll(goo.Body) if err != nil { return errors.InternalServerError(method, "Failed to read data") } @@ -386,8 +366,8 @@ func (s *Space) Read(ctx context.Context, req *pb.ReadRequest, rsp *pb.ReadRespo rsp.Object = &pb.Object{ Name: req.Name, Modified: goo.LastModified.Format(time.RFC3339Nano), - Created: created, - Visibility: vis, + Created: md.CreateTime, + Visibility: md.Visibility, Url: url, Data: b, } @@ -461,3 +441,65 @@ func (s *Space) Download(ctx context.Context, req *api.Request, rsp *api.Respons return nil } + +func (s Space) Upload(ctx context.Context, request *pb.UploadRequest, response *pb.UploadResponse) error { + method := "space.Upload" + tnt, ok := tenant.FromContext(ctx) + if !ok { + return errors.Unauthorized(method, "Unauthorized") + } + if len(request.Name) == 0 { + return errors.BadRequest(method, "Missing name param") + } + objectName := fmt.Sprintf("%s/%s", tnt, request.Name) + if err := s3utils.CheckValidObjectName(objectName); err != nil { + return errors.BadRequest(method, "Invalid name") + } + + _, err := s.client.HeadObject(&sthree.HeadObjectInput{ + Bucket: aws.String(s.conf.SpaceName), + Key: aws.String(objectName), + }) + if err != nil { + aerr, ok := err.(awserr.Error) + if !ok || aerr.Code() != "NotFound" { + return errors.InternalServerError(method, "Error creating upload URL") + } + } else { + return errors.BadRequest(method, "Object already exists") + } + + createTime := aws.String(time.Now().Format(time.RFC3339Nano)) + + if len(request.Visibility) == 0 { + request.Visibility = visibilityPrivate + } + putInput := &sthree.PutObjectInput{ + Key: aws.String(objectName), + Bucket: aws.String(s.conf.SpaceName), + } + if request.Visibility == visibilityPublic { + putInput.ACL = aws.String(mdACLPublic) + } + + req, _ := s.client.PutObjectRequest(putInput) + url, err := req.Presign(5 * time.Minute) + if err != nil { + return errors.InternalServerError(method, "Error creating upload URL") + } + response.Url = url + + // store the metadata for easy retrieval for listing + if err := store.Write(store.NewRecord( + fmt.Sprintf("%s/%s", prefixByUser, objectName), + meta{ + Visibility: request.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 upload URL") + } + + return nil +} diff --git a/space/handler/space_test.go b/space/handler/space_test.go index e8eab24..fb65430 100644 --- a/space/handler/space_test.go +++ b/space/handler/space_test.go @@ -105,8 +105,6 @@ func TestCreate(t *testing.T) { put: func(input *sthree.PutObjectInput) (*sthree.PutObjectOutput, error) { g.Expect(*input.Bucket).To(Equal("my-space")) g.Expect(input.ACL).To(BeNil()) - g.Expect(*input.Metadata[mdVisibility]).To(Equal(visibilityPrivate)) - g.Expect(input.Metadata[mdCreated]).To(Not(BeNil())) return &sthree.PutObjectOutput{}, nil }, }, @@ -121,8 +119,6 @@ func TestCreate(t *testing.T) { put: func(input *sthree.PutObjectInput) (*sthree.PutObjectOutput, error) { g.Expect(*input.Bucket).To(Equal("my-space")) g.Expect(*input.ACL).To(Equal(mdACLPublic)) - g.Expect(*input.Metadata[mdVisibility]).To(Equal(visibilityPublic)) - g.Expect(input.Metadata[mdCreated]).To(Not(BeNil())) return &sthree.PutObjectOutput{}, nil }, }, @@ -206,8 +202,6 @@ func TestUpdate(t *testing.T) { put: func(input *sthree.PutObjectInput) (*sthree.PutObjectOutput, error) { g.Expect(*input.Bucket).To(Equal("my-space")) g.Expect(input.ACL).To(BeNil()) - g.Expect(*input.Metadata[mdVisibility]).To(Equal(visibilityPrivate)) - g.Expect(input.Metadata[mdCreated]).To(Not(BeNil())) return &sthree.PutObjectOutput{}, nil }, }, @@ -222,8 +216,6 @@ func TestUpdate(t *testing.T) { put: func(input *sthree.PutObjectInput) (*sthree.PutObjectOutput, error) { g.Expect(*input.Bucket).To(Equal("my-space")) g.Expect(*input.ACL).To(Equal(mdACLPublic)) - g.Expect(*input.Metadata[mdVisibility]).To(Equal(visibilityPublic)) - g.Expect(input.Metadata[mdCreated]).To(Not(BeNil())) return &sthree.PutObjectOutput{}, nil }, }, @@ -238,19 +230,11 @@ func TestUpdate(t *testing.T) { 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{ - Metadata: map[string]*string{ - mdCreated: aws.String("1638541918"), - mdVisibility: aws.String(visibilityPrivate), - }, - }, nil + return &sthree.HeadObjectOutput{}, nil }, put: func(input *sthree.PutObjectInput) (*sthree.PutObjectOutput, error) { g.Expect(*input.Bucket).To(Equal("my-space")) g.Expect(input.ACL).To(BeNil()) - g.Expect(*input.Metadata[mdVisibility]).To(Equal(visibilityPrivate)) - // created shouuld be copied from the previous - g.Expect(*input.Metadata[mdCreated]).To(Equal("1638541918")) return &sthree.PutObjectOutput{}, nil }, url: "", @@ -261,19 +245,11 @@ func TestUpdate(t *testing.T) { 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{ - Metadata: map[string]*string{ - mdCreated: aws.String("1638541918"), - mdVisibility: aws.String(visibilityPrivate), - }, - }, nil + return &sthree.HeadObjectOutput{}, nil }, put: func(input *sthree.PutObjectInput) (*sthree.PutObjectOutput, error) { g.Expect(*input.Bucket).To(Equal("my-space")) g.Expect(*input.ACL).To(Equal(mdACLPublic)) - g.Expect(*input.Metadata[mdVisibility]).To(Equal(visibilityPublic)) - // created shouuld be copied from the previous - g.Expect(*input.Metadata[mdCreated]).To(Equal("1638541918")) return &sthree.PutObjectOutput{}, nil }, url: "https://my-space.ams3.example.com/micro/123/foo.jpg", @@ -404,10 +380,10 @@ func TestList(t *testing.T) { name: "Empty prefix", }, } - store.DefaultStore = memory.NewStore() for _, tc := range tcs { t.Run(tc.name, func(t *testing.T) { + store.DefaultStore = memory.NewStore() store.Write( store.NewRecord(fmt.Sprintf("%s/micro/123/file.jpg", prefixByUser), meta{ @@ -444,7 +420,7 @@ func TestList(t *testing.T) { }, { Key: aws.String("micro/123/file2.jpg"), - LastModified: aws.Time(time.Unix(1257894000, 0)), + LastModified: aws.Time(time.Unix(1257894001, 0)), }, }, }, nil @@ -469,9 +445,16 @@ func TestList(t *testing.T) { 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].Visibility).To(Equal("public")) + g.Expect(rsp.Objects[0].Created).To(Equal("2009-11-10T23:00:00Z")) + g.Expect(rsp.Objects[0].Modified).To(Equal("2009-11-10T23:00:00Z")) 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("")) + g.Expect(rsp.Objects[1].Visibility).To(Equal("private")) + g.Expect(rsp.Objects[1].Created).To(Equal("2009-11-10T23:00:01Z")) + g.Expect(rsp.Objects[1].Modified).To(Equal("2009-11-10T23:00:01Z")) + } }) @@ -504,10 +487,6 @@ func TestHead(t *testing.T) { return &sthree.HeadObjectOutput{ LastModified: aws.Time(time.Unix(1257894000, 0)), - Metadata: map[string]*string{ - mdCreated: aws.String("1257894000"), - mdVisibility: aws.String(visibilityPublic), - }, }, nil }, }, @@ -524,10 +503,6 @@ func TestHead(t *testing.T) { 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 }, }, @@ -544,10 +519,15 @@ func TestHead(t *testing.T) { }, }, } - store.DefaultStore = memory.NewStore() for _, tc := range tcs { t.Run(tc.name, func(t *testing.T) { + store.DefaultStore = memory.NewStore() + store.Write(store.NewRecord(fmt.Sprintf("%s/micro/123/%s", prefixByUser, tc.objectName), meta{ + Visibility: tc.visibility, + CreateTime: tc.created, + ModifiedTime: tc.modified, + })) handler := Space{ conf: conf{ AccessKey: "access", diff --git a/space/proto/space.pb.go b/space/proto/space.pb.go index 414e8d6..d3f6a10 100644 --- a/space/proto/space.pb.go +++ b/space/proto/space.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.27.1 -// protoc v3.15.6 +// protoc-gen-go v1.26.0 +// protoc v3.15.5 // source: proto/space.proto package space @@ -984,6 +984,111 @@ func (x *DownloadResponse) GetUrl() string { return "" } +// Upload a large object. Returns a time limited presigned URL to be used for uploading the object +type UploadRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // is this object public or private + Visibility string `protobuf:"bytes,2,opt,name=visibility,proto3" json:"visibility,omitempty"` +} + +func (x *UploadRequest) Reset() { + *x = UploadRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_space_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UploadRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UploadRequest) ProtoMessage() {} + +func (x *UploadRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_space_proto_msgTypes[17] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UploadRequest.ProtoReflect.Descriptor instead. +func (*UploadRequest) Descriptor() ([]byte, []int) { + return file_proto_space_proto_rawDescGZIP(), []int{17} +} + +func (x *UploadRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *UploadRequest) GetVisibility() string { + if x != nil { + return x.Visibility + } + return "" +} + +type UploadResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // a presigned url to be used for uploading + Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"` +} + +func (x *UploadResponse) Reset() { + *x = UploadResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_space_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UploadResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UploadResponse) ProtoMessage() {} + +func (x *UploadResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_space_proto_msgTypes[18] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UploadResponse.ProtoReflect.Descriptor instead. +func (*UploadResponse) Descriptor() ([]byte, []int) { + return file_proto_space_proto_rawDescGZIP(), []int{18} +} + +func (x *UploadResponse) GetUrl() string { + if x != nil { + return x.Url + } + return "" +} + var File_proto_space_proto protoreflect.FileDescriptor var file_proto_space_proto_rawDesc = []byte{ @@ -1057,34 +1162,44 @@ var file_proto_space_proto_rawDesc = []byte{ 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x24, 0x0a, 0x10, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, - 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x32, 0x8a, 0x03, - 0x0a, 0x05, 0x53, 0x70, 0x61, 0x63, 0x65, 0x12, 0x37, 0x0a, 0x06, 0x43, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x12, 0x14, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2e, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, - 0x12, 0x37, 0x0a, 0x06, 0x55, 0x70, 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, + 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x22, 0x43, 0x0a, + 0x0d, 0x55, 0x70, 0x6c, 0x6f, 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, 0x61, + 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x76, 0x69, 0x73, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x76, 0x69, 0x73, 0x69, 0x62, 0x69, 0x6c, 0x69, + 0x74, 0x79, 0x22, 0x22, 0x0a, 0x0e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x32, 0xc3, 0x03, 0x0a, 0x05, 0x53, 0x70, 0x61, 0x63, 0x65, + 0x12, 0x37, 0x0a, 0x06, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x14, 0x2e, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x15, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x37, 0x0a, 0x06, 0x55, 0x70, 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, 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, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x31, 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, 0x63, 0x65, 0x2e, 0x48, 0x65, 0x61, 0x64, 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, 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, 0x12, 0x3d, 0x0a, 0x08, 0x44, - 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x16, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2e, - 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x17, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 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, + 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, + 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x31, + 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, + 0x63, 0x65, 0x2e, 0x48, 0x65, 0x61, 0x64, 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, 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, 0x12, 0x3d, 0x0a, 0x08, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, + 0x12, 0x16, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, + 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x2e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x12, 0x37, 0x0a, 0x06, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x14, 0x2e, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2e, 0x55, 0x70, 0x6c, 0x6f, + 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 ( @@ -1099,7 +1214,7 @@ func file_proto_space_proto_rawDescGZIP() []byte { return file_proto_space_proto_rawDescData } -var file_proto_space_proto_msgTypes = make([]protoimpl.MessageInfo, 17) +var file_proto_space_proto_msgTypes = make([]protoimpl.MessageInfo, 19) var file_proto_space_proto_goTypes = []interface{}{ (*CreateRequest)(nil), // 0: space.CreateRequest (*CreateResponse)(nil), // 1: space.CreateResponse @@ -1118,6 +1233,8 @@ var file_proto_space_proto_goTypes = []interface{}{ (*ReadResponse)(nil), // 14: space.ReadResponse (*DownloadRequest)(nil), // 15: space.DownloadRequest (*DownloadResponse)(nil), // 16: space.DownloadResponse + (*UploadRequest)(nil), // 17: space.UploadRequest + (*UploadResponse)(nil), // 18: space.UploadResponse } var file_proto_space_proto_depIdxs = []int32{ 8, // 0: space.ListResponse.objects:type_name -> space.ListObject @@ -1130,15 +1247,17 @@ var file_proto_space_proto_depIdxs = []int32{ 9, // 7: space.Space.Head:input_type -> space.HeadRequest 13, // 8: space.Space.Read:input_type -> space.ReadRequest 15, // 9: space.Space.Download:input_type -> space.DownloadRequest - 1, // 10: space.Space.Create:output_type -> space.CreateResponse - 3, // 11: space.Space.Update:output_type -> space.UpdateResponse - 5, // 12: space.Space.Delete:output_type -> space.DeleteResponse - 7, // 13: space.Space.List:output_type -> space.ListResponse - 10, // 14: space.Space.Head:output_type -> space.HeadResponse - 14, // 15: space.Space.Read:output_type -> space.ReadResponse - 16, // 16: space.Space.Download:output_type -> space.DownloadResponse - 10, // [10:17] is the sub-list for method output_type - 3, // [3:10] is the sub-list for method input_type + 17, // 10: space.Space.Upload:input_type -> space.UploadRequest + 1, // 11: space.Space.Create:output_type -> space.CreateResponse + 3, // 12: space.Space.Update:output_type -> space.UpdateResponse + 5, // 13: space.Space.Delete:output_type -> space.DeleteResponse + 7, // 14: space.Space.List:output_type -> space.ListResponse + 10, // 15: space.Space.Head:output_type -> space.HeadResponse + 14, // 16: space.Space.Read:output_type -> space.ReadResponse + 16, // 17: space.Space.Download:output_type -> space.DownloadResponse + 18, // 18: space.Space.Upload:output_type -> space.UploadResponse + 11, // [11:19] is the sub-list for method output_type + 3, // [3:11] is the sub-list for method input_type 3, // [3:3] is the sub-list for extension type_name 3, // [3:3] is the sub-list for extension extendee 0, // [0:3] is the sub-list for field type_name @@ -1354,6 +1473,30 @@ func file_proto_space_proto_init() { return nil } } + file_proto_space_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UploadRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_space_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UploadResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -1361,7 +1504,7 @@ func file_proto_space_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_proto_space_proto_rawDesc, NumEnums: 0, - NumMessages: 17, + NumMessages: 19, NumExtensions: 0, NumServices: 1, }, diff --git a/space/proto/space.pb.micro.go b/space/proto/space.pb.micro.go index 06f94c3..7850300 100644 --- a/space/proto/space.pb.micro.go +++ b/space/proto/space.pb.micro.go @@ -49,6 +49,7 @@ type SpaceService interface { Head(ctx context.Context, in *HeadRequest, opts ...client.CallOption) (*HeadResponse, error) Read(ctx context.Context, in *ReadRequest, opts ...client.CallOption) (*ReadResponse, error) Download(ctx context.Context, in *DownloadRequest, opts ...client.CallOption) (*DownloadResponse, error) + Upload(ctx context.Context, in *UploadRequest, opts ...client.CallOption) (*UploadResponse, error) } type spaceService struct { @@ -133,6 +134,16 @@ func (c *spaceService) Download(ctx context.Context, in *DownloadRequest, opts . return out, nil } +func (c *spaceService) Upload(ctx context.Context, in *UploadRequest, opts ...client.CallOption) (*UploadResponse, error) { + req := c.c.NewRequest(c.name, "Space.Upload", in) + out := new(UploadResponse) + err := c.c.Call(ctx, req, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // Server API for Space service type SpaceHandler interface { @@ -143,6 +154,7 @@ type SpaceHandler interface { Head(context.Context, *HeadRequest, *HeadResponse) error Read(context.Context, *ReadRequest, *ReadResponse) error Download(context.Context, *DownloadRequest, *DownloadResponse) error + Upload(context.Context, *UploadRequest, *UploadResponse) error } func RegisterSpaceHandler(s server.Server, hdlr SpaceHandler, opts ...server.HandlerOption) error { @@ -154,6 +166,7 @@ func RegisterSpaceHandler(s server.Server, hdlr SpaceHandler, opts ...server.Han Head(ctx context.Context, in *HeadRequest, out *HeadResponse) error Read(ctx context.Context, in *ReadRequest, out *ReadResponse) error Download(ctx context.Context, in *DownloadRequest, out *DownloadResponse) error + Upload(ctx context.Context, in *UploadRequest, out *UploadResponse) error } type Space struct { space @@ -193,3 +206,7 @@ func (h *spaceHandler) Read(ctx context.Context, in *ReadRequest, out *ReadRespo func (h *spaceHandler) Download(ctx context.Context, in *DownloadRequest, out *DownloadResponse) error { return h.SpaceHandler.Download(ctx, in, out) } + +func (h *spaceHandler) Upload(ctx context.Context, in *UploadRequest, out *UploadResponse) error { + return h.SpaceHandler.Upload(ctx, in, out) +} diff --git a/space/proto/space.proto b/space/proto/space.proto index 36279dd..9ed54fb 100644 --- a/space/proto/space.proto +++ b/space/proto/space.proto @@ -12,6 +12,7 @@ service Space { rpc Head(HeadRequest) returns (HeadResponse) {} rpc Read(ReadRequest) returns (ReadResponse) {} rpc Download(DownloadRequest) returns (DownloadResponse) {} + rpc Upload(UploadRequest) returns (UploadResponse) {} } // Create an object. Returns error if object with this name already exists. If you want to update an existing object use the `Update` endpoint @@ -134,3 +135,15 @@ message DownloadResponse { // presigned url string url = 2; } + +// Upload a large object. Returns a time limited presigned URL to be used for uploading the object +message UploadRequest { + string name = 1; + // is this object public or private + string visibility = 2; +} + +message UploadResponse { + // a presigned url to be used for uploading + string url = 1; +}