refactor: use static HIS database per instance
This commit is contained in:
105
internal/databaseconfig/static.go
Normal file
105
internal/databaseconfig/static.go
Normal file
@@ -0,0 +1,105 @@
|
||||
package databaseconfig
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
mysql "github.com/go-sql-driver/mysql"
|
||||
"primaya-api/cpone-middleware/internal/repository"
|
||||
)
|
||||
|
||||
// ErrRSCodeMismatch indicates that a request was sent to the wrong hospital
|
||||
// instance. The code is a guard for this installation, not a database key.
|
||||
var ErrRSCodeMismatch = errors.New("kode RS tidak sesuai dengan instance CPONE")
|
||||
|
||||
type Setting struct {
|
||||
RSCode string `json:"kode_rs"`
|
||||
Name string `json:"nama"`
|
||||
Host string `json:"host"`
|
||||
Port string `json:"port"`
|
||||
Database string `json:"database"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
type PoolConfig struct {
|
||||
MaxOpenConns int
|
||||
MaxIdleConns int
|
||||
ConnMaxLifetime time.Duration
|
||||
}
|
||||
|
||||
func NormalizeRSCode(code string) string { return strings.ToUpper(strings.TrimSpace(code)) }
|
||||
|
||||
// StaticResolver owns one HIS connection for one CPONE installation. Resolve
|
||||
// only validates the request code and always returns the same repository.
|
||||
type StaticResolver struct {
|
||||
code string
|
||||
db *sql.DB
|
||||
repo repository.MySQLLayananRepository
|
||||
}
|
||||
|
||||
func NewStaticResolver(ctx context.Context, code string, setting Setting, pool PoolConfig) (*StaticResolver, error) {
|
||||
code = NormalizeRSCode(code)
|
||||
if code == "" {
|
||||
return nil, errors.New("CPONE_RS_CODE wajib diisi")
|
||||
}
|
||||
if setting.Host == "" || setting.Port == "" || setting.Database == "" || setting.Username == "" {
|
||||
return nil, errors.New("host, port, database, dan username HIS wajib diisi")
|
||||
}
|
||||
port, err := strconv.Atoi(setting.Port)
|
||||
if err != nil || port < 1 || port > 65535 {
|
||||
return nil, errors.New("port database HIS harus berupa angka antara 1 sampai 65535")
|
||||
}
|
||||
|
||||
db, err := sql.Open("mysql", mysqlDSN(setting))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
applyPoolConfig(db, pool)
|
||||
if err := db.PingContext(ctx); err != nil {
|
||||
_ = db.Close()
|
||||
return nil, fmt.Errorf("koneksi database HIS gagal: %w", err)
|
||||
}
|
||||
return &StaticResolver{code: code, db: db, repo: repository.NewMySQLLayananRepository(db)}, nil
|
||||
}
|
||||
|
||||
func (r *StaticResolver) Resolve(_ context.Context, code string) (repository.MySQLLayananRepository, error) {
|
||||
if NormalizeRSCode(code) != r.code {
|
||||
return repository.MySQLLayananRepository{}, ErrRSCodeMismatch
|
||||
}
|
||||
return r.repo, nil
|
||||
}
|
||||
|
||||
func (r *StaticResolver) Close() error {
|
||||
if r == nil || r.db == nil {
|
||||
return nil
|
||||
}
|
||||
return r.db.Close()
|
||||
}
|
||||
|
||||
func (r *StaticResolver) Code() string { return strings.TrimSpace(r.code) }
|
||||
|
||||
func applyPoolConfig(db *sql.DB, pool PoolConfig) {
|
||||
db.SetMaxOpenConns(pool.MaxOpenConns)
|
||||
db.SetMaxIdleConns(pool.MaxIdleConns)
|
||||
db.SetConnMaxLifetime(pool.ConnMaxLifetime)
|
||||
}
|
||||
|
||||
func mysqlDSN(setting Setting) string {
|
||||
cfg := mysql.NewConfig()
|
||||
cfg.User = setting.Username
|
||||
cfg.Passwd = setting.Password
|
||||
cfg.Net = "tcp"
|
||||
cfg.Addr = net.JoinHostPort(setting.Host, setting.Port)
|
||||
cfg.DBName = setting.Database
|
||||
cfg.ParseTime = true
|
||||
cfg.Loc = time.Local
|
||||
cfg.Params = map[string]string{"charset": "utf8mb4"}
|
||||
return cfg.FormatDSN()
|
||||
}
|
||||
Reference in New Issue
Block a user