From 8b1ae4f26933f02d1ca69df19c23af26cca3669e Mon Sep 17 00:00:00 2001 From: CityBear3 <68993998+CityBear3@users.noreply.github.com> Date: Sun, 10 Dec 2023 17:41:45 +0900 Subject: [PATCH] =?UTF-8?q?=E3=83=91=E3=83=83=E3=82=B1=E3=83=BC=E3=82=B8?= =?UTF-8?q?=E6=A7=8B=E6=88=90=E3=82=84=E9=87=8D=E8=A4=87=E3=82=B3=E3=83=BC?= =?UTF-8?q?=E3=83=89=E3=82=92=E3=83=AA=E3=83=95=E3=82=A1=E3=82=AF=E3=82=BF?= =?UTF-8?q?=E3=83=AA=E3=83=B3=E3=82=B0=20(#35)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refs #34: refactor gateway pkg * refs #34: change to reusable error handling * refs #34: transaction wrapperをリファクタリング * refs #34: import名を修正 * refs #34: transaction wrapperをリファクタリング * refs #34: archive usecaseの単体テストを実装 * refs #34: run コマンドを追加 * refs #34: kubernetes manifestを追加 --- .gitignore | 1 + Makefile | 5 +- deployment/k8s/minio.yml | 50 +++++++++ deployment/k8s/mysql.yml | 66 ++++++++++++ deployment/k8s/rabbitmq.yml | 45 ++++++++ .../{ => gateway}/repository/mock/archive.go | 64 +---------- .../adaptor/gateway/repository/mock/client.go | 56 ++++++++++ .../{ => gateway}/repository/mock/device.go | 2 +- .../{ => gateway}/repository/mock/evnet.go | 11 +- .../{ => gateway}/repository/mysql/archive.go | 36 ++++--- .../repository/mysql/archive_test.go | 28 ++--- .../{ => gateway}/repository/mysql/client.go | 13 ++- .../repository/mysql/client_test.go | 0 .../{ => gateway}/repository/mysql/device.go | 10 +- .../repository/mysql/device_test.go | 0 .../{ => gateway}/repository/mysql/event.go | 22 ++-- .../repository/mysql/event_test.go | 2 +- .../repository/mysql/shcema/archive.go | 0 .../repository/mysql/shcema/archive_event.go | 0 .../repository/mysql/shcema/boil_queries.go | 0 .../mysql/shcema/boil_table_names.go | 0 .../repository/mysql/shcema/boil_types.go | 0 .../mysql/shcema/boil_view_names.go | 0 .../repository/mysql/shcema/client.go | 0 .../repository/mysql/shcema/device.go | 0 .../repository/mysql/shcema/mysql_upsert.go | 0 .../repository/mysql/transaction.go | 30 +++--- .../transfer}/minio/handler.go | 0 .../adaptor/gateway/transfer/mock/file.go | 70 ++++++++++++ .../repository/mysql/transaction_test.go | 69 ------------ internal/adaptor/rpc/archive.go | 22 ++-- internal/adaptor/rpc/authentication.go | 16 +-- internal/adaptor/rpc/event.go | 14 ++- .../adaptor/rpc/middlewares/authentication.go | 2 +- .../adaptor/rpc/middlewares/authorization.go | 2 +- .../{ => gateway}/repository/archive.go | 10 +- .../domain/{ => gateway}/repository/client.go | 2 +- .../domain/{ => gateway}/repository/device.go | 2 +- .../domain/{ => gateway}/repository/evnet.go | 4 +- internal/domain/gateway/transfer/file.go | 14 +++ internal/domain/repository/transaction.go | 6 -- internal/driver/server.go | 4 +- internal/usecase/archive.go | 8 +- internal/usecase/archive_test.go | 101 +++++++++++++++++- internal/usecase/authentication.go | 2 +- internal/usecase/event.go | 8 +- internal/usecase/mock/transaction.go | 55 ++++++++++ internal/usecase/transaction.go | 7 +- 48 files changed, 608 insertions(+), 251 deletions(-) create mode 100644 deployment/k8s/minio.yml create mode 100644 deployment/k8s/mysql.yml create mode 100644 deployment/k8s/rabbitmq.yml rename internal/adaptor/{ => gateway}/repository/mock/archive.go (53%) create mode 100644 internal/adaptor/gateway/repository/mock/client.go rename internal/adaptor/{ => gateway}/repository/mock/device.go (97%) rename internal/adaptor/{ => gateway}/repository/mock/evnet.go (88%) rename internal/adaptor/{ => gateway}/repository/mysql/archive.go (78%) rename internal/adaptor/{ => gateway}/repository/mysql/archive_test.go (89%) rename internal/adaptor/{ => gateway}/repository/mysql/client.go (87%) rename internal/adaptor/{ => gateway}/repository/mysql/client_test.go (100%) rename internal/adaptor/{ => gateway}/repository/mysql/device.go (87%) rename internal/adaptor/{ => gateway}/repository/mysql/device_test.go (100%) rename internal/adaptor/{ => gateway}/repository/mysql/event.go (78%) rename internal/adaptor/{ => gateway}/repository/mysql/event_test.go (98%) rename internal/adaptor/{ => gateway}/repository/mysql/shcema/archive.go (100%) rename internal/adaptor/{ => gateway}/repository/mysql/shcema/archive_event.go (100%) rename internal/adaptor/{ => gateway}/repository/mysql/shcema/boil_queries.go (100%) rename internal/adaptor/{ => gateway}/repository/mysql/shcema/boil_table_names.go (100%) rename internal/adaptor/{ => gateway}/repository/mysql/shcema/boil_types.go (100%) rename internal/adaptor/{ => gateway}/repository/mysql/shcema/boil_view_names.go (100%) rename internal/adaptor/{ => gateway}/repository/mysql/shcema/client.go (100%) rename internal/adaptor/{ => gateway}/repository/mysql/shcema/device.go (100%) rename internal/adaptor/{ => gateway}/repository/mysql/shcema/mysql_upsert.go (100%) rename internal/adaptor/{ => gateway}/repository/mysql/transaction.go (52%) rename internal/adaptor/{repository => gateway/transfer}/minio/handler.go (100%) create mode 100644 internal/adaptor/gateway/transfer/mock/file.go delete mode 100644 internal/adaptor/repository/mysql/transaction_test.go rename internal/domain/{ => gateway}/repository/archive.go (50%) rename internal/domain/{ => gateway}/repository/client.go (84%) rename internal/domain/{ => gateway}/repository/device.go (84%) rename internal/domain/{ => gateway}/repository/evnet.go (71%) create mode 100644 internal/domain/gateway/transfer/file.go delete mode 100644 internal/domain/repository/transaction.go create mode 100644 internal/usecase/mock/transaction.go diff --git a/.gitignore b/.gitignore index 2f424bf..31b989c 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,4 @@ go.work docker/minio/data/** docker/mysql/db/** +deployment/secrets.yml diff --git a/Makefile b/Makefile index 1c3ea76..512a7f3 100644 --- a/Makefile +++ b/Makefile @@ -19,4 +19,7 @@ migrate-down: migrate --path db/migration --database "mysql://${MYSQL_USER}:${MYSQL_PASSWORD}@tcp(${MYSQL_HOST}:${MYSQL_PORT})/${MYSQL_DATABASE}" --verbose down migrate-drop: - migrate --path db/migration --database "mysql://${MYSQL_USER}:${MYSQL_PASSWORD}@tcp(${MYSQL_HOST}:${MYSQL_PORT})/${MYSQL_DATABASE}" --verbose drop \ No newline at end of file + migrate --path db/migration --database "mysql://${MYSQL_USER}:${MYSQL_PASSWORD}@tcp(${MYSQL_HOST}:${MYSQL_PORT})/${MYSQL_DATABASE}" --verbose drop + +run: + go run cmd/server/main.go \ No newline at end of file diff --git a/deployment/k8s/minio.yml b/deployment/k8s/minio.yml new file mode 100644 index 0000000..eb2aa84 --- /dev/null +++ b/deployment/k8s/minio.yml @@ -0,0 +1,50 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: minio +spec: + replicas: 1 + selector: + matchLabels: + app: minio + template: + metadata: + labels: + app: minio + spec: + containers: + - name: minio + image: minio/minio:latest + ports: + - containerPort: 9000 + env: + - name: MINIO_ROOT_USER + valueFrom: + secretKeyRef: + name: minio + key: user + - name: MINIO_ROOT_PASSWORD + valueFrom: + secretKeyRef: + name: minio + key: password + volumeMounts: + - mountPath: /data + name: minio-data + volumes: + - name: minio-data + persistentVolumeClaim: + claimName: minio-pvc +--- +apiVersion: v1 +kind: Service +metadata: + name: minio +spec: + type: ClusterIP + selector: + app: minio + ports: + - protocol: TCP + port: 9000 + targetPort: 9000 \ No newline at end of file diff --git a/deployment/k8s/mysql.yml b/deployment/k8s/mysql.yml new file mode 100644 index 0000000..5d0b7d2 --- /dev/null +++ b/deployment/k8s/mysql.yml @@ -0,0 +1,66 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: mysql +spec: + replicas: 1 + selector: + matchLabels: + app: mysql + template: + metadata: + labels: + app: mysql + spec: + containers: + - name: mysql + image: mysql:8.0.26 + ports: + - containerPort: 3306 + env: + - name: MYSQL_DATABASE + valueFrom: + secretKeyRef: + name: mysql + key: database + - name: MYSQL_USER + valueFrom: + secretKeyRef: + name: mysql + key: user + - name: MYSQL_PASSWORD + valueFrom: + secretKeyRef: + name: mysql + key: password + - name: MYSQL_ROOT_USER + valueFrom: + secretKeyRef: + name: mysql + key: root_user + - name: MYSQL_ROOT_PASSWORD + valueFrom: + secretKeyRef: + name: mysql + key: root_password + volumeMounts: + - mountPath: /var/lib/mysql + name: mysql-data + volumes: + - name: mysql-data + persistentVolumeClaim: + claimName: mysql-pvc +--- +apiVersion: v1 +kind: Service +metadata: + name: mysql +spec: + type: NodePort + selector: + app: mysql + ports: + - protocol: TCP + port: 3306 + targetPort: 3306 + nodePort: 30001 \ No newline at end of file diff --git a/deployment/k8s/rabbitmq.yml b/deployment/k8s/rabbitmq.yml new file mode 100644 index 0000000..5361f33 --- /dev/null +++ b/deployment/k8s/rabbitmq.yml @@ -0,0 +1,45 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: rabbitmq +spec: + serviceName: "rabbitmq" + replicas: 1 + selector: + matchLabels: + app: rabbitmq + template: + metadata: + labels: + app: rabbitmq + spec: + containers: + - name: rabbitmq + image: rabbitmq:3.11.16-management + ports: + - containerPort: 5672 + - containerPort: 15672 + env: + - name: RABBITMQ_DEFAULT_USER + valueFrom: + secretKeyRef: + name: rabbitmq + key: username + - name: RABBITMQ_DEFAULT_PASS + valueFrom: + secretKeyRef: + name: rabbitmq + key: password +--- +apiVersion: v1 +kind: Service +metadata: + name: rabbitmq +spec: + type: ClusterIP + selector: + app: rabbitmq + ports: + - protocol: TCP + port: 5672 + targetPort: 5672 diff --git a/internal/adaptor/repository/mock/archive.go b/internal/adaptor/gateway/repository/mock/archive.go similarity index 53% rename from internal/adaptor/repository/mock/archive.go rename to internal/adaptor/gateway/repository/mock/archive.go index fa2752f..9452838 100644 --- a/internal/adaptor/repository/mock/archive.go +++ b/internal/adaptor/gateway/repository/mock/archive.go @@ -3,7 +3,7 @@ // // Generated by this command: // -// mockgen -source=archive.go -package=mock_repository -destination=../../adaptor/repository/mock/archive.go +// mockgen -source=archive.go -package=mock_repository -destination=../../../adaptor/gateway/repository/mock/archive.go // // Package mock_repository is a generated GoMock package. package mock_repository @@ -14,8 +14,6 @@ import ( entity "github.com/CityBear3/satellite/internal/domain/entity" primitive "github.com/CityBear3/satellite/internal/domain/primitive" - archive "github.com/CityBear3/satellite/internal/domain/primitive/archive" - repository "github.com/CityBear3/satellite/internal/domain/repository" gomock "go.uber.org/mock/gomock" ) @@ -73,67 +71,15 @@ func (mr *MockIArchiveRepositoryMockRecorder) GetArchiveByArchiveEventID(ctx, ar } // Save mocks base method. -func (m *MockIArchiveRepository) Save(ctx context.Context, tx repository.ITx, archive entity.Archive) error { +func (m *MockIArchiveRepository) Save(ctx context.Context, archive entity.Archive) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Save", ctx, tx, archive) + ret := m.ctrl.Call(m, "Save", ctx, archive) ret0, _ := ret[0].(error) return ret0 } // Save indicates an expected call of Save. -func (mr *MockIArchiveRepositoryMockRecorder) Save(ctx, tx, archive any) *gomock.Call { +func (mr *MockIArchiveRepositoryMockRecorder) Save(ctx, archive any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Save", reflect.TypeOf((*MockIArchiveRepository)(nil).Save), ctx, tx, archive) -} - -// MockIFileTransfer is a mock of IFileTransfer interface. -type MockIFileTransfer struct { - ctrl *gomock.Controller - recorder *MockIFileTransferMockRecorder -} - -// MockIFileTransferMockRecorder is the mock recorder for MockIFileTransfer. -type MockIFileTransferMockRecorder struct { - mock *MockIFileTransfer -} - -// NewMockIFileTransfer creates a new mock instance. -func NewMockIFileTransfer(ctrl *gomock.Controller) *MockIFileTransfer { - mock := &MockIFileTransfer{ctrl: ctrl} - mock.recorder = &MockIFileTransferMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockIFileTransfer) EXPECT() *MockIFileTransferMockRecorder { - return m.recorder -} - -// GetFile mocks base method. -func (m *MockIFileTransfer) GetFile(ctx context.Context, archiveID primitive.ID, contentType archive.ContentType) (archive.Data, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetFile", ctx, archiveID, contentType) - ret0, _ := ret[0].(archive.Data) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetFile indicates an expected call of GetFile. -func (mr *MockIFileTransferMockRecorder) GetFile(ctx, archiveID, contentType any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFile", reflect.TypeOf((*MockIFileTransfer)(nil).GetFile), ctx, archiveID, contentType) -} - -// Save mocks base method. -func (m *MockIFileTransfer) Save(ctx context.Context, archiveID primitive.ID, contentType archive.ContentType, data archive.Data) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Save", ctx, archiveID, contentType, data) - ret0, _ := ret[0].(error) - return ret0 -} - -// Save indicates an expected call of Save. -func (mr *MockIFileTransferMockRecorder) Save(ctx, archiveID, contentType, data any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Save", reflect.TypeOf((*MockIFileTransfer)(nil).Save), ctx, archiveID, contentType, data) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Save", reflect.TypeOf((*MockIArchiveRepository)(nil).Save), ctx, archive) } diff --git a/internal/adaptor/gateway/repository/mock/client.go b/internal/adaptor/gateway/repository/mock/client.go new file mode 100644 index 0000000..39ee8d7 --- /dev/null +++ b/internal/adaptor/gateway/repository/mock/client.go @@ -0,0 +1,56 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: client.go +// +// Generated by this command: +// +// mockgen -source=client.go -package=mock_repository -destination=../../../adaptor/gateway/repository/mock/client.go +// +// Package mock_repository is a generated GoMock package. +package mock_repository + +import ( + context "context" + reflect "reflect" + + entity "github.com/CityBear3/satellite/internal/domain/entity" + primitive "github.com/CityBear3/satellite/internal/domain/primitive" + gomock "go.uber.org/mock/gomock" +) + +// MockIClientRepository is a mock of IClientRepository interface. +type MockIClientRepository struct { + ctrl *gomock.Controller + recorder *MockIClientRepositoryMockRecorder +} + +// MockIClientRepositoryMockRecorder is the mock recorder for MockIClientRepository. +type MockIClientRepositoryMockRecorder struct { + mock *MockIClientRepository +} + +// NewMockIClientRepository creates a new mock instance. +func NewMockIClientRepository(ctrl *gomock.Controller) *MockIClientRepository { + mock := &MockIClientRepository{ctrl: ctrl} + mock.recorder = &MockIClientRepositoryMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockIClientRepository) EXPECT() *MockIClientRepositoryMockRecorder { + return m.recorder +} + +// GetClient mocks base method. +func (m *MockIClientRepository) GetClient(ctx context.Context, clientID primitive.ID) (entity.Client, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetClient", ctx, clientID) + ret0, _ := ret[0].(entity.Client) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetClient indicates an expected call of GetClient. +func (mr *MockIClientRepositoryMockRecorder) GetClient(ctx, clientID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetClient", reflect.TypeOf((*MockIClientRepository)(nil).GetClient), ctx, clientID) +} diff --git a/internal/adaptor/repository/mock/device.go b/internal/adaptor/gateway/repository/mock/device.go similarity index 97% rename from internal/adaptor/repository/mock/device.go rename to internal/adaptor/gateway/repository/mock/device.go index 4185318..9aaddac 100644 --- a/internal/adaptor/repository/mock/device.go +++ b/internal/adaptor/gateway/repository/mock/device.go @@ -3,7 +3,7 @@ // // Generated by this command: // -// mockgen -source=device.go -package=mock_repository -destination=../../adaptor/repository/mock/device.go +// mockgen -source=device.go -package=mock_repository -destination=../../../adaptor/gateway/repository/mock/device.go // // Package mock_repository is a generated GoMock package. package mock_repository diff --git a/internal/adaptor/repository/mock/evnet.go b/internal/adaptor/gateway/repository/mock/evnet.go similarity index 88% rename from internal/adaptor/repository/mock/evnet.go rename to internal/adaptor/gateway/repository/mock/evnet.go index 120f137..2a17209 100644 --- a/internal/adaptor/repository/mock/evnet.go +++ b/internal/adaptor/gateway/repository/mock/evnet.go @@ -3,7 +3,7 @@ // // Generated by this command: // -// mockgen -source=evnet.go -package=mock_repository -destination=../../adaptor/repository/mock/evnet.go +// mockgen -source=evnet.go -package=mock_repository -destination=../../../adaptor/gateway/repository/mock/evnet.go // // Package mock_repository is a generated GoMock package. package mock_repository @@ -14,7 +14,6 @@ import ( entity "github.com/CityBear3/satellite/internal/domain/entity" primitive "github.com/CityBear3/satellite/internal/domain/primitive" - repository "github.com/CityBear3/satellite/internal/domain/repository" gomock "go.uber.org/mock/gomock" ) @@ -57,15 +56,15 @@ func (mr *MockIEventRepositoryMockRecorder) GetArchiveEvent(ctx, archiveEventID } // SaveArchiveEvent mocks base method. -func (m *MockIEventRepository) SaveArchiveEvent(ctx context.Context, tx repository.ITx, archiveEvent entity.ArchiveEvent) error { +func (m *MockIEventRepository) SaveArchiveEvent(ctx context.Context, archiveEvent entity.ArchiveEvent) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SaveArchiveEvent", ctx, tx, archiveEvent) + ret := m.ctrl.Call(m, "SaveArchiveEvent", ctx, archiveEvent) ret0, _ := ret[0].(error) return ret0 } // SaveArchiveEvent indicates an expected call of SaveArchiveEvent. -func (mr *MockIEventRepositoryMockRecorder) SaveArchiveEvent(ctx, tx, archiveEvent any) *gomock.Call { +func (mr *MockIEventRepositoryMockRecorder) SaveArchiveEvent(ctx, archiveEvent any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveArchiveEvent", reflect.TypeOf((*MockIEventRepository)(nil).SaveArchiveEvent), ctx, tx, archiveEvent) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveArchiveEvent", reflect.TypeOf((*MockIEventRepository)(nil).SaveArchiveEvent), ctx, archiveEvent) } diff --git a/internal/adaptor/repository/mysql/archive.go b/internal/adaptor/gateway/repository/mysql/archive.go similarity index 78% rename from internal/adaptor/repository/mysql/archive.go rename to internal/adaptor/gateway/repository/mysql/archive.go index c57cc42..26c065d 100644 --- a/internal/adaptor/repository/mysql/archive.go +++ b/internal/adaptor/gateway/repository/mysql/archive.go @@ -5,21 +5,21 @@ import ( "database/sql" "errors" - "github.com/CityBear3/satellite/internal/adaptor/repository/mysql/shcema" + "github.com/CityBear3/satellite/internal/adaptor/gateway/repository/mysql/shcema" "github.com/CityBear3/satellite/internal/domain/entity" + "github.com/CityBear3/satellite/internal/domain/gateway/transfer" "github.com/CityBear3/satellite/internal/domain/primitive" "github.com/CityBear3/satellite/internal/domain/primitive/archive" - "github.com/CityBear3/satellite/internal/domain/repository" "github.com/CityBear3/satellite/internal/pkg/apperrs" "github.com/volatiletech/sqlboiler/v4/boil" ) type ArchiveRepository struct { db boil.ContextExecutor - fileTransfer repository.IFileTransfer + fileTransfer transfer.IFileTransfer } -func NewArchiveRepository(db boil.ContextExecutor, fileTransfer repository.IFileTransfer) *ArchiveRepository { +func NewArchiveRepository(db boil.ContextExecutor, fileTransfer transfer.IFileTransfer) *ArchiveRepository { return &ArchiveRepository{ db: db, fileTransfer: fileTransfer, @@ -28,12 +28,12 @@ func NewArchiveRepository(db boil.ContextExecutor, fileTransfer repository.IFile func (i *ArchiveRepository) Save( ctx context.Context, - rtx repository.ITx, archive entity.Archive, ) error { - tx, err := ConvertToSqlTx(rtx) - if err != nil { - return err + var exec boil.ContextExecutor + exec, ok := getTxFromCtx(ctx) + if !ok { + exec = i.db } archiveSchema := schema.Archive{ @@ -43,11 +43,11 @@ func (i *ArchiveRepository) Save( ContentType: archive.ContentType.Value(), } - if err := archiveSchema.Upsert(ctx, tx, boil.Infer(), boil.Infer()); err != nil { + if err := archiveSchema.Upsert(ctx, exec, boil.Infer(), boil.Infer()); err != nil { return err } - if err = i.fileTransfer.Save(ctx, archive.ID, archive.ContentType, archive.Data); err != nil { + if err := i.fileTransfer.Save(ctx, archive.ID, archive.ContentType, archive.Data); err != nil { return err } @@ -58,7 +58,13 @@ func (i *ArchiveRepository) GetArchive( ctx context.Context, archiveID primitive.ID, ) (entity.Archive, error) { - archiveSchema, err := schema.Archives(schema.ArchiveWhere.ID.EQ(archiveID.Value().String())).One(ctx, i.db) + var exec boil.ContextExecutor + exec, ok := getTxFromCtx(ctx) + if !ok { + exec = i.db + } + + archiveSchema, err := schema.Archives(schema.ArchiveWhere.ID.EQ(archiveID.Value().String())).One(ctx, exec) if errors.Is(err, sql.ErrNoRows) { return entity.Archive{}, apperrs.NotFoundArchiveError } @@ -98,7 +104,13 @@ func (i *ArchiveRepository) GetArchiveByArchiveEventID( ctx context.Context, archiveEventID primitive.ID, ) (entity.Archive, error) { - archiveSchema, err := schema.Archives(schema.ArchiveWhere.ArchiveEventID.EQ(archiveEventID.Value().String())).One(ctx, i.db) + var exec boil.ContextExecutor + exec, ok := getTxFromCtx(ctx) + if !ok { + exec = i.db + } + + archiveSchema, err := schema.Archives(schema.ArchiveWhere.ArchiveEventID.EQ(archiveEventID.Value().String())).One(ctx, exec) if errors.Is(err, sql.ErrNoRows) { return entity.Archive{}, apperrs.NotFoundArchiveError } diff --git a/internal/adaptor/repository/mysql/archive_test.go b/internal/adaptor/gateway/repository/mysql/archive_test.go similarity index 89% rename from internal/adaptor/repository/mysql/archive_test.go rename to internal/adaptor/gateway/repository/mysql/archive_test.go index 67547c1..de2fdf7 100644 --- a/internal/adaptor/repository/mysql/archive_test.go +++ b/internal/adaptor/gateway/repository/mysql/archive_test.go @@ -5,7 +5,7 @@ import ( "database/sql" "testing" - mock_repository "github.com/CityBear3/satellite/internal/adaptor/repository/mock" + mock_transfer "github.com/CityBear3/satellite/internal/adaptor/gateway/transfer/mock" "github.com/CityBear3/satellite/internal/domain/entity" "github.com/CityBear3/satellite/internal/domain/primitive" "github.com/CityBear3/satellite/internal/domain/primitive/archive" @@ -29,7 +29,7 @@ func TestArchiveRepository_Save(t *testing.T) { args args tables []helper.TableOperator queries []string - mocks func(mockFileTransfer *mock_repository.MockIFileTransfer) + mocks func(mockFileTransfer *mock_transfer.MockIFileTransfer) } db, err := helper.GetTestDB() @@ -55,9 +55,9 @@ func TestArchiveRepository_Save(t *testing.T) { mockController := gomock.NewController(t) defer mockController.Finish() - mockFileTransfer := mock_repository.NewMockIFileTransfer(mockController) + mockFileTransfer := mock_transfer.NewMockIFileTransfer(mockController) - sut := NewArchiveRepository(db, mockFileTransfer) + sut := NewArchiveRepository(tx, mockFileTransfer) clientID := ulid.Make() @@ -101,7 +101,7 @@ func TestArchiveRepository_Save(t *testing.T) { queries: []string{ "SELECT * FROM `archive` WHERE `id`=?", }, - mocks: func(mockFileTransfer *mock_repository.MockIFileTransfer) { + mocks: func(mockFileTransfer *mock_transfer.MockIFileTransfer) { mockFileTransfer.EXPECT().Save(ctx, archiveID, contentType, archive.Data{}).Return(nil) }, }, @@ -118,7 +118,7 @@ func TestArchiveRepository_Save(t *testing.T) { savedResult := table.ArchiveTable{} t.Run(tt.name, func(t *testing.T) { tt.mocks(mockFileTransfer) - if err = sut.Save(tt.args.ctx, tt.args.tx, tt.args.archive); err != nil { + if err = sut.Save(tt.args.ctx, tt.args.archive); err != nil { t.Error(err) return } @@ -163,7 +163,7 @@ func TestArchiveRepository_GetArchive(t *testing.T) { want entity.Archive expectedErr error tables []helper.TableOperator - mocks func(mockFileTransfer *mock_repository.MockIFileTransfer) + mocks func(mockFileTransfer *mock_transfer.MockIFileTransfer) } db, err := helper.GetTestDB() @@ -182,7 +182,7 @@ func TestArchiveRepository_GetArchive(t *testing.T) { mockController := gomock.NewController(t) defer mockController.Finish() - mockFileTransfer := mock_repository.NewMockIFileTransfer(mockController) + mockFileTransfer := mock_transfer.NewMockIFileTransfer(mockController) clientID := primitive.NewID() deviceID := primitive.NewID() @@ -225,7 +225,7 @@ func TestArchiveRepository_GetArchive(t *testing.T) { ArchiveEventID: archiveEventID, ContentType: contentType, }, - mocks: func(mockFileTransfer *mock_repository.MockIFileTransfer) { + mocks: func(mockFileTransfer *mock_transfer.MockIFileTransfer) { mockFileTransfer.EXPECT().GetFile(ctx, archiveID, contentType).Return(archive.Data{}, nil) }, }, @@ -236,7 +236,7 @@ func TestArchiveRepository_GetArchive(t *testing.T) { archiveID, }, expectedErr: apperrs.NotFoundArchiveError, - mocks: func(mockFileTransfer *mock_repository.MockIFileTransfer) { + mocks: func(mockFileTransfer *mock_transfer.MockIFileTransfer) { mockFileTransfer.EXPECT().GetFile(ctx, archiveID, contentType).Return(archive.Data{}, nil).Times(0) }, }, @@ -288,7 +288,7 @@ func TestArchiveRepository_GetArchiveByArchiveEventID(t *testing.T) { want entity.Archive expectedErr error tables []helper.TableOperator - mocks func(mockFileTransfer *mock_repository.MockIFileTransfer) + mocks func(mockFileTransfer *mock_transfer.MockIFileTransfer) } db, err := helper.GetTestDB() @@ -307,7 +307,7 @@ func TestArchiveRepository_GetArchiveByArchiveEventID(t *testing.T) { mockController := gomock.NewController(t) defer mockController.Finish() - mockFileTransfer := mock_repository.NewMockIFileTransfer(mockController) + mockFileTransfer := mock_transfer.NewMockIFileTransfer(mockController) clientID := primitive.NewID() deviceID := primitive.NewID() @@ -350,7 +350,7 @@ func TestArchiveRepository_GetArchiveByArchiveEventID(t *testing.T) { ArchiveEventID: archiveEventID, ContentType: contentType, }, - mocks: func(mockFileTransfer *mock_repository.MockIFileTransfer) { + mocks: func(mockFileTransfer *mock_transfer.MockIFileTransfer) { mockFileTransfer.EXPECT().GetFile(ctx, archiveID, contentType).Return(archive.Data{}, nil) }, }, @@ -361,7 +361,7 @@ func TestArchiveRepository_GetArchiveByArchiveEventID(t *testing.T) { primitive.NewID(), }, expectedErr: apperrs.NotFoundArchiveError, - mocks: func(mockFileTransfer *mock_repository.MockIFileTransfer) { + mocks: func(mockFileTransfer *mock_transfer.MockIFileTransfer) { mockFileTransfer.EXPECT().GetFile(ctx, archiveID, contentType).Return(archive.Data{}, nil).Times(0) }, }, diff --git a/internal/adaptor/repository/mysql/client.go b/internal/adaptor/gateway/repository/mysql/client.go similarity index 87% rename from internal/adaptor/repository/mysql/client.go rename to internal/adaptor/gateway/repository/mysql/client.go index 5d980f0..d5152ef 100644 --- a/internal/adaptor/repository/mysql/client.go +++ b/internal/adaptor/gateway/repository/mysql/client.go @@ -3,8 +3,9 @@ package mysql import ( "context" "database/sql" + "fmt" - "github.com/CityBear3/satellite/internal/adaptor/repository/mysql/shcema" + "github.com/CityBear3/satellite/internal/adaptor/gateway/repository/mysql/shcema" "github.com/CityBear3/satellite/internal/domain/entity" "github.com/CityBear3/satellite/internal/domain/primitive" "github.com/CityBear3/satellite/internal/domain/primitive/authentication" @@ -27,10 +28,16 @@ func NewClientRepository(db boil.ContextExecutor) *ClientRepository { } func (i *ClientRepository) GetClient(ctx context.Context, clientID primitive.ID) (entity.Client, error) { + var exec boil.ContextExecutor + exec, ok := getTxFromCtx(ctx) + if !ok { + exec = i.db + } + clientSchema, err := schema.Clients( schema.ClientWhere.ID.EQ(clientID.Value().String()), - qm.Load("Devices"), - ).One(ctx, i.db) + qm.Load("Devices", qm.Where(fmt.Sprintf("%s=?", schema.DeviceColumns.IsDeleted), false)), + ).One(ctx, exec) if errors.Is(err, sql.ErrNoRows) { return entity.Client{}, apperrs.NotFoundClientError diff --git a/internal/adaptor/repository/mysql/client_test.go b/internal/adaptor/gateway/repository/mysql/client_test.go similarity index 100% rename from internal/adaptor/repository/mysql/client_test.go rename to internal/adaptor/gateway/repository/mysql/client_test.go diff --git a/internal/adaptor/repository/mysql/device.go b/internal/adaptor/gateway/repository/mysql/device.go similarity index 87% rename from internal/adaptor/repository/mysql/device.go rename to internal/adaptor/gateway/repository/mysql/device.go index 972654d..767da39 100644 --- a/internal/adaptor/repository/mysql/device.go +++ b/internal/adaptor/gateway/repository/mysql/device.go @@ -4,7 +4,7 @@ import ( "context" "database/sql" - "github.com/CityBear3/satellite/internal/adaptor/repository/mysql/shcema" + "github.com/CityBear3/satellite/internal/adaptor/gateway/repository/mysql/shcema" "github.com/CityBear3/satellite/internal/domain/entity" "github.com/CityBear3/satellite/internal/domain/primitive" "github.com/CityBear3/satellite/internal/domain/primitive/authentication" @@ -25,7 +25,13 @@ func NewDeviceRepository(db boil.ContextExecutor) *DeviceRepository { } func (d *DeviceRepository) GetDevice(ctx context.Context, deviceID primitive.ID) (entity.Device, error) { - deviceSchema, err := schema.Devices(schema.DeviceWhere.ID.EQ(deviceID.Value().String())).One(ctx, d.db) + var exec boil.ContextExecutor + exec, ok := getTxFromCtx(ctx) + if !ok { + exec = d.db + } + + deviceSchema, err := schema.Devices(schema.DeviceWhere.ID.EQ(deviceID.Value().String())).One(ctx, exec) if errors.Is(err, sql.ErrNoRows) { return entity.Device{}, apperrs.NotFoundDeviceError } diff --git a/internal/adaptor/repository/mysql/device_test.go b/internal/adaptor/gateway/repository/mysql/device_test.go similarity index 100% rename from internal/adaptor/repository/mysql/device_test.go rename to internal/adaptor/gateway/repository/mysql/device_test.go diff --git a/internal/adaptor/repository/mysql/event.go b/internal/adaptor/gateway/repository/mysql/event.go similarity index 78% rename from internal/adaptor/repository/mysql/event.go rename to internal/adaptor/gateway/repository/mysql/event.go index 113ba92..03ba29a 100644 --- a/internal/adaptor/repository/mysql/event.go +++ b/internal/adaptor/gateway/repository/mysql/event.go @@ -5,10 +5,9 @@ import ( "database/sql" "errors" - schema "github.com/CityBear3/satellite/internal/adaptor/repository/mysql/shcema" + "github.com/CityBear3/satellite/internal/adaptor/gateway/repository/mysql/shcema" "github.com/CityBear3/satellite/internal/domain/entity" "github.com/CityBear3/satellite/internal/domain/primitive" - "github.com/CityBear3/satellite/internal/domain/repository" "github.com/CityBear3/satellite/internal/pkg/apperrs" "github.com/volatiletech/sqlboiler/v4/boil" ) @@ -23,10 +22,11 @@ func NewEventRepository(db boil.ContextExecutor) *EventRepository { } } -func (r *EventRepository) SaveArchiveEvent(ctx context.Context, rtx repository.ITx, archiveEvent entity.ArchiveEvent) error { - tx, err := ConvertToSqlTx(rtx) - if err != nil { - return err +func (r *EventRepository) SaveArchiveEvent(ctx context.Context, archiveEvent entity.ArchiveEvent) error { + var exec boil.ContextExecutor + exec, ok := getTxFromCtx(ctx) + if !ok { + exec = r.db } archiveEventSchema := schema.ArchiveEvent{ @@ -36,7 +36,7 @@ func (r *EventRepository) SaveArchiveEvent(ctx context.Context, rtx repository.I RequestedAt: archiveEvent.RequestedAt, } - if err := archiveEventSchema.Insert(ctx, tx, boil.Infer()); err != nil { + if err := archiveEventSchema.Insert(ctx, exec, boil.Infer()); err != nil { return err } @@ -44,7 +44,13 @@ func (r *EventRepository) SaveArchiveEvent(ctx context.Context, rtx repository.I } func (r *EventRepository) GetArchiveEvent(ctx context.Context, archiveEventID primitive.ID) (entity.ArchiveEvent, error) { - event, err := schema.ArchiveEvents(schema.ArchiveEventWhere.ID.EQ(archiveEventID.Value().String())).One(ctx, r.db) + var exec boil.ContextExecutor + exec, ok := getTxFromCtx(ctx) + if !ok { + exec = r.db + } + + event, err := schema.ArchiveEvents(schema.ArchiveEventWhere.ID.EQ(archiveEventID.Value().String())).One(ctx, exec) if errors.Is(err, sql.ErrNoRows) { return entity.ArchiveEvent{}, apperrs.NotFoundArchiveEventError } diff --git a/internal/adaptor/repository/mysql/event_test.go b/internal/adaptor/gateway/repository/mysql/event_test.go similarity index 98% rename from internal/adaptor/repository/mysql/event_test.go rename to internal/adaptor/gateway/repository/mysql/event_test.go index 29839cd..7e81fba 100644 --- a/internal/adaptor/repository/mysql/event_test.go +++ b/internal/adaptor/gateway/repository/mysql/event_test.go @@ -86,7 +86,7 @@ func TestEventRepository_SaveArchiveEvent(t *testing.T) { saveResult := table.ArchiveEventTable{} t.Run(tt.name, func(t *testing.T) { - if err := sut.SaveArchiveEvent(tt.args.ctx, tx, tt.args.archiveEvent); err != nil { + if err := sut.SaveArchiveEvent(tt.args.ctx, tt.args.archiveEvent); err != nil { t.Error(err) return } diff --git a/internal/adaptor/repository/mysql/shcema/archive.go b/internal/adaptor/gateway/repository/mysql/shcema/archive.go similarity index 100% rename from internal/adaptor/repository/mysql/shcema/archive.go rename to internal/adaptor/gateway/repository/mysql/shcema/archive.go diff --git a/internal/adaptor/repository/mysql/shcema/archive_event.go b/internal/adaptor/gateway/repository/mysql/shcema/archive_event.go similarity index 100% rename from internal/adaptor/repository/mysql/shcema/archive_event.go rename to internal/adaptor/gateway/repository/mysql/shcema/archive_event.go diff --git a/internal/adaptor/repository/mysql/shcema/boil_queries.go b/internal/adaptor/gateway/repository/mysql/shcema/boil_queries.go similarity index 100% rename from internal/adaptor/repository/mysql/shcema/boil_queries.go rename to internal/adaptor/gateway/repository/mysql/shcema/boil_queries.go diff --git a/internal/adaptor/repository/mysql/shcema/boil_table_names.go b/internal/adaptor/gateway/repository/mysql/shcema/boil_table_names.go similarity index 100% rename from internal/adaptor/repository/mysql/shcema/boil_table_names.go rename to internal/adaptor/gateway/repository/mysql/shcema/boil_table_names.go diff --git a/internal/adaptor/repository/mysql/shcema/boil_types.go b/internal/adaptor/gateway/repository/mysql/shcema/boil_types.go similarity index 100% rename from internal/adaptor/repository/mysql/shcema/boil_types.go rename to internal/adaptor/gateway/repository/mysql/shcema/boil_types.go diff --git a/internal/adaptor/repository/mysql/shcema/boil_view_names.go b/internal/adaptor/gateway/repository/mysql/shcema/boil_view_names.go similarity index 100% rename from internal/adaptor/repository/mysql/shcema/boil_view_names.go rename to internal/adaptor/gateway/repository/mysql/shcema/boil_view_names.go diff --git a/internal/adaptor/repository/mysql/shcema/client.go b/internal/adaptor/gateway/repository/mysql/shcema/client.go similarity index 100% rename from internal/adaptor/repository/mysql/shcema/client.go rename to internal/adaptor/gateway/repository/mysql/shcema/client.go diff --git a/internal/adaptor/repository/mysql/shcema/device.go b/internal/adaptor/gateway/repository/mysql/shcema/device.go similarity index 100% rename from internal/adaptor/repository/mysql/shcema/device.go rename to internal/adaptor/gateway/repository/mysql/shcema/device.go diff --git a/internal/adaptor/repository/mysql/shcema/mysql_upsert.go b/internal/adaptor/gateway/repository/mysql/shcema/mysql_upsert.go similarity index 100% rename from internal/adaptor/repository/mysql/shcema/mysql_upsert.go rename to internal/adaptor/gateway/repository/mysql/shcema/mysql_upsert.go diff --git a/internal/adaptor/repository/mysql/transaction.go b/internal/adaptor/gateway/repository/mysql/transaction.go similarity index 52% rename from internal/adaptor/repository/mysql/transaction.go rename to internal/adaptor/gateway/repository/mysql/transaction.go index e32a67c..464e0e4 100644 --- a/internal/adaptor/repository/mysql/transaction.go +++ b/internal/adaptor/gateway/repository/mysql/transaction.go @@ -4,8 +4,6 @@ import ( "context" "database/sql" - "github.com/CityBear3/satellite/internal/domain/repository" - "github.com/CityBear3/satellite/internal/pkg/apperrs" "github.com/CityBear3/satellite/internal/usecase" ) @@ -19,30 +17,30 @@ func NewTxManger(db *sql.DB) *TxManager { } } -func (t *TxManager) DoInTx(ctx context.Context, operation usecase.Operation) error { +func (t *TxManager) DoInTx(ctx context.Context, operation usecase.Operation) (context.Context, error) { tx, err := t.db.BeginTx(ctx, nil) if err != nil { - return err + return ctx, err } - if err := operation(tx); err != nil { + ctx = context.WithValue(ctx, "tx", tx) + + if err := operation(ctx); err != nil { if err := tx.Rollback(); err != nil { - return err + return ctx, err } - return err + return ctx, err } if err = tx.Commit(); err != nil { - return err + return ctx, err } - return nil -} -func ConvertToSqlTx(rtx repository.ITx) (*sql.Tx, error) { - tx, ok := rtx.(*sql.Tx) - if !ok { - return nil, apperrs.UnexpectedError - } + ctx = context.WithValue(ctx, "tx", nil) + return ctx, nil +} - return tx, nil +func getTxFromCtx(ctx context.Context) (*sql.Tx, bool) { + tx, ok := ctx.Value("tx").(*sql.Tx) + return tx, ok } diff --git a/internal/adaptor/repository/minio/handler.go b/internal/adaptor/gateway/transfer/minio/handler.go similarity index 100% rename from internal/adaptor/repository/minio/handler.go rename to internal/adaptor/gateway/transfer/minio/handler.go diff --git a/internal/adaptor/gateway/transfer/mock/file.go b/internal/adaptor/gateway/transfer/mock/file.go new file mode 100644 index 0000000..eb320da --- /dev/null +++ b/internal/adaptor/gateway/transfer/mock/file.go @@ -0,0 +1,70 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: file.go +// +// Generated by this command: +// +// mockgen -source=file.go -package=mock_transfer -destination=../../../adaptor/gateway/transfer/mock/file.go +// +// Package mock_transfer is a generated GoMock package. +package mock_transfer + +import ( + context "context" + reflect "reflect" + + primitive "github.com/CityBear3/satellite/internal/domain/primitive" + archive "github.com/CityBear3/satellite/internal/domain/primitive/archive" + gomock "go.uber.org/mock/gomock" +) + +// MockIFileTransfer is a mock of IFileTransfer interface. +type MockIFileTransfer struct { + ctrl *gomock.Controller + recorder *MockIFileTransferMockRecorder +} + +// MockIFileTransferMockRecorder is the mock recorder for MockIFileTransfer. +type MockIFileTransferMockRecorder struct { + mock *MockIFileTransfer +} + +// NewMockIFileTransfer creates a new mock instance. +func NewMockIFileTransfer(ctrl *gomock.Controller) *MockIFileTransfer { + mock := &MockIFileTransfer{ctrl: ctrl} + mock.recorder = &MockIFileTransferMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockIFileTransfer) EXPECT() *MockIFileTransferMockRecorder { + return m.recorder +} + +// GetFile mocks base method. +func (m *MockIFileTransfer) GetFile(ctx context.Context, archiveID primitive.ID, contentType archive.ContentType) (archive.Data, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetFile", ctx, archiveID, contentType) + ret0, _ := ret[0].(archive.Data) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetFile indicates an expected call of GetFile. +func (mr *MockIFileTransferMockRecorder) GetFile(ctx, archiveID, contentType any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFile", reflect.TypeOf((*MockIFileTransfer)(nil).GetFile), ctx, archiveID, contentType) +} + +// Save mocks base method. +func (m *MockIFileTransfer) Save(ctx context.Context, archiveID primitive.ID, contentType archive.ContentType, data archive.Data) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Save", ctx, archiveID, contentType, data) + ret0, _ := ret[0].(error) + return ret0 +} + +// Save indicates an expected call of Save. +func (mr *MockIFileTransferMockRecorder) Save(ctx, archiveID, contentType, data any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Save", reflect.TypeOf((*MockIFileTransfer)(nil).Save), ctx, archiveID, contentType, data) +} diff --git a/internal/adaptor/repository/mysql/transaction_test.go b/internal/adaptor/repository/mysql/transaction_test.go deleted file mode 100644 index 5547b9d..0000000 --- a/internal/adaptor/repository/mysql/transaction_test.go +++ /dev/null @@ -1,69 +0,0 @@ -package mysql - -import ( - "database/sql" - "testing" - - "github.com/CityBear3/satellite/internal/domain/repository" - "github.com/CityBear3/satellite/internal/pkg/apperrs" - "github.com/CityBear3/satellite/testutils/helper" - "github.com/stretchr/testify/assert" -) - -func TestConvertToSqlTx(t *testing.T) { - type args struct { - rtx repository.ITx - } - - db, err := helper.GetTestDB() - if err != nil { - t.Fatal(err) - } - - defer func(db *sql.DB) { - err := db.Close() - if err != nil { - panic(err) - } - }(db) - - tx, err := db.Begin() - if err != nil { - t.Fatal(err) - } - - tests := []struct { - name string - args args - want *sql.Tx - expectedErr error - }{ - { - name: "normal case", - args: args{ - rtx: tx, - }, - want: tx, - }, - { - name: "invalid type", - args: args{ - rtx: nil, - }, - expectedErr: apperrs.UnexpectedError, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := ConvertToSqlTx(tt.args.rtx) - if tt.expectedErr != nil { - assert.Error(t, err, tt.expectedErr.Error()) - return - } - - assert.NoError(t, err) - assert.Equal(t, tt.want, got) - }) - } -} diff --git a/internal/adaptor/rpc/archive.go b/internal/adaptor/rpc/archive.go index 389921a..72f1a5f 100644 --- a/internal/adaptor/rpc/archive.go +++ b/internal/adaptor/rpc/archive.go @@ -24,12 +24,16 @@ func NewArchiveRPCService(logger *zap.Logger, archiveInteractor usecase.ArchiveU } } +func (s ArchiveRPCService) handleError(err error) error { + return convertors.ConvertError(s.logger, err) +} + func (s ArchiveRPCService) CreateArchive(server archivePb.ArchiveService_CreateArchiveServer) error { ctx := server.Context() device, err := auth.AuthenticatedDevice(ctx) if err != nil { - return convertors.ConvertError(s.logger, err) + return s.handleError(err) } var requests []*archivePb.CreateArchiveRequest @@ -40,7 +44,7 @@ func (s ArchiveRPCService) CreateArchive(server archivePb.ArchiveService_CreateA } if err != nil { - return convertors.ConvertError(s.logger, err) + return s.handleError(err) } requests = append(requests, request) @@ -48,15 +52,15 @@ func (s ArchiveRPCService) CreateArchive(server archivePb.ArchiveService_CreateA request, err := convertors.CreateArchiveRequestToInput(requests) if err != nil { - return convertors.ConvertError(s.logger, err) + return s.handleError(err) } if err := s.uploadArchiveInteractor.CreateArchive(ctx, request, device); err != nil { - return convertors.ConvertError(s.logger, err) + return s.handleError(err) } if err := server.SendAndClose(&emptypb.Empty{}); err != nil { - return convertors.ConvertError(s.logger, err) + return s.handleError(err) } return nil @@ -67,22 +71,22 @@ func (s ArchiveRPCService) GetArchive(request *archivePb.GetArchiveRequest, serv client, err := auth.AuthenticatedClient(ctx) if err != nil { - return convertors.ConvertError(s.logger, err) + return s.handleError(err) } getArchiveRequest, err := convertors.GetArchiveRequestToInput(request) if err != nil { - return convertors.ConvertError(s.logger, err) + return s.handleError(err) } result, err := s.uploadArchiveInteractor.GetArchive(ctx, getArchiveRequest, client) if err != nil { - return convertors.ConvertError(s.logger, err) + return s.handleError(err) } for _, response := range convertors.GetArchiveResultToResponse(result) { if err = server.Send(response); err != nil { - return convertors.ConvertError(s.logger, err) + return s.handleError(err) } } diff --git a/internal/adaptor/rpc/authentication.go b/internal/adaptor/rpc/authentication.go index 48f003f..a84600b 100644 --- a/internal/adaptor/rpc/authentication.go +++ b/internal/adaptor/rpc/authentication.go @@ -30,19 +30,23 @@ func NewAuthenticationRPCService( } } +func (s AuthenticationRPCService) handleError(err error) error { + return convertors.ConvertError(s.logger, err) +} + func (s AuthenticationRPCService) AuthenticateClient(ctx context.Context, request *authPb.AuthenticateRequest) (*authPb.AuthenticateResponse, error) { authenticateRequest, err := convertors.ConvertToAuthenticationRequest(request) if err != nil { - return nil, convertors.ConvertError(s.logger, err) + return nil, s.handleError(err) } if err = s.authenticationInteractor.AuthenticateClient(ctx, authenticateRequest); err != nil { - return nil, convertors.ConvertError(s.logger, err) + return nil, s.handleError(err) } token, err := createToken(request.Id, s.hmacSecret) if err != nil { - return nil, convertors.ConvertError(s.logger, err) + return nil, s.handleError(err) } return &authPb.AuthenticateResponse{ @@ -53,16 +57,16 @@ func (s AuthenticationRPCService) AuthenticateClient(ctx context.Context, reques func (s AuthenticationRPCService) AuthenticateDevice(ctx context.Context, request *authPb.AuthenticateRequest) (*authPb.AuthenticateResponse, error) { authenticateRequest, err := convertors.ConvertToAuthenticationRequest(request) if err != nil { - return nil, convertors.ConvertError(s.logger, err) + return nil, s.handleError(err) } if err = s.authenticationInteractor.AuthenticateDevice(ctx, authenticateRequest); err != nil { - return nil, convertors.ConvertError(s.logger, err) + return nil, s.handleError(err) } token, err := createToken(request.Id, s.hmacSecret) if err != nil { - return nil, convertors.ConvertError(s.logger, err) + return nil, s.handleError(err) } return &authPb.AuthenticateResponse{ diff --git a/internal/adaptor/rpc/event.go b/internal/adaptor/rpc/event.go index 50daaf8..ec01617 100644 --- a/internal/adaptor/rpc/event.go +++ b/internal/adaptor/rpc/event.go @@ -24,15 +24,19 @@ func NewEventRPCService(logger *zap.Logger, eventInteractor usecase.EventUseCase } } +func (s EventRPCService) handleError(err error) error { + return convertors.ConvertError(s.logger, err) +} + func (s EventRPCService) PublishEvent(ctx context.Context, req *emptypb.Empty) (*eventPb.PublishEventResponse, error) { client, err := auth.AuthenticatedClient(ctx) if err != nil { - return nil, convertors.ConvertError(s.logger, err) + return nil, s.handleError(err) } archiveEventID, err := s.eventInteractor.PublishArchiveEvent(ctx, client) if err != nil { - return nil, convertors.ConvertError(s.logger, err) + return nil, s.handleError(err) } return &eventPb.PublishEventResponse{ @@ -45,12 +49,12 @@ func (s EventRPCService) ReceiveEvent(req *emptypb.Empty, server eventPb.Archive device, err := auth.AuthenticatedDevice(ctx) if err != nil { - return convertors.ConvertError(s.logger, err) + return s.handleError(err) } archiveEvents, err := s.eventInteractor.ReceiveArchiveEvent(ctx, device) if err != nil { - return convertors.ConvertError(s.logger, err) + return s.handleError(err) } for archiveEvent := range archiveEvents { @@ -61,7 +65,7 @@ func (s EventRPCService) ReceiveEvent(req *emptypb.Empty, server eventPb.Archive } if err := server.Send(archiveEventResponse); err != nil { - return convertors.ConvertError(s.logger, err) + return s.handleError(err) } } diff --git a/internal/adaptor/rpc/middlewares/authentication.go b/internal/adaptor/rpc/middlewares/authentication.go index 2f95e40..0505f99 100644 --- a/internal/adaptor/rpc/middlewares/authentication.go +++ b/internal/adaptor/rpc/middlewares/authentication.go @@ -5,7 +5,7 @@ import ( "time" "github.com/CityBear3/satellite/internal/adaptor/rpc/convertors" - "github.com/CityBear3/satellite/internal/domain/repository" + "github.com/CityBear3/satellite/internal/domain/gateway/repository" "github.com/CityBear3/satellite/internal/pkg/apperrs" "github.com/golang-jwt/jwt" middleware "github.com/grpc-ecosystem/go-grpc-middleware/v2" diff --git a/internal/adaptor/rpc/middlewares/authorization.go b/internal/adaptor/rpc/middlewares/authorization.go index f598812..6d967ea 100644 --- a/internal/adaptor/rpc/middlewares/authorization.go +++ b/internal/adaptor/rpc/middlewares/authorization.go @@ -4,8 +4,8 @@ import ( "context" "github.com/CityBear3/satellite/internal/adaptor/rpc/convertors" + "github.com/CityBear3/satellite/internal/domain/gateway/repository" "github.com/CityBear3/satellite/internal/domain/primitive" - "github.com/CityBear3/satellite/internal/domain/repository" "github.com/CityBear3/satellite/internal/pkg/apperrs" middleware "github.com/grpc-ecosystem/go-grpc-middleware/v2" "go.uber.org/zap" diff --git a/internal/domain/repository/archive.go b/internal/domain/gateway/repository/archive.go similarity index 50% rename from internal/domain/repository/archive.go rename to internal/domain/gateway/repository/archive.go index cee5887..0523a58 100644 --- a/internal/domain/repository/archive.go +++ b/internal/domain/gateway/repository/archive.go @@ -1,4 +1,4 @@ -//go:generate mockgen -source=$GOFILE -package=mock_repository -destination=../../adaptor/repository/mock/$GOFILE +//go:generate mockgen -source=$GOFILE -package=mock_repository -destination=../../../adaptor/gateway/repository/mock/$GOFILE package repository import ( @@ -6,16 +6,10 @@ import ( "github.com/CityBear3/satellite/internal/domain/entity" "github.com/CityBear3/satellite/internal/domain/primitive" - "github.com/CityBear3/satellite/internal/domain/primitive/archive" ) type IArchiveRepository interface { - Save(ctx context.Context, tx ITx, archive entity.Archive) error + Save(ctx context.Context, archive entity.Archive) error GetArchive(ctx context.Context, archiveId primitive.ID) (entity.Archive, error) GetArchiveByArchiveEventID(ctx context.Context, archiveEventID primitive.ID) (entity.Archive, error) } - -type IFileTransfer interface { - Save(ctx context.Context, archiveID primitive.ID, contentType archive.ContentType, data archive.Data) error - GetFile(ctx context.Context, archiveID primitive.ID, contentType archive.ContentType) (archive.Data, error) -} diff --git a/internal/domain/repository/client.go b/internal/domain/gateway/repository/client.go similarity index 84% rename from internal/domain/repository/client.go rename to internal/domain/gateway/repository/client.go index 00451f8..bd6da6e 100644 --- a/internal/domain/repository/client.go +++ b/internal/domain/gateway/repository/client.go @@ -1,4 +1,4 @@ -//go:generate mockgen -source=$GOFILE -package=mock_repository -destination=../../adaptor/repository/mock/$GOFILE +//go:generate mockgen -source=$GOFILE -package=mock_repository -destination=../../../adaptor/gateway/repository/mock/$GOFILE package repository import ( diff --git a/internal/domain/repository/device.go b/internal/domain/gateway/repository/device.go similarity index 84% rename from internal/domain/repository/device.go rename to internal/domain/gateway/repository/device.go index aad619a..dccd11d 100644 --- a/internal/domain/repository/device.go +++ b/internal/domain/gateway/repository/device.go @@ -1,4 +1,4 @@ -//go:generate mockgen -source=$GOFILE -package=mock_repository -destination=../../adaptor/repository/mock/$GOFILE +//go:generate mockgen -source=$GOFILE -package=mock_repository -destination=../../../adaptor/gateway/repository/mock/$GOFILE package repository import ( diff --git a/internal/domain/repository/evnet.go b/internal/domain/gateway/repository/evnet.go similarity index 71% rename from internal/domain/repository/evnet.go rename to internal/domain/gateway/repository/evnet.go index 865b677..7c4488f 100644 --- a/internal/domain/repository/evnet.go +++ b/internal/domain/gateway/repository/evnet.go @@ -1,4 +1,4 @@ -//go:generate mockgen -source=$GOFILE -package=mock_repository -destination=../../adaptor/repository/mock/$GOFILE +//go:generate mockgen -source=$GOFILE -package=mock_repository -destination=../../../adaptor/gateway/repository/mock/$GOFILE package repository import ( @@ -9,6 +9,6 @@ import ( ) type IEventRepository interface { - SaveArchiveEvent(ctx context.Context, tx ITx, archiveEvent entity.ArchiveEvent) error + SaveArchiveEvent(ctx context.Context, archiveEvent entity.ArchiveEvent) error GetArchiveEvent(ctx context.Context, archiveEventID primitive.ID) (entity.ArchiveEvent, error) } diff --git a/internal/domain/gateway/transfer/file.go b/internal/domain/gateway/transfer/file.go new file mode 100644 index 0000000..b238ef5 --- /dev/null +++ b/internal/domain/gateway/transfer/file.go @@ -0,0 +1,14 @@ +//go:generate mockgen -source=$GOFILE -package=mock_transfer -destination=../../../adaptor/gateway/transfer/mock/$GOFILE +package transfer + +import ( + "context" + + "github.com/CityBear3/satellite/internal/domain/primitive" + "github.com/CityBear3/satellite/internal/domain/primitive/archive" +) + +type IFileTransfer interface { + Save(ctx context.Context, archiveID primitive.ID, contentType archive.ContentType, data archive.Data) error + GetFile(ctx context.Context, archiveID primitive.ID, contentType archive.ContentType) (archive.Data, error) +} diff --git a/internal/domain/repository/transaction.go b/internal/domain/repository/transaction.go deleted file mode 100644 index 9c336d1..0000000 --- a/internal/domain/repository/transaction.go +++ /dev/null @@ -1,6 +0,0 @@ -package repository - -type ITx interface { - Commit() error - Rollback() error -} diff --git a/internal/driver/server.go b/internal/driver/server.go index 0aaae24..87c1fc3 100644 --- a/internal/driver/server.go +++ b/internal/driver/server.go @@ -10,8 +10,8 @@ import ( "time" "github.com/CityBear3/satellite/internal/adaptor/event/rabbitmq" - file "github.com/CityBear3/satellite/internal/adaptor/repository/minio" - "github.com/CityBear3/satellite/internal/adaptor/repository/mysql" + "github.com/CityBear3/satellite/internal/adaptor/gateway/repository/mysql" + file "github.com/CityBear3/satellite/internal/adaptor/gateway/transfer/minio" "github.com/CityBear3/satellite/internal/adaptor/rpc" "github.com/CityBear3/satellite/internal/adaptor/rpc/middlewares" "github.com/CityBear3/satellite/internal/usecase" diff --git a/internal/usecase/archive.go b/internal/usecase/archive.go index f3c2c2f..8a66fe8 100644 --- a/internal/usecase/archive.go +++ b/internal/usecase/archive.go @@ -6,9 +6,9 @@ import ( "time" "github.com/CityBear3/satellite/internal/domain/entity" + "github.com/CityBear3/satellite/internal/domain/gateway/repository" "github.com/CityBear3/satellite/internal/domain/primitive" "github.com/CityBear3/satellite/internal/domain/primitive/archive" - "github.com/CityBear3/satellite/internal/domain/repository" ) type ( @@ -60,10 +60,10 @@ func (i *ArchiveInteractor) CreateArchive( request CreateArchiveInput, device entity.Device, ) error { - if err := i.txManager.DoInTx(ctx, func(rtx repository.ITx) error { + if _, err := i.txManager.DoInTx(ctx, func(ctx2 context.Context) error { archiveID := primitive.NewID() - event, err := i.eventRepository.GetArchiveEvent(ctx, request.ArchiveEventID) + event, err := i.eventRepository.GetArchiveEvent(ctx2, request.ArchiveEventID) if err != nil { return err } @@ -73,7 +73,7 @@ func (i *ArchiveInteractor) CreateArchive( } archiveEntity := entity.NewArchive(archiveID, request.ArchiveEventID, request.ContentType, device.ID, request.Data) - if err := i.archiveRepository.Save(ctx, rtx, archiveEntity); err != nil { + if err := i.archiveRepository.Save(ctx2, archiveEntity); err != nil { return err } diff --git a/internal/usecase/archive_test.go b/internal/usecase/archive_test.go index 71d4997..f0ffadd 100644 --- a/internal/usecase/archive_test.go +++ b/internal/usecase/archive_test.go @@ -1,7 +1,100 @@ -package usecase +package usecase_test -import "testing" +import ( + "context" + "testing" -func TestArchiveInteractor_UploadArchive(t *testing.T) { - // TODO implement this test + mock_repository "github.com/CityBear3/satellite/internal/adaptor/gateway/repository/mock" + "github.com/CityBear3/satellite/internal/domain/entity" + "github.com/CityBear3/satellite/internal/domain/primitive" + "github.com/CityBear3/satellite/internal/domain/primitive/archive" + "github.com/CityBear3/satellite/internal/domain/primitive/device" + "github.com/CityBear3/satellite/internal/usecase" + mock_usecase "github.com/CityBear3/satellite/internal/usecase/mock" + "github.com/stretchr/testify/assert" + "go.uber.org/mock/gomock" +) + +func TestArchiveInteractor_CreateArchive(t *testing.T) { + type args struct { + ctx context.Context + req usecase.CreateArchiveInput + device entity.Device + } + + ctrl := gomock.NewController(t) + mockArchiveRepository := mock_repository.NewMockIArchiveRepository(ctrl) + mockEventRepository := mock_repository.NewMockIEventRepository(ctrl) + mockITxManager := mock_usecase.NewMockITxManager(ctrl) + + sut := usecase.NewArchiveInteractor(mockArchiveRepository, mockEventRepository, mockITxManager) + + contentType, err := archive.NewContentType("image/png") + if err != nil { + t.Fatal(err) + } + + data, err := archive.NewData([]byte("test")) + if err != nil { + t.Fatal(err) + } + + deviceName, err := device.NewDeviceName("test") + if err != nil { + t.Fatal(err) + } + + device := entity.NewDevice(primitive.NewID(), deviceName, nil, primitive.NewID()) + + tests := []struct { + name string + args args + wantErr error + assertion assert.ErrorAssertionFunc + runMock func(ctx context.Context, req usecase.CreateArchiveInput, device entity.Device) + }{ + { + name: "success to create archive", + args: args{ + ctx: context.Background(), + req: usecase.CreateArchiveInput{ + ArchiveEventID: primitive.NewID(), + ContentType: contentType, + Data: data, + }, + device: device, + }, + + assertion: assert.NoError, + runMock: func(ctx context.Context, req usecase.CreateArchiveInput, device entity.Device) { + mockITxManager. + EXPECT(). + DoInTx(ctx, gomock.Any()). + DoAndReturn(func(ctx context.Context, f func(ctx2 context.Context) error) (interface{}, error) { + return gomock.Any(), f(ctx) + }) + + mockEventRepository. + EXPECT(). + GetArchiveEvent(ctx, req.ArchiveEventID). + Return(entity.NewArchiveEvent(req.ArchiveEventID, device.ID, device.ClientID), nil) + + mockArchiveRepository. + EXPECT().Save(ctx, gomock.Any()). + Return(nil) + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.runMock(tt.args.ctx, tt.args.req, tt.args.device) + err = sut.CreateArchive(tt.args.ctx, tt.args.req, tt.args.device) + tt.assertion(t, err) + + if tt.wantErr != nil { + assert.EqualError(t, err, tt.wantErr.Error()) + } + }) + } } diff --git a/internal/usecase/authentication.go b/internal/usecase/authentication.go index 4f62077..a4fde6d 100644 --- a/internal/usecase/authentication.go +++ b/internal/usecase/authentication.go @@ -3,9 +3,9 @@ package usecase import ( "context" + "github.com/CityBear3/satellite/internal/domain/gateway/repository" "github.com/CityBear3/satellite/internal/domain/primitive" "github.com/CityBear3/satellite/internal/domain/primitive/authentication" - "github.com/CityBear3/satellite/internal/domain/repository" "github.com/CityBear3/satellite/internal/pkg/apperrs" "github.com/CityBear3/satellite/internal/pkg/auth" ) diff --git a/internal/usecase/event.go b/internal/usecase/event.go index 04c7701..b4dc7bf 100644 --- a/internal/usecase/event.go +++ b/internal/usecase/event.go @@ -4,8 +4,8 @@ import ( "context" "github.com/CityBear3/satellite/internal/domain/entity" + "github.com/CityBear3/satellite/internal/domain/gateway/repository" "github.com/CityBear3/satellite/internal/domain/primitive" - "github.com/CityBear3/satellite/internal/domain/repository" ) type ( @@ -45,12 +45,12 @@ func NewEventInteractor( func (i EventInteractor) PublishArchiveEvent(ctx context.Context, client entity.Client) (primitive.ID, error) { archiveEvent := entity.NewArchiveEvent(primitive.NewID(), client.Devices[0].ID, client.ID) - if err := i.txManager.DoInTx(ctx, func(rtx repository.ITx) error { - if err := i.eventRepository.SaveArchiveEvent(ctx, rtx, archiveEvent); err != nil { + if _, err := i.txManager.DoInTx(ctx, func(ctx2 context.Context) error { + if err := i.eventRepository.SaveArchiveEvent(ctx2, archiveEvent); err != nil { return err } - if err := i.eventHandler.PublishArchiveEvent(ctx, archiveEvent); err != nil { + if err := i.eventHandler.PublishArchiveEvent(ctx2, archiveEvent); err != nil { return err } diff --git a/internal/usecase/mock/transaction.go b/internal/usecase/mock/transaction.go new file mode 100644 index 0000000..6f42bef --- /dev/null +++ b/internal/usecase/mock/transaction.go @@ -0,0 +1,55 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: transaction.go +// +// Generated by this command: +// +// mockgen -source=transaction.go -package=mock_usecase -destination=./mock/transaction.go +// +// Package mock_usecase is a generated GoMock package. +package mock_usecase + +import ( + context "context" + reflect "reflect" + + usecase "github.com/CityBear3/satellite/internal/usecase" + gomock "go.uber.org/mock/gomock" +) + +// MockITxManager is a mock of ITxManager interface. +type MockITxManager struct { + ctrl *gomock.Controller + recorder *MockITxManagerMockRecorder +} + +// MockITxManagerMockRecorder is the mock recorder for MockITxManager. +type MockITxManagerMockRecorder struct { + mock *MockITxManager +} + +// NewMockITxManager creates a new mock instance. +func NewMockITxManager(ctrl *gomock.Controller) *MockITxManager { + mock := &MockITxManager{ctrl: ctrl} + mock.recorder = &MockITxManagerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockITxManager) EXPECT() *MockITxManagerMockRecorder { + return m.recorder +} + +// DoInTx mocks base method. +func (m *MockITxManager) DoInTx(ctx context.Context, operation usecase.Operation) (context.Context, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DoInTx", ctx, operation) + ret0, _ := ret[0].(context.Context) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DoInTx indicates an expected call of DoInTx. +func (mr *MockITxManagerMockRecorder) DoInTx(ctx, operation any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DoInTx", reflect.TypeOf((*MockITxManager)(nil).DoInTx), ctx, operation) +} diff --git a/internal/usecase/transaction.go b/internal/usecase/transaction.go index cc9cb34..32ea675 100644 --- a/internal/usecase/transaction.go +++ b/internal/usecase/transaction.go @@ -1,13 +1,12 @@ +//go:generate mockgen -source=$GOFILE -package=mock_usecase -destination=./mock/$GOFILE package usecase import ( "context" - - "github.com/CityBear3/satellite/internal/domain/repository" ) -type Operation func(rtx repository.ITx) error +type Operation func(ctx context.Context) error type ITxManager interface { - DoInTx(ctx context.Context, operation Operation) error + DoInTx(ctx context.Context, operation Operation) (context.Context, error) }