Files

106 lines
3.0 KiB
Go

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()
}