diff --git a/docs/swagger/simplebank.swagger.json b/docs/swagger/simplebank.swagger.json index 140e185..431837a 100644 --- a/docs/swagger/simplebank.swagger.json +++ b/docs/swagger/simplebank.swagger.json @@ -2,7 +2,7 @@ "swagger": "2.0", "info": { "title": "SimpleBank", - "version": "1.0", + "version": "1.1", "contact": { "name": "Nguyen Hoang Phuc", "url": "https://github.com/cukhoaimon", @@ -23,6 +23,8 @@ "paths": { "/api/v1/user": { "post": { + "summary": "Create new user", + "description": "Use this API to create user", "operationId": "SimpleBank_CreateUser", "responses": { "200": { @@ -51,10 +53,44 @@ "tags": [ "SimpleBank" ] + }, + "patch": { + "summary": "Update user", + "description": "Use this API to update user", + "operationId": "SimpleBank_UpdateUser", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/pbUpdateUserResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/pbUpdateUserRequest" + } + } + ], + "tags": [ + "SimpleBank" + ] } }, "/api/v1/user/login": { "post": { + "summary": "login user", + "description": "Use this API to login user", "operationId": "SimpleBank_LoginUser", "responses": { "200": { @@ -148,6 +184,31 @@ } } }, + "pbUpdateUserRequest": { + "type": "object", + "properties": { + "username": { + "type": "string" + }, + "fullName": { + "type": "string" + }, + "email": { + "type": "string" + }, + "password": { + "type": "string" + } + } + }, + "pbUpdateUserResponse": { + "type": "object", + "properties": { + "user": { + "$ref": "#/definitions/pbUser" + } + } + }, "pbUser": { "type": "object", "properties": { diff --git a/internal/delivery/grpc/gapi/handler.go b/internal/delivery/grpc/gapi/handler.go index ff7faeb..ad85d13 100644 --- a/internal/delivery/grpc/gapi/handler.go +++ b/internal/delivery/grpc/gapi/handler.go @@ -1,12 +1,14 @@ package gapi import ( + "github.com/cukhoaimon/SimpleBank/internal/delivery/grpc/pb" db "github.com/cukhoaimon/SimpleBank/internal/usecase/sqlc" "github.com/cukhoaimon/SimpleBank/pkg/token" "github.com/cukhoaimon/SimpleBank/utils" ) type Handler struct { + pb.UnimplementedSimpleBankServer Config utils.Config TokenMaker token.Maker Store db.Store diff --git a/internal/delivery/grpc/gapi/rpc_update_user.go b/internal/delivery/grpc/gapi/rpc_update_user.go new file mode 100644 index 0000000..3352218 --- /dev/null +++ b/internal/delivery/grpc/gapi/rpc_update_user.go @@ -0,0 +1,85 @@ +package gapi + +import ( + "context" + "database/sql" + "errors" + "github.com/cukhoaimon/SimpleBank/internal/delivery/grpc/pb" + db "github.com/cukhoaimon/SimpleBank/internal/usecase/sqlc" + "github.com/cukhoaimon/SimpleBank/internal/usecase/val" + "github.com/cukhoaimon/SimpleBank/utils" + "google.golang.org/genproto/googleapis/rpc/errdetails" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +func (handler *Handler) UpdateUser(ctx context.Context, req *pb.UpdateUserRequest) (*pb.UpdateUserResponse, error) { + violations := validateUpdateUserRequest(req) + if violations != nil { + return nil, invalidArgumentError(violations) + } + + arg := db.UpdateUserParams{ + FullName: sql.NullString{ + String: req.GetFullName(), + Valid: req.FullName != nil, + }, + Email: sql.NullString{ + String: req.GetEmail(), + Valid: req.Email != nil, + }, + Username: req.GetUsername(), + } + + if req.Password != nil { + hashedPassword, err := utils.HashPassword(req.GetPassword()) + if err != nil { + return nil, status.Errorf(codes.Internal, "fail to hash password: %s", err) + } + + arg.HashedPassword = sql.NullString{ + String: hashedPassword, + Valid: true, + } + } + + user, err := handler.Store.UpdateUser(ctx, arg) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return nil, status.Errorf(codes.NotFound, "user not found") + } + + return nil, status.Errorf(codes.Internal, "fail to create user: %s", err) + } + + rsp := &pb.UpdateUserResponse{ + User: convertUser(user), + } + return rsp, nil +} + +func validateUpdateUserRequest(req *pb.UpdateUserRequest) (violations []*errdetails.BadRequest_FieldViolation) { + if err := val.ValidateUsername(req.GetUsername()); err != nil { + violations = append(violations, fieldViolation("username", err)) + } + + if req.FullName != nil { + if err := val.ValidateFullName(req.GetFullName()); err != nil { + violations = append(violations, fieldViolation("full_name", err)) + } + } + + if req.Email != nil { + if err := val.ValidateEmail(req.GetEmail()); err != nil { + violations = append(violations, fieldViolation("email", err)) + } + } + + if req.Password != nil { + if err := val.ValidatePassword(req.GetPassword()); err != nil { + violations = append(violations, fieldViolation("password", err)) + } + } + + return violations +} diff --git a/internal/delivery/grpc/pb/rpc_create_user.pb.go b/internal/delivery/grpc/pb/rpc_create_user.pb.go index dd1ecf4..7e3e9fb 100644 --- a/internal/delivery/grpc/pb/rpc_create_user.pb.go +++ b/internal/delivery/grpc/pb/rpc_create_user.pb.go @@ -25,10 +25,10 @@ type CreateUserRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Username string `pb:"bytes,1,opt,name=username,proto3" json:"username,omitempty"` - FullName string `pb:"bytes,2,opt,name=full_name,json=fullName,proto3" json:"full_name,omitempty"` - Email string `pb:"bytes,3,opt,name=email,proto3" json:"email,omitempty"` - Password string `pb:"bytes,4,opt,name=password,proto3" json:"password,omitempty"` + Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"` + FullName string `protobuf:"bytes,2,opt,name=full_name,json=fullName,proto3" json:"full_name,omitempty"` + Email string `protobuf:"bytes,3,opt,name=email,proto3" json:"email,omitempty"` + Password string `protobuf:"bytes,4,opt,name=password,proto3" json:"password,omitempty"` } func (x *CreateUserRequest) Reset() { @@ -96,7 +96,7 @@ type CreateUserResponse struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - User *User `pb:"bytes,1,opt,name=user,proto3" json:"user,omitempty"` + User *User `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"` } func (x *CreateUserResponse) Reset() { @@ -154,10 +154,12 @@ var file_rpc_create_user_proto_rawDesc = []byte{ 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x32, 0x0a, 0x12, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x08, 0x2e, 0x70, 0x62, - 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x42, 0x2b, 0x5a, 0x29, 0x67, + 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x42, 0x3c, 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x75, 0x6b, 0x68, 0x6f, 0x61, 0x69, 0x6d, 0x6f, 0x6e, 0x2f, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x42, 0x61, 0x6e, 0x6b, 0x2f, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, + 0x79, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( diff --git a/internal/delivery/grpc/pb/rpc_login_user.pb.go b/internal/delivery/grpc/pb/rpc_login_user.pb.go index 5f7d8a1..93fec24 100644 --- a/internal/delivery/grpc/pb/rpc_login_user.pb.go +++ b/internal/delivery/grpc/pb/rpc_login_user.pb.go @@ -26,8 +26,8 @@ type LoginUserRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Username string `pb:"bytes,1,opt,name=username,proto3" json:"username,omitempty"` - Password string `pb:"bytes,2,opt,name=password,proto3" json:"password,omitempty"` + Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"` + Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"` } func (x *LoginUserRequest) Reset() { @@ -81,12 +81,12 @@ type LoginUserResponse struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - User *User `pb:"bytes,1,opt,name=user,proto3" json:"user,omitempty"` - SessionId string `pb:"bytes,2,opt,name=session_id,json=sessionId,proto3" json:"session_id,omitempty"` - AccessToken string `pb:"bytes,3,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"` - RefreshToken string `pb:"bytes,4,opt,name=refresh_token,json=refreshToken,proto3" json:"refresh_token,omitempty"` - AccessTokenExpiresAt *timestamppb.Timestamp `pb:"bytes,5,opt,name=access_token_expires_at,json=accessTokenExpiresAt,proto3" json:"access_token_expires_at,omitempty"` - RefreshTokenExpiresAt *timestamppb.Timestamp `pb:"bytes,6,opt,name=refresh_token_expires_at,json=refreshTokenExpiresAt,proto3" json:"refresh_token_expires_at,omitempty"` + User *User `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"` + SessionId string `protobuf:"bytes,2,opt,name=session_id,json=sessionId,proto3" json:"session_id,omitempty"` + AccessToken string `protobuf:"bytes,3,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"` + RefreshToken string `protobuf:"bytes,4,opt,name=refresh_token,json=refreshToken,proto3" json:"refresh_token,omitempty"` + AccessTokenExpiresAt *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=access_token_expires_at,json=accessTokenExpiresAt,proto3" json:"access_token_expires_at,omitempty"` + RefreshTokenExpiresAt *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=refresh_token_expires_at,json=refreshTokenExpiresAt,proto3" json:"refresh_token_expires_at,omitempty"` } func (x *LoginUserResponse) Reset() { @@ -195,10 +195,11 @@ var file_rpc_login_user_proto_rawDesc = []byte{ 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x15, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x45, 0x78, 0x70, - 0x69, 0x72, 0x65, 0x73, 0x41, 0x74, 0x42, 0x2b, 0x5a, 0x29, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x69, 0x72, 0x65, 0x73, 0x41, 0x74, 0x42, 0x3c, 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x75, 0x6b, 0x68, 0x6f, 0x61, 0x69, 0x6d, 0x6f, 0x6e, 0x2f, - 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x42, 0x61, 0x6e, 0x6b, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x42, 0x61, 0x6e, 0x6b, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x2f, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x2f, 0x67, 0x72, 0x70, + 0x63, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -218,12 +219,12 @@ var file_rpc_login_user_proto_goTypes = []interface{}{ (*LoginUserRequest)(nil), // 0: pb.LoginUserRequest (*LoginUserResponse)(nil), // 1: pb.LoginUserResponse (*User)(nil), // 2: pb.User - (*timestamppb.Timestamp)(nil), // 3: google.pb.Timestamp + (*timestamppb.Timestamp)(nil), // 3: google.protobuf.Timestamp } var file_rpc_login_user_proto_depIdxs = []int32{ 2, // 0: pb.LoginUserResponse.user:type_name -> pb.User - 3, // 1: pb.LoginUserResponse.access_token_expires_at:type_name -> google.pb.Timestamp - 3, // 2: pb.LoginUserResponse.refresh_token_expires_at:type_name -> google.pb.Timestamp + 3, // 1: pb.LoginUserResponse.access_token_expires_at:type_name -> google.protobuf.Timestamp + 3, // 2: pb.LoginUserResponse.refresh_token_expires_at:type_name -> google.protobuf.Timestamp 3, // [3:3] is the sub-list for method output_type 3, // [3:3] is the sub-list for method input_type 3, // [3:3] is the sub-list for extension type_name diff --git a/internal/delivery/grpc/pb/rpc_update_user.pb.go b/internal/delivery/grpc/pb/rpc_update_user.pb.go new file mode 100644 index 0000000..3afa1c0 --- /dev/null +++ b/internal/delivery/grpc/pb/rpc_update_user.pb.go @@ -0,0 +1,246 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.32.0 +// protoc v4.25.2 +// source: rpc_update_user.proto + +package pb + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type UpdateUserRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"` + FullName *string `protobuf:"bytes,2,opt,name=full_name,json=fullName,proto3,oneof" json:"full_name,omitempty"` + Email *string `protobuf:"bytes,3,opt,name=email,proto3,oneof" json:"email,omitempty"` + Password *string `protobuf:"bytes,4,opt,name=password,proto3,oneof" json:"password,omitempty"` +} + +func (x *UpdateUserRequest) Reset() { + *x = UpdateUserRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_rpc_update_user_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UpdateUserRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateUserRequest) ProtoMessage() {} + +func (x *UpdateUserRequest) ProtoReflect() protoreflect.Message { + mi := &file_rpc_update_user_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateUserRequest.ProtoReflect.Descriptor instead. +func (*UpdateUserRequest) Descriptor() ([]byte, []int) { + return file_rpc_update_user_proto_rawDescGZIP(), []int{0} +} + +func (x *UpdateUserRequest) GetUsername() string { + if x != nil { + return x.Username + } + return "" +} + +func (x *UpdateUserRequest) GetFullName() string { + if x != nil && x.FullName != nil { + return *x.FullName + } + return "" +} + +func (x *UpdateUserRequest) GetEmail() string { + if x != nil && x.Email != nil { + return *x.Email + } + return "" +} + +func (x *UpdateUserRequest) GetPassword() string { + if x != nil && x.Password != nil { + return *x.Password + } + return "" +} + +type UpdateUserResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + User *User `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"` +} + +func (x *UpdateUserResponse) Reset() { + *x = UpdateUserResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_rpc_update_user_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UpdateUserResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateUserResponse) ProtoMessage() {} + +func (x *UpdateUserResponse) ProtoReflect() protoreflect.Message { + mi := &file_rpc_update_user_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateUserResponse.ProtoReflect.Descriptor instead. +func (*UpdateUserResponse) Descriptor() ([]byte, []int) { + return file_rpc_update_user_proto_rawDescGZIP(), []int{1} +} + +func (x *UpdateUserResponse) GetUser() *User { + if x != nil { + return x.User + } + return nil +} + +var File_rpc_update_user_proto protoreflect.FileDescriptor + +var file_rpc_update_user_proto_rawDesc = []byte{ + 0x0a, 0x15, 0x72, 0x70, 0x63, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x75, 0x73, 0x65, + 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x02, 0x70, 0x62, 0x1a, 0x0a, 0x75, 0x73, 0x65, + 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb2, 0x01, 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, + 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x09, 0x66, 0x75, 0x6c, + 0x6c, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x08, + 0x66, 0x75, 0x6c, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12, 0x19, 0x0a, 0x05, 0x65, + 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x05, 0x65, 0x6d, + 0x61, 0x69, 0x6c, 0x88, 0x01, 0x01, 0x12, 0x1f, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, + 0x72, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x02, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, + 0x77, 0x6f, 0x72, 0x64, 0x88, 0x01, 0x01, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x66, 0x75, 0x6c, 0x6c, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x42, + 0x0b, 0x0a, 0x09, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x32, 0x0a, 0x12, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x08, 0x2e, 0x70, 0x62, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, + 0x42, 0x3c, 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, + 0x75, 0x6b, 0x68, 0x6f, 0x61, 0x69, 0x6d, 0x6f, 0x6e, 0x2f, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, + 0x42, 0x61, 0x6e, 0x6b, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x64, 0x65, + 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x70, 0x62, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_rpc_update_user_proto_rawDescOnce sync.Once + file_rpc_update_user_proto_rawDescData = file_rpc_update_user_proto_rawDesc +) + +func file_rpc_update_user_proto_rawDescGZIP() []byte { + file_rpc_update_user_proto_rawDescOnce.Do(func() { + file_rpc_update_user_proto_rawDescData = protoimpl.X.CompressGZIP(file_rpc_update_user_proto_rawDescData) + }) + return file_rpc_update_user_proto_rawDescData +} + +var file_rpc_update_user_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_rpc_update_user_proto_goTypes = []interface{}{ + (*UpdateUserRequest)(nil), // 0: pb.UpdateUserRequest + (*UpdateUserResponse)(nil), // 1: pb.UpdateUserResponse + (*User)(nil), // 2: pb.User +} +var file_rpc_update_user_proto_depIdxs = []int32{ + 2, // 0: pb.UpdateUserResponse.user:type_name -> pb.User + 1, // [1:1] is the sub-list for method output_type + 1, // [1:1] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_rpc_update_user_proto_init() } +func file_rpc_update_user_proto_init() { + if File_rpc_update_user_proto != nil { + return + } + file_user_proto_init() + if !protoimpl.UnsafeEnabled { + file_rpc_update_user_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UpdateUserRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_rpc_update_user_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UpdateUserResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_rpc_update_user_proto_msgTypes[0].OneofWrappers = []interface{}{} + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_rpc_update_user_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_rpc_update_user_proto_goTypes, + DependencyIndexes: file_rpc_update_user_proto_depIdxs, + MessageInfos: file_rpc_update_user_proto_msgTypes, + }.Build() + File_rpc_update_user_proto = out.File + file_rpc_update_user_proto_rawDesc = nil + file_rpc_update_user_proto_goTypes = nil + file_rpc_update_user_proto_depIdxs = nil +} diff --git a/internal/delivery/grpc/pb/service_simple_bank.pb.go b/internal/delivery/grpc/pb/service_simple_bank.pb.go index c0af01b..cc4e26b 100644 --- a/internal/delivery/grpc/pb/service_simple_bank.pb.go +++ b/internal/delivery/grpc/pb/service_simple_bank.pb.go @@ -30,47 +30,67 @@ var file_service_simple_bank_proto_rawDesc = []byte{ 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x15, 0x72, 0x70, 0x63, 0x5f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x14, 0x72, 0x70, 0x63, 0x5f, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x5f, - 0x75, 0x73, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, - 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x32, 0xbb, 0x01, 0x0a, 0x0a, 0x53, - 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x42, 0x61, 0x6e, 0x6b, 0x12, 0x54, 0x0a, 0x0a, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x12, 0x15, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, - 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x3a, 0x01, - 0x2a, 0x22, 0x0c, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x12, - 0x57, 0x0a, 0x09, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x55, 0x73, 0x65, 0x72, 0x12, 0x14, 0x2e, 0x70, - 0x62, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x55, 0x73, 0x65, - 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x17, 0x3a, 0x01, 0x2a, 0x22, 0x12, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x73, - 0x65, 0x72, 0x2f, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x42, 0x91, 0x01, 0x92, 0x41, 0x63, 0x12, 0x61, - 0x0a, 0x0a, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x42, 0x61, 0x6e, 0x6b, 0x22, 0x4e, 0x0a, 0x11, - 0x4e, 0x67, 0x75, 0x79, 0x65, 0x6e, 0x20, 0x48, 0x6f, 0x61, 0x6e, 0x67, 0x20, 0x50, 0x68, 0x75, - 0x63, 0x12, 0x1d, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x75, 0x6b, 0x68, 0x6f, 0x61, 0x69, 0x6d, 0x6f, 0x6e, - 0x1a, 0x1a, 0x68, 0x6f, 0x61, 0x6e, 0x67, 0x70, 0x68, 0x75, 0x63, 0x6e, 0x68, 0x70, 0x31, 0x33, - 0x31, 0x32, 0x40, 0x67, 0x6d, 0x61, 0x69, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x32, 0x03, 0x31, 0x2e, - 0x30, 0x5a, 0x29, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x75, - 0x6b, 0x68, 0x6f, 0x61, 0x69, 0x6d, 0x6f, 0x6e, 0x2f, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x42, - 0x61, 0x6e, 0x6b, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, + 0x75, 0x73, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x15, 0x72, 0x70, 0x63, 0x5f, + 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x6f, 0x70, + 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, + 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x32, 0x9d, 0x03, 0x0a, 0x0a, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x42, 0x61, 0x6e, 0x6b, + 0x12, 0x85, 0x01, 0x0a, 0x0a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x12, + 0x15, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x48, + 0x92, 0x41, 0x2e, 0x12, 0x0f, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x20, 0x6e, 0x65, 0x77, 0x20, + 0x75, 0x73, 0x65, 0x72, 0x1a, 0x1b, 0x55, 0x73, 0x65, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x41, + 0x50, 0x49, 0x20, 0x74, 0x6f, 0x20, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x20, 0x75, 0x73, 0x65, + 0x72, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x3a, 0x01, 0x2a, 0x22, 0x0c, 0x2f, 0x61, 0x70, 0x69, + 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x12, 0x82, 0x01, 0x0a, 0x09, 0x4c, 0x6f, 0x67, + 0x69, 0x6e, 0x55, 0x73, 0x65, 0x72, 0x12, 0x14, 0x2e, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x67, 0x69, + 0x6e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x70, + 0x62, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x48, 0x92, 0x41, 0x28, 0x12, 0x0a, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x20, + 0x75, 0x73, 0x65, 0x72, 0x1a, 0x1a, 0x55, 0x73, 0x65, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x41, + 0x50, 0x49, 0x20, 0x74, 0x6f, 0x20, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x20, 0x75, 0x73, 0x65, 0x72, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x3a, 0x01, 0x2a, 0x22, 0x12, 0x2f, 0x61, 0x70, 0x69, 0x2f, + 0x76, 0x31, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x2f, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x81, 0x01, + 0x0a, 0x0a, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x12, 0x15, 0x2e, 0x70, + 0x62, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x70, 0x62, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x55, + 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x44, 0x92, 0x41, 0x2a, + 0x12, 0x0b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x20, 0x75, 0x73, 0x65, 0x72, 0x1a, 0x1b, 0x55, + 0x73, 0x65, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x41, 0x50, 0x49, 0x20, 0x74, 0x6f, 0x20, 0x75, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x20, 0x75, 0x73, 0x65, 0x72, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, + 0x3a, 0x01, 0x2a, 0x32, 0x0c, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x73, 0x65, + 0x72, 0x42, 0xa2, 0x01, 0x92, 0x41, 0x63, 0x12, 0x61, 0x0a, 0x0a, 0x53, 0x69, 0x6d, 0x70, 0x6c, + 0x65, 0x42, 0x61, 0x6e, 0x6b, 0x22, 0x4e, 0x0a, 0x11, 0x4e, 0x67, 0x75, 0x79, 0x65, 0x6e, 0x20, + 0x48, 0x6f, 0x61, 0x6e, 0x67, 0x20, 0x50, 0x68, 0x75, 0x63, 0x12, 0x1d, 0x68, 0x74, 0x74, 0x70, + 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, + 0x75, 0x6b, 0x68, 0x6f, 0x61, 0x69, 0x6d, 0x6f, 0x6e, 0x1a, 0x1a, 0x68, 0x6f, 0x61, 0x6e, 0x67, + 0x70, 0x68, 0x75, 0x63, 0x6e, 0x68, 0x70, 0x31, 0x33, 0x31, 0x32, 0x40, 0x67, 0x6d, 0x61, 0x69, + 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x32, 0x03, 0x31, 0x2e, 0x31, 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x75, 0x6b, 0x68, 0x6f, 0x61, 0x69, 0x6d, 0x6f, + 0x6e, 0x2f, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x42, 0x61, 0x6e, 0x6b, 0x2f, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x2f, 0x67, + 0x72, 0x70, 0x63, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var file_service_simple_bank_proto_goTypes = []interface{}{ (*CreateUserRequest)(nil), // 0: pb.CreateUserRequest (*LoginUserRequest)(nil), // 1: pb.LoginUserRequest - (*CreateUserResponse)(nil), // 2: pb.CreateUserResponse - (*LoginUserResponse)(nil), // 3: pb.LoginUserResponse + (*UpdateUserRequest)(nil), // 2: pb.UpdateUserRequest + (*CreateUserResponse)(nil), // 3: pb.CreateUserResponse + (*LoginUserResponse)(nil), // 4: pb.LoginUserResponse + (*UpdateUserResponse)(nil), // 5: pb.UpdateUserResponse } var file_service_simple_bank_proto_depIdxs = []int32{ 0, // 0: pb.SimpleBank.CreateUser:input_type -> pb.CreateUserRequest 1, // 1: pb.SimpleBank.LoginUser:input_type -> pb.LoginUserRequest - 2, // 2: pb.SimpleBank.CreateUser:output_type -> pb.CreateUserResponse - 3, // 3: pb.SimpleBank.LoginUser:output_type -> pb.LoginUserResponse - 2, // [2:4] is the sub-list for method output_type - 0, // [0:2] is the sub-list for method input_type + 2, // 2: pb.SimpleBank.UpdateUser:input_type -> pb.UpdateUserRequest + 3, // 3: pb.SimpleBank.CreateUser:output_type -> pb.CreateUserResponse + 4, // 4: pb.SimpleBank.LoginUser:output_type -> pb.LoginUserResponse + 5, // 5: pb.SimpleBank.UpdateUser:output_type -> pb.UpdateUserResponse + 3, // [3:6] is the sub-list for method output_type + 0, // [0:3] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name @@ -83,6 +103,7 @@ func file_service_simple_bank_proto_init() { } file_rpc_create_user_proto_init() file_rpc_login_user_proto_init() + file_rpc_update_user_proto_init() type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/internal/delivery/grpc/pb/service_simple_bank.pb.gw.go b/internal/delivery/grpc/pb/service_simple_bank.pb.gw.go index 81b2c55..148f89e 100644 --- a/internal/delivery/grpc/pb/service_simple_bank.pb.gw.go +++ b/internal/delivery/grpc/pb/service_simple_bank.pb.gw.go @@ -83,6 +83,32 @@ func local_request_SimpleBank_LoginUser_0(ctx context.Context, marshaler runtime } +func request_SimpleBank_UpdateUser_0(ctx context.Context, marshaler runtime.Marshaler, client SimpleBankClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq UpdateUserRequest + var metadata runtime.ServerMetadata + + if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.UpdateUser(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_SimpleBank_UpdateUser_0(ctx context.Context, marshaler runtime.Marshaler, server SimpleBankServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq UpdateUserRequest + var metadata runtime.ServerMetadata + + if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.UpdateUser(ctx, &protoReq) + return msg, metadata, err + +} + // RegisterSimpleBankHandlerServer registers the http handlers for service SimpleBank to "mux". // UnaryRPC :call SimpleBankServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. @@ -139,6 +165,31 @@ func RegisterSimpleBankHandlerServer(ctx context.Context, mux *runtime.ServeMux, }) + mux.Handle("PATCH", pattern_SimpleBank_UpdateUser_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/pb.SimpleBank/UpdateUser", runtime.WithHTTPPathPattern("/api/v1/user")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_SimpleBank_UpdateUser_0(annotatedContext, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_SimpleBank_UpdateUser_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -224,6 +275,28 @@ func RegisterSimpleBankHandlerClient(ctx context.Context, mux *runtime.ServeMux, }) + mux.Handle("PATCH", pattern_SimpleBank_UpdateUser_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/pb.SimpleBank/UpdateUser", runtime.WithHTTPPathPattern("/api/v1/user")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_SimpleBank_UpdateUser_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_SimpleBank_UpdateUser_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -231,10 +304,14 @@ var ( pattern_SimpleBank_CreateUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "user"}, "")) pattern_SimpleBank_LoginUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"api", "v1", "user", "login"}, "")) + + pattern_SimpleBank_UpdateUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "user"}, "")) ) var ( forward_SimpleBank_CreateUser_0 = runtime.ForwardResponseMessage forward_SimpleBank_LoginUser_0 = runtime.ForwardResponseMessage + + forward_SimpleBank_UpdateUser_0 = runtime.ForwardResponseMessage ) diff --git a/internal/delivery/grpc/pb/service_simple_bank_grpc.pb.go b/internal/delivery/grpc/pb/service_simple_bank_grpc.pb.go index c9adfee..0af4333 100644 --- a/internal/delivery/grpc/pb/service_simple_bank_grpc.pb.go +++ b/internal/delivery/grpc/pb/service_simple_bank_grpc.pb.go @@ -21,6 +21,7 @@ const _ = grpc.SupportPackageIsVersion7 const ( SimpleBank_CreateUser_FullMethodName = "/pb.SimpleBank/CreateUser" SimpleBank_LoginUser_FullMethodName = "/pb.SimpleBank/LoginUser" + SimpleBank_UpdateUser_FullMethodName = "/pb.SimpleBank/UpdateUser" ) // SimpleBankClient is the client API for SimpleBank service. @@ -29,6 +30,7 @@ const ( type SimpleBankClient interface { CreateUser(ctx context.Context, in *CreateUserRequest, opts ...grpc.CallOption) (*CreateUserResponse, error) LoginUser(ctx context.Context, in *LoginUserRequest, opts ...grpc.CallOption) (*LoginUserResponse, error) + UpdateUser(ctx context.Context, in *UpdateUserRequest, opts ...grpc.CallOption) (*UpdateUserResponse, error) } type simpleBankClient struct { @@ -57,12 +59,22 @@ func (c *simpleBankClient) LoginUser(ctx context.Context, in *LoginUserRequest, return out, nil } +func (c *simpleBankClient) UpdateUser(ctx context.Context, in *UpdateUserRequest, opts ...grpc.CallOption) (*UpdateUserResponse, error) { + out := new(UpdateUserResponse) + err := c.cc.Invoke(ctx, SimpleBank_UpdateUser_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // SimpleBankServer is the server API for SimpleBank service. // All implementations must embed UnimplementedSimpleBankServer // for forward compatibility type SimpleBankServer interface { CreateUser(context.Context, *CreateUserRequest) (*CreateUserResponse, error) LoginUser(context.Context, *LoginUserRequest) (*LoginUserResponse, error) + UpdateUser(context.Context, *UpdateUserRequest) (*UpdateUserResponse, error) mustEmbedUnimplementedSimpleBankServer() } @@ -76,6 +88,9 @@ func (UnimplementedSimpleBankServer) CreateUser(context.Context, *CreateUserRequ func (UnimplementedSimpleBankServer) LoginUser(context.Context, *LoginUserRequest) (*LoginUserResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method LoginUser not implemented") } +func (UnimplementedSimpleBankServer) UpdateUser(context.Context, *UpdateUserRequest) (*UpdateUserResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdateUser not implemented") +} func (UnimplementedSimpleBankServer) mustEmbedUnimplementedSimpleBankServer() {} // UnsafeSimpleBankServer may be embedded to opt out of forward compatibility for this service. @@ -125,6 +140,24 @@ func _SimpleBank_LoginUser_Handler(srv interface{}, ctx context.Context, dec fun return interceptor(ctx, in, info, handler) } +func _SimpleBank_UpdateUser_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateUserRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SimpleBankServer).UpdateUser(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: SimpleBank_UpdateUser_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SimpleBankServer).UpdateUser(ctx, req.(*UpdateUserRequest)) + } + return interceptor(ctx, in, info, handler) +} + // SimpleBank_ServiceDesc is the grpc.ServiceDesc for SimpleBank service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -140,6 +173,10 @@ var SimpleBank_ServiceDesc = grpc.ServiceDesc{ MethodName: "LoginUser", Handler: _SimpleBank_LoginUser_Handler, }, + { + MethodName: "UpdateUser", + Handler: _SimpleBank_UpdateUser_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "service_simple_bank.proto", diff --git a/internal/delivery/grpc/pb/user.pb.go b/internal/delivery/grpc/pb/user.pb.go index 59106c1..6c72748 100644 --- a/internal/delivery/grpc/pb/user.pb.go +++ b/internal/delivery/grpc/pb/user.pb.go @@ -26,11 +26,11 @@ type User struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Username string `pb:"bytes,1,opt,name=username,proto3" json:"username,omitempty"` - FullName string `pb:"bytes,2,opt,name=full_name,json=fullName,proto3" json:"full_name,omitempty"` - Email string `pb:"bytes,3,opt,name=email,proto3" json:"email,omitempty"` - PasswordChangedAt *timestamppb.Timestamp `pb:"bytes,4,opt,name=password_changed_at,json=passwordChangedAt,proto3" json:"password_changed_at,omitempty"` - CreatedAt *timestamppb.Timestamp `pb:"bytes,5,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` + Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"` + FullName string `protobuf:"bytes,2,opt,name=full_name,json=fullName,proto3" json:"full_name,omitempty"` + Email string `protobuf:"bytes,3,opt,name=email,proto3" json:"email,omitempty"` + PasswordChangedAt *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=password_changed_at,json=passwordChangedAt,proto3" json:"password_changed_at,omitempty"` + CreatedAt *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` } func (x *User) Reset() { @@ -120,10 +120,11 @@ var file_user_proto_rawDesc = []byte{ 0x5f, 0x61, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, - 0x42, 0x2b, 0x5a, 0x29, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, + 0x42, 0x3c, 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x75, 0x6b, 0x68, 0x6f, 0x61, 0x69, 0x6d, 0x6f, 0x6e, 0x2f, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, - 0x42, 0x61, 0x6e, 0x6b, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x42, 0x61, 0x6e, 0x6b, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x64, 0x65, + 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x70, 0x62, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -141,11 +142,11 @@ func file_user_proto_rawDescGZIP() []byte { var file_user_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_user_proto_goTypes = []interface{}{ (*User)(nil), // 0: pb.User - (*timestamppb.Timestamp)(nil), // 1: google.pb.Timestamp + (*timestamppb.Timestamp)(nil), // 1: google.protobuf.Timestamp } var file_user_proto_depIdxs = []int32{ - 1, // 0: pb.User.password_changed_at:type_name -> google.pb.Timestamp - 1, // 1: pb.User.created_at:type_name -> google.pb.Timestamp + 1, // 0: pb.User.password_changed_at:type_name -> google.protobuf.Timestamp + 1, // 1: pb.User.created_at:type_name -> google.protobuf.Timestamp 2, // [2:2] is the sub-list for method output_type 2, // [2:2] is the sub-list for method input_type 2, // [2:2] is the sub-list for extension type_name diff --git a/internal/delivery/grpc/proto/rpc_create_user.proto b/internal/delivery/grpc/proto/rpc_create_user.proto index 969c3ec..c2d2aef 100644 --- a/internal/delivery/grpc/proto/rpc_create_user.proto +++ b/internal/delivery/grpc/proto/rpc_create_user.proto @@ -4,7 +4,7 @@ package pb; import "user.proto"; -option go_package = "github.com/cukhoaimon/SimpleBank/pb"; +option go_package = "github.com/cukhoaimon/SimpleBank/internal/delivery/grpc/pb"; message CreateUserRequest { string username = 1; diff --git a/internal/delivery/grpc/proto/rpc_login_user.proto b/internal/delivery/grpc/proto/rpc_login_user.proto index eec0bf8..fd86d7b 100644 --- a/internal/delivery/grpc/proto/rpc_login_user.proto +++ b/internal/delivery/grpc/proto/rpc_login_user.proto @@ -5,7 +5,7 @@ package pb; import "user.proto"; import "google/protobuf/timestamp.proto"; -option go_package = "github.com/cukhoaimon/SimpleBank/pb"; +option go_package = "github.com/cukhoaimon/SimpleBank/internal/delivery/grpc/pb"; message LoginUserRequest { string username = 1; diff --git a/internal/delivery/grpc/proto/rpc_update_user.proto b/internal/delivery/grpc/proto/rpc_update_user.proto new file mode 100644 index 0000000..1aff086 --- /dev/null +++ b/internal/delivery/grpc/proto/rpc_update_user.proto @@ -0,0 +1,18 @@ +syntax = "proto3"; + +package pb; + +import "user.proto"; + +option go_package = "github.com/cukhoaimon/SimpleBank/internal/delivery/grpc/pb"; + +message UpdateUserRequest { + string username = 1; + optional string full_name = 2; + optional string email = 3; + optional string password = 4; +} + +message UpdateUserResponse { + User user = 1; +} \ No newline at end of file diff --git a/internal/delivery/grpc/proto/service_simple_bank.proto b/internal/delivery/grpc/proto/service_simple_bank.proto index 4aa1db5..cfd34c1 100644 --- a/internal/delivery/grpc/proto/service_simple_bank.proto +++ b/internal/delivery/grpc/proto/service_simple_bank.proto @@ -5,14 +5,15 @@ package pb; import "google/api/annotations.proto"; import "rpc_create_user.proto"; import "rpc_login_user.proto"; +import "rpc_update_user.proto"; import "protoc-gen-openapiv2/options/annotations.proto"; -option go_package = "github.com/cukhoaimon/SimpleBank/pb"; +option go_package = "github.com/cukhoaimon/SimpleBank/internal/delivery/grpc/pb"; option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = { info: { title: "SimpleBank"; - version: "1.0"; + version: "1.1"; contact: { name: "Nguyen Hoang Phuc"; url: "https://github.com/cukhoaimon"; @@ -27,12 +28,30 @@ service SimpleBank { post: "/api/v1/user" body: "*" }; + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + description: "Use this API to create user"; + summary: "Create new user"; + }; } rpc LoginUser (LoginUserRequest) returns (LoginUserResponse) { option (google.api.http) = { post: "/api/v1/user/login" body: "*" }; + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + description: "Use this API to login user"; + summary: "login user"; + }; + } + rpc UpdateUser (UpdateUserRequest) returns (UpdateUserResponse) { + option (google.api.http) = { + patch: "/api/v1/user" + body: "*" + }; + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + description: "Use this API to update user"; + summary: "Update user"; + }; } } diff --git a/internal/delivery/grpc/proto/user.proto b/internal/delivery/grpc/proto/user.proto index 1fa69c4..c0d761d 100644 --- a/internal/delivery/grpc/proto/user.proto +++ b/internal/delivery/grpc/proto/user.proto @@ -4,7 +4,7 @@ package pb; import "google/protobuf/timestamp.proto"; -option go_package = "github.com/cukhoaimon/SimpleBank/pb"; +option go_package = "github.com/cukhoaimon/SimpleBank/internal/delivery/grpc/pb"; message User { string username = 1; diff --git a/internal/delivery/http/account_test.go b/internal/delivery/http/account_test.go index 3311ce5..bce51ef 100644 --- a/internal/delivery/http/account_test.go +++ b/internal/delivery/http/account_test.go @@ -217,8 +217,6 @@ func TestServer_createAccount(t *testing.T) { } } -// TODO: add authorization to listAccount -// TODO: rewrite listAccount for more robust func TestServer_listAccount(t *testing.T) { user, _ := randomUser(t) n := 10 diff --git a/internal/delivery/http/mock/store.go b/internal/delivery/http/mock/store.go index fb51988..3d4c1fa 100644 --- a/internal/delivery/http/mock/store.go +++ b/internal/delivery/http/mock/store.go @@ -304,3 +304,18 @@ func (mr *MockStoreMockRecorder) UpdateAccount(arg0, arg1 interface{}) *gomock.C mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateAccount", reflect.TypeOf((*MockStore)(nil).UpdateAccount), arg0, arg1) } + +// UpdateUser mocks base method. +func (m *MockStore) UpdateUser(arg0 context.Context, arg1 usecase.UpdateUserParams) (usecase.User, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateUser", arg0, arg1) + ret0, _ := ret[0].(usecase.User) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UpdateUser indicates an expected call of UpdateUser. +func (mr *MockStoreMockRecorder) UpdateUser(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateUser", reflect.TypeOf((*MockStore)(nil).UpdateUser), arg0, arg1) +} diff --git a/internal/usecase/query/user.sql b/internal/usecase/query/user.sql index 0ed71c2..576fa2c 100644 --- a/internal/usecase/query/user.sql +++ b/internal/usecase/query/user.sql @@ -11,3 +11,13 @@ INSERT INTO users ( -- name: GetUser :one SELECT * FROM users WHERE username = $1 LIMIT 1; + +-- name: UpdateUser :one +UPDATE users +SET + hashed_password = COALESCE(sqlc.narg(hashed_password), hashed_password), + full_name = COALESCE(sqlc.narg(full_name), full_name), + email = COALESCE(sqlc.narg(email), email) +WHERE + username = sqlc.arg(username) +RETURNING *; \ No newline at end of file diff --git a/internal/usecase/sqlc/querier.go b/internal/usecase/sqlc/querier.go index 7dd0fb9..784cf9a 100644 --- a/internal/usecase/sqlc/querier.go +++ b/internal/usecase/sqlc/querier.go @@ -28,6 +28,7 @@ type Querier interface { ListEntries(ctx context.Context, arg ListEntriesParams) ([]Entry, error) ListTransfers(ctx context.Context, arg ListTransfersParams) ([]Transfer, error) UpdateAccount(ctx context.Context, arg UpdateAccountParams) (Account, error) + UpdateUser(ctx context.Context, arg UpdateUserParams) (User, error) } var _ Querier = (*Queries)(nil) diff --git a/internal/usecase/sqlc/user.sql.go b/internal/usecase/sqlc/user.sql.go index 9706a46..b4cf81c 100644 --- a/internal/usecase/sqlc/user.sql.go +++ b/internal/usecase/sqlc/user.sql.go @@ -7,6 +7,7 @@ package usecase import ( "context" + "database/sql" ) const createUser = `-- name: CreateUser :one @@ -64,3 +65,40 @@ func (q *Queries) GetUser(ctx context.Context, username string) (User, error) { ) return i, err } + +const updateUser = `-- name: UpdateUser :one +UPDATE users +SET + hashed_password = COALESCE($1, hashed_password), + full_name = COALESCE($2, full_name), + email = COALESCE($3, email) +WHERE + username = $4 +RETURNING username, hashed_password, full_name, email, password_changed_at, created_at +` + +type UpdateUserParams struct { + HashedPassword sql.NullString `json:"hashed_password"` + FullName sql.NullString `json:"full_name"` + Email sql.NullString `json:"email"` + Username string `json:"username"` +} + +func (q *Queries) UpdateUser(ctx context.Context, arg UpdateUserParams) (User, error) { + row := q.db.QueryRowContext(ctx, updateUser, + arg.HashedPassword, + arg.FullName, + arg.Email, + arg.Username, + ) + var i User + err := row.Scan( + &i.Username, + &i.HashedPassword, + &i.FullName, + &i.Email, + &i.PasswordChangedAt, + &i.CreatedAt, + ) + return i, err +} diff --git a/internal/usecase/sqlc/user.sql_test.go b/internal/usecase/sqlc/user.sql_test.go index ff6948e..5578a3e 100644 --- a/internal/usecase/sqlc/user.sql_test.go +++ b/internal/usecase/sqlc/user.sql_test.go @@ -2,6 +2,7 @@ package usecase import ( "context" + "database/sql" "github.com/cukhoaimon/SimpleBank/utils" "github.com/stretchr/testify/require" "testing" @@ -46,3 +47,72 @@ func TestQueries_GetUser(t *testing.T) { require.Nil(t, err) require.Equal(t, want, have) } + +func TestQueries_UpdateUser(t *testing.T) { + oldUser := createRandomUser(t) + + newHashedPassword, err := utils.HashPassword("secret") + require.Nil(t, err) + + tests := []struct { + name string + params UpdateUserParams + assertFunc func(t *testing.T, updated User, params UpdateUserParams) + }{ + { + name: "Update email", + params: UpdateUserParams{ + Email: newSqlString(utils.RandomEmail()), + Username: oldUser.Username, + }, + assertFunc: func(t *testing.T, updated User, params UpdateUserParams) { + require.Equal(t, params.Email.String, updated.Email) + require.Equal(t, oldUser.FullName, updated.FullName) + require.Equal(t, oldUser.Username, updated.Username) + }, + }, + { + name: "Update password", + params: UpdateUserParams{ + // new password is secret + HashedPassword: newSqlString(newHashedPassword), + Username: oldUser.Username, + }, + assertFunc: func(t *testing.T, updated User, params UpdateUserParams) { + require.Nil(t, utils.CheckPassword("secret", updated.HashedPassword)) + require.Equal(t, oldUser.Email, updated.Email) + require.Equal(t, oldUser.FullName, updated.FullName) + require.Equal(t, oldUser.Username, updated.Username) + }, + }, + { + name: "Update full name", + params: UpdateUserParams{ + FullName: newSqlString(utils.RandomOwner()), + Username: oldUser.Username, + }, + assertFunc: func(t *testing.T, updated User, params UpdateUserParams) { + require.Equal(t, params.FullName.String, updated.FullName) + require.Equal(t, oldUser.Email, updated.Email) + require.Equal(t, oldUser.Username, updated.Username) + }, + }, + } + + for _, tt := range tests { + go t.Run(tt.name, func(t *testing.T) { + updatedUser, err := testQuery.UpdateUser(context.Background(), tt.params) + require.Nil(t, err) + require.NotEmpty(t, updatedUser) + + tt.assertFunc(t, updatedUser, tt.params) + }) + } +} + +func newSqlString(value string) sql.NullString { + return sql.NullString{ + String: value, + Valid: true, + } +} diff --git a/pkg/grpc/server.go b/pkg/grpc/server.go index 9a30d61..d5a9eaf 100644 --- a/pkg/grpc/server.go +++ b/pkg/grpc/server.go @@ -18,7 +18,6 @@ import ( // Server serves gRPC request type Server struct { - pb.UnimplementedSimpleBankServer Handler *gapi.Handler } @@ -35,9 +34,7 @@ func NewServer(store db.Store, config utils.Config) (*Server, error) { Config: config, } - server := &Server{Handler: handler} - - return server, nil + return &Server{Handler: handler}, nil } // Run will run gRPC server with provided store and config @@ -48,7 +45,7 @@ func Run(store db.Store, config utils.Config) { } gRPCServer := grpc.NewServer() - pb.RegisterSimpleBankServer(gRPCServer, server) + pb.RegisterSimpleBankServer(gRPCServer, server.Handler) // allow client to know what RPCs currently available in server reflection.Register(gRPCServer) @@ -85,7 +82,7 @@ func RunGatewayServer(store db.Store, config utils.Config) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - if err = pb.RegisterSimpleBankHandlerServer(ctx, grpcMux, server); err != nil { + if err = pb.RegisterSimpleBankHandlerServer(ctx, grpcMux, server.Handler); err != nil { log.Fatalf("Cannot register handler server: %s", err) }