Merge pull request #2 from dikstub-rssa/pre-dev

Pre dev
This commit is contained in:
Munawwirul Jamal
2025-08-12 13:54:11 +07:00
committed by GitHub
53 changed files with 1210 additions and 1 deletions
+1
View File
@@ -0,0 +1 @@
# APP DESIGN CONVENTION
+37
View File
@@ -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 <span>`object`-`attribute`</span> or <span>`table`-`column`</span>
<br />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.
<br />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 <span>`object`-`attribute`</span> or <span>`table`-`column`</span>
<br />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.
<br />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.
+2 -1
View File
@@ -1 +1,2 @@
# simrs-be
# SIMRS-VX
View File
+78
View File
@@ -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"
}
+83
View File
@@ -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"
}
+2
View File
@@ -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.
View File
+47
View File
@@ -0,0 +1,47 @@
appCfg:
fullName: BPJS Bridge
codeName: simrs-vx
version: 0.1.0
env: development
lang: en
httpCfg:
host:
port:
dbCfg:
dsn:
maxOpenConns: 5
maxIdleConns: 5
maxIdleTime: 100
loggerCfg:
hideTime:
hideLevel:
msCfg:
dsn:
langCfg:
active:
path:
fileName:
minioCfg:
endpoint:
region:
accessKey:
secretKey:
useSsl:
bucketName:
- patient
corsCfg:
allowedOrigin:
allowedMethod:
satuSehatCfg:
host: localhsot:8200
bpjsCfg:
host: localhsot:8200
+14
View File
@@ -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)
}
+32
View File
@@ -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
)
+78
View File
@@ -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=
@@ -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"`
}
@@ -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"
)
+28
View File
@@ -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
}
@@ -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
}
@@ -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"`
}
@@ -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"
)
@@ -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
}
@@ -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"`
}
@@ -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
}
@@ -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"`
}
@@ -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, " ")
}
@@ -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"
)
@@ -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
}
@@ -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
}
@@ -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
}
+26
View File
@@ -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"`
}
@@ -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"`
}
View File
@@ -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"`
}
@@ -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
}
View File
View File
@@ -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) {
}
@@ -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)
// }
}
@@ -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) {
}
@@ -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)
}
@@ -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)
}
View File
@@ -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
}
@@ -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.
@@ -0,0 +1,71 @@
/*
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"
"gorm.io/gorm"
e "simrs-vx/internal/domain/main-entities/single"
)
const source = "crud"
func Create(input e.Createdto) (*d.Data, error) {
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{
"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() {
}
@@ -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) {
}
@@ -0,0 +1,25 @@
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, tx ...*gorm.DB) (*e.Single, error) {
data := e.Single{}
var db *gorm.DB
if tx != nil {
db = tx
} else {
db = dg.I
}
if err := dg.I.Create(&data).Error; err != nil {
return nil, err
}
return &data, err
}
@@ -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)
}
@@ -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
View File
View File
@@ -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)
}
}