diff --git a/.gitignore b/.gitignore index 378eac2..5acb669 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ build +.vscode diff --git a/README.md b/README.md index 8d8dc9c..6ef5776 100644 --- a/README.md +++ b/README.md @@ -86,3 +86,8 @@ The entire task should only take 2-3 hours, but you’re free to take it as far With this take home task we would like to understand how you tackle tasks like the above. In order for us to easier understand what your reasoning for certain decisions is, please make sure to write good code comments and documentation and maintain a proper Git history with commit messages explaining each step. Feel free to include a list of ideas on how to improve your final project under the assumption that you have unlimited time and resources to spend on it. + +### CosmosSDK 0.45 Documentation + In official cosmos website does not have documentation for 0.44 + Steps described for 0.45 will work for 0.44 also: + https://docs.cosmos.network/v0.45/run-node/keyring.html \ No newline at end of file diff --git a/proto/blog/v1/query.proto b/proto/blog/v1/query.proto index 09a3ce7..787da57 100644 --- a/proto/blog/v1/query.proto +++ b/proto/blog/v1/query.proto @@ -9,6 +9,7 @@ import "blog/v1/types.proto"; // Query defines the gRPC querier service. service Query { rpc AllPosts(QueryAllPostsRequest) returns (QueryAllPostsResponse); + rpc AllPostComments(QueryAllPostCommentsRequest) returns (QueryAllPostCommentsResponse); } message QueryAllPostsRequest { @@ -20,3 +21,16 @@ message QueryAllPostsResponse { cosmos.base.query.v1beta1.PageResponse pagination = 2; } + + +message QueryAllPostCommentsRequest { + string postSlug = 1; + + cosmos.base.query.v1beta1.PageRequest pagination = 2; +} + +message QueryAllPostCommentsResponse { + repeated PostComment postComments = 1; + + cosmos.base.query.v1beta1.PageRequest pagination = 2; +} \ No newline at end of file diff --git a/proto/blog/v1/tx.proto b/proto/blog/v1/tx.proto index 903b077..062e273 100644 --- a/proto/blog/v1/tx.proto +++ b/proto/blog/v1/tx.proto @@ -6,6 +6,7 @@ option go_package = "github.com/regen-network/bec/x/blog"; // Msg is the blog.v1 Msg service service Msg { rpc CreatePost(MsgCreatePost) returns (MsgCreatePostResponse); + rpc CreatePostComment(MsgCreatePostComment) returns (MsgCreatePostCommentResponse); } // MsgCreatePost is the Msg/CreatePost request type. @@ -18,3 +19,13 @@ message MsgCreatePost { // MsgCreatePostResponse is the Msg/CreatePost response type. message MsgCreatePostResponse {} + +// MsgCreatePostComment is the Msg/CreatePostCOmment request type. +message MsgCreatePostComment { + string slug = 1; + string author = 2; + string body = 3; +} + +// MsgCreatePostCommentResponse is the Msg/CreatePostResponse response type. +message MsgCreatePostCommentResponse {} \ No newline at end of file diff --git a/proto/blog/v1/types.proto b/proto/blog/v1/types.proto index c22f1bc..411c369 100644 --- a/proto/blog/v1/types.proto +++ b/proto/blog/v1/types.proto @@ -11,3 +11,11 @@ message Post { string title = 3; string body = 4; } + +message PostComment { + // slug is a short human-readable string used in commect for connecting with post. + // Used as unique. + string slug = 1; + string author = 2; + string body = 3; +} \ No newline at end of file diff --git a/x/blog/client/cli/query.go b/x/blog/client/cli/query.go index 78fbbb3..b8e4239 100644 --- a/x/blog/client/cli/query.go +++ b/x/blog/client/cli/query.go @@ -3,8 +3,6 @@ package cli import ( "fmt" - // "strings" - "github.com/regen-network/bec/x/blog" "github.com/spf13/cobra" @@ -24,6 +22,7 @@ func GetQueryCmd() *cobra.Command { } cmd.AddCommand(CmdAllPosts()) + cmd.AddCommand(CmdAllPostComments()) return cmd } @@ -63,3 +62,43 @@ func CmdAllPosts() *cobra.Command { return cmd } + +func CmdAllPostComments() *cobra.Command { + cmd := &cobra.Command{ + Use: "list-comments [post_slug]", + Short: "list all comments by post", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + argsPostSlug := string(args[0]) + + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + + queryClient := blog.NewQueryClient(clientCtx) + + params := &blog.QueryAllPostCommentsRequest{ + Pagination: pageReq, + PostSlug: argsPostSlug, + } + + res, err := queryClient.AllPostComments(cmd.Context(), params) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + flags.AddPaginationFlagsToCmd(cmd, "blog") + + return cmd +} diff --git a/x/blog/client/cli/tx.go b/x/blog/client/cli/tx.go index 8e9f3d3..76f8b9d 100644 --- a/x/blog/client/cli/tx.go +++ b/x/blog/client/cli/tx.go @@ -22,6 +22,7 @@ func GetTxCmd() *cobra.Command { } cmd.AddCommand(CmdCreatePost()) + cmd.AddCommand(CmdCreatePostComment()) return cmd } @@ -61,3 +62,37 @@ func CmdCreatePost() *cobra.Command { return cmd } + +func CmdCreatePostComment() *cobra.Command { + cmd := &cobra.Command{ + Use: "create-comment [author] [slug] [body]", + Short: "Creates a new comment for post", + Args: cobra.ExactArgs(3), + RunE: func(cmd *cobra.Command, args []string) error { + err := cmd.Flags().Set(flags.FlagFrom, args[0]) + if err != nil { + return err + } + + argsSlug := string(args[1]) + argsBody := string(args[2]) + + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + msg := &blog.MsgCreatePostComment{ + Author: clientCtx.GetFromAddress().String(), + Slug: argsSlug, + Body: argsBody, + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + return cmd +} diff --git a/x/blog/keys.go b/x/blog/keys.go index d20755d..be3785d 100644 --- a/x/blog/keys.go +++ b/x/blog/keys.go @@ -4,7 +4,8 @@ const ( ModuleName = "blog" StoreKey = ModuleName - PostKey = "post" + PostKey = "post" + PostCommentKey = "post_comment" ) func KeyPrefix(p string) []byte { diff --git a/x/blog/query.pb.go b/x/blog/query.pb.go index 25f51cf..cf66764 100644 --- a/x/blog/query.pb.go +++ b/x/blog/query.pb.go @@ -124,34 +124,146 @@ func (m *QueryAllPostsResponse) GetPagination() *query.PageResponse { return nil } +type QueryAllPostCommentsRequest struct { + PostSlug string `protobuf:"bytes,1,opt,name=postSlug,proto3" json:"postSlug,omitempty"` + Pagination *query.PageRequest `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryAllPostCommentsRequest) Reset() { *m = QueryAllPostCommentsRequest{} } +func (m *QueryAllPostCommentsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryAllPostCommentsRequest) ProtoMessage() {} +func (*QueryAllPostCommentsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_eb782a0cfb4fa324, []int{2} +} +func (m *QueryAllPostCommentsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryAllPostCommentsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryAllPostCommentsRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryAllPostCommentsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryAllPostCommentsRequest.Merge(m, src) +} +func (m *QueryAllPostCommentsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryAllPostCommentsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryAllPostCommentsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryAllPostCommentsRequest proto.InternalMessageInfo + +func (m *QueryAllPostCommentsRequest) GetPostSlug() string { + if m != nil { + return m.PostSlug + } + return "" +} + +func (m *QueryAllPostCommentsRequest) GetPagination() *query.PageRequest { + if m != nil { + return m.Pagination + } + return nil +} + +type QueryAllPostCommentsResponse struct { + PostComments []*PostComment `protobuf:"bytes,1,rep,name=postComments,proto3" json:"postComments,omitempty"` + Pagination *query.PageRequest `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryAllPostCommentsResponse) Reset() { *m = QueryAllPostCommentsResponse{} } +func (m *QueryAllPostCommentsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryAllPostCommentsResponse) ProtoMessage() {} +func (*QueryAllPostCommentsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_eb782a0cfb4fa324, []int{3} +} +func (m *QueryAllPostCommentsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryAllPostCommentsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryAllPostCommentsResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryAllPostCommentsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryAllPostCommentsResponse.Merge(m, src) +} +func (m *QueryAllPostCommentsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryAllPostCommentsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryAllPostCommentsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryAllPostCommentsResponse proto.InternalMessageInfo + +func (m *QueryAllPostCommentsResponse) GetPostComments() []*PostComment { + if m != nil { + return m.PostComments + } + return nil +} + +func (m *QueryAllPostCommentsResponse) GetPagination() *query.PageRequest { + if m != nil { + return m.Pagination + } + return nil +} + func init() { proto.RegisterType((*QueryAllPostsRequest)(nil), "blog.v1.QueryAllPostsRequest") proto.RegisterType((*QueryAllPostsResponse)(nil), "blog.v1.QueryAllPostsResponse") + proto.RegisterType((*QueryAllPostCommentsRequest)(nil), "blog.v1.QueryAllPostCommentsRequest") + proto.RegisterType((*QueryAllPostCommentsResponse)(nil), "blog.v1.QueryAllPostCommentsResponse") } func init() { proto.RegisterFile("blog/v1/query.proto", fileDescriptor_eb782a0cfb4fa324) } var fileDescriptor_eb782a0cfb4fa324 = []byte{ - // 301 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x91, 0x41, 0x4b, 0xf3, 0x30, - 0x18, 0xc7, 0x9b, 0xf7, 0x65, 0x2a, 0x19, 0x5e, 0xa2, 0xc2, 0x28, 0x18, 0xc6, 0x06, 0x3a, 0x04, - 0x13, 0x5a, 0xcf, 0x1e, 0xf4, 0xa0, 0x78, 0x9b, 0x3d, 0x7a, 0x10, 0x9a, 0xf2, 0x50, 0x8b, 0x5d, - 0xd3, 0x35, 0x69, 0x75, 0x1f, 0xc0, 0xbb, 0x1f, 0xcb, 0xe3, 0x8e, 0x1e, 0xa5, 0xfd, 0x22, 0x92, - 0xa6, 0xce, 0x29, 0x8a, 0xd7, 0xe7, 0x79, 0xfe, 0xff, 0xdf, 0xaf, 0x0d, 0xde, 0x11, 0xa9, 0x8c, - 0x79, 0xe5, 0xf1, 0x79, 0x09, 0xc5, 0x82, 0xe5, 0x85, 0xd4, 0x92, 0x6c, 0x9a, 0x21, 0xab, 0x3c, - 0xf7, 0x28, 0x92, 0x6a, 0x26, 0x15, 0x17, 0xa1, 0x02, 0x7b, 0xc1, 0x2b, 0x4f, 0x80, 0x0e, 0x3d, - 0x9e, 0x87, 0x71, 0x92, 0x85, 0x3a, 0x91, 0x99, 0x0d, 0xb9, 0xab, 0x26, 0xbd, 0xc8, 0x41, 0xd9, - 0xe1, 0xe8, 0x16, 0xef, 0x5e, 0x9b, 0xd8, 0x59, 0x9a, 0x4e, 0xa5, 0xd2, 0x2a, 0x80, 0x79, 0x09, - 0x4a, 0x93, 0x0b, 0x8c, 0x3f, 0x0b, 0x06, 0x68, 0x88, 0x26, 0x7d, 0xff, 0x80, 0x59, 0x1a, 0x33, - 0x34, 0x66, 0x7d, 0x3a, 0x1a, 0x9b, 0x86, 0x31, 0x74, 0xd9, 0x60, 0x2d, 0x39, 0x7a, 0x42, 0x78, - 0xef, 0x1b, 0x40, 0xe5, 0x32, 0x53, 0x40, 0xc6, 0xb8, 0x97, 0x9b, 0xc1, 0x00, 0x0d, 0xff, 0x4f, - 0xfa, 0xfe, 0x36, 0xeb, 0xbe, 0x89, 0x99, 0xb3, 0xc0, 0xee, 0xc8, 0xe5, 0x17, 0x8d, 0x7f, 0xad, - 0xc6, 0xe1, 0x9f, 0x1a, 0x96, 0xb0, 0xee, 0xe1, 0x07, 0xb8, 0xd7, 0x6a, 0x90, 0x2b, 0xbc, 0xf5, - 0xa1, 0x42, 0xf6, 0x57, 0xcc, 0x9f, 0xfe, 0x81, 0x4b, 0x7f, 0x5b, 0xdb, 0xfe, 0xf3, 0xd3, 0x97, - 0x9a, 0xa2, 0x65, 0x4d, 0xd1, 0x5b, 0x4d, 0xd1, 0x73, 0x43, 0x9d, 0x65, 0x43, 0x9d, 0xd7, 0x86, - 0x3a, 0x37, 0xe3, 0x38, 0xd1, 0x77, 0xa5, 0x60, 0x91, 0x9c, 0xf1, 0x02, 0x62, 0xc8, 0x8e, 0x33, - 0xd0, 0x0f, 0xb2, 0xb8, 0xe7, 0x02, 0x22, 0xfe, 0xc8, 0x4d, 0xaf, 0xd8, 0x68, 0x5f, 0xe0, 0xe4, - 0x3d, 0x00, 0x00, 0xff, 0xff, 0x83, 0x3d, 0x41, 0xc5, 0xe2, 0x01, 0x00, 0x00, + // 387 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x4e, 0xca, 0xc9, 0x4f, + 0xd7, 0x2f, 0x33, 0xd4, 0x2f, 0x2c, 0x4d, 0x2d, 0xaa, 0xd4, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, + 0x62, 0x07, 0x09, 0xea, 0x95, 0x19, 0x4a, 0x69, 0x25, 0xe7, 0x17, 0xe7, 0xe6, 0x17, 0xeb, 0x27, + 0x25, 0x16, 0xa7, 0x42, 0x54, 0xe8, 0x97, 0x19, 0x26, 0xa5, 0x96, 0x24, 0x1a, 0xea, 0x17, 0x24, + 0xa6, 0x67, 0xe6, 0x25, 0x96, 0x64, 0xe6, 0xe7, 0x41, 0x34, 0x49, 0xc1, 0x4d, 0x2a, 0xa9, 0x2c, + 0x48, 0x2d, 0x86, 0x08, 0x2a, 0xc5, 0x71, 0x89, 0x04, 0x82, 0xb4, 0x39, 0xe6, 0xe4, 0x04, 0xe4, + 0x17, 0x97, 0x14, 0x07, 0xa5, 0x16, 0x96, 0xa6, 0x16, 0x97, 0x08, 0xb9, 0x71, 0x71, 0x21, 0x0c, + 0x90, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x36, 0x52, 0xd3, 0x83, 0xd8, 0xa6, 0x07, 0xb2, 0x4d, 0x0f, + 0xe2, 0x1e, 0xa8, 0x6d, 0x7a, 0x01, 0x89, 0xe9, 0xa9, 0x50, 0xbd, 0x41, 0x48, 0x3a, 0x95, 0x5a, + 0x19, 0xb9, 0x44, 0xd1, 0x2c, 0x28, 0x2e, 0xc8, 0xcf, 0x2b, 0x4e, 0x15, 0x52, 0xe6, 0x62, 0x2d, + 0x00, 0x09, 0x48, 0x30, 0x2a, 0x30, 0x6b, 0x70, 0x1b, 0xf1, 0xea, 0x41, 0xfd, 0xa4, 0x07, 0x52, + 0x16, 0x04, 0x91, 0x13, 0x72, 0x47, 0x71, 0x06, 0x13, 0xd8, 0x19, 0xea, 0x04, 0x9d, 0x01, 0xb1, + 0x01, 0xc5, 0x1d, 0x8d, 0x8c, 0x5c, 0xd2, 0xc8, 0xee, 0x70, 0xce, 0xcf, 0xcd, 0x4d, 0xcd, 0x43, + 0xf8, 0x57, 0x8a, 0x8b, 0x03, 0x64, 0x63, 0x70, 0x4e, 0x69, 0x3a, 0xd8, 0xb7, 0x9c, 0x41, 0x70, + 0x3e, 0x5a, 0x58, 0x30, 0x91, 0x1d, 0x16, 0x0b, 0x18, 0xb9, 0x64, 0xb0, 0xbb, 0x01, 0x1a, 0x24, + 0x16, 0x5c, 0x3c, 0x05, 0x48, 0xe2, 0xd0, 0x90, 0x11, 0x41, 0x09, 0x19, 0xa8, 0x64, 0x10, 0x8a, + 0x4a, 0x6a, 0x39, 0xd1, 0x68, 0x13, 0x23, 0x17, 0x2b, 0xd8, 0x89, 0x42, 0x9e, 0x5c, 0x1c, 0xb0, + 0x28, 0x13, 0x92, 0x85, 0xbb, 0x00, 0x5b, 0x5a, 0x91, 0x92, 0xc3, 0x25, 0x0d, 0xf5, 0x56, 0x1c, + 0x17, 0x3f, 0x9a, 0x8f, 0x85, 0x54, 0xb0, 0x6a, 0x41, 0x8b, 0x14, 0x29, 0x55, 0x02, 0xaa, 0x20, + 0xe6, 0x3b, 0xd9, 0x9e, 0x78, 0x24, 0xc7, 0x78, 0xe1, 0x91, 0x1c, 0xe3, 0x83, 0x47, 0x72, 0x8c, + 0x13, 0x1e, 0xcb, 0x31, 0x5c, 0x78, 0x2c, 0xc7, 0x70, 0xe3, 0xb1, 0x1c, 0x43, 0x94, 0x72, 0x7a, + 0x66, 0x49, 0x46, 0x69, 0x92, 0x5e, 0x72, 0x7e, 0xae, 0x7e, 0x51, 0x6a, 0x7a, 0x6a, 0x9e, 0x6e, + 0x5e, 0x6a, 0x49, 0x79, 0x7e, 0x51, 0xb6, 0x7e, 0x52, 0x6a, 0xb2, 0x7e, 0x85, 0x3e, 0xc8, 0xf8, + 0x24, 0x36, 0x70, 0x4e, 0x30, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0xdd, 0xfe, 0x5f, 0xfa, 0x6a, + 0x03, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -167,6 +279,7 @@ const _ = grpc.SupportPackageIsVersion4 // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type QueryClient interface { AllPosts(ctx context.Context, in *QueryAllPostsRequest, opts ...grpc.CallOption) (*QueryAllPostsResponse, error) + AllPostComments(ctx context.Context, in *QueryAllPostCommentsRequest, opts ...grpc.CallOption) (*QueryAllPostCommentsResponse, error) } type queryClient struct { @@ -186,9 +299,19 @@ func (c *queryClient) AllPosts(ctx context.Context, in *QueryAllPostsRequest, op return out, nil } +func (c *queryClient) AllPostComments(ctx context.Context, in *QueryAllPostCommentsRequest, opts ...grpc.CallOption) (*QueryAllPostCommentsResponse, error) { + out := new(QueryAllPostCommentsResponse) + err := c.cc.Invoke(ctx, "/blog.v1.Query/AllPostComments", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // QueryServer is the server API for Query service. type QueryServer interface { AllPosts(context.Context, *QueryAllPostsRequest) (*QueryAllPostsResponse, error) + AllPostComments(context.Context, *QueryAllPostCommentsRequest) (*QueryAllPostCommentsResponse, error) } // UnimplementedQueryServer can be embedded to have forward compatible implementations. @@ -198,6 +321,9 @@ type UnimplementedQueryServer struct { func (*UnimplementedQueryServer) AllPosts(ctx context.Context, req *QueryAllPostsRequest) (*QueryAllPostsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method AllPosts not implemented") } +func (*UnimplementedQueryServer) AllPostComments(ctx context.Context, req *QueryAllPostCommentsRequest) (*QueryAllPostCommentsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method AllPostComments not implemented") +} func RegisterQueryServer(s grpc1.Server, srv QueryServer) { s.RegisterService(&_Query_serviceDesc, srv) @@ -221,6 +347,24 @@ func _Query_AllPosts_Handler(srv interface{}, ctx context.Context, dec func(inte return interceptor(ctx, in, info, handler) } +func _Query_AllPostComments_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryAllPostCommentsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).AllPostComments(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/blog.v1.Query/AllPostComments", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).AllPostComments(ctx, req.(*QueryAllPostCommentsRequest)) + } + return interceptor(ctx, in, info, handler) +} + var _Query_serviceDesc = grpc.ServiceDesc{ ServiceName: "blog.v1.Query", HandlerType: (*QueryServer)(nil), @@ -229,6 +373,10 @@ var _Query_serviceDesc = grpc.ServiceDesc{ MethodName: "AllPosts", Handler: _Query_AllPosts_Handler, }, + { + MethodName: "AllPostComments", + Handler: _Query_AllPostComments_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "blog/v1/query.proto", @@ -318,6 +466,97 @@ func (m *QueryAllPostsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *QueryAllPostCommentsRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryAllPostCommentsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryAllPostCommentsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.PostSlug) > 0 { + i -= len(m.PostSlug) + copy(dAtA[i:], m.PostSlug) + i = encodeVarintQuery(dAtA, i, uint64(len(m.PostSlug))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryAllPostCommentsResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryAllPostCommentsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryAllPostCommentsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.PostComments) > 0 { + for iNdEx := len(m.PostComments) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.PostComments[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + func encodeVarintQuery(dAtA []byte, offset int, v uint64) int { offset -= sovQuery(v) base := offset @@ -361,6 +600,42 @@ func (m *QueryAllPostsResponse) Size() (n int) { return n } +func (m *QueryAllPostCommentsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.PostSlug) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryAllPostCommentsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.PostComments) > 0 { + for _, e := range m.PostComments { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + func sovQuery(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -579,6 +854,250 @@ func (m *QueryAllPostsResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *QueryAllPostCommentsRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryAllPostCommentsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryAllPostCommentsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PostSlug", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.PostSlug = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pagination == nil { + m.Pagination = &query.PageRequest{} + } + if err := m.Pagination.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryAllPostCommentsResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryAllPostCommentsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryAllPostCommentsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PostComments", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.PostComments = append(m.PostComments, &PostComment{}) + if err := m.PostComments[len(m.PostComments)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pagination == nil { + m.Pagination = &query.PageRequest{} + } + if err := m.Pagination.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipQuery(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/x/blog/requests.go b/x/blog/requests.go index f55da6d..da0c198 100644 --- a/x/blog/requests.go +++ b/x/blog/requests.go @@ -7,6 +7,7 @@ import ( var ( _ sdk.Msg = &MsgCreatePost{} + _ sdk.Msg = &MsgCreatePostComment{} ) func (m *MsgCreatePost) ValidateBasic() error { @@ -34,3 +35,26 @@ func (m *MsgCreatePost) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{addr} } + +func (m *MsgCreatePostComment) ValidateBasic() error { + if m.Author == "" { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "no author") + } + if m.Body == "" { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "no body") + } + if m.Slug == "" { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "no slug") + } + + return nil +} + +func (m *MsgCreatePostComment) GetSigners() []sdk.AccAddress { + addr, err := sdk.AccAddressFromBech32(m.Author) + if err != nil { + panic(err) + } + + return []sdk.AccAddress{addr} +} diff --git a/x/blog/server/msg_server.go b/x/blog/server/msg_server.go index 8b60604..fb3e414 100644 --- a/x/blog/server/msg_server.go +++ b/x/blog/server/msg_server.go @@ -38,3 +38,36 @@ func (s serverImpl) CreatePost(goCtx context.Context, request *blog.MsgCreatePos return &blog.MsgCreatePostResponse{}, nil } + +func (s serverImpl) CreatePostComment(goCtx context.Context, request *blog.MsgCreatePostComment) (*blog.MsgCreatePostCommentResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + commentStore := prefix.NewStore(ctx.KVStore(s.storeKey), blog.KeyPrefix(blog.PostCommentKey)) + postStore := prefix.NewStore(ctx.KVStore(s.storeKey), blog.KeyPrefix(blog.PostKey)) + + // check if the post exists + if !postStore.Has([]byte(request.Slug)) { + return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "slug %s is found n 'post' storage", request.Slug) + } + + comment := blog.PostComment{ + Author: request.Author, + Slug: request.Slug, + Body: request.Body, + } + + bComment, err := s.cdc.Marshal(&comment) + if err != nil { + return nil, err + } + + // For key we can use body + author + slug + // explain: if the author write several similar message for one post comment will have no value + // In my opinion it's best solution for comment key + // Also we can try to generate some key, like UUID or something and for sure it's also will be working solution + key := []byte(request.Body + request.Author + request.Slug) + + commentStore.Set(key, bComment) + + return &blog.MsgCreatePostCommentResponse{}, nil +} diff --git a/x/blog/server/query_server.go b/x/blog/server/query_server.go index 57a80e0..45be16c 100644 --- a/x/blog/server/query_server.go +++ b/x/blog/server/query_server.go @@ -33,3 +33,28 @@ func (s serverImpl) AllPosts(goCtx context.Context, request *blog.QueryAllPostsR Posts: posts, }, nil } + +func (s serverImpl) AllPostComments(goCtx context.Context, request *blog.QueryAllPostCommentsRequest) (*blog.QueryAllPostCommentsResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + store := ctx.KVStore(s.storeKey) + iterator := sdk.KVStorePrefixIterator(store, blog.KeyPrefix(blog.PostCommentKey)) + + defer iterator.Close() + + var postComments []*blog.PostComment + for ; iterator.Valid(); iterator.Next() { + var msg blog.PostComment + err := s.cdc.Unmarshal(iterator.Value(), &msg) + if err != nil { + return nil, err + } + if msg.Slug == request.PostSlug { + postComments = append(postComments, &msg) + } + } + + return &blog.QueryAllPostCommentsResponse{ + PostComments: postComments, + }, nil +} diff --git a/x/blog/tx.pb.go b/x/blog/tx.pb.go index cf329b3..caae04e 100644 --- a/x/blog/tx.pb.go +++ b/x/blog/tx.pb.go @@ -133,15 +133,115 @@ func (m *MsgCreatePostResponse) XXX_DiscardUnknown() { var xxx_messageInfo_MsgCreatePostResponse proto.InternalMessageInfo +// MsgCreatePostComment is the Msg/CreatePostCOmment request type. +type MsgCreatePostComment struct { + Slug string `protobuf:"bytes,1,opt,name=slug,proto3" json:"slug,omitempty"` + Author string `protobuf:"bytes,2,opt,name=author,proto3" json:"author,omitempty"` + Body string `protobuf:"bytes,3,opt,name=body,proto3" json:"body,omitempty"` +} + +func (m *MsgCreatePostComment) Reset() { *m = MsgCreatePostComment{} } +func (m *MsgCreatePostComment) String() string { return proto.CompactTextString(m) } +func (*MsgCreatePostComment) ProtoMessage() {} +func (*MsgCreatePostComment) Descriptor() ([]byte, []int) { + return fileDescriptor_f15df8808566170d, []int{2} +} +func (m *MsgCreatePostComment) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgCreatePostComment) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgCreatePostComment.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgCreatePostComment) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgCreatePostComment.Merge(m, src) +} +func (m *MsgCreatePostComment) XXX_Size() int { + return m.Size() +} +func (m *MsgCreatePostComment) XXX_DiscardUnknown() { + xxx_messageInfo_MsgCreatePostComment.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgCreatePostComment proto.InternalMessageInfo + +func (m *MsgCreatePostComment) GetSlug() string { + if m != nil { + return m.Slug + } + return "" +} + +func (m *MsgCreatePostComment) GetAuthor() string { + if m != nil { + return m.Author + } + return "" +} + +func (m *MsgCreatePostComment) GetBody() string { + if m != nil { + return m.Body + } + return "" +} + +// MsgCreatePostCommentResponse is the Msg/CreatePostResponse response type. +type MsgCreatePostCommentResponse struct { +} + +func (m *MsgCreatePostCommentResponse) Reset() { *m = MsgCreatePostCommentResponse{} } +func (m *MsgCreatePostCommentResponse) String() string { return proto.CompactTextString(m) } +func (*MsgCreatePostCommentResponse) ProtoMessage() {} +func (*MsgCreatePostCommentResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_f15df8808566170d, []int{3} +} +func (m *MsgCreatePostCommentResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgCreatePostCommentResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgCreatePostCommentResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgCreatePostCommentResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgCreatePostCommentResponse.Merge(m, src) +} +func (m *MsgCreatePostCommentResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgCreatePostCommentResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgCreatePostCommentResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgCreatePostCommentResponse proto.InternalMessageInfo + func init() { proto.RegisterType((*MsgCreatePost)(nil), "blog.v1.MsgCreatePost") proto.RegisterType((*MsgCreatePostResponse)(nil), "blog.v1.MsgCreatePostResponse") + proto.RegisterType((*MsgCreatePostComment)(nil), "blog.v1.MsgCreatePostComment") + proto.RegisterType((*MsgCreatePostCommentResponse)(nil), "blog.v1.MsgCreatePostCommentResponse") } func init() { proto.RegisterFile("blog/v1/tx.proto", fileDescriptor_f15df8808566170d) } var fileDescriptor_f15df8808566170d = []byte{ - // 234 bytes of a gzipped FileDescriptorProto + // 281 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x48, 0xca, 0xc9, 0x4f, 0xd7, 0x2f, 0x33, 0xd4, 0x2f, 0xa9, 0xd0, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x07, 0x89, 0xe8, 0x95, 0x19, 0x2a, 0xa5, 0x72, 0xf1, 0xfa, 0x16, 0xa7, 0x3b, 0x17, 0xa5, 0x26, 0x96, 0xa4, @@ -149,14 +249,17 @@ var fileDescriptor_f15df8808566170d = []byte{ 0x6a, 0x70, 0x06, 0x81, 0xd9, 0x42, 0x62, 0x5c, 0x6c, 0x89, 0xa5, 0x25, 0x19, 0xf9, 0x45, 0x12, 0x4c, 0x60, 0x51, 0x28, 0x4f, 0x48, 0x84, 0x8b, 0xb5, 0x24, 0xb3, 0x24, 0x27, 0x55, 0x82, 0x19, 0x2c, 0x0c, 0xe1, 0x80, 0x4c, 0x48, 0xca, 0x4f, 0xa9, 0x94, 0x60, 0x81, 0x98, 0x00, 0x62, 0x2b, - 0x89, 0x73, 0x89, 0xa2, 0x58, 0x13, 0x94, 0x5a, 0x5c, 0x90, 0x9f, 0x57, 0x9c, 0x6a, 0xe4, 0xcd, - 0xc5, 0xec, 0x5b, 0x9c, 0x2e, 0xe4, 0xc2, 0xc5, 0x85, 0xe4, 0x06, 0x31, 0x3d, 0xa8, 0xf3, 0xf4, - 0x50, 0x34, 0x49, 0xc9, 0x61, 0x17, 0x87, 0x19, 0xe6, 0x64, 0x7b, 0xe2, 0x91, 0x1c, 0xe3, 0x85, - 0x47, 0x72, 0x8c, 0x0f, 0x1e, 0xc9, 0x31, 0x4e, 0x78, 0x2c, 0xc7, 0x70, 0xe1, 0xb1, 0x1c, 0xc3, - 0x8d, 0xc7, 0x72, 0x0c, 0x51, 0xca, 0xe9, 0x99, 0x25, 0x19, 0xa5, 0x49, 0x7a, 0xc9, 0xf9, 0xb9, - 0xfa, 0x45, 0xa9, 0xe9, 0xa9, 0x79, 0xba, 0x79, 0xa9, 0x25, 0xe5, 0xf9, 0x45, 0xd9, 0xfa, 0x49, - 0xa9, 0xc9, 0xfa, 0x15, 0xfa, 0x20, 0x73, 0x93, 0xd8, 0xc0, 0x61, 0x63, 0x0c, 0x08, 0x00, 0x00, - 0xff, 0xff, 0x86, 0x8e, 0xc6, 0xb8, 0x2f, 0x01, 0x00, 0x00, + 0x89, 0x73, 0x89, 0xa2, 0x58, 0x13, 0x94, 0x5a, 0x5c, 0x90, 0x9f, 0x57, 0x9c, 0xaa, 0x14, 0xc6, + 0x25, 0x82, 0x22, 0xe1, 0x9c, 0x9f, 0x9b, 0x9b, 0x9a, 0x47, 0x9a, 0x33, 0x60, 0x16, 0x32, 0x23, + 0x59, 0x28, 0xc7, 0x25, 0x83, 0xcd, 0x5c, 0x98, 0xbd, 0x46, 0xcb, 0x18, 0xb9, 0x98, 0x7d, 0x8b, + 0xd3, 0x85, 0x5c, 0xb8, 0xb8, 0x90, 0x3c, 0x2f, 0xa6, 0x07, 0x0d, 0x17, 0x3d, 0x14, 0xcd, 0x52, + 0x72, 0xd8, 0xc5, 0x61, 0xa6, 0x09, 0x45, 0x72, 0x09, 0x62, 0x7a, 0x41, 0x16, 0xbb, 0x26, 0xa8, + 0xb4, 0x94, 0x2a, 0x5e, 0x69, 0x98, 0xd1, 0x4e, 0xb6, 0x27, 0x1e, 0xc9, 0x31, 0x5e, 0x78, 0x24, + 0xc7, 0xf8, 0xe0, 0x91, 0x1c, 0xe3, 0x84, 0xc7, 0x72, 0x0c, 0x17, 0x1e, 0xcb, 0x31, 0xdc, 0x78, + 0x2c, 0xc7, 0x10, 0xa5, 0x9c, 0x9e, 0x59, 0x92, 0x51, 0x9a, 0xa4, 0x97, 0x9c, 0x9f, 0xab, 0x5f, + 0x94, 0x9a, 0x9e, 0x9a, 0xa7, 0x9b, 0x97, 0x5a, 0x52, 0x9e, 0x5f, 0x94, 0xad, 0x9f, 0x94, 0x9a, + 0xac, 0x5f, 0xa1, 0x0f, 0x32, 0x3e, 0x89, 0x0d, 0x1c, 0xdf, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x80, 0x54, 0x53, 0x75, 0x03, 0x02, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -172,6 +275,7 @@ const _ = grpc.SupportPackageIsVersion4 // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type MsgClient interface { CreatePost(ctx context.Context, in *MsgCreatePost, opts ...grpc.CallOption) (*MsgCreatePostResponse, error) + CreatePostComment(ctx context.Context, in *MsgCreatePostComment, opts ...grpc.CallOption) (*MsgCreatePostCommentResponse, error) } type msgClient struct { @@ -191,9 +295,19 @@ func (c *msgClient) CreatePost(ctx context.Context, in *MsgCreatePost, opts ...g return out, nil } +func (c *msgClient) CreatePostComment(ctx context.Context, in *MsgCreatePostComment, opts ...grpc.CallOption) (*MsgCreatePostCommentResponse, error) { + out := new(MsgCreatePostCommentResponse) + err := c.cc.Invoke(ctx, "/blog.v1.Msg/CreatePostComment", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // MsgServer is the server API for Msg service. type MsgServer interface { CreatePost(context.Context, *MsgCreatePost) (*MsgCreatePostResponse, error) + CreatePostComment(context.Context, *MsgCreatePostComment) (*MsgCreatePostCommentResponse, error) } // UnimplementedMsgServer can be embedded to have forward compatible implementations. @@ -203,6 +317,9 @@ type UnimplementedMsgServer struct { func (*UnimplementedMsgServer) CreatePost(ctx context.Context, req *MsgCreatePost) (*MsgCreatePostResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method CreatePost not implemented") } +func (*UnimplementedMsgServer) CreatePostComment(ctx context.Context, req *MsgCreatePostComment) (*MsgCreatePostCommentResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreatePostComment not implemented") +} func RegisterMsgServer(s grpc1.Server, srv MsgServer) { s.RegisterService(&_Msg_serviceDesc, srv) @@ -226,6 +343,24 @@ func _Msg_CreatePost_Handler(srv interface{}, ctx context.Context, dec func(inte return interceptor(ctx, in, info, handler) } +func _Msg_CreatePostComment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgCreatePostComment) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).CreatePostComment(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/blog.v1.Msg/CreatePostComment", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).CreatePostComment(ctx, req.(*MsgCreatePostComment)) + } + return interceptor(ctx, in, info, handler) +} + var _Msg_serviceDesc = grpc.ServiceDesc{ ServiceName: "blog.v1.Msg", HandlerType: (*MsgServer)(nil), @@ -234,6 +369,10 @@ var _Msg_serviceDesc = grpc.ServiceDesc{ MethodName: "CreatePost", Handler: _Msg_CreatePost_Handler, }, + { + MethodName: "CreatePostComment", + Handler: _Msg_CreatePostComment_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "blog/v1/tx.proto", @@ -313,6 +452,73 @@ func (m *MsgCreatePostResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *MsgCreatePostComment) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgCreatePostComment) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgCreatePostComment) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Body) > 0 { + i -= len(m.Body) + copy(dAtA[i:], m.Body) + i = encodeVarintTx(dAtA, i, uint64(len(m.Body))) + i-- + dAtA[i] = 0x1a + } + if len(m.Author) > 0 { + i -= len(m.Author) + copy(dAtA[i:], m.Author) + i = encodeVarintTx(dAtA, i, uint64(len(m.Author))) + i-- + dAtA[i] = 0x12 + } + if len(m.Slug) > 0 { + i -= len(m.Slug) + copy(dAtA[i:], m.Slug) + i = encodeVarintTx(dAtA, i, uint64(len(m.Slug))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgCreatePostCommentResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgCreatePostCommentResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgCreatePostCommentResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + func encodeVarintTx(dAtA []byte, offset int, v uint64) int { offset -= sovTx(v) base := offset @@ -358,6 +564,36 @@ func (m *MsgCreatePostResponse) Size() (n int) { return n } +func (m *MsgCreatePostComment) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Slug) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Author) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Body) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgCreatePostCommentResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + func sovTx(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -598,6 +834,208 @@ func (m *MsgCreatePostResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *MsgCreatePostComment) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgCreatePostComment: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgCreatePostComment: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Slug", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Slug = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Author", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Author = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Body", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Body = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgCreatePostCommentResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgCreatePostCommentResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgCreatePostCommentResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipTx(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/x/blog/types.pb.go b/x/blog/types.pb.go index 307e7f8..b29cd56 100644 --- a/x/blog/types.pb.go +++ b/x/blog/types.pb.go @@ -92,26 +92,90 @@ func (m *Post) GetBody() string { return "" } +type PostComment struct { + // slug is a short human-readable string used in commect for connecting with post. + // Used as unique. + Slug string `protobuf:"bytes,1,opt,name=slug,proto3" json:"slug,omitempty"` + Author string `protobuf:"bytes,2,opt,name=author,proto3" json:"author,omitempty"` + Body string `protobuf:"bytes,3,opt,name=body,proto3" json:"body,omitempty"` +} + +func (m *PostComment) Reset() { *m = PostComment{} } +func (m *PostComment) String() string { return proto.CompactTextString(m) } +func (*PostComment) ProtoMessage() {} +func (*PostComment) Descriptor() ([]byte, []int) { + return fileDescriptor_73573037ee301d2b, []int{1} +} +func (m *PostComment) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PostComment) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PostComment.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *PostComment) XXX_Merge(src proto.Message) { + xxx_messageInfo_PostComment.Merge(m, src) +} +func (m *PostComment) XXX_Size() int { + return m.Size() +} +func (m *PostComment) XXX_DiscardUnknown() { + xxx_messageInfo_PostComment.DiscardUnknown(m) +} + +var xxx_messageInfo_PostComment proto.InternalMessageInfo + +func (m *PostComment) GetSlug() string { + if m != nil { + return m.Slug + } + return "" +} + +func (m *PostComment) GetAuthor() string { + if m != nil { + return m.Author + } + return "" +} + +func (m *PostComment) GetBody() string { + if m != nil { + return m.Body + } + return "" +} + func init() { proto.RegisterType((*Post)(nil), "blog.v1.Post") + proto.RegisterType((*PostComment)(nil), "blog.v1.PostComment") } func init() { proto.RegisterFile("blog/v1/types.proto", fileDescriptor_73573037ee301d2b) } var fileDescriptor_73573037ee301d2b = []byte{ - // 187 bytes of a gzipped FileDescriptorProto + // 207 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x4e, 0xca, 0xc9, 0x4f, 0xd7, 0x2f, 0x33, 0xd4, 0x2f, 0xa9, 0x2c, 0x48, 0x2d, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x07, 0x09, 0xea, 0x95, 0x19, 0x2a, 0xc5, 0x70, 0xb1, 0x04, 0xe4, 0x17, 0x97, 0x08, 0x09, 0x71, 0xb1, 0x14, 0xe7, 0x94, 0xa6, 0x4b, 0x30, 0x2a, 0x30, 0x6a, 0x70, 0x06, 0x81, 0xd9, 0x42, 0x62, 0x5c, 0x6c, 0x89, 0xa5, 0x25, 0x19, 0xf9, 0x45, 0x12, 0x4c, 0x60, 0x51, 0x28, 0x4f, 0x48, 0x84, 0x8b, 0xb5, 0x24, 0xb3, 0x24, 0x27, 0x55, 0x82, 0x19, 0x2c, 0x0c, 0xe1, 0x80, 0x4c, 0x48, - 0xca, 0x4f, 0xa9, 0x94, 0x60, 0x81, 0x98, 0x00, 0x62, 0x3b, 0xd9, 0x9e, 0x78, 0x24, 0xc7, 0x78, - 0xe1, 0x91, 0x1c, 0xe3, 0x83, 0x47, 0x72, 0x8c, 0x13, 0x1e, 0xcb, 0x31, 0x5c, 0x78, 0x2c, 0xc7, - 0x70, 0xe3, 0xb1, 0x1c, 0x43, 0x94, 0x72, 0x7a, 0x66, 0x49, 0x46, 0x69, 0x92, 0x5e, 0x72, 0x7e, - 0xae, 0x7e, 0x51, 0x6a, 0x7a, 0x6a, 0x9e, 0x6e, 0x5e, 0x6a, 0x49, 0x79, 0x7e, 0x51, 0xb6, 0x7e, - 0x52, 0x6a, 0xb2, 0x7e, 0x85, 0x3e, 0xc8, 0x7d, 0x49, 0x6c, 0x60, 0xc7, 0x1a, 0x03, 0x02, 0x00, - 0x00, 0xff, 0xff, 0xf7, 0xa2, 0x98, 0xf3, 0xc3, 0x00, 0x00, 0x00, + 0xca, 0x4f, 0xa9, 0x94, 0x60, 0x81, 0x98, 0x00, 0x62, 0x2b, 0xf9, 0x72, 0x71, 0x83, 0x4c, 0x77, + 0xce, 0xcf, 0xcd, 0x4d, 0xcd, 0x23, 0xcd, 0x12, 0x98, 0x71, 0xcc, 0x08, 0xe3, 0x9c, 0x6c, 0x4f, + 0x3c, 0x92, 0x63, 0xbc, 0xf0, 0x48, 0x8e, 0xf1, 0xc1, 0x23, 0x39, 0xc6, 0x09, 0x8f, 0xe5, 0x18, + 0x2e, 0x3c, 0x96, 0x63, 0xb8, 0xf1, 0x58, 0x8e, 0x21, 0x4a, 0x39, 0x3d, 0xb3, 0x24, 0xa3, 0x34, + 0x49, 0x2f, 0x39, 0x3f, 0x57, 0xbf, 0x28, 0x35, 0x3d, 0x35, 0x4f, 0x37, 0x2f, 0xb5, 0xa4, 0x3c, + 0xbf, 0x28, 0x5b, 0x3f, 0x29, 0x35, 0x59, 0xbf, 0x42, 0x1f, 0xe4, 0xdd, 0x24, 0x36, 0xb0, 0xdf, + 0x8d, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0x70, 0x7b, 0xa7, 0x66, 0x12, 0x01, 0x00, 0x00, } func (m *Post) Marshal() (dAtA []byte, err error) { @@ -165,6 +229,50 @@ func (m *Post) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *PostComment) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PostComment) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PostComment) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Body) > 0 { + i -= len(m.Body) + copy(dAtA[i:], m.Body) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Body))) + i-- + dAtA[i] = 0x1a + } + if len(m.Author) > 0 { + i -= len(m.Author) + copy(dAtA[i:], m.Author) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Author))) + i-- + dAtA[i] = 0x12 + } + if len(m.Slug) > 0 { + i -= len(m.Slug) + copy(dAtA[i:], m.Slug) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Slug))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func encodeVarintTypes(dAtA []byte, offset int, v uint64) int { offset -= sovTypes(v) base := offset @@ -201,6 +309,27 @@ func (m *Post) Size() (n int) { return n } +func (m *PostComment) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Slug) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } + l = len(m.Author) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } + l = len(m.Body) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } + return n +} + func sovTypes(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -388,6 +517,155 @@ func (m *Post) Unmarshal(dAtA []byte) error { } return nil } +func (m *PostComment) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PostComment: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PostComment: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Slug", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Slug = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Author", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Author = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Body", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Body = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTypes(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipTypes(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0