Feeds service (#24)

This commit is contained in:
Janos Dobronszki
2020-11-11 17:02:39 +01:00
committed by GitHub
parent b30adabfc3
commit 7a858a62dc
17 changed files with 731 additions and 69 deletions

2
blog/feeds/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
feeds

3
blog/feeds/Dockerfile Normal file
View File

@@ -0,0 +1,3 @@
FROM alpine
ADD feeds /feeds
ENTRYPOINT [ "/feeds" ]

22
blog/feeds/Makefile Normal file
View File

@@ -0,0 +1,22 @@
GOPATH:=$(shell go env GOPATH)
.PHONY: init
init:
go get -u github.com/golang/protobuf/proto
go get -u github.com/golang/protobuf/protoc-gen-go
go get github.com/micro/micro/v3/cmd/protoc-gen-micro
.PHONY: proto
proto:
protoc --proto_path=. --micro_out=. --go_out=:. proto/feeds.proto
.PHONY: build
build:
go build -o feeds *.go
.PHONY: test
test:
go test -v ./... -cover
.PHONY: docker
docker:
docker build . -t feeds:latest

56
blog/feeds/README.md Normal file
View File

@@ -0,0 +1,56 @@
# Feeds Service
This is the Feeds service
Generated with
```
micro new feeds
```
## Usage
```
micro feeds new --name="az" --address=http://a16z.com/feed/
```
```
$ micro posts query
{
"posts": [
{
"id": "39cdfbd6e7534bcd868be9eebbf43f8f",
"title": "Anthony Albanese: From the NYSE to Crypto",
"slug": "anthony-albanese-from-the-nyse-to-crypto",
"created": "1605104742",
"updated": "1605105364",
"metadata": {
"domain": "a16z.com",
"link": "https://a16z.com/2020/10/28/anthony-albanese-from-the-nyse-to-crypto/"
}
},
{
"id": "5e9285c01311704e204322ba564cd99e",
"title": "Journal Club: From Insect Eyes to Nanomaterials",
"slug": "journal-club-from-insect-eyes-to-nanomaterials",
"created": "1605104741",
"updated": "1605105363",
"metadata": {
"domain": "a16z.com",
"link": "https://a16z.com/2020/10/29/journal-club-insect-eyes-nanomaterials/"
}
},
]
}
```
```
make proto
```
Run the service
```
micro run .
```

2
blog/feeds/generate.go Normal file
View File

@@ -0,0 +1,2 @@
package main
//go:generate make proto

View File

@@ -0,0 +1,70 @@
package handler
import (
"context"
"crypto/md5"
"fmt"
"net/url"
"github.com/SlyMarbo/rss"
log "github.com/micro/micro/v3/service/logger"
feeds "github.com/micro/services/blog/feeds/proto"
posts "github.com/micro/services/blog/posts/proto"
)
func (e *Feeds) fetchAll() {
fs := []*feeds.Feed{}
err := e.feeds.List(e.feedsNameIndex.ToQuery(nil), &fs)
if err != nil {
log.Errorf("Error listing feeds: %v", err)
return
}
if len(fs) == 0 {
log.Infof("No feeds to fetch")
return
}
for _, feed := range fs {
log.Infof("Fetching address %v", feed.Address)
fd, err := rss.Fetch(feed.Address)
if err != nil {
log.Errorf("Error fetching address %v: %v", feed.Address, err)
continue
}
domain := getDomain(feed.Address)
for _, item := range fd.Items {
id := fmt.Sprintf("%x", md5.Sum([]byte(item.ID)))
err = e.entries.Save(feeds.Entry{
Id: id,
Url: item.Link,
Title: item.Title,
Domain: domain,
Content: item.Summary,
Date: item.Date.Unix(),
})
if err != nil {
log.Errorf("Error saving item: %v", err)
}
// @todo make this optional
_, err := e.postsService.Save(context.TODO(), &posts.SaveRequest{
Id: id,
Title: item.Title,
Content: item.Content,
Timestamp: item.Date.Unix(),
Metadata: map[string]string{
"domain": domain,
"link": item.Link,
},
})
if err != nil {
log.Errorf("Error saving post: %v", err)
}
}
}
}
func getDomain(address string) string {
uri, _ := url.Parse(address)
return uri.Host
}

View File

@@ -0,0 +1,78 @@
package handler
import (
"context"
"time"
"github.com/micro/dev/model"
log "github.com/micro/micro/v3/service/logger"
"github.com/micro/micro/v3/service/store"
feeds "github.com/micro/services/blog/feeds/proto"
posts "github.com/micro/services/blog/posts/proto"
)
type Feeds struct {
feeds model.Model
entries model.Model
postsService posts.PostsService
feedsIdIndex model.Index
feedsNameIndex model.Index
entriesDateIndex model.Index
}
func NewFeeds(postsService posts.PostsService) *Feeds {
idIndex := model.ByEquality("address")
idIndex.Order.Type = model.OrderTypeUnordered
nameIndex := model.ByEquality("name")
nameIndex.Unique = true
nameIndex.Order.Type = model.OrderTypeUnordered
dateIndex := model.ByEquality("date")
dateIndex.Order.Type = model.OrderTypeDesc
f := &Feeds{
feeds: model.New(
store.DefaultStore,
"feeds",
model.Indexes(nameIndex),
&model.ModelOptions{
Debug: false,
IdIndex: idIndex,
},
),
entries: model.New(
store.DefaultStore,
"entries",
model.Indexes(dateIndex),
&model.ModelOptions{
Debug: false,
},
),
postsService: postsService,
feedsIdIndex: idIndex,
feedsNameIndex: nameIndex,
entriesDateIndex: dateIndex,
}
go f.crawl()
return f
}
func (e *Feeds) crawl() {
e.fetchAll()
tick := time.NewTicker(1 * time.Hour)
for _ = range tick.C {
e.fetchAll()
}
}
func (e *Feeds) New(ctx context.Context, req *feeds.NewRequest, rsp *feeds.NewResponse) error {
log.Info("Received Feeds.New request")
e.feeds.Save(feeds.Feed{
Name: req.Name,
Address: req.Address,
})
return nil
}

27
blog/feeds/main.go Normal file
View File

@@ -0,0 +1,27 @@
package main
import (
pb "github.com/micro/services/blog/feeds/proto"
posts "github.com/micro/services/blog/posts/proto"
"github.com/micro/services/blog/feeds/handler"
"github.com/micro/micro/v3/service"
"github.com/micro/micro/v3/service/logger"
)
func main() {
// Create service
srv := service.New(
service.Name("feeds"),
service.Version("latest"),
)
// Register handler
pb.RegisterFeedsHandler(srv.Server(), handler.NewFeeds(posts.NewPostsService("posts", srv.Client())))
// Run service
if err := srv.Run(); err != nil {
logger.Fatal(err)
}
}

1
blog/feeds/micro.mu Normal file
View File

@@ -0,0 +1 @@
service feeds

View File

@@ -0,0 +1,257 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: proto/feeds.proto
package feeds
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 Feed struct {
// rss feed name
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
// rss feed address
Address string `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Feed) Reset() { *m = Feed{} }
func (m *Feed) String() string { return proto.CompactTextString(m) }
func (*Feed) ProtoMessage() {}
func (*Feed) Descriptor() ([]byte, []int) {
return fileDescriptor_dd517c38176c13bf, []int{0}
}
func (m *Feed) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Feed.Unmarshal(m, b)
}
func (m *Feed) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Feed.Marshal(b, m, deterministic)
}
func (m *Feed) XXX_Merge(src proto.Message) {
xxx_messageInfo_Feed.Merge(m, src)
}
func (m *Feed) XXX_Size() int {
return xxx_messageInfo_Feed.Size(m)
}
func (m *Feed) XXX_DiscardUnknown() {
xxx_messageInfo_Feed.DiscardUnknown(m)
}
var xxx_messageInfo_Feed proto.InternalMessageInfo
func (m *Feed) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *Feed) GetAddress() string {
if m != nil {
return m.Address
}
return ""
}
type Entry struct {
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
Domain string `protobuf:"bytes,2,opt,name=domain,proto3" json:"domain,omitempty"`
Url string `protobuf:"bytes,3,opt,name=url,proto3" json:"url,omitempty"`
Title string `protobuf:"bytes,4,opt,name=title,proto3" json:"title,omitempty"`
Content string `protobuf:"bytes,5,opt,name=content,proto3" json:"content,omitempty"`
Date int64 `protobuf:"varint,6,opt,name=date,proto3" json:"date,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Entry) Reset() { *m = Entry{} }
func (m *Entry) String() string { return proto.CompactTextString(m) }
func (*Entry) ProtoMessage() {}
func (*Entry) Descriptor() ([]byte, []int) {
return fileDescriptor_dd517c38176c13bf, []int{1}
}
func (m *Entry) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Entry.Unmarshal(m, b)
}
func (m *Entry) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Entry.Marshal(b, m, deterministic)
}
func (m *Entry) XXX_Merge(src proto.Message) {
xxx_messageInfo_Entry.Merge(m, src)
}
func (m *Entry) XXX_Size() int {
return xxx_messageInfo_Entry.Size(m)
}
func (m *Entry) XXX_DiscardUnknown() {
xxx_messageInfo_Entry.DiscardUnknown(m)
}
var xxx_messageInfo_Entry proto.InternalMessageInfo
func (m *Entry) GetId() string {
if m != nil {
return m.Id
}
return ""
}
func (m *Entry) GetDomain() string {
if m != nil {
return m.Domain
}
return ""
}
func (m *Entry) GetUrl() string {
if m != nil {
return m.Url
}
return ""
}
func (m *Entry) GetTitle() string {
if m != nil {
return m.Title
}
return ""
}
func (m *Entry) GetContent() string {
if m != nil {
return m.Content
}
return ""
}
func (m *Entry) GetDate() int64 {
if m != nil {
return m.Date
}
return 0
}
type NewRequest struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Address string `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *NewRequest) Reset() { *m = NewRequest{} }
func (m *NewRequest) String() string { return proto.CompactTextString(m) }
func (*NewRequest) ProtoMessage() {}
func (*NewRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_dd517c38176c13bf, []int{2}
}
func (m *NewRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_NewRequest.Unmarshal(m, b)
}
func (m *NewRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_NewRequest.Marshal(b, m, deterministic)
}
func (m *NewRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_NewRequest.Merge(m, src)
}
func (m *NewRequest) XXX_Size() int {
return xxx_messageInfo_NewRequest.Size(m)
}
func (m *NewRequest) XXX_DiscardUnknown() {
xxx_messageInfo_NewRequest.DiscardUnknown(m)
}
var xxx_messageInfo_NewRequest proto.InternalMessageInfo
func (m *NewRequest) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *NewRequest) GetAddress() string {
if m != nil {
return m.Address
}
return ""
}
type NewResponse struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *NewResponse) Reset() { *m = NewResponse{} }
func (m *NewResponse) String() string { return proto.CompactTextString(m) }
func (*NewResponse) ProtoMessage() {}
func (*NewResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_dd517c38176c13bf, []int{3}
}
func (m *NewResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_NewResponse.Unmarshal(m, b)
}
func (m *NewResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_NewResponse.Marshal(b, m, deterministic)
}
func (m *NewResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_NewResponse.Merge(m, src)
}
func (m *NewResponse) XXX_Size() int {
return xxx_messageInfo_NewResponse.Size(m)
}
func (m *NewResponse) XXX_DiscardUnknown() {
xxx_messageInfo_NewResponse.DiscardUnknown(m)
}
var xxx_messageInfo_NewResponse proto.InternalMessageInfo
func init() {
proto.RegisterType((*Feed)(nil), "feeds.Feed")
proto.RegisterType((*Entry)(nil), "feeds.Entry")
proto.RegisterType((*NewRequest)(nil), "feeds.NewRequest")
proto.RegisterType((*NewResponse)(nil), "feeds.NewResponse")
}
func init() {
proto.RegisterFile("proto/feeds.proto", fileDescriptor_dd517c38176c13bf)
}
var fileDescriptor_dd517c38176c13bf = []byte{
// 235 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x94, 0x90, 0xb1, 0x4a, 0x04, 0x31,
0x10, 0x86, 0xdd, 0xdb, 0xcd, 0x8a, 0x73, 0x9c, 0x78, 0x83, 0x48, 0xb0, 0x92, 0xad, 0xac, 0x56,
0x50, 0x41, 0xd0, 0x4e, 0xd0, 0xd2, 0x62, 0x4b, 0xbb, 0xd5, 0x8c, 0xb0, 0x70, 0x97, 0x9c, 0xc9,
0x2c, 0xe2, 0x03, 0xf8, 0xde, 0x26, 0x93, 0x88, 0xb6, 0x76, 0xff, 0xf7, 0x85, 0x3f, 0x93, 0x0c,
0xac, 0x77, 0xde, 0xb1, 0xbb, 0x78, 0x23, 0x32, 0xa1, 0x97, 0x8c, 0x4a, 0xa0, 0xbb, 0x86, 0xe6,
0x31, 0x06, 0x44, 0x68, 0xec, 0xb8, 0x25, 0x5d, 0x9d, 0x55, 0xe7, 0x07, 0x83, 0x64, 0xd4, 0xb0,
0x3f, 0x1a, 0xe3, 0x29, 0x04, 0xbd, 0x10, 0xfd, 0x83, 0xdd, 0x57, 0x05, 0xea, 0xc1, 0xb2, 0xff,
0xc4, 0x43, 0x58, 0x4c, 0xa6, 0xb4, 0x62, 0xc2, 0x13, 0x68, 0x8d, 0xdb, 0x8e, 0x93, 0x2d, 0x95,
0x42, 0x78, 0x04, 0xf5, 0xec, 0x37, 0xba, 0x16, 0x99, 0x22, 0x1e, 0x83, 0xe2, 0x89, 0x37, 0xa4,
0x1b, 0x71, 0x19, 0xd2, 0xcc, 0x57, 0x67, 0x99, 0x2c, 0x6b, 0x95, 0x67, 0x16, 0x4c, 0x2f, 0x34,
0x23, 0x93, 0x6e, 0xa3, 0xae, 0x07, 0xc9, 0xdd, 0x2d, 0xc0, 0x13, 0x7d, 0x0c, 0xf4, 0x3e, 0x53,
0xe0, 0x7f, 0xfe, 0x61, 0x05, 0x4b, 0xe9, 0x86, 0x9d, 0xb3, 0x81, 0x2e, 0x6f, 0x40, 0xa5, 0x45,
0x04, 0xec, 0xa1, 0x8e, 0x1e, 0xd7, 0x7d, 0xde, 0xd6, 0xef, 0xfd, 0xa7, 0xf8, 0x57, 0xe5, 0x5a,
0xb7, 0x77, 0xbf, 0x7a, 0x5e, 0xca, 0x46, 0xef, 0xe4, 0xf0, 0xa5, 0x15, 0xb8, 0xfa, 0x0e, 0x00,
0x00, 0xff, 0xff, 0x04, 0x4b, 0x80, 0xda, 0x73, 0x01, 0x00, 0x00,
}

View File

@@ -0,0 +1,93 @@
// Code generated by protoc-gen-micro. DO NOT EDIT.
// source: proto/feeds.proto
package feeds
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 Feeds service
func NewFeedsEndpoints() []*api.Endpoint {
return []*api.Endpoint{}
}
// Client API for Feeds service
type FeedsService interface {
New(ctx context.Context, in *NewRequest, opts ...client.CallOption) (*NewResponse, error)
}
type feedsService struct {
c client.Client
name string
}
func NewFeedsService(name string, c client.Client) FeedsService {
return &feedsService{
c: c,
name: name,
}
}
func (c *feedsService) New(ctx context.Context, in *NewRequest, opts ...client.CallOption) (*NewResponse, error) {
req := c.c.NewRequest(c.name, "Feeds.New", in)
out := new(NewResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for Feeds service
type FeedsHandler interface {
New(context.Context, *NewRequest, *NewResponse) error
}
func RegisterFeedsHandler(s server.Server, hdlr FeedsHandler, opts ...server.HandlerOption) error {
type feeds interface {
New(ctx context.Context, in *NewRequest, out *NewResponse) error
}
type Feeds struct {
feeds
}
h := &feedsHandler{hdlr}
return s.Handle(s.NewHandler(&Feeds{h}, opts...))
}
type feedsHandler struct {
FeedsHandler
}
func (h *feedsHandler) New(ctx context.Context, in *NewRequest, out *NewResponse) error {
return h.FeedsHandler.New(ctx, in, out)
}

View File

@@ -0,0 +1,33 @@
syntax = "proto3";
package feeds;
option go_package = "proto;feeds";
service Feeds {
rpc New(NewRequest) returns (NewResponse) {}
}
message Feed {
// rss feed name
string name = 1;
// rss feed address
string address = 2;
}
message Entry {
string id = 1;
string domain = 2;
string url = 3;
string title = 4;
string content = 5;
int64 date = 6;
}
message NewRequest {
string name = 1;
string address = 2;
}
message NewResponse {
}

View File

@@ -58,12 +58,13 @@ func (p *Posts) Save(ctx context.Context, req *proto.SaveRequest, rsp *proto.Sav
// If no existing record is found, create a new one
if len(posts) == 0 {
post := &proto.Post{
Id: req.Id,
Title: req.Title,
Content: req.Content,
Tags: req.Tags,
Slug: postSlug,
Created: time.Now().Unix(),
Id: req.Id,
Title: req.Title,
Content: req.Content,
Tags: req.Tags,
Slug: postSlug,
Created: time.Now().Unix(),
Metadata: req.Metadata,
}
err := p.savePost(ctx, nil, post)
if err != nil {
@@ -74,13 +75,14 @@ func (p *Posts) Save(ctx context.Context, req *proto.SaveRequest, rsp *proto.Sav
oldPost := posts[0]
post := &proto.Post{
Id: req.Id,
Title: oldPost.Title,
Content: oldPost.Content,
Slug: oldPost.Slug,
Tags: oldPost.Tags,
Created: oldPost.Created,
Updated: time.Now().Unix(),
Id: req.Id,
Title: oldPost.Title,
Content: oldPost.Content,
Slug: oldPost.Slug,
Tags: oldPost.Tags,
Created: oldPost.Created,
Updated: time.Now().Unix(),
Metadata: req.Metadata,
}
if len(req.Title) > 0 {
post.Title = req.Title
@@ -197,25 +199,12 @@ func (p *Posts) Query(ctx context.Context, req *proto.QueryRequest, rsp *proto.Q
logger.Infof("Listing posts, offset: %v, limit: %v", req.Offset, limit)
}
posts := []*proto.Post{}
err := p.db.List(q, &posts)
if err != nil {
return errors.BadRequest("proto.query.store-read", "Failed to read from store: %v", err.Error())
}
rsp.Posts = make([]*proto.Post, len(posts))
for i, post := range posts {
rsp.Posts[i] = &proto.Post{
Id: post.Id,
Title: post.Title,
Slug: post.Slug,
Content: post.Content,
Tags: post.Tags,
}
}
return nil
return p.db.List(q, &rsp.Posts)
}
func (p *Posts) Delete(ctx context.Context, req *proto.DeleteRequest, rsp *proto.DeleteResponse) error {
logger.Info("Received Post.Delete request")
return p.db.Delete(model.Equals("id", req.Id))
q := model.Equals("id", req.Id)
q.Order.Type = model.OrderTypeUnordered
return p.db.Delete(q)
}

View File

@@ -21,17 +21,18 @@ var _ = math.Inf
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type Post 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"`
Created int64 `protobuf:"varint,5,opt,name=created,proto3" json:"created,omitempty"`
Updated int64 `protobuf:"varint,6,opt,name=updated,proto3" json:"updated,omitempty"`
Author string `protobuf:"bytes,7,opt,name=author,proto3" json:"author,omitempty"`
Tags []string `protobuf:"bytes,8,rep,name=tags,proto3" json:"tags,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
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"`
Created int64 `protobuf:"varint,5,opt,name=created,proto3" json:"created,omitempty"`
Updated int64 `protobuf:"varint,6,opt,name=updated,proto3" json:"updated,omitempty"`
Author string `protobuf:"bytes,7,opt,name=author,proto3" json:"author,omitempty"`
Tags []string `protobuf:"bytes,8,rep,name=tags,proto3" json:"tags,omitempty"`
Metadata map[string]string `protobuf:"bytes,9,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Post) Reset() { *m = Post{} }
@@ -115,6 +116,13 @@ func (m *Post) GetTags() []string {
return nil
}
func (m *Post) GetMetadata() map[string]string {
if m != nil {
return m.Metadata
}
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 {
@@ -235,10 +243,11 @@ type SaveRequest struct {
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:"-"`
XXX_sizecache int32 `json:"-"`
Tags []string `protobuf:"bytes,6,rep,name=tags,proto3" json:"tags,omitempty"`
Metadata map[string]string `protobuf:"bytes,7,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *SaveRequest) Reset() { *m = SaveRequest{} }
@@ -308,6 +317,13 @@ func (m *SaveRequest) GetTags() []string {
return nil
}
func (m *SaveRequest) GetMetadata() map[string]string {
if m != nil {
return m.Metadata
}
return nil
}
type SaveResponse struct {
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
@@ -419,9 +435,11 @@ var xxx_messageInfo_DeleteResponse proto.InternalMessageInfo
func init() {
proto.RegisterType((*Post)(nil), "posts.Post")
proto.RegisterMapType((map[string]string)(nil), "posts.Post.MetadataEntry")
proto.RegisterType((*QueryRequest)(nil), "posts.QueryRequest")
proto.RegisterType((*QueryResponse)(nil), "posts.QueryResponse")
proto.RegisterType((*SaveRequest)(nil), "posts.SaveRequest")
proto.RegisterMapType((map[string]string)(nil), "posts.SaveRequest.MetadataEntry")
proto.RegisterType((*SaveResponse)(nil), "posts.SaveResponse")
proto.RegisterType((*DeleteRequest)(nil), "posts.DeleteRequest")
proto.RegisterType((*DeleteResponse)(nil), "posts.DeleteResponse")
@@ -432,28 +450,32 @@ func init() {
}
var fileDescriptor_e93dc7d934d9dc10 = []byte{
// 367 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xac, 0x53, 0x5d, 0x4e, 0xf2, 0x50,
0x10, 0xfd, 0x4a, 0x7f, 0xf8, 0x18, 0x7e, 0x82, 0x03, 0x9a, 0x1b, 0x62, 0x14, 0xfb, 0xc4, 0x13,
0x46, 0x34, 0x71, 0x03, 0x2e, 0x40, 0xeb, 0x0a, 0xaa, 0x5c, 0xb0, 0x49, 0xe1, 0xd6, 0xde, 0xa9,
0x89, 0xeb, 0x70, 0x15, 0x6e, 0xc1, 0xd5, 0xd9, 0xfb, 0x53, 0x6c, 0x89, 0xbc, 0xf9, 0x36, 0xe7,
0x9c, 0xce, 0xcc, 0x99, 0xd3, 0x5c, 0x38, 0xca, 0x72, 0x41, 0xe2, 0x32, 0x13, 0x92, 0xe4, 0x5c,
0xd7, 0xe8, 0x6b, 0x10, 0x7e, 0x39, 0xe0, 0xdd, 0x97, 0x15, 0x0e, 0xa0, 0x95, 0x2c, 0x99, 0x33,
0x75, 0x66, 0x9d, 0xa8, 0xac, 0x70, 0x0c, 0x3e, 0x25, 0x94, 0x72, 0xd6, 0xd2, 0x94, 0x01, 0x88,
0xe0, 0xc9, 0xb4, 0x58, 0x33, 0x57, 0x93, 0xba, 0x46, 0x06, 0xed, 0x67, 0xb1, 0x25, 0xbe, 0x25,
0xe6, 0x69, 0xba, 0x82, 0x5a, 0xc9, 0x79, 0x4c, 0x7c, 0xc9, 0xfc, 0x52, 0x71, 0xa3, 0x0a, 0x2a,
0xa5, 0xc8, 0x96, 0x5a, 0x09, 0x8c, 0x62, 0x21, 0x9e, 0x40, 0x10, 0x17, 0xf4, 0x22, 0x72, 0xd6,
0xd6, 0xc3, 0x2c, 0x52, 0x9b, 0x29, 0x5e, 0x4b, 0xf6, 0x7f, 0xea, 0xaa, 0xcd, 0xaa, 0x0e, 0x73,
0xe8, 0x3d, 0x14, 0x3c, 0x7f, 0x8f, 0xf8, 0x6b, 0xc1, 0x7f, 0xb9, 0xa1, 0x72, 0xdb, 0xaa, 0xb9,
0x1d, 0x82, 0x5b, 0xf6, 0xda, 0x03, 0x54, 0xa9, 0x36, 0x8a, 0xd5, 0x4a, 0x72, 0x63, 0xdf, 0x8d,
0x2c, 0x52, 0x09, 0xa4, 0xc9, 0x26, 0x21, 0xeb, 0xdd, 0x80, 0x70, 0x01, 0x7d, 0xbb, 0x53, 0x66,
0x62, 0x2b, 0x39, 0x5e, 0x80, 0x89, 0xb2, 0xdc, 0xeb, 0xce, 0xba, 0x8b, 0xee, 0xdc, 0xa4, 0xac,
0x42, 0x8d, 0x6c, 0xc8, 0x1f, 0x0e, 0x74, 0x1f, 0xe3, 0x37, 0x7e, 0xc8, 0xe7, 0x5f, 0x64, 0x7d,
0x0a, 0x1d, 0x4a, 0x36, 0xe5, 0xf4, 0x78, 0x93, 0x59, 0xc7, 0x3f, 0xc4, 0x2e, 0xbd, 0xa0, 0x96,
0xde, 0x19, 0xf4, 0x8c, 0x29, 0x7b, 0xc8, 0x9e, 0xab, 0xf0, 0x1c, 0xfa, 0x77, 0x3c, 0xe5, 0x74,
0xc8, 0x76, 0x38, 0x84, 0x41, 0xf5, 0x81, 0x19, 0xb1, 0xf8, 0x74, 0xc0, 0x57, 0x87, 0x4b, 0xbc,
0x01, 0x5f, 0xc7, 0x84, 0x23, 0x9b, 0x47, 0xfd, 0x47, 0x4d, 0xc6, 0x4d, 0xd2, 0x74, 0x87, 0xff,
0xf0, 0x0a, 0x3c, 0x65, 0x09, 0xd1, 0xea, 0xb5, 0xd0, 0x26, 0xa3, 0x06, 0xb7, 0x6b, 0xb9, 0x85,
0xc0, 0x98, 0xc0, 0x6a, 0x68, 0xc3, 0xf4, 0xe4, 0x78, 0x8f, 0xad, 0x1a, 0x9f, 0x02, 0xfd, 0x0e,
0xae, 0xbf, 0x03, 0x00, 0x00, 0xff, 0xff, 0x5f, 0x8c, 0x67, 0xbb, 0x1c, 0x03, 0x00, 0x00,
// 430 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xb4, 0x53, 0x4b, 0x0f, 0xd2, 0x40,
0x10, 0xb6, 0x4f, 0x60, 0x78, 0x04, 0x07, 0x34, 0x6b, 0x63, 0x14, 0x7b, 0xf2, 0x84, 0x11, 0x35,
0x1a, 0xf5, 0xa8, 0x47, 0x13, 0xad, 0xbf, 0x60, 0x95, 0x05, 0x1b, 0xfb, 0xb2, 0xbb, 0x25, 0xe1,
0x4f, 0xf8, 0x3f, 0xbc, 0xf8, 0x1b, 0xdd, 0x57, 0x6b, 0x8b, 0x72, 0xd3, 0xdb, 0x7c, 0x33, 0xdd,
0x99, 0xef, 0x01, 0x70, 0xb3, 0xaa, 0x4b, 0x51, 0x3e, 0xaa, 0x4a, 0x2e, 0xf8, 0x56, 0xd7, 0x18,
0x68, 0x10, 0xff, 0x74, 0xc1, 0x7f, 0x2f, 0x2b, 0x5c, 0x80, 0x9b, 0xee, 0x89, 0xb3, 0x71, 0x1e,
0x4e, 0x12, 0x59, 0xe1, 0x1a, 0x02, 0x91, 0x8a, 0x8c, 0x11, 0x57, 0xb7, 0x0c, 0x40, 0x04, 0x9f,
0x67, 0xcd, 0x91, 0x78, 0xba, 0xa9, 0x6b, 0x24, 0x30, 0xfa, 0x5c, 0x16, 0x82, 0x15, 0x82, 0xf8,
0xba, 0xdd, 0x42, 0x3d, 0xa9, 0x19, 0x15, 0x6c, 0x4f, 0x02, 0x39, 0xf1, 0x92, 0x16, 0xaa, 0x49,
0x53, 0xed, 0xf5, 0x24, 0x34, 0x13, 0x0b, 0xf1, 0x36, 0x84, 0xb4, 0x11, 0x5f, 0xca, 0x9a, 0x8c,
0xf4, 0x32, 0x8b, 0xd4, 0x65, 0x41, 0x8f, 0x9c, 0x8c, 0x37, 0x9e, 0xba, 0xac, 0x6a, 0x7c, 0x06,
0xe3, 0x9c, 0x09, 0x2a, 0x1f, 0x52, 0x32, 0x91, 0xfd, 0xe9, 0xee, 0xce, 0xd6, 0x68, 0x54, 0x92,
0xb6, 0xef, 0xec, 0xec, 0x6d, 0x21, 0xea, 0x73, 0xd2, 0x7d, 0x1a, 0xbd, 0x82, 0xf9, 0x60, 0x84,
0x4b, 0xf0, 0xbe, 0xb2, 0xb3, 0x15, 0xaf, 0x4a, 0xa5, 0xfe, 0x44, 0xb3, 0xa6, 0x53, 0xaf, 0xc1,
0x4b, 0xf7, 0x85, 0x13, 0xd7, 0x30, 0xfb, 0xd0, 0x30, 0xb9, 0x8f, 0x7d, 0x6b, 0xd8, 0x5f, 0x7c,
0x6b, 0x1d, 0x72, 0x7b, 0x0e, 0xc9, 0xfd, 0x92, 0xaf, 0x35, 0x4d, 0x95, 0x4a, 0x65, 0x79, 0x38,
0x70, 0x66, 0x2c, 0xf3, 0x12, 0x8b, 0xd4, 0xdd, 0x2c, 0xcd, 0x53, 0x61, 0xfd, 0x32, 0x20, 0xde,
0xc1, 0xdc, 0xde, 0xe4, 0x55, 0x59, 0x70, 0x86, 0x0f, 0xc0, 0xc4, 0x27, 0xef, 0x2a, 0xd5, 0xd3,
0x9e, 0xea, 0xc4, 0x06, 0xfb, 0xdd, 0x85, 0xe9, 0x47, 0x7a, 0x62, 0xd7, 0x78, 0xfe, 0x8b, 0x7c,
0xef, 0xc2, 0x44, 0xa4, 0xb9, 0xdc, 0x4e, 0xf3, 0xca, 0x32, 0xfe, 0xdd, 0xe8, 0x12, 0x0b, 0x7b,
0x89, 0xbd, 0xee, 0x25, 0x36, 0xd2, 0xdc, 0x37, 0x96, 0x7b, 0x8f, 0xeb, 0xff, 0x09, 0xee, 0x1e,
0xcc, 0xcc, 0x0d, 0xeb, 0xe1, 0x85, 0x21, 0xf1, 0x7d, 0x98, 0xbf, 0x61, 0x19, 0x13, 0xd7, 0x1c,
0x8b, 0x97, 0xb0, 0x68, 0x3f, 0x30, 0x2b, 0x76, 0x3f, 0x1c, 0x08, 0x94, 0xe7, 0x1c, 0x9f, 0x42,
0xa0, 0x13, 0xc2, 0x95, 0x95, 0xd3, 0xff, 0x8d, 0x44, 0xeb, 0x61, 0xd3, 0xbc, 0x8e, 0x6f, 0xe0,
0x63, 0xf0, 0x15, 0x25, 0xc4, 0x3f, 0x3d, 0x88, 0x56, 0x83, 0x5e, 0xf7, 0xe4, 0x39, 0x84, 0x86,
0x04, 0xb6, 0x4b, 0x07, 0xa4, 0xa3, 0x5b, 0x17, 0xdd, 0xf6, 0xe1, 0xa7, 0x50, 0xff, 0xed, 0x9f,
0xfc, 0x0a, 0x00, 0x00, 0xff, 0xff, 0x50, 0xbf, 0x89, 0x8b, 0x0b, 0x04, 0x00, 0x00,
}

View File

@@ -18,6 +18,7 @@ message Post {
int64 updated = 6;
string author = 7;
repeated string tags = 8;
map<string,string> metadata = 9;
}
// Query posts. Acts as a listing when no id or slug provided.
@@ -43,6 +44,7 @@ message SaveRequest {
// 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;
map<string,string> metadata = 7;
}
message SaveResponse {

1
go.mod
View File

@@ -3,6 +3,7 @@ module github.com/micro/services
go 1.14
require (
github.com/SlyMarbo/rss v1.0.1
github.com/go-sql-driver/mysql v1.5.0 // indirect
github.com/golang/protobuf v1.4.3
github.com/google/uuid v1.1.2

4
go.sum
View File

@@ -53,6 +53,8 @@ github.com/Microsoft/hcsshim v0.8.7-0.20191101173118-65519b62243c/go.mod h1:7xhj
github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87/go.mod h1:iGLljf5n9GjT6kc0HBvyI1nOKnGQbNB66VzSNbK5iks=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/SlyMarbo/rss v1.0.1 h1:fiaIU5UhcXauVOniHOIocWG7uj8Ej6pHNarMGPJilzA=
github.com/SlyMarbo/rss v1.0.1/go.mod h1:JNF+T33oj4m5WLCQXpBTCgO+SxRbYVgdiiimHNgzcbA=
github.com/akamai/AkamaiOPEN-edgegrid-golang v0.9.0/go.mod h1:zpDJeKyp9ScW4NNrbdr+Eyxvry3ilGPewKoXw3XGN1k=
github.com/alangpierce/go-forceexport v0.0.0-20160317203124-8f1d6941cd75/go.mod h1:uAXEEpARkRhCZfEvy/y0Jcc888f9tHCc1W7/UeEtreE=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
@@ -64,6 +66,8 @@ github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYU
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/aws/aws-sdk-go v1.23.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394 h1:OYA+5W64v3OgClL+IrOD63t4i/RW7RqrAVl9LTZ9UqQ=
github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394/go.mod h1:Q8n74mJTIgjX4RBBcHnJ05h//6/k6foqmgE45jTQtxg=
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=