From 5c716eb269cc6de7eced04ad5a6ed9cbb78b59bc Mon Sep 17 00:00:00 2001 From: dpurbosakti Date: Wed, 20 Aug 2025 16:51:24 +0700 Subject: [PATCH] refactor use case --- cmd/migration/migrations/20250820052409.sql | 26 -- cmd/migration/migrations/20250820055632.sql | 12 - cmd/migration/migrations/20250820061823.sql | 11 - cmd/migration/migrations/20250820062943.sql | 12 - cmd/migration/migrations/20250820083721.sql | 103 ++++ cmd/migration/migrations/atlas.sum | 7 +- internal/domain/main-entities/district/dto.go | 62 +++ .../domain/main-entities/district/entity.go | 11 + .../main-entities/division-position/entity.go | 2 +- .../domain/main-entities/division/entity.go | 2 +- .../main-entities/installation/entity.go | 2 +- internal/domain/main-entities/province/dto.go | 20 +- .../domain/main-entities/province/entity.go | 8 +- internal/domain/main-entities/regency/dto.go | 62 +++ .../domain/main-entities/regency/entity.go | 11 + internal/domain/main-entities/unit/entity.go | 2 +- internal/domain/main-entities/village/dto.go | 59 +++ .../domain/main-entities/village/entity.go | 8 + .../main-handler/district/handler.go | 71 +++ .../interface/main-handler/main-handler.go | 10 + .../main-handler/province/handler.go | 71 +++ .../interface/main-handler/regency/handler.go | 71 +++ .../interface/main-handler/village/handler.go | 71 +++ internal/interface/migration/migration.go | 28 +- .../use-case/main-use-case/district/case.go | 430 +++++++++++++++++ .../use-case/main-use-case/district/helper.go | 23 + .../use-case/main-use-case/district/lib.go | 97 ++++ .../main-use-case/district/tycovar.go | 32 ++ .../main-use-case/division-position/case.go | 46 +- .../use-case/main-use-case/division/case.go | 44 +- .../main-use-case/installation/case.go | 44 +- .../use-case/main-use-case/province/case.go | 431 +++++++++++++++++ .../use-case/main-use-case/province/helper.go | 22 + .../use-case/main-use-case/province/lib.go | 97 ++++ .../main-use-case/province/tycovar.go | 32 ++ .../use-case/main-use-case/regency/case.go | 431 +++++++++++++++++ .../use-case/main-use-case/regency/helper.go | 29 ++ .../use-case/main-use-case/regency/lib.go | 97 ++++ .../use-case/main-use-case/regency/tycovar.go | 32 ++ internal/use-case/main-use-case/unit/case.go | 45 +- internal/use-case/main-use-case/user/case.go | 46 +- .../use-case/main-use-case/village/case.go | 439 ++++++++++++++++++ .../use-case/main-use-case/village/helper.go | 22 + .../use-case/main-use-case/village/lib.go | 97 ++++ .../use-case/main-use-case/village/tycovar.go | 32 ++ pkg/use-case-helper/use-case-helper.go | 48 ++ 46 files changed, 3080 insertions(+), 278 deletions(-) delete mode 100644 cmd/migration/migrations/20250820052409.sql delete mode 100644 cmd/migration/migrations/20250820055632.sql delete mode 100644 cmd/migration/migrations/20250820061823.sql delete mode 100644 cmd/migration/migrations/20250820062943.sql create mode 100644 cmd/migration/migrations/20250820083721.sql create mode 100644 internal/domain/main-entities/district/dto.go create mode 100644 internal/domain/main-entities/district/entity.go create mode 100644 internal/domain/main-entities/regency/dto.go create mode 100644 internal/domain/main-entities/regency/entity.go create mode 100644 internal/domain/main-entities/village/dto.go create mode 100644 internal/domain/main-entities/village/entity.go create mode 100644 internal/interface/main-handler/district/handler.go create mode 100644 internal/interface/main-handler/province/handler.go create mode 100644 internal/interface/main-handler/regency/handler.go create mode 100644 internal/interface/main-handler/village/handler.go create mode 100644 internal/use-case/main-use-case/district/case.go create mode 100644 internal/use-case/main-use-case/district/helper.go create mode 100644 internal/use-case/main-use-case/district/lib.go create mode 100644 internal/use-case/main-use-case/district/tycovar.go create mode 100644 internal/use-case/main-use-case/province/case.go create mode 100644 internal/use-case/main-use-case/province/helper.go create mode 100644 internal/use-case/main-use-case/province/lib.go create mode 100644 internal/use-case/main-use-case/province/tycovar.go create mode 100644 internal/use-case/main-use-case/regency/case.go create mode 100644 internal/use-case/main-use-case/regency/helper.go create mode 100644 internal/use-case/main-use-case/regency/lib.go create mode 100644 internal/use-case/main-use-case/regency/tycovar.go create mode 100644 internal/use-case/main-use-case/village/case.go create mode 100644 internal/use-case/main-use-case/village/helper.go create mode 100644 internal/use-case/main-use-case/village/lib.go create mode 100644 internal/use-case/main-use-case/village/tycovar.go create mode 100644 pkg/use-case-helper/use-case-helper.go diff --git a/cmd/migration/migrations/20250820052409.sql b/cmd/migration/migrations/20250820052409.sql deleted file mode 100644 index b3df477e..00000000 --- a/cmd/migration/migrations/20250820052409.sql +++ /dev/null @@ -1,26 +0,0 @@ --- Create "Division" table -CREATE TABLE "public"."Division" ( - "Id" serial NOT NULL, - "CreatedAt" timestamptz NULL, - "UpdatedAt" timestamptz NULL, - "DeletedAt" timestamptz NULL, - "Code" character varying(10) NULL, - "Name" character varying(50) NULL, - "Parent_Id" smallint NULL, - PRIMARY KEY ("Id") -); --- Create "User" table -CREATE TABLE "public"."User" ( - "Id" bigserial NOT NULL, - "CreatedAt" timestamptz NULL, - "UpdatedAt" timestamptz NULL, - "DeletedAt" timestamptz NULL, - "Name" character varying(25) NOT NULL, - "Password" character varying(255) NOT NULL, - "Status_Code" character varying(10) NOT NULL, - "FailedLoginCount" smallint NULL, - "LoginAttemptCount" bigint NULL, - "LastSuccessLogin" timestamptz NULL, - "LastAllowdLogin" timestamptz NULL, - PRIMARY KEY ("Id") -); diff --git a/cmd/migration/migrations/20250820055632.sql b/cmd/migration/migrations/20250820055632.sql deleted file mode 100644 index 9e9369be..00000000 --- a/cmd/migration/migrations/20250820055632.sql +++ /dev/null @@ -1,12 +0,0 @@ --- Create "DivisionPosition" table -CREATE TABLE "public"."DivisionPosition" ( - "Id" serial NOT NULL, - "CreatedAt" timestamptz NULL, - "UpdatedAt" timestamptz NULL, - "DeletedAt" timestamptz NULL, - "Division_Id" integer NULL, - "Code" character varying(10) NULL, - "Name" character varying(50) NULL, - PRIMARY KEY ("Id"), - CONSTRAINT "fk_DivisionPosition_Division" FOREIGN KEY ("Division_Id") REFERENCES "public"."Division" ("Id") ON UPDATE NO ACTION ON DELETE NO ACTION -); diff --git a/cmd/migration/migrations/20250820061823.sql b/cmd/migration/migrations/20250820061823.sql deleted file mode 100644 index cd50b42b..00000000 --- a/cmd/migration/migrations/20250820061823.sql +++ /dev/null @@ -1,11 +0,0 @@ --- Create "Installation" table -CREATE TABLE "public"."Installation" ( - "Id" serial NOT NULL, - "CreatedAt" timestamptz NULL, - "UpdatedAt" timestamptz NULL, - "DeletedAt" timestamptz NULL, - "Code" character varying(10) NULL, - "Name" character varying(50) NULL, - "EncounterClass_Code" character varying(10) NULL, - PRIMARY KEY ("Id") -); diff --git a/cmd/migration/migrations/20250820062943.sql b/cmd/migration/migrations/20250820062943.sql deleted file mode 100644 index 36a3046b..00000000 --- a/cmd/migration/migrations/20250820062943.sql +++ /dev/null @@ -1,12 +0,0 @@ --- Create "Unit" table -CREATE TABLE "public"."Unit" ( - "Id" serial NOT NULL, - "CreatedAt" timestamptz NULL, - "UpdatedAt" timestamptz NULL, - "DeletedAt" timestamptz NULL, - "Installation_Id" integer NULL, - "Code" character varying(10) NULL, - "Name" character varying(50) NULL, - PRIMARY KEY ("Id"), - CONSTRAINT "fk_Unit_Installation" FOREIGN KEY ("Installation_Id") REFERENCES "public"."Installation" ("Id") ON UPDATE NO ACTION ON DELETE NO ACTION -); diff --git a/cmd/migration/migrations/20250820083721.sql b/cmd/migration/migrations/20250820083721.sql new file mode 100644 index 00000000..cee52edc --- /dev/null +++ b/cmd/migration/migrations/20250820083721.sql @@ -0,0 +1,103 @@ +-- Create "Province" table +CREATE TABLE "public"."Province" ( + "Id" smallserial NOT NULL, + "Code" character varying(2) NULL, + "Name" character varying(50) NULL, + PRIMARY KEY ("Id"), + CONSTRAINT "uni_Province_Code" UNIQUE ("Code") +); +-- Create "User" table +CREATE TABLE "public"."User" ( + "Id" bigserial NOT NULL, + "CreatedAt" timestamptz NULL, + "UpdatedAt" timestamptz NULL, + "DeletedAt" timestamptz NULL, + "Name" character varying(25) NOT NULL, + "Password" character varying(255) NOT NULL, + "Status_Code" character varying(10) NOT NULL, + "FailedLoginCount" smallint NULL, + "LoginAttemptCount" bigint NULL, + "LastSuccessLogin" timestamptz NULL, + "LastAllowdLogin" timestamptz NULL, + PRIMARY KEY ("Id") +); +-- Create "Regency" table +CREATE TABLE "public"."Regency" ( + "Id" serial NOT NULL, + "Province_Code" character varying(2) NULL, + "Code" character varying(4) NULL, + "Name" character varying(50) NULL, + PRIMARY KEY ("Id"), + CONSTRAINT "uni_Regency_Code" UNIQUE ("Code"), + CONSTRAINT "fk_Province_Regencies" FOREIGN KEY ("Province_Code") REFERENCES "public"."Province" ("Code") ON UPDATE NO ACTION ON DELETE NO ACTION +); +-- Create "District" table +CREATE TABLE "public"."District" ( + "Id" bigserial NOT NULL, + "Regency_Code" character varying(4) NULL, + "Code" character varying(6) NULL, + "Name" character varying(50) NULL, + PRIMARY KEY ("Id"), + CONSTRAINT "uni_District_Code" UNIQUE ("Code"), + CONSTRAINT "fk_Regency_Districts" FOREIGN KEY ("Regency_Code") REFERENCES "public"."Regency" ("Code") ON UPDATE NO ACTION ON DELETE NO ACTION +); +-- Create "Division" table +CREATE TABLE "public"."Division" ( + "Id" serial NOT NULL, + "CreatedAt" timestamptz NULL, + "UpdatedAt" timestamptz NULL, + "DeletedAt" timestamptz NULL, + "Code" character varying(10) NULL, + "Name" character varying(50) NULL, + "Parent_Id" smallint NULL, + PRIMARY KEY ("Id"), + CONSTRAINT "uni_Division_Code" UNIQUE ("Code") +); +-- Create "DivisionPosition" table +CREATE TABLE "public"."DivisionPosition" ( + "Id" serial NOT NULL, + "CreatedAt" timestamptz NULL, + "UpdatedAt" timestamptz NULL, + "DeletedAt" timestamptz NULL, + "Division_Id" integer NULL, + "Code" character varying(10) NULL, + "Name" character varying(50) NULL, + PRIMARY KEY ("Id"), + CONSTRAINT "uni_DivisionPosition_Code" UNIQUE ("Code"), + CONSTRAINT "fk_DivisionPosition_Division" FOREIGN KEY ("Division_Id") REFERENCES "public"."Division" ("Id") ON UPDATE NO ACTION ON DELETE NO ACTION +); +-- Create "Installation" table +CREATE TABLE "public"."Installation" ( + "Id" serial NOT NULL, + "CreatedAt" timestamptz NULL, + "UpdatedAt" timestamptz NULL, + "DeletedAt" timestamptz NULL, + "Code" character varying(10) NULL, + "Name" character varying(50) NULL, + "EncounterClass_Code" character varying(10) NULL, + PRIMARY KEY ("Id"), + CONSTRAINT "uni_Installation_Code" UNIQUE ("Code") +); +-- Create "Unit" table +CREATE TABLE "public"."Unit" ( + "Id" serial NOT NULL, + "CreatedAt" timestamptz NULL, + "UpdatedAt" timestamptz NULL, + "DeletedAt" timestamptz NULL, + "Installation_Id" integer NULL, + "Code" character varying(10) NULL, + "Name" character varying(50) NULL, + PRIMARY KEY ("Id"), + CONSTRAINT "uni_Unit_Code" UNIQUE ("Code"), + CONSTRAINT "fk_Unit_Installation" FOREIGN KEY ("Installation_Id") REFERENCES "public"."Installation" ("Id") ON UPDATE NO ACTION ON DELETE NO ACTION +); +-- Create "Village" table +CREATE TABLE "public"."Village" ( + "Id" bigserial NOT NULL, + "District_Code" character varying(6) NULL, + "Code" character varying(10) NULL, + "Name" character varying(50) NULL, + PRIMARY KEY ("Id"), + CONSTRAINT "uni_Village_Code" UNIQUE ("Code"), + CONSTRAINT "fk_District_Villages" FOREIGN KEY ("District_Code") REFERENCES "public"."District" ("Code") ON UPDATE NO ACTION ON DELETE NO ACTION +); diff --git a/cmd/migration/migrations/atlas.sum b/cmd/migration/migrations/atlas.sum index a6638e1e..b94d1d8d 100644 --- a/cmd/migration/migrations/atlas.sum +++ b/cmd/migration/migrations/atlas.sum @@ -1,5 +1,2 @@ -h1:sflrLy3WNiI1HdFXmrmyufKJOFz2lUT1ElPaNRU+uCo= -20250820052409.sql h1:W2zifi3eF+hG0fHvIrY15dH82WO/4QPGdZOEUkgKlAg= -20250820055632.sql h1:sPoDUmT4aZV4qiaq63ssIcoevr3N2WAjRcU1c+Kz6kc= -20250820061823.sql h1:mj37CorTCwngLSo5PRKGC3Wy4I4IapmNXaJGgGxU6y4= -20250820062943.sql h1:Cr8vZGsdJ8Bh+ZKGVAmEh+4rnsjgt5eIy3lExpE4etc= +h1:BeM3qkN2alMJypXIeFt15SbwthvdFivDIoB6gVZayPM= +20250820083721.sql h1:I3MicNCsXGQJLAWD6axmyvjKZHEObHbF5WKfhQf1ijQ= diff --git a/internal/domain/main-entities/district/dto.go b/internal/domain/main-entities/district/dto.go new file mode 100644 index 00000000..156d97b3 --- /dev/null +++ b/internal/domain/main-entities/district/dto.go @@ -0,0 +1,62 @@ +package district + +import ev "simrs-vx/internal/domain/main-entities/village" + +type CreateDto struct { + Regency_Code string `json:"regency_code" validate:"numeric;maxLength=4"` + Code string `json:"code" validate:"numeric;maxLength=6"` + Name string `json:"name" validate:"alphaSpace;maxLength=50"` +} + +type ReadListDto struct { + Regency_Code string `json:"regency_code"` + Code string `json:"code"` + Name string `json:"name"` + + Page int `json:"page"` + PageSize int `json:"page_size"` + NoPagination int `json:"no_pagination"` +} + +type ReadDetailDto struct { + Id uint32 `json:"id"` + Regency_Code string `json:"regency_code"` + Code string `json:"code"` + Name string `json:"name"` +} + +type UpdateDto struct { + Id uint32 `json:"id"` + CreateDto +} + +type DeleteDto struct { + Id uint32 `json:"id"` +} + +type MetaDto struct { + PageNumber int `json:"page_number"` + PageSize int `json:"page_size"` + Count int `json:"count"` +} + +type ResponseDto struct { + Id uint32 `json:"id"` + Regency_Code string `json:"regency_code"` + Code string `json:"code"` + Name string `json:"name"` + Villages []*ev.Village `json:"villages,omitempty"` +} + +func (d District) ToResponse() ResponseDto { + resp := ResponseDto(d) + return resp +} + +func ToResponseList(users []District) []ResponseDto { + resp := make([]ResponseDto, len(users)) + for i, u := range users { + resp[i] = u.ToResponse() + } + return resp +} diff --git a/internal/domain/main-entities/district/entity.go b/internal/domain/main-entities/district/entity.go new file mode 100644 index 00000000..a3ad27e7 --- /dev/null +++ b/internal/domain/main-entities/district/entity.go @@ -0,0 +1,11 @@ +package district + +import ev "simrs-vx/internal/domain/main-entities/village" + +type District struct { + Id uint32 `json:"id" gorm:"primaryKey"` + Regency_Code string `json:"regency_code" gorm:"size:4"` + Code string `json:"code" gorm:"unique;size:6"` // NOTE: THE PROPER SIZE IS 6 + Name string `json:"name" gorm:"size:50"` + Villages []*ev.Village `json:"villages,omitempty" gorm:"foreignKey:District_Code;references:Code"` +} diff --git a/internal/domain/main-entities/division-position/entity.go b/internal/domain/main-entities/division-position/entity.go index 88311e96..6ff32055 100644 --- a/internal/domain/main-entities/division-position/entity.go +++ b/internal/domain/main-entities/division-position/entity.go @@ -9,6 +9,6 @@ type DivisionPosition struct { ecore.SmallMain // adjust this according to the needs Division_Id *uint16 `json:"division_id"` Division *ed.Division `json:"division" gorm:"foreignKey:Division_Id"` - Code string `json:"code" gorm:"size:10"` + Code string `json:"code" gorm:"unique;size:10"` Name string `json:"name" gorm:"size:50"` } diff --git a/internal/domain/main-entities/division/entity.go b/internal/domain/main-entities/division/entity.go index 55918a8d..7d3bc65d 100644 --- a/internal/domain/main-entities/division/entity.go +++ b/internal/domain/main-entities/division/entity.go @@ -6,7 +6,7 @@ import ( type Division struct { ecore.SmallMain // adjust this according to the needs - Code string `json:"code" gorm:"size:10"` + Code string `json:"code" gorm:"unique;size:10"` Name string `json:"name" gorm:"size:50"` Parent_Id *int16 `json:"parent_id"` } diff --git a/internal/domain/main-entities/installation/entity.go b/internal/domain/main-entities/installation/entity.go index 84159810..203b231c 100644 --- a/internal/domain/main-entities/installation/entity.go +++ b/internal/domain/main-entities/installation/entity.go @@ -7,7 +7,7 @@ import ( type Installation struct { ecore.SmallMain // adjust this according to the needs - Code string `json:"code" gorm:"size:10"` + Code string `json:"code" gorm:"unique;size:10"` Name string `json:"name" gorm:"size:50"` EncounterClass_Code ere.EncounterClass `json:"encounterClass_code" gorm:"size:10"` } diff --git a/internal/domain/main-entities/province/dto.go b/internal/domain/main-entities/province/dto.go index 5fb9aae3..c1b771fa 100644 --- a/internal/domain/main-entities/province/dto.go +++ b/internal/domain/main-entities/province/dto.go @@ -1,12 +1,8 @@ package province -import ( - ecore "simrs-vx/internal/domain/base-entities/core" -) - type CreateDto struct { - Code string `json:"code"` - Name string `json:"name"` + Code string `json:"code" validate:"required;minLength=2;maxLength=2"` + Name string `json:"name" validate:"required;maxLength=10"` } type ReadListDto struct { @@ -19,16 +15,18 @@ type ReadListDto struct { } type ReadDetailDto struct { + Id int16 `json:"id"` Code string `json:"code"` Name string `json:"name"` } type UpdateDto struct { + Id int16 `json:"id"` CreateDto } type DeleteDto struct { - Code string `json:"code"` + Id int16 `json:"id"` } type MetaDto struct { @@ -38,14 +36,14 @@ type MetaDto struct { } type ResponseDto struct { - ecore.SmallMain - Code string `json:"code"` - Name string `json:"name"` - Parent_Id *int16 `json:"parent_id"` + Id int16 `json:"id"` + Code string `json:"code"` + Name string `json:"name"` } func (d Province) ToResponse() ResponseDto { resp := ResponseDto{ + Id: d.Id, Code: d.Code, Name: d.Name, } diff --git a/internal/domain/main-entities/province/entity.go b/internal/domain/main-entities/province/entity.go index dbe76750..ab024b22 100644 --- a/internal/domain/main-entities/province/entity.go +++ b/internal/domain/main-entities/province/entity.go @@ -1,6 +1,10 @@ package province +import er "simrs-vx/internal/domain/main-entities/regency" + type Province struct { - Code string `json:"code" gorm:"size:2"` - Name string `json:"name" gorm:"size:50"` + Id int16 `json:"id" gorm:"primaryKey"` + Code string `json:"code" gorm:"unique;size:2"` + Name string `json:"name" gorm:"size:50"` + Regencies []*er.Regency `json:"regencies,omitempty" gorm:"foreignKey:Province_Code;references:Code"` } diff --git a/internal/domain/main-entities/regency/dto.go b/internal/domain/main-entities/regency/dto.go new file mode 100644 index 00000000..56cc0a76 --- /dev/null +++ b/internal/domain/main-entities/regency/dto.go @@ -0,0 +1,62 @@ +package regency + +import ed "simrs-vx/internal/domain/main-entities/district" + +type CreateDto struct { + Province_Code string `json:"province_code" validate:"numeric;maxLength=2"` + Code string `json:"code" validate:"numeric;maxLength=4"` + Name string `json:"name" validate:"alphaSpace;maxLength=50"` +} + +type ReadListDto struct { + Province_Code string `json:"province_code"` + Code string `json:"code"` + Name string `json:"name"` + + Page int `json:"page"` + PageSize int `json:"page_size"` + NoPagination int `json:"no_pagination"` +} + +type ReadDetailDto struct { + Id uint16 `json:"id"` + Province_Code string `json:"province_code"` + Code string `json:"code"` + Name string `json:"name"` +} + +type UpdateDto struct { + Id uint16 `json:"id"` + CreateDto +} + +type DeleteDto struct { + Id uint16 `json:"id"` +} + +type MetaDto struct { + PageNumber int `json:"page_number"` + PageSize int `json:"page_size"` + Count int `json:"count"` +} + +type ResponseDto struct { + Id uint16 `json:"id"` + Province_Code string `json:"province_code"` + Code string `json:"code"` + Name string `json:"name"` + Districts []*ed.District `json:"districts,omitempty"` +} + +func (r Regency) ToResponse() ResponseDto { + resp := ResponseDto(r) + return resp +} + +func ToResponseList(users []Regency) []ResponseDto { + resp := make([]ResponseDto, len(users)) + for i, u := range users { + resp[i] = u.ToResponse() + } + return resp +} diff --git a/internal/domain/main-entities/regency/entity.go b/internal/domain/main-entities/regency/entity.go new file mode 100644 index 00000000..3bdcf934 --- /dev/null +++ b/internal/domain/main-entities/regency/entity.go @@ -0,0 +1,11 @@ +package regency + +import ed "simrs-vx/internal/domain/main-entities/district" + +type Regency struct { + Id uint16 `json:"id" gorm:"primaryKey"` + Province_Code string `json:"province_code" gorm:"size:2"` + Code string `json:"code" gorm:"unique;size:4"` + Name string `json:"name" gorm:"size:50"` + Districts []*ed.District `json:"districts,omitempty" gorm:"foreignKey:Regency_Code;references:Code"` +} diff --git a/internal/domain/main-entities/unit/entity.go b/internal/domain/main-entities/unit/entity.go index 3953692f..3b93475f 100644 --- a/internal/domain/main-entities/unit/entity.go +++ b/internal/domain/main-entities/unit/entity.go @@ -9,6 +9,6 @@ type Unit struct { ecore.SmallMain // adjust this according to the needs Installation_Id *uint16 `json:"installation_id"` Installation *ei.Installation `json:"installation" gorm:"foreignKey:Installation_Id"` - Code string `json:"code" gorm:"size:10"` + Code string `json:"code" gorm:"unique;size:10"` Name string `json:"name" gorm:"size:50"` } diff --git a/internal/domain/main-entities/village/dto.go b/internal/domain/main-entities/village/dto.go new file mode 100644 index 00000000..750a382c --- /dev/null +++ b/internal/domain/main-entities/village/dto.go @@ -0,0 +1,59 @@ +package village + +type CreateDto struct { + District_Code string `json:"district_code" validate:"numeric;maxLength=6"` + Code string `json:"code" validate:"numeric;maxLength=10"` + Name string `json:"name" validate:"alphaSpace;maxLength=50"` +} + +type ReadListDto struct { + District_Code string `json:"district_code"` + Code string `json:"code"` + Name string `json:"name"` + + Page int `json:"page"` + PageSize int `json:"page_size"` + NoPagination int `json:"no_pagination"` +} + +type ReadDetailDto struct { + Id uint32 `json:"id"` + District_Code string `json:"district_code"` + Code string `json:"code"` + Name string `json:"name"` +} + +type UpdateDto struct { + Id uint32 `json:"id"` + CreateDto +} + +type DeleteDto struct { + Id uint32 `json:"id"` +} + +type MetaDto struct { + PageNumber int `json:"page_number"` + PageSize int `json:"page_size"` + Count int `json:"count"` +} + +type ResponseDto struct { + Id uint32 `json:"id"` + District_Code string `json:"district_code"` + Code string `json:"code"` + Name string `json:"name"` +} + +func (v Village) ToResponse() ResponseDto { + resp := ResponseDto(v) + return resp +} + +func ToResponseList(users []Village) []ResponseDto { + resp := make([]ResponseDto, len(users)) + for i, u := range users { + resp[i] = u.ToResponse() + } + return resp +} diff --git a/internal/domain/main-entities/village/entity.go b/internal/domain/main-entities/village/entity.go new file mode 100644 index 00000000..aae0caf5 --- /dev/null +++ b/internal/domain/main-entities/village/entity.go @@ -0,0 +1,8 @@ +package village + +type Village struct { + Id uint32 `json:"id" gorm:"primaryKey"` + District_Code string `json:"district_code" gorm:"size:6"` // NOT: THE PROPER SIZE IS 6 + Code string `json:"code" gorm:"unique;size:10"` + Name string `json:"name" gorm:"size:50"` +} diff --git a/internal/interface/main-handler/district/handler.go b/internal/interface/main-handler/district/handler.go new file mode 100644 index 00000000..bdba5d96 --- /dev/null +++ b/internal/interface/main-handler/district/handler.go @@ -0,0 +1,71 @@ +package district + +import ( + "net/http" + + rw "github.com/karincake/risoles" + sf "github.com/karincake/semprit" + + // ua "github.com/karincake/tumpeng/auth/svc" + + e "simrs-vx/internal/domain/main-entities/district" + u "simrs-vx/internal/use-case/main-use-case/district" +) + +type myBase struct{} + +var O myBase + +func (obj myBase) Create(w http.ResponseWriter, r *http.Request) { + dto := e.CreateDto{} + if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { + return + } + res, err := u.Create(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) GetList(w http.ResponseWriter, r *http.Request) { + dto := e.ReadListDto{} + sf.UrlQueryParam(&dto, *r.URL) + res, err := u.ReadList(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) GetDetail(w http.ResponseWriter, r *http.Request) { + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + dto := e.ReadDetailDto{} + dto.Id = uint32(id) + res, err := u.ReadDetail(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) Update(w http.ResponseWriter, r *http.Request) { + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + + dto := e.UpdateDto{} + if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { + return + } + dto.Id = uint32(id) + res, err := u.Update(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) Delete(w http.ResponseWriter, r *http.Request) { + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + + dto := e.DeleteDto{} + dto.Id = uint32(id) + res, err := u.Delete(dto) + rw.DataResponse(w, res, err) +} diff --git a/internal/interface/main-handler/main-handler.go b/internal/interface/main-handler/main-handler.go index 6659b264..d066f8ff 100644 --- a/internal/interface/main-handler/main-handler.go +++ b/internal/interface/main-handler/main-handler.go @@ -27,6 +27,11 @@ import ( installation "simrs-vx/internal/interface/main-handler/installation" unit "simrs-vx/internal/interface/main-handler/unit" + district "simrs-vx/internal/interface/main-handler/district" + province "simrs-vx/internal/interface/main-handler/province" + regency "simrs-vx/internal/interface/main-handler/regency" + village "simrs-vx/internal/interface/main-handler/village" + ///// Internal "simrs-vx/internal/interface/main-handler/home" ) @@ -55,6 +60,11 @@ func SetRoutes() http.Handler { hc.RegCrud(r, "/v1/installation", installation.O) hc.RegCrud(r, "/v1/unit", unit.O) + hc.RegCrud(r, "/v1/village", village.O) + hc.RegCrud(r, "/v1/district", district.O) + hc.RegCrud(r, "/v1/regency", regency.O) + hc.RegCrud(r, "/v1/province", province.O) + ///// return cmw.SetCors(handlerlogger.SetLog(r)) } diff --git a/internal/interface/main-handler/province/handler.go b/internal/interface/main-handler/province/handler.go new file mode 100644 index 00000000..f95aec2e --- /dev/null +++ b/internal/interface/main-handler/province/handler.go @@ -0,0 +1,71 @@ +package province + +import ( + "net/http" + + rw "github.com/karincake/risoles" + sf "github.com/karincake/semprit" + + // ua "github.com/karincake/tumpeng/auth/svc" + + e "simrs-vx/internal/domain/main-entities/province" + u "simrs-vx/internal/use-case/main-use-case/province" +) + +type myBase struct{} + +var O myBase + +func (obj myBase) Create(w http.ResponseWriter, r *http.Request) { + dto := e.CreateDto{} + if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { + return + } + res, err := u.Create(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) GetList(w http.ResponseWriter, r *http.Request) { + dto := e.ReadListDto{} + sf.UrlQueryParam(&dto, *r.URL) + res, err := u.ReadList(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) GetDetail(w http.ResponseWriter, r *http.Request) { + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + dto := e.ReadDetailDto{} + dto.Id = int16(id) + res, err := u.ReadDetail(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) Update(w http.ResponseWriter, r *http.Request) { + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + + dto := e.UpdateDto{} + if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { + return + } + dto.Id = int16(id) + res, err := u.Update(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) Delete(w http.ResponseWriter, r *http.Request) { + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + + dto := e.DeleteDto{} + dto.Id = int16(id) + res, err := u.Delete(dto) + rw.DataResponse(w, res, err) +} diff --git a/internal/interface/main-handler/regency/handler.go b/internal/interface/main-handler/regency/handler.go new file mode 100644 index 00000000..e6170867 --- /dev/null +++ b/internal/interface/main-handler/regency/handler.go @@ -0,0 +1,71 @@ +package regency + +import ( + "net/http" + + rw "github.com/karincake/risoles" + sf "github.com/karincake/semprit" + + // ua "github.com/karincake/tumpeng/auth/svc" + + e "simrs-vx/internal/domain/main-entities/regency" + u "simrs-vx/internal/use-case/main-use-case/regency" +) + +type myBase struct{} + +var O myBase + +func (obj myBase) Create(w http.ResponseWriter, r *http.Request) { + dto := e.CreateDto{} + if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { + return + } + res, err := u.Create(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) GetList(w http.ResponseWriter, r *http.Request) { + dto := e.ReadListDto{} + sf.UrlQueryParam(&dto, *r.URL) + res, err := u.ReadList(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) GetDetail(w http.ResponseWriter, r *http.Request) { + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + dto := e.ReadDetailDto{} + dto.Id = uint16(id) + res, err := u.ReadDetail(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) Update(w http.ResponseWriter, r *http.Request) { + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + + dto := e.UpdateDto{} + if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { + return + } + dto.Id = uint16(id) + res, err := u.Update(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) Delete(w http.ResponseWriter, r *http.Request) { + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + + dto := e.DeleteDto{} + dto.Id = uint16(id) + res, err := u.Delete(dto) + rw.DataResponse(w, res, err) +} diff --git a/internal/interface/main-handler/village/handler.go b/internal/interface/main-handler/village/handler.go new file mode 100644 index 00000000..76138e72 --- /dev/null +++ b/internal/interface/main-handler/village/handler.go @@ -0,0 +1,71 @@ +package village + +import ( + "net/http" + + rw "github.com/karincake/risoles" + sf "github.com/karincake/semprit" + + // ua "github.com/karincake/tumpeng/auth/svc" + + e "simrs-vx/internal/domain/main-entities/village" + u "simrs-vx/internal/use-case/main-use-case/village" +) + +type myBase struct{} + +var O myBase + +func (obj myBase) Create(w http.ResponseWriter, r *http.Request) { + dto := e.CreateDto{} + if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { + return + } + res, err := u.Create(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) GetList(w http.ResponseWriter, r *http.Request) { + dto := e.ReadListDto{} + sf.UrlQueryParam(&dto, *r.URL) + res, err := u.ReadList(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) GetDetail(w http.ResponseWriter, r *http.Request) { + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + dto := e.ReadDetailDto{} + dto.Id = uint32(id) + res, err := u.ReadDetail(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) Update(w http.ResponseWriter, r *http.Request) { + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + + dto := e.UpdateDto{} + if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { + return + } + dto.Id = uint32(id) + res, err := u.Update(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) Delete(w http.ResponseWriter, r *http.Request) { + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + + dto := e.DeleteDto{} + dto.Id = uint32(id) + res, err := u.Delete(dto) + rw.DataResponse(w, res, err) +} diff --git a/internal/interface/migration/migration.go b/internal/interface/migration/migration.go index 723d097e..4f962b4b 100644 --- a/internal/interface/migration/migration.go +++ b/internal/interface/migration/migration.go @@ -5,11 +5,15 @@ import ( "io" "os" "os/exec" - ed "simrs-vx/internal/domain/main-entities/division" - edp "simrs-vx/internal/domain/main-entities/division-position" - ei "simrs-vx/internal/domain/main-entities/installation" - eun "simrs-vx/internal/domain/main-entities/unit" - eu "simrs-vx/internal/domain/main-entities/user" + district "simrs-vx/internal/domain/main-entities/district" + division "simrs-vx/internal/domain/main-entities/division" + divisionposition "simrs-vx/internal/domain/main-entities/division-position" + installation "simrs-vx/internal/domain/main-entities/installation" + province "simrs-vx/internal/domain/main-entities/province" + regency "simrs-vx/internal/domain/main-entities/regency" + unit "simrs-vx/internal/domain/main-entities/unit" + user "simrs-vx/internal/domain/main-entities/user" + village "simrs-vx/internal/domain/main-entities/village" "ariga.io/atlas-provider-gorm/gormschema" "gorm.io/gorm" @@ -44,11 +48,15 @@ func Loader() { func GetEntities() []any { return []any{ - &eu.User{}, - &ed.Division{}, - &edp.DivisionPosition{}, - &ei.Installation{}, - &eun.Unit{}, + &user.User{}, + &division.Division{}, + &divisionposition.DivisionPosition{}, + &installation.Installation{}, + &unit.Unit{}, + &village.Village{}, + &district.District{}, + ®ency.Regency{}, + &province.Province{}, } } diff --git a/internal/use-case/main-use-case/district/case.go b/internal/use-case/main-use-case/district/case.go new file mode 100644 index 00000000..c5f16ca9 --- /dev/null +++ b/internal/use-case/main-use-case/district/case.go @@ -0,0 +1,430 @@ +package district + +import ( + "fmt" + e "simrs-vx/internal/domain/main-entities/district" + "strconv" + + dg "github.com/karincake/apem/db-gorm-pg" + d "github.com/karincake/dodol" + + pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" + + "gorm.io/gorm" +) + +const source = "district" + +func Create(input e.CreateDto) (*d.Data, error) { + data := e.District{} + + setData(&input, &data) + + event := pl.Event{ + Feature: "Create", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "create") + + err := dg.I.Transaction(func(tx *gorm.DB) error { + for i := range createPreMw { + mwName := fmt.Sprintf("createPreMw[%d]", i) + + pl.SetLogInfo(&event, data, "started", mwName) + + if err := createPreMw[i](&input, &data, tx); err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "MW_PRE_FAILED", // TODO: add to lang json + Detail: fmt.Sprintf("Pre-middleware %s failed", mwName), + Raw: err, + } + return pl.SetLogError(event, data) + } + + pl.SetLogInfo(&event, nil, "complete") + } + + pl.SetLogInfo(&event, data, "started", "DBCreate") + _, err := CreateData(&data, tx) + if err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-create-fail", + Detail: "Database insert failed", + Raw: err, + } + return pl.SetLogError(event, data) + } + + pl.SetLogInfo(&event, nil, "complete") + + for i := range createPostMw { + mwName := fmt.Sprintf("createPostMw[%d]", i) + + pl.SetLogInfo(&event, input, "started", mwName) + if err := createPostMw[i](&input, &data, tx); err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "MW_POST_FAILED", // TODO: add to lang json + Detail: fmt.Sprintf("Post-middleware %s failed", mwName), + Raw: err, + } + return pl.SetLogError(event, data) + } + } + + pl.SetLogInfo(&event, nil, "complete") + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.II{ + "source": source, + "structure": "single-data", + "status": "created", + }, + Data: data.ToResponse(), + }, nil +} + +func ReadList(input e.ReadListDto) (*d.Data, error) { + var data *e.District + var dataList []e.District + var metaList *e.MetaDto + var err error + + event := pl.Event{ + Feature: "ReadList", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "readList") + + err = dg.I.Transaction(func(tx *gorm.DB) error { + for i := range readListPreMw { + mwName := fmt.Sprintf("readListPreMw[%d]", i) + + pl.SetLogInfo(&event, input, "started", mwName) + + if err := readListPreMw[i](&input, data, tx); err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "MW_PRE_FAILED", // TODO: add to lang json + Detail: fmt.Sprintf("Pre-middleware %s failed", mwName), + Raw: err, + } + return pl.SetLogError(event, input) + } + + pl.SetLogInfo(&event, nil, "complete") + } + + pl.SetLogInfo(&event, input, "started", "DBReadList") + dataList, metaList, err = ReadListData(input, tx) + if err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-read-list-fail", + Detail: "Database read list failed", + Raw: err, + } + return pl.SetLogError(event, input) + } + + pl.SetLogInfo(&event, nil, "complete") + + for i := range readListPostMw { + mwName := fmt.Sprintf("readListPostMw[%d]", i) + + pl.SetLogInfo(&event, input, "started", mwName) + + if err := readListPostMw[i](&input, data, tx); err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "MW_POST_FAILED", // TODO: add to lang json + Detail: fmt.Sprintf("Post-middleware %s failed", mwName), + Raw: err, + } + return pl.SetLogError(event, input) + } + + pl.SetLogInfo(&event, nil, "complete") + } + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "list-data", + "status": "fetched", + "page_number": strconv.Itoa(metaList.PageNumber), + "page_size": strconv.Itoa(metaList.PageSize), + "record_totalCount": strconv.Itoa(metaList.Count), + }, + Data: e.ToResponseList(dataList), + }, nil +} + +func ReadDetail(input e.ReadDetailDto) (*d.Data, error) { + var data *e.District + var err error + + event := pl.Event{ + Feature: "ReadDetail", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "readDetail") + + err = dg.I.Transaction(func(tx *gorm.DB) error { + for i := range readDetailPreMw { + mwName := fmt.Sprintf("readDetailPreMw[%d]", i) + + pl.SetLogInfo(&event, input, "started", mwName) + + if err := readDetailPreMw[i](&input, data, tx); err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "MW_PRE_FAILED", // TODO: add to lang json + Detail: fmt.Sprintf("Pre-middleware %s failed", mwName), + Raw: err, + } + return pl.SetLogError(event, input) + } + + pl.SetLogInfo(&event, nil, "complete") + } + + pl.SetLogInfo(&event, input, "started", "DBReadDetail") + data, err = ReadDetailData(input, tx) + if processedErr := pu.HandleReadError(err, &event, source, input.Id, data); processedErr != nil { + return processedErr + } + + for i := range readDetailPostMw { + mwName := fmt.Sprintf("readDetailPostMw[%d]", i) + + pl.SetLogInfo(&event, data, "started", mwName) + + if err := readDetailPostMw[i](&input, data, tx); err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "MW_POST_FAILED", // TODO: add to lang json + Detail: fmt.Sprintf("Post-middleware %s failed", mwName), + Raw: err, + } + return pl.SetLogError(event, data) + } + + pl.SetLogInfo(&event, nil, "complete") + } + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "fetched", + }, + Data: pu.SafeToResponse(data), + }, nil +} + +func Update(input e.UpdateDto) (*d.Data, error) { + rdDto := e.ReadDetailDto{Id: input.Id} + var data *e.District + var err error + + event := pl.Event{ + Feature: "Update", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "update") + + err = dg.I.Transaction(func(tx *gorm.DB) error { + pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") + data, err = ReadDetailData(rdDto, tx) + if processedErr := pu.HandleReadError(err, &event, source, input.Id, data); processedErr != nil { + return processedErr + } + pl.SetLogInfo(&event, input, "started", "setUpdate") + setData(&input, data) + + for i := range updatePreMw { + mwName := fmt.Sprintf("updatePreMw[%d]", i) + + pl.SetLogInfo(&event, data, "started", mwName) + + if err := updatePreMw[i](&rdDto, data, tx); err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "MW_PRE_FAILED", // TODO: add to lang json + Detail: fmt.Sprintf("Pre-middleware %s failed", mwName), + Raw: err, + } + return pl.SetLogError(event, data) + } + + pl.SetLogInfo(&event, nil, "complete") + } + + pl.SetLogInfo(&event, data, "started", "DBUpdate") + if err := UpdateData(*data, tx); err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-update-fail", + Detail: "Database update failed", + Raw: err, + } + return pl.SetLogError(event, data) + } + + pl.SetLogInfo(&event, nil, "complete") + + for i := range updatePostMw { + mwName := fmt.Sprintf("updatePostMw[%d]", i) + + pl.SetLogInfo(&event, data, "started", mwName) + + if err := updatePostMw[i](&rdDto, data, tx); err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "MW_POST_FAILED", // TODO: add to lang json + Detail: fmt.Sprintf("Post-middleware %s failed", mwName), + Raw: err, + } + return pl.SetLogError(event, data) + } + + pl.SetLogInfo(&event, nil, "complete") + } + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "updated", + }, + Data: data.ToResponse(), + }, nil + +} + +func Delete(input e.DeleteDto) (*d.Data, error) { + rdDto := e.ReadDetailDto{Id: input.Id} + var data *e.District + var err error + + event := pl.Event{ + Feature: "Delete", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "delete") + + err = dg.I.Transaction(func(tx *gorm.DB) error { + pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") + data, err = ReadDetailData(rdDto, tx) + if processedErr := pu.HandleReadError(err, &event, source, input.Id, data); processedErr != nil { + return processedErr + } + + for i := range deletePreMw { + mwName := fmt.Sprintf("deletePreMw[%d]", i) + + pl.SetLogInfo(&event, data, "started", mwName) + + if err := deletePreMw[i](&rdDto, data, tx); err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "MW_PRE_FAILED", // TODO: add to lang json + Detail: fmt.Sprintf("Pre-middleware %s failed", mwName), + Raw: err, + } + return pl.SetLogError(event, data) + } + + pl.SetLogInfo(&event, nil, "complete") + } + + pl.SetLogInfo(&event, data, "started", "DBDelete") + if err := DeleteData(data, tx); err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-delete-fail", + Detail: "Database delete failed", + Raw: err, + } + return pl.SetLogError(event, data) + } + + pl.SetLogInfo(&event, nil, "complete") + + for i := range deletePostMw { + mwName := fmt.Sprintf("deletePostMw[%d]", i) + + pl.SetLogInfo(&event, data, "started", mwName) + + if err := deletePostMw[i](&rdDto, data, tx); err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "MW_POST_FAILED", // TODO: add to lang json + Detail: fmt.Sprintf("Post-middleware %s failed", mwName), + Raw: err, + } + return pl.SetLogError(event, data) + } + + pl.SetLogInfo(&event, nil, "complete") + } + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "deleted", + }, + Data: data.ToResponse(), + }, nil + +} diff --git a/internal/use-case/main-use-case/district/helper.go b/internal/use-case/main-use-case/district/helper.go new file mode 100644 index 00000000..3959233c --- /dev/null +++ b/internal/use-case/main-use-case/district/helper.go @@ -0,0 +1,23 @@ +/* +DESCRIPTION: +Any functions that are used internally by the use-case +*/ +package district + +import ( + e "simrs-vx/internal/domain/main-entities/district" +) + +func setData[T *e.CreateDto | *e.UpdateDto](input T, data *e.District) { + var inputSrc *e.CreateDto + if inputT, ok := any(input).(*e.CreateDto); ok { + inputSrc = inputT + } else { + inputTemp := any(input).(*e.UpdateDto) + inputSrc = &inputTemp.CreateDto + } + + data.Regency_Code = inputSrc.Regency_Code + data.Code = inputSrc.Code + data.Name = inputSrc.Name +} diff --git a/internal/use-case/main-use-case/district/lib.go b/internal/use-case/main-use-case/district/lib.go new file mode 100644 index 00000000..402cae63 --- /dev/null +++ b/internal/use-case/main-use-case/district/lib.go @@ -0,0 +1,97 @@ +package district + +import ( + e "simrs-vx/internal/domain/main-entities/district" + + dg "github.com/karincake/apem/db-gorm-pg" + gh "github.com/karincake/getuk" + "gorm.io/gorm" +) + +func CreateData(input *e.District, dbx ...*gorm.DB) (*e.District, error) { + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if err := tx.Create(&input).Error; err != nil { + return nil, err + } + + return input, nil +} + +func ReadListData(input e.ReadListDto, dbx ...*gorm.DB) ([]e.District, *e.MetaDto, error) { + data := []e.District{} + pagination := gh.Pagination{} + count := int64(0) + meta := e.MetaDto{} + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + tx = tx. + Model(&e.District{}). + // Joins("Patient"). // if needed + // Preload("Patient"). // if needed + Scopes(gh.Filter(input)). + Count(&count). + Scopes(gh.Paginate(input, &pagination)) + + if err := tx.Debug().Find(&data).Error; err != nil { + if err == gorm.ErrRecordNotFound { + return nil, &meta, nil + } + return nil, nil, err + + } + meta.Count = int(count) + meta.PageNumber = pagination.PageNumber + meta.PageSize = pagination.PageSize + return data, &meta, nil +} + +func ReadDetailData(input e.ReadDetailDto, dbx ...*gorm.DB) (*e.District, error) { + data := e.District{} + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if err := tx.First(&data, input.Id).Error; err != nil { + return nil, err + } + + return &data, nil +} + +func UpdateData(input e.District, dbx ...*gorm.DB) error { + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + return tx.Save(&input).Error +} + +func DeleteData(input *e.District, dbx ...*gorm.DB) error { + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + return tx.Delete(input).Error +} diff --git a/internal/use-case/main-use-case/district/tycovar.go b/internal/use-case/main-use-case/district/tycovar.go new file mode 100644 index 00000000..99670a00 --- /dev/null +++ b/internal/use-case/main-use-case/district/tycovar.go @@ -0,0 +1,32 @@ +/* +DESCRIPTION: +A sample, part of the package that contains type, constants, and/or variables. + +In this sample it also provides type and variable regarding the needs of the +middleware to separate from main use-case which has the basic CRUD +functionality. The purpose of this is to make the code more maintainable. +*/ +package district + +import ( + "gorm.io/gorm" + + e "simrs-vx/internal/domain/main-entities/district" +) + +type createMw func(input *e.CreateDto, data *e.District, tx *gorm.DB) error +type readListMw func(input *e.ReadListDto, data *e.District, tx *gorm.DB) error +type readDetailMw func(input *e.ReadDetailDto, data *e.District, tx *gorm.DB) error +type updateMw func(input *e.ReadDetailDto, data *e.District, tx *gorm.DB) error +type deleteMw func(input *e.ReadDetailDto, data *e.District, tx *gorm.DB) error + +var createPreMw []createMw // preprocess middleware +var createPostMw []createMw // postprocess middleware +var readListPreMw []readListMw // .. +var readListPostMw []readListMw // .. +var readDetailPreMw []readDetailMw +var readDetailPostMw []readDetailMw +var updatePreMw []readDetailMw +var updatePostMw []readDetailMw +var deletePreMw []readDetailMw +var deletePostMw []readDetailMw diff --git a/internal/use-case/main-use-case/division-position/case.go b/internal/use-case/main-use-case/division-position/case.go index 2749ca1b..7d00343e 100644 --- a/internal/use-case/main-use-case/division-position/case.go +++ b/internal/use-case/main-use-case/division-position/case.go @@ -1,7 +1,6 @@ package divisionposition import ( - "errors" "fmt" e "simrs-vx/internal/domain/main-entities/division-position" "strconv" @@ -10,6 +9,7 @@ import ( d "github.com/karincake/dodol" pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" "gorm.io/gorm" ) @@ -214,24 +214,10 @@ func ReadDetail(input e.ReadDetailDto) (*d.Data, error) { pl.SetLogInfo(&event, input, "started", "DBReadDetail") data, err = ReadDetailData(input, tx) - if errors.Is(err, gorm.ErrRecordNotFound) { - event.Status = "failed" - event.ErrInfo = pl.ErrorInfo{ - Code: "data-notFound", - Detail: fmt.Sprintf("Data with ID %v not found", input.Id), - Raw: err, - } - } else { - event.Status = "failed" - event.ErrInfo = pl.ErrorInfo{ - Code: "data-read-detail-fail", - Detail: "Database read detail failed", - Raw: err, - } + if processedErr := pu.HandleReadError(err, &event, source, input.Id, data); processedErr != nil { + return processedErr } - pl.SetLogInfo(&event, nil, "complete") - for i := range readDetailPostMw { mwName := fmt.Sprintf("readDetailPostMw[%d]", i) @@ -263,7 +249,7 @@ func ReadDetail(input e.ReadDetailDto) (*d.Data, error) { "structure": "single-data", "status": "fetched", }, - Data: data.ToResponse(), + Data: pu.SafeToResponse(data), }, nil } @@ -283,17 +269,9 @@ func Update(input e.UpdateDto) (*d.Data, error) { err = dg.I.Transaction(func(tx *gorm.DB) error { pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") data, err = ReadDetailData(rdDto, tx) - if err != nil { - event.Status = "failed" - event.ErrInfo = pl.ErrorInfo{ - Code: "data-read-detail-fail", - Detail: "Database read detail failed", - Raw: err, - } - return pl.SetLogError(event, rdDto) + if processedErr := pu.HandleReadError(err, &event, source, input.Id, data); processedErr != nil { + return processedErr } - - pl.SetLogInfo(&event, input, "started", "setUpdate") setData(&input, data) for i := range updatePreMw { @@ -379,18 +357,10 @@ func Delete(input e.DeleteDto) (*d.Data, error) { err = dg.I.Transaction(func(tx *gorm.DB) error { pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") data, err = ReadDetailData(rdDto, tx) - if err != nil { - event.Status = "failed" - event.ErrInfo = pl.ErrorInfo{ - Code: "data-read-detail-fail", - Detail: "Database read detail failed", - Raw: err, - } - return pl.SetLogError(event, rdDto) + if processedErr := pu.HandleReadError(err, &event, source, input.Id, data); processedErr != nil { + return processedErr } - pl.SetLogInfo(&event, nil, "complete") - for i := range deletePreMw { mwName := fmt.Sprintf("deletePreMw[%d]", i) diff --git a/internal/use-case/main-use-case/division/case.go b/internal/use-case/main-use-case/division/case.go index dfd9d985..3333d61b 100644 --- a/internal/use-case/main-use-case/division/case.go +++ b/internal/use-case/main-use-case/division/case.go @@ -1,7 +1,6 @@ package division import ( - "errors" "fmt" e "simrs-vx/internal/domain/main-entities/division" "strconv" @@ -10,6 +9,7 @@ import ( d "github.com/karincake/dodol" pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" "gorm.io/gorm" ) @@ -214,24 +214,10 @@ func ReadDetail(input e.ReadDetailDto) (*d.Data, error) { pl.SetLogInfo(&event, input, "started", "DBReadDetail") data, err = ReadDetailData(input, tx) - if errors.Is(err, gorm.ErrRecordNotFound) { - event.Status = "failed" - event.ErrInfo = pl.ErrorInfo{ - Code: "data-notFound", - Detail: fmt.Sprintf("Data with ID %v not found", input.Id), - Raw: err, - } - } else { - event.Status = "failed" - event.ErrInfo = pl.ErrorInfo{ - Code: "data-read-detail-fail", - Detail: "Database read detail failed", - Raw: err, - } + if processedErr := pu.HandleReadError(err, &event, source, input.Id, data); processedErr != nil { + return processedErr } - pl.SetLogInfo(&event, nil, "complete") - for i := range readDetailPostMw { mwName := fmt.Sprintf("readDetailPostMw[%d]", i) @@ -263,7 +249,7 @@ func ReadDetail(input e.ReadDetailDto) (*d.Data, error) { "structure": "single-data", "status": "fetched", }, - Data: data.ToResponse(), + Data: pu.SafeToResponse(data), }, nil } @@ -283,14 +269,8 @@ func Update(input e.UpdateDto) (*d.Data, error) { err = dg.I.Transaction(func(tx *gorm.DB) error { pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") data, err = ReadDetailData(rdDto, tx) - if err != nil { - event.Status = "failed" - event.ErrInfo = pl.ErrorInfo{ - Code: "data-read-detail-fail", - Detail: "Database read detail failed", - Raw: err, - } - return pl.SetLogError(event, rdDto) + if processedErr := pu.HandleReadError(err, &event, source, input.Id, data); processedErr != nil { + return processedErr } pl.SetLogInfo(&event, input, "started", "setUpdate") @@ -379,18 +359,10 @@ func Delete(input e.DeleteDto) (*d.Data, error) { err = dg.I.Transaction(func(tx *gorm.DB) error { pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") data, err = ReadDetailData(rdDto, tx) - if err != nil { - event.Status = "failed" - event.ErrInfo = pl.ErrorInfo{ - Code: "data-read-detail-fail", - Detail: "Database read detail failed", - Raw: err, - } - return pl.SetLogError(event, rdDto) + if processedErr := pu.HandleReadError(err, &event, source, input.Id, data); processedErr != nil { + return processedErr } - pl.SetLogInfo(&event, nil, "complete") - for i := range deletePreMw { mwName := fmt.Sprintf("deletePreMw[%d]", i) diff --git a/internal/use-case/main-use-case/installation/case.go b/internal/use-case/main-use-case/installation/case.go index 9a2689ec..e12186f6 100644 --- a/internal/use-case/main-use-case/installation/case.go +++ b/internal/use-case/main-use-case/installation/case.go @@ -1,7 +1,6 @@ package installation import ( - "errors" "fmt" e "simrs-vx/internal/domain/main-entities/installation" "strconv" @@ -10,6 +9,7 @@ import ( d "github.com/karincake/dodol" pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" "gorm.io/gorm" ) @@ -214,24 +214,10 @@ func ReadDetail(input e.ReadDetailDto) (*d.Data, error) { pl.SetLogInfo(&event, input, "started", "DBReadDetail") data, err = ReadDetailData(input, tx) - if errors.Is(err, gorm.ErrRecordNotFound) { - event.Status = "failed" - event.ErrInfo = pl.ErrorInfo{ - Code: "data-notFound", - Detail: fmt.Sprintf("Data with ID %v not found", input.Id), - Raw: err, - } - } else { - event.Status = "failed" - event.ErrInfo = pl.ErrorInfo{ - Code: "data-read-detail-fail", - Detail: "Database read detail failed", - Raw: err, - } + if processedErr := pu.HandleReadError(err, &event, source, input.Id, data); processedErr != nil { + return processedErr } - pl.SetLogInfo(&event, nil, "complete") - for i := range readDetailPostMw { mwName := fmt.Sprintf("readDetailPostMw[%d]", i) @@ -263,7 +249,7 @@ func ReadDetail(input e.ReadDetailDto) (*d.Data, error) { "structure": "single-data", "status": "fetched", }, - Data: data.ToResponse(), + Data: pu.SafeToResponse(data), }, nil } @@ -283,14 +269,8 @@ func Update(input e.UpdateDto) (*d.Data, error) { err = dg.I.Transaction(func(tx *gorm.DB) error { pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") data, err = ReadDetailData(rdDto, tx) - if err != nil { - event.Status = "failed" - event.ErrInfo = pl.ErrorInfo{ - Code: "data-read-detail-fail", - Detail: "Database read detail failed", - Raw: err, - } - return pl.SetLogError(event, rdDto) + if processedErr := pu.HandleReadError(err, &event, source, input.Id, data); processedErr != nil { + return processedErr } pl.SetLogInfo(&event, input, "started", "setUpdate") @@ -379,18 +359,10 @@ func Delete(input e.DeleteDto) (*d.Data, error) { err = dg.I.Transaction(func(tx *gorm.DB) error { pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") data, err = ReadDetailData(rdDto, tx) - if err != nil { - event.Status = "failed" - event.ErrInfo = pl.ErrorInfo{ - Code: "data-read-detail-fail", - Detail: "Database read detail failed", - Raw: err, - } - return pl.SetLogError(event, rdDto) + if processedErr := pu.HandleReadError(err, &event, source, input.Id, data); processedErr != nil { + return processedErr } - pl.SetLogInfo(&event, nil, "complete") - for i := range deletePreMw { mwName := fmt.Sprintf("deletePreMw[%d]", i) diff --git a/internal/use-case/main-use-case/province/case.go b/internal/use-case/main-use-case/province/case.go new file mode 100644 index 00000000..758f4c18 --- /dev/null +++ b/internal/use-case/main-use-case/province/case.go @@ -0,0 +1,431 @@ +package province + +import ( + "fmt" + e "simrs-vx/internal/domain/main-entities/province" + "strconv" + + dg "github.com/karincake/apem/db-gorm-pg" + d "github.com/karincake/dodol" + + pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" + + "gorm.io/gorm" +) + +const source = "province" + +func Create(input e.CreateDto) (*d.Data, error) { + data := e.Province{} + + setData(&input, &data) + + event := pl.Event{ + Feature: "Create", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "create") + + err := dg.I.Transaction(func(tx *gorm.DB) error { + for i := range createPreMw { + mwName := fmt.Sprintf("createPreMw[%d]", i) + + pl.SetLogInfo(&event, data, "started", mwName) + + if err := createPreMw[i](&input, &data, tx); err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "MW_PRE_FAILED", // TODO: add to lang json + Detail: fmt.Sprintf("Pre-middleware %s failed", mwName), + Raw: err, + } + return pl.SetLogError(event, data) + } + + pl.SetLogInfo(&event, nil, "complete") + } + + pl.SetLogInfo(&event, data, "started", "DBCreate") + _, err := CreateData(&data, tx) + if err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-create-fail", + Detail: "Database insert failed", + Raw: err, + } + return pl.SetLogError(event, data) + } + + pl.SetLogInfo(&event, nil, "complete") + + for i := range createPostMw { + mwName := fmt.Sprintf("createPostMw[%d]", i) + + pl.SetLogInfo(&event, input, "started", mwName) + if err := createPostMw[i](&input, &data, tx); err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "MW_POST_FAILED", // TODO: add to lang json + Detail: fmt.Sprintf("Post-middleware %s failed", mwName), + Raw: err, + } + return pl.SetLogError(event, data) + } + } + + pl.SetLogInfo(&event, nil, "complete") + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.II{ + "source": source, + "structure": "single-data", + "status": "created", + }, + Data: data.ToResponse(), + }, nil +} + +func ReadList(input e.ReadListDto) (*d.Data, error) { + var data *e.Province + var dataList []e.Province + var metaList *e.MetaDto + var err error + + event := pl.Event{ + Feature: "ReadList", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "readList") + + err = dg.I.Transaction(func(tx *gorm.DB) error { + for i := range readListPreMw { + mwName := fmt.Sprintf("readListPreMw[%d]", i) + + pl.SetLogInfo(&event, input, "started", mwName) + + if err := readListPreMw[i](&input, data, tx); err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "MW_PRE_FAILED", // TODO: add to lang json + Detail: fmt.Sprintf("Pre-middleware %s failed", mwName), + Raw: err, + } + return pl.SetLogError(event, input) + } + + pl.SetLogInfo(&event, nil, "complete") + } + + pl.SetLogInfo(&event, input, "started", "DBReadList") + dataList, metaList, err = ReadListData(input, tx) + if err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-read-list-fail", + Detail: "Database read list failed", + Raw: err, + } + return pl.SetLogError(event, input) + } + + pl.SetLogInfo(&event, nil, "complete") + + for i := range readListPostMw { + mwName := fmt.Sprintf("readListPostMw[%d]", i) + + pl.SetLogInfo(&event, input, "started", mwName) + + if err := readListPostMw[i](&input, data, tx); err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "MW_POST_FAILED", // TODO: add to lang json + Detail: fmt.Sprintf("Post-middleware %s failed", mwName), + Raw: err, + } + return pl.SetLogError(event, input) + } + + pl.SetLogInfo(&event, nil, "complete") + } + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "list-data", + "status": "fetched", + "page_number": strconv.Itoa(metaList.PageNumber), + "page_size": strconv.Itoa(metaList.PageSize), + "record_totalCount": strconv.Itoa(metaList.Count), + }, + Data: e.ToResponseList(dataList), + }, nil +} + +func ReadDetail(input e.ReadDetailDto) (*d.Data, error) { + var data *e.Province + var err error + + event := pl.Event{ + Feature: "ReadDetail", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "readDetail") + + err = dg.I.Transaction(func(tx *gorm.DB) error { + for i := range readDetailPreMw { + mwName := fmt.Sprintf("readDetailPreMw[%d]", i) + + pl.SetLogInfo(&event, input, "started", mwName) + + if err := readDetailPreMw[i](&input, data, tx); err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "MW_PRE_FAILED", // TODO: add to lang json + Detail: fmt.Sprintf("Pre-middleware %s failed", mwName), + Raw: err, + } + return pl.SetLogError(event, input) + } + + pl.SetLogInfo(&event, nil, "complete") + } + + pl.SetLogInfo(&event, input, "started", "DBReadDetail") + data, err = ReadDetailData(input, tx) + if processedErr := pu.HandleReadError(err, &event, source, input.Id, data); processedErr != nil { + return processedErr + } + + for i := range readDetailPostMw { + mwName := fmt.Sprintf("readDetailPostMw[%d]", i) + + pl.SetLogInfo(&event, data, "started", mwName) + + if err := readDetailPostMw[i](&input, data, tx); err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "MW_POST_FAILED", // TODO: add to lang json + Detail: fmt.Sprintf("Post-middleware %s failed", mwName), + Raw: err, + } + return pl.SetLogError(event, data) + } + + pl.SetLogInfo(&event, nil, "complete") + } + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "fetched", + }, + Data: pu.SafeToResponse(data), + }, nil +} + +func Update(input e.UpdateDto) (*d.Data, error) { + rdDto := e.ReadDetailDto{Id: input.Id} + var data *e.Province + var err error + + event := pl.Event{ + Feature: "Update", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "update") + + err = dg.I.Transaction(func(tx *gorm.DB) error { + pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") + data, err = ReadDetailData(rdDto, tx) + if processedErr := pu.HandleReadError(err, &event, source, input.Id, data); processedErr != nil { + return processedErr + } + + pl.SetLogInfo(&event, input, "started", "setUpdate") + setData(&input, data) + + for i := range updatePreMw { + mwName := fmt.Sprintf("updatePreMw[%d]", i) + + pl.SetLogInfo(&event, data, "started", mwName) + + if err := updatePreMw[i](&rdDto, data, tx); err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "MW_PRE_FAILED", // TODO: add to lang json + Detail: fmt.Sprintf("Pre-middleware %s failed", mwName), + Raw: err, + } + return pl.SetLogError(event, data) + } + + pl.SetLogInfo(&event, nil, "complete") + } + + pl.SetLogInfo(&event, data, "started", "DBUpdate") + if err := UpdateData(*data, tx); err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-update-fail", + Detail: "Database update failed", + Raw: err, + } + return pl.SetLogError(event, data) + } + + pl.SetLogInfo(&event, nil, "complete") + + for i := range updatePostMw { + mwName := fmt.Sprintf("updatePostMw[%d]", i) + + pl.SetLogInfo(&event, data, "started", mwName) + + if err := updatePostMw[i](&rdDto, data, tx); err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "MW_POST_FAILED", // TODO: add to lang json + Detail: fmt.Sprintf("Post-middleware %s failed", mwName), + Raw: err, + } + return pl.SetLogError(event, data) + } + + pl.SetLogInfo(&event, nil, "complete") + } + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "updated", + }, + Data: data.ToResponse(), + }, nil + +} + +func Delete(input e.DeleteDto) (*d.Data, error) { + rdDto := e.ReadDetailDto{Id: input.Id} + var data *e.Province + var err error + + event := pl.Event{ + Feature: "Delete", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "delete") + + err = dg.I.Transaction(func(tx *gorm.DB) error { + pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") + data, err = ReadDetailData(rdDto, tx) + if processedErr := pu.HandleReadError(err, &event, source, input.Id, data); processedErr != nil { + return processedErr + } + + for i := range deletePreMw { + mwName := fmt.Sprintf("deletePreMw[%d]", i) + + pl.SetLogInfo(&event, data, "started", mwName) + + if err := deletePreMw[i](&rdDto, data, tx); err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "MW_PRE_FAILED", // TODO: add to lang json + Detail: fmt.Sprintf("Pre-middleware %s failed", mwName), + Raw: err, + } + return pl.SetLogError(event, data) + } + + pl.SetLogInfo(&event, nil, "complete") + } + + pl.SetLogInfo(&event, data, "started", "DBDelete") + if err := DeleteData(data, tx); err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-delete-fail", + Detail: "Database delete failed", + Raw: err, + } + return pl.SetLogError(event, data) + } + + pl.SetLogInfo(&event, nil, "complete") + + for i := range deletePostMw { + mwName := fmt.Sprintf("deletePostMw[%d]", i) + + pl.SetLogInfo(&event, data, "started", mwName) + + if err := deletePostMw[i](&rdDto, data, tx); err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "MW_POST_FAILED", // TODO: add to lang json + Detail: fmt.Sprintf("Post-middleware %s failed", mwName), + Raw: err, + } + return pl.SetLogError(event, data) + } + + pl.SetLogInfo(&event, nil, "complete") + } + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "deleted", + }, + Data: data.ToResponse(), + }, nil + +} diff --git a/internal/use-case/main-use-case/province/helper.go b/internal/use-case/main-use-case/province/helper.go new file mode 100644 index 00000000..62774538 --- /dev/null +++ b/internal/use-case/main-use-case/province/helper.go @@ -0,0 +1,22 @@ +/* +DESCRIPTION: +Any functions that are used internally by the use-case +*/ +package province + +import ( + e "simrs-vx/internal/domain/main-entities/province" +) + +func setData[T *e.CreateDto | *e.UpdateDto](input T, data *e.Province) { + var inputSrc *e.CreateDto + if inputT, ok := any(input).(*e.CreateDto); ok { + inputSrc = inputT + } else { + inputTemp := any(input).(*e.UpdateDto) + inputSrc = &inputTemp.CreateDto + } + + data.Code = inputSrc.Code + data.Name = inputSrc.Name +} diff --git a/internal/use-case/main-use-case/province/lib.go b/internal/use-case/main-use-case/province/lib.go new file mode 100644 index 00000000..df44515b --- /dev/null +++ b/internal/use-case/main-use-case/province/lib.go @@ -0,0 +1,97 @@ +package province + +import ( + e "simrs-vx/internal/domain/main-entities/province" + + dg "github.com/karincake/apem/db-gorm-pg" + gh "github.com/karincake/getuk" + "gorm.io/gorm" +) + +func CreateData(input *e.Province, dbx ...*gorm.DB) (*e.Province, error) { + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if err := tx.Create(&input).Error; err != nil { + return nil, err + } + + return input, nil +} + +func ReadListData(input e.ReadListDto, dbx ...*gorm.DB) ([]e.Province, *e.MetaDto, error) { + data := []e.Province{} + pagination := gh.Pagination{} + count := int64(0) + meta := e.MetaDto{} + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + tx = tx. + Model(&e.Province{}). + // Joins("Patient"). // if needed + // Preload("Patient"). // if needed + Scopes(gh.Filter(input)). + Count(&count). + Scopes(gh.Paginate(input, &pagination)) + + if err := tx.Debug().Find(&data).Error; err != nil { + if err == gorm.ErrRecordNotFound { + return nil, &meta, nil + } + return nil, nil, err + + } + meta.Count = int(count) + meta.PageNumber = pagination.PageNumber + meta.PageSize = pagination.PageSize + return data, &meta, nil +} + +func ReadDetailData(input e.ReadDetailDto, dbx ...*gorm.DB) (*e.Province, error) { + data := e.Province{} + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if err := tx.First(&data, input.Id).Error; err != nil { + return nil, err + } + + return &data, nil +} + +func UpdateData(input e.Province, dbx ...*gorm.DB) error { + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + return tx.Save(&input).Error +} + +func DeleteData(input *e.Province, dbx ...*gorm.DB) error { + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + return tx.Delete(input).Error +} diff --git a/internal/use-case/main-use-case/province/tycovar.go b/internal/use-case/main-use-case/province/tycovar.go new file mode 100644 index 00000000..e65ab008 --- /dev/null +++ b/internal/use-case/main-use-case/province/tycovar.go @@ -0,0 +1,32 @@ +/* +DESCRIPTION: +A sample, part of the package that contains type, constants, and/or variables. + +In this sample it also provides type and variable regarding the needs of the +middleware to separate from main use-case which has the basic CRUD +functionality. The purpose of this is to make the code more maintainable. +*/ +package province + +import ( + "gorm.io/gorm" + + e "simrs-vx/internal/domain/main-entities/province" +) + +type createMw func(input *e.CreateDto, data *e.Province, tx *gorm.DB) error +type readListMw func(input *e.ReadListDto, data *e.Province, tx *gorm.DB) error +type readDetailMw func(input *e.ReadDetailDto, data *e.Province, tx *gorm.DB) error +type updateMw func(input *e.ReadDetailDto, data *e.Province, tx *gorm.DB) error +type deleteMw func(input *e.ReadDetailDto, data *e.Province, tx *gorm.DB) error + +var createPreMw []createMw // preprocess middleware +var createPostMw []createMw // postprocess middleware +var readListPreMw []readListMw // .. +var readListPostMw []readListMw // .. +var readDetailPreMw []readDetailMw +var readDetailPostMw []readDetailMw +var updatePreMw []readDetailMw +var updatePostMw []readDetailMw +var deletePreMw []readDetailMw +var deletePostMw []readDetailMw diff --git a/internal/use-case/main-use-case/regency/case.go b/internal/use-case/main-use-case/regency/case.go new file mode 100644 index 00000000..deabdc4c --- /dev/null +++ b/internal/use-case/main-use-case/regency/case.go @@ -0,0 +1,431 @@ +package regency + +import ( + "fmt" + e "simrs-vx/internal/domain/main-entities/regency" + "strconv" + + dg "github.com/karincake/apem/db-gorm-pg" + d "github.com/karincake/dodol" + + pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" + + "gorm.io/gorm" +) + +const source = "regency" + +func Create(input e.CreateDto) (*d.Data, error) { + data := e.Regency{} + + setData(&input, &data) + + event := pl.Event{ + Feature: "Create", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "create") + + err := dg.I.Transaction(func(tx *gorm.DB) error { + for i := range createPreMw { + mwName := fmt.Sprintf("createPreMw[%d]", i) + + pl.SetLogInfo(&event, data, "started", mwName) + + if err := createPreMw[i](&input, &data, tx); err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "MW_PRE_FAILED", // TODO: add to lang json + Detail: fmt.Sprintf("Pre-middleware %s failed", mwName), + Raw: err, + } + return pl.SetLogError(event, data) + } + + pl.SetLogInfo(&event, nil, "complete") + } + + pl.SetLogInfo(&event, data, "started", "DBCreate") + _, err := CreateData(&data, tx) + if err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-create-fail", + Detail: "Database insert failed", + Raw: err, + } + return pl.SetLogError(event, data) + } + + pl.SetLogInfo(&event, nil, "complete") + + for i := range createPostMw { + mwName := fmt.Sprintf("createPostMw[%d]", i) + + pl.SetLogInfo(&event, input, "started", mwName) + if err := createPostMw[i](&input, &data, tx); err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "MW_POST_FAILED", // TODO: add to lang json + Detail: fmt.Sprintf("Post-middleware %s failed", mwName), + Raw: err, + } + return pl.SetLogError(event, data) + } + } + + pl.SetLogInfo(&event, nil, "complete") + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.II{ + "source": source, + "structure": "single-data", + "status": "created", + }, + Data: data.ToResponse(), + }, nil +} + +func ReadList(input e.ReadListDto) (*d.Data, error) { + var data *e.Regency + var dataList []e.Regency + var metaList *e.MetaDto + var err error + + event := pl.Event{ + Feature: "ReadList", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "readList") + + err = dg.I.Transaction(func(tx *gorm.DB) error { + for i := range readListPreMw { + mwName := fmt.Sprintf("readListPreMw[%d]", i) + + pl.SetLogInfo(&event, input, "started", mwName) + + if err := readListPreMw[i](&input, data, tx); err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "MW_PRE_FAILED", // TODO: add to lang json + Detail: fmt.Sprintf("Pre-middleware %s failed", mwName), + Raw: err, + } + return pl.SetLogError(event, input) + } + + pl.SetLogInfo(&event, nil, "complete") + } + + pl.SetLogInfo(&event, input, "started", "DBReadList") + dataList, metaList, err = ReadListData(input, tx) + if err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-read-list-fail", + Detail: "Database read list failed", + Raw: err, + } + return pl.SetLogError(event, input) + } + + pl.SetLogInfo(&event, nil, "complete") + + for i := range readListPostMw { + mwName := fmt.Sprintf("readListPostMw[%d]", i) + + pl.SetLogInfo(&event, input, "started", mwName) + + if err := readListPostMw[i](&input, data, tx); err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "MW_POST_FAILED", // TODO: add to lang json + Detail: fmt.Sprintf("Post-middleware %s failed", mwName), + Raw: err, + } + return pl.SetLogError(event, input) + } + + pl.SetLogInfo(&event, nil, "complete") + } + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "list-data", + "status": "fetched", + "page_number": strconv.Itoa(metaList.PageNumber), + "page_size": strconv.Itoa(metaList.PageSize), + "record_totalCount": strconv.Itoa(metaList.Count), + }, + Data: e.ToResponseList(dataList), + }, nil +} + +func ReadDetail(input e.ReadDetailDto) (*d.Data, error) { + var data *e.Regency + var err error + + event := pl.Event{ + Feature: "ReadDetail", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "readDetail") + + err = dg.I.Transaction(func(tx *gorm.DB) error { + for i := range readDetailPreMw { + mwName := fmt.Sprintf("readDetailPreMw[%d]", i) + + pl.SetLogInfo(&event, input, "started", mwName) + + if err := readDetailPreMw[i](&input, data, tx); err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "MW_PRE_FAILED", // TODO: add to lang json + Detail: fmt.Sprintf("Pre-middleware %s failed", mwName), + Raw: err, + } + return pl.SetLogError(event, input) + } + + pl.SetLogInfo(&event, nil, "complete") + } + + pl.SetLogInfo(&event, input, "started", "DBReadDetail") + data, err = ReadDetailData(input, tx) + if processedErr := pu.HandleReadError(err, &event, source, input.Id, data); processedErr != nil { + return processedErr + } + + for i := range readDetailPostMw { + mwName := fmt.Sprintf("readDetailPostMw[%d]", i) + + pl.SetLogInfo(&event, data, "started", mwName) + + if err := readDetailPostMw[i](&input, data, tx); err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "MW_POST_FAILED", // TODO: add to lang json + Detail: fmt.Sprintf("Post-middleware %s failed", mwName), + Raw: err, + } + return pl.SetLogError(event, data) + } + + pl.SetLogInfo(&event, nil, "complete") + } + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "fetched", + }, + Data: pu.SafeToResponse(data), + }, nil +} + +func Update(input e.UpdateDto) (*d.Data, error) { + rdDto := e.ReadDetailDto{Id: input.Id} + var data *e.Regency + var err error + + event := pl.Event{ + Feature: "Update", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "update") + + err = dg.I.Transaction(func(tx *gorm.DB) error { + pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") + data, err = ReadDetailData(rdDto, tx) + if processedErr := pu.HandleReadError(err, &event, source, input.Id, data); processedErr != nil { + return processedErr + } + + pl.SetLogInfo(&event, input, "started", "setUpdate") + setData(&input, data) + + for i := range updatePreMw { + mwName := fmt.Sprintf("updatePreMw[%d]", i) + + pl.SetLogInfo(&event, data, "started", mwName) + + if err := updatePreMw[i](&rdDto, data, tx); err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "MW_PRE_FAILED", // TODO: add to lang json + Detail: fmt.Sprintf("Pre-middleware %s failed", mwName), + Raw: err, + } + return pl.SetLogError(event, data) + } + + pl.SetLogInfo(&event, nil, "complete") + } + + pl.SetLogInfo(&event, data, "started", "DBUpdate") + if err := UpdateData(*data, tx); err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-update-fail", + Detail: "Database update failed", + Raw: err, + } + return pl.SetLogError(event, data) + } + + pl.SetLogInfo(&event, nil, "complete") + + for i := range updatePostMw { + mwName := fmt.Sprintf("updatePostMw[%d]", i) + + pl.SetLogInfo(&event, data, "started", mwName) + + if err := updatePostMw[i](&rdDto, data, tx); err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "MW_POST_FAILED", // TODO: add to lang json + Detail: fmt.Sprintf("Post-middleware %s failed", mwName), + Raw: err, + } + return pl.SetLogError(event, data) + } + + pl.SetLogInfo(&event, nil, "complete") + } + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "updated", + }, + Data: data.ToResponse(), + }, nil + +} + +func Delete(input e.DeleteDto) (*d.Data, error) { + rdDto := e.ReadDetailDto{Id: input.Id} + var data *e.Regency + var err error + + event := pl.Event{ + Feature: "Delete", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "delete") + + err = dg.I.Transaction(func(tx *gorm.DB) error { + pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") + data, err = ReadDetailData(rdDto, tx) + if processedErr := pu.HandleReadError(err, &event, source, input.Id, data); processedErr != nil { + return processedErr + } + + for i := range deletePreMw { + mwName := fmt.Sprintf("deletePreMw[%d]", i) + + pl.SetLogInfo(&event, data, "started", mwName) + + if err := deletePreMw[i](&rdDto, data, tx); err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "MW_PRE_FAILED", // TODO: add to lang json + Detail: fmt.Sprintf("Pre-middleware %s failed", mwName), + Raw: err, + } + return pl.SetLogError(event, data) + } + + pl.SetLogInfo(&event, nil, "complete") + } + + pl.SetLogInfo(&event, data, "started", "DBDelete") + if err := DeleteData(data, tx); err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-delete-fail", + Detail: "Database delete failed", + Raw: err, + } + return pl.SetLogError(event, data) + } + + pl.SetLogInfo(&event, nil, "complete") + + for i := range deletePostMw { + mwName := fmt.Sprintf("deletePostMw[%d]", i) + + pl.SetLogInfo(&event, data, "started", mwName) + + if err := deletePostMw[i](&rdDto, data, tx); err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "MW_POST_FAILED", // TODO: add to lang json + Detail: fmt.Sprintf("Post-middleware %s failed", mwName), + Raw: err, + } + return pl.SetLogError(event, data) + } + + pl.SetLogInfo(&event, nil, "complete") + } + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "deleted", + }, + Data: data.ToResponse(), + }, nil + +} diff --git a/internal/use-case/main-use-case/regency/helper.go b/internal/use-case/main-use-case/regency/helper.go new file mode 100644 index 00000000..aa867cf0 --- /dev/null +++ b/internal/use-case/main-use-case/regency/helper.go @@ -0,0 +1,29 @@ +/* +DESCRIPTION: +Any functions that are used internally by the use-case +*/ +package regency + +import ( + e "simrs-vx/internal/domain/main-entities/regency" +) + +func setData[T *e.CreateDto | *e.UpdateDto](input T, data *e.Regency) { + var inputSrc *e.CreateDto + if inputT, ok := any(input).(*e.CreateDto); ok { + inputSrc = inputT + } else { + inputTemp := any(input).(*e.UpdateDto) + inputSrc = &inputTemp.CreateDto + } + data.Province_Code = inputSrc.Province_Code + data.Code = inputSrc.Code + data.Name = inputSrc.Name +} + +func toResponseOrNil(data interface{ ToResponse() any }) any { + if data == nil { + return nil + } + return data.ToResponse() +} diff --git a/internal/use-case/main-use-case/regency/lib.go b/internal/use-case/main-use-case/regency/lib.go new file mode 100644 index 00000000..67bb1a17 --- /dev/null +++ b/internal/use-case/main-use-case/regency/lib.go @@ -0,0 +1,97 @@ +package regency + +import ( + e "simrs-vx/internal/domain/main-entities/regency" + + dg "github.com/karincake/apem/db-gorm-pg" + gh "github.com/karincake/getuk" + "gorm.io/gorm" +) + +func CreateData(input *e.Regency, dbx ...*gorm.DB) (*e.Regency, error) { + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if err := tx.Create(&input).Error; err != nil { + return nil, err + } + + return input, nil +} + +func ReadListData(input e.ReadListDto, dbx ...*gorm.DB) ([]e.Regency, *e.MetaDto, error) { + data := []e.Regency{} + pagination := gh.Pagination{} + count := int64(0) + meta := e.MetaDto{} + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + tx = tx. + Model(&e.Regency{}). + // Joins("Patient"). // if needed + // Preload("Patient"). // if needed + Scopes(gh.Filter(input)). + Count(&count). + Scopes(gh.Paginate(input, &pagination)) + + if err := tx.Debug().Find(&data).Error; err != nil { + if err == gorm.ErrRecordNotFound { + return nil, &meta, nil + } + return nil, nil, err + + } + meta.Count = int(count) + meta.PageNumber = pagination.PageNumber + meta.PageSize = pagination.PageSize + return data, &meta, nil +} + +func ReadDetailData(input e.ReadDetailDto, dbx ...*gorm.DB) (*e.Regency, error) { + data := e.Regency{} + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if err := tx.First(&data, input.Id).Error; err != nil { + return nil, err + } + + return &data, nil +} + +func UpdateData(input e.Regency, dbx ...*gorm.DB) error { + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + return tx.Save(&input).Error +} + +func DeleteData(input *e.Regency, dbx ...*gorm.DB) error { + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + return tx.Delete(input).Error +} diff --git a/internal/use-case/main-use-case/regency/tycovar.go b/internal/use-case/main-use-case/regency/tycovar.go new file mode 100644 index 00000000..711d0831 --- /dev/null +++ b/internal/use-case/main-use-case/regency/tycovar.go @@ -0,0 +1,32 @@ +/* +DESCRIPTION: +A sample, part of the package that contains type, constants, and/or variables. + +In this sample it also provides type and variable regarding the needs of the +middleware to separate from main use-case which has the basic CRUD +functionality. The purpose of this is to make the code more maintainable. +*/ +package regency + +import ( + "gorm.io/gorm" + + e "simrs-vx/internal/domain/main-entities/regency" +) + +type createMw func(input *e.CreateDto, data *e.Regency, tx *gorm.DB) error +type readListMw func(input *e.ReadListDto, data *e.Regency, tx *gorm.DB) error +type readDetailMw func(input *e.ReadDetailDto, data *e.Regency, tx *gorm.DB) error +type updateMw func(input *e.ReadDetailDto, data *e.Regency, tx *gorm.DB) error +type deleteMw func(input *e.ReadDetailDto, data *e.Regency, tx *gorm.DB) error + +var createPreMw []createMw // preprocess middleware +var createPostMw []createMw // postprocess middleware +var readListPreMw []readListMw // .. +var readListPostMw []readListMw // .. +var readDetailPreMw []readDetailMw +var readDetailPostMw []readDetailMw +var updatePreMw []readDetailMw +var updatePostMw []readDetailMw +var deletePreMw []readDetailMw +var deletePostMw []readDetailMw diff --git a/internal/use-case/main-use-case/unit/case.go b/internal/use-case/main-use-case/unit/case.go index 37082abf..78f86cc5 100644 --- a/internal/use-case/main-use-case/unit/case.go +++ b/internal/use-case/main-use-case/unit/case.go @@ -1,7 +1,6 @@ package unit import ( - "errors" "fmt" e "simrs-vx/internal/domain/main-entities/unit" "strconv" @@ -10,6 +9,7 @@ import ( d "github.com/karincake/dodol" pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" "gorm.io/gorm" ) @@ -214,24 +214,9 @@ func ReadDetail(input e.ReadDetailDto) (*d.Data, error) { pl.SetLogInfo(&event, input, "started", "DBReadDetail") data, err = ReadDetailData(input, tx) - if errors.Is(err, gorm.ErrRecordNotFound) { - event.Status = "failed" - event.ErrInfo = pl.ErrorInfo{ - Code: "data-notFound", - Detail: fmt.Sprintf("Data with ID %v not found", input.Id), - Raw: err, - } - } else { - event.Status = "failed" - event.ErrInfo = pl.ErrorInfo{ - Code: "data-read-detail-fail", - Detail: "Database read detail failed", - Raw: err, - } + if processedErr := pu.HandleReadError(err, &event, source, input.Id, data); processedErr != nil { + return processedErr } - return pl.SetLogError(event, input) - - pl.SetLogInfo(&event, nil, "complete") for i := range readDetailPostMw { mwName := fmt.Sprintf("readDetailPostMw[%d]", i) @@ -264,7 +249,7 @@ func ReadDetail(input e.ReadDetailDto) (*d.Data, error) { "structure": "single-data", "status": "fetched", }, - Data: data.ToResponse(), + Data: pu.SafeToResponse(data), }, nil } @@ -284,14 +269,8 @@ func Update(input e.UpdateDto) (*d.Data, error) { err = dg.I.Transaction(func(tx *gorm.DB) error { pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") data, err = ReadDetailData(rdDto, tx) - if err != nil { - event.Status = "failed" - event.ErrInfo = pl.ErrorInfo{ - Code: "data-read-detail-fail", - Detail: "Database read detail failed", - Raw: err, - } - return pl.SetLogError(event, rdDto) + if processedErr := pu.HandleReadError(err, &event, source, input.Id, data); processedErr != nil { + return processedErr } pl.SetLogInfo(&event, input, "started", "setUpdate") @@ -380,18 +359,10 @@ func Delete(input e.DeleteDto) (*d.Data, error) { err = dg.I.Transaction(func(tx *gorm.DB) error { pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") data, err = ReadDetailData(rdDto, tx) - if err != nil { - event.Status = "failed" - event.ErrInfo = pl.ErrorInfo{ - Code: "data-read-detail-fail", - Detail: "Database read detail failed", - Raw: err, - } - return pl.SetLogError(event, rdDto) + if processedErr := pu.HandleReadError(err, &event, source, input.Id, data); processedErr != nil { + return processedErr } - pl.SetLogInfo(&event, nil, "complete") - for i := range deletePreMw { mwName := fmt.Sprintf("deletePreMw[%d]", i) diff --git a/internal/use-case/main-use-case/user/case.go b/internal/use-case/main-use-case/user/case.go index fbcb65f3..0f901af9 100644 --- a/internal/use-case/main-use-case/user/case.go +++ b/internal/use-case/main-use-case/user/case.go @@ -1,7 +1,6 @@ package user import ( - "errors" "fmt" e "simrs-vx/internal/domain/main-entities/user" "strconv" @@ -10,6 +9,7 @@ import ( d "github.com/karincake/dodol" pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" "gorm.io/gorm" ) @@ -214,24 +214,10 @@ func ReadDetail(input e.ReadDetailDto) (*d.Data, error) { pl.SetLogInfo(&event, input, "started", "DBReadDetail") data, err = ReadDetailData(input, tx) - if errors.Is(err, gorm.ErrRecordNotFound) { - event.Status = "failed" - event.ErrInfo = pl.ErrorInfo{ - Code: "data-notFound", - Detail: fmt.Sprintf("Data with ID %v not found", input.Id), - Raw: err, - } - } else { - event.Status = "failed" - event.ErrInfo = pl.ErrorInfo{ - Code: "data-read-detail-fail", - Detail: "Database read detail failed", - Raw: err, - } + if processedErr := pu.HandleReadError(err, &event, source, input.Id, data); processedErr != nil { + return processedErr } - pl.SetLogInfo(&event, nil, "complete") - for i := range readDetailPostMw { mwName := fmt.Sprintf("readDetailPostMw[%d]", i) @@ -263,7 +249,7 @@ func ReadDetail(input e.ReadDetailDto) (*d.Data, error) { "structure": "single-data", "status": "fetched", }, - Data: data.ToResponse(), + Data: pu.SafeToResponse(data), }, nil } @@ -283,18 +269,10 @@ func Update(input e.UpdateDto) (*d.Data, error) { err = dg.I.Transaction(func(tx *gorm.DB) error { pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") data, err = ReadDetailData(rdDto, tx) - if err != nil { - event.Status = "failed" - event.ErrInfo = pl.ErrorInfo{ - Code: "data-read-detail-fail", - Detail: "Database read detail failed", - Raw: err, - } - return pl.SetLogError(event, rdDto) + if processedErr := pu.HandleReadError(err, &event, source, input.Id, data); processedErr != nil { + return processedErr } - pl.SetLogInfo(&event, nil, "complete") - pl.SetLogInfo(&event, input, "started", "setUpdate") err = setUpdate(input, data) if err != nil { @@ -392,18 +370,10 @@ func Delete(input e.DeleteDto) (*d.Data, error) { err = dg.I.Transaction(func(tx *gorm.DB) error { pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") data, err = ReadDetailData(rdDto, tx) - if err != nil { - event.Status = "failed" - event.ErrInfo = pl.ErrorInfo{ - Code: "data-read-detail-fail", - Detail: "Database read detail failed", - Raw: err, - } - return pl.SetLogError(event, rdDto) + if processedErr := pu.HandleReadError(err, &event, source, input.Id, data); processedErr != nil { + return processedErr } - pl.SetLogInfo(&event, nil, "complete") - for i := range deletePreMw { mwName := fmt.Sprintf("deletePreMw[%d]", i) diff --git a/internal/use-case/main-use-case/village/case.go b/internal/use-case/main-use-case/village/case.go new file mode 100644 index 00000000..dbcc501a --- /dev/null +++ b/internal/use-case/main-use-case/village/case.go @@ -0,0 +1,439 @@ +package village + +import ( + "fmt" + e "simrs-vx/internal/domain/main-entities/village" + "strconv" + + dg "github.com/karincake/apem/db-gorm-pg" + d "github.com/karincake/dodol" + + pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" + + "gorm.io/gorm" +) + +const source = "village" + +func Create(input e.CreateDto) (*d.Data, error) { + data := e.Village{} + + setData(&input, &data) + + event := pl.Event{ + Feature: "Create", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "create") + + err := dg.I.Transaction(func(tx *gorm.DB) error { + for i := range createPreMw { + mwName := fmt.Sprintf("createPreMw[%d]", i) + + pl.SetLogInfo(&event, data, "started", mwName) + + if err := createPreMw[i](&input, &data, tx); err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "MW_PRE_FAILED", // TODO: add to lang json + Detail: fmt.Sprintf("Pre-middleware %s failed", mwName), + Raw: err, + } + return pl.SetLogError(event, data) + } + + pl.SetLogInfo(&event, nil, "complete") + } + + pl.SetLogInfo(&event, data, "started", "DBCreate") + _, err := CreateData(&data, tx) + if err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-create-fail", + Detail: "Database insert failed", + Raw: err, + } + return pl.SetLogError(event, data) + } + + pl.SetLogInfo(&event, nil, "complete") + + for i := range createPostMw { + mwName := fmt.Sprintf("createPostMw[%d]", i) + + pl.SetLogInfo(&event, input, "started", mwName) + if err := createPostMw[i](&input, &data, tx); err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "MW_POST_FAILED", // TODO: add to lang json + Detail: fmt.Sprintf("Post-middleware %s failed", mwName), + Raw: err, + } + return pl.SetLogError(event, data) + } + } + + pl.SetLogInfo(&event, nil, "complete") + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.II{ + "source": source, + "structure": "single-data", + "status": "created", + }, + Data: data.ToResponse(), + }, nil +} + +func ReadList(input e.ReadListDto) (*d.Data, error) { + var data *e.Village + var dataList []e.Village + var metaList *e.MetaDto + var err error + + event := pl.Event{ + Feature: "ReadList", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "readList") + + err = dg.I.Transaction(func(tx *gorm.DB) error { + for i := range readListPreMw { + mwName := fmt.Sprintf("readListPreMw[%d]", i) + + pl.SetLogInfo(&event, input, "started", mwName) + + if err := readListPreMw[i](&input, data, tx); err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "MW_PRE_FAILED", // TODO: add to lang json + Detail: fmt.Sprintf("Pre-middleware %s failed", mwName), + Raw: err, + } + return pl.SetLogError(event, input) + } + + pl.SetLogInfo(&event, nil, "complete") + } + + pl.SetLogInfo(&event, input, "started", "DBReadList") + dataList, metaList, err = ReadListData(input, tx) + if err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-read-list-fail", + Detail: "Database read list failed", + Raw: err, + } + return pl.SetLogError(event, input) + } + + pl.SetLogInfo(&event, nil, "complete") + + for i := range readListPostMw { + mwName := fmt.Sprintf("readListPostMw[%d]", i) + + pl.SetLogInfo(&event, input, "started", mwName) + + if err := readListPostMw[i](&input, data, tx); err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "MW_POST_FAILED", // TODO: add to lang json + Detail: fmt.Sprintf("Post-middleware %s failed", mwName), + Raw: err, + } + return pl.SetLogError(event, input) + } + + pl.SetLogInfo(&event, nil, "complete") + } + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "list-data", + "status": "fetched", + "page_number": strconv.Itoa(metaList.PageNumber), + "page_size": strconv.Itoa(metaList.PageSize), + "record_totalCount": strconv.Itoa(metaList.Count), + }, + Data: e.ToResponseList(dataList), + }, nil +} + +func ReadDetail(input e.ReadDetailDto) (*d.Data, error) { + var data *e.Village + var err error + + event := pl.Event{ + Feature: "ReadDetail", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "readDetail") + + err = dg.I.Transaction(func(tx *gorm.DB) error { + for i := range readDetailPreMw { + mwName := fmt.Sprintf("readDetailPreMw[%d]", i) + + pl.SetLogInfo(&event, input, "started", mwName) + + if err := readDetailPreMw[i](&input, data, tx); err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "MW_PRE_FAILED", // TODO: add to lang json + Detail: fmt.Sprintf("Pre-middleware %s failed", mwName), + Raw: err, + } + return pl.SetLogError(event, input) + } + + pl.SetLogInfo(&event, nil, "complete") + } + + pl.SetLogInfo(&event, input, "started", "DBReadDetail") + data, err = ReadDetailData(input, tx) + if processedErr := pu.HandleReadError(err, &event, source, input.Id, data); processedErr != nil { + return processedErr + } + + for i := range readDetailPostMw { + mwName := fmt.Sprintf("readDetailPostMw[%d]", i) + + pl.SetLogInfo(&event, data, "started", mwName) + + if err := readDetailPostMw[i](&input, data, tx); err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "MW_POST_FAILED", // TODO: add to lang json + Detail: fmt.Sprintf("Post-middleware %s failed", mwName), + Raw: err, + } + return pl.SetLogError(event, data) + } + + pl.SetLogInfo(&event, nil, "complete") + } + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "fetched", + }, + Data: pu.SafeToResponse(data), + }, nil +} + +func Update(input e.UpdateDto) (*d.Data, error) { + rdDto := e.ReadDetailDto{Id: input.Id} + var data *e.Village + var err error + + event := pl.Event{ + Feature: "Update", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "update") + + err = dg.I.Transaction(func(tx *gorm.DB) error { + pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") + data, err = ReadDetailData(rdDto, tx) + if processedErr := pu.HandleReadError(err, &event, source, input.Id, data); processedErr != nil { + return processedErr + } + + pl.SetLogInfo(&event, input, "started", "setUpdate") + setData(&input, data) + + for i := range updatePreMw { + mwName := fmt.Sprintf("updatePreMw[%d]", i) + + pl.SetLogInfo(&event, data, "started", mwName) + + if err := updatePreMw[i](&rdDto, data, tx); err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "MW_PRE_FAILED", // TODO: add to lang json + Detail: fmt.Sprintf("Pre-middleware %s failed", mwName), + Raw: err, + } + return pl.SetLogError(event, data) + } + + pl.SetLogInfo(&event, nil, "complete") + } + + pl.SetLogInfo(&event, data, "started", "DBUpdate") + if err := UpdateData(*data, tx); err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-update-fail", + Detail: "Database update failed", + Raw: err, + } + return pl.SetLogError(event, data) + } + + pl.SetLogInfo(&event, nil, "complete") + + for i := range updatePostMw { + mwName := fmt.Sprintf("updatePostMw[%d]", i) + + pl.SetLogInfo(&event, data, "started", mwName) + + if err := updatePostMw[i](&rdDto, data, tx); err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "MW_POST_FAILED", // TODO: add to lang json + Detail: fmt.Sprintf("Post-middleware %s failed", mwName), + Raw: err, + } + return pl.SetLogError(event, data) + } + + pl.SetLogInfo(&event, nil, "complete") + } + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "updated", + }, + Data: data.ToResponse(), + }, nil + +} + +func Delete(input e.DeleteDto) (*d.Data, error) { + rdDto := e.ReadDetailDto{Id: input.Id} + var data *e.Village + var err error + + event := pl.Event{ + Feature: "Delete", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "delete") + + err = dg.I.Transaction(func(tx *gorm.DB) error { + pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") + data, err = ReadDetailData(rdDto, tx) + if err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-read-detail-fail", + Detail: "Database read detail failed", + Raw: err, + } + return pl.SetLogError(event, rdDto) + } + + pl.SetLogInfo(&event, nil, "complete") + + for i := range deletePreMw { + mwName := fmt.Sprintf("deletePreMw[%d]", i) + + pl.SetLogInfo(&event, data, "started", mwName) + + if err := deletePreMw[i](&rdDto, data, tx); err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "MW_PRE_FAILED", // TODO: add to lang json + Detail: fmt.Sprintf("Pre-middleware %s failed", mwName), + Raw: err, + } + return pl.SetLogError(event, data) + } + + pl.SetLogInfo(&event, nil, "complete") + } + + pl.SetLogInfo(&event, data, "started", "DBDelete") + if err := DeleteData(data, tx); err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-delete-fail", + Detail: "Database delete failed", + Raw: err, + } + return pl.SetLogError(event, data) + } + + pl.SetLogInfo(&event, nil, "complete") + + for i := range deletePostMw { + mwName := fmt.Sprintf("deletePostMw[%d]", i) + + pl.SetLogInfo(&event, data, "started", mwName) + + if err := deletePostMw[i](&rdDto, data, tx); err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "MW_POST_FAILED", // TODO: add to lang json + Detail: fmt.Sprintf("Post-middleware %s failed", mwName), + Raw: err, + } + return pl.SetLogError(event, data) + } + + pl.SetLogInfo(&event, nil, "complete") + } + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "deleted", + }, + Data: data.ToResponse(), + }, nil + +} diff --git a/internal/use-case/main-use-case/village/helper.go b/internal/use-case/main-use-case/village/helper.go new file mode 100644 index 00000000..bc50fd04 --- /dev/null +++ b/internal/use-case/main-use-case/village/helper.go @@ -0,0 +1,22 @@ +/* +DESCRIPTION: +Any functions that are used internally by the use-case +*/ +package village + +import ( + e "simrs-vx/internal/domain/main-entities/village" +) + +func setData[T *e.CreateDto | *e.UpdateDto](input T, data *e.Village) { + var inputSrc *e.CreateDto + if inputT, ok := any(input).(*e.CreateDto); ok { + inputSrc = inputT + } else { + inputTemp := any(input).(*e.UpdateDto) + inputSrc = &inputTemp.CreateDto + } + data.District_Code = inputSrc.District_Code + data.Code = inputSrc.Code + data.Name = inputSrc.Name +} diff --git a/internal/use-case/main-use-case/village/lib.go b/internal/use-case/main-use-case/village/lib.go new file mode 100644 index 00000000..aec8e05a --- /dev/null +++ b/internal/use-case/main-use-case/village/lib.go @@ -0,0 +1,97 @@ +package village + +import ( + e "simrs-vx/internal/domain/main-entities/village" + + dg "github.com/karincake/apem/db-gorm-pg" + gh "github.com/karincake/getuk" + "gorm.io/gorm" +) + +func CreateData(input *e.Village, dbx ...*gorm.DB) (*e.Village, error) { + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if err := tx.Create(&input).Error; err != nil { + return nil, err + } + + return input, nil +} + +func ReadListData(input e.ReadListDto, dbx ...*gorm.DB) ([]e.Village, *e.MetaDto, error) { + data := []e.Village{} + pagination := gh.Pagination{} + count := int64(0) + meta := e.MetaDto{} + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + tx = tx. + Model(&e.Village{}). + // Joins("Patient"). // if needed + // Preload("Patient"). // if needed + Scopes(gh.Filter(input)). + Count(&count). + Scopes(gh.Paginate(input, &pagination)) + + if err := tx.Debug().Find(&data).Error; err != nil { + if err == gorm.ErrRecordNotFound { + return nil, &meta, nil + } + return nil, nil, err + + } + meta.Count = int(count) + meta.PageNumber = pagination.PageNumber + meta.PageSize = pagination.PageSize + return data, &meta, nil +} + +func ReadDetailData(input e.ReadDetailDto, dbx ...*gorm.DB) (*e.Village, error) { + data := e.Village{} + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if err := tx.First(&data, input.Id).Error; err != nil { + return nil, err + } + + return &data, nil +} + +func UpdateData(input e.Village, dbx ...*gorm.DB) error { + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + return tx.Save(&input).Error +} + +func DeleteData(input *e.Village, dbx ...*gorm.DB) error { + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + return tx.Delete(input).Error +} diff --git a/internal/use-case/main-use-case/village/tycovar.go b/internal/use-case/main-use-case/village/tycovar.go new file mode 100644 index 00000000..e1aa31c5 --- /dev/null +++ b/internal/use-case/main-use-case/village/tycovar.go @@ -0,0 +1,32 @@ +/* +DESCRIPTION: +A sample, part of the package that contains type, constants, and/or variables. + +In this sample it also provides type and variable regarding the needs of the +middleware to separate from main use-case which has the basic CRUD +functionality. The purpose of this is to make the code more maintainable. +*/ +package village + +import ( + "gorm.io/gorm" + + e "simrs-vx/internal/domain/main-entities/village" +) + +type createMw func(input *e.CreateDto, data *e.Village, tx *gorm.DB) error +type readListMw func(input *e.ReadListDto, data *e.Village, tx *gorm.DB) error +type readDetailMw func(input *e.ReadDetailDto, data *e.Village, tx *gorm.DB) error +type updateMw func(input *e.ReadDetailDto, data *e.Village, tx *gorm.DB) error +type deleteMw func(input *e.ReadDetailDto, data *e.Village, tx *gorm.DB) error + +var createPreMw []createMw // preprocess middleware +var createPostMw []createMw // postprocess middleware +var readListPreMw []readListMw // .. +var readListPostMw []readListMw // .. +var readDetailPreMw []readDetailMw +var readDetailPostMw []readDetailMw +var updatePreMw []readDetailMw +var updatePostMw []readDetailMw +var deletePreMw []readDetailMw +var deletePostMw []readDetailMw diff --git a/pkg/use-case-helper/use-case-helper.go b/pkg/use-case-helper/use-case-helper.go new file mode 100644 index 00000000..20550048 --- /dev/null +++ b/pkg/use-case-helper/use-case-helper.go @@ -0,0 +1,48 @@ +package usecasehelper + +import ( + "errors" + "fmt" + + pl "simrs-vx/pkg/logger" + + "gorm.io/gorm" +) + +func SafeToResponse[T any](data *T) any { + if data == nil { + return nil + } + + // Use type assertion to call ToResponse if the type has it + if converter, ok := any(data).(interface{ ToResponse() any }); ok { + return converter.ToResponse() + } + + return nil +} + +func HandleReadError(err error, event *pl.Event, itemType string, id interface{}, data any) error { + if err == nil { + pl.SetLogInfo(event, data, "complete") + return nil + } + + if errors.Is(err, gorm.ErrRecordNotFound) { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-notFound", + Detail: fmt.Sprintf("%s with ID %v not found", itemType, id), + Raw: err, + } + } else { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-read-detail-fail", + Detail: fmt.Sprintf("%s read failed", itemType), + Raw: err, + } + } + + return pl.SetLogError(*event, nil) +}