Merge pull request #59 from spotify/set-fact-endpoint

feat: set fact endpoint
This commit is contained in:
Jefferson Girão
2020-02-06 10:34:24 +01:00
committed by GitHub
7 changed files with 235 additions and 61 deletions
+8
View File
@@ -31,3 +31,11 @@ func (s *Server) GetEntity(ctx context.Context, req *pb.GetEntityRequest) (*pb.G
}
return &pb.GetEntityReply{Entity: &pb.Entity{Uri: entityUri}}, nil
}
func (s *Server) SetFact(ctx context.Context, req *pb.SetFactRequest) (*pb.SetFactReply, error) {
factUri, err := s.Storage.SetFact(req.EntityUri, req.Name, req.Value)
if err != nil {
return nil, status.Error(codes.Internal, "could not set fact")
}
return &pb.SetFactReply{FactUri: factUri} , nil
}
+67 -28
View File
@@ -11,34 +11,6 @@ import (
pb "github.com/spotify/backstage/proto/inventory/v1"
)
type TestStorage struct {
Storage *storage.Storage
Path string
}
// NewTestStorage returns a TestStorage using a temporary path.
func NewTestStorage() *TestStorage {
f, err := ioutil.TempFile("", "")
if err != nil {
panic(fmt.Sprintf("temp file: %s", err))
}
path := f.Name()
f.Close()
os.Remove(path)
// Open the database.
config := storage.Config{Path: path}
storage, _ := storage.OpenStorage(config)
// Return wrapped type.
return &TestStorage{Storage: storage, Path: path}
}
// Close and delete Bolt database.
func (db *TestStorage) Close() {
defer os.Remove(db.Path)
db.Storage.Close()
}
func TestServerCreateEntity(t *testing.T) {
testStorage := NewTestStorage()
defer testStorage.Close()
@@ -76,3 +48,70 @@ func TestServerGetEntity(t *testing.T) {
t.Errorf("ServerTest(GetEntity) got %v, wanted %v", resp.GetEntity().GetUri(), entity.GetUri())
}
}
func TestServerSetFactForExistingEntity(t *testing.T) {
testStorage := NewTestStorage()
defer testStorage.Close()
s := Server{Storage: testStorage.Storage}
entity := &pb.Entity{Uri: "boss://test/test"}
createReq := &pb.CreateEntityRequest{Entity: entity}
s.CreateEntity(context.Background(), createReq)
req := &pb.SetFactRequest{EntityUri: "boss://test/test", Name: "test-name", Value: "test-value"}
resp, err := s.SetFact(context.Background(), req)
if err != nil {
t.Errorf("ServerTest(SetFact) got unexpected error %v", err)
}
if resp == nil {
t.Errorf("ServerTest(SetFact) returned nil")
}
if resp.GetFactUri() != entity.GetUri() + "/" + req.Name {
t.Errorf("ServerTest(SetFact) got %v, wanted %v", resp.GetFactUri(), entity.GetUri() + "/" + req.Name)
}
}
func TestServerSetFactForNonExistingEntity(t *testing.T) {
testStorage := NewTestStorage()
defer testStorage.Close()
s := Server{Storage: testStorage.Storage}
entityUri := "boss://test/test"
req := &pb.SetFactRequest{EntityUri: entityUri, Name: "test-name", Value: "test-value"}
resp, err := s.SetFact(context.Background(), req)
if err != nil {
t.Errorf("ServerTest(SetFact) got unexpected error %v", err)
}
if resp == nil {
t.Errorf("ServerTest(SetFact) returned nil")
}
if resp.GetFactUri() != entityUri + "/" + req.Name {
t.Errorf("ServerTest(SetFact) got %v, wanted %v", resp.GetFactUri(), entityUri + "/" + req.Name)
}
}
type TestStorage struct {
Storage *storage.Storage
Path string
}
// NewTestStorage returns a TestStorage using a temporary path.
func NewTestStorage() *TestStorage {
f, err := ioutil.TempFile("", "")
if err != nil {
panic(fmt.Sprintf("temp file: %s", err))
}
path := f.Name()
f.Close()
os.Remove(path)
config := storage.Config{Path: path}
storage, _ := storage.OpenStorage(config)
return &TestStorage{Storage: storage, Path: path}
}
func (db *TestStorage) Close() {
defer os.Remove(db.Path)
db.Storage.Close()
}
-2
View File
@@ -1,2 +0,0 @@
#!/bin/bash
protoc -I ../../ protos/inventory.proto --go_out=plugins=grpc:.
+3
View File
@@ -8,5 +8,8 @@ require (
github.com/golang/protobuf v1.3.3
github.com/spotify/backstage/proto v0.0.0-00010101000000-000000000000
go.etcd.io/bbolt v1.3.3
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3 // indirect
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135 // indirect
google.golang.org/grpc v1.27.0
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc // indirect
)
+12 -3
View File
@@ -37,14 +37,23 @@ func (s *Storage) Close() error {
return s.db.Close()
}
func (s *Storage) SetFact(entityUri, name, value string) error {
return s.db.Update(func(tx *bbolt.Tx) error {
func (s *Storage) SetFact(entityUri, name, value string) (factUri string, err error) {
err = s.db.Update(func(tx *bbolt.Tx) error {
b, err := tx.CreateBucketIfNotExists([]byte(entityUri))
if err != nil {
return err
}
return b.Put([]byte(name), []byte(value))
err = b.Put([]byte(name), []byte(value))
if err != nil {
return err
}
return nil
})
if err != nil {
return "", err
}
return entityUri + "/" + name, nil
}
func (s *Storage) GetFact(entityUri, name string) (string, error) {
+131 -25
View File
@@ -196,6 +196,100 @@ func (m *CreateEntityReply) GetEntity() *Entity {
return nil
}
type SetFactRequest struct {
EntityUri string `protobuf:"bytes,1,opt,name=entityUri,proto3" json:"entityUri,omitempty"`
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
Value string `protobuf:"bytes,3,opt,name=value,proto3" json:"value,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *SetFactRequest) Reset() { *m = SetFactRequest{} }
func (m *SetFactRequest) String() string { return proto.CompactTextString(m) }
func (*SetFactRequest) ProtoMessage() {}
func (*SetFactRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_70be9028e322f9d8, []int{4}
}
func (m *SetFactRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SetFactRequest.Unmarshal(m, b)
}
func (m *SetFactRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_SetFactRequest.Marshal(b, m, deterministic)
}
func (m *SetFactRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_SetFactRequest.Merge(m, src)
}
func (m *SetFactRequest) XXX_Size() int {
return xxx_messageInfo_SetFactRequest.Size(m)
}
func (m *SetFactRequest) XXX_DiscardUnknown() {
xxx_messageInfo_SetFactRequest.DiscardUnknown(m)
}
var xxx_messageInfo_SetFactRequest proto.InternalMessageInfo
func (m *SetFactRequest) GetEntityUri() string {
if m != nil {
return m.EntityUri
}
return ""
}
func (m *SetFactRequest) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *SetFactRequest) GetValue() string {
if m != nil {
return m.Value
}
return ""
}
type SetFactReply struct {
FactUri string `protobuf:"bytes,1,opt,name=factUri,proto3" json:"factUri,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *SetFactReply) Reset() { *m = SetFactReply{} }
func (m *SetFactReply) String() string { return proto.CompactTextString(m) }
func (*SetFactReply) ProtoMessage() {}
func (*SetFactReply) Descriptor() ([]byte, []int) {
return fileDescriptor_70be9028e322f9d8, []int{5}
}
func (m *SetFactReply) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SetFactReply.Unmarshal(m, b)
}
func (m *SetFactReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_SetFactReply.Marshal(b, m, deterministic)
}
func (m *SetFactReply) XXX_Merge(src proto.Message) {
xxx_messageInfo_SetFactReply.Merge(m, src)
}
func (m *SetFactReply) XXX_Size() int {
return xxx_messageInfo_SetFactReply.Size(m)
}
func (m *SetFactReply) XXX_DiscardUnknown() {
xxx_messageInfo_SetFactReply.DiscardUnknown(m)
}
var xxx_messageInfo_SetFactReply proto.InternalMessageInfo
func (m *SetFactReply) GetFactUri() string {
if m != nil {
return m.FactUri
}
return ""
}
type Entity struct {
Uri string `protobuf:"bytes,1,opt,name=uri,proto3" json:"uri,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
@@ -207,7 +301,7 @@ func (m *Entity) Reset() { *m = Entity{} }
func (m *Entity) String() string { return proto.CompactTextString(m) }
func (*Entity) ProtoMessage() {}
func (*Entity) Descriptor() ([]byte, []int) {
return fileDescriptor_70be9028e322f9d8, []int{4}
return fileDescriptor_70be9028e322f9d8, []int{6}
}
func (m *Entity) XXX_Unmarshal(b []byte) error {
@@ -236,8 +330,9 @@ func (m *Entity) GetUri() string {
}
type Fact struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"`
EntityUri string `protobuf:"bytes,1,opt,name=entityUri,proto3" json:"entityUri,omitempty"`
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
Value string `protobuf:"bytes,3,opt,name=value,proto3" json:"value,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@@ -247,7 +342,7 @@ func (m *Fact) Reset() { *m = Fact{} }
func (m *Fact) String() string { return proto.CompactTextString(m) }
func (*Fact) ProtoMessage() {}
func (*Fact) Descriptor() ([]byte, []int) {
return fileDescriptor_70be9028e322f9d8, []int{5}
return fileDescriptor_70be9028e322f9d8, []int{7}
}
func (m *Fact) XXX_Unmarshal(b []byte) error {
@@ -268,6 +363,13 @@ func (m *Fact) XXX_DiscardUnknown() {
var xxx_messageInfo_Fact proto.InternalMessageInfo
func (m *Fact) GetEntityUri() string {
if m != nil {
return m.EntityUri
}
return ""
}
func (m *Fact) GetName() string {
if m != nil {
return m.Name
@@ -287,6 +389,8 @@ func init() {
proto.RegisterType((*GetEntityReply)(nil), "spotify.backstage.inventory.v1.GetEntityReply")
proto.RegisterType((*CreateEntityRequest)(nil), "spotify.backstage.inventory.v1.CreateEntityRequest")
proto.RegisterType((*CreateEntityReply)(nil), "spotify.backstage.inventory.v1.CreateEntityReply")
proto.RegisterType((*SetFactRequest)(nil), "spotify.backstage.inventory.v1.SetFactRequest")
proto.RegisterType((*SetFactReply)(nil), "spotify.backstage.inventory.v1.SetFactReply")
proto.RegisterType((*Entity)(nil), "spotify.backstage.inventory.v1.Entity")
proto.RegisterType((*Fact)(nil), "spotify.backstage.inventory.v1.Fact")
}
@@ -294,27 +398,29 @@ func init() {
func init() { proto.RegisterFile("inventory/v1/inventory.proto", fileDescriptor_70be9028e322f9d8) }
var fileDescriptor_70be9028e322f9d8 = []byte{
// 307 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0xc9, 0xcc, 0x2b, 0x4b,
0xcd, 0x2b, 0xc9, 0x2f, 0xaa, 0xd4, 0x2f, 0x33, 0xd4, 0x87, 0x73, 0xf4, 0x0a, 0x8a, 0xf2, 0x4b,
0xf2, 0x85, 0xe4, 0x8a, 0x0b, 0xf2, 0x4b, 0x32, 0xd3, 0x2a, 0xf5, 0x92, 0x12, 0x93, 0xb3, 0x8b,
0x4b, 0x12, 0xd3, 0x53, 0xf5, 0x10, 0x4a, 0xca, 0x0c, 0x95, 0xca, 0xb9, 0x04, 0xdc, 0x53, 0x4b,
0x5c, 0xf3, 0x4a, 0x32, 0x4b, 0x2a, 0x83, 0x52, 0x0b, 0x4b, 0x53, 0x8b, 0x4b, 0x84, 0xec, 0xb8,
0xd8, 0x52, 0xc1, 0x02, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0xdc, 0x46, 0x6a, 0x7a, 0xf8, 0x0d, 0xd1,
0x83, 0x6a, 0x87, 0xea, 0x12, 0x52, 0xe6, 0xe2, 0xcd, 0xcc, 0x4b, 0xce, 0x29, 0x4d, 0x49, 0x8d,
0x4f, 0x4b, 0x4c, 0x2e, 0x29, 0x96, 0x60, 0x52, 0x60, 0xd6, 0xe0, 0x0c, 0xe2, 0x81, 0x0a, 0xba,
0x81, 0xc4, 0x94, 0x7a, 0x18, 0xb9, 0xf8, 0x90, 0x6c, 0x2e, 0xc8, 0xa9, 0xa4, 0xd8, 0x5e, 0x2b,
0x2e, 0x56, 0x84, 0x7d, 0xdc, 0x46, 0x2a, 0x84, 0xb4, 0x83, 0x1c, 0x12, 0x04, 0xd1, 0xa2, 0x14,
0xca, 0x25, 0xec, 0x5c, 0x94, 0x9a, 0x58, 0x92, 0x4a, 0xd5, 0xa0, 0x50, 0x0a, 0xe6, 0x12, 0x44,
0x35, 0x96, 0x0a, 0xfe, 0x54, 0x92, 0xe2, 0x62, 0x83, 0x88, 0x08, 0x09, 0x70, 0x31, 0x97, 0x16,
0x65, 0x82, 0x8d, 0xe1, 0x0c, 0x02, 0x31, 0x95, 0x0c, 0xb8, 0x58, 0x40, 0xde, 0x12, 0x12, 0xe2,
0x62, 0xc9, 0x4b, 0xcc, 0x4d, 0x85, 0x4a, 0x81, 0xd9, 0x42, 0x22, 0x5c, 0xac, 0x65, 0x89, 0x39,
0xa5, 0xa9, 0x12, 0x4c, 0x60, 0x41, 0x08, 0xc7, 0xe8, 0x13, 0x23, 0x17, 0xa7, 0x27, 0xcc, 0x36,
0xa1, 0x5c, 0x2e, 0x4e, 0x78, 0xac, 0x08, 0x19, 0x10, 0x72, 0x18, 0x7a, 0xd2, 0x91, 0xd2, 0x23,
0x41, 0x07, 0x28, 0x28, 0xca, 0xb8, 0x78, 0x90, 0xc3, 0x47, 0xc8, 0x98, 0x90, 0x7e, 0x2c, 0x91,
0x24, 0x65, 0x48, 0x9a, 0xa6, 0x82, 0x9c, 0x4a, 0x27, 0xde, 0x28, 0x6e, 0xb8, 0x8a, 0x32, 0xc3,
0x24, 0x36, 0x70, 0x66, 0x31, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0x24, 0xd0, 0xf1, 0x50, 0x4c,
0x03, 0x00, 0x00,
// 346 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x53, 0x4d, 0x4b, 0xc3, 0x40,
0x10, 0x25, 0xfd, 0x92, 0x4c, 0x3f, 0xa8, 0xab, 0x87, 0x50, 0x8a, 0x94, 0x55, 0xa4, 0xa7, 0xd4,
0xb4, 0x37, 0x0f, 0x1e, 0x14, 0x15, 0x2f, 0x1e, 0x52, 0x0a, 0xe2, 0x45, 0xb6, 0x75, 0x2a, 0xc1,
0x34, 0x89, 0xc9, 0x66, 0x25, 0xff, 0xc1, 0x5f, 0xeb, 0x2f, 0x90, 0xdd, 0xa4, 0x49, 0x15, 0xb1,
0x14, 0x73, 0xdb, 0x79, 0xb3, 0xef, 0xcd, 0xcb, 0xcb, 0x2c, 0xf4, 0x1d, 0x4f, 0xa0, 0xc7, 0xfd,
0x30, 0x19, 0x09, 0x6b, 0x94, 0x17, 0x66, 0x10, 0xfa, 0xdc, 0x27, 0x47, 0x51, 0xe0, 0x73, 0x67,
0x99, 0x98, 0x73, 0xb6, 0x78, 0x8d, 0x38, 0x7b, 0x41, 0xb3, 0xb8, 0x22, 0x2c, 0xfa, 0x0e, 0xdd,
0x5b, 0xe4, 0xd7, 0x1e, 0x77, 0x78, 0x62, 0xe3, 0x5b, 0x8c, 0x11, 0x27, 0x17, 0xd0, 0x40, 0x05,
0x18, 0xda, 0x40, 0x1b, 0x36, 0xc7, 0xa7, 0xe6, 0xdf, 0x22, 0x66, 0x46, 0xcf, 0x58, 0xe4, 0x18,
0xda, 0x8e, 0xb7, 0x70, 0xe3, 0x67, 0x7c, 0x5a, 0xb2, 0x05, 0x8f, 0x8c, 0xca, 0xa0, 0x3a, 0xd4,
0xed, 0x56, 0x06, 0xde, 0x48, 0x8c, 0x7e, 0x68, 0xd0, 0xd9, 0x98, 0x1c, 0xb8, 0xc9, 0xbf, 0xe7,
0x9e, 0x43, 0xbd, 0x98, 0xd7, 0x1c, 0x9f, 0x6c, 0xa3, 0x4b, 0x23, 0x76, 0x4a, 0xa1, 0x33, 0x38,
0xb8, 0x0a, 0x91, 0x71, 0x2c, 0x35, 0x0a, 0x3a, 0x85, 0xfd, 0xef, 0xb2, 0x25, 0x7c, 0x27, 0x7d,
0x80, 0xce, 0x14, 0xb9, 0x72, 0x9f, 0xd9, 0xec, 0x83, 0x9e, 0xf6, 0x66, 0xa1, 0xa3, 0x44, 0x75,
0xbb, 0x00, 0x08, 0x81, 0x9a, 0xc7, 0x56, 0x68, 0x54, 0x54, 0x43, 0x9d, 0xc9, 0x21, 0xd4, 0x05,
0x73, 0x63, 0x34, 0xaa, 0x0a, 0x4c, 0x0b, 0x3a, 0x84, 0x56, 0xae, 0x2c, 0x9d, 0x1a, 0xb0, 0x27,
0xe3, 0x29, 0x54, 0xd7, 0x25, 0xed, 0x41, 0x23, 0x75, 0x45, 0xba, 0x50, 0x8d, 0xf3, 0xbe, 0x3c,
0xd2, 0x7b, 0xa8, 0x49, 0x89, 0xb2, 0x5c, 0x8d, 0x3f, 0x35, 0xd0, 0xef, 0xd6, 0x79, 0x90, 0x15,
0xe8, 0xf9, 0xde, 0x90, 0xb3, 0x6d, 0xd1, 0xfd, 0x5c, 0xee, 0x9e, 0xb9, 0x03, 0x43, 0x46, 0x20,
0xa0, 0xb5, 0xf9, 0x07, 0xc9, 0x64, 0x1b, 0xff, 0x97, 0x35, 0xea, 0x59, 0xbb, 0x91, 0x02, 0x37,
0xb9, 0x6c, 0x3f, 0x36, 0xf3, 0x1b, 0xc2, 0x9a, 0x37, 0xd4, 0x73, 0x9e, 0x7c, 0x05, 0x00, 0x00,
0xff, 0xff, 0xae, 0xd9, 0x2b, 0x51, 0xee, 0x03, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
+14 -3
View File
@@ -27,11 +27,22 @@ message CreateEntityReply {
Entity entity = 1;
}
message SetFactRequest {
string entityUri = 1;
string name = 2;
string value = 3;
}
message SetFactReply {
string factUri = 1;
}
message Entity {
string uri = 1;
}
message Fact {
string name = 1;
string value = 2;
}
string entityUri = 1;
string name = 2;
string value = 3;
}