init post-service

This commit is contained in:
2023-09-27 16:17:47 +01:00
parent e0bd8ef953
commit 0341a938fd
31 changed files with 5666 additions and 0 deletions

View File

@@ -0,0 +1,179 @@
package rpc
import (
"context"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
emptypb "google.golang.org/protobuf/types/known/emptypb"
"github.com/hexolan/panels/post-service/internal"
pb "github.com/hexolan/panels/post-service/internal/rpc/postv1"
)
type postServer struct {
pb.UnimplementedPostServiceServer
service internal.PostService
}
func NewPostServer(service internal.PostService) postServer {
return postServer{service: service}
}
func (svr *postServer) CreatePost(ctx context.Context, request *pb.CreatePostRequest) (*pb.Post, error) {
// Ensure the required args are provided
if request.GetData() == nil {
return nil, status.Error(codes.InvalidArgument, "malformed request")
}
if request.GetPanelId() == "" {
return nil, status.Error(codes.InvalidArgument, "panel id not provided")
}
if request.GetUserId() == "" {
return nil, status.Error(codes.InvalidArgument, "user id not provided")
}
// Convert to service model
data := pb.PostCreateFromProto(request.GetData())
// Pass to service method for creation
post, err := svr.service.CreatePost(ctx, request.GetPanelId(), request.GetUserId(), data)
if err != nil {
return nil, err
}
return pb.PostToProto(post), nil
}
func (svr *postServer) GetPost(ctx context.Context, request *pb.GetPostRequest) (*pb.Post, error) {
// Ensure the required args are provided
if request.GetId() == "" {
return nil, status.Error(codes.InvalidArgument, "post id not provided")
}
// Convert to business model
id, err := internal.NewPostIdFromRepr(request.GetId())
if err != nil {
return nil, status.Error(codes.InvalidArgument, "invalid post id")
}
// Pass to service method for retrieval
post, err := svr.service.GetPost(ctx, *id)
if err != nil {
return nil, err
}
return pb.PostToProto(post), nil
}
func (svr *postServer) GetPanelPost(ctx context.Context, request *pb.GetPanelPostRequest) (*pb.Post, error) {
// Ensure the required args are provided
if request.GetId() == "" {
return nil, status.Error(codes.InvalidArgument, "post id not provided")
}
if request.GetPanelId() == "" {
return nil, status.Error(codes.InvalidArgument, "panel id not provided")
}
// Convert to service model
id, err := internal.NewPostIdFromRepr(request.GetId())
if err != nil {
return nil, status.Error(codes.InvalidArgument, "invalid post id")
}
// Pass to service method for retrieval
post, err := svr.service.GetPanelPost(ctx, *id, request.GetPanelId())
if err != nil {
return nil, err
}
return pb.PostToProto(post), nil
}
func (svr *postServer) UpdatePost(ctx context.Context, request *pb.UpdatePostRequest) (*pb.Post, error) {
// Ensure the required args are provided
if request.GetId() == "" {
return nil, status.Error(codes.InvalidArgument, "post id not provided")
}
if request.GetData() == nil {
return nil, status.Error(codes.InvalidArgument, "malformed request")
}
// Convert to service models
id, err := internal.NewPostIdFromRepr(request.GetId())
if err != nil {
return nil, status.Error(codes.InvalidArgument, "invalid post id")
}
data := pb.PostUpdateFromProto(request.GetData())
// Pass to service method for update
post, err := svr.service.UpdatePost(ctx, *id, data)
if err != nil {
return nil, err
}
return pb.PostToProto(post), nil
}
func (svr *postServer) DeletePost(ctx context.Context, request *pb.DeletePostRequest) (*emptypb.Empty, error) {
// Ensure the required args are provided
if request.GetId() == "" {
return nil, status.Error(codes.InvalidArgument, "post id not provided")
}
// Convert to service model
id, err := internal.NewPostIdFromRepr(request.GetId())
if err != nil {
return nil, status.Error(codes.InvalidArgument, "invalid post id")
}
// Pass to service method for deletion
err = svr.service.DeletePost(ctx, *id)
if err != nil {
return nil, err
}
return &emptypb.Empty{}, nil
}
func (svr *postServer) GetFeedPosts(ctx context.Context, request *pb.GetFeedPostsRequest) (*pb.FeedPosts, error) {
// Get the posts
posts, err := svr.service.GetFeedPosts(ctx)
if err != nil {
return nil, err
}
return &pb.FeedPosts{Posts: pb.PostsToProto(posts)}, nil
}
func (svr *postServer) GetUserPosts(ctx context.Context, request *pb.GetUserPostsRequest) (*pb.UserPosts, error) {
// Ensure the required args are provided
if request.GetUserId() == "" {
return nil, status.Error(codes.InvalidArgument, "user id not provided")
}
// Get the posts
posts, err := svr.service.GetUserPosts(ctx, request.GetUserId())
if err != nil {
return nil, err
}
return &pb.UserPosts{Posts: pb.PostsToProto(posts)}, nil
}
func (svr *postServer) GetPanelPosts(ctx context.Context, request *pb.GetPanelPostsRequest) (*pb.PanelPosts, error) {
// Ensure the required args are provided
if request.GetPanelId() == "" {
return nil, status.Error(codes.InvalidArgument, "panel id not provided")
}
// Get the posts
posts, err := svr.service.GetPanelPosts(ctx, request.GetPanelId())
if err != nil {
return nil, err
}
return &pb.PanelPosts{Posts: pb.PostsToProto(posts)}, nil
}

