From ee9b4a903594531d1ce43a151a24e8d4d8f8cc34 Mon Sep 17 00:00:00 2001 From: Munawwirul Jamal Date: Mon, 11 Aug 2025 10:25:54 +0700 Subject: [PATCH 1/3] pre-dev: initial commit --- .gitignore | 1 + README-APP.md | 1 + README-DB.md | 37 +++++++++ README.md | 3 +- cmd/.keep | 0 cmd/main-api/config.yml-example | 26 +++++++ cmd/main-api/main.go | 14 ++++ go.mod | 32 ++++++++ go.sum | 78 +++++++++++++++++++ .../domain/_template/child/base/entity.go | 26 +++++++ .../domain/_template/child/base/tycovar.go | 17 ++++ internal/domain/_template/child/entity.go | 28 +++++++ .../domain/_template/child/simple/entity.go | 21 +++++ .../domain/_template/parent/base/entity.go | 26 +++++++ .../domain/_template/parent/base/tycovar.go | 17 ++++ internal/domain/_template/parent/entity.go | 31 ++++++++ .../domain/_template/parent/full/entity.go | 24 ++++++ .../domain/_template/parent/minimal/entity.go | 19 +++++ .../domain/_template/separated/base/entity.go | 26 +++++++ .../domain/_template/separated/base/helper.go | 13 ++++ .../_template/separated/base/tycovar.go | 17 ++++ internal/domain/_template/separated/entity.go | 34 ++++++++ .../_template/separated/minimal/entity.go | 21 +++++ .../_template/separated/simple/entity.go | 22 ++++++ internal/domain/_template/single/dto.go | 26 +++++++ internal/domain/_template/single/entity.go | 25 ++++++ internal/domain/base-entities/.keep | 0 internal/domain/base-entities/actor/entity.go | 11 +++ internal/domain/base-entities/core/entity.go | 31 ++++++++ internal/infra/.keep | 0 internal/interface/.keep | 0 internal/interface/_template/crud/handler.go | 40 ++++++++++ .../_template/invoke-auth/handler.go | 16 ++++ .../interface/_template/non-crud/handler.go | 15 ++++ .../interface/main-handler/home/handler.go | 23 ++++++ .../interface/main-handler/main-handler.go | 25 ++++++ internal/use-case/.keep | 0 internal/use-case/_plugin-template/.keep | 0 .../_plugin-template/modifier/modifire.go | 20 +++++ internal/use-case/_use-case-template/.keep | 0 .../_use-case-template/crud/README.md | 59 ++++++++++++++ .../use-case/_use-case-template/crud/case.go | 55 +++++++++++++ .../_use-case-template/crud/helper.go | 13 ++++ .../use-case/_use-case-template/crud/lib.go | 34 ++++++++ .../_use-case-template/crud/middleware.go | 10 +++ .../_use-case-template/crud/tycovar.go | 32 ++++++++ internal/use-case/main-use-case/.keep | 0 internal/use-case/plugin/.keep | 0 pkg/middleware/.keep | 0 .../handler-logger/handler-logger.go | 52 +++++++++++++ 50 files changed, 1020 insertions(+), 1 deletion(-) create mode 100644 README-APP.md create mode 100644 README-DB.md create mode 100644 cmd/.keep create mode 100644 cmd/main-api/config.yml-example create mode 100644 cmd/main-api/main.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 internal/domain/_template/child/base/entity.go create mode 100644 internal/domain/_template/child/base/tycovar.go create mode 100644 internal/domain/_template/child/entity.go create mode 100644 internal/domain/_template/child/simple/entity.go create mode 100644 internal/domain/_template/parent/base/entity.go create mode 100644 internal/domain/_template/parent/base/tycovar.go create mode 100644 internal/domain/_template/parent/entity.go create mode 100644 internal/domain/_template/parent/full/entity.go create mode 100644 internal/domain/_template/parent/minimal/entity.go create mode 100644 internal/domain/_template/separated/base/entity.go create mode 100644 internal/domain/_template/separated/base/helper.go create mode 100644 internal/domain/_template/separated/base/tycovar.go create mode 100644 internal/domain/_template/separated/entity.go create mode 100644 internal/domain/_template/separated/minimal/entity.go create mode 100644 internal/domain/_template/separated/simple/entity.go create mode 100644 internal/domain/_template/single/dto.go create mode 100644 internal/domain/_template/single/entity.go create mode 100644 internal/domain/base-entities/.keep create mode 100644 internal/domain/base-entities/actor/entity.go create mode 100644 internal/domain/base-entities/core/entity.go create mode 100644 internal/infra/.keep create mode 100644 internal/interface/.keep create mode 100644 internal/interface/_template/crud/handler.go create mode 100644 internal/interface/_template/invoke-auth/handler.go create mode 100644 internal/interface/_template/non-crud/handler.go create mode 100644 internal/interface/main-handler/home/handler.go create mode 100644 internal/interface/main-handler/main-handler.go create mode 100644 internal/use-case/.keep create mode 100644 internal/use-case/_plugin-template/.keep create mode 100644 internal/use-case/_plugin-template/modifier/modifire.go create mode 100644 internal/use-case/_use-case-template/.keep create mode 100644 internal/use-case/_use-case-template/crud/README.md create mode 100644 internal/use-case/_use-case-template/crud/case.go create mode 100644 internal/use-case/_use-case-template/crud/helper.go create mode 100644 internal/use-case/_use-case-template/crud/lib.go create mode 100644 internal/use-case/_use-case-template/crud/middleware.go create mode 100644 internal/use-case/_use-case-template/crud/tycovar.go create mode 100644 internal/use-case/main-use-case/.keep create mode 100644 internal/use-case/plugin/.keep create mode 100644 pkg/middleware/.keep create mode 100644 pkg/middleware/handler-logger/handler-logger.go diff --git a/.gitignore b/.gitignore index aaadf736..1fe7cee2 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,7 @@ go.work.sum # env file .env +config.yml # Editor/IDE # .idea/ diff --git a/README-APP.md b/README-APP.md new file mode 100644 index 00000000..a31fed54 --- /dev/null +++ b/README-APP.md @@ -0,0 +1 @@ +# APP DESIGN CONVENTION \ No newline at end of file diff --git a/README-DB.md b/README-DB.md new file mode 100644 index 00000000..c361ca31 --- /dev/null +++ b/README-DB.md @@ -0,0 +1,37 @@ +# DATABASE MODELING CONVETION + +## Naming +1. Table names are written in combination, with the following rules: + 1. Basically uses `PascalCase` + 2. An underscore is used in `derived-table`, separating the original name and the derived name. + 3. A derived table is a table derived from another table but not used in the main flow of the business process. + 4. Examples of derived table implementations include: + 1. Archiving, for example: `Transaction_2019`, is an archive of the `Transaction` table for the year `2019`. + 2. Trashing for deleted data, for example: `Transaction_deleted`, stores data from the deleted `transaction` table. + 3. History for storing historical information, for example: `ProductStock_History`, stores historical data from the `ProductStock` table. +2. Columns are written in combination, with the following rules: + 1. Basically, uses `PascalCase` + 2. Use underscores to represent relationships `object`-`attribute` or `table`-`column` +
Example: + 1. `User_Id` represents the `User` object and the `Id` attribute + 2. `BestProduct_Code` represents the `BestProduct` object and the `Code` attribute + 3. When more than one column has a similar definition, a `prefix` can be added to indicate the column's purpose. +
Example: + 1. `Admin_User_Id` represents the `Admin` of the `User` object and the `Id` attribute + 2. `Operator_User_Id` refers to the `Operator` of the `User` object and the `Id` column. +3. Attributes/properties in JSON (for output purpose) are written in combination, with the following rules: + 1. Basically uses `camelCase` + 2. Using underscores to represent relationships `object`-`attribute` or `table`-`column` +
Example: + 1. `user_id` represents the `User` object and the `Id` attribute. + 2. `bestProduct_code` represents the `bestProduct` object and the `code` attribute. + 3. When more than one column has a similar definition, a `prefix` can be added to indicate the column's purpose. +
Example: + 1. `admin_user_id` refers to the `admin` of the `user` object and `id` attribute + 2. `operator_user_id` refers to the `operator` of the `user` object, column `id` + 3. `newArrival_bestProduct_code` refers to the `newArrival` of the `bestProduct` object, column `code` + 4. `favorite_bestProduct_code` refers to the `favorite` of the `bestProduct` object, column `code` + +## Avoiding Circular Dependency +1. Prioritize the child due to it's nature to define the foreign key. This means the child is allowed to import main entities that define the table, not the base. +2. Parent imports the child's non main entities, can be the base or others depending on the needs. diff --git a/README.md b/README.md index 3bd78fd3..a186c2f0 100644 --- a/README.md +++ b/README.md @@ -1 +1,2 @@ -# simrs-be \ No newline at end of file +# SIMRS-VX + diff --git a/cmd/.keep b/cmd/.keep new file mode 100644 index 00000000..e69de29b diff --git a/cmd/main-api/config.yml-example b/cmd/main-api/config.yml-example new file mode 100644 index 00000000..6bed47f9 --- /dev/null +++ b/cmd/main-api/config.yml-example @@ -0,0 +1,26 @@ +appCfg: + fullName: BPJS Bridge + codeName: simrs-vx + version: 0.1.0 + env: development + lang: en + +httpCfg: + host: + port: + +loggerCfg: + hideTime: + hideLevel: + +msCfg: + dsn: + +langCfg: + active: + path: + fileName: + +corsCfg: + allowedOrigin: + allowedMethod: \ No newline at end of file diff --git a/cmd/main-api/main.go b/cmd/main-api/main.go new file mode 100644 index 00000000..74be3afd --- /dev/null +++ b/cmd/main-api/main.go @@ -0,0 +1,14 @@ +package main + +import ( + a "github.com/karincake/apem" + d "github.com/karincake/apem/db-gorm-pg" + l "github.com/karincake/apem/logger-zerolog" + m "github.com/karincake/apem/ms-redis" + + h "simrs-vx/internal/interface/main-handler" +) + +func main() { + a.Run(h.SetRoutes(), &l.O, &m.O, &d.O) +} diff --git a/go.mod b/go.mod new file mode 100644 index 00000000..535bb824 --- /dev/null +++ b/go.mod @@ -0,0 +1,32 @@ +module simrs-vx + +go 1.23.0 + +toolchain go1.23.11 + +require ( + github.com/karincake/apem v0.0.16-e + github.com/karincake/dodol v0.0.1 + gorm.io/gorm v1.25.10 +) + +require ( + github.com/go-redis/redis v6.15.9+incompatible // indirect + github.com/google/go-cmp v0.7.0 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect + github.com/jackc/pgx/v5 v5.4.3 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect + github.com/nxadm/tail v1.4.11 // indirect + github.com/rs/zerolog v1.33.0 // indirect + golang.org/x/crypto v0.40.0 // indirect + golang.org/x/net v0.42.0 // indirect + golang.org/x/sys v0.34.0 // indirect + golang.org/x/text v0.27.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + gorm.io/driver/postgres v1.5.7 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 00000000..79fad110 --- /dev/null +++ b/go.sum @@ -0,0 +1,78 @@ +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg= +github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.4.3 h1:cxFyXhxlvAifxnkKKdlxv8XqUf59tDlYjnV5YYfsJJY= +github.com/jackc/pgx/v5 v5.4.3/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSljmHRA= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/karincake/apem v0.0.16-e h1:KGeQYmgNw8luidSWkZyGcMbW1PP3KMRCHMTEHQ7twU8= +github.com/karincake/apem v0.0.16-e/go.mod h1:cQP2sJfDrLRIiwWoaLWw/z8uAya+DWu/FpmYeinMQXM= +github.com/karincake/dodol v0.0.1 h1:jUXmJh1r0Ei4fmHPZ6IUkoplW/V9d27L63JEl6zudL0= +github.com/karincake/dodol v0.0.1/go.mod h1:2f1NcvkvY0J3GMUkwILNDYVvRUpz0W3lpPp/Ha/Ld24= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY= +github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk= +github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= +github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= +golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= +golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= +golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= +golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= +golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/driver/postgres v1.5.7 h1:8ptbNJTDbEmhdr62uReG5BGkdQyeasu/FZHxI0IMGnM= +gorm.io/driver/postgres v1.5.7/go.mod h1:3e019WlBaYI5o5LIdNV+LyxCMNtLOQETBXL2h4chKpA= +gorm.io/gorm v1.25.10 h1:dQpO+33KalOA+aFYGlK+EfxcI5MbO7EP2yYygwh9h+s= +gorm.io/gorm v1.25.10/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= diff --git a/internal/domain/_template/child/base/entity.go b/internal/domain/_template/child/base/entity.go new file mode 100644 index 00000000..62d55346 --- /dev/null +++ b/internal/domain/_template/child/base/entity.go @@ -0,0 +1,26 @@ +/* +DESCRIPTION: +Sample of base-entity, results of the extraction of the attributes from the +original entity. + +NOTE: +Make sure to use 'case-sensitive' option when searching + +TODO: +create several types according to the needs +*/ +package base // adjust this + +type Basic struct { + Code string `json:"code" gorm:"uniqueIndex;not null;size:10"` + Name string `json:"name" gorm:"not null;size:100"` +} + +type Default struct { + Type_Code TypeCode `json:"type" gorm:"not null;size:10"` + Status_Code StatusCode `json:"status" gorm:"not null;size:10"` +} + +type Extra struct { + Description string `json:"description"` +} diff --git a/internal/domain/_template/child/base/tycovar.go b/internal/domain/_template/child/base/tycovar.go new file mode 100644 index 00000000..7665aa9a --- /dev/null +++ b/internal/domain/_template/child/base/tycovar.go @@ -0,0 +1,17 @@ +/* +DESCRIPTION: +A sample, part of the package that contains type, constants, and/or variables. +*/ +package base // adjust this + +type StatusCode string +type TypeCode string + +const ( + SCActive StatusCode = "active" // prefixed with SC that stands for StatusCode + SCInactive StatusCode = "inactive" + + TCPrimary TypeCode = "prim" // prefixed with TC that stands for TypeCode + TCScondary TypeCode = "seco" + TCTertiary TypeCode = "tert" +) diff --git a/internal/domain/_template/child/entity.go b/internal/domain/_template/child/entity.go new file mode 100644 index 00000000..7d3e0109 --- /dev/null +++ b/internal/domain/_template/child/entity.go @@ -0,0 +1,28 @@ +/* +DESCRIPTION: +Sample of struct to define an entity both in the application and database. +Uses the base struct from the core package for the standard attributes. + +The sampel is part of the the workaround sample on how to avoid the +circle-depency. The child is used to define the relationship for the database. + +TODO: +- replace 'child' with the name of the package, ie: myentity +- replace 'Child' with the name of the entity, ie: MyEntity +*/ +package child + +import ( + ecore "simrs-vx/internal/domain/base-entities/core" + eb "simrs-vx/internal/domain/main-entities/child/base" + ep "simrs-vx/internal/domain/main-entities/parent" +) + +type Child struct { + ecore.Main + eb.Basic + Parent_Id int `json:"parent_id"` + Pareng ep.Parent `json:"parent" gorm:"foreignKey:Parent_Id;references:Id"` + eb.Default + eb.Extra +} diff --git a/internal/domain/_template/child/simple/entity.go b/internal/domain/_template/child/simple/entity.go new file mode 100644 index 00000000..dbdd1169 --- /dev/null +++ b/internal/domain/_template/child/simple/entity.go @@ -0,0 +1,21 @@ +/* +DESCRIPTION: +Sample of a case-entity that utilized the base according to the needs. + +NOTE: +Make sure to use 'case-sensitive' option when searching + +TODO: +replace 'child' with the name of the package, ie: myentity +replace 'Child' with the name of the entity, ie: MyEntity +*/ +package child + +import ( + eb "simrs-vx/internal/domain/main-entities/child/base" +) + +type Child struct { + Id int `json:"id"` // in many cases the usage of + eb.Basic +} diff --git a/internal/domain/_template/parent/base/entity.go b/internal/domain/_template/parent/base/entity.go new file mode 100644 index 00000000..62d55346 --- /dev/null +++ b/internal/domain/_template/parent/base/entity.go @@ -0,0 +1,26 @@ +/* +DESCRIPTION: +Sample of base-entity, results of the extraction of the attributes from the +original entity. + +NOTE: +Make sure to use 'case-sensitive' option when searching + +TODO: +create several types according to the needs +*/ +package base // adjust this + +type Basic struct { + Code string `json:"code" gorm:"uniqueIndex;not null;size:10"` + Name string `json:"name" gorm:"not null;size:100"` +} + +type Default struct { + Type_Code TypeCode `json:"type" gorm:"not null;size:10"` + Status_Code StatusCode `json:"status" gorm:"not null;size:10"` +} + +type Extra struct { + Description string `json:"description"` +} diff --git a/internal/domain/_template/parent/base/tycovar.go b/internal/domain/_template/parent/base/tycovar.go new file mode 100644 index 00000000..7665aa9a --- /dev/null +++ b/internal/domain/_template/parent/base/tycovar.go @@ -0,0 +1,17 @@ +/* +DESCRIPTION: +A sample, part of the package that contains type, constants, and/or variables. +*/ +package base // adjust this + +type StatusCode string +type TypeCode string + +const ( + SCActive StatusCode = "active" // prefixed with SC that stands for StatusCode + SCInactive StatusCode = "inactive" + + TCPrimary TypeCode = "prim" // prefixed with TC that stands for TypeCode + TCScondary TypeCode = "seco" + TCTertiary TypeCode = "tert" +) diff --git a/internal/domain/_template/parent/entity.go b/internal/domain/_template/parent/entity.go new file mode 100644 index 00000000..2983af6f --- /dev/null +++ b/internal/domain/_template/parent/entity.go @@ -0,0 +1,31 @@ +/* +DESCRIPTION: +Sample of struct to define an entity both in the application and database. +Uses the base struct from the core package for the standard attributes. + +The sampel shows a workaround on how to avoid the circle-depency which is a +common problem when it involves the parent-child relationship. By default, +the relationship is defined in the child entity, and when the parent entity +needs to use the child as an attribute (array for example), the circle-depency +happens. + +To avoid this problem, the parent will have a case-entity like 'full' entity, +which embeds the 'base' and the 'child' structs. + +TODO: +- replace 'parent' with the name of the package, ie: myentity +- replace 'Parent' with the name of the entity, ie: MyEntity +*/ +package parent + +import ( + ecore "simrs-vx/internal/domain/base-entities/core" + eb "simrs-vx/internal/domain/main-entities/parent/base" +) + +type Parent struct { + ecore.Main + eb.Basic + eb.Default + eb.Extra +} diff --git a/internal/domain/_template/parent/full/entity.go b/internal/domain/_template/parent/full/entity.go new file mode 100644 index 00000000..ccbae089 --- /dev/null +++ b/internal/domain/_template/parent/full/entity.go @@ -0,0 +1,24 @@ +/* +DESCRIPTION: +Sample of a case-entity that utilized the base according to the needs. In this +sample, it imports the child entity, and uses it as an attribute. And by +separating this entity from the original entity, it avoids the circle dependency. + +TODO: +replace 'parent' with the name of the package, ie: patient +replace 'Parent' with the name of the entity, ie: Patient +*/ +package parent + +import ( + ec "simrs-vx/internal/domain/main-entities/child" + eb "simrs-vx/internal/domain/main-entities/parent/base" + // ec "simrs-vx/internal/domain/main-entities/child/minimal" // can use other case +) + +type Parent struct { + Id int `json:"id"` + eb.Basic + eb.Default + Childs []ec.Child `json:"childs" gorm:"foreignKey:Parent_Id;references:Id"` +} diff --git a/internal/domain/_template/parent/minimal/entity.go b/internal/domain/_template/parent/minimal/entity.go new file mode 100644 index 00000000..9ea7630e --- /dev/null +++ b/internal/domain/_template/parent/minimal/entity.go @@ -0,0 +1,19 @@ +/* +DESCRIPTION: +Sample of a case-entity that utilized the base according to the needs. + +TODO: +replace 'parent' with the name of the package, ie: patient +replace 'Parent' with the name of the entity, ie: Patient +*/ +package parent + +import ( + eb "simrs-vx/internal/domain/main-entities/parent/base" +) + +type Parent struct { + Id int `json:"id"` + eb.Basic + eb.Default +} diff --git a/internal/domain/_template/separated/base/entity.go b/internal/domain/_template/separated/base/entity.go new file mode 100644 index 00000000..62d55346 --- /dev/null +++ b/internal/domain/_template/separated/base/entity.go @@ -0,0 +1,26 @@ +/* +DESCRIPTION: +Sample of base-entity, results of the extraction of the attributes from the +original entity. + +NOTE: +Make sure to use 'case-sensitive' option when searching + +TODO: +create several types according to the needs +*/ +package base // adjust this + +type Basic struct { + Code string `json:"code" gorm:"uniqueIndex;not null;size:10"` + Name string `json:"name" gorm:"not null;size:100"` +} + +type Default struct { + Type_Code TypeCode `json:"type" gorm:"not null;size:10"` + Status_Code StatusCode `json:"status" gorm:"not null;size:10"` +} + +type Extra struct { + Description string `json:"description"` +} diff --git a/internal/domain/_template/separated/base/helper.go b/internal/domain/_template/separated/base/helper.go new file mode 100644 index 00000000..9a910db3 --- /dev/null +++ b/internal/domain/_template/separated/base/helper.go @@ -0,0 +1,13 @@ +/* +DESCRIPTION: +A sample, part of the package that contains functions. +*/ +package base + +import ( + "strings" +) + +func (b *Basic) ExtractName() []string { + return strings.Split(b.Name, " ") +} diff --git a/internal/domain/_template/separated/base/tycovar.go b/internal/domain/_template/separated/base/tycovar.go new file mode 100644 index 00000000..7665aa9a --- /dev/null +++ b/internal/domain/_template/separated/base/tycovar.go @@ -0,0 +1,17 @@ +/* +DESCRIPTION: +A sample, part of the package that contains type, constants, and/or variables. +*/ +package base // adjust this + +type StatusCode string +type TypeCode string + +const ( + SCActive StatusCode = "active" // prefixed with SC that stands for StatusCode + SCInactive StatusCode = "inactive" + + TCPrimary TypeCode = "prim" // prefixed with TC that stands for TypeCode + TCScondary TypeCode = "seco" + TCTertiary TypeCode = "tert" +) diff --git a/internal/domain/_template/separated/entity.go b/internal/domain/_template/separated/entity.go new file mode 100644 index 00000000..dfcc3726 --- /dev/null +++ b/internal/domain/_template/separated/entity.go @@ -0,0 +1,34 @@ +/* +DESCRIPTION: +Sample of struct to define an entity both in the application and database. +Uses the base struct from the core package for the standard attributes. + +The original attributes are extracted and grouped into several parts (structs), +which later embeded. The extracted attributes are stored into the subdirectory +'base'. + +The extraction is done to cover several cases regarding the eficiency the of +the data retreiving by using only needed attributes. Each case has its own +directory with the same struct name with the main entity that embeds the +needed attributes from the 'base'. + +NOTES: +Make sure to use 'case-sensitive' option when doing search-replace + +TODO: +- replace 'separated' with the name of the package, ie: myentity +- replace 'Separated' with the name of the entity, ie: MyEntity +*/ +package separated + +import ( + ecore "simrs-vx/internal/domain/base-entities/core" + eb "simrs-vx/internal/domain/main-entities/separated/base" +) + +type Separated struct { + ecore.Main // adjust this according to the needs + eb.Basic + eb.Default + eb.Extra +} diff --git a/internal/domain/_template/separated/minimal/entity.go b/internal/domain/_template/separated/minimal/entity.go new file mode 100644 index 00000000..b4a7c45a --- /dev/null +++ b/internal/domain/_template/separated/minimal/entity.go @@ -0,0 +1,21 @@ +/* +DESCRIPTION: +Sample of a case-entity that utilized the base according to the needs. + +NOTE: +Make sure to use 'case-sensitive' option when searching + +TODO: +replace 'separated' with the name of the package, ie: myentity +replace 'Separated' with the name of the entity, ie: MyEntity +*/ +package separated + +import ( + eb "simrs-vx/internal/domain/main-entities/separated/base" +) + +type Separated struct { + Id int `json:"id"` + eb.Basic +} diff --git a/internal/domain/_template/separated/simple/entity.go b/internal/domain/_template/separated/simple/entity.go new file mode 100644 index 00000000..d9b74b23 --- /dev/null +++ b/internal/domain/_template/separated/simple/entity.go @@ -0,0 +1,22 @@ +/* +DESCRIPTION: +Sample of a case-entity that utilized the base according to the needs. + +NOTE: +Make sure to use 'case-sensitive' option when searching + +TODO: +replace 'separated' with the name of the package, ie: myentity +replace 'Separated' with the name of the entity, ie: MyEntity +*/ +package separated + +import ( + eb "simrs-vx/internal/domain/main-entities/separated/base" +) + +type Separated struct { + Id int `json:"id"` // in many cases the usage of + eb.Basic + eb.Default +} diff --git a/internal/domain/_template/single/dto.go b/internal/domain/_template/single/dto.go new file mode 100644 index 00000000..ba3e602b --- /dev/null +++ b/internal/domain/_template/single/dto.go @@ -0,0 +1,26 @@ +package single + +type Createdto struct { + Code string `json:"code"` + Name string `json:"name"` + Description string `json:"description"` +} + +type ReadListDto struct { + Code string `json:"code"` + Name string `json:"name"` +} + +type ReadDetailDto struct { + Code string `json:"code"` + Name string `json:"name"` +} + +type Updatedto struct { + Id uint `json:"id"` + Createdto +} + +type Deletedto struct { + Id uint `json:"id"` +} diff --git a/internal/domain/_template/single/entity.go b/internal/domain/_template/single/entity.go new file mode 100644 index 00000000..0b9d4d79 --- /dev/null +++ b/internal/domain/_template/single/entity.go @@ -0,0 +1,25 @@ +/* +DESCRIPTION: +Sample of struct to define an entity both in the application and database. +Uses the base struct from the core package for the standard +attributes. + +NOTES: +Make sure to use 'case-sensitive' option when doing search-replace + +TODO: +- replace 'single' with the name of the package, ie: 'patient' +- replace 'Single' with the name of the entity, ie: 'Patient' +*/ +package single + +import ( + ecore "simrs-vx/internal/domain/base-entities/core" +) + +type Single struct { + ecore.Main // adjust this according to the needs + Code string `json:"code" gorm:"uniqueIndex;not null;size:10"` + Name string `json:"name" gorm:"not null;size:100"` + Description string `json:"description"` +} diff --git a/internal/domain/base-entities/.keep b/internal/domain/base-entities/.keep new file mode 100644 index 00000000..e69de29b diff --git a/internal/domain/base-entities/actor/entity.go b/internal/domain/base-entities/actor/entity.go new file mode 100644 index 00000000..ce538bb1 --- /dev/null +++ b/internal/domain/base-entities/actor/entity.go @@ -0,0 +1,11 @@ +package actor + +type Actor struct { + CreatedBy_User_Id uint `json:"createdBy_user_id"` + UpdatedBy_User_Id uint `json:"updatedBy_user_id"` + DeletedBy_User_Id uint `json:"deletedBy_user_id"` +} + +type Owner struct { + OwnedBy_User_Id uint `json:"ownedBy_user_id"` +} diff --git a/internal/domain/base-entities/core/entity.go b/internal/domain/base-entities/core/entity.go new file mode 100644 index 00000000..6a88e7af --- /dev/null +++ b/internal/domain/base-entities/core/entity.go @@ -0,0 +1,31 @@ +package core + +import "gorm.io/gorm" + +type Base struct { + CreatedAt string `json:"createdAt"` + UpdatedAt string `json:"updatedAt"` + DeteledAt gorm.DeletedAt `json:"deletedAt,omitempty"` +} + +// +type Main struct { + Id uint `json:"id" gorm:"primaryKey"` // depends on the system architecture + Base +} + +type TinyMain struct { + Id uint8 `json:"id" gorm:"primaryKey"` + Base +} + +type SmallMain struct { + Id uint16 `json:"id" gorm:"primaryKey"` + Base +} + +// Mainly for transaction +type BigMain struct { + Id uint64 `json:"id" gorm:"primaryKey"` + Base +} diff --git a/internal/infra/.keep b/internal/infra/.keep new file mode 100644 index 00000000..e69de29b diff --git a/internal/interface/.keep b/internal/interface/.keep new file mode 100644 index 00000000..e69de29b diff --git a/internal/interface/_template/crud/handler.go b/internal/interface/_template/crud/handler.go new file mode 100644 index 00000000..5ee7c413 --- /dev/null +++ b/internal/interface/_template/crud/handler.go @@ -0,0 +1,40 @@ +/* +DESCRIPTION: +Sample of the use-case that has fully CRUD functionality. Uses object with CRUD +functions to satisfy CRUD interface so it will be able to be generated into +CRUD routing. + +Functions other than CRUD can be writen outside the object. +*/ +package crud + +import ( + "net/http" +) + +type myBase struct{} + +var O myBase + +func (obj myBase) Create(w http.ResponseWriter, r *http.Request) { + // MULAI MEETING E + // JIKA SUATU SAAT MAU OUTPUT XML, HANDLER YG MELAKUKAN + // BUKAN CASE + // YES MIRIP HANDLER + // MEETING E MULAI +} + +func (obj myBase) Read(w http.ResponseWriter, r *http.Request) { +} + +func (obj myBase) Update(w http.ResponseWriter, r *http.Request) { +} + +func (obj myBase) Delete(w http.ResponseWriter, r *http.Request) { +} + +func OtherFunc(w http.ResponseWriter, r *http.Request) { +} + +func AnotherFunc(w http.ResponseWriter, r *http.Request) { +} diff --git a/internal/interface/_template/invoke-auth/handler.go b/internal/interface/_template/invoke-auth/handler.go new file mode 100644 index 00000000..87462bd4 --- /dev/null +++ b/internal/interface/_template/invoke-auth/handler.go @@ -0,0 +1,16 @@ +/* +DESCRIPTION: +Sample of a use-case that has fully CRUD functionality. User Id and the other +things related to credentials are passed within the dto. +*/ +package invokeauth + +import ( + "net/http" +) + +func SomeFunc(w http.ResponseWriter, r *http.Request) { + // if dto.User_Id > 0 { + // query = query.Where("User_Id = ?", dto.User_Id) + // } +} diff --git a/internal/interface/_template/non-crud/handler.go b/internal/interface/_template/non-crud/handler.go new file mode 100644 index 00000000..a98a807c --- /dev/null +++ b/internal/interface/_template/non-crud/handler.go @@ -0,0 +1,15 @@ +package crud + +import ( + "net/http" +) + +type myBase struct{} + +var O myBase + +func SomeFunc(w http.ResponseWriter, r *http.Request) { +} + +func Read(w http.ResponseWriter, r *http.Request) { +} diff --git a/internal/interface/main-handler/home/handler.go b/internal/interface/main-handler/home/handler.go new file mode 100644 index 00000000..ad0461da --- /dev/null +++ b/internal/interface/main-handler/home/handler.go @@ -0,0 +1,23 @@ +package home + +import ( + "encoding/json" + "net/http" +) + +func Home(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/" { + jsonText, _ := json.Marshal(map[string]string{"message": "Resource can not be found!!"}) + w.WriteHeader(http.StatusNotFound) + w.Write(jsonText) + return + } + + jsonText, _ := json.Marshal(map[string]string{"message": "Welcome to the API"}) + w.Write(jsonText) +} + +func TempResponse(w http.ResponseWriter, r *http.Request) { + jsonText, _ := json.Marshal(map[string]string{"message": "Nice move by the programmer, now tell him that he forgot this part"}) + w.Write(jsonText) +} diff --git a/internal/interface/main-handler/main-handler.go b/internal/interface/main-handler/main-handler.go new file mode 100644 index 00000000..b959178b --- /dev/null +++ b/internal/interface/main-handler/main-handler.go @@ -0,0 +1,25 @@ +package handler + +import ( + "net/http" + + ///// PKG + handlerlogger "simrs-vx/pkg/middleware/handler-logger" + + ///// Internal + "simrs-vx/internal/interface/main-handler/home" +) + +// One place route to relatively easier to manage, ESPECIALLY in tracking +func SetRoutes() http.Handler { + ///// + // a.RegisterExtCall(gs.Adjust) + + r := http.NewServeMux() + + /******************** Main ********************/ + r.HandleFunc("/", home.Home) + + ///// + return handlerlogger.SetLog(r) +} diff --git a/internal/use-case/.keep b/internal/use-case/.keep new file mode 100644 index 00000000..e69de29b diff --git a/internal/use-case/_plugin-template/.keep b/internal/use-case/_plugin-template/.keep new file mode 100644 index 00000000..e69de29b diff --git a/internal/use-case/_plugin-template/modifier/modifire.go b/internal/use-case/_plugin-template/modifier/modifire.go new file mode 100644 index 00000000..c6696963 --- /dev/null +++ b/internal/use-case/_plugin-template/modifier/modifire.go @@ -0,0 +1,20 @@ +package modifier + +import ( + "gorm.io/gorm" + + e "simrs-vx/internal/domain/main-entities/single" +) + +// a sampel of modifying some input +func ModifInput(input *e.Createdto, data *e.Single, tx *gorm.DB) error { + input.Name = "Prefix_" + input.Name + return nil +} + +// a sampel of utilizing transaction +func CheckData(input *e.Createdto, data *e.Single, tx *gorm.DB) error { + tx.Where("name = ?", input.Name) + input.Name = "Prefix_" + input.Name + return nil +} diff --git a/internal/use-case/_use-case-template/.keep b/internal/use-case/_use-case-template/.keep new file mode 100644 index 00000000..e69de29b diff --git a/internal/use-case/_use-case-template/crud/README.md b/internal/use-case/_use-case-template/crud/README.md new file mode 100644 index 00000000..27825b2e --- /dev/null +++ b/internal/use-case/_use-case-template/crud/README.md @@ -0,0 +1,59 @@ +# Use-Case +Standard pattern to be used to maintain the code scalability by providing +middleware-based flow. Each function can have its own middlewares for +pre- and post-processing. + +According to the pattern above all of the case-functions runs only basic +rdbms operations, like getting data or writing data. And it's all based +on the data structure. The middlewares are used to for additional actions. + +## How It's Done +The use-case is divided into at least 2 parts: +1. Case (`case.go`), where the `case` functions (the main functions) are stored +2. Tycovar (`tycovar.go`), where package scoped types, const, and variables + are stored + +Optionally, there can be other parts, such as: +1. Helper (`helper.go`), where internal functions are stored +2. Middleware (`middleware.go`), where the pre- and post-processing functions + are stored + +Then proceed the following steps (example): +1. Inside the `tycovar.go`, define the middleware types for each case + functions. Make sure to: + 1. Include dto, table definition, and transaction for the parameters + since the middlewares are to utilize or modifies one ot the parameters. + 2. Make all of the parameter pointer for easier handling. + 3. Return `error` for blocking purpose when error occurs. + ```go + type createMw func(input *e.Createdto, tx *gorm.DB) error + . + . + ``` +2. Inside the `tycovar.go`, create variables in form of array to make it + possible to have more than one middleware. There are 2 modes (pre- and + post-processing) for each case functions: + + ```go + var createPreMw []createMw + var createPostMw []createMw + . + . + ``` +3. Inside the `middleware.go`, create the necessary middlewares + ```go + func createPreMwAddNamePrefix(input *e.Createdto, tx *gorm.DB) error { + input.Name = "Prefix_" + input.Name + return nil + } + ``` +4. Inside the `middleware.go`, append the middlewares during initialization. + The order of the does not really is important, except there are steps that + are dependent on each other. + ```go + func init() { + createPreMw = append(createPreMw, createPreMwAddNamePrefix) + } + ``` +5. Loop through the middleware variables and call each middleware. Since there + are 2 modes (pre- and post-processing), the order of the calls is important. diff --git a/internal/use-case/_use-case-template/crud/case.go b/internal/use-case/_use-case-template/crud/case.go new file mode 100644 index 00000000..1c0e3aff --- /dev/null +++ b/internal/use-case/_use-case-template/crud/case.go @@ -0,0 +1,55 @@ +/* +DESCRIPTION: +Any functions that are available to be used externally. +*/ +package crud + +import ( + dg "github.com/karincake/apem/db-gorm-pg" + d "github.com/karincake/dodol" + + e "simrs-vx/internal/domain/main-entities/single" +) + +const source = "crud" + +func Create(input e.Createdto) (*d.Data, error) { + result, err := CreateData(input) + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.II{ + "source": source, + "type": "list", + "status": "created", + }, + Data: result, + }, nil +} + +func ReadList(input e.ReadListDto) (*d.Data, error) { + data := d.Data{} + query := dg.I + query.Find(&data) + if err := query.Error; err != nil { + return nil, err + } + return &d.Data{ + Meta: d.II{}, + Data: data, + }, nil +} + +func ReadDetail() { + +} + +func Update() { + +} + +func Delete() { + +} diff --git a/internal/use-case/_use-case-template/crud/helper.go b/internal/use-case/_use-case-template/crud/helper.go new file mode 100644 index 00000000..0d09eb8f --- /dev/null +++ b/internal/use-case/_use-case-template/crud/helper.go @@ -0,0 +1,13 @@ +/* +DESCRIPTION: +Any functions that are used internally by the use-case +*/ +package crud + +import ( + e "simrs-vx/internal/domain/main-entities/single" +) + +func setData(src e.Createdto, dst *e.Single) { + +} diff --git a/internal/use-case/_use-case-template/crud/lib.go b/internal/use-case/_use-case-template/crud/lib.go new file mode 100644 index 00000000..0c774e0b --- /dev/null +++ b/internal/use-case/_use-case-template/crud/lib.go @@ -0,0 +1,34 @@ +package crud + +import ( + e "simrs-vx/internal/domain/main-entities/single" + + dg "github.com/karincake/apem/db-gorm-pg" + "gorm.io/gorm" +) + +func CreateData(input e.Createdto) (*e.Single, error) { + data := e.Single{} + + err := dg.I.Transaction(func(tx *gorm.DB) error { + for i := range createPreMw { + if err := createPreMw[i](&input, &data, dg.I); err != nil { + return nil + } + } + + if err := dg.I.Create(&data).Error; err != nil { + return nil + } + + for i := range createPostMw { + if err := createPostMw[i](&input, &data, dg.I); err != nil { + return nil + } + } + + return nil + }) + + return &data, err +} diff --git a/internal/use-case/_use-case-template/crud/middleware.go b/internal/use-case/_use-case-template/crud/middleware.go new file mode 100644 index 00000000..6ca644fd --- /dev/null +++ b/internal/use-case/_use-case-template/crud/middleware.go @@ -0,0 +1,10 @@ +package crud + +import ( + pm "simrs-vx/internal/use-case/plugin/modifier" +) + +func init() { + createPreMw = append(createPreMw, pm.ModifInput) + createPreMw = append(createPreMw, pm.CheckData) +} diff --git a/internal/use-case/_use-case-template/crud/tycovar.go b/internal/use-case/_use-case-template/crud/tycovar.go new file mode 100644 index 00000000..17945a31 --- /dev/null +++ b/internal/use-case/_use-case-template/crud/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 crud + +import ( + "gorm.io/gorm" + + e "simrs-vx/internal/domain/main-entities/single" +) + +type createMw func(input *e.Createdto, data *e.Single, tx *gorm.DB) error +type readListMw func(input *e.ReadListDto, data *e.Single, tx *gorm.DB) error +type readDetailMw func(input *e.ReadDetailDto, data *e.Single, tx *gorm.DB) error +type updateMw func(input *e.ReadDetailDto, data *e.Single, tx *gorm.DB) error +type deleteMw func(input *e.ReadDetailDto, data *e.Single, 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 udpatePreMw []readDetailMw +var udpatePostMw []readDetailMw +var deletePreMw []readDetailMw +var deletePostMw []readDetailMw diff --git a/internal/use-case/main-use-case/.keep b/internal/use-case/main-use-case/.keep new file mode 100644 index 00000000..e69de29b diff --git a/internal/use-case/plugin/.keep b/internal/use-case/plugin/.keep new file mode 100644 index 00000000..e69de29b diff --git a/pkg/middleware/.keep b/pkg/middleware/.keep new file mode 100644 index 00000000..e69de29b diff --git a/pkg/middleware/handler-logger/handler-logger.go b/pkg/middleware/handler-logger/handler-logger.go new file mode 100644 index 00000000..ed6b77c6 --- /dev/null +++ b/pkg/middleware/handler-logger/handler-logger.go @@ -0,0 +1,52 @@ +package handlerloggermw + +import ( + "encoding/json" + "fmt" + "net/http" + "time" + + l "github.com/karincake/apem/loggera" + lo "github.com/karincake/apem/loggero" +) + +var Logger l.LoggerItf + +type wrappedWriter struct { + http.ResponseWriter + statusCode int +} + +func (w *wrappedWriter) WriteHeader(statusCode int) { + w.statusCode = statusCode + w.ResponseWriter.WriteHeader(statusCode) +} + +func SetLog(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + start := time.Now() + wrapped := &wrappedWriter{ + ResponseWriter: w, + statusCode: http.StatusOK, + } + + next.ServeHTTP(wrapped, r) + Logger.Info(). + String("scope", "request"). + Int("status", wrapped.statusCode). + String("method", r.Method). + String("path", r.URL.Path). + String("query", r.URL.RawQuery). + String("duration", time.Since(start).String()).Send() + }) +} + +func WriteJson(data any) { + lo.I.Println("Showing additional info for the payload") + js, err := json.Marshal(data) + if err == nil { + fmt.Println(string(js)) + } else { + fmt.Println("error converting data or result to json:", err) + } +} From ae524c8c476825ddf732828731d57ef30417b341 Mon Sep 17 00:00:00 2001 From: Munawwirul Jamal Date: Mon, 11 Aug 2025 11:46:32 +0700 Subject: [PATCH 2/3] pre-dev: update flow for lib vs case --- .../_plugin-template/modifier/modifire.go | 2 +- .../use-case/_use-case-template/crud/case.go | 24 ++++++++++++--- .../use-case/_use-case-template/crud/lib.go | 29 +++++++------------ 3 files changed, 31 insertions(+), 24 deletions(-) diff --git a/internal/use-case/_plugin-template/modifier/modifire.go b/internal/use-case/_plugin-template/modifier/modifire.go index c6696963..1c546e32 100644 --- a/internal/use-case/_plugin-template/modifier/modifire.go +++ b/internal/use-case/_plugin-template/modifier/modifire.go @@ -14,7 +14,7 @@ func ModifInput(input *e.Createdto, data *e.Single, tx *gorm.DB) error { // a sampel of utilizing transaction func CheckData(input *e.Createdto, data *e.Single, tx *gorm.DB) error { - tx.Where("name = ?", input.Name) + tx.Where("Name = ?", input.Name) input.Name = "Prefix_" + input.Name return nil } diff --git a/internal/use-case/_use-case-template/crud/case.go b/internal/use-case/_use-case-template/crud/case.go index 1c0e3aff..cf617ff7 100644 --- a/internal/use-case/_use-case-template/crud/case.go +++ b/internal/use-case/_use-case-template/crud/case.go @@ -7,6 +7,7 @@ package crud import ( dg "github.com/karincake/apem/db-gorm-pg" d "github.com/karincake/dodol" + "gorm.io/gorm" e "simrs-vx/internal/domain/main-entities/single" ) @@ -14,10 +15,25 @@ import ( const source = "crud" func Create(input e.Createdto) (*d.Data, error) { - result, err := CreateData(input) - if err != nil { - return nil, err - } + err := dg.I.Transaction(func(tx *gorm.DB) error { + for i := range createPreMw { + if err := createPreMw[i](&input, &data, dg.I); err != nil { + return nil + } + } + + if err := dg.I.Create(&data).Error; err != nil { + return nil + } + + for i := range createPostMw { + if err := createPostMw[i](&input, &data, dg.I); err != nil { + return nil + } + } + + return nil + }) return &d.Data{ Meta: d.II{ diff --git a/internal/use-case/_use-case-template/crud/lib.go b/internal/use-case/_use-case-template/crud/lib.go index 0c774e0b..0283c7d8 100644 --- a/internal/use-case/_use-case-template/crud/lib.go +++ b/internal/use-case/_use-case-template/crud/lib.go @@ -7,28 +7,19 @@ import ( "gorm.io/gorm" ) -func CreateData(input e.Createdto) (*e.Single, error) { +func CreateData(input e.Createdto, tx ...*gorm.DB) (*e.Single, error) { data := e.Single{} - err := dg.I.Transaction(func(tx *gorm.DB) error { - for i := range createPreMw { - if err := createPreMw[i](&input, &data, dg.I); err != nil { - return nil - } - } + var db *gorm.DB + if tx != nil { + db = tx + } else { + db = dg.I + } - if err := dg.I.Create(&data).Error; err != nil { - return nil - } - - for i := range createPostMw { - if err := createPostMw[i](&input, &data, dg.I); err != nil { - return nil - } - } - - return nil - }) + if err := dg.I.Create(&data).Error; err != nil { + return nil, err + } return &data, err } From 882a8ae046761e6af16a96bbd39e3f46147ec5da Mon Sep 17 00:00:00 2001 From: Munawwirul Jamal Date: Tue, 12 Aug 2025 13:52:39 +0700 Subject: [PATCH 3/3] pre-dev: added more config --- assets/.keep | 0 assets/language/en/data.json | 78 +++++++++++++++++++++++++++++++ assets/language/id/data.json | 83 +++++++++++++++++++++++++++++++++ assets/language/readme.md | 2 + cmd/main-api/config.yml-example | 23 ++++++++- 5 files changed, 185 insertions(+), 1 deletion(-) create mode 100644 assets/.keep create mode 100644 assets/language/en/data.json create mode 100644 assets/language/id/data.json create mode 100644 assets/language/readme.md diff --git a/assets/.keep b/assets/.keep new file mode 100644 index 00000000..e69de29b diff --git a/assets/language/en/data.json b/assets/language/en/data.json new file mode 100644 index 00000000..be5c94c1 --- /dev/null +++ b/assets/language/en/data.json @@ -0,0 +1,78 @@ +{ + "request-ok": "request is done succesfully", + "request-methodNotAllowed": "request has been rejected, method is not allowed", + "request-tooMany": "too many request", + "payload-bad": "invalid data structure", + + "auth-required": "authentication required", + "auth-missingHeader": "authentication header is missing", + "auth-forbidden": "forbidden", + "auth-login-success": "login success", + "auth-login-incorrect": "login failed, icorrect username or password", + "auth-login-tooMany": "login failed, too many login attempts", + "auth-login-blocked": "login failed, account is blocked", + "auth-login-unverified": "login failed, account is not verified", + "auth-logout-success": "logout success", + "auth-reject-suspend": "restricted for suspended account", + + "balance-exceeded": "must not exceeds balance", + + "email-confirmation-success": "email confirmation success", + "email-confirmation-fail": "failed to confirm email", + "email-confirmation-resend-fail": "failed to resend email confirmation", + + "data-create-success": "data has been created", + "data-create-fail": "failed to create data", + "data-notFound": "data or resource can not be found", + "data-notFound-condition": "\"%v\" with %v \"%v\" can not be found", + "data-get-fail": "get data failed, %v", + "data-update-fail": "failed to update data", + "data-delete-success": "data has been deleted", + "data-delete-fail": "failed to delete data", + "data-send-fail": "send data failed, %v", + "data-fetch-fail": "failed to fetch data", + "data-copy-fail": "failed to copy data", + "data-process-fail": "failed to process data", + "data-confirm-fail": "failed to confirm data", + "data-generate-fail": "failed to generate data", + "data-registered": "is already registered", + "data-state-mismatch": "\"%v\" is not in \"%v\" state", + "data-duplicate": "duplicate data", + "data-payment-fail": "failed to create payment", + + "expired": "is expired", + "registered": "is already registered", + "mustUuid": "must be a valid uuid", + "invalid": "invalid value", + "equalToField": "must equal to", + "constraint-vioalate": "must not violate constraint", + + "config-fetch-done": "app config is fetched successfully", + "userToken-incorrect": "incorrect email, token, or token type", + + "token-missing": "missing required token", + "token-invalid": "invalid token", + "token-invalidType": "invalid token type", + "token-sign-unexcpeted": "unexpected signing method: %v", + "token-sign-err": "token signing failed, %v", + "token-parse-fail": "token parsing failed, %v", + "token-expired": "token is expired", + "token-unidentified": "token is could not be identified", + + "file-detect-fail": "file type detection failed", + "file-type-mismatch": "file type mismatch", + "file-create-fail": "failed to create file, %v", + "file-open-fail": "failed to open file, %v", + "file-copy-fail": "failed to copy file, %v", + + "uuid-gen-fail" : "failed to generate uuid, %v", + "nanoid-gen-fail" : "failed to generate nanoid, %v", + + "redis-store-fail": "redis storing value failed, %v", + + "eq": "must be equal to %v", + "gt": "must be greater than %v", + "gte": "must be greater than or equal to %v", + "lt": "must be less than %v", + "lte": "must be less than or equal to %v" +} diff --git a/assets/language/id/data.json b/assets/language/id/data.json new file mode 100644 index 00000000..9e064e9a --- /dev/null +++ b/assets/language/id/data.json @@ -0,0 +1,83 @@ +{ + "request-ok": "'request' berhasil diproses", + "request-methodNotAllowed": "'request' ditolak, method tidak diijinkan", + "request-tooMany": "terlalu banyak 'request'", + "payload-bad": "struktur data tidak sesuai standar", + + "auth-required": "butuh autentikasi", + "auth-missingHeader": "Header autentikasi tidak ditemukan", + "auth-forbidden": "tidak diijinkan", + "auth-login-success": "login berhasil", + "auth-login-incorrect": "Username atau Password Tidak Sesuai", + "auth-login-tooMany": "login gagal, terlalu banyak percobaan", + "auth-login-blocked": "login gagal, akun diblokir", + "auth-login-unverified": "login gagal, akun belum terverifikasi", + "auth-logout-success": "logout berhasil", + "auth-reject-suspend": "dibatasi untuk akun dibekukan", + + "balance-exceeded": "tidak boleh melebihi saldo", + + "email-confirmation-success": "konfimrasi email berhasil", + "email-confirmation-fail": "gagal mengkonfirmasi email", + "email-confirmation-resend-fail": "gagal mengirim konfirmasi email", + + "data-create-success": "data telah dibuat", + "data-create-fail": "gagal membuat data", + "data-notFound": "data atau sumber tidak daoat ditemukan", + "data-notFound-condition": "\"%v\" dengan %v \"%v\" tidak dapat ditemukan", + "data-get-fail": "gagal mengambil data, %v", + "data-update-success": "berhasil memperbarui data", + "data-update-fail": "gagal memperbarui data", + "data-delete-success": "data telah dihapus", + "data-delete-fail": "gagal menghapus data", + "data-fetch-fail": "gagal mengambil data", + "data-copy-fail": "gagal menyalin data", + "data-process-fail": "gagal memproses data", + "data-confirm-fail": "gagal mengirim data", + "data-generate-fail": "gagal membuat data", + "data-registered": "sudah pernah dibuat", + "data-state-mismatch": "\"%v\" tidak dalam kondisi \"%v\"", + "data-duplicate": "duplicate data", + "data-payment-fail": "failed to create payment", + + "expired": "expired", + "registered": "sudah terdaftar", + "mustUuid": "harus UUID yang valid", + "invalid": "nilai tidak sesuai", + "equalToField": "harus sama dengan", + "constraint-vioalate": "tidak boleh melanggar batasan", + + "config-fetch-done": "konfigurasi app berhasil diterapkan", + "userToken-incorrect": "salah email, token, atau jenis token", + + "token-missing": "token tidak dapat ditemukan", + "token-invalid": "token tidak berlaku", + "token-invalidType": "jenis token tidak berlaku", + "token-sign-unexcpeted": "metode penanda tidak beralku: %v", + "token-sign-err": "penanda token tidak sesuai, %v", + "token-parse-fail": "%v", + "token-expired": "Token Expired", + "token-unidentified": "token tidak dapat diidentifikasi", + + "file-detect-fail": "gagal mendeteksi tipe file", + "file-type-mismatch": "tipe file tidak sesuai", + "file-create-fail": "gagal membuat file, %v", + "file-open-fail": "gagal membuka file, %v", + "file-copy-fail": "gagal menyalin file, %v", + + "uuid-gen-fail" : "gagal membuat UUID, %v", + "nanoid-gen-fail" : "gagal membuat NANOID, %v", + + "redis-store-fail": "gagal mengimpan data pada redis, %v", + + "eq": "must be equal to %v", + "gt": "must be greater than %v", + "gte": "lebih besar atau sama dengan %v", + "lt": "must be less than %v", + "lte": "lebih kecil atau sama dengan %v", + + "jknData-required": "%v Belum Diisi", + "jknData-length": "Format %v Tidak Sesuai", + "jknData-numeric": "Format %v Tidak Sesuai", + "jknData-notFound": "%v Tidak Ditemukan" +} diff --git a/assets/language/readme.md b/assets/language/readme.md new file mode 100644 index 00000000..720e25c8 --- /dev/null +++ b/assets/language/readme.md @@ -0,0 +1,2 @@ +# convention +Key - value data indexed. Key prefered to be be consisting of 3 part: [feature]-[action]-[status]. Special case for validation that is intended to point error on an input (field), the key can be stated directly to the validation index, for example: min is for minimum value needed. \ No newline at end of file diff --git a/cmd/main-api/config.yml-example b/cmd/main-api/config.yml-example index 6bed47f9..19893fd7 100644 --- a/cmd/main-api/config.yml-example +++ b/cmd/main-api/config.yml-example @@ -9,6 +9,12 @@ httpCfg: host: port: +dbCfg: + dsn: + maxOpenConns: 5 + maxIdleConns: 5 + maxIdleTime: 100 + loggerCfg: hideTime: hideLevel: @@ -21,6 +27,21 @@ langCfg: path: fileName: +minioCfg: + endpoint: + region: + accessKey: + secretKey: + useSsl: + bucketName: + - patient + corsCfg: allowedOrigin: - allowedMethod: \ No newline at end of file + allowedMethod: + +satuSehatCfg: + host: localhsot:8200 + +bpjsCfg: + host: localhsot:8200