mirror of
https://github.com/kevin-DL/services.git
synced 2026-01-11 10:54:28 +00:00
add comments api
This commit is contained in:
29
comments/Makefile
Normal file
29
comments/Makefile
Normal file
@@ -0,0 +1,29 @@
|
||||
|
||||
GOPATH:=$(shell go env GOPATH)
|
||||
|
||||
.PHONY: api
|
||||
api:
|
||||
protoc --openapi_out=. --proto_path=. proto/comments.proto
|
||||
|
||||
.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
|
||||
go get github.com/micro/micro/v3/cmd/protoc-gen-openapi
|
||||
|
||||
.PHONY: proto
|
||||
proto:
|
||||
protoc --proto_path=. --micro_out=. --go_out=:. proto/comments.proto
|
||||
|
||||
.PHONY: build
|
||||
build:
|
||||
go build -o comments *.go
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
go test -v ./... -cover
|
||||
|
||||
.PHONY: docker
|
||||
docker:
|
||||
docker build . -t comments:latest
|
||||
7
comments/README.md
Normal file
7
comments/README.md
Normal file
@@ -0,0 +1,7 @@
|
||||
Add comments to any App
|
||||
|
||||
# Comments Service
|
||||
|
||||
Add comments and replies to any app. Simple CRUD based storage to build a comments feed
|
||||
anywhere on the web.
|
||||
|
||||
111
comments/examples.json
Normal file
111
comments/examples.json
Normal file
@@ -0,0 +1,111 @@
|
||||
|
||||
{
|
||||
"create": [{
|
||||
"subject": "Create a comment",
|
||||
"description": "Create a simple text based comment",
|
||||
"run_check": false,
|
||||
"request": {
|
||||
"subject": "New Comment",
|
||||
"text": "This is my comment"
|
||||
},
|
||||
"response": {
|
||||
"comment": {
|
||||
"id": "63c0cdf8-2121-11ec-a881-0242e36f037a",
|
||||
"created": "2021-09-29T13:33:03+01:00",
|
||||
"updated": "2021-09-29T13:33:03+01:00",
|
||||
"subject": "New Comment",
|
||||
"text": "This is my comment"
|
||||
}
|
||||
}
|
||||
}],
|
||||
"read": [{
|
||||
"subject": "Read a comment",
|
||||
"description": "Read a comment by its ID",
|
||||
"run_check": false,
|
||||
"request": {
|
||||
"id": "63c0cdf8-2121-11ec-a881-0242e36f037a"
|
||||
},
|
||||
"response": {
|
||||
"comment": {
|
||||
"id": "63c0cdf8-2121-11ec-a881-0242e36f037a",
|
||||
"created": "2021-09-29T13:33:03+01:00",
|
||||
"updated": "2021-09-29T13:33:03+01:00",
|
||||
"subject": "New Comment",
|
||||
"text": "This is my comment"
|
||||
}
|
||||
}
|
||||
}],
|
||||
"list": [{
|
||||
"subject": "List all comments",
|
||||
"description": "List all your available comments",
|
||||
"run_check": false,
|
||||
"request": {},
|
||||
"response": {
|
||||
"comments": [
|
||||
{
|
||||
"id": "63c0cdf8-2121-11ec-a881-0242e36f037a",
|
||||
"created": "2021-09-29T13:33:03+01:00",
|
||||
"updated": "2021-09-29T13:33:03+01:00",
|
||||
"subject": "New Comment",
|
||||
"text": "This is my comment"
|
||||
}
|
||||
]
|
||||
}
|
||||
}],
|
||||
"update": [{
|
||||
"subject": "Update a Comment",
|
||||
"description": "Update the comment subject and text",
|
||||
"run_check": false,
|
||||
"request": {
|
||||
"comment": {
|
||||
"id": "63c0cdf8-2121-11ec-a881-0242e36f037a",
|
||||
"subject": "Update Comment",
|
||||
"text": "Updated comment text"
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"comment": {
|
||||
"id": "63c0cdf8-2121-11ec-a881-0242e36f037a",
|
||||
"created": "2021-09-29T13:33:03+01:00",
|
||||
"updated": "2021-09-29T13:50:20+01:00",
|
||||
"subject": "Update Comment",
|
||||
"text": "Updated comment text"
|
||||
}
|
||||
}
|
||||
}],
|
||||
"delete": [{
|
||||
"subject": "Delete a Comment",
|
||||
"description": "Delete a comment by id",
|
||||
"run_check": false,
|
||||
"request": {
|
||||
"id": "63c0cdf8-2121-11ec-a881-0242e36f037a"
|
||||
},
|
||||
"response": {
|
||||
"comment": {
|
||||
"id": "63c0cdf8-2121-11ec-a881-0242e36f037a",
|
||||
"created": "2021-09-29T13:33:03+01:00",
|
||||
"updated": "2021-09-29T13:50:20+01:00",
|
||||
"subject": "Update Comment",
|
||||
"text": "Updated comment text"
|
||||
}
|
||||
}
|
||||
}],
|
||||
"events": [{
|
||||
"subject": "Subscribe to events",
|
||||
"description": "Subscribe to comment change events",
|
||||
"run_check": false,
|
||||
"request": {
|
||||
"id": "63c0cdf8-2121-11ec-a881-0242e36f037a"
|
||||
},
|
||||
"response": {
|
||||
"event": "deleted",
|
||||
"comment": {
|
||||
"id": "63c0cdf8-2121-11ec-a881-0242e36f037a",
|
||||
"created": "2021-09-29T13:33:03+01:00",
|
||||
"updated": "2021-09-29T13:50:20+01:00",
|
||||
"subject": "Update Comment",
|
||||
"text": "Updated comment text"
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
323
comments/handler/comments.go
Normal file
323
comments/handler/comments.go
Normal file
@@ -0,0 +1,323 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/micro/micro/v3/service/client"
|
||||
"github.com/micro/micro/v3/service/errors"
|
||||
"github.com/micro/micro/v3/service/logger"
|
||||
"github.com/micro/micro/v3/service/store"
|
||||
pb "github.com/micro/services/comments/proto"
|
||||
streamPb "github.com/micro/services/mq/proto"
|
||||
pauth "github.com/micro/services/pkg/auth"
|
||||
adminpb "github.com/micro/services/pkg/service/proto"
|
||||
"github.com/micro/services/pkg/tenant"
|
||||
"google.golang.org/protobuf/types/known/structpb"
|
||||
)
|
||||
|
||||
// New returns an initialized Comments
|
||||
func New(c client.Client) *Comments {
|
||||
return &Comments{
|
||||
Stream: streamPb.NewMqService("mq", c),
|
||||
}
|
||||
}
|
||||
|
||||
// Comments implements the comments proto definition
|
||||
type Comments struct {
|
||||
Stream streamPb.MqService
|
||||
}
|
||||
|
||||
func newMessage(ev map[string]interface{}) *structpb.Struct {
|
||||
st := new(structpb.Struct)
|
||||
b, _ := json.Marshal(ev)
|
||||
json.Unmarshal(b, st)
|
||||
return st
|
||||
}
|
||||
|
||||
// Create inserts a new comment in the store
|
||||
func (h *Comments) Create(ctx context.Context, req *pb.CreateRequest, rsp *pb.CreateResponse) error {
|
||||
if len(req.Subject) == 0 && len(req.Text) == 0 {
|
||||
return errors.BadRequest("comments.create", "missing name and text")
|
||||
}
|
||||
|
||||
tnt, ok := tenant.FromContext(ctx)
|
||||
if !ok {
|
||||
tnt = "default"
|
||||
}
|
||||
|
||||
// generate a key (uuid v4)
|
||||
id, err := uuid.NewUUID()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t := time.Now().Format(time.RFC3339)
|
||||
// set the generated fields on the comment
|
||||
comment := &pb.Comment{
|
||||
Id: id.String(),
|
||||
Created: t,
|
||||
Updated: t,
|
||||
Subject: req.Subject,
|
||||
Text: req.Text,
|
||||
}
|
||||
|
||||
key := path.Join("comment", tnt, id.String())
|
||||
rec := store.NewRecord(key, comment)
|
||||
|
||||
if err = store.Write(rec); err != nil {
|
||||
return errors.InternalServerError("comments.created", "failed to create comment")
|
||||
}
|
||||
|
||||
// return the comment in the response
|
||||
rsp.Comment = comment
|
||||
|
||||
h.Stream.Publish(ctx, &streamPb.PublishRequest{
|
||||
Topic: "comments",
|
||||
Message: newMessage(map[string]interface{}{
|
||||
"event": "create",
|
||||
"comment": comment,
|
||||
}),
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *Comments) Read(ctx context.Context, req *pb.ReadRequest, rsp *pb.ReadResponse) error {
|
||||
if len(req.Id) == 0 {
|
||||
return errors.BadRequest("comments.read", "Missing Comment ID")
|
||||
}
|
||||
|
||||
tnt, ok := tenant.FromContext(ctx)
|
||||
if !ok {
|
||||
tnt = "default"
|
||||
}
|
||||
|
||||
key := path.Join("comment", tnt, req.Id)
|
||||
|
||||
// read the specific comment
|
||||
recs, err := store.Read(key)
|
||||
if err == store.ErrNotFound {
|
||||
return errors.NotFound("comments.read", "Comment not found")
|
||||
} else if err != nil {
|
||||
return errors.InternalServerError("comments.read", "Error reading from store: %v", err.Error())
|
||||
}
|
||||
|
||||
// Decode the comment
|
||||
var comment *pb.Comment
|
||||
if err := recs[0].Decode(&comment); err != nil {
|
||||
return errors.InternalServerError("comments.update", "Error unmarshaling JSON: %v", err.Error())
|
||||
}
|
||||
|
||||
// return the comment
|
||||
rsp.Comment = comment
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Update is a unary API which updates a comment in the store
|
||||
func (h *Comments) Update(ctx context.Context, req *pb.UpdateRequest, rsp *pb.UpdateResponse) error {
|
||||
// Validate the request
|
||||
if req.Comment == nil {
|
||||
return errors.BadRequest("comments.update", "Missing Comment")
|
||||
}
|
||||
if len(req.Comment.Id) == 0 {
|
||||
return errors.BadRequest("comments.update", "Missing Comment ID")
|
||||
}
|
||||
|
||||
tnt, ok := tenant.FromContext(ctx)
|
||||
if !ok {
|
||||
tnt = "default"
|
||||
}
|
||||
|
||||
key := path.Join("comment", tnt, req.Comment.Id)
|
||||
|
||||
// read the specific comment
|
||||
recs, err := store.Read(key)
|
||||
if err == store.ErrNotFound {
|
||||
return errors.NotFound("comments.update", "Comment not found")
|
||||
} else if err != nil {
|
||||
return errors.InternalServerError("comments.update", "Error reading from store: %v", err.Error())
|
||||
}
|
||||
|
||||
// Decode the comment
|
||||
var comment *pb.Comment
|
||||
if err := recs[0].Decode(&comment); err != nil {
|
||||
return errors.InternalServerError("comments.update", "Error unmarshaling JSON: %v", err.Error())
|
||||
}
|
||||
|
||||
// Update the comments name and text
|
||||
comment.Subject = req.Comment.Subject
|
||||
comment.Text = req.Comment.Text
|
||||
comment.Updated = time.Now().Format(time.RFC3339)
|
||||
|
||||
rec := store.NewRecord(key, comment)
|
||||
|
||||
// Write the updated comment to the store
|
||||
if err = store.Write(rec); err != nil {
|
||||
return errors.InternalServerError("comments.update", "Error writing to store: %v", err.Error())
|
||||
}
|
||||
|
||||
h.Stream.Publish(ctx, &streamPb.PublishRequest{
|
||||
Topic: "comments",
|
||||
Message: newMessage(map[string]interface{}{
|
||||
"event": "update",
|
||||
"comment": comment,
|
||||
}),
|
||||
})
|
||||
|
||||
rsp.Comment = comment
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *Comments) Events(ctx context.Context, req *pb.EventsRequest, stream pb.Comments_EventsStream) error {
|
||||
backendStream, err := h.Stream.Subscribe(ctx, &streamPb.SubscribeRequest{
|
||||
Topic: "comments",
|
||||
})
|
||||
if err != nil {
|
||||
return errors.InternalServerError("comments.subscribe", "Failed to subscribe to comments")
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
default:
|
||||
}
|
||||
|
||||
// receive messages from the stream
|
||||
msg, err := backendStream.Recv()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
v, err := msg.Message.MarshalJSON()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
rsp := new(pb.EventsResponse)
|
||||
|
||||
if err := json.Unmarshal(v, rsp); err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
comment := rsp.Comment
|
||||
|
||||
// filter if necessary by id
|
||||
if len(req.Id) > 0 && comment.Id != req.Id {
|
||||
continue
|
||||
}
|
||||
|
||||
// send back the event to the client
|
||||
if err := stream.Send(rsp); err != nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete removes the comment from the store, looking up using ID
|
||||
func (h *Comments) Delete(ctx context.Context, req *pb.DeleteRequest, rsp *pb.DeleteResponse) error {
|
||||
// Validate the request
|
||||
if len(req.Id) == 0 {
|
||||
return errors.BadRequest("comments.delete", "Missing Comment ID")
|
||||
}
|
||||
|
||||
tnt, ok := tenant.FromContext(ctx)
|
||||
if !ok {
|
||||
tnt = "default"
|
||||
}
|
||||
|
||||
key := path.Join("comment", tnt, req.Id)
|
||||
|
||||
// read the specific comment
|
||||
recs, err := store.Read(key)
|
||||
if err == store.ErrNotFound {
|
||||
return nil
|
||||
} else if err != nil {
|
||||
return errors.InternalServerError("comments.delete", "Error reading from store: %v", err.Error())
|
||||
}
|
||||
|
||||
// Decode the comment
|
||||
var comment *pb.Comment
|
||||
if err := recs[0].Decode(&comment); err != nil {
|
||||
return errors.InternalServerError("comments.delete", "Error unmarshaling JSON: %v", err.Error())
|
||||
}
|
||||
|
||||
// now delete it
|
||||
if err := store.Delete(key); err != nil && err != store.ErrNotFound {
|
||||
return errors.InternalServerError("comments.delete", "Failed to delete comment")
|
||||
}
|
||||
|
||||
h.Stream.Publish(ctx, &streamPb.PublishRequest{
|
||||
Topic: "comments",
|
||||
Message: newMessage(map[string]interface{}{
|
||||
"event": "delete",
|
||||
"comment": comment,
|
||||
}),
|
||||
})
|
||||
|
||||
rsp.Comment = comment
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Comment returns all of the comments in the store
|
||||
func (h *Comments) List(ctx context.Context, req *pb.ListRequest, rsp *pb.ListResponse) error {
|
||||
tnt, ok := tenant.FromContext(ctx)
|
||||
if !ok {
|
||||
tnt = "default"
|
||||
}
|
||||
|
||||
key := path.Join("comment", tnt) + "/"
|
||||
|
||||
// Retrieve all of the records in the store
|
||||
recs, err := store.Read(key, store.ReadPrefix())
|
||||
if err != nil {
|
||||
return errors.InternalServerError("comments.list", "Error reading from store: %v", err.Error())
|
||||
}
|
||||
|
||||
// Initialize the response comments slice
|
||||
rsp.Comments = make([]*pb.Comment, len(recs))
|
||||
|
||||
// Unmarshal the comments into the response
|
||||
for i, r := range recs {
|
||||
if err := r.Decode(&rsp.Comments[i]); err != nil {
|
||||
return errors.InternalServerError("comments.list", "Error decoding comment: %v", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *Comments) DeleteData(ctx context.Context, request *adminpb.DeleteDataRequest, response *adminpb.DeleteDataResponse) error {
|
||||
method := "admin.DeleteData"
|
||||
_, err := pauth.VerifyMicroAdmin(ctx, method)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(request.TenantId) < 10 { // deliberate length check so we don't delete all the things
|
||||
return errors.BadRequest(method, "Missing tenant ID")
|
||||
}
|
||||
|
||||
keys, err := store.List(store.ListPrefix(path.Join("comment", request.TenantId) + "/"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, k := range keys {
|
||||
if err := store.Delete(k); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
logger.Infof("Deleted %d keys for %s", len(keys), request.TenantId)
|
||||
return nil
|
||||
}
|
||||
30
comments/main.go
Normal file
30
comments/main.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/micro/micro/v3/service"
|
||||
log "github.com/micro/micro/v3/service/logger"
|
||||
"github.com/micro/services/comments/handler"
|
||||
pb "github.com/micro/services/comments/proto"
|
||||
admin "github.com/micro/services/pkg/service/proto"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// New Service
|
||||
srv := service.New(
|
||||
service.Name("comments"),
|
||||
service.Version("latest"),
|
||||
)
|
||||
|
||||
// Initialise service
|
||||
srv.Init()
|
||||
|
||||
h := handler.New(srv.Client())
|
||||
// Register Handler
|
||||
pb.RegisterCommentsHandler(srv.Server(), h)
|
||||
admin.RegisterAdminHandler(srv.Server(), h)
|
||||
|
||||
// Run service
|
||||
if err := srv.Run(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
1010
comments/proto/comments.pb.go
Normal file
1010
comments/proto/comments.pb.go
Normal file
File diff suppressed because it is too large
Load Diff
253
comments/proto/comments.pb.micro.go
Normal file
253
comments/proto/comments.pb.micro.go
Normal file
@@ -0,0 +1,253 @@
|
||||
// Code generated by protoc-gen-micro. DO NOT EDIT.
|
||||
// source: proto/comments.proto
|
||||
|
||||
package comments
|
||||
|
||||
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 Comments service
|
||||
|
||||
func NewCommentsEndpoints() []*api.Endpoint {
|
||||
return []*api.Endpoint{}
|
||||
}
|
||||
|
||||
// Client API for Comments service
|
||||
|
||||
type CommentsService interface {
|
||||
List(ctx context.Context, in *ListRequest, opts ...client.CallOption) (*ListResponse, error)
|
||||
Create(ctx context.Context, in *CreateRequest, opts ...client.CallOption) (*CreateResponse, error)
|
||||
Read(ctx context.Context, in *ReadRequest, opts ...client.CallOption) (*ReadResponse, error)
|
||||
Delete(ctx context.Context, in *DeleteRequest, opts ...client.CallOption) (*DeleteResponse, error)
|
||||
Update(ctx context.Context, in *UpdateRequest, opts ...client.CallOption) (*UpdateResponse, error)
|
||||
Events(ctx context.Context, in *EventsRequest, opts ...client.CallOption) (Comments_EventsService, error)
|
||||
}
|
||||
|
||||
type commentsService struct {
|
||||
c client.Client
|
||||
name string
|
||||
}
|
||||
|
||||
func NewCommentsService(name string, c client.Client) CommentsService {
|
||||
return &commentsService{
|
||||
c: c,
|
||||
name: name,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *commentsService) List(ctx context.Context, in *ListRequest, opts ...client.CallOption) (*ListResponse, error) {
|
||||
req := c.c.NewRequest(c.name, "Comments.List", in)
|
||||
out := new(ListResponse)
|
||||
err := c.c.Call(ctx, req, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *commentsService) Create(ctx context.Context, in *CreateRequest, opts ...client.CallOption) (*CreateResponse, error) {
|
||||
req := c.c.NewRequest(c.name, "Comments.Create", in)
|
||||
out := new(CreateResponse)
|
||||
err := c.c.Call(ctx, req, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *commentsService) Read(ctx context.Context, in *ReadRequest, opts ...client.CallOption) (*ReadResponse, error) {
|
||||
req := c.c.NewRequest(c.name, "Comments.Read", in)
|
||||
out := new(ReadResponse)
|
||||
err := c.c.Call(ctx, req, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *commentsService) Delete(ctx context.Context, in *DeleteRequest, opts ...client.CallOption) (*DeleteResponse, error) {
|
||||
req := c.c.NewRequest(c.name, "Comments.Delete", in)
|
||||
out := new(DeleteResponse)
|
||||
err := c.c.Call(ctx, req, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *commentsService) Update(ctx context.Context, in *UpdateRequest, opts ...client.CallOption) (*UpdateResponse, error) {
|
||||
req := c.c.NewRequest(c.name, "Comments.Update", in)
|
||||
out := new(UpdateResponse)
|
||||
err := c.c.Call(ctx, req, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *commentsService) Events(ctx context.Context, in *EventsRequest, opts ...client.CallOption) (Comments_EventsService, error) {
|
||||
req := c.c.NewRequest(c.name, "Comments.Events", &EventsRequest{})
|
||||
stream, err := c.c.Stream(ctx, req, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := stream.Send(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &commentsServiceEvents{stream}, nil
|
||||
}
|
||||
|
||||
type Comments_EventsService interface {
|
||||
Context() context.Context
|
||||
SendMsg(interface{}) error
|
||||
RecvMsg(interface{}) error
|
||||
Close() error
|
||||
Recv() (*EventsResponse, error)
|
||||
}
|
||||
|
||||
type commentsServiceEvents struct {
|
||||
stream client.Stream
|
||||
}
|
||||
|
||||
func (x *commentsServiceEvents) Close() error {
|
||||
return x.stream.Close()
|
||||
}
|
||||
|
||||
func (x *commentsServiceEvents) Context() context.Context {
|
||||
return x.stream.Context()
|
||||
}
|
||||
|
||||
func (x *commentsServiceEvents) SendMsg(m interface{}) error {
|
||||
return x.stream.Send(m)
|
||||
}
|
||||
|
||||
func (x *commentsServiceEvents) RecvMsg(m interface{}) error {
|
||||
return x.stream.Recv(m)
|
||||
}
|
||||
|
||||
func (x *commentsServiceEvents) Recv() (*EventsResponse, error) {
|
||||
m := new(EventsResponse)
|
||||
err := x.stream.Recv(m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// Server API for Comments service
|
||||
|
||||
type CommentsHandler interface {
|
||||
List(context.Context, *ListRequest, *ListResponse) error
|
||||
Create(context.Context, *CreateRequest, *CreateResponse) error
|
||||
Read(context.Context, *ReadRequest, *ReadResponse) error
|
||||
Delete(context.Context, *DeleteRequest, *DeleteResponse) error
|
||||
Update(context.Context, *UpdateRequest, *UpdateResponse) error
|
||||
Events(context.Context, *EventsRequest, Comments_EventsStream) error
|
||||
}
|
||||
|
||||
func RegisterCommentsHandler(s server.Server, hdlr CommentsHandler, opts ...server.HandlerOption) error {
|
||||
type comments interface {
|
||||
List(ctx context.Context, in *ListRequest, out *ListResponse) error
|
||||
Create(ctx context.Context, in *CreateRequest, out *CreateResponse) error
|
||||
Read(ctx context.Context, in *ReadRequest, out *ReadResponse) error
|
||||
Delete(ctx context.Context, in *DeleteRequest, out *DeleteResponse) error
|
||||
Update(ctx context.Context, in *UpdateRequest, out *UpdateResponse) error
|
||||
Events(ctx context.Context, stream server.Stream) error
|
||||
}
|
||||
type Comments struct {
|
||||
comments
|
||||
}
|
||||
h := &commentsHandler{hdlr}
|
||||
return s.Handle(s.NewHandler(&Comments{h}, opts...))
|
||||
}
|
||||
|
||||
type commentsHandler struct {
|
||||
CommentsHandler
|
||||
}
|
||||
|
||||
func (h *commentsHandler) List(ctx context.Context, in *ListRequest, out *ListResponse) error {
|
||||
return h.CommentsHandler.List(ctx, in, out)
|
||||
}
|
||||
|
||||
func (h *commentsHandler) Create(ctx context.Context, in *CreateRequest, out *CreateResponse) error {
|
||||
return h.CommentsHandler.Create(ctx, in, out)
|
||||
}
|
||||
|
||||
func (h *commentsHandler) Read(ctx context.Context, in *ReadRequest, out *ReadResponse) error {
|
||||
return h.CommentsHandler.Read(ctx, in, out)
|
||||
}
|
||||
|
||||
func (h *commentsHandler) Delete(ctx context.Context, in *DeleteRequest, out *DeleteResponse) error {
|
||||
return h.CommentsHandler.Delete(ctx, in, out)
|
||||
}
|
||||
|
||||
func (h *commentsHandler) Update(ctx context.Context, in *UpdateRequest, out *UpdateResponse) error {
|
||||
return h.CommentsHandler.Update(ctx, in, out)
|
||||
}
|
||||
|
||||
func (h *commentsHandler) Events(ctx context.Context, stream server.Stream) error {
|
||||
m := new(EventsRequest)
|
||||
if err := stream.Recv(m); err != nil {
|
||||
return err
|
||||
}
|
||||
return h.CommentsHandler.Events(ctx, m, &commentsEventsStream{stream})
|
||||
}
|
||||
|
||||
type Comments_EventsStream interface {
|
||||
Context() context.Context
|
||||
SendMsg(interface{}) error
|
||||
RecvMsg(interface{}) error
|
||||
Close() error
|
||||
Send(*EventsResponse) error
|
||||
}
|
||||
|
||||
type commentsEventsStream struct {
|
||||
stream server.Stream
|
||||
}
|
||||
|
||||
func (x *commentsEventsStream) Close() error {
|
||||
return x.stream.Close()
|
||||
}
|
||||
|
||||
func (x *commentsEventsStream) Context() context.Context {
|
||||
return x.stream.Context()
|
||||
}
|
||||
|
||||
func (x *commentsEventsStream) SendMsg(m interface{}) error {
|
||||
return x.stream.Send(m)
|
||||
}
|
||||
|
||||
func (x *commentsEventsStream) RecvMsg(m interface{}) error {
|
||||
return x.stream.Recv(m)
|
||||
}
|
||||
|
||||
func (x *commentsEventsStream) Send(m *EventsResponse) error {
|
||||
return x.stream.Send(m)
|
||||
}
|
||||
91
comments/proto/comments.proto
Normal file
91
comments/proto/comments.proto
Normal file
@@ -0,0 +1,91 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package comments;
|
||||
|
||||
option go_package = "./proto;comments";
|
||||
|
||||
service Comments {
|
||||
rpc List(ListRequest) returns (ListResponse);
|
||||
rpc Create(CreateRequest) returns (CreateResponse);
|
||||
rpc Read(ReadRequest) returns (ReadResponse);
|
||||
rpc Delete(DeleteRequest) returns (DeleteResponse);
|
||||
rpc Update(UpdateRequest) returns (UpdateResponse);
|
||||
rpc Events(EventsRequest) returns (stream EventsResponse);
|
||||
}
|
||||
|
||||
message Comment {
|
||||
// unique id for the comment, generated if not specified
|
||||
string id = 1;
|
||||
// time at which the comment was created
|
||||
string created = 2;
|
||||
// time at which the comment was updated
|
||||
string updated = 3;
|
||||
// subject of the comment
|
||||
string subject = 4;
|
||||
// text of the comment
|
||||
string text = 5;
|
||||
}
|
||||
|
||||
// Create a new comment
|
||||
message CreateRequest {
|
||||
// comment subject
|
||||
string subject = 1;
|
||||
// comment items
|
||||
string text = 2;
|
||||
}
|
||||
|
||||
message CreateResponse {
|
||||
// The created comment
|
||||
Comment comment = 1;
|
||||
}
|
||||
|
||||
// Read a comment
|
||||
message ReadRequest {
|
||||
// the comment id
|
||||
string id = 1;
|
||||
}
|
||||
|
||||
message ReadResponse {
|
||||
// The comment
|
||||
Comment comment = 1;
|
||||
}
|
||||
|
||||
// Update a comment
|
||||
message UpdateRequest {
|
||||
Comment comment = 1;
|
||||
}
|
||||
|
||||
message UpdateResponse {
|
||||
Comment comment = 1;
|
||||
}
|
||||
|
||||
// Delete a comment
|
||||
message DeleteRequest {
|
||||
// specify the id of the comment
|
||||
string id = 1;
|
||||
}
|
||||
|
||||
message DeleteResponse {
|
||||
Comment comment = 1;
|
||||
}
|
||||
|
||||
// List all the comments
|
||||
message ListRequest {}
|
||||
|
||||
message ListResponse {
|
||||
// the comment of comments
|
||||
repeated Comment comments = 1;
|
||||
}
|
||||
|
||||
// Subscribe to comments events
|
||||
message EventsRequest {
|
||||
// optionally specify a comment id
|
||||
string id = 1;
|
||||
}
|
||||
|
||||
message EventsResponse {
|
||||
// the event which occured; create, delete, update
|
||||
string event = 1;
|
||||
// the comment which the operation occured on
|
||||
Comment comment = 2;
|
||||
}
|
||||
6
comments/publicapi.json
Normal file
6
comments/publicapi.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "comments",
|
||||
"icon": "🗨️",
|
||||
"category": "storage",
|
||||
"display_name": "Comments"
|
||||
}
|
||||
Reference in New Issue
Block a user