View File

@@ -0,0 +1,55 @@
// Handles conversion between Protobuf types and service types
package postv1
import (
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
"github.com/hexolan/panels/post-service/internal"
)
// Post -> Protobuf 'Post'
func PostToProto(post *internal.Post) *Post {
proto := Post{
Id: post.Id.GetReprId(),
PanelId: post.PanelId,
AuthorId: post.AuthorId,
Title: post.Title,
Content: post.Content,
CreatedAt: timestamppb.New(post.CreatedAt.Time),
}
// convert nullable attributes to PB form (if present)
if post.UpdatedAt.Valid == true {
proto.UpdatedAt = timestamppb.New(post.UpdatedAt.Time)
}
return &proto
}
// []Post -> []Protobuf 'Post'
func PostsToProto(posts []*internal.Post) []*Post {
protoPosts := []*Post{}
for _, post := range posts {
protoPosts = append(protoPosts, PostToProto(post))
}
return protoPosts
}
// Protobuf 'PostMutable' -> PostCreate
func PostCreateFromProto(proto *PostMutable) internal.PostCreate {
return internal.PostCreate{
Title: proto.GetTitle(),
Content: proto.GetContent(),
}
}
// Protobuf 'PostMutable' -> PostUpdate
func PostUpdateFromProto(proto *PostMutable) internal.PostUpdate {
return internal.PostUpdate{
Title: proto.Title,
Content: proto.Content,
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,358 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.2.0
// - protoc v4.23.4
// source: post.proto
package postv1
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
emptypb "google.golang.org/protobuf/types/known/emptypb"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
// PostServiceClient is the client API for PostService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type PostServiceClient interface {
CreatePost(ctx context.Context, in *CreatePostRequest, opts ...grpc.CallOption) (*Post, error)
GetPost(ctx context.Context, in *GetPostRequest, opts ...grpc.CallOption) (*Post, error)
GetPanelPost(ctx context.Context, in *GetPanelPostRequest, opts ...grpc.CallOption) (*Post, error)
UpdatePost(ctx context.Context, in *UpdatePostRequest, opts ...grpc.CallOption) (*Post, error)
DeletePost(ctx context.Context, in *DeletePostRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
GetFeedPosts(ctx context.Context, in *GetFeedPostsRequest, opts ...grpc.CallOption) (*FeedPosts, error)
GetUserPosts(ctx context.Context, in *GetUserPostsRequest, opts ...grpc.CallOption) (*UserPosts, error)
GetPanelPosts(ctx context.Context, in *GetPanelPostsRequest, opts ...grpc.CallOption) (*PanelPosts, error)
}
type postServiceClient struct {
cc grpc.ClientConnInterface
}
func NewPostServiceClient(cc grpc.ClientConnInterface) PostServiceClient {
return &postServiceClient{cc}
}
func (c *postServiceClient) CreatePost(ctx context.Context, in *CreatePostRequest, opts ...grpc.CallOption) (*Post, error) {
out := new(Post)
err := c.cc.Invoke(ctx, "/panels.post.v1.PostService/CreatePost", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *postServiceClient) GetPost(ctx context.Context, in *GetPostRequest, opts ...grpc.CallOption) (*Post, error) {
out := new(Post)
err := c.cc.Invoke(ctx, "/panels.post.v1.PostService/GetPost", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *postServiceClient) GetPanelPost(ctx context.Context, in *GetPanelPostRequest, opts ...grpc.CallOption) (*Post, error) {
out := new(Post)
err := c.cc.Invoke(ctx, "/panels.post.v1.PostService/GetPanelPost", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *postServiceClient) UpdatePost(ctx context.Context, in *UpdatePostRequest, opts ...grpc.CallOption) (*Post, error) {
out := new(Post)
err := c.cc.Invoke(ctx, "/panels.post.v1.PostService/UpdatePost", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *postServiceClient) DeletePost(ctx context.Context, in *DeletePostRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
out := new(emptypb.Empty)
err := c.cc.Invoke(ctx, "/panels.post.v1.PostService/DeletePost", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *postServiceClient) GetFeedPosts(ctx context.Context, in *GetFeedPostsRequest, opts ...grpc.CallOption) (*FeedPosts, error) {
out := new(FeedPosts)
err := c.cc.Invoke(ctx, "/panels.post.v1.PostService/GetFeedPosts", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *postServiceClient) GetUserPosts(ctx context.Context, in *GetUserPostsRequest, opts ...grpc.CallOption) (*UserPosts, error) {
out := new(UserPosts)
err := c.cc.Invoke(ctx, "/panels.post.v1.PostService/GetUserPosts", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *postServiceClient) GetPanelPosts(ctx context.Context, in *GetPanelPostsRequest, opts ...grpc.CallOption) (*PanelPosts, error) {
out := new(PanelPosts)
err := c.cc.Invoke(ctx, "/panels.post.v1.PostService/GetPanelPosts", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// PostServiceServer is the server API for PostService service.
// All implementations must embed UnimplementedPostServiceServer
// for forward compatibility
type PostServiceServer interface {
CreatePost(context.Context, *CreatePostRequest) (*Post, error)
GetPost(context.Context, *GetPostRequest) (*Post, error)
GetPanelPost(context.Context, *GetPanelPostRequest) (*Post, error)
UpdatePost(context.Context, *UpdatePostRequest) (*Post, error)
DeletePost(context.Context, *DeletePostRequest) (*emptypb.Empty, error)
GetFeedPosts(context.Context, *GetFeedPostsRequest) (*FeedPosts, error)
GetUserPosts(context.Context, *GetUserPostsRequest) (*UserPosts, error)
GetPanelPosts(context.Context, *GetPanelPostsRequest) (*PanelPosts, error)
mustEmbedUnimplementedPostServiceServer()
}
// UnimplementedPostServiceServer must be embedded to have forward compatible implementations.
type UnimplementedPostServiceServer struct {
}
func (UnimplementedPostServiceServer) CreatePost(context.Context, *CreatePostRequest) (*Post, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreatePost not implemented")
}
func (UnimplementedPostServiceServer) GetPost(context.Context, *GetPostRequest) (*Post, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetPost not implemented")
}
func (UnimplementedPostServiceServer) GetPanelPost(context.Context, *GetPanelPostRequest) (*Post, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetPanelPost not implemented")
}
func (UnimplementedPostServiceServer) UpdatePost(context.Context, *UpdatePostRequest) (*Post, error) {
return nil, status.Errorf(codes.Unimplemented, "method UpdatePost not implemented")
}
func (UnimplementedPostServiceServer) DeletePost(context.Context, *DeletePostRequest) (*emptypb.Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeletePost not implemented")
}
func (UnimplementedPostServiceServer) GetFeedPosts(context.Context, *GetFeedPostsRequest) (*FeedPosts, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetFeedPosts not implemented")
}
func (UnimplementedPostServiceServer) GetUserPosts(context.Context, *GetUserPostsRequest) (*UserPosts, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetUserPosts not implemented")
}
func (UnimplementedPostServiceServer) GetPanelPosts(context.Context, *GetPanelPostsRequest) (*PanelPosts, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetPanelPosts not implemented")
}
func (UnimplementedPostServiceServer) mustEmbedUnimplementedPostServiceServer() {}
// UnsafePostServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to PostServiceServer will
// result in compilation errors.
type UnsafePostServiceServer interface {
mustEmbedUnimplementedPostServiceServer()
}
func RegisterPostServiceServer(s grpc.ServiceRegistrar, srv PostServiceServer) {
s.RegisterService(&PostService_ServiceDesc, srv)
}
func _PostService_CreatePost_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CreatePostRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(PostServiceServer).CreatePost(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/panels.post.v1.PostService/CreatePost",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(PostServiceServer).CreatePost(ctx, req.(*CreatePostRequest))
}
return interceptor(ctx, in, info, handler)
}
func _PostService_GetPost_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetPostRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(PostServiceServer).GetPost(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/panels.post.v1.PostService/GetPost",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(PostServiceServer).GetPost(ctx, req.(*GetPostRequest))
}
return interceptor(ctx, in, info, handler)
}
func _PostService_GetPanelPost_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetPanelPostRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(PostServiceServer).GetPanelPost(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/panels.post.v1.PostService/GetPanelPost",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(PostServiceServer).GetPanelPost(ctx, req.(*GetPanelPostRequest))
}
return interceptor(ctx, in, info, handler)
}
func _PostService_UpdatePost_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(UpdatePostRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(PostServiceServer).UpdatePost(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/panels.post.v1.PostService/UpdatePost",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(PostServiceServer).UpdatePost(ctx, req.(*UpdatePostRequest))
}
return interceptor(ctx, in, info, handler)
}
func _PostService_DeletePost_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(DeletePostRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(PostServiceServer).DeletePost(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/panels.post.v1.PostService/DeletePost",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(PostServiceServer).DeletePost(ctx, req.(*DeletePostRequest))
}
return interceptor(ctx, in, info, handler)
}
func _PostService_GetFeedPosts_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetFeedPostsRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(PostServiceServer).GetFeedPosts(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/panels.post.v1.PostService/GetFeedPosts",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(PostServiceServer).GetFeedPosts(ctx, req.(*GetFeedPostsRequest))
}
return interceptor(ctx, in, info, handler)
}
func _PostService_GetUserPosts_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetUserPostsRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(PostServiceServer).GetUserPosts(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/panels.post.v1.PostService/GetUserPosts",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(PostServiceServer).GetUserPosts(ctx, req.(*GetUserPostsRequest))
}
return interceptor(ctx, in, info, handler)
}
func _PostService_GetPanelPosts_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetPanelPostsRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(PostServiceServer).GetPanelPosts(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/panels.post.v1.PostService/GetPanelPosts",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(PostServiceServer).GetPanelPosts(ctx, req.(*GetPanelPostsRequest))
}
return interceptor(ctx, in, info, handler)
}
// PostService_ServiceDesc is the grpc.ServiceDesc for PostService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var PostService_ServiceDesc = grpc.ServiceDesc{
ServiceName: "panels.post.v1.PostService",
HandlerType: (*PostServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "CreatePost",
Handler: _PostService_CreatePost_Handler,
},
{
MethodName: "GetPost",
Handler: _PostService_GetPost_Handler,
},
{
MethodName: "GetPanelPost",
Handler: _PostService_GetPanelPost_Handler,
},
{
MethodName: "UpdatePost",
Handler: _PostService_UpdatePost_Handler,
},
{
MethodName: "DeletePost",
Handler: _PostService_DeletePost_Handler,
},
{
MethodName: "GetFeedPosts",
Handler: _PostService_GetFeedPosts_Handler,
},
{
MethodName: "GetUserPosts",
Handler: _PostService_GetUserPosts_Handler,
},
{
MethodName: "GetPanelPosts",
Handler: _PostService_GetPanelPosts_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "post.proto",
}

View File

@@ -0,0 +1,77 @@
package rpc
import (
"net"
"context"
"google.golang.org/grpc"
"google.golang.org/grpc/health"
"google.golang.org/grpc/health/grpc_health_v1"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging"
"github.com/hexolan/panels/post-service/internal"
"github.com/hexolan/panels/post-service/internal/rpc/postv1"
)
type RPCServer struct {
grpcSvr *grpc.Server
}
func NewRPCServer(service internal.PostService) *RPCServer {
logger := log.Logger.With().Timestamp().Str("src", "rpc").Logger()
svr := grpc.NewServer(
grpc.ChainUnaryInterceptor(
logging.UnaryServerInterceptor(loggingInterceptor(logger)),
),
grpc.ChainStreamInterceptor(
logging.StreamServerInterceptor(loggingInterceptor(logger)),
),
)
// Post Service Server
postSvr := NewPostServer(service)
postv1.RegisterPostServiceServer(svr, &postSvr)
// Health Check Server
healthSvr := health.NewServer()
grpc_health_v1.RegisterHealthServer(svr, healthSvr)
return &RPCServer{grpcSvr: svr}
}
func loggingInterceptor(logger zerolog.Logger) logging.Logger {
return logging.LoggerFunc(func(ctx context.Context, lvl logging.Level, msg string, fields ...any) {
logger := logger.With().Fields(fields).Logger()
switch lvl {
case logging.LevelError:
logger.Error().Msg(msg)
case logging.LevelWarn:
logger.Warn().Msg(msg)
case logging.LevelInfo:
logger.Info().Msg(msg)
case logging.LevelDebug:
logger.Debug().Msg(msg)
default:
logger.Debug().Interface("unknown-log-level", lvl).Msg(msg)
}
})
}
func (r *RPCServer) Serve() {
// Prepare the listening port
lis, err := net.Listen("tcp", "0.0.0.0:9090")
if err != nil {
log.Panic().Err(err).Caller().Msg("failed to listen on RPC port (:9090)")
}
// Begin serving RPC
log.Info().Str("address", lis.Addr().String()).Msg("Attempting to serve RPC...")
err = r.grpcSvr.Serve(lis)
if err != nil {
log.Panic().Err(err).Caller().Msg("failed to serve RPC")
}
}