Merge branch 'master' into chat

This commit is contained in:
ben-toogood
2020-10-16 10:30:01 +01:00
committed by GitHub
18 changed files with 950 additions and 146 deletions

View File

@@ -8,6 +8,7 @@ Services provides a home for real world examples for using Micro v3.
- [blog](blog) - A blog app composed as micro services
- [helloworld](helloworld) - A simple helloworld service
- [messages](messages) - A service for text messages
- [test](test) - A set of sample test services for Micro
## Usage

View File

@@ -11,4 +11,5 @@ A list of open source services to be created
- inbox - a simple inbox for mail and private messages
* messages - store the messages for the inbox
* notifications - holds updates and alerts
- notes - a simple note taking service
- users - manages user accounts and login

View File

@@ -39,8 +39,8 @@ type Posts struct {
}
func (p *Posts) Save(ctx context.Context, req *posts.SaveRequest, rsp *posts.SaveResponse) error {
if len(req.Id) == 0 || len(req.Title) == 0 || len(req.Content) == 0 {
return errors.BadRequest("posts.save.input-check", "Id, title or content is missing")
if len(req.Id) == 0 {
return errors.BadRequest("posts.save.input-check", "Id is missing")
}
// read by post
@@ -71,27 +71,48 @@ func (p *Posts) Save(ctx context.Context, req *posts.SaveRequest, rsp *posts.Sav
if err != nil {
return errors.InternalServerError("posts.save.unmarshal", "Failed to unmarshal old post: %v", err.Error())
}
post := &Post{
ID: req.Id,
Title: req.Title,
Content: req.Content,
Slug: postSlug,
Tags: req.Tags,
Title: oldPost.Title,
Content: oldPost.Content,
Slug: oldPost.Slug,
Tags: oldPost.Tags,
CreateTimestamp: oldPost.CreateTimestamp,
UpdateTimestamp: time.Now().Unix(),
}
if len(req.Title) > 0 {
post.Title = req.Title
post.Slug = slug.Make(post.Title)
}
if len(req.Slug) > 0 {
post.Slug = req.Slug
}
if len(req.Content) > 0 {
post.Content = req.Content
}
if len(req.Tags) > 0 {
// Handle the special case of deletion
if len(req.Tags) == 0 && req.Tags[0] == "" {
post.Tags = []string{}
} else {
post.Tags = req.Tags
}
}
// Check if slug exists
recordsBySlug, err := store.Read(fmt.Sprintf("%v:%v", slugPrefix, postSlug))
if err != nil && err != store.ErrNotFound {
return errors.InternalServerError("posts.save.store-read", "Failed to read post by slug: %v", err.Error())
}
otherSlugPost := &Post{}
err = json.Unmarshal(record.Value, otherSlugPost)
if err != nil {
return errors.InternalServerError("posts.save.slug-unmarshal", "Error unmarshaling other post with same slug: %v", err.Error())
}
if len(recordsBySlug) > 0 && oldPost.ID != otherSlugPost.ID {
if len(recordsBySlug) > 0 {
otherSlugPost := &Post{}
err = json.Unmarshal(recordsBySlug[0].Value, otherSlugPost)
if oldPost.ID != otherSlugPost.ID {
if err != nil {
return errors.InternalServerError("posts.save.slug-unmarshal", "Error unmarshaling other post with same slug: %v", err.Error())
}
}
return errors.BadRequest("posts.save.slug-check", "An other post with this slug already exists")
}
@@ -135,9 +156,9 @@ func (p *Posts) savePost(ctx context.Context, oldPost, post *Post) error {
if oldPost == nil {
for _, tagName := range post.Tags {
_, err := p.Tags.Add(ctx, &tags.AddRequest{
ParentID: post.ID,
Type: tagType,
Title: tagName,
ResourceID: post.ID,
Type: tagType,
Title: tagName,
})
if err != nil {
return err
@@ -161,9 +182,9 @@ func (p *Posts) diffTags(ctx context.Context, parentID string, oldTagNames, newT
_, stillThere := newTags[i]
if !stillThere {
_, err := p.Tags.Remove(ctx, &tags.RemoveRequest{
ParentID: parentID,
Type: tagType,
Title: i,
ResourceID: parentID,
Type: tagType,
Title: i,
})
if err != nil {
logger.Errorf("Error decreasing count for tag '%v' with type '%v' for parent '%v'", i, tagType, parentID)
@@ -174,9 +195,9 @@ func (p *Posts) diffTags(ctx context.Context, parentID string, oldTagNames, newT
_, newlyAdded := oldTags[i]
if newlyAdded {
_, err := p.Tags.Add(ctx, &tags.AddRequest{
ParentID: parentID,
Type: tagType,
Title: i,
ResourceID: parentID,
Type: tagType,
Title: i,
})
if err != nil {
logger.Errorf("Error increasing count for tag '%v' with type '%v' for parent '%v': %v", i, tagType, parentID, err)
@@ -193,6 +214,10 @@ func (p *Posts) Query(ctx context.Context, req *pb.QueryRequest, rsp *pb.QueryRe
key := fmt.Sprintf("%v:%v", slugPrefix, req.Slug)
logger.Infof("Reading post by slug: %v", req.Slug)
records, err = store.Read("", store.Prefix(key))
} else if len(req.Id) > 0 {
key := fmt.Sprintf("%v:%v", idPrefix, req.Id)
logger.Infof("Reading post by id: %v", req.Id)
records, err = store.Read("", store.Prefix(key))
} else {
key := fmt.Sprintf("%v:", timeStampPrefix)
var limit uint

View File

@@ -115,10 +115,13 @@ func (m *Post) GetTags() []string {
return nil
}
// Query posts. Acts as a listing when no id or slug provided.
// Gets a single post by id or slug if any of them provided.
type QueryRequest struct {
Slug string `protobuf:"bytes,1,opt,name=slug,proto3" json:"slug,omitempty"`
Offset int64 `protobuf:"varint,2,opt,name=offset,proto3" json:"offset,omitempty"`
Limit int64 `protobuf:"varint,3,opt,name=limit,proto3" json:"limit,omitempty"`
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
Slug string `protobuf:"bytes,2,opt,name=slug,proto3" json:"slug,omitempty"`
Offset int64 `protobuf:"varint,3,opt,name=offset,proto3" json:"offset,omitempty"`
Limit int64 `protobuf:"varint,4,opt,name=limit,proto3" json:"limit,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@@ -149,6 +152,13 @@ func (m *QueryRequest) XXX_DiscardUnknown() {
var xxx_messageInfo_QueryRequest proto.InternalMessageInfo
func (m *QueryRequest) GetId() string {
if m != nil {
return m.Id
}
return ""
}
func (m *QueryRequest) GetSlug() string {
if m != nil {
return m.Slug
@@ -210,11 +220,13 @@ func (m *QueryResponse) GetPosts() []*Post {
}
type SaveRequest struct {
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
Title string `protobuf:"bytes,2,opt,name=title,proto3" json:"title,omitempty"`
Slug string `protobuf:"bytes,3,opt,name=slug,proto3" json:"slug,omitempty"`
Content string `protobuf:"bytes,4,opt,name=content,proto3" json:"content,omitempty"`
Timestamp int64 `protobuf:"varint,5,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
Title string `protobuf:"bytes,2,opt,name=title,proto3" json:"title,omitempty"`
Slug string `protobuf:"bytes,3,opt,name=slug,proto3" json:"slug,omitempty"`
Content string `protobuf:"bytes,4,opt,name=content,proto3" json:"content,omitempty"`
Timestamp int64 `protobuf:"varint,5,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
// When updating a post and wanting to delete all tags,
// send a list of tags with only one member being an empty string [""]
Tags []string `protobuf:"bytes,6,rep,name=tags,proto3" json:"tags,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
@@ -412,28 +424,28 @@ func init() {
}
var fileDescriptor_a1e4efc789192621 = []byte{
// 363 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xb4, 0x52, 0x5d, 0x4e, 0xf3, 0x30,
// 365 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xac, 0x52, 0x5d, 0x4e, 0xf3, 0x30,
0x10, 0xfc, 0xd2, 0xfc, 0xf4, 0xeb, 0xf6, 0x47, 0x68, 0x5b, 0xc0, 0xaa, 0x10, 0x94, 0x3c, 0xf5,
0xa9, 0x88, 0x82, 0xc4, 0x05, 0x38, 0x40, 0x09, 0x27, 0x08, 0xd4, 0x2d, 0x91, 0xd2, 0x26, 0xc4,
0x0e, 0x12, 0xe7, 0xe0, 0x14, 0x5c, 0x81, 0xd3, 0x61, 0xaf, 0xed, 0x92, 0x56, 0xe2, 0x91, 0x97,
0x68, 0x67, 0x36, 0x1e, 0xcf, 0x4c, 0x02, 0xa7, 0x65, 0x55, 0xc8, 0xe2, 0xaa, 0x2c, 0x84, 0x14,
0xe6, 0x39, 0x23, 0x06, 0x43, 0x02, 0xf1, 0x97, 0x07, 0xc1, 0x42, 0x4d, 0x38, 0x80, 0x56, 0xb6,
0x64, 0xde, 0xc4, 0x9b, 0x76, 0x12, 0x35, 0xe1, 0x08, 0x42, 0x99, 0xc9, 0x9c, 0xb3, 0x16, 0x51,
0x06, 0x20, 0x42, 0x20, 0xf2, 0x7a, 0xcd, 0x7c, 0x22, 0x69, 0x46, 0x06, 0xed, 0xe7, 0x62, 0x2b,
0xf9, 0x56, 0xb2, 0x80, 0x68, 0x07, 0x69, 0x53, 0xf1, 0x54, 0xf2, 0x25, 0x0b, 0xd5, 0xc6, 0x4f,
0x1c, 0xd4, 0x9b, 0xba, 0x5c, 0xd2, 0x26, 0x32, 0x1b, 0x0b, 0xf1, 0x04, 0xa2, 0xb4, 0x96, 0x2f,
0x45, 0xc5, 0xda, 0x24, 0x66, 0x91, 0xbe, 0x59, 0xa6, 0x6b, 0xc1, 0xfe, 0x4f, 0x7c, 0x7d, 0xb3,
0x9e, 0xe3, 0x05, 0xf4, 0x1e, 0x6a, 0x5e, 0xbd, 0x27, 0xfc, 0xb5, 0xe6, 0x2a, 0x83, 0x73, 0xe7,
0x35, 0xdc, 0x29, 0xbd, 0x62, 0xb5, 0x12, 0x5c, 0x52, 0x10, 0x3f, 0xb1, 0x48, 0xe7, 0xcb, 0xb3,
0x4d, 0x26, 0x29, 0x8a, 0x9f, 0x18, 0x10, 0xcf, 0xa1, 0x6f, 0x15, 0x45, 0x59, 0x6c, 0x05, 0xc7,
0x4b, 0x30, 0x45, 0x29, 0x4d, 0x7f, 0xda, 0x9d, 0x77, 0x67, 0xa6, 0x43, 0x5d, 0x59, 0x62, 0x2b,
0xfc, 0xf0, 0xa0, 0xfb, 0x98, 0xbe, 0x71, 0xe7, 0xe2, 0x2f, 0x9a, 0x3c, 0x83, 0x8e, 0xcc, 0x36,
0x4a, 0x3d, 0xdd, 0x94, 0xb6, 0xcb, 0x1f, 0x62, 0xd7, 0x4d, 0xd4, 0xe8, 0xe6, 0x1c, 0x7a, 0xc6,
0x94, 0x0d, 0x72, 0xe0, 0x2a, 0xbe, 0x80, 0xfe, 0x3d, 0xcf, 0xb9, 0xfc, 0xcd, 0x76, 0x7c, 0x04,
0x03, 0xf7, 0x82, 0x91, 0x98, 0x7f, 0x7a, 0x10, 0xea, 0xe0, 0x02, 0x6f, 0x21, 0xa4, 0x9a, 0x70,
0x68, 0xfb, 0x68, 0x7e, 0x86, 0xf1, 0x68, 0x9f, 0x34, 0xa7, 0xe3, 0x7f, 0x78, 0x0d, 0x81, 0xb6,
0x84, 0x68, 0xf7, 0x8d, 0xd2, 0xc6, 0xc3, 0x3d, 0x6e, 0x77, 0xe4, 0x0e, 0x22, 0x63, 0x02, 0x9d,
0xe8, 0x9e, 0xe9, 0xf1, 0xf1, 0x01, 0xeb, 0x0e, 0x3e, 0x45, 0xf4, 0x97, 0xdf, 0x7c, 0x07, 0x00,
0x00, 0xff, 0xff, 0xed, 0x03, 0x8f, 0x37, 0x00, 0x03, 0x00, 0x00,
0xa9, 0x88, 0x82, 0xc4, 0x05, 0x38, 0x00, 0x84, 0x0b, 0x10, 0xa8, 0x5b, 0x22, 0xa5, 0x75, 0x88,
0x1d, 0x24, 0xce, 0xc1, 0x29, 0xb8, 0x02, 0xa7, 0x23, 0x5e, 0x3b, 0x25, 0xa9, 0xe8, 0x1b, 0x2f,
0xd1, 0xce, 0x6c, 0x76, 0x77, 0x66, 0x12, 0x38, 0xce, 0x72, 0xa1, 0xc4, 0x45, 0x26, 0xa4, 0x92,
0xe6, 0x39, 0x23, 0x06, 0x7d, 0x02, 0xe1, 0x97, 0x03, 0xde, 0x5d, 0x59, 0xe1, 0x00, 0x5a, 0xc9,
0x82, 0x39, 0x13, 0x67, 0xda, 0x89, 0xca, 0x0a, 0x47, 0xe0, 0xab, 0x44, 0xa5, 0x9c, 0xb5, 0x88,
0x32, 0x00, 0x11, 0x3c, 0x99, 0x16, 0x2b, 0xe6, 0x12, 0x49, 0x35, 0x32, 0x68, 0x3f, 0x8b, 0x8d,
0xe2, 0x1b, 0xc5, 0x3c, 0xa2, 0x2b, 0x48, 0x9d, 0x9c, 0xc7, 0x8a, 0x2f, 0x98, 0x5f, 0x76, 0xdc,
0xa8, 0x82, 0xba, 0x53, 0x64, 0x0b, 0xea, 0x04, 0xa6, 0x63, 0x21, 0x1e, 0x41, 0x10, 0x17, 0xea,
0x45, 0xe4, 0xac, 0x4d, 0xcb, 0x2c, 0xd2, 0x97, 0x55, 0xbc, 0x92, 0xec, 0xff, 0xc4, 0xd5, 0x97,
0x75, 0x1d, 0x3e, 0x42, 0xef, 0xbe, 0xe0, 0xf9, 0x7b, 0xc4, 0x5f, 0x0b, 0xfe, 0x8b, 0x87, 0x4a,
0x6d, 0xab, 0xa6, 0xb6, 0xdc, 0x2f, 0x96, 0x4b, 0xc9, 0x15, 0x79, 0x70, 0x23, 0x8b, 0xb4, 0xdf,
0x34, 0x59, 0x27, 0xc6, 0x83, 0x1b, 0x19, 0x10, 0xce, 0xa1, 0x6f, 0x2f, 0xc8, 0x4c, 0x6c, 0x24,
0xc7, 0x73, 0x30, 0xc1, 0x95, 0x57, 0xdc, 0x69, 0x77, 0xde, 0x9d, 0x99, 0x4c, 0x75, 0x84, 0x91,
0x8d, 0xf4, 0xc3, 0x81, 0xee, 0x43, 0xfc, 0xc6, 0xf7, 0xa9, 0xfa, 0x8b, 0x64, 0x4f, 0xa0, 0xa3,
0x92, 0x75, 0xb9, 0x3d, 0x5e, 0x67, 0x36, 0xdb, 0x1f, 0x62, 0x9b, 0x55, 0x50, 0xcb, 0xea, 0x14,
0x7a, 0x46, 0x94, 0x35, 0xb2, 0xa3, 0x2a, 0x3c, 0x83, 0xfe, 0x2d, 0x4f, 0xb9, 0xda, 0x27, 0x3b,
0x3c, 0x80, 0x41, 0xf5, 0x82, 0x59, 0x31, 0xff, 0x74, 0xc0, 0xd7, 0xc6, 0x25, 0x5e, 0x83, 0x4f,
0x31, 0xe1, 0xd0, 0xe6, 0x51, 0xff, 0x2c, 0xe3, 0x51, 0x93, 0x34, 0xd3, 0xe1, 0x3f, 0xbc, 0x04,
0x4f, 0x4b, 0x42, 0xb4, 0xfd, 0x5a, 0x68, 0xe3, 0x61, 0x83, 0xdb, 0x8e, 0xdc, 0x40, 0x60, 0x44,
0x60, 0xb5, 0xb4, 0x21, 0x7a, 0x7c, 0xb8, 0xc3, 0x56, 0x83, 0x4f, 0x01, 0xfd, 0xf5, 0x57, 0xdf,
0x01, 0x00, 0x00, 0xff, 0xff, 0x05, 0x88, 0xa9, 0x15, 0x10, 0x03, 0x00, 0x00,
}

View File

@@ -20,10 +20,14 @@ message Post {
repeated string tags = 8;
}
// Query posts. Acts as a listing when no id or slug provided.
// Gets a single post by id or slug if any of them provided.
message QueryRequest {
string slug = 1;
int64 offset = 2;
int64 limit = 3;
string id = 1;
string slug = 2;
string tag = 3;
int64 offset = 4;
int64 limit = 5;
}
message QueryResponse {
@@ -36,6 +40,8 @@ message SaveRequest {
string slug = 3;
string content = 4;
int64 timestamp = 5;
// When updating a post and wanting to delete all tags,
// send a list of tags with only one member being an empty string [""]
repeated string tags = 6;
}

View File

@@ -15,9 +15,10 @@ import (
const (
slugPrefix = "bySlug"
parentPrefix = "byParent"
resourcePrefix = "byResource"
typePrefix = "byType"
tagCountPrefix = "tagCount"
childrenByTag = "childrenByTag"
)
type Tag struct {
@@ -30,14 +31,14 @@ type Tag struct {
type Tags struct{}
func (t *Tags) Add(ctx context.Context, req *pb.AddRequest, rsp *pb.AddResponse) error {
if len(req.ParentID) == 0 || len(req.Type) == 0 {
return errors.BadRequest("tags.increasecount.input-check", "parent id and type is required")
if len(req.ResourceID) == 0 || len(req.Type) == 0 {
return errors.BadRequest("tags.increasecount.input-check", "resource id and type is required")
}
tagSlug := slug.Make(req.GetTitle())
key := fmt.Sprintf("%v:%v", slugPrefix, tagSlug)
// read by parent ID + slug, the record is identical in boths places anyway
// read by resource ID + slug, the record is identical in boths places anyway
records, err := store.Read(key)
if err != nil && err != store.ErrNotFound {
return err
@@ -62,7 +63,7 @@ func (t *Tags) Add(ctx context.Context, req *pb.AddRequest, rsp *pb.AddResponse)
// increase tag count
err = store.Write(&store.Record{
Key: fmt.Sprintf("%v:%v:%v", tagCountPrefix, tag.Slug, req.GetParentID()),
Key: fmt.Sprintf("%v:%v:%v", tagCountPrefix, tag.Slug, req.GetResourceID()),
Value: nil,
})
if err != nil {
@@ -85,7 +86,7 @@ func (t *Tags) Add(ctx context.Context, req *pb.AddRequest, rsp *pb.AddResponse)
return err
}
err = store.Write(&store.Record{
Key: fmt.Sprintf("%v:%v:%v", parentPrefix, req.GetParentID(), tag.Slug),
Key: fmt.Sprintf("%v:%v:%v", resourcePrefix, req.GetResourceID(), tag.Slug),
Value: tagJSON,
})
if err != nil {
@@ -105,7 +106,7 @@ func (t *Tags) saveTag(tag *Tag) error {
return err
}
// write parentId:slug to enable prefix listing based on type
// write resourceId:slug to enable prefix listing based on type
err = store.Write(&store.Record{
Key: key,
Value: bytes,
@@ -120,15 +121,15 @@ func (t *Tags) saveTag(tag *Tag) error {
}
func (t *Tags) Remove(ctx context.Context, req *pb.RemoveRequest, rsp *pb.RemoveResponse) error {
if len(req.ParentID) == 0 || len(req.Type) == 0 {
return errors.BadRequest("tags.decreaseecount.input-check", "parent id and type is required")
if len(req.ResourceID) == 0 || len(req.Type) == 0 {
return errors.BadRequest("tags.decreaseecount.input-check", "resource id and type is required")
}
tagSlug := slug.Make(req.GetTitle())
parentKey := fmt.Sprintf("%v:%v:%v", parentPrefix, req.GetParentID(), tagSlug)
resourceKey := fmt.Sprintf("%v:%v:%v", resourcePrefix, req.GetResourceID(), tagSlug)
// read by parent ID + slug, the record is identical in boths places anyway
records, err := store.Read(parentKey)
// read by resource ID + slug, the record is identical in boths places anyway
records, err := store.Read(resourceKey)
if err != nil && err != store.ErrNotFound {
return err
}
@@ -146,7 +147,7 @@ func (t *Tags) Remove(ctx context.Context, req *pb.RemoveRequest, rsp *pb.Remove
}
// decrease tag count
err = store.Delete(fmt.Sprintf("%v:%v:%v", tagCountPrefix, tag.Slug, req.GetParentID()))
err = store.Delete(fmt.Sprintf("%v:%v:%v", tagCountPrefix, tag.Slug, req.GetResourceID()))
if err != nil {
return err
}
@@ -163,12 +164,12 @@ func (t *Tags) Remove(ctx context.Context, req *pb.RemoveRequest, rsp *pb.Remove
func (t *Tags) List(ctx context.Context, req *pb.ListRequest, rsp *pb.ListResponse) error {
logger.Info("Received Tags.List request")
key := ""
if len(req.ParentID) > 0 {
key = fmt.Sprintf("%v:%v", parentPrefix, req.ParentID)
if len(req.ResourceID) > 0 {
key = fmt.Sprintf("%v:%v", resourcePrefix, req.ResourceID)
} else if len(req.Type) > 0 {
key = fmt.Sprintf("%v:%v", typePrefix, req.Type)
} else {
return errors.BadRequest("tags.list.input-check", "parent id or type is required")
return errors.BadRequest("tags.list.input-check", "resource id or type is required")
}
records, err := store.Read("", store.Prefix(key))
@@ -200,10 +201,10 @@ func (t *Tags) Update(ctx context.Context, req *pb.UpdateRequest, rsp *pb.Update
}
tagSlug := slug.Make(req.GetTitle())
parentID := fmt.Sprintf("%v:%v", slugPrefix, tagSlug)
resourceID := fmt.Sprintf("%v:%v", slugPrefix, tagSlug)
// read by parent ID + slug, the record is identical in boths places anyway
records, err := store.Read(parentID)
// read by resource ID + slug, the record is identical in boths places anyway
records, err := store.Read(resourceID)
if err != nil {
return err
}

View File

@@ -21,7 +21,7 @@ var _ = math.Inf
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type Tag struct {
// Type is useful for namespacing and listing across parents,
// Type is useful for namespacing and listing across resources,
// ie. list tags for posts, customers etc.
Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"`
Slug string `protobuf:"bytes,2,opt,name=slug,proto3" json:"slug,omitempty"`
@@ -94,9 +94,10 @@ func (m *Tag) GetCount() int64 {
}
type AddRequest struct {
ParentID string `protobuf:"bytes,1,opt,name=parentID,proto3" json:"parentID,omitempty"`
ResourceID string `protobuf:"bytes,1,opt,name=resourceID,proto3" json:"resourceID,omitempty"`
Type string `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"`
Title string `protobuf:"bytes,3,opt,name=title,proto3" json:"title,omitempty"`
ResourceCreated int64 `protobuf:"varint,4,opt,name=resourceCreated,proto3" json:"resourceCreated,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@@ -127,9 +128,9 @@ func (m *AddRequest) XXX_DiscardUnknown() {
var xxx_messageInfo_AddRequest proto.InternalMessageInfo
func (m *AddRequest) GetParentID() string {
func (m *AddRequest) GetResourceID() string {
if m != nil {
return m.ParentID
return m.ResourceID
}
return ""
}
@@ -148,6 +149,13 @@ func (m *AddRequest) GetTitle() string {
return ""
}
func (m *AddRequest) GetResourceCreated() int64 {
if m != nil {
return m.ResourceCreated
}
return 0
}
type AddResponse struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
@@ -180,7 +188,7 @@ func (m *AddResponse) XXX_DiscardUnknown() {
var xxx_messageInfo_AddResponse proto.InternalMessageInfo
type RemoveRequest struct {
ParentID string `protobuf:"bytes,1,opt,name=parentID,proto3" json:"parentID,omitempty"`
ResourceID string `protobuf:"bytes,1,opt,name=resourceID,proto3" json:"resourceID,omitempty"`
Type string `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"`
Title string `protobuf:"bytes,3,opt,name=title,proto3" json:"title,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
@@ -213,9 +221,9 @@ func (m *RemoveRequest) XXX_DiscardUnknown() {
var xxx_messageInfo_RemoveRequest proto.InternalMessageInfo
func (m *RemoveRequest) GetParentID() string {
func (m *RemoveRequest) GetResourceID() string {
if m != nil {
return m.ParentID
return m.ResourceID
}
return ""
}
@@ -351,10 +359,10 @@ func (m *UpdateResponse) XXX_DiscardUnknown() {
var xxx_messageInfo_UpdateResponse proto.InternalMessageInfo
// ListRequest: list either by parent id or type.
// ListRequest: list either by resource id or type.
// Optionally filter by min or max count.
type ListRequest struct {
ParentID string `protobuf:"bytes,1,opt,name=parentID,proto3" json:"parentID,omitempty"`
ResourceID string `protobuf:"bytes,1,opt,name=resourceID,proto3" json:"resourceID,omitempty"`
Type string `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"`
MinCount int64 `protobuf:"varint,3,opt,name=minCount,proto3" json:"minCount,omitempty"`
MaxCount int64 `protobuf:"varint,4,opt,name=maxCount,proto3" json:"maxCount,omitempty"`
@@ -388,9 +396,9 @@ func (m *ListRequest) XXX_DiscardUnknown() {
var xxx_messageInfo_ListRequest proto.InternalMessageInfo
func (m *ListRequest) GetParentID() string {
func (m *ListRequest) GetResourceID() string {
if m != nil {
return m.ParentID
return m.ResourceID
}
return ""
}
@@ -472,28 +480,29 @@ func init() {
}
var fileDescriptor_53c4e9b325a9c45b = []byte{
// 357 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xac, 0x53, 0x41, 0x4b, 0xf3, 0x40,
0x10, 0x6d, 0xbb, 0x69, 0xf9, 0x3a, 0xf9, 0x2a, 0x75, 0xec, 0x21, 0x04, 0x84, 0x92, 0x53, 0x0f,
0xda, 0x42, 0xc5, 0x1f, 0x20, 0x7a, 0x11, 0x3c, 0x05, 0x7b, 0xf2, 0x14, 0x9b, 0xa5, 0x04, 0xda,
0x24, 0x76, 0x37, 0xa2, 0xf8, 0x33, 0xfd, 0x43, 0x66, 0x67, 0x37, 0xeb, 0x46, 0x8b, 0x07, 0xf1,
0x36, 0xf3, 0x26, 0x3b, 0xef, 0xed, 0x7b, 0x1b, 0x18, 0x97, 0xfb, 0x42, 0x16, 0x0b, 0x99, 0x6c,
0xc4, 0x9c, 0x4a, 0xf4, 0x54, 0x1d, 0xbd, 0x01, 0xbb, 0x4f, 0x36, 0x88, 0xe0, 0xc9, 0xd7, 0x92,
0x07, 0xdd, 0x69, 0x77, 0x36, 0x8c, 0xa9, 0x56, 0x98, 0xd8, 0x56, 0x9b, 0xa0, 0xa7, 0x31, 0x55,
0xe3, 0x04, 0xfa, 0x32, 0x93, 0x5b, 0x1e, 0x30, 0x02, 0x75, 0x83, 0x53, 0xf0, 0x53, 0x2e, 0xd6,
0xfb, 0xac, 0x94, 0x59, 0x91, 0x07, 0x1e, 0xcd, 0x5c, 0x48, 0x9d, 0x5b, 0x17, 0x55, 0x2e, 0x83,
0x7e, 0x3d, 0x63, 0xb1, 0x6e, 0xa2, 0x18, 0xe0, 0x2a, 0x4d, 0x63, 0xfe, 0x54, 0x71, 0x21, 0x31,
0x84, 0x7f, 0x65, 0xb2, 0xe7, 0xb9, 0xbc, 0xbd, 0x31, 0x3a, 0x6c, 0x6f, 0xf5, 0xf5, 0x1c, 0x7d,
0x07, 0xb5, 0x44, 0x23, 0xf0, 0x69, 0xa7, 0x28, 0x8b, 0x5c, 0xf0, 0x68, 0x05, 0xa3, 0x98, 0xef,
0x8a, 0x67, 0xfe, 0xb7, 0x2c, 0x63, 0x38, 0x6a, 0xd6, 0x1a, 0xa2, 0x07, 0x18, 0xad, 0xca, 0x34,
0x91, 0x96, 0xe8, 0x90, 0xa5, 0x76, 0x59, 0xef, 0x07, 0xfb, 0xd8, 0x37, 0xfb, 0x14, 0x5d, 0xb3,
0xdc, 0xd0, 0x55, 0xe0, 0xdf, 0x65, 0x42, 0xfe, 0xf6, 0x56, 0xf5, 0xf7, 0xbb, 0x2c, 0xbf, 0xa6,
0x48, 0x18, 0x45, 0x62, 0x7b, 0x9a, 0x25, 0x2f, 0x7a, 0xe6, 0x99, 0x99, 0xe9, 0xa3, 0x73, 0xf8,
0xaf, 0x69, 0xb5, 0x0c, 0x3c, 0x05, 0x7a, 0x46, 0x35, 0x27, 0x9b, 0xf9, 0xcb, 0xe1, 0x9c, 0xde,
0x57, 0xfd, 0xa0, 0x62, 0x82, 0x97, 0xef, 0x5d, 0xf0, 0xea, 0x4e, 0xe0, 0x19, 0xb0, 0x3a, 0x15,
0x1c, 0xeb, 0x0f, 0x3e, 0x43, 0x0f, 0x8f, 0x1d, 0xc4, 0x5c, 0xad, 0x83, 0x97, 0x30, 0xd0, 0xee,
0xe2, 0x89, 0x1e, 0xb7, 0x22, 0x0c, 0x27, 0x6d, 0xd0, 0x1e, 0x5b, 0x80, 0xa7, 0xc4, 0xa1, 0xd9,
0xe9, 0xf8, 0x13, 0xa2, 0x0b, 0xb9, 0x3c, 0xda, 0xd6, 0x86, 0xa7, 0x95, 0x60, 0xc3, 0xf3, 0xc5,
0xf9, 0xce, 0xe3, 0x80, 0x7e, 0xa0, 0x8b, 0x8f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xdd, 0x86, 0xfc,
0x24, 0x54, 0x03, 0x00, 0x00,
// 377 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xac, 0x53, 0x41, 0x4f, 0xf2, 0x40,
0x10, 0xa5, 0x6c, 0x21, 0x1f, 0xd3, 0x0f, 0xc5, 0x91, 0x43, 0xd3, 0x44, 0x43, 0x7a, 0xe2, 0xa0,
0x90, 0x60, 0xfc, 0x01, 0x06, 0x2f, 0x26, 0x9e, 0x1a, 0x3d, 0x18, 0x4f, 0x95, 0x6e, 0x48, 0x13,
0x68, 0x6b, 0x77, 0x6b, 0x24, 0x5e, 0xfc, 0x8f, 0xfe, 0x21, 0xdb, 0xd9, 0x6d, 0x5d, 0x90, 0x78,
0xd1, 0xdb, 0xcc, 0x9b, 0xce, 0xbc, 0xb7, 0x6f, 0xa6, 0x30, 0xc8, 0xf2, 0x54, 0xa6, 0x53, 0x19,
0x2e, 0xc5, 0x84, 0x42, 0xb4, 0xab, 0xd8, 0x7f, 0x03, 0x76, 0x17, 0x2e, 0x11, 0xc1, 0x96, 0x9b,
0x8c, 0xbb, 0xd6, 0xc8, 0x1a, 0xf7, 0x02, 0x8a, 0x2b, 0x4c, 0xac, 0x8a, 0xa5, 0xdb, 0x56, 0x58,
0x15, 0xe3, 0x10, 0x3a, 0x32, 0x96, 0x2b, 0xee, 0x32, 0x02, 0x55, 0x82, 0x23, 0x70, 0x22, 0x2e,
0x16, 0x79, 0x9c, 0xc9, 0x38, 0x4d, 0x5c, 0x9b, 0x6a, 0x26, 0x54, 0xf5, 0x2d, 0xd2, 0x22, 0x91,
0x6e, 0xa7, 0xac, 0xb1, 0x40, 0x25, 0xfe, 0xbb, 0x05, 0x70, 0x15, 0x45, 0x01, 0x7f, 0x2e, 0xb8,
0x90, 0x78, 0x0a, 0x90, 0x73, 0x91, 0x16, 0xf9, 0x82, 0xdf, 0x5c, 0x6b, 0x29, 0x06, 0xd2, 0x88,
0x6c, 0x1b, 0x22, 0xf7, 0x0b, 0x1a, 0xc3, 0x61, 0xdd, 0x37, 0xcf, 0x79, 0x28, 0x79, 0x44, 0xa2,
0x58, 0xb0, 0x0b, 0xfb, 0x7d, 0x70, 0x48, 0x81, 0xc8, 0xd2, 0x44, 0x70, 0xff, 0x01, 0xfa, 0x01,
0x5f, 0xa7, 0x2f, 0xfc, 0xcf, 0x35, 0xf9, 0x03, 0x38, 0xa8, 0x47, 0x6b, 0xb2, 0x47, 0xe8, 0xdf,
0x67, 0x51, 0x29, 0xa3, 0x26, 0xdb, 0xb7, 0x85, 0x66, 0x58, 0xfb, 0x07, 0xc7, 0xd9, 0x37, 0xc7,
0x2b, 0xba, 0x7a, 0xb8, 0xa6, 0xdb, 0x80, 0x73, 0x1b, 0x0b, 0xf9, 0x9b, 0x97, 0x79, 0xf0, 0x6f,
0x1d, 0x27, 0x73, 0xda, 0x24, 0x23, 0x43, 0x9b, 0x9c, 0x6a, 0xe1, 0xab, 0xaa, 0xd9, 0xba, 0xa6,
0x73, 0xff, 0x1c, 0xfe, 0x2b, 0x6a, 0x25, 0x05, 0x4f, 0x80, 0xae, 0xaf, 0x64, 0x65, 0x63, 0x67,
0xd6, 0x9b, 0xd0, 0x59, 0x96, 0x77, 0x18, 0x10, 0x3c, 0xfb, 0xb0, 0xc0, 0x2e, 0x33, 0x81, 0x67,
0xc0, 0xca, 0xed, 0xe0, 0x40, 0x7d, 0xf0, 0x75, 0x2a, 0xde, 0x91, 0x81, 0xe8, 0xe7, 0xb5, 0xf0,
0x12, 0xba, 0xca, 0x61, 0x3c, 0x56, 0xe5, 0xad, 0x55, 0x7a, 0xc3, 0x6d, 0xb0, 0x69, 0x9b, 0x82,
0x5d, 0x89, 0x43, 0x3d, 0xd3, 0xf0, 0xc8, 0x43, 0x13, 0x32, 0x79, 0x94, 0xb5, 0x35, 0xcf, 0xd6,
0x16, 0x6b, 0x9e, 0x1d, 0xf7, 0x5b, 0x4f, 0x5d, 0xfa, 0xef, 0x2e, 0x3e, 0x03, 0x00, 0x00, 0xff,
0xff, 0x9a, 0x72, 0x97, 0xbb, 0x8b, 0x03, 0x00, 0x00,
}

View File

@@ -42,9 +42,9 @@ func NewTagsEndpoints() []*api.Endpoint {
// Client API for Tags service
type TagsService interface {
// Add a tag to a parent
// Add a tag to a resource
Add(ctx context.Context, in *AddRequest, opts ...client.CallOption) (*AddResponse, error)
// Remove a tag from a parent
// Remove a tag from a resource
Remove(ctx context.Context, in *RemoveRequest, opts ...client.CallOption) (*RemoveResponse, error)
// List tags by
List(ctx context.Context, in *ListRequest, opts ...client.CallOption) (*ListResponse, error)
@@ -107,9 +107,9 @@ func (c *tagsService) Update(ctx context.Context, in *UpdateRequest, opts ...cli
// Server API for Tags service
type TagsHandler interface {
// Add a tag to a parent
// Add a tag to a resource
Add(context.Context, *AddRequest, *AddResponse) error
// Remove a tag from a parent
// Remove a tag from a resource
Remove(context.Context, *RemoveRequest, *RemoveResponse) error
// List tags by
List(context.Context, *ListRequest, *ListResponse) error

View File

@@ -3,9 +3,9 @@ syntax = "proto3";
package tags;
service Tags {
// Add a tag to a parent
// Add a tag to a resource
rpc Add(AddRequest) returns (AddResponse) {}
// Remove a tag from a parent
// Remove a tag from a resource
rpc Remove(RemoveRequest) returns (RemoveResponse) {}
// List tags by
rpc List(ListRequest) returns (ListResponse) {}
@@ -14,7 +14,7 @@ service Tags {
}
message Tag {
// Type is useful for namespacing and listing across parents,
// Type is useful for namespacing and listing across resources,
// ie. list tags for posts, customers etc.
string type = 1;
string slug = 2;
@@ -24,15 +24,16 @@ message Tag {
}
message AddRequest {
string parentID = 1;
string resourceID = 1;
string type = 2;
string title = 3;
int64 resourceCreated = 4;
}
message AddResponse{}
message RemoveRequest {
string parentID = 1;
string resourceID = 1;
string type = 2;
string title = 3;
}
@@ -47,10 +48,10 @@ message UpdateRequest {
message UpdateResponse{}
// ListRequest: list either by parent id or type.
// ListRequest: list either by resource id or type.
// Optionally filter by min or max count.
message ListRequest{
string parentID = 1;
string resourceID = 1;
string type = 2;
int64 minCount = 3;
int64 maxCount = 4;

2
go.mod
View File

@@ -6,7 +6,7 @@ require (
github.com/golang/protobuf v1.4.2
github.com/google/uuid v1.1.2
github.com/gosimple/slug v1.9.0
github.com/micro/go-micro/v3 v3.0.0-beta.2
github.com/micro/go-micro/v3 v3.0.0-beta.3
github.com/micro/micro/v3 v3.0.0-beta.6.0.20201015134940-68ac1ddfa568
github.com/miekg/dns v1.1.31 // indirect
github.com/ulikunitz/xz v0.5.8 // indirect

26
go.sum
View File

@@ -102,15 +102,6 @@ github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv
github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=
github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=
github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.18+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f h1:JOrtw2xFKzlg+cbHpyrpLDmnN1HqhBfnX7WDiW7eG2c=
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpu/goacmedns v0.0.1/go.mod h1:sesf/pNnCYwUevQEQfEwY0Y3DydlQWSGZbaMElOWxok=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
@@ -240,7 +231,6 @@ github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gosimple/slug v1.9.0 h1:r5vDcYrFz9BmfIAMC829un9hq7hKM4cHUrsv36LbEqs=
github.com/gosimple/slug v1.9.0/go.mod h1:AMZ+sOVe65uByN3kgEyf9WEBKBCSS+dJjMX9x4vDJbg=
github.com/grpc-ecosystem/go-grpc-middleware v1.1.0/go.mod h1:f5nM7jw/oeRSadq3xCzHAvxcr8HZnzsqU6ILg/0NiiE=
@@ -321,8 +311,8 @@ github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+tw
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-tty v0.0.0-20180219170247-931426f7535a/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/micro/go-micro/v3 v3.0.0-beta.2 h1:LYaTCdw0T8So1EC74F/5c/8oBGT2L25ogTlk0NGo4CA=
github.com/micro/go-micro/v3 v3.0.0-beta.2/go.mod h1:8VQzHPkol6RwDYO4vhxJk3irqb2XDz/mM8S6j7QNXjQ=
github.com/micro/go-micro/v3 v3.0.0-beta.3 h1:4B8UbMg+HRL3Cb7Ly1tIY6c9t0dNjNwEf0Tyh+Jk4zk=
github.com/micro/go-micro/v3 v3.0.0-beta.3/go.mod h1:DiwYchJ8kCWOZVXMsZwuFcNDAdSCS6aEbYfEni4LvK4=
github.com/micro/micro/v3 v3.0.0-beta.6.0.20201015134940-68ac1ddfa568 h1:Nz+CWXbuowAMG8GBJYufNCaWoNW0WqN0IohFAdlMGdY=
github.com/micro/micro/v3 v3.0.0-beta.6.0.20201015134940-68ac1ddfa568/go.mod h1:PK1Fa+RtdIVinOLBPgJQJP5Ov8V0ExXx+ywA80f9TdQ=
github.com/miekg/dns v1.1.15/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
@@ -335,8 +325,6 @@ github.com/minio/minio-go/v7 v7.0.5/go.mod h1:TA0CQCjJZHM5SJj9IjqR0NmpmQJ6bCbXif
github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed/go.mod h1:3rdaFaCv4AyBgu5ALFM0+tSuHrBh6v692nyQe3ikrq0=
github.com/mitchellh/hashstructure v1.0.0 h1:ZkRJX1CyOoTkar7p/mLS5TZU4nJ1Rn/F8u9dGS02Q3Y=
github.com/mitchellh/hashstructure v1.0.0/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
@@ -454,7 +442,6 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
@@ -500,14 +487,7 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/ratelimit v0.0.0-20180316092928-c15da0234277/go.mod h1:2X8KaoNd1J0lZV+PxJk/5+DGbO/tpwLR1m++a7FnB/Y=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
golang.org/x/crypto v0.0.0-20180621125126-a49355c7e3f8/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@@ -688,8 +668,6 @@ golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgw
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=

38
messages/README.md Normal file
View File

@@ -0,0 +1,38 @@
# Messages Service
The messages service is a simplified service for sending messages, much like email. You can send a message using the CLI:
```bash
> micro messages send --to=John --from=Barry --subject=HelloWorld --text="Hello John"
```
And then list the messages a user has recieved:
```bash
> micro messages list --user=John
{
"messages": [
{
"id": "78efd836-ca51-4163-af43-65985f7c6587",
"to": "John",
"from": "Barry",
"subject": "HelloWorld",
"text": "Hello John",
"sent_at": "1602777240"
}
]
}
```
Or lookup an individual email by ID:
```bash
> micro messages read --id=78efd836-ca51-4163-af43-65985f7c6587
{
"message": {
"id": "78efd836-ca51-4163-af43-65985f7c6587",
"to": "John",
"from": "Barry",
"subject": "HelloWorld",
"text": "Hello John",
"sent_at": "1602777240"
}
}
```

121
messages/handler/handler.go Normal file
View File

@@ -0,0 +1,121 @@
package handler
import (
"context"
"encoding/json"
"strings"
"time"
"github.com/google/uuid"
"github.com/micro/micro/v3/service/errors"
"github.com/micro/micro/v3/service/store"
pb "github.com/micro/services/messages/proto"
)
const (
messagePrefix = "message"
joinKey = "/"
)
// New returns an initialized messages handler
func New() pb.MessagesHandler {
return new(handler)
}
type handler struct{}
// Send a message
func (h *handler) Send(ctx context.Context, req *pb.SendRequest, rsp *pb.SendResponse) error {
// validate the request
if len(req.To) == 0 {
return errors.BadRequest("messages.Send.MissingTo", "Missing to")
}
if len(req.From) == 0 {
return errors.BadRequest("messages.Send.MissingFrom", "Missing from")
}
if len(req.Text) == 0 {
return errors.BadRequest("messages.Send.MissingText", "Missing text")
}
// construct the message and marshal it to json
msg := &pb.Message{
Id: uuid.New().String(),
To: req.To,
From: req.From,
Subject: req.Subject,
Text: req.Text,
SentAt: time.Now().Unix(),
}
bytes, err := json.Marshal(msg)
if err != nil {
return errors.BadRequest("messages.Send.Unknown", "Error encoding message")
}
// write the message to the store under the recipients key
key := strings.Join([]string{messagePrefix, req.To, msg.Id}, joinKey)
if err := store.Write(&store.Record{Key: key, Value: bytes}); err != nil {
return errors.BadRequest("messages.Send.Unknown", "Error writing to the store")
}
// write the message to the store under the id (so it can be looked up without needing to know
// the users id)
key = strings.Join([]string{messagePrefix, msg.Id}, joinKey)
if err := store.Write(&store.Record{Key: key, Value: bytes}); err != nil {
return errors.BadRequest("messages.Send.Unknown", "Error writing to the store")
}
return nil
}
// List messages for a user
func (h *handler) List(ctx context.Context, req *pb.ListRequest, rsp *pb.ListResponse) error {
// validate the request
if len(req.User) == 0 {
return errors.BadRequest("messages.List.MissingUser", "Missing user")
}
// query the store for any messages sent to the user
prefix := strings.Join([]string{messagePrefix, req.User}, joinKey)
recs, err := store.Read("", store.Prefix(prefix))
if err != nil {
return errors.BadRequest("messages.List.Unknown", "Error reading from the store")
}
// serialize the result
rsp.Messages = make([]*pb.Message, len(recs))
for i, r := range recs {
var msg pb.Message
if err := json.Unmarshal(r.Value, &msg); err != nil {
return errors.BadRequest("messages.List.Unknown", "Error decoding message")
}
rsp.Messages[i] = &msg
}
return nil
}
// Read a message
func (h *handler) Read(ctx context.Context, req *pb.ReadRequest, rsp *pb.ReadResponse) error {
// validate the request
if len(req.Id) == 0 {
return errors.BadRequest("messages.Read.MissingUser", "Missing user")
}
// query the store
key := strings.Join([]string{messagePrefix, req.Id}, joinKey)
recs, err := store.Read(key)
if err == store.ErrNotFound {
return errors.NotFound("message.Read.InvalidID", "Message not found with ID")
} else if err != nil {
return errors.BadRequest("messages.Read.Unknown", "Error reading from the store")
}
// serialize the response
var msg pb.Message
if err := json.Unmarshal(recs[0].Value, &msg); err != nil {
return errors.BadRequest("messages.Read.Unknown", "Error decoding message")
}
rsp.Message = &msg
return nil
}

View File

@@ -0,0 +1,390 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: github.com/micro/services/messages/proto/messages.proto
package messages
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
math "math"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type Message struct {
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
To string `protobuf:"bytes,2,opt,name=to,proto3" json:"to,omitempty"`
From string `protobuf:"bytes,3,opt,name=from,proto3" json:"from,omitempty"`
Subject string `protobuf:"bytes,4,opt,name=subject,proto3" json:"subject,omitempty"`
Text string `protobuf:"bytes,5,opt,name=text,proto3" json:"text,omitempty"`
SentAt int64 `protobuf:"varint,6,opt,name=sent_at,json=sentAt,proto3" json:"sent_at,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Message) Reset() { *m = Message{} }
func (m *Message) String() string { return proto.CompactTextString(m) }
func (*Message) ProtoMessage() {}
func (*Message) Descriptor() ([]byte, []int) {
return fileDescriptor_926763a275409928, []int{0}
}
func (m *Message) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Message.Unmarshal(m, b)
}
func (m *Message) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Message.Marshal(b, m, deterministic)
}
func (m *Message) XXX_Merge(src proto.Message) {
xxx_messageInfo_Message.Merge(m, src)
}
func (m *Message) XXX_Size() int {
return xxx_messageInfo_Message.Size(m)
}
func (m *Message) XXX_DiscardUnknown() {
xxx_messageInfo_Message.DiscardUnknown(m)
}
var xxx_messageInfo_Message proto.InternalMessageInfo
func (m *Message) GetId() string {
if m != nil {
return m.Id
}
return ""
}
func (m *Message) GetTo() string {
if m != nil {
return m.To
}
return ""
}
func (m *Message) GetFrom() string {
if m != nil {
return m.From
}
return ""
}
func (m *Message) GetSubject() string {
if m != nil {
return m.Subject
}
return ""
}
func (m *Message) GetText() string {
if m != nil {
return m.Text
}
return ""
}
func (m *Message) GetSentAt() int64 {
if m != nil {
return m.SentAt
}
return 0
}
type SendRequest struct {
To string `protobuf:"bytes,1,opt,name=to,proto3" json:"to,omitempty"`
From string `protobuf:"bytes,2,opt,name=from,proto3" json:"from,omitempty"`
Subject string `protobuf:"bytes,3,opt,name=subject,proto3" json:"subject,omitempty"`
Text string `protobuf:"bytes,4,opt,name=text,proto3" json:"text,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *SendRequest) Reset() { *m = SendRequest{} }
func (m *SendRequest) String() string { return proto.CompactTextString(m) }
func (*SendRequest) ProtoMessage() {}
func (*SendRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_926763a275409928, []int{1}
}
func (m *SendRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SendRequest.Unmarshal(m, b)
}
func (m *SendRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_SendRequest.Marshal(b, m, deterministic)
}
func (m *SendRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_SendRequest.Merge(m, src)
}
func (m *SendRequest) XXX_Size() int {
return xxx_messageInfo_SendRequest.Size(m)
}
func (m *SendRequest) XXX_DiscardUnknown() {
xxx_messageInfo_SendRequest.DiscardUnknown(m)
}
var xxx_messageInfo_SendRequest proto.InternalMessageInfo
func (m *SendRequest) GetTo() string {
if m != nil {
return m.To
}
return ""
}
func (m *SendRequest) GetFrom() string {
if m != nil {
return m.From
}
return ""
}
func (m *SendRequest) GetSubject() string {
if m != nil {
return m.Subject
}
return ""
}
func (m *SendRequest) GetText() string {
if m != nil {
return m.Text
}
return ""
}
type SendResponse struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *SendResponse) Reset() { *m = SendResponse{} }
func (m *SendResponse) String() string { return proto.CompactTextString(m) }
func (*SendResponse) ProtoMessage() {}
func (*SendResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_926763a275409928, []int{2}
}
func (m *SendResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SendResponse.Unmarshal(m, b)
}
func (m *SendResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_SendResponse.Marshal(b, m, deterministic)
}
func (m *SendResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_SendResponse.Merge(m, src)
}
func (m *SendResponse) XXX_Size() int {
return xxx_messageInfo_SendResponse.Size(m)
}
func (m *SendResponse) XXX_DiscardUnknown() {
xxx_messageInfo_SendResponse.DiscardUnknown(m)
}
var xxx_messageInfo_SendResponse proto.InternalMessageInfo
type ListRequest struct {
User string `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ListRequest) Reset() { *m = ListRequest{} }
func (m *ListRequest) String() string { return proto.CompactTextString(m) }
func (*ListRequest) ProtoMessage() {}
func (*ListRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_926763a275409928, []int{3}
}
func (m *ListRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ListRequest.Unmarshal(m, b)
}
func (m *ListRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ListRequest.Marshal(b, m, deterministic)
}
func (m *ListRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_ListRequest.Merge(m, src)
}
func (m *ListRequest) XXX_Size() int {
return xxx_messageInfo_ListRequest.Size(m)
}
func (m *ListRequest) XXX_DiscardUnknown() {
xxx_messageInfo_ListRequest.DiscardUnknown(m)
}
var xxx_messageInfo_ListRequest proto.InternalMessageInfo
func (m *ListRequest) GetUser() string {
if m != nil {
return m.User
}
return ""
}
type ListResponse struct {
Messages []*Message `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ListResponse) Reset() { *m = ListResponse{} }
func (m *ListResponse) String() string { return proto.CompactTextString(m) }
func (*ListResponse) ProtoMessage() {}
func (*ListResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_926763a275409928, []int{4}
}
func (m *ListResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ListResponse.Unmarshal(m, b)
}
func (m *ListResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ListResponse.Marshal(b, m, deterministic)
}
func (m *ListResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_ListResponse.Merge(m, src)
}
func (m *ListResponse) XXX_Size() int {
return xxx_messageInfo_ListResponse.Size(m)
}
func (m *ListResponse) XXX_DiscardUnknown() {
xxx_messageInfo_ListResponse.DiscardUnknown(m)
}
var xxx_messageInfo_ListResponse proto.InternalMessageInfo
func (m *ListResponse) GetMessages() []*Message {
if m != nil {
return m.Messages
}
return nil
}
type ReadRequest struct {
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ReadRequest) Reset() { *m = ReadRequest{} }
func (m *ReadRequest) String() string { return proto.CompactTextString(m) }
func (*ReadRequest) ProtoMessage() {}
func (*ReadRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_926763a275409928, []int{5}
}
func (m *ReadRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ReadRequest.Unmarshal(m, b)
}
func (m *ReadRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ReadRequest.Marshal(b, m, deterministic)
}
func (m *ReadRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_ReadRequest.Merge(m, src)
}
func (m *ReadRequest) XXX_Size() int {
return xxx_messageInfo_ReadRequest.Size(m)
}
func (m *ReadRequest) XXX_DiscardUnknown() {
xxx_messageInfo_ReadRequest.DiscardUnknown(m)
}
var xxx_messageInfo_ReadRequest proto.InternalMessageInfo
func (m *ReadRequest) GetId() string {
if m != nil {
return m.Id
}
return ""
}
type ReadResponse struct {
Message *Message `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ReadResponse) Reset() { *m = ReadResponse{} }
func (m *ReadResponse) String() string { return proto.CompactTextString(m) }
func (*ReadResponse) ProtoMessage() {}
func (*ReadResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_926763a275409928, []int{6}
}
func (m *ReadResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ReadResponse.Unmarshal(m, b)
}
func (m *ReadResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ReadResponse.Marshal(b, m, deterministic)
}
func (m *ReadResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_ReadResponse.Merge(m, src)
}
func (m *ReadResponse) XXX_Size() int {
return xxx_messageInfo_ReadResponse.Size(m)
}
func (m *ReadResponse) XXX_DiscardUnknown() {
xxx_messageInfo_ReadResponse.DiscardUnknown(m)
}
var xxx_messageInfo_ReadResponse proto.InternalMessageInfo
func (m *ReadResponse) GetMessage() *Message {
if m != nil {
return m.Message
}
return nil
}
func init() {
proto.RegisterType((*Message)(nil), "messages.Message")
proto.RegisterType((*SendRequest)(nil), "messages.SendRequest")
proto.RegisterType((*SendResponse)(nil), "messages.SendResponse")
proto.RegisterType((*ListRequest)(nil), "messages.ListRequest")
proto.RegisterType((*ListResponse)(nil), "messages.ListResponse")
proto.RegisterType((*ReadRequest)(nil), "messages.ReadRequest")
proto.RegisterType((*ReadResponse)(nil), "messages.ReadResponse")
}
func init() {
proto.RegisterFile("github.com/micro/services/messages/proto/messages.proto", fileDescriptor_926763a275409928)
}
var fileDescriptor_926763a275409928 = []byte{
// 347 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0xcf, 0x4a, 0xf3, 0x40,
0x14, 0xc5, 0x99, 0x64, 0xbe, 0xa6, 0xdf, 0x4d, 0x29, 0x38, 0xa0, 0x0e, 0x82, 0x50, 0xb3, 0x2a,
0x88, 0x0d, 0xb6, 0x14, 0x17, 0xc5, 0x85, 0xae, 0x75, 0x13, 0x77, 0x6e, 0x4a, 0x9b, 0x8e, 0x35,
0x42, 0x3a, 0x35, 0x77, 0x22, 0x3e, 0x80, 0xef, 0xe3, 0x2b, 0xca, 0xfc, 0x49, 0x3b, 0x6a, 0x16,
0xee, 0xee, 0xef, 0xf6, 0x9e, 0x39, 0x3d, 0x87, 0xc0, 0xd5, 0xba, 0x50, 0xcf, 0xf5, 0x72, 0x94,
0xcb, 0x32, 0x2d, 0x8b, 0xbc, 0x92, 0x29, 0x8a, 0xea, 0xad, 0xc8, 0x05, 0xa6, 0xa5, 0x40, 0x5c,
0xac, 0x05, 0xa6, 0xdb, 0x4a, 0x2a, 0xb9, 0xc3, 0x91, 0x41, 0xd6, 0x6d, 0x38, 0xf9, 0x20, 0x10,
0xdd, 0x5b, 0x60, 0x7d, 0x08, 0x8a, 0x15, 0x27, 0x03, 0x32, 0xfc, 0x9f, 0x05, 0xc5, 0x4a, 0xb3,
0x92, 0x3c, 0xb0, 0xac, 0x24, 0x63, 0x40, 0x9f, 0x2a, 0x59, 0xf2, 0xd0, 0x6c, 0xcc, 0xcc, 0x38,
0x44, 0x58, 0x2f, 0x5f, 0x44, 0xae, 0x38, 0x35, 0xeb, 0x06, 0xf5, 0xb5, 0x12, 0xef, 0x8a, 0xff,
0xb3, 0xd7, 0x7a, 0x66, 0xc7, 0x10, 0xa1, 0xd8, 0xa8, 0xf9, 0x42, 0xf1, 0xce, 0x80, 0x0c, 0xc3,
0xac, 0xa3, 0xf1, 0x46, 0x25, 0x73, 0x88, 0x1f, 0xc4, 0x66, 0x95, 0x89, 0xd7, 0x5a, 0xa0, 0x72,
0xce, 0xe4, 0x97, 0x73, 0xd0, 0xee, 0x1c, 0xb6, 0x3b, 0xd3, 0xbd, 0x73, 0xd2, 0x87, 0x9e, 0x35,
0xc0, 0xad, 0xdc, 0xa0, 0x48, 0xce, 0x20, 0xbe, 0x2b, 0x50, 0x35, 0x86, 0x0c, 0x68, 0x8d, 0xa2,
0x72, 0x96, 0x66, 0x4e, 0xae, 0xa1, 0x67, 0x4f, 0xac, 0x84, 0x5d, 0xc0, 0xae, 0x36, 0x4e, 0x06,
0xe1, 0x30, 0x1e, 0x1f, 0x8c, 0x76, 0xbd, 0xba, 0x0e, 0xb3, 0x7d, 0xb3, 0xa7, 0x10, 0x67, 0x62,
0xe1, 0x47, 0xf2, 0xcb, 0x4d, 0x66, 0xd0, 0xb3, 0x3f, 0xbb, 0xd7, 0xcf, 0x21, 0x72, 0x52, 0x73,
0xd4, 0xfa, 0x78, 0x73, 0x31, 0xfe, 0x24, 0xd0, 0x75, 0x4b, 0x64, 0x53, 0xa0, 0x3a, 0x1a, 0x3b,
0xdc, 0x0b, 0xbc, 0x2e, 0x4f, 0x8e, 0x7e, 0xae, 0x9d, 0xe1, 0x14, 0xa8, 0x8e, 0xe7, 0xcb, 0xbc,
0x46, 0x7c, 0xd9, 0xb7, 0x16, 0xa6, 0x40, 0xf5, 0xff, 0xf6, 0x65, 0x5e, 0x4c, 0x5f, 0xe6, 0xc7,
0xbb, 0x9d, 0x3c, 0x5e, 0xfe, 0xf5, 0x63, 0x9d, 0x35, 0xb8, 0xec, 0x18, 0x9e, 0x7c, 0x05, 0x00,
0x00, 0xff, 0xff, 0x90, 0x20, 0xa1, 0xca, 0xe8, 0x02, 0x00, 0x00,
}

View File

@@ -0,0 +1,127 @@
// Code generated by protoc-gen-micro. DO NOT EDIT.
// source: github.com/micro/services/messages/proto/messages.proto
package messages
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
math "math"
)
import (
context "context"
api "github.com/micro/micro/v3/service/api"
client "github.com/micro/micro/v3/service/client"
server "github.com/micro/micro/v3/service/server"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
// Reference imports to suppress errors if they are not otherwise used.
var _ api.Endpoint
var _ context.Context
var _ client.Option
var _ server.Option
// Api Endpoints for Messages service
func NewMessagesEndpoints() []*api.Endpoint {
return []*api.Endpoint{}
}
// Client API for Messages service
type MessagesService interface {
Send(ctx context.Context, in *SendRequest, opts ...client.CallOption) (*SendResponse, error)
List(ctx context.Context, in *ListRequest, opts ...client.CallOption) (*ListResponse, error)
Read(ctx context.Context, in *ReadRequest, opts ...client.CallOption) (*ReadResponse, error)
}
type messagesService struct {
c client.Client
name string
}
func NewMessagesService(name string, c client.Client) MessagesService {
return &messagesService{
c: c,
name: name,
}
}
func (c *messagesService) Send(ctx context.Context, in *SendRequest, opts ...client.CallOption) (*SendResponse, error) {
req := c.c.NewRequest(c.name, "Messages.Send", in)
out := new(SendResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *messagesService) List(ctx context.Context, in *ListRequest, opts ...client.CallOption) (*ListResponse, error) {
req := c.c.NewRequest(c.name, "Messages.List", in)
out := new(ListResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *messagesService) Read(ctx context.Context, in *ReadRequest, opts ...client.CallOption) (*ReadResponse, error) {
req := c.c.NewRequest(c.name, "Messages.Read", in)
out := new(ReadResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for Messages service
type MessagesHandler interface {
Send(context.Context, *SendRequest, *SendResponse) error
List(context.Context, *ListRequest, *ListResponse) error
Read(context.Context, *ReadRequest, *ReadResponse) error
}
func RegisterMessagesHandler(s server.Server, hdlr MessagesHandler, opts ...server.HandlerOption) error {
type messages interface {
Send(ctx context.Context, in *SendRequest, out *SendResponse) error
List(ctx context.Context, in *ListRequest, out *ListResponse) error
Read(ctx context.Context, in *ReadRequest, out *ReadResponse) error
}
type Messages struct {
messages
}
h := &messagesHandler{hdlr}
return s.Handle(s.NewHandler(&Messages{h}, opts...))
}
type messagesHandler struct {
MessagesHandler
}
func (h *messagesHandler) Send(ctx context.Context, in *SendRequest, out *SendResponse) error {
return h.MessagesHandler.Send(ctx, in, out)
}
func (h *messagesHandler) List(ctx context.Context, in *ListRequest, out *ListResponse) error {
return h.MessagesHandler.List(ctx, in, out)
}
func (h *messagesHandler) Read(ctx context.Context, in *ReadRequest, out *ReadResponse) error {
return h.MessagesHandler.Read(ctx, in, out)
}

View File

@@ -0,0 +1,44 @@
syntax = "proto3";
package messages;
option go_package = "github.com/micro/services/messages/proto;messages";
service Messages {
rpc Send(SendRequest) returns (SendResponse);
rpc List(ListRequest) returns (ListResponse);
rpc Read(ReadRequest) returns (ReadResponse);
}
message Message {
string id = 1;
string to = 2;
string from = 3;
string subject = 4;
string text = 5;
int64 sent_at = 6;
}
message SendRequest {
string to = 1;
string from = 2;
string subject = 3;
string text = 4;
}
message SendResponse {}
message ListRequest {
string user = 1;
}
message ListResponse {
repeated Message messages = 1;
}
message ReadRequest {
string id = 1;
}
message ReadResponse {
Message message = 1;
}

View File

@@ -2,4 +2,4 @@ cp services/test/image/.dockerignore .
pushd micro
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build
popd
DOCKER_BUILDKIT=1 docker build -t micro -f test/image/Dockerfile .
DOCKER_BUILDKIT=1 docker build -t micro -f services/test/image/Dockerfile .

View File

@@ -236,4 +236,54 @@ func testPosts(t *test.T) {
t.Fatal(tagsActual.Tags[1], string(outp1), string(outp2))
return
}
// test updating fields fields and removing tags
outp, err = cmd.Exec("posts", "--id=2", "--title=Hi2", "--tags=a", "save")
if err != nil {
t.Fatal(string(outp))
return
}
outp, err = cmd.Exec("tags", "list", "--type=post-tag")
json.Unmarshal(outp, &tagsActual)
if len(tagsActual.Tags) == 0 {
outp1, _ := cmd.Exec("logs", "tags")
t.Fatal(string(append(outp, outp1...)))
return
}
if len(tagsActual.Tags) != 2 {
t.Fatal(tagsActual.Tags)
return
}
for _, tag := range tagsActual.Tags {
if tag.Title == "b" {
if tag.Count != "1" {
t.Fatal("Tag b should have a count 1")
return
}
}
if tag.Title == "a" {
if tag.Count != "2" {
t.Fatal("Tag b should have a count 2")
return
}
}
}
outp, err = cmd.Exec("posts", "--id=2", "query")
if err != nil {
t.Fatal(string(outp))
return
}
json.Unmarshal(outp, &actual)
if len(actual.Posts) == 0 {
t.Fatal(string(outp))
return
}
if actual.Posts[0].Title != "Hi2" ||
actual.Posts[0].Content != "Hi there1" ||
actual.Posts[0].Slug != "hi2" || len(actual.Posts[0].Tags) != 1 {
t.Fatal(actual)
return
}
}