diff --git a/.env.example b/.env.example index 4d6aa63..4562452 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,4 @@ -API_NAME=gomora +API_NAME=celeste API_ENV=local API_URL_GRPC=http://localhost API_URL_GRPC_PORT=9090 diff --git a/Makefile b/Makefile index 4680423..612648e 100644 --- a/Makefile +++ b/Makefile @@ -17,13 +17,13 @@ lint: .PHONY: build build: mkdir -p bin - go build -o bin/gomora \ + go build -o bin/celeste \ cmd/main.go .PHONY: build-dev build-dev: mkdir -p bin - go build -race -o bin/gomora \ + go build -race -o bin/celeste \ cmd/main.go .PHONY: test @@ -32,19 +32,19 @@ test: .PHONY: run run: build - ./bin/gomora + ./bin/celeste .PHONY: run-dev run-dev: build-dev - ./bin/gomora + ./bin/celeste .PHONY: up up: docker compose down docker compose up -d --build -.PHONY: schema -schema: +.PHONY: migrate-schema +migrate-schema: mkdir -p infrastructures/database/mysql/migrations migrate create -ext sql -dir infrastructures/database/mysql/migrations -seq ${NAME} diff --git a/cmd/main.go b/cmd/main.go index 06f62ba..af32fc0 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -17,8 +17,8 @@ import ( "github.com/joho/godotenv" - "gomora/interfaces/http/grpc" - "gomora/interfaces/http/rest" + "celeste/interfaces/http/grpc" + "celeste/interfaces/http/rest" ) func init() { diff --git a/docker-compose.yml b/docker-compose.yml index eb5b47c..738e42e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,5 +1,5 @@ services: - gomora: + celeste: build: . container_name: ${API_NAME} mem_limit: "3g" diff --git a/docs/index.html b/docs/index.html index 4a6e8a8..aeadafd 100644 --- a/docs/index.html +++ b/docs/index.html @@ -7,7 +7,7 @@ name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /> - Gomora API Documentation + Celeste API Documentation google.protobuf.Timestamp - 0, // 1: record.RecordCommandService.CreateRecord:input_type -> record.CreateRecordRequest - 1, // 2: record.RecordQueryService.GetRecordByID:input_type -> record.GetRecordRequest - 2, // 3: record.RecordCommandService.CreateRecord:output_type -> record.RecordResponse - 2, // 4: record.RecordQueryService.GetRecordByID:output_type -> record.RecordResponse + 3, // 0: record.UserResponse.createdAt:type_name -> google.protobuf.Timestamp + 0, // 1: record.UserCommandService.CreateUser:input_type -> record.CreateUserRequest + 1, // 2: record.UserQueryService.GetUserByID:input_type -> record.GetUserRequest + 2, // 3: record.UserCommandService.CreateUser:output_type -> record.UserResponse + 2, // 4: record.UserQueryService.GetUserByID:output_type -> record.UserResponse 3, // [3:5] is the sub-list for method output_type 1, // [1:3] is the sub-list for method input_type 1, // [1:1] is the sub-list for extension type_name @@ -270,7 +270,7 @@ func file_module_record_interfaces_http_grpc_pb_record_proto_init() { } if !protoimpl.UnsafeEnabled { file_module_record_interfaces_http_grpc_pb_record_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CreateRecordRequest); i { + switch v := v.(*CreateUserRequest); i { case 0: return &v.state case 1: @@ -282,7 +282,7 @@ func file_module_record_interfaces_http_grpc_pb_record_proto_init() { } } file_module_record_interfaces_http_grpc_pb_record_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetRecordRequest); i { + switch v := v.(*GetUserRequest); i { case 0: return &v.state case 1: @@ -294,7 +294,7 @@ func file_module_record_interfaces_http_grpc_pb_record_proto_init() { } } file_module_record_interfaces_http_grpc_pb_record_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RecordResponse); i { + switch v := v.(*UserResponse); i { case 0: return &v.state case 1: @@ -334,144 +334,144 @@ var _ grpc.ClientConnInterface // is compatible with the grpc package it is being compiled against. const _ = grpc.SupportPackageIsVersion6 -// RecordCommandServiceClient is the client API for RecordCommandService service. +// UserCommandServiceClient is the client API for UserCommandService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. -type RecordCommandServiceClient interface { - CreateRecord(ctx context.Context, in *CreateRecordRequest, opts ...grpc.CallOption) (*RecordResponse, error) +type UserCommandServiceClient interface { + CreateUser(ctx context.Context, in *CreateUserRequest, opts ...grpc.CallOption) (*UserResponse, error) } type recordCommandServiceClient struct { cc grpc.ClientConnInterface } -func NewRecordCommandServiceClient(cc grpc.ClientConnInterface) RecordCommandServiceClient { +func NewUserCommandServiceClient(cc grpc.ClientConnInterface) UserCommandServiceClient { return &recordCommandServiceClient{cc} } -func (c *recordCommandServiceClient) CreateRecord(ctx context.Context, in *CreateRecordRequest, opts ...grpc.CallOption) (*RecordResponse, error) { - out := new(RecordResponse) - err := c.cc.Invoke(ctx, "/record.RecordCommandService/CreateRecord", in, out, opts...) +func (c *recordCommandServiceClient) CreateUser(ctx context.Context, in *CreateUserRequest, opts ...grpc.CallOption) (*UserResponse, error) { + out := new(UserResponse) + err := c.cc.Invoke(ctx, "/record.UserCommandService/CreateUser", in, out, opts...) if err != nil { return nil, err } return out, nil } -// RecordCommandServiceServer is the server API for RecordCommandService service. -type RecordCommandServiceServer interface { - CreateRecord(context.Context, *CreateRecordRequest) (*RecordResponse, error) +// UserCommandServiceServer is the server API for UserCommandService service. +type UserCommandServiceServer interface { + CreateUser(context.Context, *CreateUserRequest) (*UserResponse, error) } -// UnimplementedRecordCommandServiceServer can be embedded to have forward compatible implementations. -type UnimplementedRecordCommandServiceServer struct { +// UnimplementedUserCommandServiceServer can be embedded to have forward compatible implementations. +type UnimplementedUserCommandServiceServer struct { } -func (*UnimplementedRecordCommandServiceServer) CreateRecord(context.Context, *CreateRecordRequest) (*RecordResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method CreateRecord not implemented") +func (*UnimplementedUserCommandServiceServer) CreateUser(context.Context, *CreateUserRequest) (*UserResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateUser not implemented") } -func RegisterRecordCommandServiceServer(s *grpc.Server, srv RecordCommandServiceServer) { - s.RegisterService(&_RecordCommandService_serviceDesc, srv) +func RegisterUserCommandServiceServer(s *grpc.Server, srv UserCommandServiceServer) { + s.RegisterService(&_UserCommandService_serviceDesc, srv) } -func _RecordCommandService_CreateRecord_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(CreateRecordRequest) +func _UserCommandService_CreateUser_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateUserRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(RecordCommandServiceServer).CreateRecord(ctx, in) + return srv.(UserCommandServiceServer).CreateUser(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/record.RecordCommandService/CreateRecord", + FullMethod: "/record.UserCommandService/CreateUser", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(RecordCommandServiceServer).CreateRecord(ctx, req.(*CreateRecordRequest)) + return srv.(UserCommandServiceServer).CreateUser(ctx, req.(*CreateUserRequest)) } return interceptor(ctx, in, info, handler) } -var _RecordCommandService_serviceDesc = grpc.ServiceDesc{ - ServiceName: "record.RecordCommandService", - HandlerType: (*RecordCommandServiceServer)(nil), +var _UserCommandService_serviceDesc = grpc.ServiceDesc{ + ServiceName: "record.UserCommandService", + HandlerType: (*UserCommandServiceServer)(nil), Methods: []grpc.MethodDesc{ { - MethodName: "CreateRecord", - Handler: _RecordCommandService_CreateRecord_Handler, + MethodName: "CreateUser", + Handler: _UserCommandService_CreateUser_Handler, }, }, Streams: []grpc.StreamDesc{}, Metadata: "module/record/interfaces/http/grpc/pb/record.proto", } -// RecordQueryServiceClient is the client API for RecordQueryService service. +// UserQueryServiceClient is the client API for UserQueryService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. -type RecordQueryServiceClient interface { - GetRecordByID(ctx context.Context, in *GetRecordRequest, opts ...grpc.CallOption) (*RecordResponse, error) +type UserQueryServiceClient interface { + GetUserByID(ctx context.Context, in *GetUserRequest, opts ...grpc.CallOption) (*UserResponse, error) } type recordQueryServiceClient struct { cc grpc.ClientConnInterface } -func NewRecordQueryServiceClient(cc grpc.ClientConnInterface) RecordQueryServiceClient { +func NewUserQueryServiceClient(cc grpc.ClientConnInterface) UserQueryServiceClient { return &recordQueryServiceClient{cc} } -func (c *recordQueryServiceClient) GetRecordByID(ctx context.Context, in *GetRecordRequest, opts ...grpc.CallOption) (*RecordResponse, error) { - out := new(RecordResponse) - err := c.cc.Invoke(ctx, "/record.RecordQueryService/GetRecordByID", in, out, opts...) +func (c *recordQueryServiceClient) GetUserByID(ctx context.Context, in *GetUserRequest, opts ...grpc.CallOption) (*UserResponse, error) { + out := new(UserResponse) + err := c.cc.Invoke(ctx, "/record.UserQueryService/GetUserByID", in, out, opts...) if err != nil { return nil, err } return out, nil } -// RecordQueryServiceServer is the server API for RecordQueryService service. -type RecordQueryServiceServer interface { - GetRecordByID(context.Context, *GetRecordRequest) (*RecordResponse, error) +// UserQueryServiceServer is the server API for UserQueryService service. +type UserQueryServiceServer interface { + GetUserByID(context.Context, *GetUserRequest) (*UserResponse, error) } -// UnimplementedRecordQueryServiceServer can be embedded to have forward compatible implementations. -type UnimplementedRecordQueryServiceServer struct { +// UnimplementedUserQueryServiceServer can be embedded to have forward compatible implementations. +type UnimplementedUserQueryServiceServer struct { } -func (*UnimplementedRecordQueryServiceServer) GetRecordByID(context.Context, *GetRecordRequest) (*RecordResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method GetRecordByID not implemented") +func (*UnimplementedUserQueryServiceServer) GetUserByID(context.Context, *GetUserRequest) (*UserResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetUserByID not implemented") } -func RegisterRecordQueryServiceServer(s *grpc.Server, srv RecordQueryServiceServer) { - s.RegisterService(&_RecordQueryService_serviceDesc, srv) +func RegisterUserQueryServiceServer(s *grpc.Server, srv UserQueryServiceServer) { + s.RegisterService(&_UserQueryService_serviceDesc, srv) } -func _RecordQueryService_GetRecordByID_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetRecordRequest) +func _UserQueryService_GetUserByID_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetUserRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(RecordQueryServiceServer).GetRecordByID(ctx, in) + return srv.(UserQueryServiceServer).GetUserByID(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/record.RecordQueryService/GetRecordByID", + FullMethod: "/record.UserQueryService/GetUserByID", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(RecordQueryServiceServer).GetRecordByID(ctx, req.(*GetRecordRequest)) + return srv.(UserQueryServiceServer).GetUserByID(ctx, req.(*GetUserRequest)) } return interceptor(ctx, in, info, handler) } -var _RecordQueryService_serviceDesc = grpc.ServiceDesc{ - ServiceName: "record.RecordQueryService", - HandlerType: (*RecordQueryServiceServer)(nil), +var _UserQueryService_serviceDesc = grpc.ServiceDesc{ + ServiceName: "record.UserQueryService", + HandlerType: (*UserQueryServiceServer)(nil), Methods: []grpc.MethodDesc{ { - MethodName: "GetRecordByID", - Handler: _RecordQueryService_GetRecordByID_Handler, + MethodName: "GetUserByID", + Handler: _UserQueryService_GetUserByID_Handler, }, }, Streams: []grpc.StreamDesc{}, diff --git a/module/record/interfaces/http/grpc/pb/record.proto b/module/iam/interfaces/http/grpc/pb/record.proto similarity index 100% rename from module/record/interfaces/http/grpc/pb/record.proto rename to module/iam/interfaces/http/grpc/pb/record.proto diff --git a/module/iam/interfaces/http/rest/IAMCommandController.go b/module/iam/interfaces/http/rest/IAMCommandController.go new file mode 100644 index 0000000..117dd7a --- /dev/null +++ b/module/iam/interfaces/http/rest/IAMCommandController.go @@ -0,0 +1,10 @@ +package rest + +import ( + "celeste/module/iam/application" +) + +// IAMCommandController request controller for iam command +type IAMCommandController struct { + application.IAMCommandServiceInterface +} diff --git a/module/iam/interfaces/http/rest/IAMQueryController.go b/module/iam/interfaces/http/rest/IAMQueryController.go new file mode 100644 index 0000000..83c564e --- /dev/null +++ b/module/iam/interfaces/http/rest/IAMQueryController.go @@ -0,0 +1,10 @@ +package rest + +import ( + "celeste/module/iam/application" +) + +// IAMQueryController request controller for iam query +type IAMQueryController struct { + application.IAMQueryServiceInterface +} diff --git a/module/record/application/RecordCommandServiceInterface.go b/module/record/application/RecordCommandServiceInterface.go deleted file mode 100644 index 9d87858..0000000 --- a/module/record/application/RecordCommandServiceInterface.go +++ /dev/null @@ -1,13 +0,0 @@ -package application - -import ( - "context" - - "gomora/module/record/domain/entity" - "gomora/module/record/infrastructure/service/types" -) - -// RecordCommandServiceInterface holds the implementable methods for the record command service -type RecordCommandServiceInterface interface { - CreateRecord(ctx context.Context, data types.CreateRecord) (entity.Record, error) -} diff --git a/module/record/application/RecordQueryServiceInterface.go b/module/record/application/RecordQueryServiceInterface.go deleted file mode 100644 index 9c3b57e..0000000 --- a/module/record/application/RecordQueryServiceInterface.go +++ /dev/null @@ -1,12 +0,0 @@ -package application - -import ( - "context" - - "gomora/module/record/domain/entity" -) - -// RecordQueryServiceInterface holds the implementable methods for the record query service -type RecordQueryServiceInterface interface { - GetRecordByID(ctx context.Context, ID string) (entity.Record, error) -} diff --git a/module/record/domain/entity/Record.go b/module/record/domain/entity/Record.go deleted file mode 100644 index a18d58f..0000000 --- a/module/record/domain/entity/Record.go +++ /dev/null @@ -1,17 +0,0 @@ -package entity - -import ( - "time" -) - -// Record holds the record entity fields -type Record struct { - ID string - Data string - CreatedAt time.Time `db:"created_at"` -} - -// GetModelName returns the model name of record entity that can be used for naming schemas -func (entity *Record) GetModelName() string { - return "records" -} diff --git a/module/record/domain/repository/RecordCommandRepositoryInterface.go b/module/record/domain/repository/RecordCommandRepositoryInterface.go deleted file mode 100644 index 61c122b..0000000 --- a/module/record/domain/repository/RecordCommandRepositoryInterface.go +++ /dev/null @@ -1,11 +0,0 @@ -package repository - -import ( - "gomora/module/record/domain/entity" - "gomora/module/record/infrastructure/repository/types" -) - -// RecordCommandRepositoryInterface holds the implementable methods for record command repository -type RecordCommandRepositoryInterface interface { - InsertRecord(data types.CreateRecord) (entity.Record, error) -} diff --git a/module/record/domain/repository/RecordQueryRepositoryInterface.go b/module/record/domain/repository/RecordQueryRepositoryInterface.go deleted file mode 100644 index 1f4c6c2..0000000 --- a/module/record/domain/repository/RecordQueryRepositoryInterface.go +++ /dev/null @@ -1,10 +0,0 @@ -package repository - -import ( - "gomora/module/record/domain/entity" -) - -// RecordQueryRepositoryInterface holds the implementable method for record query repository -type RecordQueryRepositoryInterface interface { - SelectRecordByID(ID string) (entity.Record, error) -} diff --git a/module/record/infrastructure/repository/RecordCommandRepository.go b/module/record/infrastructure/repository/RecordCommandRepository.go deleted file mode 100644 index ee57357..0000000 --- a/module/record/infrastructure/repository/RecordCommandRepository.go +++ /dev/null @@ -1,38 +0,0 @@ -package repository - -import ( - "errors" - "fmt" - - "github.com/go-sql-driver/mysql" - - "gomora/infrastructures/database/mysql/types" - apiError "gomora/internal/errors" - "gomora/module/record/domain/entity" - repositoryTypes "gomora/module/record/infrastructure/repository/types" -) - -// RecordCommandRepository handles the record command repository logic -type RecordCommandRepository struct { - types.MySQLDBHandlerInterface -} - -// InsertRecord creates a new record -func (repository *RecordCommandRepository) InsertRecord(data repositoryTypes.CreateRecord) (entity.Record, error) { - record := entity.Record{ - ID: data.ID, - Data: data.Data, - } - - stmt := fmt.Sprintf("INSERT INTO %s (id, data) VALUES (:id, :data)", record.GetModelName()) - _, err := repository.MySQLDBHandlerInterface.Execute(stmt, record) - if err != nil { - var mysqlErr *mysql.MySQLError - if errors.As(err, &mysqlErr) && mysqlErr.Number == 1062 { - return entity.Record{}, errors.New(apiError.DuplicateRecord) - } - return entity.Record{}, errors.New(apiError.DatabaseError) - } - - return record, nil -} diff --git a/module/record/infrastructure/repository/RecordCommandRepositoryCircuitBreaker.go b/module/record/infrastructure/repository/RecordCommandRepositoryCircuitBreaker.go deleted file mode 100644 index ef373bc..0000000 --- a/module/record/infrastructure/repository/RecordCommandRepositoryCircuitBreaker.go +++ /dev/null @@ -1,39 +0,0 @@ -package repository - -import ( - "github.com/afex/hystrix-go/hystrix" - - hystrix_config "gomora/configs/hystrix" - "gomora/module/record/domain/entity" - "gomora/module/record/domain/repository" - repositoryTypes "gomora/module/record/infrastructure/repository/types" -) - -// RecordCommandRepositoryCircuitBreaker circuit breaker for record command repository -type RecordCommandRepositoryCircuitBreaker struct { - repository.RecordCommandRepositoryInterface -} - -var config = hystrix_config.Config{} - -// InsertRecord decorator pattern to insert record -func (repository *RecordCommandRepositoryCircuitBreaker) InsertRecord(data repositoryTypes.CreateRecord) (entity.Record, error) { - output := make(chan entity.Record, 1) - hystrix.ConfigureCommand("insert_record", config.Settings()) - errors := hystrix.Go("insert_record", func() error { - record, err := repository.RecordCommandRepositoryInterface.InsertRecord(data) - if err != nil { - return err - } - - output <- record - return nil - }, nil) - - select { - case out := <-output: - return out, nil - case err := <-errors: - return entity.Record{}, err - } -} diff --git a/module/record/infrastructure/repository/RecordQueryRepository.go b/module/record/infrastructure/repository/RecordQueryRepository.go deleted file mode 100644 index a31c534..0000000 --- a/module/record/infrastructure/repository/RecordQueryRepository.go +++ /dev/null @@ -1,35 +0,0 @@ -package repository - -import ( - "database/sql" - "errors" - "fmt" - - "gomora/infrastructures/database/mysql/types" - apiError "gomora/internal/errors" - "gomora/module/record/domain/entity" -) - -// RecordQueryRepository handles the record query repository logic -type RecordQueryRepository struct { - types.MySQLDBHandlerInterface -} - -// SelectRecordByID select a record by id -func (repository *RecordQueryRepository) SelectRecordByID(ID string) (entity.Record, error) { - var record entity.Record - - stmt := fmt.Sprintf("SELECT * FROM %s WHERE id=:id", record.GetModelName()) - err := repository.QueryRow(stmt, map[string]interface{}{ - "id": ID, - }, &record) - if err != nil { - if err == sql.ErrNoRows { - return record, errors.New(apiError.MissingRecord) - } - - return record, errors.New(apiError.DatabaseError) - } - - return record, nil -} diff --git a/module/record/infrastructure/repository/RecordQueryRepositoryCircuitBreaker.go b/module/record/infrastructure/repository/RecordQueryRepositoryCircuitBreaker.go deleted file mode 100644 index 76aeb00..0000000 --- a/module/record/infrastructure/repository/RecordQueryRepositoryCircuitBreaker.go +++ /dev/null @@ -1,35 +0,0 @@ -package repository - -import ( - "github.com/afex/hystrix-go/hystrix" - - "gomora/module/record/domain/entity" - "gomora/module/record/domain/repository" -) - -// RecordQueryRepositoryCircuitBreaker holds the implementable methods for record query circuitbreaker -type RecordQueryRepositoryCircuitBreaker struct { - repository.RecordQueryRepositoryInterface -} - -// SelectRecordByID decorator pattern for select record repository -func (repository *RecordQueryRepositoryCircuitBreaker) SelectRecordByID(ID string) (entity.Record, error) { - output := make(chan entity.Record, 1) - hystrix.ConfigureCommand("select_record_by_id", config.Settings()) - errors := hystrix.Go("select_record_by_id", func() error { - record, err := repository.RecordQueryRepositoryInterface.SelectRecordByID(ID) - if err != nil { - return err - } - - output <- record - return nil - }, nil) - - select { - case out := <-output: - return out, nil - case err := <-errors: - return entity.Record{}, err - } -} diff --git a/module/record/infrastructure/repository/types/dto.go b/module/record/infrastructure/repository/types/dto.go deleted file mode 100644 index 830e716..0000000 --- a/module/record/infrastructure/repository/types/dto.go +++ /dev/null @@ -1,7 +0,0 @@ -package types - -// CreateRecord data struct for create record repository -type CreateRecord struct { - ID string - Data string -} diff --git a/module/record/infrastructure/service/RecordCommandService.go b/module/record/infrastructure/service/RecordCommandService.go deleted file mode 100644 index b51ad48..0000000 --- a/module/record/infrastructure/service/RecordCommandService.go +++ /dev/null @@ -1,42 +0,0 @@ -package service - -import ( - "context" - - "github.com/segmentio/ksuid" - - "gomora/module/record/domain/entity" - "gomora/module/record/domain/repository" - repositoryTypes "gomora/module/record/infrastructure/repository/types" - "gomora/module/record/infrastructure/service/types" -) - -// RecordCommandService handles the record command service logic -type RecordCommandService struct { - repository.RecordCommandRepositoryInterface -} - -// CreateRecord create a record -func (service *RecordCommandService) CreateRecord(ctx context.Context, data types.CreateRecord) (entity.Record, error) { - record := repositoryTypes.CreateRecord{ - ID: data.ID, - Data: data.Data, - } - - // check id if empty create new unique id - if len(record.ID) == 0 { - record.ID = generateID() - } - - res, err := service.RecordCommandRepositoryInterface.InsertRecord(record) - if err != nil { - return entity.Record{}, err - } - - return res, nil -} - -// generateID generates unique id -func generateID() string { - return ksuid.New().String() -} diff --git a/module/record/infrastructure/service/RecordQueryService.go b/module/record/infrastructure/service/RecordQueryService.go deleted file mode 100644 index b211691..0000000 --- a/module/record/infrastructure/service/RecordQueryService.go +++ /dev/null @@ -1,23 +0,0 @@ -package service - -import ( - "context" - - "gomora/module/record/domain/entity" - "gomora/module/record/domain/repository" -) - -// RecordQueryService handles the record query service logic -type RecordQueryService struct { - repository.RecordQueryRepositoryInterface -} - -// GetRecordByID retrieves the record provided by its id -func (service *RecordQueryService) GetRecordByID(ctx context.Context, ID string) (entity.Record, error) { - res, err := service.RecordQueryRepositoryInterface.SelectRecordByID(ID) - if err != nil { - return res, err - } - - return res, nil -} diff --git a/module/record/infrastructure/service/types/dto.go b/module/record/infrastructure/service/types/dto.go deleted file mode 100644 index b2feb3a..0000000 --- a/module/record/infrastructure/service/types/dto.go +++ /dev/null @@ -1,7 +0,0 @@ -package types - -// CreateRecord service types for create record -type CreateRecord struct { - ID string - Data string -} diff --git a/module/record/interfaces/http/grpc/RecordCommandController.go b/module/record/interfaces/http/grpc/RecordCommandController.go deleted file mode 100644 index 13ccc2b..0000000 --- a/module/record/interfaces/http/grpc/RecordCommandController.go +++ /dev/null @@ -1,55 +0,0 @@ -package grpc - -import ( - "context" - "fmt" - "time" - - "github.com/golang/protobuf/ptypes" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - - "gomora/internal/errors" - "gomora/module/record/application" - serviceTypes "gomora/module/record/infrastructure/service/types" - grpcPB "gomora/module/record/interfaces/http/grpc/pb" -) - -// RecordCommandController handles the grpc record command requests -type RecordCommandController struct { - application.RecordCommandServiceInterface -} - -// CreateRecord creates a new record -func (controller *RecordCommandController) CreateRecord(ctx context.Context, req *grpcPB.CreateRecordRequest) (*grpcPB.RecordResponse, error) { - record := serviceTypes.CreateRecord{ - ID: req.Id, - Data: req.Data, - } - - res, err := controller.RecordCommandServiceInterface.CreateRecord(context.TODO(), record) - if err != nil { - var code codes.Code - - switch err.Error() { - case errors.DatabaseError: - code = codes.Internal - case errors.MissingRecord: - code = codes.NotFound - default: - code = codes.Unknown - } - - st := status.New(code, fmt.Sprintf("[RECORD] %s", err.Error())) - - return nil, st.Err() - } - - createProtoTime, _ := ptypes.TimestampProto(time.Now()) - - return &grpcPB.RecordResponse{ - Id: res.ID, - Data: res.Data, - CreatedAt: createProtoTime, - }, nil -} diff --git a/module/user/application/UserCommandServiceInterface.go b/module/user/application/UserCommandServiceInterface.go new file mode 100644 index 0000000..38c3595 --- /dev/null +++ b/module/user/application/UserCommandServiceInterface.go @@ -0,0 +1,13 @@ +package application + +import ( + "context" + + "celeste/module/user/domain/entity" + "celeste/module/user/infrastructure/service/types" +) + +// UserCommandServiceInterface holds the implementable methods for the user command service +type UserCommandServiceInterface interface { + CreateUser(ctx context.Context, data types.CreateUser) (entity.User, error) +} diff --git a/module/user/application/UserQueryServiceInterface.go b/module/user/application/UserQueryServiceInterface.go new file mode 100644 index 0000000..a1dae74 --- /dev/null +++ b/module/user/application/UserQueryServiceInterface.go @@ -0,0 +1,12 @@ +package application + +import ( + "context" + + "celeste/module/user/domain/entity" +) + +// UserQueryServiceInterface holds the implementable methods for the user query service +type UserQueryServiceInterface interface { + GetUserByID(ctx context.Context, ID string) (entity.User, error) +} diff --git a/module/user/domain/entity/User.go b/module/user/domain/entity/User.go new file mode 100644 index 0000000..982a079 --- /dev/null +++ b/module/user/domain/entity/User.go @@ -0,0 +1,17 @@ +package entity + +import ( + "time" +) + +// User holds the user entity fields +type User struct { + ID string + Data string + CreatedAt time.Time `db:"created_at"` +} + +// GetModelName returns the model name of user entity that can be used for naming schemas +func (entity *User) GetModelName() string { + return "users" +} diff --git a/module/user/domain/repository/UserCommandRepositoryInterface.go b/module/user/domain/repository/UserCommandRepositoryInterface.go new file mode 100644 index 0000000..d896986 --- /dev/null +++ b/module/user/domain/repository/UserCommandRepositoryInterface.go @@ -0,0 +1,11 @@ +package repository + +import ( + "celeste/module/user/domain/entity" + "celeste/module/user/infrastructure/repository/types" +) + +// UserCommandRepositoryInterface holds the implementable methods for user command repository +type UserCommandRepositoryInterface interface { + InsertUser(data types.CreateUser) (entity.User, error) +} diff --git a/module/user/domain/repository/UserQueryRepositoryInterface.go b/module/user/domain/repository/UserQueryRepositoryInterface.go new file mode 100644 index 0000000..940ccd9 --- /dev/null +++ b/module/user/domain/repository/UserQueryRepositoryInterface.go @@ -0,0 +1,10 @@ +package repository + +import ( + "celeste/module/user/domain/entity" +) + +// UserQueryRepositoryInterface holds the implementable method for user query repository +type UserQueryRepositoryInterface interface { + SelectUserByID(ID string) (entity.User, error) +} diff --git a/module/user/infrastructure/repository/UserCommandRepository.go b/module/user/infrastructure/repository/UserCommandRepository.go new file mode 100644 index 0000000..cae448c --- /dev/null +++ b/module/user/infrastructure/repository/UserCommandRepository.go @@ -0,0 +1,38 @@ +package repository + +import ( + "errors" + "fmt" + + "github.com/go-sql-driver/mysql" + + "celeste/infrastructures/database/mysql/types" + apiError "celeste/internal/errors" + "celeste/module/user/domain/entity" + repositoryTypes "celeste/module/user/infrastructure/repository/types" +) + +// UserCommandRepository handles the user command repository logic +type UserCommandRepository struct { + types.MySQLDBHandlerInterface +} + +// InsertUser creates a new user +func (repository *UserCommandRepository) InsertUser(data repositoryTypes.CreateUser) (entity.User, error) { + user := entity.User{ + ID: data.ID, + Data: data.Data, + } + + stmt := fmt.Sprintf("INSERT INTO %s (id, data) VALUES (:id, :data)", user.GetModelName()) + _, err := repository.MySQLDBHandlerInterface.Execute(stmt, user) + if err != nil { + var mysqlErr *mysql.MySQLError + if errors.As(err, &mysqlErr) && mysqlErr.Number == 1062 { + return entity.User{}, errors.New(apiError.DuplicateRecord) + } + return entity.User{}, errors.New(apiError.DatabaseError) + } + + return user, nil +} diff --git a/module/user/infrastructure/repository/UserCommandRepositoryCircuitBreaker.go b/module/user/infrastructure/repository/UserCommandRepositoryCircuitBreaker.go new file mode 100644 index 0000000..234fd16 --- /dev/null +++ b/module/user/infrastructure/repository/UserCommandRepositoryCircuitBreaker.go @@ -0,0 +1,39 @@ +package repository + +import ( + "github.com/afex/hystrix-go/hystrix" + + hystrix_config "celeste/configs/hystrix" + "celeste/module/user/domain/entity" + "celeste/module/user/domain/repository" + repositoryTypes "celeste/module/user/infrastructure/repository/types" +) + +// UserCommandRepositoryCircuitBreaker circuit breaker for user command repository +type UserCommandRepositoryCircuitBreaker struct { + repository.UserCommandRepositoryInterface +} + +var config = hystrix_config.Config{} + +// InsertUser decorator pattern to insert user +func (repository *UserCommandRepositoryCircuitBreaker) InsertUser(data repositoryTypes.CreateUser) (entity.User, error) { + output := make(chan entity.User, 1) + hystrix.ConfigureCommand("insert_user", config.Settings()) + errors := hystrix.Go("insert_user", func() error { + user, err := repository.UserCommandRepositoryInterface.InsertUser(data) + if err != nil { + return err + } + + output <- user + return nil + }, nil) + + select { + case out := <-output: + return out, nil + case err := <-errors: + return entity.User{}, err + } +} diff --git a/module/user/infrastructure/repository/UserQueryRepository.go b/module/user/infrastructure/repository/UserQueryRepository.go new file mode 100644 index 0000000..5493655 --- /dev/null +++ b/module/user/infrastructure/repository/UserQueryRepository.go @@ -0,0 +1,35 @@ +package repository + +import ( + "database/sql" + "errors" + "fmt" + + "celeste/infrastructures/database/mysql/types" + apiError "celeste/internal/errors" + "celeste/module/user/domain/entity" +) + +// UserQueryRepository handles the user query repository logic +type UserQueryRepository struct { + types.MySQLDBHandlerInterface +} + +// SelectUserByID select a user by id +func (repository *UserQueryRepository) SelectUserByID(ID string) (entity.User, error) { + var user entity.User + + stmt := fmt.Sprintf("SELECT * FROM %s WHERE id=:id", user.GetModelName()) + err := repository.QueryRow(stmt, map[string]interface{}{ + "id": ID, + }, &user) + if err != nil { + if err == sql.ErrNoRows { + return user, errors.New(apiError.MissingRecord) + } + + return user, errors.New(apiError.DatabaseError) + } + + return user, nil +} diff --git a/module/user/infrastructure/repository/UserQueryRepositoryCircuitBreaker.go b/module/user/infrastructure/repository/UserQueryRepositoryCircuitBreaker.go new file mode 100644 index 0000000..f530e76 --- /dev/null +++ b/module/user/infrastructure/repository/UserQueryRepositoryCircuitBreaker.go @@ -0,0 +1,35 @@ +package repository + +import ( + "github.com/afex/hystrix-go/hystrix" + + "celeste/module/user/domain/entity" + "celeste/module/user/domain/repository" +) + +// UserQueryRepositoryCircuitBreaker holds the implementable methods for user query circuitbreaker +type UserQueryRepositoryCircuitBreaker struct { + repository.UserQueryRepositoryInterface +} + +// SelectUserByID decorator pattern for select user repository +func (repository *UserQueryRepositoryCircuitBreaker) SelectUserByID(ID string) (entity.User, error) { + output := make(chan entity.User, 1) + hystrix.ConfigureCommand("select_user_by_id", config.Settings()) + errors := hystrix.Go("select_user_by_id", func() error { + user, err := repository.UserQueryRepositoryInterface.SelectUserByID(ID) + if err != nil { + return err + } + + output <- user + return nil + }, nil) + + select { + case out := <-output: + return out, nil + case err := <-errors: + return entity.User{}, err + } +} diff --git a/module/user/infrastructure/repository/types/dto.go b/module/user/infrastructure/repository/types/dto.go new file mode 100644 index 0000000..7e5f1b4 --- /dev/null +++ b/module/user/infrastructure/repository/types/dto.go @@ -0,0 +1,7 @@ +package types + +// CreateUser data struct for create user repository +type CreateUser struct { + ID string + Data string +} diff --git a/module/user/infrastructure/service/UserCommandService.go b/module/user/infrastructure/service/UserCommandService.go new file mode 100644 index 0000000..99fae5c --- /dev/null +++ b/module/user/infrastructure/service/UserCommandService.go @@ -0,0 +1,42 @@ +package service + +import ( + "context" + + "github.com/segmentio/ksuid" + + "celeste/module/user/domain/entity" + "celeste/module/user/domain/repository" + repositoryTypes "celeste/module/user/infrastructure/repository/types" + "celeste/module/user/infrastructure/service/types" +) + +// UserCommandService handles the user command service logic +type UserCommandService struct { + repository.UserCommandRepositoryInterface +} + +// CreateUser create a user +func (service *UserCommandService) CreateUser(ctx context.Context, data types.CreateUser) (entity.User, error) { + user := repositoryTypes.CreateUser{ + ID: data.ID, + Data: data.Data, + } + + // check id if empty create new unique id + if len(user.ID) == 0 { + user.ID = generateID() + } + + res, err := service.UserCommandRepositoryInterface.InsertUser(user) + if err != nil { + return entity.User{}, err + } + + return res, nil +} + +// generateID generates unique id +func generateID() string { + return ksuid.New().String() +} diff --git a/module/user/infrastructure/service/UserQueryService.go b/module/user/infrastructure/service/UserQueryService.go new file mode 100644 index 0000000..e1ef31f --- /dev/null +++ b/module/user/infrastructure/service/UserQueryService.go @@ -0,0 +1,23 @@ +package service + +import ( + "context" + + "celeste/module/user/domain/entity" + "celeste/module/user/domain/repository" +) + +// UserQueryService handles the user query service logic +type UserQueryService struct { + repository.UserQueryRepositoryInterface +} + +// GetUserByID retrieves the user provided by its id +func (service *UserQueryService) GetUserByID(ctx context.Context, ID string) (entity.User, error) { + res, err := service.UserQueryRepositoryInterface.SelectUserByID(ID) + if err != nil { + return res, err + } + + return res, nil +} diff --git a/module/user/infrastructure/service/types/dto.go b/module/user/infrastructure/service/types/dto.go new file mode 100644 index 0000000..9003eb2 --- /dev/null +++ b/module/user/infrastructure/service/types/dto.go @@ -0,0 +1,7 @@ +package types + +// CreateUser service types for create user +type CreateUser struct { + ID string + Data string +} diff --git a/module/user/interfaces/http/dto.go b/module/user/interfaces/http/dto.go new file mode 100644 index 0000000..ad605d1 --- /dev/null +++ b/module/user/interfaces/http/dto.go @@ -0,0 +1,26 @@ +package http + +import ( + "github.com/go-playground/validator/v10" +) + +var ( + Validate *validator.Validate = validator.New(validator.WithRequiredStructEnabled()) + ValidationErrors map[string]string = map[string]string{ + "CreateUserRequest.ID": "ID field is required.", + "CreateUserRequest.Data": "Data field is required.", + } +) + +// CreateUserRequest request struct for create user +type CreateUserRequest struct { + ID string `json:"id" validate:"required"` + Data string `json:"data" validate:"required"` +} + +// UserResponse response struct +type UserResponse struct { + ID string `json:"id"` + Data string `json:"data"` + CreatedAt int64 `json:"createdAt"` +} diff --git a/module/user/interfaces/http/grpc/UserCommandController.go b/module/user/interfaces/http/grpc/UserCommandController.go new file mode 100644 index 0000000..233b980 --- /dev/null +++ b/module/user/interfaces/http/grpc/UserCommandController.go @@ -0,0 +1,55 @@ +package grpc + +import ( + "context" + "fmt" + "time" + + "github.com/golang/protobuf/ptypes" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "celeste/internal/errors" + "celeste/module/user/application" + serviceTypes "celeste/module/user/infrastructure/service/types" + grpcPB "celeste/module/user/interfaces/http/grpc/pb" +) + +// UserCommandController handles the grpc user command requests +type UserCommandController struct { + application.UserCommandServiceInterface +} + +// CreateUser creates a new user +func (controller *UserCommandController) CreateUser(ctx context.Context, req *grpcPB.CreateUserRequest) (*grpcPB.UserResponse, error) { + user := serviceTypes.CreateUser{ + ID: req.Id, + Data: req.Data, + } + + res, err := controller.UserCommandServiceInterface.CreateUser(context.TODO(), user) + if err != nil { + var code codes.Code + + switch err.Error() { + case errors.DatabaseError: + code = codes.Internal + case errors.MissingRecord: + code = codes.NotFound + default: + code = codes.Unknown + } + + st := status.New(code, fmt.Sprintf("[RECORD] %s", err.Error())) + + return nil, st.Err() + } + + createProtoTime, _ := ptypes.TimestampProto(time.Now()) + + return &grpcPB.UserResponse{ + Id: res.ID, + Data: res.Data, + CreatedAt: createProtoTime, + }, nil +} diff --git a/module/record/interfaces/http/grpc/RecordQueryController.go b/module/user/interfaces/http/grpc/UserQueryController.go similarity index 51% rename from module/record/interfaces/http/grpc/RecordQueryController.go rename to module/user/interfaces/http/grpc/UserQueryController.go index 7b024e9..cafc6b0 100644 --- a/module/record/interfaces/http/grpc/RecordQueryController.go +++ b/module/user/interfaces/http/grpc/UserQueryController.go @@ -8,19 +8,19 @@ import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - "gomora/internal/errors" - "gomora/module/record/application" - grpcPB "gomora/module/record/interfaces/http/grpc/pb" + "celeste/internal/errors" + "celeste/module/user/application" + grpcPB "celeste/module/user/interfaces/http/grpc/pb" ) -// RecordQueryController handles the grpc record query requests -type RecordQueryController struct { - application.RecordQueryServiceInterface +// UserQueryController handles the grpc user query requests +type UserQueryController struct { + application.UserQueryServiceInterface } -// GetRecordByID retrieves the record id from the proto -func (controller *RecordQueryController) GetRecordByID(ctx context.Context, req *grpcPB.GetRecordRequest) (*grpcPB.RecordResponse, error) { - res, err := controller.RecordQueryServiceInterface.GetRecordByID(context.TODO(), req.Id) +// GetUserByID retrieves the user id from the proto +func (controller *UserQueryController) GetUserByID(ctx context.Context, req *grpcPB.GetUserRequest) (*grpcPB.UserResponse, error) { + res, err := controller.UserQueryServiceInterface.GetUserByID(context.TODO(), req.Id) if err != nil { var code codes.Code @@ -40,7 +40,7 @@ func (controller *RecordQueryController) GetRecordByID(ctx context.Context, req createProtoTime, _ := ptypes.TimestampProto(res.CreatedAt) - return &grpcPB.RecordResponse{ + return &grpcPB.UserResponse{ Id: res.ID, Data: res.Data, CreatedAt: createProtoTime, diff --git a/module/user/interfaces/http/grpc/pb/record.pb.go b/module/user/interfaces/http/grpc/pb/record.pb.go new file mode 100644 index 0000000..d0ff16c --- /dev/null +++ b/module/user/interfaces/http/grpc/pb/record.pb.go @@ -0,0 +1,479 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.25.0 +// protoc v3.12.3 +// source: module/record/interfaces/http/grpc/pb/record.proto + +package record + +import ( + context "context" + proto "github.com/golang/protobuf/proto" + timestamp "github.com/golang/protobuf/ptypes/timestamp" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + 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) +) + +// This is a compile-time assertion that a sufficiently up-to-date version +// of the legacy proto package is being used. +const _ = proto.ProtoPackageIsVersion4 + +type CreateUserRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Data string `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` +} + +func (x *CreateUserRequest) Reset() { + *x = CreateUserRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_module_record_interfaces_http_grpc_pb_record_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CreateUserRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateUserRequest) ProtoMessage() {} + +func (x *CreateUserRequest) ProtoReflect() protoreflect.Message { + mi := &file_module_record_interfaces_http_grpc_pb_record_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 CreateUserRequest.ProtoReflect.Descriptor instead. +func (*CreateUserRequest) Descriptor() ([]byte, []int) { + return file_module_record_interfaces_http_grpc_pb_record_proto_rawDescGZIP(), []int{0} +} + +func (x *CreateUserRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *CreateUserRequest) GetData() string { + if x != nil { + return x.Data + } + return "" +} + +type GetUserRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *GetUserRequest) Reset() { + *x = GetUserRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_module_record_interfaces_http_grpc_pb_record_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetUserRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetUserRequest) ProtoMessage() {} + +func (x *GetUserRequest) ProtoReflect() protoreflect.Message { + mi := &file_module_record_interfaces_http_grpc_pb_record_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 GetUserRequest.ProtoReflect.Descriptor instead. +func (*GetUserRequest) Descriptor() ([]byte, []int) { + return file_module_record_interfaces_http_grpc_pb_record_proto_rawDescGZIP(), []int{1} +} + +func (x *GetUserRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type UserResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Data string `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` + CreatedAt *timestamp.Timestamp `protobuf:"bytes,3,opt,name=createdAt,proto3" json:"createdAt,omitempty"` +} + +func (x *UserResponse) Reset() { + *x = UserResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_module_record_interfaces_http_grpc_pb_record_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UserResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UserResponse) ProtoMessage() {} + +func (x *UserResponse) ProtoReflect() protoreflect.Message { + mi := &file_module_record_interfaces_http_grpc_pb_record_proto_msgTypes[2] + 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 UserResponse.ProtoReflect.Descriptor instead. +func (*UserResponse) Descriptor() ([]byte, []int) { + return file_module_record_interfaces_http_grpc_pb_record_proto_rawDescGZIP(), []int{2} +} + +func (x *UserResponse) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *UserResponse) GetData() string { + if x != nil { + return x.Data + } + return "" +} + +func (x *UserResponse) GetCreatedAt() *timestamp.Timestamp { + if x != nil { + return x.CreatedAt + } + return nil +} + +var File_module_record_interfaces_http_grpc_pb_record_proto protoreflect.FileDescriptor + +var file_module_record_interfaces_http_grpc_pb_record_proto_rawDesc = []byte{ + 0x0a, 0x32, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x2f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x2f, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x73, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x2f, + 0x67, 0x72, 0x70, 0x63, 0x2f, 0x70, 0x62, 0x2f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x1a, 0x1f, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x39, 0x0a, + 0x13, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x22, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x52, + 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x6e, 0x0a, 0x0e, + 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, + 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, + 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x61, + 0x74, 0x61, 0x12, 0x38, 0x0a, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x18, + 0x03, 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, 0x32, 0x5d, 0x0a, 0x14, + 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x53, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x12, 0x45, 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, + 0x63, 0x6f, 0x72, 0x64, 0x12, 0x1b, 0x2e, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x2e, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x16, 0x2e, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x72, + 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x32, 0x59, 0x0a, 0x12, 0x52, + 0x65, 0x63, 0x6f, 0x72, 0x64, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x12, 0x43, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x42, 0x79, + 0x49, 0x44, 0x12, 0x18, 0x2e, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x52, + 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x72, + 0x65, 0x63, 0x6f, 0x72, 0x64, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_module_record_interfaces_http_grpc_pb_record_proto_rawDescOnce sync.Once + file_module_record_interfaces_http_grpc_pb_record_proto_rawDescData = file_module_record_interfaces_http_grpc_pb_record_proto_rawDesc +) + +func file_module_record_interfaces_http_grpc_pb_record_proto_rawDescGZIP() []byte { + file_module_record_interfaces_http_grpc_pb_record_proto_rawDescOnce.Do(func() { + file_module_record_interfaces_http_grpc_pb_record_proto_rawDescData = protoimpl.X.CompressGZIP(file_module_record_interfaces_http_grpc_pb_record_proto_rawDescData) + }) + return file_module_record_interfaces_http_grpc_pb_record_proto_rawDescData +} + +var file_module_record_interfaces_http_grpc_pb_record_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_module_record_interfaces_http_grpc_pb_record_proto_goTypes = []interface{}{ + (*CreateUserRequest)(nil), // 0: record.CreateUserRequest + (*GetUserRequest)(nil), // 1: record.GetUserRequest + (*UserResponse)(nil), // 2: record.UserResponse + (*timestamp.Timestamp)(nil), // 3: google.protobuf.Timestamp +} +var file_module_record_interfaces_http_grpc_pb_record_proto_depIdxs = []int32{ + 3, // 0: record.UserResponse.createdAt:type_name -> google.protobuf.Timestamp + 0, // 1: record.UserCommandService.CreateUser:input_type -> record.CreateUserRequest + 1, // 2: record.UserQueryService.GetUserByID:input_type -> record.GetUserRequest + 2, // 3: record.UserCommandService.CreateUser:output_type -> record.UserResponse + 2, // 4: record.UserQueryService.GetUserByID:output_type -> record.UserResponse + 3, // [3:5] is the sub-list for method output_type + 1, // [1:3] 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_module_record_interfaces_http_grpc_pb_record_proto_init() } +func file_module_record_interfaces_http_grpc_pb_record_proto_init() { + if File_module_record_interfaces_http_grpc_pb_record_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_module_record_interfaces_http_grpc_pb_record_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CreateUserRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_module_record_interfaces_http_grpc_pb_record_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetUserRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_module_record_interfaces_http_grpc_pb_record_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UserResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_module_record_interfaces_http_grpc_pb_record_proto_rawDesc, + NumEnums: 0, + NumMessages: 3, + NumExtensions: 0, + NumServices: 2, + }, + GoTypes: file_module_record_interfaces_http_grpc_pb_record_proto_goTypes, + DependencyIndexes: file_module_record_interfaces_http_grpc_pb_record_proto_depIdxs, + MessageInfos: file_module_record_interfaces_http_grpc_pb_record_proto_msgTypes, + }.Build() + File_module_record_interfaces_http_grpc_pb_record_proto = out.File + file_module_record_interfaces_http_grpc_pb_record_proto_rawDesc = nil + file_module_record_interfaces_http_grpc_pb_record_proto_goTypes = nil + file_module_record_interfaces_http_grpc_pb_record_proto_depIdxs = nil +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConnInterface + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion6 + +// UserCommandServiceClient is the client API for UserCommandService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type UserCommandServiceClient interface { + CreateUser(ctx context.Context, in *CreateUserRequest, opts ...grpc.CallOption) (*UserResponse, error) +} + +type recordCommandServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewUserCommandServiceClient(cc grpc.ClientConnInterface) UserCommandServiceClient { + return &recordCommandServiceClient{cc} +} + +func (c *recordCommandServiceClient) CreateUser(ctx context.Context, in *CreateUserRequest, opts ...grpc.CallOption) (*UserResponse, error) { + out := new(UserResponse) + err := c.cc.Invoke(ctx, "/record.UserCommandService/CreateUser", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// UserCommandServiceServer is the server API for UserCommandService service. +type UserCommandServiceServer interface { + CreateUser(context.Context, *CreateUserRequest) (*UserResponse, error) +} + +// UnimplementedUserCommandServiceServer can be embedded to have forward compatible implementations. +type UnimplementedUserCommandServiceServer struct { +} + +func (*UnimplementedUserCommandServiceServer) CreateUser(context.Context, *CreateUserRequest) (*UserResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateUser not implemented") +} + +func RegisterUserCommandServiceServer(s *grpc.Server, srv UserCommandServiceServer) { + s.RegisterService(&_UserCommandService_serviceDesc, srv) +} + +func _UserCommandService_CreateUser_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateUserRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(UserCommandServiceServer).CreateUser(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/record.UserCommandService/CreateUser", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(UserCommandServiceServer).CreateUser(ctx, req.(*CreateUserRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _UserCommandService_serviceDesc = grpc.ServiceDesc{ + ServiceName: "record.UserCommandService", + HandlerType: (*UserCommandServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "CreateUser", + Handler: _UserCommandService_CreateUser_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "module/record/interfaces/http/grpc/pb/record.proto", +} + +// UserQueryServiceClient is the client API for UserQueryService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type UserQueryServiceClient interface { + GetUserByID(ctx context.Context, in *GetUserRequest, opts ...grpc.CallOption) (*UserResponse, error) +} + +type recordQueryServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewUserQueryServiceClient(cc grpc.ClientConnInterface) UserQueryServiceClient { + return &recordQueryServiceClient{cc} +} + +func (c *recordQueryServiceClient) GetUserByID(ctx context.Context, in *GetUserRequest, opts ...grpc.CallOption) (*UserResponse, error) { + out := new(UserResponse) + err := c.cc.Invoke(ctx, "/record.UserQueryService/GetUserByID", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// UserQueryServiceServer is the server API for UserQueryService service. +type UserQueryServiceServer interface { + GetUserByID(context.Context, *GetUserRequest) (*UserResponse, error) +} + +// UnimplementedUserQueryServiceServer can be embedded to have forward compatible implementations. +type UnimplementedUserQueryServiceServer struct { +} + +func (*UnimplementedUserQueryServiceServer) GetUserByID(context.Context, *GetUserRequest) (*UserResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetUserByID not implemented") +} + +func RegisterUserQueryServiceServer(s *grpc.Server, srv UserQueryServiceServer) { + s.RegisterService(&_UserQueryService_serviceDesc, srv) +} + +func _UserQueryService_GetUserByID_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetUserRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(UserQueryServiceServer).GetUserByID(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/record.UserQueryService/GetUserByID", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(UserQueryServiceServer).GetUserByID(ctx, req.(*GetUserRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _UserQueryService_serviceDesc = grpc.ServiceDesc{ + ServiceName: "record.UserQueryService", + HandlerType: (*UserQueryServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GetUserByID", + Handler: _UserQueryService_GetUserByID_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "module/record/interfaces/http/grpc/pb/record.proto", +} diff --git a/module/user/interfaces/http/grpc/pb/record.proto b/module/user/interfaces/http/grpc/pb/record.proto new file mode 100644 index 0000000..f88b24d --- /dev/null +++ b/module/user/interfaces/http/grpc/pb/record.proto @@ -0,0 +1,26 @@ +syntax = "proto3"; +package record; + +import "google/protobuf/timestamp.proto"; + +message CreateRecordRequest { + string id = 1; + string data = 2; +} + +message GetRecordRequest { + string id = 1; +} + +message RecordResponse { + string id = 1; + string data = 2; + google.protobuf.Timestamp createdAt = 3; +} + +service RecordCommandService { + rpc CreateRecord (CreateRecordRequest) returns (RecordResponse) {}; +} +service RecordQueryService { + rpc GetRecordByID (GetRecordRequest) returns (RecordResponse) {}; +} \ No newline at end of file diff --git a/module/record/interfaces/http/rest/RecordCommandController.go b/module/user/interfaces/http/rest/UserCommandController.go similarity index 66% rename from module/record/interfaces/http/rest/RecordCommandController.go rename to module/user/interfaces/http/rest/UserCommandController.go index 2db362e..d059d18 100644 --- a/module/record/interfaces/http/rest/RecordCommandController.go +++ b/module/user/interfaces/http/rest/UserCommandController.go @@ -8,22 +8,22 @@ import ( "github.com/go-playground/validator/v10" - "gomora/interfaces/http/rest/viewmodels" - "gomora/internal/errors" - apiError "gomora/internal/errors" - "gomora/module/record/application" - serviceTypes "gomora/module/record/infrastructure/service/types" - types "gomora/module/record/interfaces/http" + "celeste/interfaces/http/rest/viewmodels" + "celeste/internal/errors" + apiError "celeste/internal/errors" + "celeste/module/user/application" + serviceTypes "celeste/module/user/infrastructure/service/types" + types "celeste/module/user/interfaces/http" ) -// RecordCommandController request controller for record command -type RecordCommandController struct { - application.RecordCommandServiceInterface +// UserCommandController request controller for user command +type UserCommandController struct { + application.UserCommandServiceInterface } -// CreateRecord request handler to create record -func (controller *RecordCommandController) CreateRecord(w http.ResponseWriter, r *http.Request) { - var request types.CreateRecordRequest +// CreateUser request handler to create user +func (controller *UserCommandController) CreateUser(w http.ResponseWriter, r *http.Request) { + var request types.CreateUserRequest if err := json.NewDecoder(r.Body).Decode(&request); err != nil { response := viewmodels.HTTPResponseVM{ @@ -64,12 +64,12 @@ func (controller *RecordCommandController) CreateRecord(w http.ResponseWriter, r return } - record := serviceTypes.CreateRecord{ + user := serviceTypes.CreateUser{ ID: request.ID, Data: request.Data, } - res, err := controller.RecordCommandServiceInterface.CreateRecord(context.TODO(), record) + res, err := controller.UserCommandServiceInterface.CreateUser(context.TODO(), user) if err != nil { var httpCode int var errorMsg string @@ -77,10 +77,10 @@ func (controller *RecordCommandController) CreateRecord(w http.ResponseWriter, r switch err.Error() { case errors.DatabaseError: httpCode = http.StatusInternalServerError - errorMsg = "Error occurred while saving record." + errorMsg = "Error occurred while saving user." case errors.DuplicateRecord: httpCode = http.StatusConflict - errorMsg = "Record ID already exist." + errorMsg = "User ID already exist." default: httpCode = http.StatusInternalServerError errorMsg = "Please contact technical support." @@ -100,8 +100,8 @@ func (controller *RecordCommandController) CreateRecord(w http.ResponseWriter, r response := viewmodels.HTTPResponseVM{ Status: http.StatusOK, Success: true, - Message: "Successfully created record.", - Data: &types.RecordResponse{ + Message: "Successfully created user.", + Data: &types.UserResponse{ ID: res.ID, Data: res.Data, CreatedAt: time.Now().Unix(), diff --git a/module/record/interfaces/http/rest/RecordQueryController.go b/module/user/interfaces/http/rest/UserQueryController.go similarity index 53% rename from module/record/interfaces/http/rest/RecordQueryController.go rename to module/user/interfaces/http/rest/UserQueryController.go index 934bb3c..1ec091e 100644 --- a/module/record/interfaces/http/rest/RecordQueryController.go +++ b/module/user/interfaces/http/rest/UserQueryController.go @@ -4,28 +4,28 @@ import ( "context" "net/http" - "github.com/go-chi/chi" + "github.com/go-chi/chi/v5" - "gomora/interfaces/http/rest/viewmodels" - "gomora/internal/errors" - "gomora/module/record/application" - types "gomora/module/record/interfaces/http" + "celeste/interfaces/http/rest/viewmodels" + "celeste/internal/errors" + "celeste/module/user/application" + types "celeste/module/user/interfaces/http" ) -// RecordQueryController request controller for record query -type RecordQueryController struct { - application.RecordQueryServiceInterface +// UserQueryController request controller for user query +type UserQueryController struct { + application.UserQueryServiceInterface } -// GetRecordByID retrieves the tenant id from the rest request -func (controller *RecordQueryController) GetRecordByID(w http.ResponseWriter, r *http.Request) { - recordID := chi.URLParam(r, "id") +// GetUserByID retrieves the tenant id from the rest request +func (controller *UserQueryController) GetUserByID(w http.ResponseWriter, r *http.Request) { + userID := chi.URLParam(r, "id") - if len(recordID) == 0 { + if len(userID) == 0 { response := viewmodels.HTTPResponseVM{ Status: http.StatusBadRequest, Success: false, - Message: "Invalid record ID", + Message: "Invalid user ID", ErrorCode: errors.InvalidRequestPayload, } @@ -33,7 +33,7 @@ func (controller *RecordQueryController) GetRecordByID(w http.ResponseWriter, r return } - res, err := controller.RecordQueryServiceInterface.GetRecordByID(context.TODO(), recordID) + res, err := controller.UserQueryServiceInterface.GetUserByID(context.TODO(), userID) if err != nil { var httpCode int var errorMsg string @@ -41,10 +41,10 @@ func (controller *RecordQueryController) GetRecordByID(w http.ResponseWriter, r switch err.Error() { case errors.DatabaseError: httpCode = http.StatusInternalServerError - errorMsg = "Error while fetching record." + errorMsg = "Error while fetching user." case errors.MissingRecord: httpCode = http.StatusNotFound - errorMsg = "No record found." + errorMsg = "No user found." default: httpCode = http.StatusInternalServerError errorMsg = "Please contact technical support." @@ -64,8 +64,8 @@ func (controller *RecordQueryController) GetRecordByID(w http.ResponseWriter, r response := viewmodels.HTTPResponseVM{ Status: http.StatusOK, Success: true, - Message: "Record successfully fetched.", - Data: &types.RecordResponse{ + Message: "User successfully fetched.", + Data: &types.UserResponse{ ID: res.ID, Data: res.Data, CreatedAt: res.CreatedAt.Unix(),