Update Worker Satu Sehat

This commit is contained in:
meninjar
2026-04-14 05:14:09 +00:00
parent 35c101725f
commit 9a15d74283
162 changed files with 538 additions and 9489 deletions
+1 -1
View File
@@ -14,7 +14,7 @@ RUN go mod download
COPY . .
# Expose port
EXPOSE 8094
EXPOSE 8098
# Run air untuk hot reload
CMD ["air", "-c", ".air.toml"]
+2 -125
View File
@@ -10,30 +10,12 @@ import (
"syscall"
"service/internal/auth"
antrol "service/internal/bpjs/antrol/reference"
aplicareBed "service/internal/bpjs/aplicare/bed"
apotekDpho "service/internal/bpjs/apotek/reference/dpho"
apotekPoli "service/internal/bpjs/apotek/reference/poli"
"service/internal/bpjs/vclaim/peserta"
"service/internal/bpjs/vclaim/sep"
"service/internal/infrastructure/cache"
"service/internal/infrastructure/config"
"service/internal/infrastructure/database"
grpcHandlers "service/internal/infrastructure/transport/grpc/handlers"
httpServer "service/internal/infrastructure/transport/http/servers"
bpjsFactory "service/internal/interfaces/bpjs"
"service/internal/interfaces/minio"
satuSehatFactory "service/internal/interfaces/satusehat"
satuSehatAuth "service/internal/satusehat/reference/auth"
satuSehatKfa "service/internal/satusehat/reference/kfa"
satuSehatLocation "service/internal/satusehat/reference/location"
satuSehatOrganization "service/internal/satusehat/reference/organization"
satuSehatPatient "service/internal/satusehat/reference/patient"
satuSehatPractitioner "service/internal/satusehat/reference/practitioner"
satuSehatCondition "service/internal/satusehat/usecase/condition"
satuSehatEncounter "service/internal/satusehat/usecase/encounter"
satuSehatProcedure "service/internal/satusehat/usecase/procedure"
"service/pkg/logger"
imagingStudyWorker "service/internal/worker/satusehat/imagingstudy"
@@ -169,69 +151,6 @@ func main() {
// roleComponentCmdRepo := roleComponent.NewCommandRepository(dbService, "postgres")
// roleComponentQueryRepo := roleComponent.NewQueryRepository(dbService, "postgres")
// roleComponentService := roleComponent.NewService(roleComponentCmdRepo, roleComponentQueryRepo)
// --- [NEW] Inisialisasi Modul CQRS Baru (Memanfaatkan DB Service) ---
// Contoh ketika mengimplementasikan entity "Province" atau "Person":
// provinceCmdRepo := province.NewCommandRepository(dbService, "default")
// provinceQueryRepo := province.NewQueryRepository(dbService, "default")
// provinceSvc := province.NewService(provinceCmdRepo, provinceQueryRepo)
// --- [NEW] Inisialisasi Factory BPJS ---
bpjsServices := bpjsFactory.NewBPJSFactory(cfg.Bpjs)
// 1. Modul VClaim (Lama)
vclaimServiceEngine := bpjsServices.VClaim()
sepRepo := sep.NewRepository(vclaimServiceEngine)
sepService := sep.NewService(sepRepo)
pesertaRepo := peserta.NewRepository(vclaimServiceEngine)
pesertaService := peserta.NewService(pesertaRepo)
// 2. Modul Antrean RS (Baru)
antreanClient := bpjsServices.AntreanRS()
antrolRepo := antrol.NewRepository(antreanClient)
antrolService := antrol.NewService(antrolRepo)
// 3. Modul Aplicares (Bed Ketersediaan Tempat Tidur)
aplicareClient := bpjsServices.Aplicare()
bedRepo := aplicareBed.NewRepository(aplicareClient)
bedService := aplicareBed.NewService(bedRepo)
// 4. Modul Apotek (Referensi DPHO dan Poli)
apotekClient := bpjsServices.Apotek()
dphoRepo := apotekDpho.NewRepository(apotekClient)
dphoService := apotekDpho.NewService(dphoRepo)
poliApotekRepo := apotekPoli.NewRepository(apotekClient)
poliApotekService := apotekPoli.NewService(poliApotekRepo)
// --- [NEW] Inisialisasi Modul Satu Sehat ---
satusehatFact := satuSehatFactory.NewSatuSehatFactory(cfg.SatuSehat)
satusehatClient := satusehatFact.Client()
satuSehatAuthSvc := satuSehatAuth.NewService(satusehatClient, cacheManager)
satuSehatPatientRepo := satuSehatPatient.NewRepository(satusehatClient)
satuSehatPatientSvc := satuSehatPatient.NewService(satuSehatPatientRepo)
satuSehatPractitionerRepo := satuSehatPractitioner.NewRepository(satusehatClient)
satuSehatPractitionerSvc := satuSehatPractitioner.NewService(satuSehatPractitionerRepo)
satuSehatOrganizationRepo := satuSehatOrganization.NewRepository(satusehatClient)
satuSehatOrganizationSvc := satuSehatOrganization.NewService(satuSehatOrganizationRepo)
satuSehatLocationRepo := satuSehatLocation.NewRepository(satusehatClient)
satuSehatLocationSvc := satuSehatLocation.NewService(satuSehatLocationRepo)
satuSehatKfaRepo := satuSehatKfa.NewRepository(satusehatClient)
satuSehatKfaSvc := satuSehatKfa.NewService(satuSehatKfaRepo)
satuSehatEncounterRepo := satuSehatEncounter.NewRepository(satusehatClient, dbService)
satuSehatEncounterSvc := satuSehatEncounter.NewService(satuSehatEncounterRepo)
satuSehatProcedureRepo := satuSehatProcedure.NewRepository(satusehatClient)
satuSehatProcedureSvc := satuSehatProcedure.NewService(satuSehatProcedureRepo)
satuSehatConditionRepo := satuSehatCondition.NewRepository(satusehatClient)
satuSehatConditionSvc := satuSehatCondition.NewService(satuSehatConditionRepo)
// 6. Server Orchestration (Dual Protocol & Background Workers)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
@@ -273,48 +192,6 @@ func main() {
RolePermission: rolePermissionService,
RoleMaster: roleMasterService,
},
VClaim: &httpServer.VClaimServices{
Sep: sepService,
Peserta: pesertaService,
},
Antrol: &httpServer.AntrolServices{
Reference: antrolService,
},
Aplicare: &httpServer.AplicaresServices{
Bed: bedService,
},
Apotek: &httpServer.ApotekServices{
DPHO: dphoService,
Poli: poliApotekService,
},
SatuSehat: &httpServer.SatuSehatServices{
Auth: satuSehatAuthSvc,
Patient: satuSehatPatientSvc,
Practitioner: satuSehatPractitionerSvc,
Organization: satuSehatOrganizationSvc,
Location: satuSehatLocationSvc,
KFA: satuSehatKfaSvc,
Encounter: satuSehatEncounterSvc,
Procedure: satuSehatProcedureSvc,
Condition: satuSehatConditionSvc,
// TODO: Inisialisasi service sisanya di sini saat repo & service usecase lainnya sudah Anda buat, contoh:
// AllergyIntolerance: allergySvc,
// CarePlan: careplanSvc,
// ClinicalImpression: clinicalImpressionSvc,
// Composition: compositionSvc,
// DiagnosticReport: diagnosticReportSvc,
// EpisodeOfCare: episodeOfCareSvc,
// ImagingStudy: imagingStudySvc,
// Immunization: immunizationSvc,
// Medication: medicationSvc,
// MedicationDispense: medicationDispenseSvc,
// MedicationRequest: medicationRequestSvc,
// MedicationStatement: medicationStatementSvc,
// Observation: observationSvc,
// QuestionnaireResponse: questionnaireResponseSvc,
// ServiceRequest: serviceRequestSvc,
// Specimen: specimenSvc,
},
}
restSrv := httpServer.NewHTTPServer(&cfg.Server.REST, registry)
@@ -334,10 +211,10 @@ func main() {
serverCount++
// 1. Buat gRPC handlers
permissionHandler := grpcHandlers.NewPermissionHandler(rolePermissionService)
// permissionHandler := grpcHandlers.NewPermissionHandler(rolePermissionService)
// 2. Buat dan isi gRPC service registry
grpcRegistry := &grpcServers.ServiceRegistry{
PermissionHandler: permissionHandler,
// PermissionHandler: permissionHandler,
}
// 3. Buat gRPC server dengan registry yang sudah diisi
-141
View File
@@ -1,141 +0,0 @@
package main
import (
"context"
"net/http"
"os"
"os/signal"
"syscall"
"service/internal/infrastructure/cache"
"service/internal/infrastructure/config"
"service/internal/infrastructure/database"
httpServer "service/internal/infrastructure/transport/http/servers"
satuSehatFactory "service/internal/interfaces/satusehat"
"service/pkg/logger"
satuSehatAuth "service/internal/satusehat/reference/auth"
satuSehatKfa "service/internal/satusehat/reference/kfa"
satuSehatLocation "service/internal/satusehat/reference/location"
satuSehatOrganization "service/internal/satusehat/reference/organization"
satuSehatPatient "service/internal/satusehat/reference/patient"
satuSehatPractitioner "service/internal/satusehat/reference/practitioner"
satuSehatCondition "service/internal/satusehat/usecase/condition"
satuSehatEncounter "service/internal/satusehat/usecase/encounter"
satuSehatProcedure "service/internal/satusehat/usecase/procedure"
_ "service/docs/swagger" // Import swagger docs
"golang.org/x/sync/errgroup"
)
func main() {
// 1. Load Config
cfg := config.LoadConfig()
// 2. Init Logger khusus untuk service Satu Sehat
loggerConfig := logger.Config{
Level: cfg.Logger.Level,
Format: cfg.Logger.Format,
Output: "both",
ServiceName: "service-satusehat", // Penanda log
EnableCaller: cfg.Server.Mode != "production",
Environment: cfg.Server.Mode,
}
logger.Init(loggerConfig)
logger.Default().Info("SatuSehat Microservice starting...",
logger.String("environment", cfg.Server.Mode),
logger.String("log_level", cfg.Logger.Level),
)
if err := cfg.Validate(); err != nil {
logger.Default().Fatal("Config validation failed", logger.ErrorField(err))
}
// 3. Init Database Manager
dbService := database.New(cfg)
defer dbService.Close()
gormDB, err := dbService.GetGormDB("default")
if err != nil {
logger.Default().Fatal("Failed to get primary database", logger.ErrorField(err))
}
// 4. Init Cache
cacheFactory := cache.NewFactory(cfg.Cache)
cacheManager, err := cacheFactory.CreateManager()
if err != nil {
logger.Default().Fatal("Failed to initialize cache manager", logger.ErrorField(err))
}
defer cacheManager.Close()
// 5. Init Layers (Hanya Modul Satu Sehat)
satusehatFact := satuSehatFactory.NewSatuSehatFactory(cfg.SatuSehat)
satusehatClient := satusehatFact.Client()
// - Reference Services
satuSehatAuthSvc := satuSehatAuth.NewService(satusehatClient, cacheManager)
satuSehatPatientSvc := satuSehatPatient.NewService(satuSehatPatient.NewRepository(satusehatClient))
satuSehatPractitionerSvc := satuSehatPractitioner.NewService(satuSehatPractitioner.NewRepository(satusehatClient))
satuSehatOrganizationSvc := satuSehatOrganization.NewService(satuSehatOrganization.NewRepository(satusehatClient))
satuSehatLocationSvc := satuSehatLocation.NewService(satuSehatLocation.NewRepository(satusehatClient))
satuSehatKfaSvc := satuSehatKfa.NewService(satuSehatKfa.NewRepository(satusehatClient))
// - Usecase Services
satuSehatEncounterSvc := satuSehatEncounter.NewService(satuSehatEncounter.NewRepository(satusehatClient, dbService))
satuSehatProcedureSvc := satuSehatProcedure.NewService(satuSehatProcedure.NewRepository(satusehatClient))
satuSehatConditionSvc := satuSehatCondition.NewService(satuSehatCondition.NewRepository(satusehatClient))
// 6. Server Orchestration
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
g, ctx := errgroup.WithContext(ctx)
// --- Start REST Server (Hanya me-load endpoint Satu Sehat & Health) ---
if cfg.Server.REST.Enabled {
registry := &httpServer.ServiceRegistry{
Config: cfg,
DBManager: dbService,
PrimaryDB: gormDB,
CacheManager: cacheManager,
SatuSehat: &httpServer.SatuSehatServices{
Auth: satuSehatAuthSvc,
Patient: satuSehatPatientSvc,
Practitioner: satuSehatPractitionerSvc,
Organization: satuSehatOrganizationSvc,
Location: satuSehatLocationSvc,
KFA: satuSehatKfaSvc,
Encounter: satuSehatEncounterSvc,
Procedure: satuSehatProcedureSvc,
Condition: satuSehatConditionSvc,
},
}
restSrv := httpServer.NewHTTPServer(&cfg.Server.REST, registry)
g.Go(func() error {
logger.Default().Info("SatuSehat REST API running", logger.Int("port", cfg.Server.REST.Port))
if err := restSrv.Start(&cfg.Server); err != nil && err != http.ErrServerClosed {
return err
}
return nil
})
}
// --- Graceful Shutdown Listener ---
g.Go(func() error {
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
logger.Default().Warn("Signal received, shutting down SatuSehat service...")
cancel()
return nil
})
if err := g.Wait(); err != nil {
logger.Default().Error("SatuSehat service shutdown with error", logger.ErrorField(err))
} else {
logger.Default().Info("SatuSehat service shutdown successful")
}
}
-9
View File
@@ -1,9 +0,0 @@
package main
import (
"service/internal/infrastructure/database/seeders"
)
func main() {
seeders.MainCLI()
}
-9
View File
@@ -1,9 +0,0 @@
package antrol
// Poli merepresentasikan data referensi poli dari API Antrean RS BPJS
type Poli struct {
KdPoli string `json:"kdpoli"`
NmPoli string `json:"nmpoli"`
KdSubSpesialis string `json:"kdsubspesialis"`
NmSubSpesialis string `json:"nmsubspesialis"`
}
@@ -1,47 +0,0 @@
package antrol
import (
"context"
"encoding/json"
"fmt"
"service/internal/interfaces/bpjs"
)
type Repository interface {
GetRefPoli(ctx context.Context) ([]Poli, error)
}
type repository struct {
client bpjs.BpjsClient
}
func NewRepository(client bpjs.BpjsClient) Repository {
return &repository{client: client}
}
// GetRefPoli fetches referensi poli from BPJS API
//
// # This function will return an array of Poli and error if any
//
// Context is used to pass the request context to the underlying
// client
//
// The function will return an error if the request to BPJS API
// fails or if the response cannot be unmarshalled into an array
// of Poli
//
// The function will return an empty array and nil error if the request
// to BPJS API succeeds but the response is an empty array
func (r *repository) GetRefPoli(ctx context.Context) ([]Poli, error) {
respBytes, err := r.client.DoRequest(ctx, "GET", "ref/poli", nil)
if err != nil {
return nil, err
}
var result []Poli
if err := json.Unmarshal(respBytes, &result); err != nil {
return nil, fmt.Errorf("failed to unmarshal ref poli antrol response: %w", err)
}
return result, nil
}
-26
View File
@@ -1,26 +0,0 @@
package antrol
import (
"context"
"service/pkg/errors"
)
type Service interface {
GetRefPoli(ctx context.Context) ([]Poli, error)
}
type service struct {
repo Repository
}
func NewService(repo Repository) Service {
return &service{repo: repo}
}
func (s *service) GetRefPoli(ctx context.Context) ([]Poli, error) {
res, err := s.repo.GetRefPoli(ctx)
if err != nil {
return nil, errors.ExternalError().Message("Gagal mengambil referensi poli Antrean RS BPJS").Cause(err).Build()
}
return res, nil
}
-24
View File
@@ -1,24 +0,0 @@
package bed
// BedData merepresentasikan payload untuk membuat, mengubah, atau mengambil data ketersediaan Bed
type BedData struct {
KodeKelas string `json:"kodekelas"`
KodeRuang string `json:"koderuang"`
NamaRuang string `json:"namaruang"`
Kapasitas int `json:"kapasitas"`
Tersedia int `json:"tersedia"`
TersediaPria int `json:"tersediapria"`
TersediaWanita int `json:"tersediawanita"`
TersediaPriaWanita int `json:"tersediapriawanita"`
}
// BedDeletePayload merepresentasikan payload untuk menghapus data Bed
type BedDeletePayload struct {
KodeKelas string `json:"kodekelas"`
KodeRuang string `json:"koderuang"`
}
// BedReadResponse merepresentasikan balikan dari endpoint GET Read Bed
type BedReadResponse struct {
List []BedData `json:"list"`
}
-60
View File
@@ -1,60 +0,0 @@
package bed
import (
"context"
"encoding/json"
"fmt"
"service/internal/interfaces/bpjs"
)
type Repository interface {
Read(ctx context.Context, kdPpk string, start, limit int) ([]BedData, error)
Create(ctx context.Context, kdPpk string, payload BedData) error
Update(ctx context.Context, kdPpk string, payload BedData) error
Delete(ctx context.Context, kdPpk string, payload BedDeletePayload) error
}
type repository struct {
client bpjs.BpjsClient
}
func NewRepository(client bpjs.BpjsClient) Repository {
return &repository{client: client}
}
func (r *repository) Read(ctx context.Context, kdPpk string, start, limit int) ([]BedData, error) {
endpoint := fmt.Sprintf("rest/bed/read/%s/%d/%d", kdPpk, start, limit)
respBytes, err := r.client.DoRequest(ctx, "GET", endpoint, nil)
if err != nil {
return nil, err
}
var result BedReadResponse
if err := json.Unmarshal(respBytes, &result); err != nil {
return nil, fmt.Errorf("failed to unmarshal applicare read response: %w", err)
}
return result.List, nil
}
func (r *repository) Create(ctx context.Context, kdPpk string, payload BedData) error {
endpoint := fmt.Sprintf("rest/bed/create/%s", kdPpk)
_, err := r.client.DoRequest(ctx, "POST", endpoint, payload)
// Aplicares biasanya mengembalikan sukses di metadata yang sudah dihandle oleh client.go
return err
}
func (r *repository) Update(ctx context.Context, kdPpk string, payload BedData) error {
// Endpoint Update Aplicares menggunakan method POST, bukan PUT
endpoint := fmt.Sprintf("rest/bed/update/%s", kdPpk)
_, err := r.client.DoRequest(ctx, "POST", endpoint, payload)
return err
}
func (r *repository) Delete(ctx context.Context, kdPpk string, payload BedDeletePayload) error {
// Endpoint Delete Aplicares juga menggunakan method POST
endpoint := fmt.Sprintf("rest/bed/delete/%s", kdPpk)
_, err := r.client.DoRequest(ctx, "POST", endpoint, payload)
return err
}
-57
View File
@@ -1,57 +0,0 @@
package bed
import (
"context"
"service/pkg/errors"
)
type Service interface {
GetBedList(ctx context.Context, kdPpk string, start, limit int) ([]BedData, error)
CreateBed(ctx context.Context, kdPpk string, req BedData) error
UpdateBed(ctx context.Context, kdPpk string, req BedData) error
DeleteBed(ctx context.Context, kdPpk string, req BedDeletePayload) error
}
type service struct {
repo Repository
}
func NewService(repo Repository) Service {
return &service{repo: repo}
}
func (s *service) GetBedList(ctx context.Context, kdPpk string, start, limit int) ([]BedData, error) {
if kdPpk == "" {
return nil, errors.NewValidationError().Message("Kode PPK (Kode Faskes) wajib diisi").Build()
}
res, err := s.repo.Read(ctx, kdPpk, start, limit)
if err != nil {
return nil, errors.ExternalError().Message("Gagal mengambil data ketersediaan tempat tidur (Aplicares)").Cause(err).Build()
}
return res, nil
}
func (s *service) CreateBed(ctx context.Context, kdPpk string, req BedData) error {
err := s.repo.Create(ctx, kdPpk, req)
if err != nil {
return errors.ExternalError().Message("Gagal membuat data tempat tidur baru (Aplicares)").Cause(err).Build()
}
return nil
}
func (s *service) UpdateBed(ctx context.Context, kdPpk string, req BedData) error {
err := s.repo.Update(ctx, kdPpk, req)
if err != nil {
return errors.ExternalError().Message("Gagal memperbarui data tempat tidur (Aplicares)").Cause(err).Build()
}
return nil
}
func (s *service) DeleteBed(ctx context.Context, kdPpk string, req BedDeletePayload) error {
err := s.repo.Delete(ctx, kdPpk, req)
if err != nil {
return errors.ExternalError().Message("Gagal menghapus data tempat tidur (Aplicares)").Cause(err).Build()
}
return nil
}
@@ -1,11 +0,0 @@
package dpho
// DPHOData merepresentasikan detail referensi DPHO dari API Apotek
type DPHOData struct {
KodeObat string `json:"kodeobat"`
NamaObat string `json:"namaobat"`
PRB string `json:"prb"`
Kronis string `json:"kronis"`
Kemo string `json:"kemo"`
Harga string `json:"harga"`
}
@@ -1,42 +0,0 @@
package dpho
import (
"context"
"encoding/json"
"fmt"
"service/internal/interfaces/bpjs"
)
type Repository interface {
GetDPHO(ctx context.Context) ([]DPHOData, error)
}
type repository struct {
client bpjs.BpjsClient
}
func NewRepository(client bpjs.BpjsClient) Repository {
return &repository{client: client}
}
func (r *repository) GetDPHO(ctx context.Context) ([]DPHOData, error) {
respBytes, err := r.client.DoRequest(ctx, "GET", "referensi/dpho", nil)
if err != nil {
return nil, err
}
// Coba parsing ke dalam format object { "list": [...] }
var result struct {
List []DPHOData `json:"list"`
}
if err := json.Unmarshal(respBytes, &result); err != nil {
// Fallback jika API mengembalikan array langsung [...]
var directList []DPHOData
if err2 := json.Unmarshal(respBytes, &directList); err2 == nil {
return directList, nil
}
return nil, fmt.Errorf("failed to unmarshal apotek dpho response: %w", err)
}
return result.List, nil
}
@@ -1,26 +0,0 @@
package dpho
import (
"context"
"service/pkg/errors"
)
type Service interface {
GetDPHO(ctx context.Context) ([]DPHOData, error)
}
type service struct {
repo Repository
}
func NewService(repo Repository) Service {
return &service{repo: repo}
}
func (s *service) GetDPHO(ctx context.Context) ([]DPHOData, error) {
res, err := s.repo.GetDPHO(ctx)
if err != nil {
return nil, errors.ExternalError().Message("Gagal mengambil referensi DPHO Apotek BPJS").Cause(err).Build()
}
return res, nil
}
@@ -1,7 +0,0 @@
package poli
// PoliData merepresentasikan detail referensi Poli dari API Apotek
type PoliData struct {
KodePoli string `json:"kodepoli"`
NamaPoli string `json:"namapoli"`
}
@@ -1,41 +0,0 @@
package poli
import (
"context"
"encoding/json"
"fmt"
"service/internal/interfaces/bpjs"
)
type Repository interface {
GetPoli(ctx context.Context, param string) ([]PoliData, error)
}
type repository struct {
client bpjs.BpjsClient
}
func NewRepository(client bpjs.BpjsClient) Repository {
return &repository{client: client}
}
func (r *repository) GetPoli(ctx context.Context, param string) ([]PoliData, error) {
endpoint := fmt.Sprintf("referensi/poli/%s", param)
respBytes, err := r.client.DoRequest(ctx, "GET", endpoint, nil)
if err != nil {
return nil, err
}
var result struct {
List []PoliData `json:"list"`
}
if err := json.Unmarshal(respBytes, &result); err != nil {
var directList []PoliData
if err2 := json.Unmarshal(respBytes, &directList); err2 == nil {
return directList, nil
}
return nil, fmt.Errorf("failed to unmarshal apotek poli response: %w", err)
}
return result.List, nil
}
@@ -1,29 +0,0 @@
package poli
import (
"context"
"service/pkg/errors"
)
type Service interface {
GetPoli(ctx context.Context, param string) ([]PoliData, error)
}
type service struct {
repo Repository
}
func NewService(repo Repository) Service {
return &service{repo: repo}
}
func (s *service) GetPoli(ctx context.Context, param string) ([]PoliData, error) {
if param == "" {
return nil, errors.NewValidationError().Message("Parameter pencarian Poli wajib diisi").Build()
}
res, err := s.repo.GetPoli(ctx, param)
if err != nil {
return nil, errors.ExternalError().Message("Gagal mengambil referensi Poli Apotek BPJS").Cause(err).Build()
}
return res, nil
}
-53
View File
@@ -1,53 +0,0 @@
package peserta
// PesertaResponse merepresentasikan wrapper data dari API BPJS VClaim
type PesertaResponse struct {
Peserta PesertaData `json:"peserta"`
}
// PesertaData merepresentasikan entitas detail Peserta BPJS
type PesertaData struct {
NoKartu string `json:"noKartu"`
Nik string `json:"nik"`
Nama string `json:"nama"`
Pisa string `json:"pisa"`
Sex string `json:"sex"`
Umur Umur `json:"umur"`
TglLahir string `json:"tglLahir"`
StatusPeserta StatusPeserta `json:"statusPeserta"`
ProviderUmum ProviderUmum `json:"provUmum"`
JenisPeserta JenisPeserta `json:"jenisPeserta"`
HakKelas HakKelas `json:"hakKelas"`
Informasi Informasi `json:"informasi"`
}
type Umur struct {
UmurSaatPelayanan string `json:"umurSaatPelayanan"`
UmurSekarang string `json:"umurSekarang"`
}
type StatusPeserta struct {
Kode string `json:"kode"`
Keterangan string `json:"keterangan"`
}
type ProviderUmum struct {
KdProvider string `json:"kdProvider"`
NmProvider string `json:"nmProvider"`
}
type JenisPeserta struct {
Kode string `json:"kode"`
Keterangan string `json:"keterangan"`
}
type HakKelas struct {
Kode string `json:"kode"`
Keterangan string `json:"keterangan"`
}
type Informasi struct {
Dinsos string `json:"dinsos"`
ProlanisPRB string `json:"prolanisPRB"`
NoSKTM string `json:"noSKTM"`
}
@@ -1,52 +0,0 @@
package peserta
import (
"context"
"encoding/json"
"fmt"
"service/internal/interfaces/bpjs"
)
type Repository interface {
GetByNoKartu(ctx context.Context, noKartu, tglSEP string) (*PesertaData, error)
GetByNIK(ctx context.Context, nik, tglSEP string) (*PesertaData, error)
}
type repository struct {
client bpjs.BpjsClient
}
func NewRepository(client bpjs.BpjsClient) Repository {
return &repository{client: client}
}
func (r *repository) GetByNoKartu(ctx context.Context, noKartu, tglSEP string) (*PesertaData, error) {
endpoint := fmt.Sprintf("Peserta/nokartu/%s/tglSEP/%s", noKartu, tglSEP)
respBytes, err := r.client.DoRequest(ctx, "GET", endpoint, nil)
if err != nil {
return nil, err
}
var result PesertaResponse
if err := json.Unmarshal(respBytes, &result); err != nil {
return nil, fmt.Errorf("failed to unmarshal peserta response: %w", err)
}
return &result.Peserta, nil
}
func (r *repository) GetByNIK(ctx context.Context, nik, tglSEP string) (*PesertaData, error) {
endpoint := fmt.Sprintf("Peserta/nik/%s/tglSEP/%s", nik, tglSEP)
respBytes, err := r.client.DoRequest(ctx, "GET", endpoint, nil)
if err != nil {
return nil, err
}
var result PesertaResponse
if err := json.Unmarshal(respBytes, &result); err != nil {
return nil, fmt.Errorf("failed to unmarshal peserta response: %w", err)
}
return &result.Peserta, nil
}
-43
View File
@@ -1,43 +0,0 @@
package peserta
import (
"context"
"service/pkg/errors"
)
type Service interface {
GetPesertaByNoKartu(ctx context.Context, noKartu, tglSEP string) (*PesertaData, error)
GetPesertaByNIK(ctx context.Context, nik, tglSEP string) (*PesertaData, error)
}
type service struct {
repo Repository
}
func NewService(repo Repository) Service {
return &service{repo: repo}
}
func (s *service) GetPesertaByNoKartu(ctx context.Context, noKartu, tglSEP string) (*PesertaData, error) {
if noKartu == "" || tglSEP == "" {
return nil, errors.NewValidationError().Message("Nomor Kartu dan Tanggal SEP wajib diisi").Build()
}
res, err := s.repo.GetByNoKartu(ctx, noKartu, tglSEP)
if err != nil {
return nil, errors.ExternalError().Message("Gagal mengambil data peserta BPJS berdasarkan No Kartu").Cause(err).Build()
}
return res, nil
}
func (s *service) GetPesertaByNIK(ctx context.Context, nik, tglSEP string) (*PesertaData, error) {
if nik == "" || tglSEP == "" {
return nil, errors.NewValidationError().Message("NIK dan Tanggal SEP wajib diisi").Build()
}
res, err := s.repo.GetByNIK(ctx, nik, tglSEP)
if err != nil {
return nil, errors.ExternalError().Message("Gagal mengambil data peserta BPJS berdasarkan NIK").Cause(err).Build()
}
return res, nil
}
-136
View File
@@ -1,136 +0,0 @@
package sep
// CreateSEPRequest adalah data payload untuk insert pembuatan SEP.
// Strukturnya mengikuti format yang dibutuhkan oleh API VClaim 2.0.
type CreateSEPRequest struct {
Request struct {
Sep `json:"t_sep"`
} `json:"request"`
}
// Sep adalah detail data untuk pembuatan SEP.
type Sep struct {
NoKartu string `json:"noKartu"`
TglSep string `json:"tglSep"`
PpkPelayanan string `json:"ppkPelayanan"`
JnsPelayanan string `json:"jnsPelayanan"`
KlsRawat KlsRawat `json:"klsRawat"`
NoMR string `json:"noMR"`
Rujukan Rujukan `json:"rujukan"`
Catatan string `json:"catatan"`
DiagAwal string `json:"diagAwal"`
Poli Poli `json:"poli"`
Cob string `json:"cob"`
Katarak string `json:"katarak"`
Jaminan Jaminan `json:"jaminan"`
Tujuan string `json:"tujuan"`
FlagProcedure string `json:"flagProcedure"`
KdPenunjang string `json:"kdPenunjang"`
AssesmentPel string `json:"assesmentPel"`
NoSurat string `json:"noSurat"`
KodeDPJP string `json:"kodeDPJP"`
DpjpLayan string `json:"dpjpLayan"`
NoTelp string `json:"noTelp"`
User string `json:"user"`
}
type KlsRawat struct {
KlsRawatHak string `json:"klsRawatHak"`
KlsRawatNaik string `json:"klsRawatNaik,omitempty"`
Pembiayaan string `json:"pembiayaan,omitempty"`
PenanggungJawab string `json:"penanggungJawab,omitempty"`
}
type Rujukan struct {
AsalRujukan string `json:"asalRujukan"`
TglRujukan string `json:"tglRujukan"`
NoRujukan string `json:"noRujukan"`
PpkRujukan string `json:"ppkRujukan"`
}
type Poli struct {
Tujuan string `json:"tujuan"`
Eksekutif string `json:"eksekutif"`
}
type Jaminan struct {
LakaLantas string `json:"lakaLantas"`
NoLP string `json:"noLP,omitempty"`
Penjamin Penjamin `json:"penjamin,omitempty"`
}
type Penjamin struct {
TglKejadian string `json:"tglKejadian,omitempty"`
Keterangan string `json:"keterangan,omitempty"`
Suplesi struct {
Suplesi string `json:"suplesi"`
NoSepSuplesi string `json:"noSepSuplesi,omitempty"`
LokasiLaka struct {
KdPropinsi string `json:"kdPropinsi"`
KdKabupaten string `json:"kdKabupaten"`
KdKecamatan string `json:"kdKecamatan"`
} `json:"lokasiLaka,omitempty"`
} `json:"suplesi,omitempty"`
}
// CreateSEPResponse adalah data balikan setelah SEP berhasil dicreate.
type CreateSEPResponse struct {
Sep struct {
NoSep string `json:"noSep"`
TglSep string `json:"tglSep"`
Poli string `json:"poli"`
Diagnosa string `json:"diagnosa"`
Catatan string `json:"catatan"`
JnsRawat string `json:"jnsRawat"`
KlsRawat string `json:"klsRawat"`
Penjamin string `json:"penjamin"`
} `json:"sep"`
}
// UpdateSEPRequest adalah data payload untuk update SEP.
type UpdateSEPRequest struct {
Request struct {
Sep struct {
NoSep string `json:"noSep"`
KlsRawat KlsRawat `json:"klsRawat"`
NoMR string `json:"noMR"`
Catatan string `json:"catatan"`
DiagAwal string `json:"diagAwal"`
Poli Poli `json:"poli"`
Eksekutif string `json:"eksekutif"`
Cob string `json:"cob"`
Katarak string `json:"katarak"`
Jaminan Jaminan `json:"jaminan"`
NoTelp string `json:"noTelp"`
User string `json:"user"`
} `json:"t_sep"`
} `json:"request"`
}
// DeleteSEPRequest adalah data payload untuk menghapus SEP.
type DeleteSEPRequest struct {
Request struct {
Sep struct {
NoSep string `json:"noSep"`
User string `json:"user"`
} `json:"t_sep"`
} `json:"request"`
}
// SEPDetailResponse adalah data balikan untuk detail SEP.
type SEPDetailResponse struct {
Catatan string `json:"catatan"`
Diagnosa string `json:"diagnosa"`
JnsPelayanan string `json:"jnsPelayanan"`
KelasRawat string `json:"kelasRawat"`
NoRujukan string `json:"noRujukan"`
NoSep string `json:"noSep"`
Penjamin string `json:"penjamin"`
Peserta struct {
Nama string `json:"nama"`
NoKartu string `json:"noKartu"`
NoMr string `json:"noMr"`
} `json:"peserta"`
Poli string `json:"poli"`
TglSep string `json:"tglSep"`
}
-67
View File
@@ -1,67 +0,0 @@
package sep
import (
"context"
"encoding/json"
"fmt"
"service/internal/interfaces/bpjs"
)
type Repository interface {
CreateSEP(ctx context.Context, req CreateSEPRequest) (*CreateSEPResponse, error)
UpdateSEP(ctx context.Context, req UpdateSEPRequest) (string, error)
DeleteSEP(ctx context.Context, req DeleteSEPRequest) (string, error)
GetSEP(ctx context.Context, noSEP string) (*SEPDetailResponse, error)
}
type repository struct {
client bpjs.BpjsClient
}
func NewRepository(client bpjs.BpjsClient) Repository {
return &repository{client: client}
}
func (r *repository) CreateSEP(ctx context.Context, req CreateSEPRequest) (*CreateSEPResponse, error) {
endpoint := "SEP/2.0/insert"
decryptedBytes, err := r.client.DoRequest(ctx, "POST", endpoint, req)
if err != nil {
return nil, err
}
var result CreateSEPResponse
return &result, json.Unmarshal(decryptedBytes, &result)
}
func (r *repository) UpdateSEP(ctx context.Context, req UpdateSEPRequest) (string, error) {
endpoint := "SEP/2.0/update"
decryptedBytes, err := r.client.DoRequest(ctx, "PUT", endpoint, req)
if err != nil {
return "", err
}
// Response dari update SEP biasanya hanya string nomor SEP itu sendiri
return string(decryptedBytes), nil
}
func (r *repository) DeleteSEP(ctx context.Context, req DeleteSEPRequest) (string, error) {
endpoint := "SEP/2.0/delete"
// BPJS API untuk delete menggunakan POST method dengan payload spesifik
decryptedBytes, err := r.client.DoRequest(ctx, "POST", endpoint, req)
if err != nil {
return "", err
}
// Response dari delete SEP biasanya hanya string "OK" atau pesan konfirmasi
return string(decryptedBytes), nil
}
func (r *repository) GetSEP(ctx context.Context, noSEP string) (*SEPDetailResponse, error) {
endpoint := fmt.Sprintf("SEP/%s", noSEP)
decryptedBytes, err := r.client.DoRequest(ctx, "GET", endpoint, nil)
if err != nil {
return nil, err
}
var result SEPDetailResponse
if err := json.Unmarshal(decryptedBytes, &result); err != nil {
return nil, fmt.Errorf("failed to unmarshal SEP detail response: %w", err)
}
return &result, nil
}
-41
View File
@@ -1,41 +0,0 @@
package sep
import (
"context"
)
type Service interface {
Create(ctx context.Context, req CreateSEPRequest) (*CreateSEPResponse, error)
Update(ctx context.Context, req UpdateSEPRequest) (string, error)
Delete(ctx context.Context, req DeleteSEPRequest) (string, error)
GetDetail(ctx context.Context, noSEP string) (*SEPDetailResponse, error)
}
type service struct {
repo Repository
}
func NewService(repo Repository) Service {
return &service{repo: repo}
}
func (s *service) Create(ctx context.Context, req CreateSEPRequest) (*CreateSEPResponse, error) {
// Anda bisa menambahkan validasi atau logika bisnis di sini sebelum memanggil repository.
return s.repo.CreateSEP(ctx, req)
}
func (s *service) Update(ctx context.Context, req UpdateSEPRequest) (string, error) {
// TODO: Tambahkan validasi untuk request update di sini.
// Contoh: if req.Request.Sep.NoSep == "" { return "", errors.New("nomor SEP wajib diisi") }
return s.repo.UpdateSEP(ctx, req)
}
func (s *service) Delete(ctx context.Context, req DeleteSEPRequest) (string, error) {
// TODO: Tambahkan validasi untuk request delete di sini.
return s.repo.DeleteSEP(ctx, req)
}
func (s *service) GetDetail(ctx context.Context, noSEP string) (*SEPDetailResponse, error) {
// TODO: Tambahkan validasi untuk noSEP di sini.
return s.repo.GetSEP(ctx, noSEP)
}
@@ -1,484 +0,0 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v3.12.4
// source: internal/infrastructure/transport/grpc/proto/permission/v1/permission.proto
package permissionV1
import (
reflect "reflect"
sync "sync"
unsafe "unsafe"
wrappers "github.com/golang/protobuf/ptypes/wrappers"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// Pesan request untuk RPC GetRolePermissionTree.
type GetRolePermissionTreeRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
RoleKeycloak string `protobuf:"bytes,1,opt,name=role_keycloak,json=roleKeycloak,proto3" json:"role_keycloak,omitempty"`
GroupKeycloak []string `protobuf:"bytes,2,rep,name=group_keycloak,json=groupKeycloak,proto3" json:"group_keycloak,omitempty"`
// Menggunakan wrapper BoolValue untuk membedakan antara 'false' dan 'tidak diset'.
ActiveOnly *wrappers.BoolValue `protobuf:"bytes,3,opt,name=active_only,json=activeOnly,proto3" json:"active_only,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GetRolePermissionTreeRequest) Reset() {
*x = GetRolePermissionTreeRequest{}
mi := &file_internal_infrastructure_transport_grpc_proto_permission_v1_permission_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *GetRolePermissionTreeRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetRolePermissionTreeRequest) ProtoMessage() {}
func (x *GetRolePermissionTreeRequest) ProtoReflect() protoreflect.Message {
mi := &file_internal_infrastructure_transport_grpc_proto_permission_v1_permission_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetRolePermissionTreeRequest.ProtoReflect.Descriptor instead.
func (*GetRolePermissionTreeRequest) Descriptor() ([]byte, []int) {
return file_internal_infrastructure_transport_grpc_proto_permission_v1_permission_proto_rawDescGZIP(), []int{0}
}
func (x *GetRolePermissionTreeRequest) GetRoleKeycloak() string {
if x != nil {
return x.RoleKeycloak
}
return ""
}
func (x *GetRolePermissionTreeRequest) GetGroupKeycloak() []string {
if x != nil {
return x.GroupKeycloak
}
return nil
}
func (x *GetRolePermissionTreeRequest) GetActiveOnly() *wrappers.BoolValue {
if x != nil {
return x.ActiveOnly
}
return nil
}
// Pesan response untuk RPC GetRolePermissionTree.
type RolePermissionTreeResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"`
Data *RolePermissionData `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *RolePermissionTreeResponse) Reset() {
*x = RolePermissionTreeResponse{}
mi := &file_internal_infrastructure_transport_grpc_proto_permission_v1_permission_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *RolePermissionTreeResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*RolePermissionTreeResponse) ProtoMessage() {}
func (x *RolePermissionTreeResponse) ProtoReflect() protoreflect.Message {
mi := &file_internal_infrastructure_transport_grpc_proto_permission_v1_permission_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use RolePermissionTreeResponse.ProtoReflect.Descriptor instead.
func (*RolePermissionTreeResponse) Descriptor() ([]byte, []int) {
return file_internal_infrastructure_transport_grpc_proto_permission_v1_permission_proto_rawDescGZIP(), []int{1}
}
func (x *RolePermissionTreeResponse) GetSuccess() bool {
if x != nil {
return x.Success
}
return false
}
func (x *RolePermissionTreeResponse) GetData() *RolePermissionData {
if x != nil {
return x.Data
}
return nil
}
type RolePermissionData struct {
state protoimpl.MessageState `protogen:"open.v1"`
Role string `protobuf:"bytes,1,opt,name=role,proto3" json:"role,omitempty"`
Group []string `protobuf:"bytes,2,rep,name=group,proto3" json:"group,omitempty"`
Access []*AccessTreeItem `protobuf:"bytes,3,rep,name=access,proto3" json:"access,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *RolePermissionData) Reset() {
*x = RolePermissionData{}
mi := &file_internal_infrastructure_transport_grpc_proto_permission_v1_permission_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *RolePermissionData) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*RolePermissionData) ProtoMessage() {}
func (x *RolePermissionData) ProtoReflect() protoreflect.Message {
mi := &file_internal_infrastructure_transport_grpc_proto_permission_v1_permission_proto_msgTypes[2]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use RolePermissionData.ProtoReflect.Descriptor instead.
func (*RolePermissionData) Descriptor() ([]byte, []int) {
return file_internal_infrastructure_transport_grpc_proto_permission_v1_permission_proto_rawDescGZIP(), []int{2}
}
func (x *RolePermissionData) GetRole() string {
if x != nil {
return x.Role
}
return ""
}
func (x *RolePermissionData) GetGroup() []string {
if x != nil {
return x.Group
}
return nil
}
func (x *RolePermissionData) GetAccess() []*AccessTreeItem {
if x != nil {
return x.Access
}
return nil
}
type AccessTreeItem struct {
state protoimpl.MessageState `protogen:"open.v1"`
Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
Icon string `protobuf:"bytes,3,opt,name=icon,proto3" json:"icon,omitempty"`
Url string `protobuf:"bytes,4,opt,name=url,proto3" json:"url,omitempty"`
Level int32 `protobuf:"varint,5,opt,name=level,proto3" json:"level,omitempty"`
Sort int32 `protobuf:"varint,6,opt,name=sort,proto3" json:"sort,omitempty"`
Active bool `protobuf:"varint,7,opt,name=active,proto3" json:"active,omitempty"`
Permission *Permission `protobuf:"bytes,8,opt,name=permission,proto3,oneof" json:"permission,omitempty"`
Children []*AccessTreeItem `protobuf:"bytes,9,rep,name=children,proto3" json:"children,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *AccessTreeItem) Reset() {
*x = AccessTreeItem{}
mi := &file_internal_infrastructure_transport_grpc_proto_permission_v1_permission_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *AccessTreeItem) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*AccessTreeItem) ProtoMessage() {}
func (x *AccessTreeItem) ProtoReflect() protoreflect.Message {
mi := &file_internal_infrastructure_transport_grpc_proto_permission_v1_permission_proto_msgTypes[3]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use AccessTreeItem.ProtoReflect.Descriptor instead.
func (*AccessTreeItem) Descriptor() ([]byte, []int) {
return file_internal_infrastructure_transport_grpc_proto_permission_v1_permission_proto_rawDescGZIP(), []int{3}
}
func (x *AccessTreeItem) GetId() int64 {
if x != nil {
return x.Id
}
return 0
}
func (x *AccessTreeItem) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *AccessTreeItem) GetIcon() string {
if x != nil {
return x.Icon
}
return ""
}
func (x *AccessTreeItem) GetUrl() string {
if x != nil {
return x.Url
}
return ""
}
func (x *AccessTreeItem) GetLevel() int32 {
if x != nil {
return x.Level
}
return 0
}
func (x *AccessTreeItem) GetSort() int32 {
if x != nil {
return x.Sort
}
return 0
}
func (x *AccessTreeItem) GetActive() bool {
if x != nil {
return x.Active
}
return false
}
func (x *AccessTreeItem) GetPermission() *Permission {
if x != nil {
return x.Permission
}
return nil
}
func (x *AccessTreeItem) GetChildren() []*AccessTreeItem {
if x != nil {
return x.Children
}
return nil
}
type Permission struct {
state protoimpl.MessageState `protogen:"open.v1"`
Create bool `protobuf:"varint,1,opt,name=create,proto3" json:"create,omitempty"`
Read bool `protobuf:"varint,2,opt,name=read,proto3" json:"read,omitempty"`
Update bool `protobuf:"varint,3,opt,name=update,proto3" json:"update,omitempty"`
Delete bool `protobuf:"varint,4,opt,name=delete,proto3" json:"delete,omitempty"`
Disable bool `protobuf:"varint,5,opt,name=disable,proto3" json:"disable,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *Permission) Reset() {
*x = Permission{}
mi := &file_internal_infrastructure_transport_grpc_proto_permission_v1_permission_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *Permission) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Permission) ProtoMessage() {}
func (x *Permission) ProtoReflect() protoreflect.Message {
mi := &file_internal_infrastructure_transport_grpc_proto_permission_v1_permission_proto_msgTypes[4]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Permission.ProtoReflect.Descriptor instead.
func (*Permission) Descriptor() ([]byte, []int) {
return file_internal_infrastructure_transport_grpc_proto_permission_v1_permission_proto_rawDescGZIP(), []int{4}
}
func (x *Permission) GetCreate() bool {
if x != nil {
return x.Create
}
return false
}
func (x *Permission) GetRead() bool {
if x != nil {
return x.Read
}
return false
}
func (x *Permission) GetUpdate() bool {
if x != nil {
return x.Update
}
return false
}
func (x *Permission) GetDelete() bool {
if x != nil {
return x.Delete
}
return false
}
func (x *Permission) GetDisable() bool {
if x != nil {
return x.Disable
}
return false
}
var File_internal_infrastructure_transport_grpc_proto_permission_v1_permission_proto protoreflect.FileDescriptor
const file_internal_infrastructure_transport_grpc_proto_permission_v1_permission_proto_rawDesc = "" +
"\n" +
"Kinternal/infrastructure/transport/grpc/proto/permission/v1/permission.proto\x12\rpermission.v1\x1a\x1egoogle/protobuf/wrappers.proto\"\xa7\x01\n" +
"\x1cGetRolePermissionTreeRequest\x12#\n" +
"\rrole_keycloak\x18\x01 \x01(\tR\froleKeycloak\x12%\n" +
"\x0egroup_keycloak\x18\x02 \x03(\tR\rgroupKeycloak\x12;\n" +
"\vactive_only\x18\x03 \x01(\v2\x1a.google.protobuf.BoolValueR\n" +
"activeOnly\"m\n" +
"\x1aRolePermissionTreeResponse\x12\x18\n" +
"\asuccess\x18\x01 \x01(\bR\asuccess\x125\n" +
"\x04data\x18\x02 \x01(\v2!.permission.v1.RolePermissionDataR\x04data\"u\n" +
"\x12RolePermissionData\x12\x12\n" +
"\x04role\x18\x01 \x01(\tR\x04role\x12\x14\n" +
"\x05group\x18\x02 \x03(\tR\x05group\x125\n" +
"\x06access\x18\x03 \x03(\v2\x1d.permission.v1.AccessTreeItemR\x06access\"\xa6\x02\n" +
"\x0eAccessTreeItem\x12\x0e\n" +
"\x02id\x18\x01 \x01(\x03R\x02id\x12\x12\n" +
"\x04name\x18\x02 \x01(\tR\x04name\x12\x12\n" +
"\x04icon\x18\x03 \x01(\tR\x04icon\x12\x10\n" +
"\x03url\x18\x04 \x01(\tR\x03url\x12\x14\n" +
"\x05level\x18\x05 \x01(\x05R\x05level\x12\x12\n" +
"\x04sort\x18\x06 \x01(\x05R\x04sort\x12\x16\n" +
"\x06active\x18\a \x01(\bR\x06active\x12>\n" +
"\n" +
"permission\x18\b \x01(\v2\x19.permission.v1.PermissionH\x00R\n" +
"permission\x88\x01\x01\x129\n" +
"\bchildren\x18\t \x03(\v2\x1d.permission.v1.AccessTreeItemR\bchildrenB\r\n" +
"\v_permission\"\x82\x01\n" +
"\n" +
"Permission\x12\x16\n" +
"\x06create\x18\x01 \x01(\bR\x06create\x12\x12\n" +
"\x04read\x18\x02 \x01(\bR\x04read\x12\x16\n" +
"\x06update\x18\x03 \x01(\bR\x06update\x12\x16\n" +
"\x06delete\x18\x04 \x01(\bR\x06delete\x12\x18\n" +
"\adisable\x18\x05 \x01(\bR\adisable2\x87\x01\n" +
"\x14RolPermissionService\x12o\n" +
"\x15GetRolePermissionTree\x12+.permission.v1.GetRolePermissionTreeRequest\x1a).permission.v1.RolePermissionTreeResponseBGZEinternal/infrastructure/transport/grpc/gen/permission/v1;permissionV1b\x06proto3"
var (
file_internal_infrastructure_transport_grpc_proto_permission_v1_permission_proto_rawDescOnce sync.Once
file_internal_infrastructure_transport_grpc_proto_permission_v1_permission_proto_rawDescData []byte
)
func file_internal_infrastructure_transport_grpc_proto_permission_v1_permission_proto_rawDescGZIP() []byte {
file_internal_infrastructure_transport_grpc_proto_permission_v1_permission_proto_rawDescOnce.Do(func() {
file_internal_infrastructure_transport_grpc_proto_permission_v1_permission_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_internal_infrastructure_transport_grpc_proto_permission_v1_permission_proto_rawDesc), len(file_internal_infrastructure_transport_grpc_proto_permission_v1_permission_proto_rawDesc)))
})
return file_internal_infrastructure_transport_grpc_proto_permission_v1_permission_proto_rawDescData
}
var file_internal_infrastructure_transport_grpc_proto_permission_v1_permission_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
var file_internal_infrastructure_transport_grpc_proto_permission_v1_permission_proto_goTypes = []any{
(*GetRolePermissionTreeRequest)(nil), // 0: permission.v1.GetRolePermissionTreeRequest
(*RolePermissionTreeResponse)(nil), // 1: permission.v1.RolePermissionTreeResponse
(*RolePermissionData)(nil), // 2: permission.v1.RolePermissionData
(*AccessTreeItem)(nil), // 3: permission.v1.AccessTreeItem
(*Permission)(nil), // 4: permission.v1.Permission
(*wrappers.BoolValue)(nil), // 5: google.protobuf.BoolValue
}
var file_internal_infrastructure_transport_grpc_proto_permission_v1_permission_proto_depIdxs = []int32{
5, // 0: permission.v1.GetRolePermissionTreeRequest.active_only:type_name -> google.protobuf.BoolValue
2, // 1: permission.v1.RolePermissionTreeResponse.data:type_name -> permission.v1.RolePermissionData
3, // 2: permission.v1.RolePermissionData.access:type_name -> permission.v1.AccessTreeItem
4, // 3: permission.v1.AccessTreeItem.permission:type_name -> permission.v1.Permission
3, // 4: permission.v1.AccessTreeItem.children:type_name -> permission.v1.AccessTreeItem
0, // 5: permission.v1.RolPermissionService.GetRolePermissionTree:input_type -> permission.v1.GetRolePermissionTreeRequest
1, // 6: permission.v1.RolPermissionService.GetRolePermissionTree:output_type -> permission.v1.RolePermissionTreeResponse
6, // [6:7] is the sub-list for method output_type
5, // [5:6] is the sub-list for method input_type
5, // [5:5] is the sub-list for extension type_name
5, // [5:5] is the sub-list for extension extendee
0, // [0:5] is the sub-list for field type_name
}
func init() { file_internal_infrastructure_transport_grpc_proto_permission_v1_permission_proto_init() }
func file_internal_infrastructure_transport_grpc_proto_permission_v1_permission_proto_init() {
if File_internal_infrastructure_transport_grpc_proto_permission_v1_permission_proto != nil {
return
}
file_internal_infrastructure_transport_grpc_proto_permission_v1_permission_proto_msgTypes[3].OneofWrappers = []any{}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_internal_infrastructure_transport_grpc_proto_permission_v1_permission_proto_rawDesc), len(file_internal_infrastructure_transport_grpc_proto_permission_v1_permission_proto_rawDesc)),
NumEnums: 0,
NumMessages: 5,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_internal_infrastructure_transport_grpc_proto_permission_v1_permission_proto_goTypes,
DependencyIndexes: file_internal_infrastructure_transport_grpc_proto_permission_v1_permission_proto_depIdxs,
MessageInfos: file_internal_infrastructure_transport_grpc_proto_permission_v1_permission_proto_msgTypes,
}.Build()
File_internal_infrastructure_transport_grpc_proto_permission_v1_permission_proto = out.File
file_internal_infrastructure_transport_grpc_proto_permission_v1_permission_proto_goTypes = nil
file_internal_infrastructure_transport_grpc_proto_permission_v1_permission_proto_depIdxs = nil
}
@@ -1,128 +0,0 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.6.0
// - protoc v3.12.4
// source: internal/infrastructure/transport/grpc/proto/permission/v1/permission.proto
package permissionV1
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.64.0 or later.
const _ = grpc.SupportPackageIsVersion9
const (
RolPermissionService_GetRolePermissionTree_FullMethodName = "/permission.v1.RolPermissionService/GetRolePermissionTree"
)
// RolPermissionServiceClient is the client API for RolPermissionService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
//
// Definisi service.
type RolPermissionServiceClient interface {
// Mengambil permission tree untuk role dan/atau group tertentu.
GetRolePermissionTree(ctx context.Context, in *GetRolePermissionTreeRequest, opts ...grpc.CallOption) (*RolePermissionTreeResponse, error)
}
type rolPermissionServiceClient struct {
cc grpc.ClientConnInterface
}
func NewRolPermissionServiceClient(cc grpc.ClientConnInterface) RolPermissionServiceClient {
return &rolPermissionServiceClient{cc}
}
func (c *rolPermissionServiceClient) GetRolePermissionTree(ctx context.Context, in *GetRolePermissionTreeRequest, opts ...grpc.CallOption) (*RolePermissionTreeResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(RolePermissionTreeResponse)
err := c.cc.Invoke(ctx, RolPermissionService_GetRolePermissionTree_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// RolPermissionServiceServer is the server API for RolPermissionService service.
// All implementations must embed UnimplementedRolPermissionServiceServer
// for forward compatibility.
//
// Definisi service.
type RolPermissionServiceServer interface {
// Mengambil permission tree untuk role dan/atau group tertentu.
GetRolePermissionTree(context.Context, *GetRolePermissionTreeRequest) (*RolePermissionTreeResponse, error)
mustEmbedUnimplementedRolPermissionServiceServer()
}
// UnimplementedRolPermissionServiceServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedRolPermissionServiceServer struct{}
func (UnimplementedRolPermissionServiceServer) GetRolePermissionTree(context.Context, *GetRolePermissionTreeRequest) (*RolePermissionTreeResponse, error) {
return nil, status.Error(codes.Unimplemented, "method GetRolePermissionTree not implemented")
}
func (UnimplementedRolPermissionServiceServer) mustEmbedUnimplementedRolPermissionServiceServer() {}
func (UnimplementedRolPermissionServiceServer) testEmbeddedByValue() {}
// UnsafeRolPermissionServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to RolPermissionServiceServer will
// result in compilation errors.
type UnsafeRolPermissionServiceServer interface {
mustEmbedUnimplementedRolPermissionServiceServer()
}
func RegisterRolPermissionServiceServer(s grpc.ServiceRegistrar, srv RolPermissionServiceServer) {
// If the following call panics, it indicates UnimplementedRolPermissionServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&RolPermissionService_ServiceDesc, srv)
}
func _RolPermissionService_GetRolePermissionTree_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetRolePermissionTreeRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(RolPermissionServiceServer).GetRolePermissionTree(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: RolPermissionService_GetRolePermissionTree_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(RolPermissionServiceServer).GetRolePermissionTree(ctx, req.(*GetRolePermissionTreeRequest))
}
return interceptor(ctx, in, info, handler)
}
// RolPermissionService_ServiceDesc is the grpc.ServiceDesc for RolPermissionService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var RolPermissionService_ServiceDesc = grpc.ServiceDesc{
ServiceName: "permission.v1.RolPermissionService",
HandlerType: (*RolPermissionServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "GetRolePermissionTree",
Handler: _RolPermissionService_GetRolePermissionTree_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "internal/infrastructure/transport/grpc/proto/permission/v1/permission.proto",
}
@@ -1,32 +0,0 @@
package handlers
import (
"service/pkg/errors"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// / handleServiceError mengkonversi error dari service layer ke gRPC status error
func handleServiceError(err error) error {
appErr := errors.FromError(err)
if appErr == nil {
return status.Error(codes.Internal, "internal server error")
}
// Mapping kategori error ke gRPC codes
switch appErr.Category() {
case errors.CategoryValidation:
return status.Error(codes.InvalidArgument, appErr.Error())
case errors.CategoryNotFound:
return status.Error(codes.NotFound, appErr.Error())
case errors.CategoryConflict:
return status.Error(codes.AlreadyExists, appErr.Error())
case errors.CategoryUnauthorized:
return status.Error(codes.Unauthenticated, appErr.Error())
case errors.CategoryForbidden:
return status.Error(codes.PermissionDenied, appErr.Error())
default:
return status.Error(codes.Internal, appErr.Error())
}
}
@@ -1,50 +0,0 @@
package handlers
import (
"context"
"service/internal/infrastructure/transport/grpc/mappers"
"service/internal/master/role/permission"
// Asumsi path ke proto-generated file
permissionV1 "service/internal/infrastructure/transport/grpc/gen/permission/v1"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// PermissionHandler mengimplementasikan gRPC service untuk role permission.
type PermissionHandler struct {
permissionV1.UnimplementedRolPermissionServiceServer
service permission.Service
}
// NewPermissionHandler membuat instance baru dari PermissionHandler.
func NewPermissionHandler(service permission.Service) *PermissionHandler {
return &PermissionHandler{
service: service,
}
}
// GetRolePermissionTree adalah implementasi dari RPC.
func (h *PermissionHandler) GetRolePermissionTree(ctx context.Context, req *permissionV1.GetRolePermissionTreeRequest) (*permissionV1.RolePermissionTreeResponse, error) {
if req == nil {
return nil, status.Error(codes.InvalidArgument, "request cannot be nil")
}
// Konversi wrapper BoolValue ke pointer *bool yang diharapkan oleh service
var activeOnly *bool
if val := req.GetActiveOnly(); val != nil {
b := val.GetValue()
activeOnly = &b
}
tree, err := h.service.GetRolePermissionTree(ctx, req.GetRoleKeycloak(), req.GetGroupKeycloak(), activeOnly)
if err != nil {
return nil, handleServiceError(err)
}
// Menggunakan mapper untuk mengubah hasil dari service ke format proto
protoResponse := mappers.MapRolePermissionTreeToProto(tree)
return protoResponse, nil
}
@@ -1,18 +0,0 @@
package mappers
import "fmt"
func convertToString(value interface{}) string {
if value == nil {
return ""
}
switch v := value.(type) {
case string:
return v
case int, int64, float64:
return fmt.Sprintf("%v", v)
default:
return fmt.Sprintf("%v", v)
}
}
@@ -1,55 +0,0 @@
package mappers
import (
permissionV1 "service/internal/infrastructure/transport/grpc/gen/permission/v1"
"service/internal/master/role/permission"
)
// mapAccessTreeToProto secara rekursif mengubah tree dari service ke format proto.
func mapAccessTreeToProto(serviceAccess []*permission.AccessTreeItem) []*permissionV1.AccessTreeItem {
if serviceAccess == nil {
return nil
}
protoAccess := make([]*permissionV1.AccessTreeItem, len(serviceAccess))
for i, item := range serviceAccess {
protoItem := &permissionV1.AccessTreeItem{
Id: item.ID,
Name: item.Name,
Icon: item.Icon,
Url: item.URL,
Level: int32(item.Level),
Sort: int32(item.Sort),
Active: item.Active,
Children: mapAccessTreeToProto(item.Children), // Panggilan rekursif
}
if item.Permission != nil {
protoItem.Permission = &permissionV1.Permission{
Create: item.Permission.Create,
Read: item.Permission.Read,
Update: item.Permission.Update,
Delete: item.Permission.Delete,
Disable: item.Permission.Disable,
}
}
protoAccess[i] = protoItem
}
return protoAccess
}
// MapRolePermissionTreeToProto adalah fungsi utama untuk mengubah seluruh respons tree.
func MapRolePermissionTreeToProto(serviceResponse *permission.RolePermissionTreeResponse) *permissionV1.RolePermissionTreeResponse {
if serviceResponse == nil {
return nil
}
return &permissionV1.RolePermissionTreeResponse{
Success: serviceResponse.Success,
Data: &permissionV1.RolePermissionData{
Role: serviceResponse.Data.Role,
// Group: serviceResponse.Data.Group,
Access: mapAccessTreeToProto(serviceResponse.Data.Access),
},
}
}
@@ -1,83 +0,0 @@
syntax = "proto3";
package master.v1;
// Path should be relative to the module root.
// The package name is specified after the semicolon.
option go_package = "internal/infrastructure/transport/grpc/gen/master/role/master/v1;masterV1";
import "google/protobuf/wrappers.proto";
import "google/protobuf/timestamp.proto";
import "google/protobuf/empty.proto";
// Service definition for RoleAccessRolMaster.
service RoleAccessRolMasterService {
// Get a single RoleAccessRolMaster by its ID.
rpc GetRoleAccessRolMaster(GetRoleAccessRolMasterRequest) returns (RoleAccessRolMasterResponse);
// Get a list of RoleAccessRolMasters with pagination and filtering.
rpc ListRoleAccessRolMasters(ListRoleAccessRolMastersRequest) returns (ListRoleAccessRolMastersResponse);
// Create a new RoleAccessRolMaster.
rpc CreateRoleAccessRolMaster(CreateRoleAccessRolMasterRequest) returns (RoleAccessRolMasterResponse);
// Update an existing RoleAccessRolMaster.
rpc UpdateRoleAccessRolMaster(UpdateRoleAccessRolMasterRequest) returns (RoleAccessRolMasterResponse);
// Delete a RoleAccessRolMaster by its ID.
rpc DeleteRoleAccessRolMaster(DeleteRoleAccessRolMasterRequest) returns (google.protobuf.Empty);
}
// The main message representing a RoleAccessRolMaster.
message RoleAccessRolMaster {
string id = 1;
optional string name = 2;
optional bool active = 3;
optional google.protobuf.Timestamp created_at = 4;
optional google.protobuf.Timestamp updated_at = 5;
optional string s_e_l_e_c_t = 6;
}
// --- Request/Response Messages ---
message GetRoleAccessRolMasterRequest {
int64 id = 1;
}
message RoleAccessRolMasterResponse {
RoleAccessRolMaster data = 1;
}
message ListRoleAccessRolMastersRequest {
int32 page = 1;
int32 page_size = 2;
// Add filter fields here if needed
}
message ListRoleAccessRolMastersResponse {
repeated RoleAccessRolMaster data = 1;
int64 total_items = 2;
}
message CreateRoleAccessRolMasterRequest {
string id = 1;
string name = 2;
bool active = 3;
google.protobuf.Timestamp created_at = 4;
google.protobuf.Timestamp updated_at = 5;
string s_e_l_e_c_t = 6;
}
message UpdateRoleAccessRolMasterRequest {
int64 id = 1;
optional string id = 2;
optional string name = 3;
optional bool active = 4;
optional google.protobuf.Timestamp created_at = 5;
optional google.protobuf.Timestamp updated_at = 6;
optional string s_e_l_e_c_t = 7;
}
message DeleteRoleAccessRolMasterRequest {
int64 id = 1;
}
@@ -1,56 +0,0 @@
syntax = "proto3";
package permission.v1;
// Opsi go_package ini sangat penting.
// Path harus relatif terhadap root modul (tanpa nama modul "service-general").
// Nama paket eksplisit (permissionV1) ditambahkan setelah titik koma.
option go_package = "internal/infrastructure/transport/grpc/gen/permission/v1;permissionV1";
import "google/protobuf/wrappers.proto";
// Definisi service.
service RolPermissionService {
// Mengambil permission tree untuk role dan/atau group tertentu.
rpc GetRolePermissionTree(GetRolePermissionTreeRequest) returns (RolePermissionTreeResponse);
}
// Pesan request untuk RPC GetRolePermissionTree.
message GetRolePermissionTreeRequest {
string role_keycloak = 1;
repeated string group_keycloak = 2;
// Menggunakan wrapper BoolValue untuk membedakan antara 'false' dan 'tidak diset'.
google.protobuf.BoolValue active_only = 3;
}
// Pesan response untuk RPC GetRolePermissionTree.
message RolePermissionTreeResponse {
bool success = 1;
RolePermissionData data = 2;
}
message RolePermissionData {
string role = 1;
repeated string group = 2;
repeated AccessTreeItem access = 3;
}
message AccessTreeItem {
int64 id = 1;
string name = 2;
string icon = 3;
string url = 4;
int32 level = 5;
int32 sort = 6;
bool active = 7;
optional Permission permission = 8;
repeated AccessTreeItem children = 9;
}
message Permission {
bool create = 1;
bool read = 2;
bool update = 3;
bool delete = 4;
bool disable = 5;
}
@@ -1,64 +0,0 @@
syntax = "proto3";
package auth;
// PERBAIKAN: Tambahkan 'transport' dan hapus suffix '/auth' agar sesuai direktori import
option go_package = "gen/auth/v1;auth";
message LoginRequest {
string username = 1;
string password = 2;
}
message LoginResponse {
string token = 1;
string expires_at = 2;
User user = 3;
}
message RegisterRequest {
string username = 1;
string email = 2;
string password = 3;
string full_name = 4;
string role = 5;
}
message RegisterResponse {
string id = 1;
string username = 2;
string email = 3;
string full_name = 4;
}
message ValidateTokenRequest {
string token = 1;
}
message ValidateTokenResponse {
bool valid = 1;
User user = 2;
}
message LogoutRequest {
string token = 1;
}
message LogoutResponse {
bool success = 1;
}
message User {
string id = 1;
string username = 2;
string email = 3;
string full_name = 4;
string role = 5;
}
service AuthService {
rpc Register (RegisterRequest) returns (RegisterResponse);
rpc Login(LoginRequest) returns (LoginResponse);
rpc ValidateToken (ValidateTokenRequest) returns (ValidateTokenResponse);
rpc Logout (LogoutRequest) returns (LogoutResponse);
}
@@ -4,18 +4,15 @@ import (
"fmt"
"net"
"service/internal/infrastructure/config"
"service/internal/infrastructure/transport/grpc/handlers"
"service/pkg/logger"
// Import generated proto files. Asumsi path ini benar.
permissionV1 "service/internal/infrastructure/transport/grpc/gen/permission/v1"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
)
type ServiceRegistry struct {
PermissionHandler *handlers.PermissionHandler
// PermissionHandler *handlers.PermissionHandler
}
// GRPCServer membungkus instance grpc.Server.
@@ -30,10 +27,10 @@ func NewGRPCServer(config *config.ServerGRPCConfig, registry *ServiceRegistry) *
srv := grpc.NewServer()
// Daftarkan semua service yang ada di registry
if registry.PermissionHandler != nil {
permissionV1.RegisterRolPermissionServiceServer(srv, registry.PermissionHandler)
logger.Default().Info("Registered RolPermission gRPC service")
}
// if registry.PermissionHandler != nil {
// permissionV1.RegisterRolPermissionServiceServer(srv, registry.PermissionHandler)
// logger.Default().Info("Registered RolPermission gRPC service")
// }
// Aktifkan reflection agar bisa di-debug dengan tools seperti grpcurl
reflection.Register(srv)
@@ -1,38 +0,0 @@
package antrol
import (
"net/http"
antrolService "service/internal/bpjs/antrol/reference"
"service/pkg/errors"
"service/pkg/response"
"github.com/gin-gonic/gin"
)
type AntrolHandler struct {
service antrolService.Service
}
func NewAntrolHandler(service antrolService.Service) *AntrolHandler {
return &AntrolHandler{service: service}
}
func (h *AntrolHandler) RegisterRoutes(router *gin.RouterGroup) {
group := router.Group("/bpjs/antrol/reference")
{
group.GET("/poli", h.GetRefPoli)
}
}
func (h *AntrolHandler) GetRefPoli(c *gin.Context) {
ctx := c.Request.Context()
res, err := h.service.GetRefPoli(ctx)
if err != nil {
appErr := errors.FromError(err)
response.Error(c, appErr.HTTPStatus(), appErr.Error(), appErr.Metadata())
return
}
response.Success(c, http.StatusOK, "Successfully retrieved BPJS Antrol Poli reference", res)
}
@@ -1,100 +0,0 @@
package aplicare
import (
"net/http"
"strconv"
bedService "service/internal/bpjs/aplicare/bed"
"service/pkg/errors"
"service/pkg/response"
"github.com/gin-gonic/gin"
)
type BedHandler struct {
service bedService.Service
}
func NewBedHandler(service bedService.Service) *BedHandler {
return &BedHandler{service: service}
}
func (h *BedHandler) RegisterRoutes(router *gin.RouterGroup) {
group := router.Group("/bpjs/aplicare/bed")
{
group.GET("/:kdppk/:start/:limit", h.GetBedList)
group.POST("/:kdppk", h.CreateBed)
group.PUT("/:kdppk", h.UpdateBed)
group.DELETE("/:kdppk", h.DeleteBed)
}
}
func (h *BedHandler) GetBedList(c *gin.Context) {
kdPpk := c.Param("kdppk")
start, _ := strconv.Atoi(c.Param("start"))
limit, _ := strconv.Atoi(c.Param("limit"))
ctx := c.Request.Context()
res, err := h.service.GetBedList(ctx, kdPpk, start, limit)
if err != nil {
appErr := errors.FromError(err)
response.Error(c, appErr.HTTPStatus(), appErr.Error(), appErr.Metadata())
return
}
response.Success(c, http.StatusOK, "Successfully retrieved BPJS Aplicares bed list", res)
}
func (h *BedHandler) CreateBed(c *gin.Context) {
kdPpk := c.Param("kdppk")
var req bedService.BedData
if err := c.ShouldBindJSON(&req); err != nil {
response.Error(c, http.StatusBadRequest, "Invalid request body", err.Error())
return
}
ctx := c.Request.Context()
if err := h.service.CreateBed(ctx, kdPpk, req); err != nil {
appErr := errors.FromError(err)
response.Error(c, appErr.HTTPStatus(), appErr.Error(), appErr.Metadata())
return
}
response.Success(c, http.StatusCreated, "Successfully created BPJS Aplicares bed data", nil)
}
func (h *BedHandler) UpdateBed(c *gin.Context) {
kdPpk := c.Param("kdppk")
var req bedService.BedData
if err := c.ShouldBindJSON(&req); err != nil {
response.Error(c, http.StatusBadRequest, "Invalid request body", err.Error())
return
}
ctx := c.Request.Context()
if err := h.service.UpdateBed(ctx, kdPpk, req); err != nil {
appErr := errors.FromError(err)
response.Error(c, appErr.HTTPStatus(), appErr.Error(), appErr.Metadata())
return
}
response.Success(c, http.StatusOK, "Successfully updated BPJS Aplicares bed data", nil)
}
func (h *BedHandler) DeleteBed(c *gin.Context) {
kdPpk := c.Param("kdppk")
var req bedService.BedDeletePayload
if err := c.ShouldBindJSON(&req); err != nil {
response.Error(c, http.StatusBadRequest, "Invalid request body", err.Error())
return
}
ctx := c.Request.Context()
if err := h.service.DeleteBed(ctx, kdPpk, req); err != nil {
appErr := errors.FromError(err)
response.Error(c, appErr.HTTPStatus(), appErr.Error(), appErr.Metadata())
return
}
response.Success(c, http.StatusOK, "Successfully deleted BPJS Aplicares bed data", nil)
}
@@ -1,53 +0,0 @@
package apotek
import (
"net/http"
dphoService "service/internal/bpjs/apotek/reference/dpho"
poliService "service/internal/bpjs/apotek/reference/poli"
"service/pkg/errors"
"service/pkg/response"
"github.com/gin-gonic/gin"
)
type ReferenceHandler struct {
dphoService dphoService.Service
poliService poliService.Service
}
func NewReferenceHandler(dpho dphoService.Service, poli poliService.Service) *ReferenceHandler {
return &ReferenceHandler{
dphoService: dpho,
poliService: poli,
}
}
func (h *ReferenceHandler) RegisterRoutes(router *gin.RouterGroup) {
group := router.Group("/bpjs/apotek/reference")
{
group.GET("/dpho", h.GetDPHO)
group.GET("/poli/:param", h.GetPoli)
}
}
func (h *ReferenceHandler) GetDPHO(c *gin.Context) {
res, err := h.dphoService.GetDPHO(c.Request.Context())
if err != nil {
appErr := errors.FromError(err)
response.Error(c, appErr.HTTPStatus(), appErr.Error(), appErr.Metadata())
return
}
response.Success(c, http.StatusOK, "Successfully retrieved BPJS Apotek DPHO reference", res)
}
func (h *ReferenceHandler) GetPoli(c *gin.Context) {
param := c.Param("param")
res, err := h.poliService.GetPoli(c.Request.Context(), param)
if err != nil {
appErr := errors.FromError(err)
response.Error(c, appErr.HTTPStatus(), appErr.Error(), appErr.Metadata())
return
}
response.Success(c, http.StatusOK, "Successfully retrieved BPJS Apotek Poli reference", res)
}
@@ -1,64 +0,0 @@
package vclaim
import (
"net/http"
"time"
pesertaService "service/internal/bpjs/vclaim/peserta"
"service/pkg/errors"
"service/pkg/response"
"github.com/gin-gonic/gin"
)
type PesertaHandler struct {
service pesertaService.Service
}
func NewPesertaHandler(service pesertaService.Service) *PesertaHandler {
return &PesertaHandler{service: service}
}
func (h *PesertaHandler) RegisterRoutes(router *gin.RouterGroup) {
group := router.Group("/bpjs/vclaim/peserta")
{
group.GET("/nik/:nik", h.GetPesertaByNIK)
group.GET("/nokartu/:nokartu", h.GetPesertaByNoKartu)
}
}
func (h *PesertaHandler) GetPesertaByNIK(c *gin.Context) {
nik := c.Param("nik")
tglSEP := c.Query("tgl_sep")
if tglSEP == "" {
tglSEP = time.Now().Format("2006-01-02")
}
ctx := c.Request.Context()
res, err := h.service.GetPesertaByNIK(ctx, nik, tglSEP)
if err != nil {
appErr := errors.FromError(err)
response.Error(c, appErr.HTTPStatus(), appErr.Error(), appErr.Metadata())
return
}
response.Success(c, http.StatusOK, "Successfully retrieved participant data by NIK", res)
}
func (h *PesertaHandler) GetPesertaByNoKartu(c *gin.Context) {
noKartu := c.Param("nokartu")
tglSEP := c.Query("tgl_sep")
if tglSEP == "" {
tglSEP = time.Now().Format("2006-01-02")
}
ctx := c.Request.Context()
res, err := h.service.GetPesertaByNoKartu(ctx, noKartu, tglSEP)
if err != nil {
appErr := errors.FromError(err)
response.Error(c, appErr.HTTPStatus(), appErr.Error(), appErr.Metadata())
return
}
response.Success(c, http.StatusOK, "Successfully retrieved participant data by card number", res)
}
@@ -1,105 +0,0 @@
package vclaim
import (
"net/http"
sepService "service/internal/bpjs/vclaim/sep"
"service/pkg/errors"
"service/pkg/response"
"github.com/gin-gonic/gin"
)
// SepHandler menangani semua request HTTP terkait VClaim.
type SepHandler struct {
sepService sepService.Service
}
// NewSepHandler membuat instance SepHandler baru.
func NewSepHandler(sepService sepService.Service) *SepHandler {
return &SepHandler{
sepService: sepService,
}
}
// RegisterRoutes mendaftarkan semua rute untuk BPJS VClaim.
func (h *SepHandler) RegisterRoutes(router *gin.RouterGroup) {
bpjsGroup := router.Group("/bpjs/vclaim")
{
sepGroup := bpjsGroup.Group("/sep")
{
sepGroup.POST("", h.CreateSEP)
sepGroup.PUT("", h.UpdateSEP)
// BPJS API untuk delete menggunakan method POST, tapi kita ekspos sebagai DELETE untuk konsistensi RESTful.
sepGroup.DELETE("", h.DeleteSEP)
sepGroup.GET("/:nosep", h.GetSEPDetail)
}
}
}
// CreateSEP menangani request untuk membuat SEP baru.
func (h *SepHandler) CreateSEP(c *gin.Context) {
var req sepService.CreateSEPRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.Error(c, http.StatusBadRequest, "Invalid request body", err.Error())
return
}
result, err := h.sepService.Create(c.Request.Context(), req)
if err != nil {
appErr := errors.FromError(err)
response.Error(c, appErr.HTTPStatus(), appErr.Error(), nil)
return
}
response.Success(c, http.StatusCreated, "Successfully created SEP", result)
}
// UpdateSEP menangani request untuk memperbarui SEP.
func (h *SepHandler) UpdateSEP(c *gin.Context) {
var req sepService.UpdateSEPRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.Error(c, http.StatusBadRequest, "Invalid request body", err.Error())
return
}
result, err := h.sepService.Update(c.Request.Context(), req)
if err != nil {
appErr := errors.FromError(err)
response.Error(c, appErr.HTTPStatus(), appErr.Error(), nil)
return
}
response.Success(c, http.StatusOK, "Successfully updated SEP", result)
}
// GetSEPDetail menangani request untuk mendapatkan detail SEP.
func (h *SepHandler) GetSEPDetail(c *gin.Context) {
noSEP := c.Param("no_sep")
result, err := h.sepService.GetDetail(c.Request.Context(), noSEP)
if err != nil {
appErr := errors.FromError(err)
response.Error(c, appErr.HTTPStatus(), appErr.Error(), nil)
return
}
response.Success(c, http.StatusOK, "Successfully retrieved SEP detail", result)
}
// DeleteSEP menangani request untuk menghapus SEP.
func (h *SepHandler) DeleteSEP(c *gin.Context) {
var req sepService.DeleteSEPRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.Error(c, http.StatusBadRequest, "Invalid request body for SEP deletion", err.Error())
return
}
result, err := h.sepService.Delete(c.Request.Context(), req)
if err != nil {
appErr := errors.FromError(err)
response.Error(c, appErr.HTTPStatus(), appErr.Error(), nil)
return
}
response.Success(c, http.StatusOK, "Successfully deleted SEP", result)
}
@@ -1,90 +0,0 @@
package reference
import (
stdErrors "errors"
"net/http"
"service/internal/interfaces/satusehat"
"service/internal/satusehat/reference/auth"
"service/pkg/errors"
"service/pkg/response"
"time"
"github.com/gin-gonic/gin"
)
// AuthHandler menangani endpoint HTTP untuk Auth Satu Sehat.
type AuthHandler struct {
service auth.Service
}
// RegisterRoutes mendaftarkan endpoint handler ini ke router Gin
func (h *AuthHandler) RegisterRoutes(router *gin.RouterGroup) {
group := router.Group("/satusehat/reference")
{
group.GET("/auth/token", h.GetToken)
group.POST("/auth/token/refresh", h.RefreshToken)
}
}
// NewAuthHandler membuat instance baru dari AuthHandler.
func NewAuthHandler(service auth.Service) *AuthHandler {
return &AuthHandler{
service: service,
}
}
// handleSatuSehatError mengekstrak OperationOutcome agar JSON bisa tampil terstruktur di response
func handleSatuSehatError(c *gin.Context, err error) {
var ssErr *satusehat.ErrorOperationOutcome
if stdErrors.As(err, &ssErr) {
c.JSON(ssErr.StatusCode, gin.H{
"status": "error",
"message": ssErr.Outcome,
"error": gin.H{
"severity": "error",
"timestamp": time.Now().UTC().Format(time.RFC3339),
},
})
return
}
appErr := errors.FromError(err)
response.Error(c, appErr.HTTPStatus(), appErr.Error(), appErr.Metadata())
}
// GetToken godoc
//
// @Summary Get SatuSehat Access Token
// @Description Mendapatkan token aktif untuk API Satu Sehat Kemenkes (menggunakan cache internal)
// @Tags Satu Sehat - Auth
// @Produce json
// @Success 200 {object} response.Response
// @Failure 500 {object} response.Response
// @Router /satusehat/reference/auth/token [get]
// @Security BearerAuth
func (h *AuthHandler) GetToken(c *gin.Context) {
data, err := h.service.GetToken(c.Request.Context())
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Berhasil mendapatkan token Satu Sehat", data)
}
// RefreshToken godoc
//
// @Summary Refresh SatuSehat Access Token
// @Description Memaksa request token baru dari API Satu Sehat Kemenkes (bypass cache)
// @Tags Satu Sehat - Auth
// @Produce json
// @Success 200 {object} response.Response
// @Failure 500 {object} response.Response
// @Router /satusehat/reference/auth/token/refresh [post]
// @Security BearerAuth
func (h *AuthHandler) RefreshToken(c *gin.Context) {
data, err := h.service.RefreshToken(c.Request.Context())
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Berhasil me-refresh token Satu Sehat", data)
}
@@ -1,77 +0,0 @@
package reference
import (
"net/http"
"service/internal/satusehat/reference/kfa"
"service/pkg/response"
"github.com/gin-gonic/gin"
)
type KFAHandler struct {
service kfa.Service
}
func NewKFAHandler(service kfa.Service) *KFAHandler {
return &KFAHandler{
service: service,
}
}
// GetByCode godoc
//
// @Summary Cari Produk KFA berdasarkan Kode
// @Description Mencari detail produk farmasi/alkes dari API Kamus Farmasi dan Alat Kesehatan (KFA) Satu Sehat
// @Tags Satu Sehat - KFA
// @Produce json
// @Param code path string true "Kode KFA (contoh: 93000469)"
// @Success 200 {object} response.Response
// @Router /satusehat/reference/kfa/products/{code} [get]
// @Security BearerAuth
func (h *KFAHandler) GetByCode(c *gin.Context) {
code := c.Param("code")
data, err := h.service.GetByCode(c.Request.Context(), code)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Berhasil mendapatkan data produk KFA", data)
}
// GetProducts godoc
//
// @Summary Daftar Produk KFA
// @Description Mengambil daftar produk KFA dengan kapabilitas paginasi
// @Tags Satu Sehat - KFA
// @Produce json
// @Param page query int false "Nomor Halaman (default: 1)"
// @Param size query int false "Jumlah Data (default: 10)"
// @Param product_type query string false "Tipe Produk (farmasi/alkes)"
// @Param keyword query string false "Kata Kunci Pencarian"
// @Param from_ query string false "Parameter waktu (from_)"
// @Success 200 {object} response.Response
// @Router /satusehat/reference/kfa/products [get]
// @Security BearerAuth
func (h *KFAHandler) GetProducts(c *gin.Context) {
var params kfa.KFASearchParams
if err := c.ShouldBindQuery(&params); err != nil {
response.Error(c, http.StatusBadRequest, "Format parameter tidak valid", err.Error())
return
}
data, err := h.service.GetProducts(c.Request.Context(), params)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Berhasil mengambil daftar produk KFA", data)
}
func (h *KFAHandler) RegisterRoutes(router *gin.RouterGroup) {
group := router.Group("/satusehat/reference/kfa")
{
group.GET("/products/:code", h.GetByCode)
group.GET("/products", h.GetProducts)
}
}
@@ -1,126 +0,0 @@
package reference
import (
"net/http"
"service/internal/satusehat/reference/location"
"service/pkg/response"
"github.com/gin-gonic/gin"
)
type LocationHandler struct {
service location.Service
}
func NewLocationHandler(service location.Service) *LocationHandler {
return &LocationHandler{
service: service,
}
}
// GetByID godoc
//
// @Summary Cari Lokasi (Satu Sehat) berdasarkan ID
// @Description Mencari data Location FHIR Satu Sehat berdasarkan ID
// @Tags Satu Sehat - Location
// @Produce json
// @Param id path string true "Location ID"
// @Success 200 {object} response.Response
// @Router /satusehat/reference/location/{id} [get]
// @Security BearerAuth
func (h *LocationHandler) GetByID(c *gin.Context) {
id := c.Param("id")
data, err := h.service.GetByID(c.Request.Context(), id)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Berhasil mendapatkan data lokasi", data)
}
// Search godoc
//
// @Summary Pencarian Lokasi (Satu Sehat)
// @Description Mencari data Location berdasarkan parameter
// @Tags Satu Sehat - Location
// @Produce json
// @Param name query string false "Nama Lokasi"
// @Param organization query string false "ID Organisasi Pemilik"
// @Param identifier query string false "Identifier Lokasi"
// @Success 200 {object} response.Response
// @Router /satusehat/reference/location [get]
// @Security BearerAuth
func (h *LocationHandler) Search(c *gin.Context) {
var params location.LocationSearchParams
if err := c.ShouldBindQuery(&params); err != nil {
response.Error(c, http.StatusBadRequest, "Format pencarian tidak valid", err.Error())
return
}
data, err := h.service.Search(c.Request.Context(), params)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Berhasil mendapatkan data lokasi", data)
}
func (h *LocationHandler) Create(c *gin.Context) {
var payload map[string]interface{}
if err := c.ShouldBindJSON(&payload); err != nil {
response.Error(c, http.StatusBadRequest, "Format request tidak valid", err.Error())
return
}
data, err := h.service.Create(c.Request.Context(), payload)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusCreated, "Berhasil membuat data lokasi", data)
}
func (h *LocationHandler) Update(c *gin.Context) {
id := c.Param("id")
var payload map[string]interface{}
if err := c.ShouldBindJSON(&payload); err != nil {
response.Error(c, http.StatusBadRequest, "Format request tidak valid", err.Error())
return
}
data, err := h.service.Update(c.Request.Context(), id, payload)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Berhasil mengubah data lokasi", data)
}
func (h *LocationHandler) Patch(c *gin.Context) {
id := c.Param("id")
var payload interface{}
if err := c.ShouldBindJSON(&payload); err != nil {
response.Error(c, http.StatusBadRequest, "Format request tidak valid", err.Error())
return
}
data, err := h.service.Patch(c.Request.Context(), id, payload)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Berhasil melakukan patch data lokasi", data)
}
// RegisterRoutes mendaftarkan endpoint handler ini ke router Gin
func (h *LocationHandler) RegisterRoutes(router *gin.RouterGroup) {
group := router.Group("/satusehat/reference")
{
group.GET("/location/:id", h.GetByID)
group.GET("/location", h.Search)
group.POST("/location", h.Create)
group.PUT("/location/:id", h.Update)
group.PATCH("/location/:id", h.Patch)
}
}
@@ -1,133 +0,0 @@
package reference
import (
"net/http"
"service/internal/satusehat/reference/organization"
"service/pkg/response"
"github.com/gin-gonic/gin"
)
type OrganizationHandler struct {
service organization.Service
}
func NewOrganizationHandler(service organization.Service) *OrganizationHandler {
return &OrganizationHandler{
service: service,
}
}
// GetByID godoc
//
// @Summary Cari Organisasi (Satu Sehat) berdasarkan ID
// @Description Mencari data Organization FHIR Satu Sehat berdasarkan ID
// @Tags Satu Sehat - Organization
// @Produce json
// @Param id path string true "Organization ID"
// @Success 200 {object} response.Response
// @Router /satusehat/reference/organization/{id} [get]
// @Security BearerAuth
func (h *OrganizationHandler) GetByID(c *gin.Context) {
id := c.Param("id")
data, err := h.service.GetByID(c.Request.Context(), id)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Berhasil mendapatkan data organisasi", data)
}
// Search godoc
//
// @Summary Pencarian Organisasi (Satu Sehat)
// @Description Mencari data Organization berdasarkan Name atau PartOf
// @Tags Satu Sehat - Organization
// @Produce json
// @Param name query string false "Nama Organisasi"
// @Param partof query string false "ID Organisasi Induk"
// @Param identifier query string false "Identifier Organisasi"
// @Success 200 {object} response.Response
// @Router /satusehat/reference/organization [get]
// @Security BearerAuth
func (h *OrganizationHandler) Search(c *gin.Context) {
var params organization.OrganizationSearchParams
if err := c.ShouldBindQuery(&params); err != nil {
response.Error(c, http.StatusBadRequest, "Format pencarian tidak valid", err.Error())
return
}
// Validasi mandiri sebelum menembak ke API Kemenkes
if params.Name == "" && params.PartOf == "" && params.Identifier == "" {
response.Error(c, http.StatusBadRequest, "Parameter pencarian tidak lengkap", "Harap masukkan minimal salah satu parameter query: name, partof, atau identifier")
return
}
data, err := h.service.Search(c.Request.Context(), params)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Berhasil mendapatkan data organisasi", data)
}
func (h *OrganizationHandler) Create(c *gin.Context) {
var payload map[string]interface{}
if err := c.ShouldBindJSON(&payload); err != nil {
response.Error(c, http.StatusBadRequest, "Format request tidak valid", err.Error())
return
}
data, err := h.service.Create(c.Request.Context(), payload)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusCreated, "Berhasil membuat data organisasi", data)
}
func (h *OrganizationHandler) Update(c *gin.Context) {
id := c.Param("id")
var payload map[string]interface{}
if err := c.ShouldBindJSON(&payload); err != nil {
response.Error(c, http.StatusBadRequest, "Format request tidak valid", err.Error())
return
}
data, err := h.service.Update(c.Request.Context(), id, payload)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Berhasil mengubah data organisasi", data)
}
func (h *OrganizationHandler) Patch(c *gin.Context) {
id := c.Param("id")
// JSON Patch biasanya array of objects, jadi gunakan interface{} general
var payload interface{}
if err := c.ShouldBindJSON(&payload); err != nil {
response.Error(c, http.StatusBadRequest, "Format request tidak valid", err.Error())
return
}
data, err := h.service.Patch(c.Request.Context(), id, payload)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Berhasil melakukan patch data organisasi", data)
}
// RegisterRoutes mendaftarkan endpoint handler ini ke router Gin
func (h *OrganizationHandler) RegisterRoutes(router *gin.RouterGroup) {
group := router.Group("/satusehat/reference")
{
group.GET("/organization/:id", h.GetByID)
group.GET("/organization", h.Search)
group.POST("/organization", h.Create)
group.PUT("/organization/:id", h.Update)
group.PATCH("/organization/:id", h.Patch)
}
}
@@ -1,127 +0,0 @@
package reference
import (
"net/http"
"service/internal/satusehat/reference/patient"
"service/pkg/response"
"github.com/gin-gonic/gin"
)
// PatientHandler menangani endpoint HTTP untuk resource Patient Satu Sehat.
type PatientHandler struct {
service patient.Service
}
// RegisterRoutes mendaftarkan endpoint handler ini ke router Gin
func (h *PatientHandler) RegisterRoutes(router *gin.RouterGroup) {
group := router.Group("/satusehat/reference")
{
group.GET("/patient/nik/:nik", h.GetByNIK)
group.GET("/patient/:id", h.GetByID)
group.GET("/patient", h.Search)
group.POST("/patient", h.Create)
}
}
// NewPatientHandler membuat instance baru dari PatientHandler.
func NewPatientHandler(service patient.Service) *PatientHandler {
return &PatientHandler{
service: service,
}
}
// GetByNIK godoc
//
// @Summary Cari Pasien (Satu Sehat) berdasarkan NIK
// @Description Mencari data pasien FHIR Satu Sehat berdasarkan Nomor Induk Kependudukan (NIK)
// @Tags Satu Sehat - Patient
// @Produce json
// @Param nik path string true "Nomor Induk Kependudukan"
// @Success 200 {object} response.Response
// @Router /satusehat/reference/patient/nik/{nik} [get]
// @Security BearerAuth
func (h *PatientHandler) GetByNIK(c *gin.Context) {
nik := c.Param("nik")
data, err := h.service.GetByNIK(c.Request.Context(), nik)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Berhasil mendapatkan data pasien", data)
}
// GetByID godoc
//
// @Summary Cari Pasien (Satu Sehat) berdasarkan ID
// @Description Mencari data pasien FHIR Satu Sehat berdasarkan IHS Number / ID
// @Tags Satu Sehat - Patient
// @Produce json
// @Param id path string true "IHS Number / Patient ID"
// @Success 200 {object} response.Response
// @Router /satusehat/reference/patient/{id} [get]
// @Security BearerAuth
func (h *PatientHandler) GetByID(c *gin.Context) {
id := c.Param("id")
data, err := h.service.GetByID(c.Request.Context(), id)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Berhasil mendapatkan data pasien", data)
}
// Search godoc
//
// @Summary Pencarian Pasien (Satu Sehat) Multi-Parameter
// @Description Mencari data pasien FHIR Satu Sehat berdasarkan kombinasi parameter (Nama, NIK, NIK Ibu, Tanggal Lahir, Gender)
// @Tags Satu Sehat - Patient
// @Produce json
// @Param nik query string false "Nomor Induk Kependudukan"
// @Param nik_ibu query string false "Nomor Induk Kependudukan Ibu (Untuk bayi)"
// @Param name query string false "Nama Pasien"
// @Param birthdate query string false "Tanggal Lahir (YYYY-MM-DD)"
// @Param gender query string false "Jenis Kelamin (male/female)"
// @Success 200 {object} response.Response
// @Router /satusehat/reference/patient [get]
// @Security BearerAuth
func (h *PatientHandler) Search(c *gin.Context) {
var params patient.PatientSearchParams
if err := c.ShouldBindQuery(&params); err != nil {
response.Error(c, http.StatusBadRequest, "Format pencarian tidak valid", err.Error())
return
}
data, err := h.service.Search(c.Request.Context(), params)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Berhasil mendapatkan data pasien", data)
}
// Create godoc
//
// @Summary Daftar Pasien Baru (Satu Sehat)
// @Description Mendaftarkan data pasien baru ke API Satu Sehat dan mengembalikan IHS Number
// @Tags Satu Sehat - Patient
// @Accept json
// @Produce json
// @Param request body patient.CreatePatientRequest true "Data Pasien Baru"
// @Success 201 {object} response.Response
// @Router /satusehat/reference/patient [post]
// @Security BearerAuth
func (h *PatientHandler) Create(c *gin.Context) {
var req patient.CreatePatientRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.Error(c, http.StatusBadRequest, "Format request tidak valid", err.Error())
return
}
data, err := h.service.Create(c.Request.Context(), req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusCreated, "Berhasil mendaftarkan pasien", data)
}
@@ -1,98 +0,0 @@
package reference
import (
"net/http"
"service/internal/satusehat/reference/practitioner"
"service/pkg/response"
"github.com/gin-gonic/gin"
)
type PractitionerHandler struct {
service practitioner.Service
}
func NewPractitionerHandler(service practitioner.Service) *PractitionerHandler {
return &PractitionerHandler{
service: service,
}
}
// GetByNIK godoc
//
// @Summary Cari Tenaga Medis (Satu Sehat) berdasarkan NIK
// @Description Mencari data Practitioner FHIR Satu Sehat berdasarkan NIK
// @Tags Satu Sehat - Practitioner
// @Produce json
// @Param nik path string true "Nomor Induk Kependudukan"
// @Success 200 {object} response.Response
// @Router /satusehat/reference/practitioner/nik/{nik} [get]
// @Security BearerAuth
func (h *PractitionerHandler) GetByNIK(c *gin.Context) {
nik := c.Param("nik")
data, err := h.service.GetByNIK(c.Request.Context(), nik)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Berhasil mendapatkan data tenaga medis", data)
}
// GetByID godoc
//
// @Summary Cari Tenaga Medis (Satu Sehat) berdasarkan ID
// @Description Mencari data Practitioner FHIR Satu Sehat berdasarkan IHS Number / ID
// @Tags Satu Sehat - Practitioner
// @Produce json
// @Param id path string true "IHS Number / Practitioner ID"
// @Success 200 {object} response.Response
// @Router /satusehat/reference/practitioner/{id} [get]
// @Security BearerAuth
func (h *PractitionerHandler) GetByID(c *gin.Context) {
id := c.Param("id")
data, err := h.service.GetByID(c.Request.Context(), id)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Berhasil mendapatkan data tenaga medis", data)
}
// Search godoc
//
// @Summary Pencarian Tenaga Medis (Satu Sehat) Multi-Parameter
// @Description Mencari data Practitioner FHIR Satu Sehat berdasarkan parameter
// @Tags Satu Sehat - Practitioner
// @Produce json
// @Param nik query string false "Nomor Induk Kependudukan"
// @Param name query string false "Nama Tenaga Medis"
// @Param gender query string false "Jenis Kelamin (male/female)"
// @Param birthdate query string false "Tanggal Lahir (YYYY-MM-DD)"
// @Success 200 {object} response.Response
// @Router /satusehat/reference/practitioner [get]
// @Security BearerAuth
func (h *PractitionerHandler) Search(c *gin.Context) {
var params practitioner.PractitionerSearchParams
if err := c.ShouldBindQuery(&params); err != nil {
response.Error(c, http.StatusBadRequest, "Format pencarian tidak valid", err.Error())
return
}
data, err := h.service.Search(c.Request.Context(), params)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Berhasil mendapatkan data tenaga medis", data)
}
// RegisterRoutes mendaftarkan endpoint handler ini ke router Gin
func (h *PractitionerHandler) RegisterRoutes(router *gin.RouterGroup) {
group := router.Group("/satusehat/reference")
{
group.GET("/practitioner/nik/:nik", h.GetByNIK)
group.GET("/practitioner/:id", h.GetByID)
group.GET("/practitioner", h.Search)
}
}
@@ -1,81 +0,0 @@
package usecase
import (
"net/http"
"service/internal/satusehat/usecase/allergyintolerance"
"service/pkg/response"
"service/pkg/utils/validator"
"github.com/gin-gonic/gin"
)
type AllergyIntoleranceHandler struct{ service allergyintolerance.Service }
func NewAllergyIntoleranceHandler(s allergyintolerance.Service) *AllergyIntoleranceHandler {
return &AllergyIntoleranceHandler{service: s}
}
func (h *AllergyIntoleranceHandler) RegisterRoutes(router *gin.RouterGroup) {
g := router.Group("/satusehat/allergyintolerance")
g.POST("", h.Create)
g.GET("", h.Search)
g.GET("/:id", h.GetByID)
g.PUT("/:id", h.Update)
g.PATCH("/:id", h.Patch)
}
func (h *AllergyIntoleranceHandler) Create(c *gin.Context) {
var req allergyintolerance.AllergyIntoleranceRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorWithLog(c, err, http.StatusBadRequest, "Invalid request", validator.TranslateError(err))
return
}
res, err := h.service.Create(c.Request.Context(), req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusCreated, "Success", res.FullResponse)
}
func (h *AllergyIntoleranceHandler) Update(c *gin.Context) {
var req allergyintolerance.AllergyIntoleranceRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorWithLog(c, err, http.StatusBadRequest, "Invalid request", validator.TranslateError(err))
return
}
res, err := h.service.Update(c.Request.Context(), c.Param("id"), req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Success", res.FullResponse)
}
func (h *AllergyIntoleranceHandler) Patch(c *gin.Context) {
var req allergyintolerance.AllergyIntolerancePatchRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorWithLog(c, err, http.StatusBadRequest, "Invalid patch", validator.TranslateError(err))
return
}
res, err := h.service.Patch(c.Request.Context(), c.Param("id"), req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Success", res.FullResponse)
}
func (h *AllergyIntoleranceHandler) GetByID(c *gin.Context) {
res, err := h.service.GetByID(c.Request.Context(), c.Param("id"))
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Success", res.FullResponse)
}
func (h *AllergyIntoleranceHandler) Search(c *gin.Context) {
res, err := h.service.Search(c.Request.Context(), c.Request.URL.Query())
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Success", res.FullResponse)
}
@@ -1,79 +0,0 @@
package usecase
import (
"net/http"
"service/internal/satusehat/usecase/careplan"
"service/pkg/response"
"service/pkg/utils/validator"
"github.com/gin-gonic/gin"
)
type CarePlanHandler struct{ service careplan.Service }
func NewCarePlanHandler(s careplan.Service) *CarePlanHandler { return &CarePlanHandler{service: s} }
func (h *CarePlanHandler) RegisterRoutes(router *gin.RouterGroup) {
g := router.Group("/satusehat/careplan")
g.POST("", h.Create)
g.GET("", h.Search)
g.GET("/:id", h.GetByID)
g.PUT("/:id", h.Update)
g.PATCH("/:id", h.Patch)
}
func (h *CarePlanHandler) Create(c *gin.Context) {
var req careplan.CarePlanRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorWithLog(c, err, http.StatusBadRequest, "Invalid request", validator.TranslateError(err))
return
}
res, err := h.service.Create(c.Request.Context(), req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusCreated, "Success", res.FullResponse)
}
func (h *CarePlanHandler) Update(c *gin.Context) {
var req careplan.CarePlanRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorWithLog(c, err, http.StatusBadRequest, "Invalid request", validator.TranslateError(err))
return
}
res, err := h.service.Update(c.Request.Context(), c.Param("id"), req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Success", res.FullResponse)
}
func (h *CarePlanHandler) Patch(c *gin.Context) {
var req careplan.CarePlanPatchRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorWithLog(c, err, http.StatusBadRequest, "Invalid patch", validator.TranslateError(err))
return
}
res, err := h.service.Patch(c.Request.Context(), c.Param("id"), req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Success", res.FullResponse)
}
func (h *CarePlanHandler) GetByID(c *gin.Context) {
res, err := h.service.GetByID(c.Request.Context(), c.Param("id"))
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Success", res.FullResponse)
}
func (h *CarePlanHandler) Search(c *gin.Context) {
res, err := h.service.Search(c.Request.Context(), c.Request.URL.Query())
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Success", res.FullResponse)
}
@@ -1,81 +0,0 @@
package usecase
import (
"net/http"
clinicalimpression "service/internal/satusehat/usecase/clinicalImpression"
"service/pkg/response"
"service/pkg/utils/validator"
"github.com/gin-gonic/gin"
)
type ClinicalImpressionHandler struct{ service clinicalimpression.Service }
func NewClinicalImpressionHandler(s clinicalimpression.Service) *ClinicalImpressionHandler {
return &ClinicalImpressionHandler{service: s}
}
func (h *ClinicalImpressionHandler) RegisterRoutes(router *gin.RouterGroup) {
g := router.Group("/satusehat/clinicalimpression")
g.POST("", h.Create)
g.GET("", h.Search)
g.GET("/:id", h.GetByID)
g.PUT("/:id", h.Update)
g.PATCH("/:id", h.Patch)
}
func (h *ClinicalImpressionHandler) Create(c *gin.Context) {
var req clinicalimpression.ClinicalImpressionRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorWithLog(c, err, http.StatusBadRequest, "Invalid request", validator.TranslateError(err))
return
}
res, err := h.service.Create(c.Request.Context(), req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusCreated, "Success", res.FullResponse)
}
func (h *ClinicalImpressionHandler) Update(c *gin.Context) {
var req clinicalimpression.ClinicalImpressionRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorWithLog(c, err, http.StatusBadRequest, "Invalid request", validator.TranslateError(err))
return
}
res, err := h.service.Update(c.Request.Context(), c.Param("id"), req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Success", res.FullResponse)
}
func (h *ClinicalImpressionHandler) Patch(c *gin.Context) {
var req clinicalimpression.ClinicalImpressionPatchRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorWithLog(c, err, http.StatusBadRequest, "Invalid patch", validator.TranslateError(err))
return
}
res, err := h.service.Patch(c.Request.Context(), c.Param("id"), req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Success", res.FullResponse)
}
func (h *ClinicalImpressionHandler) GetByID(c *gin.Context) {
res, err := h.service.GetByID(c.Request.Context(), c.Param("id"))
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Success", res.FullResponse)
}
func (h *ClinicalImpressionHandler) Search(c *gin.Context) {
res, err := h.service.Search(c.Request.Context(), c.Request.URL.Query())
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Success", res.FullResponse)
}
@@ -1,102 +0,0 @@
package usecase
import (
"net/http"
"service/internal/satusehat/usecase/composition"
"service/pkg/response"
"service/pkg/utils/validator"
"github.com/gin-gonic/gin"
)
type CompositionHandler struct {
service composition.Service
}
func NewCompositionHandler(service composition.Service) *CompositionHandler {
return &CompositionHandler{
service: service,
}
}
func (h *CompositionHandler) RegisterRoutes(router *gin.RouterGroup) {
group := router.Group("/satusehat/composition")
{
group.POST("", h.Create)
group.GET("", h.Search)
group.GET("/:id", h.GetByID)
group.PUT("/:id", h.Update)
group.PATCH("/:id", h.Patch)
}
}
func (h *CompositionHandler) Create(c *gin.Context) {
var req composition.CompositionRequest
if err := c.ShouldBindJSON(&req); err != nil {
customErr := validator.TranslateError(err)
response.ErrorWithLog(c, err, http.StatusBadRequest, "Format permintaan tidak valid", customErr)
return
}
result, err := h.service.Create(c.Request.Context(), req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusCreated, "Successfully created Composition", result.FullResponse)
}
func (h *CompositionHandler) Update(c *gin.Context) {
id := c.Param("id")
var req composition.CompositionRequest
if err := c.ShouldBindJSON(&req); err != nil {
customErr := validator.TranslateError(err)
response.ErrorWithLog(c, err, http.StatusBadRequest, "Format permintaan tidak valid", customErr)
return
}
result, err := h.service.Update(c.Request.Context(), id, req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Successfully updated Composition", result.FullResponse)
}
func (h *CompositionHandler) Patch(c *gin.Context) {
id := c.Param("id")
var req composition.CompositionPatchRequest
if err := c.ShouldBindJSON(&req); err != nil {
customErr := validator.TranslateError(err)
response.ErrorWithLog(c, err, http.StatusBadRequest, "Format permintaan patch tidak valid", customErr)
return
}
result, err := h.service.Patch(c.Request.Context(), id, req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Successfully patched Composition", result.FullResponse)
}
func (h *CompositionHandler) GetByID(c *gin.Context) {
id := c.Param("id")
result, err := h.service.GetByID(c.Request.Context(), id)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Successfully retrieved Composition", result.FullResponse)
}
func (h *CompositionHandler) Search(c *gin.Context) {
queryParams := c.Request.URL.Query()
result, err := h.service.Search(c.Request.Context(), queryParams)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Successfully retrieved Compositions", result.FullResponse)
}
@@ -1,102 +0,0 @@
package usecase
import (
"net/http"
"service/internal/satusehat/usecase/condition"
"service/pkg/response"
"service/pkg/utils/validator"
"github.com/gin-gonic/gin"
)
type ConditionHandler struct {
service condition.Service
}
func NewConditionHandler(service condition.Service) *ConditionHandler {
return &ConditionHandler{
service: service,
}
}
func (h *ConditionHandler) RegisterRoutes(router *gin.RouterGroup) {
group := router.Group("/satusehat/condition")
{
group.POST("", h.Create)
group.GET("", h.Search)
group.GET("/:id", h.GetByID)
group.PUT("/:id", h.Update)
group.PATCH("/:id", h.Patch)
}
}
func (h *ConditionHandler) Create(c *gin.Context) {
var req condition.ConditionRequest
if err := c.ShouldBindJSON(&req); err != nil {
customErr := validator.TranslateError(err)
response.ErrorWithLog(c, err, http.StatusBadRequest, "Format permintaan tidak valid", customErr)
return
}
result, err := h.service.Create(c.Request.Context(), req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusCreated, "Successfully created Condition", result)
}
func (h *ConditionHandler) Update(c *gin.Context) {
id := c.Param("id")
var req condition.ConditionRequest
if err := c.ShouldBindJSON(&req); err != nil {
customErr := validator.TranslateError(err)
response.ErrorWithLog(c, err, http.StatusBadRequest, "Format permintaan tidak valid", customErr)
return
}
result, err := h.service.Update(c.Request.Context(), id, req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Successfully updated Condition", result)
}
func (h *ConditionHandler) Patch(c *gin.Context) {
id := c.Param("id")
var req condition.ConditionPatchRequest
if err := c.ShouldBindJSON(&req); err != nil {
customErr := validator.TranslateError(err)
response.ErrorWithLog(c, err, http.StatusBadRequest, "Format permintaan patch tidak valid", customErr)
return
}
result, err := h.service.Patch(c.Request.Context(), id, req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Successfully patched Condition", result)
}
func (h *ConditionHandler) GetByID(c *gin.Context) {
id := c.Param("id")
result, err := h.service.GetByID(c.Request.Context(), id)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Successfully retrieved Condition", result)
}
func (h *ConditionHandler) Search(c *gin.Context) {
queryParams := c.Request.URL.Query()
result, err := h.service.Search(c.Request.Context(), queryParams)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Successfully retrieved Conditions", result)
}
@@ -1,81 +0,0 @@
package usecase
import (
"net/http"
"service/internal/satusehat/usecase/diagnosticreport"
"service/pkg/response"
"service/pkg/utils/validator"
"github.com/gin-gonic/gin"
)
type DiagnosticReportHandler struct{ service diagnosticreport.Service }
func NewDiagnosticReportHandler(s diagnosticreport.Service) *DiagnosticReportHandler {
return &DiagnosticReportHandler{service: s}
}
func (h *DiagnosticReportHandler) RegisterRoutes(router *gin.RouterGroup) {
g := router.Group("/satusehat/diagnosticreport")
g.POST("", h.Create)
g.GET("", h.Search)
g.GET("/:id", h.GetByID)
g.PUT("/:id", h.Update)
g.PATCH("/:id", h.Patch)
}
func (h *DiagnosticReportHandler) Create(c *gin.Context) {
var req diagnosticreport.DiagnosticReportRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorWithLog(c, err, http.StatusBadRequest, "Invalid request", validator.TranslateError(err))
return
}
res, err := h.service.Create(c.Request.Context(), req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusCreated, "Success", res.FullResponse)
}
func (h *DiagnosticReportHandler) Update(c *gin.Context) {
var req diagnosticreport.DiagnosticReportRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorWithLog(c, err, http.StatusBadRequest, "Invalid request", validator.TranslateError(err))
return
}
res, err := h.service.Update(c.Request.Context(), c.Param("id"), req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Success", res.FullResponse)
}
func (h *DiagnosticReportHandler) Patch(c *gin.Context) {
var req diagnosticreport.DiagnosticReportPatchRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorWithLog(c, err, http.StatusBadRequest, "Invalid patch", validator.TranslateError(err))
return
}
res, err := h.service.Patch(c.Request.Context(), c.Param("id"), req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Success", res.FullResponse)
}
func (h *DiagnosticReportHandler) GetByID(c *gin.Context) {
res, err := h.service.GetByID(c.Request.Context(), c.Param("id"))
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Success", res.FullResponse)
}
func (h *DiagnosticReportHandler) Search(c *gin.Context) {
res, err := h.service.Search(c.Request.Context(), c.Request.URL.Query())
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Success", res.FullResponse)
}
@@ -1,149 +0,0 @@
package usecase
import (
"errors"
"net/http"
"strconv"
"time"
"service/internal/interfaces/satusehat"
"service/internal/satusehat/usecase/encounter"
pkgErrors "service/pkg/errors"
"service/pkg/response"
"service/pkg/utils/validator"
"github.com/gin-gonic/gin"
)
type EncounterHandler struct {
service encounter.Service
}
func NewEncounterHandler(service encounter.Service) *EncounterHandler {
return &EncounterHandler{
service: service,
}
}
func (h *EncounterHandler) RegisterRoutes(router *gin.RouterGroup) {
group := router.Group("/satusehat/encounter")
{
group.POST("", h.Create)
group.POST("/sync/:idxdaftar", h.SyncFromSIMRS)
group.GET("", h.Search)
group.GET("/:id", h.GetByID)
group.PUT("/:id", h.Update)
group.PATCH("/:id", h.Patch)
}
}
// handleSatuSehatError mengekstrak OperationOutcome agar JSON bisa tampil terstruktur
func handleSatuSehatError(c *gin.Context, err error) {
var ssErr *satusehat.ErrorOperationOutcome
if errors.As(err, &ssErr) {
c.Error(err) // Log error asli
c.JSON(ssErr.StatusCode, gin.H{
"status": "error",
"message": ssErr.Outcome,
"error": gin.H{
"severity": "error",
"timestamp": time.Now().UTC().Format(time.RFC3339),
},
})
return
}
appErr := pkgErrors.FromError(err)
response.ErrorWithLog(c, err, appErr.HTTPStatus(), appErr.Error(), appErr.Metadata())
}
func (h *EncounterHandler) Create(c *gin.Context) {
var req encounter.EncounterRequest
if err := c.ShouldBindJSON(&req); err != nil {
customErr := validator.TranslateError(err)
response.ErrorWithLog(c, err, http.StatusBadRequest, "Format permintaan tidak valid", customErr)
return
}
result, err := h.service.Create(c.Request.Context(), req)
if err != nil {
handleSatuSehatError(c, err)
return
}
// Di sini, Anda bisa menyimpan result.RawResponse atau result.ID ke database log jika diperlukan.
// Contoh:
// go logService.Create(c.Request.Context(), "encounter_create", req, result.RawResponse, http.StatusCreated)
response.Success(c, http.StatusCreated, "Successfully created Encounter", result.FullResponse)
}
func (h *EncounterHandler) Update(c *gin.Context) {
id := c.Param("id")
var req encounter.EncounterRequest
if err := c.ShouldBindJSON(&req); err != nil {
customErr := validator.TranslateError(err)
response.ErrorWithLog(c, err, http.StatusBadRequest, "Format permintaan tidak valid", customErr)
return
}
result, err := h.service.Update(c.Request.Context(), id, req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Successfully updated Encounter", result.FullResponse)
}
func (h *EncounterHandler) Patch(c *gin.Context) {
id := c.Param("id")
var req encounter.EncounterPatchRequest
if err := c.ShouldBindJSON(&req); err != nil {
customErr := validator.TranslateError(err)
response.ErrorWithLog(c, err, http.StatusBadRequest, "Format permintaan patch tidak valid", customErr)
return
}
result, err := h.service.Patch(c.Request.Context(), id, req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Successfully patched Encounter", result.FullResponse)
}
func (h *EncounterHandler) GetByID(c *gin.Context) {
id := c.Param("id")
result, err := h.service.GetByID(c.Request.Context(), id)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Successfully retrieved Encounter", result.FullResponse)
}
func (h *EncounterHandler) Search(c *gin.Context) {
// Get query parameters string natively and fetch it directly (like: ?patient=xxx&status=active)
queryParams := c.Request.URL.Query()
result, err := h.service.Search(c.Request.Context(), queryParams)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Successfully retrieved Encounters", result.FullResponse)
}
func (h *EncounterHandler) SyncFromSIMRS(c *gin.Context) {
idxdaftarStr := c.Param("idxdaftar")
idxdaftar, err := strconv.ParseInt(idxdaftarStr, 10, 64)
if err != nil {
response.ErrorWithLog(c, err, http.StatusBadRequest, "Format idxdaftar tidak valid", nil)
return
}
result, err := h.service.SyncFromSIMRS(c.Request.Context(), idxdaftar)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Successfully synced Encounter from SIMRS", result.FullResponse)
}
@@ -1,102 +0,0 @@
package usecase
import (
"net/http"
"service/internal/satusehat/usecase/episodeofcare"
"service/pkg/response"
"service/pkg/utils/validator"
"github.com/gin-gonic/gin"
)
type EpisodeOfCareHandler struct {
service episodeofcare.Service
}
func NewEpisodeOfCareHandler(service episodeofcare.Service) *EpisodeOfCareHandler {
return &EpisodeOfCareHandler{
service: service,
}
}
func (h *EpisodeOfCareHandler) RegisterRoutes(router *gin.RouterGroup) {
group := router.Group("/satusehat/episodeofcare")
{
group.POST("", h.Create)
group.GET("", h.Search)
group.GET("/:id", h.GetByID)
group.PUT("/:id", h.Update)
group.PATCH("/:id", h.Patch)
}
}
func (h *EpisodeOfCareHandler) Create(c *gin.Context) {
var req episodeofcare.EpisodeOfCareRequest
if err := c.ShouldBindJSON(&req); err != nil {
customErr := validator.TranslateError(err)
response.ErrorWithLog(c, err, http.StatusBadRequest, "Format permintaan tidak valid", customErr)
return
}
result, err := h.service.Create(c.Request.Context(), req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusCreated, "Successfully created EpisodeOfCare", result)
}
func (h *EpisodeOfCareHandler) Update(c *gin.Context) {
id := c.Param("id")
var req episodeofcare.EpisodeOfCareRequest
if err := c.ShouldBindJSON(&req); err != nil {
customErr := validator.TranslateError(err)
response.ErrorWithLog(c, err, http.StatusBadRequest, "Format permintaan tidak valid", customErr)
return
}
result, err := h.service.Update(c.Request.Context(), id, req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Successfully updated EpisodeOfCare", result)
}
func (h *EpisodeOfCareHandler) Patch(c *gin.Context) {
id := c.Param("id")
var req episodeofcare.EpisodeOfCarePatchRequest
if err := c.ShouldBindJSON(&req); err != nil {
customErr := validator.TranslateError(err)
response.ErrorWithLog(c, err, http.StatusBadRequest, "Format permintaan patch tidak valid", customErr)
return
}
result, err := h.service.Patch(c.Request.Context(), id, req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Successfully patched EpisodeOfCare", result)
}
func (h *EpisodeOfCareHandler) GetByID(c *gin.Context) {
id := c.Param("id")
result, err := h.service.GetByID(c.Request.Context(), id)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Successfully retrieved EpisodeOfCare", result)
}
func (h *EpisodeOfCareHandler) Search(c *gin.Context) {
queryParams := c.Request.URL.Query()
result, err := h.service.Search(c.Request.Context(), queryParams)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Successfully retrieved EpisodeOfCare records", result)
}
@@ -1,102 +0,0 @@
package usecase
import (
"net/http"
"service/internal/satusehat/usecase/imagingstudy"
"service/pkg/response"
"service/pkg/utils/validator"
"github.com/gin-gonic/gin"
)
type ImagingStudyHandler struct {
service imagingstudy.Service
}
func NewImagingStudyHandler(service imagingstudy.Service) *ImagingStudyHandler {
return &ImagingStudyHandler{
service: service,
}
}
func (h *ImagingStudyHandler) RegisterRoutes(router *gin.RouterGroup) {
group := router.Group("/satusehat/imagingstudy")
{
group.POST("", h.Create)
group.GET("", h.Search)
group.GET("/:id", h.GetByID)
group.PUT("/:id", h.Update)
group.PATCH("/:id", h.Patch)
}
}
func (h *ImagingStudyHandler) Create(c *gin.Context) {
var req imagingstudy.ImagingStudyRequest
if err := c.ShouldBindJSON(&req); err != nil {
customErr := validator.TranslateError(err)
response.ErrorWithLog(c, err, http.StatusBadRequest, "Format permintaan tidak valid", customErr)
return
}
result, err := h.service.Create(c.Request.Context(), req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusCreated, "Successfully created ImagingStudy", result)
}
func (h *ImagingStudyHandler) Update(c *gin.Context) {
id := c.Param("id")
var req imagingstudy.ImagingStudyRequest
if err := c.ShouldBindJSON(&req); err != nil {
customErr := validator.TranslateError(err)
response.ErrorWithLog(c, err, http.StatusBadRequest, "Format permintaan tidak valid", customErr)
return
}
result, err := h.service.Update(c.Request.Context(), id, req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Successfully updated ImagingStudy", result)
}
func (h *ImagingStudyHandler) Patch(c *gin.Context) {
id := c.Param("id")
var req imagingstudy.ImagingStudyPatchRequest
if err := c.ShouldBindJSON(&req); err != nil {
customErr := validator.TranslateError(err)
response.ErrorWithLog(c, err, http.StatusBadRequest, "Format permintaan patch tidak valid", customErr)
return
}
result, err := h.service.Patch(c.Request.Context(), id, req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Successfully patched ImagingStudy", result)
}
func (h *ImagingStudyHandler) GetByID(c *gin.Context) {
id := c.Param("id")
result, err := h.service.GetByID(c.Request.Context(), id)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Successfully retrieved ImagingStudy", result)
}
func (h *ImagingStudyHandler) Search(c *gin.Context) {
queryParams := c.Request.URL.Query()
result, err := h.service.Search(c.Request.Context(), queryParams)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Successfully retrieved ImagingStudies", result)
}
@@ -1,81 +0,0 @@
package usecase
import (
"net/http"
"service/internal/satusehat/usecase/immunization"
"service/pkg/response"
"service/pkg/utils/validator"
"github.com/gin-gonic/gin"
)
type ImmunizationHandler struct{ service immunization.Service }
func NewImmunizationHandler(s immunization.Service) *ImmunizationHandler {
return &ImmunizationHandler{service: s}
}
func (h *ImmunizationHandler) RegisterRoutes(router *gin.RouterGroup) {
g := router.Group("/satusehat/immunization")
g.POST("", h.Create)
g.GET("", h.Search)
g.GET("/:id", h.GetByID)
g.PUT("/:id", h.Update)
g.PATCH("/:id", h.Patch)
}
func (h *ImmunizationHandler) Create(c *gin.Context) {
var req immunization.ImmunizationRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorWithLog(c, err, http.StatusBadRequest, "Invalid request", validator.TranslateError(err))
return
}
res, err := h.service.Create(c.Request.Context(), req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusCreated, "Success", res.FullResponse)
}
func (h *ImmunizationHandler) Update(c *gin.Context) {
var req immunization.ImmunizationRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorWithLog(c, err, http.StatusBadRequest, "Invalid request", validator.TranslateError(err))
return
}
res, err := h.service.Update(c.Request.Context(), c.Param("id"), req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Success", res.FullResponse)
}
func (h *ImmunizationHandler) Patch(c *gin.Context) {
var req immunization.ImmunizationPatchRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorWithLog(c, err, http.StatusBadRequest, "Invalid patch", validator.TranslateError(err))
return
}
res, err := h.service.Patch(c.Request.Context(), c.Param("id"), req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Success", res.FullResponse)
}
func (h *ImmunizationHandler) GetByID(c *gin.Context) {
res, err := h.service.GetByID(c.Request.Context(), c.Param("id"))
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Success", res.FullResponse)
}
func (h *ImmunizationHandler) Search(c *gin.Context) {
res, err := h.service.Search(c.Request.Context(), c.Request.URL.Query())
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Success", res.FullResponse)
}
@@ -1,102 +0,0 @@
package usecase
import (
"net/http"
"service/internal/satusehat/usecase/medication"
"service/pkg/response"
"service/pkg/utils/validator"
"github.com/gin-gonic/gin"
)
type MedicationHandler struct {
service medication.Service
}
func NewMedicationHandler(service medication.Service) *MedicationHandler {
return &MedicationHandler{
service: service,
}
}
func (h *MedicationHandler) RegisterRoutes(router *gin.RouterGroup) {
group := router.Group("/satusehat/medication")
{
group.POST("", h.Create)
group.GET("", h.Search)
group.GET("/:id", h.GetByID)
group.PUT("/:id", h.Update)
group.PATCH("/:id", h.Patch)
}
}
func (h *MedicationHandler) Create(c *gin.Context) {
var req medication.MedicationRequest
if err := c.ShouldBindJSON(&req); err != nil {
customErr := validator.TranslateError(err)
response.ErrorWithLog(c, err, http.StatusBadRequest, "Format permintaan tidak valid", customErr)
return
}
result, err := h.service.Create(c.Request.Context(), req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusCreated, "Successfully created Medication", result.FullResponse)
}
func (h *MedicationHandler) Update(c *gin.Context) {
id := c.Param("id")
var req medication.MedicationRequest
if err := c.ShouldBindJSON(&req); err != nil {
customErr := validator.TranslateError(err)
response.ErrorWithLog(c, err, http.StatusBadRequest, "Format permintaan tidak valid", customErr)
return
}
result, err := h.service.Update(c.Request.Context(), id, req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Successfully updated Medication", result.FullResponse)
}
func (h *MedicationHandler) Patch(c *gin.Context) {
id := c.Param("id")
var req medication.MedicationPatchRequest
if err := c.ShouldBindJSON(&req); err != nil {
customErr := validator.TranslateError(err)
response.ErrorWithLog(c, err, http.StatusBadRequest, "Format permintaan patch tidak valid", customErr)
return
}
result, err := h.service.Patch(c.Request.Context(), id, req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Successfully patched Medication", result.FullResponse)
}
func (h *MedicationHandler) GetByID(c *gin.Context) {
id := c.Param("id")
result, err := h.service.GetByID(c.Request.Context(), id)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Successfully retrieved Medication", result.FullResponse)
}
func (h *MedicationHandler) Search(c *gin.Context) {
queryParams := c.Request.URL.Query()
result, err := h.service.Search(c.Request.Context(), queryParams)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Successfully retrieved Medications", result.FullResponse)
}
@@ -1,91 +0,0 @@
package usecase
import (
"net/http"
"service/internal/satusehat/usecase/medicationdispense"
"service/pkg/response"
"service/pkg/utils/validator"
"github.com/gin-gonic/gin"
)
type MedicationDispenseHandler struct {
service medicationdispense.Service
}
func NewMedicationDispenseHandler(service medicationdispense.Service) *MedicationDispenseHandler {
return &MedicationDispenseHandler{service: service}
}
func (h *MedicationDispenseHandler) RegisterRoutes(router *gin.RouterGroup) {
group := router.Group("/satusehat/medicationdispense")
{
group.POST("", h.Create)
group.GET("", h.Search)
group.GET("/:id", h.GetByID)
group.PUT("/:id", h.Update)
group.PATCH("/:id", h.Patch)
}
}
func (h *MedicationDispenseHandler) Create(c *gin.Context) {
var req medicationdispense.MedicationDispenseRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorWithLog(c, err, http.StatusBadRequest, "Format permintaan tidak valid", validator.TranslateError(err))
return
}
result, err := h.service.Create(c.Request.Context(), req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusCreated, "Successfully created MedicationDispense", result.FullResponse)
}
func (h *MedicationDispenseHandler) Update(c *gin.Context) {
id := c.Param("id")
var req medicationdispense.MedicationDispenseRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorWithLog(c, err, http.StatusBadRequest, "Format permintaan tidak valid", validator.TranslateError(err))
return
}
result, err := h.service.Update(c.Request.Context(), id, req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Successfully updated MedicationDispense", result.FullResponse)
}
func (h *MedicationDispenseHandler) Patch(c *gin.Context) {
id := c.Param("id")
var req medicationdispense.MedicationDispensePatchRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorWithLog(c, err, http.StatusBadRequest, "Format permintaan patch tidak valid", validator.TranslateError(err))
return
}
result, err := h.service.Patch(c.Request.Context(), id, req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Successfully patched MedicationDispense", result.FullResponse)
}
func (h *MedicationDispenseHandler) GetByID(c *gin.Context) {
result, err := h.service.GetByID(c.Request.Context(), c.Param("id"))
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Successfully retrieved", result.FullResponse)
}
func (h *MedicationDispenseHandler) Search(c *gin.Context) {
result, err := h.service.Search(c.Request.Context(), c.Request.URL.Query())
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Successfully retrieved", result.FullResponse)
}
@@ -1,91 +0,0 @@
package usecase
import (
"net/http"
"service/internal/satusehat/usecase/medicationrequest"
"service/pkg/response"
"service/pkg/utils/validator"
"github.com/gin-gonic/gin"
)
type MedicationRequestHandler struct {
service medicationrequest.Service
}
func NewMedicationRequestHandler(service medicationrequest.Service) *MedicationRequestHandler {
return &MedicationRequestHandler{service: service}
}
func (h *MedicationRequestHandler) RegisterRoutes(router *gin.RouterGroup) {
group := router.Group("/satusehat/medicationrequest")
{
group.POST("", h.Create)
group.GET("", h.Search)
group.GET("/:id", h.GetByID)
group.PUT("/:id", h.Update)
group.PATCH("/:id", h.Patch)
}
}
func (h *MedicationRequestHandler) Create(c *gin.Context) {
var req medicationrequest.MedicationRequestRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorWithLog(c, err, http.StatusBadRequest, "Format permintaan tidak valid", validator.TranslateError(err))
return
}
result, err := h.service.Create(c.Request.Context(), req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusCreated, "Successfully created MedicationRequest", result.FullResponse)
}
func (h *MedicationRequestHandler) Update(c *gin.Context) {
id := c.Param("id")
var req medicationrequest.MedicationRequestRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorWithLog(c, err, http.StatusBadRequest, "Format permintaan tidak valid", validator.TranslateError(err))
return
}
result, err := h.service.Update(c.Request.Context(), id, req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Successfully updated MedicationRequest", result.FullResponse)
}
func (h *MedicationRequestHandler) Patch(c *gin.Context) {
id := c.Param("id")
var req medicationrequest.MedicationRequestPatchRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorWithLog(c, err, http.StatusBadRequest, "Format permintaan patch tidak valid", validator.TranslateError(err))
return
}
result, err := h.service.Patch(c.Request.Context(), id, req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Successfully patched MedicationRequest", result.FullResponse)
}
func (h *MedicationRequestHandler) GetByID(c *gin.Context) {
result, err := h.service.GetByID(c.Request.Context(), c.Param("id"))
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Successfully retrieved", result.FullResponse)
}
func (h *MedicationRequestHandler) Search(c *gin.Context) {
result, err := h.service.Search(c.Request.Context(), c.Request.URL.Query())
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Successfully retrieved", result.FullResponse)
}
@@ -1,91 +0,0 @@
package usecase
import (
"net/http"
"service/internal/satusehat/usecase/medicationstatement"
"service/pkg/response"
"service/pkg/utils/validator"
"github.com/gin-gonic/gin"
)
type MedicationStatementHandler struct {
service medicationstatement.Service
}
func NewMedicationStatementHandler(service medicationstatement.Service) *MedicationStatementHandler {
return &MedicationStatementHandler{service: service}
}
func (h *MedicationStatementHandler) RegisterRoutes(router *gin.RouterGroup) {
group := router.Group("/satusehat/medicationstatement")
{
group.POST("", h.Create)
group.GET("", h.Search)
group.GET("/:id", h.GetByID)
group.PUT("/:id", h.Update)
group.PATCH("/:id", h.Patch)
}
}
func (h *MedicationStatementHandler) Create(c *gin.Context) {
var req medicationstatement.MedicationStatementRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorWithLog(c, err, http.StatusBadRequest, "Format permintaan tidak valid", validator.TranslateError(err))
return
}
result, err := h.service.Create(c.Request.Context(), req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusCreated, "Successfully created MedicationStatement", result.FullResponse)
}
func (h *MedicationStatementHandler) Update(c *gin.Context) {
id := c.Param("id")
var req medicationstatement.MedicationStatementRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorWithLog(c, err, http.StatusBadRequest, "Format permintaan tidak valid", validator.TranslateError(err))
return
}
result, err := h.service.Update(c.Request.Context(), id, req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Successfully updated MedicationStatement", result.FullResponse)
}
func (h *MedicationStatementHandler) Patch(c *gin.Context) {
id := c.Param("id")
var req medicationstatement.MedicationStatementPatchRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorWithLog(c, err, http.StatusBadRequest, "Format permintaan patch tidak valid", validator.TranslateError(err))
return
}
result, err := h.service.Patch(c.Request.Context(), id, req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Successfully patched MedicationStatement", result.FullResponse)
}
func (h *MedicationStatementHandler) GetByID(c *gin.Context) {
result, err := h.service.GetByID(c.Request.Context(), c.Param("id"))
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Successfully retrieved", result.FullResponse)
}
func (h *MedicationStatementHandler) Search(c *gin.Context) {
result, err := h.service.Search(c.Request.Context(), c.Request.URL.Query())
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Successfully retrieved", result.FullResponse)
}
@@ -1,102 +0,0 @@
package usecase
import (
"net/http"
"service/internal/satusehat/usecase/observation"
"service/pkg/response"
"service/pkg/utils/validator"
"github.com/gin-gonic/gin"
)
type ObservationHandler struct {
service observation.Service
}
func NewObservationHandler(service observation.Service) *ObservationHandler {
return &ObservationHandler{
service: service,
}
}
func (h *ObservationHandler) RegisterRoutes(router *gin.RouterGroup) {
group := router.Group("/satusehat/observation")
{
group.POST("", h.Create)
group.GET("", h.Search)
group.GET("/:id", h.GetByID)
group.PUT("/:id", h.Update)
group.PATCH("/:id", h.Patch)
}
}
func (h *ObservationHandler) Create(c *gin.Context) {
var req observation.ObservationRequest
if err := c.ShouldBindJSON(&req); err != nil {
customErr := validator.TranslateError(err)
response.ErrorWithLog(c, err, http.StatusBadRequest, "Format permintaan tidak valid", customErr)
return
}
result, err := h.service.Create(c.Request.Context(), req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusCreated, "Successfully created Observation", result.FullResponse)
}
func (h *ObservationHandler) Update(c *gin.Context) {
id := c.Param("id")
var req observation.ObservationRequest
if err := c.ShouldBindJSON(&req); err != nil {
customErr := validator.TranslateError(err)
response.ErrorWithLog(c, err, http.StatusBadRequest, "Format permintaan tidak valid", customErr)
return
}
result, err := h.service.Update(c.Request.Context(), id, req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Successfully updated Observation", result.FullResponse)
}
func (h *ObservationHandler) Patch(c *gin.Context) {
id := c.Param("id")
var req observation.ObservationPatchRequest
if err := c.ShouldBindJSON(&req); err != nil {
customErr := validator.TranslateError(err)
response.ErrorWithLog(c, err, http.StatusBadRequest, "Format permintaan patch tidak valid", customErr)
return
}
result, err := h.service.Patch(c.Request.Context(), id, req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Successfully patched Observation", result.FullResponse)
}
func (h *ObservationHandler) GetByID(c *gin.Context) {
id := c.Param("id")
result, err := h.service.GetByID(c.Request.Context(), id)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Successfully retrieved Observation", result.FullResponse)
}
func (h *ObservationHandler) Search(c *gin.Context) {
queryParams := c.Request.URL.Query()
result, err := h.service.Search(c.Request.Context(), queryParams)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Successfully retrieved Observations", result.FullResponse)
}
@@ -1,102 +0,0 @@
package usecase
import (
"net/http"
"service/internal/satusehat/usecase/procedure"
"service/pkg/response"
"service/pkg/utils/validator"
"github.com/gin-gonic/gin"
)
type ProcedureHandler struct {
service procedure.Service
}
func NewProcedureHandler(service procedure.Service) *ProcedureHandler {
return &ProcedureHandler{
service: service,
}
}
func (h *ProcedureHandler) RegisterRoutes(router *gin.RouterGroup) {
group := router.Group("/satusehat/procedure")
{
group.POST("", h.Create)
group.GET("", h.Search)
group.GET("/:id", h.GetByID)
group.PUT("/:id", h.Update)
group.PATCH("/:id", h.Patch)
}
}
func (h *ProcedureHandler) Create(c *gin.Context) {
var req procedure.ProcedureRequest
if err := c.ShouldBindJSON(&req); err != nil {
customErr := validator.TranslateError(err)
response.ErrorWithLog(c, err, http.StatusBadRequest, "Format permintaan tidak valid", customErr)
return
}
result, err := h.service.Create(c.Request.Context(), req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusCreated, "Successfully created Procedure", result.FullResponse)
}
func (h *ProcedureHandler) Update(c *gin.Context) {
id := c.Param("id")
var req procedure.ProcedureRequest
if err := c.ShouldBindJSON(&req); err != nil {
customErr := validator.TranslateError(err)
response.ErrorWithLog(c, err, http.StatusBadRequest, "Format permintaan tidak valid", customErr)
return
}
result, err := h.service.Update(c.Request.Context(), id, req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Successfully updated Procedure", result.FullResponse)
}
func (h *ProcedureHandler) Patch(c *gin.Context) {
id := c.Param("id")
var req procedure.ProcedurePatchRequest
if err := c.ShouldBindJSON(&req); err != nil {
customErr := validator.TranslateError(err)
response.ErrorWithLog(c, err, http.StatusBadRequest, "Format permintaan patch tidak valid", customErr)
return
}
result, err := h.service.Patch(c.Request.Context(), id, req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Successfully patched Procedure", result.FullResponse)
}
func (h *ProcedureHandler) GetByID(c *gin.Context) {
id := c.Param("id")
result, err := h.service.GetByID(c.Request.Context(), id)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Successfully retrieved Procedure", result.FullResponse)
}
func (h *ProcedureHandler) Search(c *gin.Context) {
queryParams := c.Request.URL.Query()
result, err := h.service.Search(c.Request.Context(), queryParams)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Successfully retrieved Procedures", result.FullResponse)
}
@@ -1,91 +0,0 @@
package usecase
import (
"net/http"
"service/internal/satusehat/usecase/questionnaireresponse"
"service/pkg/response"
"service/pkg/utils/validator"
"github.com/gin-gonic/gin"
)
type QuestionnaireResponseHandler struct {
service questionnaireresponse.Service
}
func NewQuestionnaireResponseHandler(service questionnaireresponse.Service) *QuestionnaireResponseHandler {
return &QuestionnaireResponseHandler{service: service}
}
func (h *QuestionnaireResponseHandler) RegisterRoutes(router *gin.RouterGroup) {
group := router.Group("/satusehat/questionnaireresponse")
{
group.POST("", h.Create)
group.GET("", h.Search)
group.GET("/:id", h.GetByID)
group.PUT("/:id", h.Update)
group.PATCH("/:id", h.Patch)
}
}
func (h *QuestionnaireResponseHandler) Create(c *gin.Context) {
var req questionnaireresponse.QuestionnaireResponseRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorWithLog(c, err, http.StatusBadRequest, "Format permintaan tidak valid", validator.TranslateError(err))
return
}
result, err := h.service.Create(c.Request.Context(), req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusCreated, "Successfully created QuestionnaireResponse", result.FullResponse)
}
func (h *QuestionnaireResponseHandler) Update(c *gin.Context) {
id := c.Param("id")
var req questionnaireresponse.QuestionnaireResponseRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorWithLog(c, err, http.StatusBadRequest, "Format permintaan tidak valid", validator.TranslateError(err))
return
}
result, err := h.service.Update(c.Request.Context(), id, req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Successfully updated QuestionnaireResponse", result.FullResponse)
}
func (h *QuestionnaireResponseHandler) Patch(c *gin.Context) {
id := c.Param("id")
var req questionnaireresponse.QuestionnaireResponsePatchRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorWithLog(c, err, http.StatusBadRequest, "Format permintaan patch tidak valid", validator.TranslateError(err))
return
}
result, err := h.service.Patch(c.Request.Context(), id, req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Successfully patched QuestionnaireResponse", result.FullResponse)
}
func (h *QuestionnaireResponseHandler) GetByID(c *gin.Context) {
result, err := h.service.GetByID(c.Request.Context(), c.Param("id"))
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Successfully retrieved", result.FullResponse)
}
func (h *QuestionnaireResponseHandler) Search(c *gin.Context) {
result, err := h.service.Search(c.Request.Context(), c.Request.URL.Query())
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Successfully retrieved", result.FullResponse)
}
@@ -1,102 +0,0 @@
package usecase
import (
"net/http"
"service/internal/satusehat/usecase/servicerequest"
"service/pkg/response"
"service/pkg/utils/validator"
"github.com/gin-gonic/gin"
)
type ServiceRequestHandler struct {
service servicerequest.Service
}
func NewServiceRequestHandler(service servicerequest.Service) *ServiceRequestHandler {
return &ServiceRequestHandler{
service: service,
}
}
func (h *ServiceRequestHandler) RegisterRoutes(router *gin.RouterGroup) {
group := router.Group("/satusehat/servicerequest")
{
group.POST("", h.Create)
group.GET("", h.Search)
group.GET("/:id", h.GetByID)
group.PUT("/:id", h.Update)
group.PATCH("/:id", h.Patch)
}
}
func (h *ServiceRequestHandler) Create(c *gin.Context) {
var req servicerequest.ServiceRequestRequest
if err := c.ShouldBindJSON(&req); err != nil {
customErr := validator.TranslateError(err)
response.ErrorWithLog(c, err, http.StatusBadRequest, "Format permintaan tidak valid", customErr)
return
}
result, err := h.service.Create(c.Request.Context(), req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusCreated, "Successfully created ServiceRequest", result.FullResponse)
}
func (h *ServiceRequestHandler) Update(c *gin.Context) {
id := c.Param("id")
var req servicerequest.ServiceRequestRequest
if err := c.ShouldBindJSON(&req); err != nil {
customErr := validator.TranslateError(err)
response.ErrorWithLog(c, err, http.StatusBadRequest, "Format permintaan tidak valid", customErr)
return
}
result, err := h.service.Update(c.Request.Context(), id, req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Successfully updated ServiceRequest", result.FullResponse)
}
func (h *ServiceRequestHandler) Patch(c *gin.Context) {
id := c.Param("id")
var req servicerequest.ServiceRequestPatchRequest
if err := c.ShouldBindJSON(&req); err != nil {
customErr := validator.TranslateError(err)
response.ErrorWithLog(c, err, http.StatusBadRequest, "Format permintaan patch tidak valid", customErr)
return
}
result, err := h.service.Patch(c.Request.Context(), id, req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Successfully patched ServiceRequest", result.FullResponse)
}
func (h *ServiceRequestHandler) GetByID(c *gin.Context) {
id := c.Param("id")
result, err := h.service.GetByID(c.Request.Context(), id)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Successfully retrieved ServiceRequest", result.FullResponse)
}
func (h *ServiceRequestHandler) Search(c *gin.Context) {
queryParams := c.Request.URL.Query()
result, err := h.service.Search(c.Request.Context(), queryParams)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Successfully retrieved ServiceRequests", result.FullResponse)
}
@@ -1,79 +0,0 @@
package usecase
import (
"net/http"
"service/internal/satusehat/usecase/specimen"
"service/pkg/response"
"service/pkg/utils/validator"
"github.com/gin-gonic/gin"
)
type SpecimenHandler struct{ service specimen.Service }
func NewSpecimenHandler(s specimen.Service) *SpecimenHandler { return &SpecimenHandler{service: s} }
func (h *SpecimenHandler) RegisterRoutes(router *gin.RouterGroup) {
g := router.Group("/satusehat/specimen")
g.POST("", h.Create)
g.GET("", h.Search)
g.GET("/:id", h.GetByID)
g.PUT("/:id", h.Update)
g.PATCH("/:id", h.Patch)
}
func (h *SpecimenHandler) Create(c *gin.Context) {
var req specimen.SpecimenRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorWithLog(c, err, http.StatusBadRequest, "Invalid request", validator.TranslateError(err))
return
}
res, err := h.service.Create(c.Request.Context(), req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusCreated, "Success", res.FullResponse)
}
func (h *SpecimenHandler) Update(c *gin.Context) {
var req specimen.SpecimenRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorWithLog(c, err, http.StatusBadRequest, "Invalid request", validator.TranslateError(err))
return
}
res, err := h.service.Update(c.Request.Context(), c.Param("id"), req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Success", res.FullResponse)
}
func (h *SpecimenHandler) Patch(c *gin.Context) {
var req specimen.SpecimenPatchRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorWithLog(c, err, http.StatusBadRequest, "Invalid patch", validator.TranslateError(err))
return
}
res, err := h.service.Patch(c.Request.Context(), c.Param("id"), req)
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Success", res.FullResponse)
}
func (h *SpecimenHandler) GetByID(c *gin.Context) {
res, err := h.service.GetByID(c.Request.Context(), c.Param("id"))
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Success", res.FullResponse)
}
func (h *SpecimenHandler) Search(c *gin.Context) {
res, err := h.service.Search(c.Request.Context(), c.Request.URL.Query())
if err != nil {
handleSatuSehatError(c, err)
return
}
response.Success(c, http.StatusOK, "Success", res.FullResponse)
}
@@ -8,15 +8,9 @@ import (
"service/internal/infrastructure/config"
"service/internal/infrastructure/transport/http/middleware"
antrolHttp "service/internal/infrastructure/transport/http/handlers/bpjs/antrol"
aplicareHttp "service/internal/infrastructure/transport/http/handlers/bpjs/aplicare"
apotekHttp "service/internal/infrastructure/transport/http/handlers/bpjs/apotek"
vclaimHttp "service/internal/infrastructure/transport/http/handlers/bpjs/vclaim"
authHttp "service/internal/infrastructure/transport/http/handlers/main/auth"
healthHttp "service/internal/infrastructure/transport/http/handlers/main/health"
roleHttp "service/internal/infrastructure/transport/http/handlers/main/master/roles"
satuSehatRefHttp "service/internal/infrastructure/transport/http/handlers/satusehat/reference"
satuSehatUsecaseHttp "service/internal/infrastructure/transport/http/handlers/satusehat/usecase"
"github.com/gin-gonic/gin"
swaggerFiles "github.com/swaggo/files"
@@ -26,40 +20,10 @@ import (
// ModuleHandlers menampung seluruh HTTP handler opsional dari berbagai modul.
// Jika sebuah handler bernilai nil, maka rutenya akan otomatis diabaikan (disabled).
type ModuleHandlers struct {
Auth *authHttp.AuthHandler
RolePages *roleHttp.RolPagesHandler
RolePermission *roleHttp.RolPermissionHandler
RoleMaster *roleHttp.RoleMasterHandler
Sep *vclaimHttp.SepHandler
Peserta *vclaimHttp.PesertaHandler
Antrol *antrolHttp.AntrolHandler
AplicareBed *aplicareHttp.BedHandler
ApotekReference *apotekHttp.ReferenceHandler
SatuSehatAuth *satuSehatRefHttp.AuthHandler
SatuSehatPatient *satuSehatRefHttp.PatientHandler
SatuSehatPractitioner *satuSehatRefHttp.PractitionerHandler
SatuSehatOrganization *satuSehatRefHttp.OrganizationHandler
SatuSehatLocation *satuSehatRefHttp.LocationHandler
SatuSehatKFA *satuSehatRefHttp.KFAHandler
SSAllergyIntolerance *satuSehatUsecaseHttp.AllergyIntoleranceHandler
SSCarePlan *satuSehatUsecaseHttp.CarePlanHandler
SSClinicalImpression *satuSehatUsecaseHttp.ClinicalImpressionHandler
SSComposition *satuSehatUsecaseHttp.CompositionHandler
SSCondition *satuSehatUsecaseHttp.ConditionHandler
SSDiagnosticReport *satuSehatUsecaseHttp.DiagnosticReportHandler
SSEncounter *satuSehatUsecaseHttp.EncounterHandler
SSEpisodeOfCare *satuSehatUsecaseHttp.EpisodeOfCareHandler
SSImagingStudy *satuSehatUsecaseHttp.ImagingStudyHandler
SSImmunization *satuSehatUsecaseHttp.ImmunizationHandler
SSMedication *satuSehatUsecaseHttp.MedicationHandler
SSMedicationDispense *satuSehatUsecaseHttp.MedicationDispenseHandler
SSMedicationRequest *satuSehatUsecaseHttp.MedicationRequestHandler
SSMedicationStatement *satuSehatUsecaseHttp.MedicationStatementHandler
SSObservation *satuSehatUsecaseHttp.ObservationHandler
SSProcedure *satuSehatUsecaseHttp.ProcedureHandler
SSQuestionnaireResponse *satuSehatUsecaseHttp.QuestionnaireResponseHandler
SSServiceRequest *satuSehatUsecaseHttp.ServiceRequestHandler
SSSpecimen *satuSehatUsecaseHttp.SpecimenHandler
Auth *authHttp.AuthHandler
RolePages *roleHttp.RolPagesHandler
RolePermission *roleHttp.RolPermissionHandler
RoleMaster *roleHttp.RoleMasterHandler
}
// SetupRoutes mendaftarkan seluruh endpoint API secara dinamis berdasarkan
@@ -153,111 +117,6 @@ func SetupRoutes(
if h.RoleMaster != nil {
h.RoleMaster.RegisterRoutes(protected)
}
// --- Routes Modul BPJS VClaim ---
if h.Sep != nil {
h.Sep.RegisterRoutes(protected)
}
if h.Peserta != nil {
h.Peserta.RegisterRoutes(protected)
}
// --- Routes Modul BPJS Antrol ---
if h.Antrol != nil {
h.Antrol.RegisterRoutes(protected)
}
// --- Routes Modul BPJS Aplicares ---
if h.AplicareBed != nil {
h.AplicareBed.RegisterRoutes(protected)
}
// --- Routes Modul BPJS Apotek ---
if h.ApotekReference != nil {
h.ApotekReference.RegisterRoutes(protected)
}
// --- Routes Modul Satu Sehat ---
// Buat grup khusus untuk membatasi traffic hit API ke Kemenkes
satuSehatGroup := protected.Group("")
satuSehatGroup.Use(middleware.MemoryRateLimitMiddleware(5.0, 10)) // Max 5 RPS, Burst capacity 10
{
if h.SatuSehatAuth != nil {
h.SatuSehatAuth.RegisterRoutes(satuSehatGroup)
}
if h.SatuSehatPatient != nil {
h.SatuSehatPatient.RegisterRoutes(satuSehatGroup)
}
if h.SatuSehatPractitioner != nil {
h.SatuSehatPractitioner.RegisterRoutes(satuSehatGroup)
}
if h.SatuSehatOrganization != nil {
h.SatuSehatOrganization.RegisterRoutes(satuSehatGroup)
}
if h.SatuSehatLocation != nil {
h.SatuSehatLocation.RegisterRoutes(satuSehatGroup)
}
if h.SatuSehatKFA != nil {
h.SatuSehatKFA.RegisterRoutes(satuSehatGroup)
}
if h.SSAllergyIntolerance != nil {
h.SSAllergyIntolerance.RegisterRoutes(satuSehatGroup)
}
if h.SSCarePlan != nil {
h.SSCarePlan.RegisterRoutes(satuSehatGroup)
}
if h.SSClinicalImpression != nil {
h.SSClinicalImpression.RegisterRoutes(satuSehatGroup)
}
if h.SSComposition != nil {
h.SSComposition.RegisterRoutes(satuSehatGroup)
}
if h.SSCondition != nil {
h.SSCondition.RegisterRoutes(satuSehatGroup)
}
if h.SSDiagnosticReport != nil {
h.SSDiagnosticReport.RegisterRoutes(satuSehatGroup)
}
if h.SSEncounter != nil {
h.SSEncounter.RegisterRoutes(satuSehatGroup)
}
if h.SSEpisodeOfCare != nil {
h.SSEpisodeOfCare.RegisterRoutes(satuSehatGroup)
}
if h.SSImagingStudy != nil {
h.SSImagingStudy.RegisterRoutes(satuSehatGroup)
}
if h.SSImmunization != nil {
h.SSImmunization.RegisterRoutes(satuSehatGroup)
}
if h.SSMedication != nil {
h.SSMedication.RegisterRoutes(satuSehatGroup)
}
if h.SSMedicationDispense != nil {
h.SSMedicationDispense.RegisterRoutes(satuSehatGroup)
}
if h.SSMedicationRequest != nil {
h.SSMedicationRequest.RegisterRoutes(satuSehatGroup)
}
if h.SSMedicationStatement != nil {
h.SSMedicationStatement.RegisterRoutes(satuSehatGroup)
}
if h.SSObservation != nil {
h.SSObservation.RegisterRoutes(satuSehatGroup)
}
if h.SSProcedure != nil {
h.SSProcedure.RegisterRoutes(satuSehatGroup)
}
if h.SSQuestionnaireResponse != nil {
h.SSQuestionnaireResponse.RegisterRoutes(satuSehatGroup)
}
if h.SSServiceRequest != nil {
h.SSServiceRequest.RegisterRoutes(satuSehatGroup)
}
if h.SSSpecimen != nil {
h.SSSpecimen.RegisterRoutes(satuSehatGroup)
}
}
}
}
}
@@ -8,40 +8,9 @@ import (
"gorm.io/gorm"
"service/internal/auth"
antrol "service/internal/bpjs/antrol/reference"
aplicareBed "service/internal/bpjs/aplicare/bed"
apotekDpho "service/internal/bpjs/apotek/reference/dpho"
apotekPoli "service/internal/bpjs/apotek/reference/poli"
peserta "service/internal/bpjs/vclaim/peserta"
sep "service/internal/bpjs/vclaim/sep"
roleMaster "service/internal/master/role/master"
rolePages "service/internal/master/role/pages"
rolePermission "service/internal/master/role/permission"
satuSehatAuth "service/internal/satusehat/reference/auth"
satuSehatKfa "service/internal/satusehat/reference/kfa"
satuSehatLocation "service/internal/satusehat/reference/location"
satuSehatOrganization "service/internal/satusehat/reference/organization"
satuSehatPatient "service/internal/satusehat/reference/patient"
satuSehatPractitioner "service/internal/satusehat/reference/practitioner"
"service/internal/satusehat/usecase/allergyintolerance"
"service/internal/satusehat/usecase/careplan"
clinicalimpression "service/internal/satusehat/usecase/clinicalImpression"
"service/internal/satusehat/usecase/composition"
"service/internal/satusehat/usecase/condition"
"service/internal/satusehat/usecase/diagnosticreport"
"service/internal/satusehat/usecase/encounter"
"service/internal/satusehat/usecase/episodeofcare"
"service/internal/satusehat/usecase/imagingstudy"
"service/internal/satusehat/usecase/immunization"
"service/internal/satusehat/usecase/medication"
"service/internal/satusehat/usecase/medicationdispense"
"service/internal/satusehat/usecase/medicationrequest"
"service/internal/satusehat/usecase/medicationstatement"
"service/internal/satusehat/usecase/observation"
"service/internal/satusehat/usecase/procedure"
"service/internal/satusehat/usecase/questionnaireresponse"
"service/internal/satusehat/usecase/servicerequest"
"service/internal/satusehat/usecase/specimen"
)
// MasterServices menampung kumpulan service untuk domain Master Data
@@ -51,59 +20,6 @@ type MasterServices struct {
RoleMaster roleMaster.Service
}
// VClaimServices menampung kumpulan service untuk integrasi BPJS VClaim
type VClaimServices struct {
Sep sep.Service
Peserta peserta.Service
}
// AntrolServices menampung kumpulan service untuk integrasi BPJS Antrean RS
type AntrolServices struct {
Reference antrol.Service
}
// AplicaresServices menampung kumpulan service untuk integrasi BPJS Aplicares
type AplicaresServices struct {
Bed aplicareBed.Service
}
// ApotekServices menampung kumpulan service untuk integrasi BPJS Apotek
type ApotekServices struct {
DPHO apotekDpho.Service
Poli apotekPoli.Service
}
// SatuSehatServices menampung kumpulan service untuk integrasi Kemenkes Satu Sehat
type SatuSehatServices struct {
Auth satuSehatAuth.Service
Patient satuSehatPatient.Service
Practitioner satuSehatPractitioner.Service
Organization satuSehatOrganization.Service
Location satuSehatLocation.Service
KFA satuSehatKfa.Service
// Usecase Services
AllergyIntolerance allergyintolerance.Service
CarePlan careplan.Service
ClinicalImpression clinicalimpression.Service
Composition composition.Service
Condition condition.Service
DiagnosticReport diagnosticreport.Service
Encounter encounter.Service
EpisodeOfCare episodeofcare.Service
ImagingStudy imagingstudy.Service
Immunization immunization.Service
Medication medication.Service
MedicationDispense medicationdispense.Service
MedicationRequest medicationrequest.Service
MedicationStatement medicationstatement.Service
Observation observation.Service
Procedure procedure.Service
QuestionnaireResponse questionnaireresponse.Service
ServiceRequest servicerequest.Service
Specimen specimen.Service
}
// ServiceRegistry berfungsi sebagai Dependency Injection Container untuk transport HTTP.
// Struktur ini mencegah membengkaknya parameter pada saat inisialisasi API.
type ServiceRegistry struct {
@@ -115,9 +31,4 @@ type ServiceRegistry struct {
// Modul Aplikasi (Pisahkan berdasarkan Bounded Context)
AuthService auth.Service
Master *MasterServices
VClaim *VClaimServices
Antrol *AntrolServices
Aplicare *AplicaresServices
Apotek *ApotekServices
SatuSehat *SatuSehatServices
}
@@ -13,15 +13,9 @@ import (
"service/internal/infrastructure/transport/http/middleware"
"service/internal/infrastructure/transport/http/routes"
antrolHttp "service/internal/infrastructure/transport/http/handlers/bpjs/antrol"
aplicareHttp "service/internal/infrastructure/transport/http/handlers/bpjs/aplicare"
apotekHttp "service/internal/infrastructure/transport/http/handlers/bpjs/apotek"
vclaimHttp "service/internal/infrastructure/transport/http/handlers/bpjs/vclaim"
authHttp "service/internal/infrastructure/transport/http/handlers/main/auth"
healthHttp "service/internal/infrastructure/transport/http/handlers/main/health"
roleHttp "service/internal/infrastructure/transport/http/handlers/main/master/roles"
satuSehatRefHttp "service/internal/infrastructure/transport/http/handlers/satusehat/reference"
satuSehatUsecaseHttp "service/internal/infrastructure/transport/http/handlers/satusehat/usecase"
// Inisialisasi pkg
"service/pkg/errors"
@@ -88,118 +82,6 @@ func NewHTTPServer(
}
}
// Inisialisasi Handlers Modul VClaim
if registry.VClaim != nil {
if registry.VClaim.Sep != nil {
appHandlers.Sep = vclaimHttp.NewSepHandler(registry.VClaim.Sep)
}
if registry.VClaim.Peserta != nil {
appHandlers.Peserta = vclaimHttp.NewPesertaHandler(registry.VClaim.Peserta)
}
}
// Inisialisasi Handlers Modul Antrol
if registry.Antrol != nil {
if registry.Antrol.Reference != nil {
appHandlers.Antrol = antrolHttp.NewAntrolHandler(registry.Antrol.Reference)
}
}
// Inisialisasi Handlers Modul Aplicares
if registry.Aplicare != nil {
if registry.Aplicare.Bed != nil {
appHandlers.AplicareBed = aplicareHttp.NewBedHandler(registry.Aplicare.Bed)
}
}
// Inisialisasi Handlers Modul Apotek
if registry.Apotek != nil {
if registry.Apotek.DPHO != nil && registry.Apotek.Poli != nil {
appHandlers.ApotekReference = apotekHttp.NewReferenceHandler(registry.Apotek.DPHO, registry.Apotek.Poli)
}
}
// Inisialisasi Handlers Modul Satu Sehat
if registry.SatuSehat != nil {
if registry.SatuSehat.Auth != nil {
appHandlers.SatuSehatAuth = satuSehatRefHttp.NewAuthHandler(registry.SatuSehat.Auth)
}
if registry.SatuSehat.Patient != nil {
appHandlers.SatuSehatPatient = satuSehatRefHttp.NewPatientHandler(registry.SatuSehat.Patient)
}
if registry.SatuSehat.Practitioner != nil {
appHandlers.SatuSehatPractitioner = satuSehatRefHttp.NewPractitionerHandler(registry.SatuSehat.Practitioner)
}
if registry.SatuSehat.Organization != nil {
appHandlers.SatuSehatOrganization = satuSehatRefHttp.NewOrganizationHandler(registry.SatuSehat.Organization)
}
if registry.SatuSehat.Location != nil {
appHandlers.SatuSehatLocation = satuSehatRefHttp.NewLocationHandler(registry.SatuSehat.Location)
}
if registry.SatuSehat.KFA != nil {
appHandlers.SatuSehatKFA = satuSehatRefHttp.NewKFAHandler(registry.SatuSehat.KFA)
}
// Inisialisasi Handlers Modul Satu Sehat Usecase
if registry.SatuSehat.AllergyIntolerance != nil {
appHandlers.SSAllergyIntolerance = satuSehatUsecaseHttp.NewAllergyIntoleranceHandler(registry.SatuSehat.AllergyIntolerance)
}
if registry.SatuSehat.CarePlan != nil {
appHandlers.SSCarePlan = satuSehatUsecaseHttp.NewCarePlanHandler(registry.SatuSehat.CarePlan)
}
if registry.SatuSehat.ClinicalImpression != nil {
appHandlers.SSClinicalImpression = satuSehatUsecaseHttp.NewClinicalImpressionHandler(registry.SatuSehat.ClinicalImpression)
}
if registry.SatuSehat.Composition != nil {
appHandlers.SSComposition = satuSehatUsecaseHttp.NewCompositionHandler(registry.SatuSehat.Composition)
}
if registry.SatuSehat.Condition != nil {
appHandlers.SSCondition = satuSehatUsecaseHttp.NewConditionHandler(registry.SatuSehat.Condition)
}
if registry.SatuSehat.DiagnosticReport != nil {
appHandlers.SSDiagnosticReport = satuSehatUsecaseHttp.NewDiagnosticReportHandler(registry.SatuSehat.DiagnosticReport)
}
if registry.SatuSehat.Encounter != nil {
appHandlers.SSEncounter = satuSehatUsecaseHttp.NewEncounterHandler(registry.SatuSehat.Encounter)
}
if registry.SatuSehat.EpisodeOfCare != nil {
appHandlers.SSEpisodeOfCare = satuSehatUsecaseHttp.NewEpisodeOfCareHandler(registry.SatuSehat.EpisodeOfCare)
}
if registry.SatuSehat.ImagingStudy != nil {
appHandlers.SSImagingStudy = satuSehatUsecaseHttp.NewImagingStudyHandler(registry.SatuSehat.ImagingStudy)
}
if registry.SatuSehat.Immunization != nil {
appHandlers.SSImmunization = satuSehatUsecaseHttp.NewImmunizationHandler(registry.SatuSehat.Immunization)
}
if registry.SatuSehat.Medication != nil {
appHandlers.SSMedication = satuSehatUsecaseHttp.NewMedicationHandler(registry.SatuSehat.Medication)
}
if registry.SatuSehat.MedicationDispense != nil {
appHandlers.SSMedicationDispense = satuSehatUsecaseHttp.NewMedicationDispenseHandler(registry.SatuSehat.MedicationDispense)
}
if registry.SatuSehat.MedicationRequest != nil {
appHandlers.SSMedicationRequest = satuSehatUsecaseHttp.NewMedicationRequestHandler(registry.SatuSehat.MedicationRequest)
}
if registry.SatuSehat.MedicationStatement != nil {
appHandlers.SSMedicationStatement = satuSehatUsecaseHttp.NewMedicationStatementHandler(registry.SatuSehat.MedicationStatement)
}
if registry.SatuSehat.Observation != nil {
appHandlers.SSObservation = satuSehatUsecaseHttp.NewObservationHandler(registry.SatuSehat.Observation)
}
if registry.SatuSehat.Procedure != nil {
appHandlers.SSProcedure = satuSehatUsecaseHttp.NewProcedureHandler(registry.SatuSehat.Procedure)
}
if registry.SatuSehat.QuestionnaireResponse != nil {
appHandlers.SSQuestionnaireResponse = satuSehatUsecaseHttp.NewQuestionnaireResponseHandler(registry.SatuSehat.QuestionnaireResponse)
}
if registry.SatuSehat.ServiceRequest != nil {
appHandlers.SSServiceRequest = satuSehatUsecaseHttp.NewServiceRequestHandler(registry.SatuSehat.ServiceRequest)
}
if registry.SatuSehat.Specimen != nil {
appHandlers.SSSpecimen = satuSehatUsecaseHttp.NewSpecimenHandler(registry.SatuSehat.Specimen)
}
}
// Inisialisasi Health Handler dengan dependensi yang benar
// Gunakan constructor baru yang menerima cache manager
healthHandler := healthHttp.NewHealthHandlerWithCache(registry.PrimaryDB, registry.CacheManager, registry.Config, registry.DBManager)
@@ -1,83 +0,0 @@
package auth
import (
"context"
"encoding/json"
"time"
"service/internal/infrastructure/cache"
"service/internal/interfaces/satusehat"
"service/pkg/errors"
"service/pkg/logger"
)
// Service mendefinisikan interface untuk manajemen Auth Satu Sehat di level aplikasi.
type Service interface {
GetToken(ctx context.Context) (map[string]interface{}, error)
RefreshToken(ctx context.Context) (map[string]interface{}, error)
}
type service struct {
client satusehat.SatuSehatClient
cache *cache.Manager
}
// NewService membuat instance baru dari auth service.
func NewService(client satusehat.SatuSehatClient, cacheManager *cache.Manager) Service {
return &service{
client: client,
cache: cacheManager,
}
}
func (s *service) GetToken(ctx context.Context) (map[string]interface{}, error) {
cacheKey := "satusehat:auth_data"
// 1. Coba ambil dari Redis cache (sangat efisien untuk multi-instance/load balancer)
if s.cache != nil {
var cachedToken string
if err := s.cache.Get(ctx, cacheKey, &cachedToken); err == nil && cachedToken != "" {
var data map[string]interface{}
if err := json.Unmarshal([]byte(cachedToken), &data); err == nil {
logger.Default().Debug("🔑 Mendapatkan token Satu Sehat dari Redis Cache")
return data, nil
}
}
}
// 2. Jika tidak ada di Redis (atau expired), ambil dari Klien Kemenkes
data, err := s.client.GetAccessToken(ctx)
if err != nil {
logger.Default().Error("Gagal mendapatkan token dari SatuSehat Client", logger.ErrorField(err))
return nil, errors.InternalError().Message("Gagal mendapatkan token Satu Sehat dari Kemenkes").Cause(err).Build()
}
// 3. Simpan ke Redis cache (Diset 55 menit karena token Kemenkes biasanya expired dalam 60 menit)
if s.cache != nil {
if bytes, err := json.Marshal(data); err == nil {
_ = s.cache.Set(ctx, cacheKey, string(bytes), 55*time.Minute)
}
}
return data, nil
}
func (s *service) RefreshToken(ctx context.Context) (map[string]interface{}, error) {
// Refresh token secara paksa melewati cache in-memory HTTP Client
data, err := s.client.RefreshToken(ctx)
if err != nil {
logger.Default().Error("Gagal me-refresh token SatuSehat Client", logger.ErrorField(err))
return nil, errors.InternalError().Message("Gagal melakukan refresh token Satu Sehat").Cause(err).Build()
}
// Update sinkronisasi token terbaru ke Redis cache
if s.cache != nil {
cacheKey := "satusehat:auth_data"
if bytes, err := json.Marshal(data); err == nil {
_ = s.cache.Set(ctx, cacheKey, string(bytes), 55*time.Minute)
}
}
logger.Default().Info("🔄 Token Satu Sehat berhasil di-refresh secara manual")
return data, nil
}
-10
View File
@@ -1,10 +0,0 @@
package kfa
// KFASearchParams menampung parameter pencarian produk KFA
type KFASearchParams struct {
Page int `form:"page,default=1"`
Size int `form:"size,default=10"`
ProductType string `form:"product_type"` // Contoh: farmasi, alkes
Keyword string `form:"keyword"` // Kata kunci pencarian produk (jika didukung KFA)
From string `form:"from_"` // Query param from_ (digunakan untuk memfilter tanggal/waktu spesifik)
}
@@ -1,67 +0,0 @@
package kfa
import (
"context"
"encoding/json"
"fmt"
"net/url"
"strconv"
"service/internal/interfaces/satusehat"
"service/pkg/errors"
"service/pkg/logger"
)
type Repository interface {
GetByCode(ctx context.Context, code string) (map[string]interface{}, error)
GetProducts(ctx context.Context, params KFASearchParams) (map[string]interface{}, error)
}
type repository struct {
client satusehat.SatuSehatClient
}
func NewRepository(client satusehat.SatuSehatClient) Repository {
return &repository{client: client}
}
func parseResponse(body []byte) (map[string]interface{}, error) {
var res map[string]interface{}
if err := json.Unmarshal(body, &res); err != nil {
return nil, fmt.Errorf("failed to parse KFA response: %w", err)
}
return res, nil
}
func (r *repository) GetByCode(ctx context.Context, code string) (map[string]interface{}, error) {
endpoint := fmt.Sprintf("/products?identifier=kfa&code=%s", url.QueryEscape(code))
respBytes, err := r.client.DoKFA(ctx, "GET", endpoint, nil)
if err != nil {
logger.Default().Error("Gagal mencari produk KFA berdasarkan kode", logger.ErrorField(err))
return nil, errors.InternalError().Message("Gagal mencari produk KFA").Cause(err).Build()
}
return parseResponse(respBytes)
}
func (r *repository) GetProducts(ctx context.Context, params KFASearchParams) (map[string]interface{}, error) {
q := url.Values{}
q.Add("page", strconv.Itoa(params.Page))
q.Add("size", strconv.Itoa(params.Size))
if params.ProductType != "" {
q.Add("product_type", params.ProductType)
}
if params.Keyword != "" {
q.Add("keyword", params.Keyword)
}
if params.From != "" {
q.Add("from_", params.From)
}
endpoint := "/products/all?" + q.Encode()
respBytes, err := r.client.DoKFA(ctx, "GET", endpoint, nil)
if err != nil {
logger.Default().Error("Gagal mencari daftar produk KFA", logger.ErrorField(err))
return nil, errors.InternalError().Message("Gagal mengambil daftar produk KFA").Cause(err).Build()
}
return parseResponse(respBytes)
}
@@ -1,24 +0,0 @@
package kfa
import "context"
type Service interface {
GetByCode(ctx context.Context, code string) (map[string]interface{}, error)
GetProducts(ctx context.Context, params KFASearchParams) (map[string]interface{}, error)
}
type service struct {
repo Repository
}
func NewService(repo Repository) Service {
return &service{repo: repo}
}
func (s *service) GetByCode(ctx context.Context, code string) (map[string]interface{}, error) {
return s.repo.GetByCode(ctx, code)
}
func (s *service) GetProducts(ctx context.Context, params KFASearchParams) (map[string]interface{}, error) {
return s.repo.GetProducts(ctx, params)
}
@@ -1,8 +0,0 @@
package location
// LocationSearchParams menampung kriteria pencarian Ruangan/Lokasi Satu Sehat
type LocationSearchParams struct {
Name string `form:"name"`
Organization string `form:"organization"` // ID Faskes pembuat
Identifier string `form:"identifier"` // Format {System}|{Value}
}
@@ -1,7 +0,0 @@
package location
// Konstanta standar untuk sistem identifier Location di Satu Sehat.
const (
IdentifierSystemLocation = "http://sys-ids.kemkes.go.id/location"
PhysicalTypeSystem = "http://terminology.hl7.org/CodeSystem/location-physical-type"
)
@@ -1,90 +0,0 @@
package location
import (
"context"
"encoding/json"
"fmt"
"net/url"
"service/internal/interfaces/satusehat"
"service/pkg/errors"
"service/pkg/logger"
)
type Repository interface {
GetByID(ctx context.Context, id string) (map[string]interface{}, error)
Search(ctx context.Context, params LocationSearchParams) (map[string]interface{}, error)
Create(ctx context.Context, payload interface{}) (map[string]interface{}, error)
Update(ctx context.Context, id string, payload interface{}) (map[string]interface{}, error)
Patch(ctx context.Context, id string, payload interface{}) (map[string]interface{}, error)
}
type repository struct {
client satusehat.SatuSehatClient
}
func NewRepository(client satusehat.SatuSehatClient) Repository {
return &repository{client: client}
}
func parseResponse(body []byte) (map[string]interface{}, error) {
var res map[string]interface{}
if err := json.Unmarshal(body, &res); err != nil {
return nil, fmt.Errorf("failed to parse SatuSehat response: %w", err)
}
return res, nil
}
func (r *repository) GetByID(ctx context.Context, id string) (map[string]interface{}, error) {
endpoint := fmt.Sprintf("/Location/%s", id)
respBytes, err := r.client.DoRequest(ctx, "GET", endpoint, nil)
if err != nil {
logger.Default().Error("Gagal mencari Location berdasarkan ID", logger.ErrorField(err))
return nil, errors.InternalError().Message("Gagal mencari Location").Cause(err).Build()
}
return parseResponse(respBytes)
}
func (r *repository) Search(ctx context.Context, params LocationSearchParams) (map[string]interface{}, error) {
q := url.Values{}
if params.Name != "" {
q.Add("name", params.Name)
}
if params.Organization != "" {
q.Add("organization", params.Organization)
}
if params.Identifier != "" {
q.Add("identifier", params.Identifier)
}
endpoint := "/Location?" + q.Encode()
respBytes, err := r.client.DoRequest(ctx, "GET", endpoint, nil)
if err != nil {
logger.Default().Error("Gagal mencari Location", logger.ErrorField(err))
return nil, errors.InternalError().Message("Gagal melakukan pencarian Location").Cause(err).Build()
}
return parseResponse(respBytes)
}
func (r *repository) Create(ctx context.Context, payload interface{}) (map[string]interface{}, error) {
respBytes, err := r.client.DoRequest(ctx, "POST", "/Location", payload)
if err != nil {
return nil, err
}
return parseResponse(respBytes)
}
func (r *repository) Update(ctx context.Context, id string, payload interface{}) (map[string]interface{}, error) {
respBytes, err := r.client.DoRequest(ctx, "PUT", fmt.Sprintf("/Location/%s", id), payload)
if err != nil {
return nil, err
}
return parseResponse(respBytes)
}
func (r *repository) Patch(ctx context.Context, id string, payload interface{}) (map[string]interface{}, error) {
respBytes, err := r.client.DoRequest(ctx, "PATCH", fmt.Sprintf("/Location/%s", id), payload)
if err != nil {
return nil, err
}
return parseResponse(respBytes)
}
@@ -1,37 +0,0 @@
package location
import "context"
type Service interface {
GetByID(ctx context.Context, id string) (map[string]interface{}, error)
Search(ctx context.Context, params LocationSearchParams) (map[string]interface{}, error)
Create(ctx context.Context, payload interface{}) (map[string]interface{}, error)
Update(ctx context.Context, id string, payload interface{}) (map[string]interface{}, error)
Patch(ctx context.Context, id string, payload interface{}) (map[string]interface{}, error)
}
type service struct {
repo Repository
}
func NewService(repo Repository) Service {
return &service{repo: repo}
}
func (s *service) GetByID(ctx context.Context, id string) (map[string]interface{}, error) {
return s.repo.GetByID(ctx, id)
}
func (s *service) Search(ctx context.Context, params LocationSearchParams) (map[string]interface{}, error) {
return s.repo.Search(ctx, params)
}
func (s *service) Create(ctx context.Context, payload interface{}) (map[string]interface{}, error) {
return s.repo.Create(ctx, payload)
}
func (s *service) Update(ctx context.Context, id string, payload interface{}) (map[string]interface{}, error) {
return s.repo.Update(ctx, id, payload)
}
func (s *service) Patch(ctx context.Context, id string, payload interface{}) (map[string]interface{}, error) {
return s.repo.Patch(ctx, id, payload)
}
@@ -1,8 +0,0 @@
package organization
// OrganizationSearchParams menampung parameter untuk pencarian Organisasi
type OrganizationSearchParams struct {
Name string `form:"name"`
PartOf string `form:"partof"` // ID Organisasi Parent
Identifier string `form:"identifier"` // Identifier Organisasi (contoh: http://sys-ids.kemkes.go.id/organization/10000004|R220001)
}
@@ -1,7 +0,0 @@
package organization
// Konstanta standar untuk sistem identifier Organization di Satu Sehat.
const (
IdentifierSystemOrg = "http://sys-ids.kemkes.go.id/organization"
TypeSystemOrg = "http://terminology.hl7.org/CodeSystem/organization-type"
)
@@ -1,91 +0,0 @@
package organization
import (
"context"
"encoding/json"
"fmt"
"net/url"
"service/internal/interfaces/satusehat"
"service/pkg/errors"
"service/pkg/logger"
)
type Repository interface {
GetByID(ctx context.Context, id string) (map[string]interface{}, error)
Search(ctx context.Context, params OrganizationSearchParams) (map[string]interface{}, error)
Create(ctx context.Context, payload interface{}) (map[string]interface{}, error)
Update(ctx context.Context, id string, payload interface{}) (map[string]interface{}, error)
Patch(ctx context.Context, id string, payload interface{}) (map[string]interface{}, error)
}
type repository struct {
client satusehat.SatuSehatClient
}
func NewRepository(client satusehat.SatuSehatClient) Repository {
return &repository{client: client}
}
func parseResponse(body []byte) (map[string]interface{}, error) {
var res map[string]interface{}
if err := json.Unmarshal(body, &res); err != nil {
return nil, fmt.Errorf("failed to parse SatuSehat response: %w", err)
}
return res, nil
}
func (r *repository) GetByID(ctx context.Context, id string) (map[string]interface{}, error) {
endpoint := fmt.Sprintf("/Organization/%s", id)
respBytes, err := r.client.DoRequest(ctx, "GET", endpoint, nil)
if err != nil {
logger.Default().Error("Gagal mencari Organization berdasarkan ID", logger.ErrorField(err))
return nil, errors.InternalError().Message("Gagal mencari Organization").Cause(err).Build()
}
return parseResponse(respBytes)
}
func (r *repository) Search(ctx context.Context, params OrganizationSearchParams) (map[string]interface{}, error) {
q := url.Values{}
if params.Name != "" {
q.Add("name", params.Name)
}
if params.PartOf != "" {
q.Add("partof", params.PartOf)
}
if params.Identifier != "" {
q.Add("identifier", params.Identifier)
}
endpoint := "/Organization?" + q.Encode()
respBytes, err := r.client.DoRequest(ctx, "GET", endpoint, nil)
if err != nil {
logger.Default().Error("Gagal mencari Organization", logger.ErrorField(err))
return nil, errors.InternalError().Message("Gagal melakukan pencarian Organization").Cause(err).Build()
}
return parseResponse(respBytes)
}
func (r *repository) Create(ctx context.Context, payload interface{}) (map[string]interface{}, error) {
respBytes, err := r.client.DoRequest(ctx, "POST", "/Organization", payload)
if err != nil {
return nil, err
}
return parseResponse(respBytes)
}
func (r *repository) Update(ctx context.Context, id string, payload interface{}) (map[string]interface{}, error) {
respBytes, err := r.client.DoRequest(ctx, "PUT", fmt.Sprintf("/Organization/%s", id), payload)
if err != nil {
return nil, err
}
return parseResponse(respBytes)
}
func (r *repository) Patch(ctx context.Context, id string, payload interface{}) (map[string]interface{}, error) {
respBytes, err := r.client.DoRequest(ctx, "PATCH", fmt.Sprintf("/Organization/%s", id), payload)
if err != nil {
return nil, err
}
return parseResponse(respBytes)
}
@@ -1,37 +0,0 @@
package organization
import "context"
type Service interface {
GetByID(ctx context.Context, id string) (map[string]interface{}, error)
Search(ctx context.Context, params OrganizationSearchParams) (map[string]interface{}, error)
Create(ctx context.Context, payload interface{}) (map[string]interface{}, error)
Update(ctx context.Context, id string, payload interface{}) (map[string]interface{}, error)
Patch(ctx context.Context, id string, payload interface{}) (map[string]interface{}, error)
}
type service struct {
repo Repository
}
func NewService(repo Repository) Service {
return &service{repo: repo}
}
func (s *service) GetByID(ctx context.Context, id string) (map[string]interface{}, error) {
return s.repo.GetByID(ctx, id)
}
func (s *service) Search(ctx context.Context, params OrganizationSearchParams) (map[string]interface{}, error) {
return s.repo.Search(ctx, params)
}
func (s *service) Create(ctx context.Context, payload interface{}) (map[string]interface{}, error) {
return s.repo.Create(ctx, payload)
}
func (s *service) Update(ctx context.Context, id string, payload interface{}) (map[string]interface{}, error) {
return s.repo.Update(ctx, id, payload)
}
func (s *service) Patch(ctx context.Context, id string, payload interface{}) (map[string]interface{}, error) {
return s.repo.Patch(ctx, id, payload)
}
@@ -1,20 +0,0 @@
package patient
// CreatePatientRequest adalah DTO internal yang lebih sederhana untuk diisi oleh Frontend.
type CreatePatientRequest struct {
NIK string `json:"nik" validate:"required"`
Name string `json:"name" validate:"required"`
Gender string `json:"gender" validate:"required"` // male, female, other, unknown
BirthDate string `json:"birth_date" validate:"required"` // Format: YYYY-MM-DD
Phone string `json:"phone"` // Opsional
Address string `json:"address"` // Opsional
}
// PatientSearchParams menampung berbagai kriteria pencarian pasien Satu Sehat.
type PatientSearchParams struct {
Name string `form:"name"`
BirthDate string `form:"birthdate"` // Format: YYYY-MM-DD
Gender string `form:"gender"` // male, female, other, unknown
NIK string `form:"nik"`
NIKIbu string `form:"nik_ibu"`
}
@@ -1,14 +0,0 @@
package patient
// PatientEntity mewakili konstanta dan aturan domain (business rules) untuk Pasien Satu Sehat.
const (
// Standar Identifier System Kemenkes
IdentifierSystemNIK = "https://fhir.kemkes.go.id/id/nik"
IdentifierSystemNIKIbu = "https://fhir.kemkes.go.id/id/nik-ibu"
IdentifierSystemIHS = "https://fhir.kemkes.go.id/id/ihs-number"
GenderMale = "male"
GenderFemale = "female"
GenderOther = "other"
GenderUnknown = "unknown"
)
@@ -1,123 +0,0 @@
package patient
import (
"context"
"encoding/json"
"fmt"
"net/url"
"service/internal/interfaces/satusehat"
"service/pkg/errors"
"service/pkg/logger"
)
// Repository mendefinisikan antarmuka komunikasi ke eksternal (Kemenkes API).
type Repository interface {
GetByNIK(ctx context.Context, nik string) (map[string]interface{}, error)
GetByID(ctx context.Context, id string) (map[string]interface{}, error)
Search(ctx context.Context, params PatientSearchParams) (map[string]interface{}, error)
Create(ctx context.Context, req CreatePatientRequest) (map[string]interface{}, error)
}
type repository struct {
client satusehat.SatuSehatClient
}
// NewRepository membuat instance baru dari patient repository.
func NewRepository(client satusehat.SatuSehatClient) Repository {
return &repository{client: client}
}
// helper untuk mem-parsing response body []byte menjadi Map JSON
func parseResponse(body []byte) (map[string]interface{}, error) {
var res map[string]interface{}
if err := json.Unmarshal(body, &res); err != nil {
return nil, fmt.Errorf("failed to parse SatuSehat response: %w", err)
}
return res, nil
}
func (r *repository) GetByNIK(ctx context.Context, nik string) (map[string]interface{}, error) {
endpoint := fmt.Sprintf("/Patient?identifier=%s|%s", IdentifierSystemNIK, nik)
respBytes, err := r.client.DoRequest(ctx, "GET", endpoint, nil)
if err != nil {
logger.Default().Error("Gagal mencari Pasien berdasarkan NIK", logger.ErrorField(err))
return nil, errors.InternalError().Message("Gagal mencari Pasien ke Satu Sehat").Cause(err).Build()
}
return parseResponse(respBytes)
}
func (r *repository) GetByID(ctx context.Context, id string) (map[string]interface{}, error) {
endpoint := fmt.Sprintf("/Patient/%s", id)
respBytes, err := r.client.DoRequest(ctx, "GET", endpoint, nil)
if err != nil {
logger.Default().Error("Gagal mencari Pasien berdasarkan ID", logger.ErrorField(err))
return nil, errors.InternalError().Message("Gagal mencari Pasien ke Satu Sehat").Cause(err).Build()
}
return parseResponse(respBytes)
}
func (r *repository) Search(ctx context.Context, params PatientSearchParams) (map[string]interface{}, error) {
q := url.Values{}
if params.Name != "" {
q.Add("name", params.Name)
}
if params.BirthDate != "" {
q.Add("birthdate", params.BirthDate)
}
if params.Gender != "" {
q.Add("gender", params.Gender)
}
if params.NIK != "" {
q.Add("identifier", IdentifierSystemNIK+"|"+params.NIK)
} else if params.NIKIbu != "" {
q.Add("identifier", IdentifierSystemNIKIbu+"|"+params.NIKIbu)
}
endpoint := "/Patient?" + q.Encode()
respBytes, err := r.client.DoRequest(ctx, "GET", endpoint, nil)
if err != nil {
logger.Default().Error("Gagal mencari Pasien dengan parameter dinamis", logger.ErrorField(err))
return nil, errors.InternalError().Message("Gagal melakukan pencarian Pasien ke Satu Sehat").Cause(err).Build()
}
return parseResponse(respBytes)
}
func (r *repository) Create(ctx context.Context, req CreatePatientRequest) (map[string]interface{}, error) {
payload := satusehat.NewFHIRPayload("Patient").
Set("active", true).
Set("gender", req.Gender).
Set("birthDate", req.BirthDate).
Append("identifier", map[string]interface{}{
"use": "official",
"system": IdentifierSystemNIK,
"value": req.NIK,
}).
Append("name", map[string]interface{}{
"use": "official",
"text": req.Name,
})
if req.Phone != "" {
payload.Append("telecom", map[string]interface{}{
"system": "phone",
"value": req.Phone,
"use": "mobile",
})
}
if req.Address != "" {
payload.Append("address", map[string]interface{}{
"use": "home",
"text": req.Address,
})
}
respBytes, err := r.client.DoRequest(ctx, "POST", "/Patient", payload)
if err != nil {
logger.Default().Error("Gagal mendaftarkan Pasien", logger.ErrorField(err))
return nil, errors.InternalError().Message("Gagal mendaftarkan Pasien ke Satu Sehat").Cause(err).Build()
}
return parseResponse(respBytes)
}
@@ -1,38 +0,0 @@
package patient
import (
"context"
)
// Service mendefinisikan interface untuk manajemen Pasien Satu Sehat
type Service interface {
GetByNIK(ctx context.Context, nik string) (map[string]interface{}, error)
GetByID(ctx context.Context, id string) (map[string]interface{}, error)
Search(ctx context.Context, params PatientSearchParams) (map[string]interface{}, error)
Create(ctx context.Context, req CreatePatientRequest) (map[string]interface{}, error)
}
type service struct {
repo Repository
}
// NewService membuat instance baru dari patient service.
func NewService(repo Repository) Service {
return &service{repo: repo}
}
func (s *service) GetByNIK(ctx context.Context, nik string) (map[string]interface{}, error) {
return s.repo.GetByNIK(ctx, nik)
}
func (s *service) GetByID(ctx context.Context, id string) (map[string]interface{}, error) {
return s.repo.GetByID(ctx, id)
}
func (s *service) Search(ctx context.Context, params PatientSearchParams) (map[string]interface{}, error) {
return s.repo.Search(ctx, params)
}
func (s *service) Create(ctx context.Context, req CreatePatientRequest) (map[string]interface{}, error) {
return s.repo.Create(ctx, req)
}
@@ -1,9 +0,0 @@
package practitioner
// PractitionerSearchParams menampung berbagai kriteria pencarian Tenaga Medis Satu Sehat.
type PractitionerSearchParams struct {
Name string `form:"name"`
NIK string `form:"nik"`
Gender string `form:"gender"` // male, female
BirthDate string `form:"birthdate"` // Format: YYYY-MM-DD
}
@@ -1,8 +0,0 @@
package practitioner
// Konstanta standar untuk sistem identifier Practitioner di Satu Sehat.
const (
IdentifierSystemNIK = "https://fhir.kemkes.go.id/id/nik"
IdentifierSystemIHS = "https://fhir.kemkes.go.id/id/nakes-his-number"
IdentifierSystemSTR = "https://fhir.kemkes.go.id/id/str-kki-number"
)
@@ -1,78 +0,0 @@
package practitioner
import (
"context"
"encoding/json"
"fmt"
"net/url"
"service/internal/interfaces/satusehat"
"service/pkg/errors"
"service/pkg/logger"
)
type Repository interface {
GetByNIK(ctx context.Context, nik string) (map[string]interface{}, error)
GetByID(ctx context.Context, id string) (map[string]interface{}, error)
Search(ctx context.Context, params PractitionerSearchParams) (map[string]interface{}, error)
}
type repository struct {
client satusehat.SatuSehatClient
}
func NewRepository(client satusehat.SatuSehatClient) Repository {
return &repository{client: client}
}
func parseResponse(body []byte) (map[string]interface{}, error) {
var res map[string]interface{}
if err := json.Unmarshal(body, &res); err != nil {
return nil, fmt.Errorf("failed to parse SatuSehat response: %w", err)
}
return res, nil
}
func (r *repository) GetByNIK(ctx context.Context, nik string) (map[string]interface{}, error) {
endpoint := fmt.Sprintf("/Practitioner?identifier=%s|%s", IdentifierSystemNIK, nik)
respBytes, err := r.client.DoRequest(ctx, "GET", endpoint, nil)
if err != nil {
logger.Default().Error("Gagal mencari Practitioner berdasarkan NIK", logger.ErrorField(err))
return nil, errors.InternalError().Message("Gagal mencari Practitioner ke Satu Sehat").Cause(err).Build()
}
return parseResponse(respBytes)
}
func (r *repository) GetByID(ctx context.Context, id string) (map[string]interface{}, error) {
endpoint := fmt.Sprintf("/Practitioner/%s", id)
respBytes, err := r.client.DoRequest(ctx, "GET", endpoint, nil)
if err != nil {
logger.Default().Error("Gagal mencari Practitioner berdasarkan ID", logger.ErrorField(err))
return nil, errors.InternalError().Message("Gagal mencari Practitioner ke Satu Sehat").Cause(err).Build()
}
return parseResponse(respBytes)
}
func (r *repository) Search(ctx context.Context, params PractitionerSearchParams) (map[string]interface{}, error) {
q := url.Values{}
if params.Name != "" {
q.Add("name", params.Name)
}
if params.Gender != "" {
q.Add("gender", params.Gender)
}
if params.BirthDate != "" {
q.Add("birthdate", params.BirthDate)
}
if params.NIK != "" {
q.Add("identifier", IdentifierSystemNIK+"|"+params.NIK)
}
endpoint := "/Practitioner?" + q.Encode()
respBytes, err := r.client.DoRequest(ctx, "GET", endpoint, nil)
if err != nil {
logger.Default().Error("Gagal mencari Practitioner", logger.ErrorField(err))
return nil, errors.InternalError().Message("Gagal melakukan pencarian Practitioner").Cause(err).Build()
}
return parseResponse(respBytes)
}
@@ -1,29 +0,0 @@
package practitioner
import "context"
type Service interface {
GetByNIK(ctx context.Context, nik string) (map[string]interface{}, error)
GetByID(ctx context.Context, id string) (map[string]interface{}, error)
Search(ctx context.Context, params PractitionerSearchParams) (map[string]interface{}, error)
}
type service struct {
repo Repository
}
func NewService(repo Repository) Service {
return &service{repo: repo}
}
func (s *service) GetByNIK(ctx context.Context, nik string) (map[string]interface{}, error) {
return s.repo.GetByNIK(ctx, nik)
}
func (s *service) GetByID(ctx context.Context, id string) (map[string]interface{}, error) {
return s.repo.GetByID(ctx, id)
}
func (s *service) Search(ctx context.Context, params PractitionerSearchParams) (map[string]interface{}, error) {
return s.repo.Search(ctx, params)
}
@@ -1,17 +0,0 @@
package allergyintolerance
import "time"
type AllergyIntoleranceRequest struct {
PatientID string `json:"patient_id" binding:"required"`
PatientName string `json:"patient_name" binding:"required"`
EncounterID string `json:"encounter_id" binding:"required"`
ClinicalStatus string `json:"clinical_status" binding:"required,oneof=active inactive resolved"`
VerificationStatus string `json:"verification_status" binding:"required,oneof=unconfirmed presumed confirmed refuted entered-in-error"`
Category string `json:"category" binding:"required,oneof=food medication environment biologic"`
Code string `json:"code" binding:"required"` // SNOMED CT Code
Display string `json:"display" binding:"required"` // Display Name
RecordedDate time.Time `json:"recorded_date" binding:"required"`
}
type AllergyIntolerancePatchRequest []map[string]interface{}
@@ -1,42 +0,0 @@
package allergyintolerance
import (
"time"
"service/internal/interfaces/satusehat"
)
func MapRequestToFHIR(req AllergyIntoleranceRequest) satusehat.FHIRPayload {
return satusehat.NewFHIRPayload("AllergyIntolerance").
Set("clinicalStatus", map[string]interface{}{
"coding": []map[string]interface{}{
{
"system": "http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical",
"code": req.ClinicalStatus,
},
},
}).
Set("verificationStatus", map[string]interface{}{
"coding": []map[string]interface{}{
{
"system": "http://terminology.hl7.org/CodeSystem/allergyintolerance-verification",
"code": req.VerificationStatus,
},
},
}).
Set("category", []string{req.Category}).
Set("code", map[string]interface{}{
"coding": []map[string]interface{}{
{
"system": "http://snomed.info/sct",
"code": req.Code,
"display": req.Display,
},
},
}).
Set("patient", map[string]interface{}{
"reference": "Patient/" + req.PatientID,
"display": req.PatientName,
}).
Set("recordedDate", req.RecordedDate.Format(time.RFC3339))
}
@@ -1,51 +0,0 @@
package allergyintolerance
import (
"context"
"encoding/json"
"fmt"
"net/url"
"service/internal/interfaces/satusehat"
)
type Repository interface {
Create(ctx context.Context, payload interface{}) (*satusehat.FHIRResponse, error)
Update(ctx context.Context, id string, payload interface{}) (*satusehat.FHIRResponse, error)
Patch(ctx context.Context, id string, req AllergyIntolerancePatchRequest) (*satusehat.FHIRResponse, error)
GetByID(ctx context.Context, id string) (*satusehat.FHIRResponse, error)
Search(ctx context.Context, queryParams url.Values) (*satusehat.FHIRResponse, error)
}
type repository struct{ client satusehat.SatuSehatClient }
func NewRepository(client satusehat.SatuSehatClient) Repository { return &repository{client: client} }
func (r *repository) executeRequest(ctx context.Context, method, endpoint string, req interface{}) (*satusehat.FHIRResponse, error) {
resp, err := r.client.DoRequest(ctx, method, endpoint, req)
if err != nil {
return nil, err
}
var result map[string]interface{}
if err := json.Unmarshal(resp, &result); err != nil {
return nil, fmt.Errorf("failed to unmarshal response: %w", err)
}
var resourceID string
if id, ok := result["id"].(string); ok {
resourceID = id
}
return &satusehat.FHIRResponse{ID: resourceID, FullResponse: result, RawResponse: resp}, nil
}
func (r *repository) Create(ctx context.Context, payload interface{}) (*satusehat.FHIRResponse, error) {
return r.executeRequest(ctx, "POST", "/AllergyIntolerance", payload)
}
func (r *repository) Update(ctx context.Context, id string, payload interface{}) (*satusehat.FHIRResponse, error) {
return r.executeRequest(ctx, "PUT", fmt.Sprintf("/AllergyIntolerance/%s", id), payload)
}
func (r *repository) Patch(ctx context.Context, id string, req AllergyIntolerancePatchRequest) (*satusehat.FHIRResponse, error) {
return r.executeRequest(ctx, "PATCH", fmt.Sprintf("/AllergyIntolerance/%s", id), req)
}
func (r *repository) GetByID(ctx context.Context, id string) (*satusehat.FHIRResponse, error) {
return r.executeRequest(ctx, "GET", fmt.Sprintf("/AllergyIntolerance/%s", id), nil)
}
func (r *repository) Search(ctx context.Context, queryParams url.Values) (*satusehat.FHIRResponse, error) {
return r.executeRequest(ctx, "GET", fmt.Sprintf("/AllergyIntolerance?%s", queryParams.Encode()), nil)
}
@@ -1,38 +0,0 @@
package allergyintolerance
import (
"context"
"net/url"
"service/internal/interfaces/satusehat"
"service/pkg/errors"
)
type Service interface {
Create(ctx context.Context, req AllergyIntoleranceRequest) (*satusehat.FHIRResponse, error)
Update(ctx context.Context, id string, req AllergyIntoleranceRequest) (*satusehat.FHIRResponse, error)
Patch(ctx context.Context, id string, req AllergyIntolerancePatchRequest) (*satusehat.FHIRResponse, error)
GetByID(ctx context.Context, id string) (*satusehat.FHIRResponse, error)
Search(ctx context.Context, queryParams url.Values) (*satusehat.FHIRResponse, error)
}
type service struct{ repo Repository }
func NewService(repo Repository) Service { return &service{repo: repo} }
func (s *service) Create(ctx context.Context, req AllergyIntoleranceRequest) (*satusehat.FHIRResponse, error) {
return s.repo.Create(ctx, MapRequestToFHIR(req))
}
func (s *service) Update(ctx context.Context, id string, req AllergyIntoleranceRequest) (*satusehat.FHIRResponse, error) {
if id == "" {
return nil, errors.NewValidationError().Message("ID is required").Build()
}
return s.repo.Update(ctx, id, MapRequestToFHIR(req).Set("id", id))
}
func (s *service) Patch(ctx context.Context, id string, req AllergyIntolerancePatchRequest) (*satusehat.FHIRResponse, error) {
return s.repo.Patch(ctx, id, req)
}
func (s *service) GetByID(ctx context.Context, id string) (*satusehat.FHIRResponse, error) {
return s.repo.GetByID(ctx, id)
}
func (s *service) Search(ctx context.Context, queryParams url.Values) (*satusehat.FHIRResponse, error) {
return s.repo.Search(ctx, queryParams)
}
@@ -1,18 +0,0 @@
package careplan
import "time"
type CarePlanRequest struct {
PatientID string `json:"patient_id" binding:"required"`
PatientName string `json:"patient_name" binding:"required"`
EncounterID string `json:"encounter_id" binding:"required"`
PractitionerID string `json:"practitioner_id" binding:"required"`
PractitionerName string `json:"practitioner_name" binding:"required"`
Status string `json:"status" binding:"required,oneof=draft active on-hold revoked completed entered-in-error unknown"`
Intent string `json:"intent" binding:"required,oneof=proposal plan order option"`
Title string `json:"title" binding:"required"`
Description string `json:"description" binding:"required"`
CreatedDate time.Time `json:"created_date" binding:"required"`
}
type CarePlanPatchRequest []map[string]interface{}
@@ -1,24 +0,0 @@
package careplan
import (
"service/internal/interfaces/satusehat"
"time"
)
func MapRequestToFHIR(req CarePlanRequest) satusehat.FHIRPayload {
return satusehat.NewFHIRPayload("CarePlan").
Set("status", req.Status).
Set("intent", req.Intent).
Set("title", req.Title).
Set("description", req.Description).
Set("subject", map[string]interface{}{
"reference": "Patient/" + req.PatientID,
"display": req.PatientName,
}).
Set("encounter", map[string]interface{}{"reference": "Encounter/" + req.EncounterID}).
Set("author", map[string]interface{}{
"reference": "Practitioner/" + req.PractitionerID,
"display": req.PractitionerName,
}).
Set("created", req.CreatedDate.Format(time.RFC3339))
}
@@ -1,51 +0,0 @@
package careplan
import (
"context"
"encoding/json"
"fmt"
"net/url"
"service/internal/interfaces/satusehat"
)
type Repository interface {
Create(ctx context.Context, payload interface{}) (*satusehat.FHIRResponse, error)
Update(ctx context.Context, id string, payload interface{}) (*satusehat.FHIRResponse, error)
Patch(ctx context.Context, id string, req CarePlanPatchRequest) (*satusehat.FHIRResponse, error)
GetByID(ctx context.Context, id string) (*satusehat.FHIRResponse, error)
Search(ctx context.Context, queryParams url.Values) (*satusehat.FHIRResponse, error)
}
type repository struct{ client satusehat.SatuSehatClient }
func NewRepository(client satusehat.SatuSehatClient) Repository { return &repository{client: client} }
func (r *repository) executeRequest(ctx context.Context, method, endpoint string, req interface{}) (*satusehat.FHIRResponse, error) {
resp, err := r.client.DoRequest(ctx, method, endpoint, req)
if err != nil {
return nil, err
}
var result map[string]interface{}
if err := json.Unmarshal(resp, &result); err != nil {
return nil, fmt.Errorf("failed to unmarshal response: %w", err)
}
var resourceID string
if id, ok := result["id"].(string); ok {
resourceID = id
}
return &satusehat.FHIRResponse{ID: resourceID, FullResponse: result, RawResponse: resp}, nil
}
func (r *repository) Create(ctx context.Context, payload interface{}) (*satusehat.FHIRResponse, error) {
return r.executeRequest(ctx, "POST", "/CarePlan", payload)
}
func (r *repository) Update(ctx context.Context, id string, payload interface{}) (*satusehat.FHIRResponse, error) {
return r.executeRequest(ctx, "PUT", fmt.Sprintf("/CarePlan/%s", id), payload)
}
func (r *repository) Patch(ctx context.Context, id string, req CarePlanPatchRequest) (*satusehat.FHIRResponse, error) {
return r.executeRequest(ctx, "PATCH", fmt.Sprintf("/CarePlan/%s", id), req)
}
func (r *repository) GetByID(ctx context.Context, id string) (*satusehat.FHIRResponse, error) {
return r.executeRequest(ctx, "GET", fmt.Sprintf("/CarePlan/%s", id), nil)
}
func (r *repository) Search(ctx context.Context, queryParams url.Values) (*satusehat.FHIRResponse, error) {
return r.executeRequest(ctx, "GET", fmt.Sprintf("/CarePlan?%s", queryParams.Encode()), nil)
}
@@ -1,37 +0,0 @@
package careplan
import (
"context"
"net/url"
"service/internal/interfaces/satusehat"
"service/pkg/errors"
)
type Service interface {
Create(ctx context.Context, req CarePlanRequest) (*satusehat.FHIRResponse, error)
Update(ctx context.Context, id string, req CarePlanRequest) (*satusehat.FHIRResponse, error)
Patch(ctx context.Context, id string, req CarePlanPatchRequest) (*satusehat.FHIRResponse, error)
GetByID(ctx context.Context, id string) (*satusehat.FHIRResponse, error)
Search(ctx context.Context, queryParams url.Values) (*satusehat.FHIRResponse, error)
}
type service struct{ repo Repository }
func NewService(repo Repository) Service { return &service{repo: repo} }
func (s *service) Create(ctx context.Context, req CarePlanRequest) (*satusehat.FHIRResponse, error) {
return s.repo.Create(ctx, MapRequestToFHIR(req))
}
func (s *service) Update(ctx context.Context, id string, req CarePlanRequest) (*satusehat.FHIRResponse, error) {
if id == "" {
return nil, errors.NewValidationError().Message("ID is required").Build()
}
return s.repo.Update(ctx, id, MapRequestToFHIR(req).Set("id", id))
}
func (s *service) Patch(ctx context.Context, id string, req CarePlanPatchRequest) (*satusehat.FHIRResponse, error) {
return s.repo.Patch(ctx, id, req)
}
func (s *service) GetByID(ctx context.Context, id string) (*satusehat.FHIRResponse, error) {
return s.repo.GetByID(ctx, id)
}
func (s *service) Search(ctx context.Context, queryParams url.Values) (*satusehat.FHIRResponse, error) {
return s.repo.Search(ctx, queryParams)
}
@@ -1,17 +0,0 @@
package clinicalimpression
import "time"
type ClinicalImpressionRequest struct {
PatientID string `json:"patient_id" binding:"required"`
PatientName string `json:"patient_name" binding:"required"`
EncounterID string `json:"encounter_id" binding:"required"`
PractitionerID string `json:"practitioner_id" binding:"required"`
PractitionerName string `json:"practitioner_name" binding:"required"`
Status string `json:"status" binding:"required,oneof=in-progress completed entered-in-error"`
Date time.Time `json:"date" binding:"required"`
Summary string `json:"summary" binding:"required"`
Description string `json:"description" binding:"required"`
}
type ClinicalImpressionPatchRequest []map[string]interface{}
@@ -1,23 +0,0 @@
package clinicalimpression
import (
"service/internal/interfaces/satusehat"
"time"
)
func MapRequestToFHIR(req ClinicalImpressionRequest) satusehat.FHIRPayload {
return satusehat.NewFHIRPayload("ClinicalImpression").
Set("status", req.Status).
Set("description", req.Description).
Set("summary", req.Summary).
Set("subject", map[string]interface{}{
"reference": "Patient/" + req.PatientID,
"display": req.PatientName,
}).
Set("encounter", map[string]interface{}{"reference": "Encounter/" + req.EncounterID}).
Set("assessor", map[string]interface{}{
"reference": "Practitioner/" + req.PractitionerID,
"display": req.PractitionerName,
}).
Set("date", req.Date.Format(time.RFC3339))
}
@@ -1,51 +0,0 @@
package clinicalimpression
import (
"context"
"encoding/json"
"fmt"
"net/url"
"service/internal/interfaces/satusehat"
)
type Repository interface {
Create(ctx context.Context, payload interface{}) (*satusehat.FHIRResponse, error)
Update(ctx context.Context, id string, payload interface{}) (*satusehat.FHIRResponse, error)
Patch(ctx context.Context, id string, req ClinicalImpressionPatchRequest) (*satusehat.FHIRResponse, error)
GetByID(ctx context.Context, id string) (*satusehat.FHIRResponse, error)
Search(ctx context.Context, queryParams url.Values) (*satusehat.FHIRResponse, error)
}
type repository struct{ client satusehat.SatuSehatClient }
func NewRepository(client satusehat.SatuSehatClient) Repository { return &repository{client: client} }
func (r *repository) executeRequest(ctx context.Context, method, endpoint string, req interface{}) (*satusehat.FHIRResponse, error) {
resp, err := r.client.DoRequest(ctx, method, endpoint, req)
if err != nil {
return nil, err
}
var result map[string]interface{}
if err := json.Unmarshal(resp, &result); err != nil {
return nil, fmt.Errorf("failed to unmarshal response: %w", err)
}
var resourceID string
if id, ok := result["id"].(string); ok {
resourceID = id
}
return &satusehat.FHIRResponse{ID: resourceID, FullResponse: result, RawResponse: resp}, nil
}
func (r *repository) Create(ctx context.Context, payload interface{}) (*satusehat.FHIRResponse, error) {
return r.executeRequest(ctx, "POST", "/ClinicalImpression", payload)
}
func (r *repository) Update(ctx context.Context, id string, payload interface{}) (*satusehat.FHIRResponse, error) {
return r.executeRequest(ctx, "PUT", fmt.Sprintf("/ClinicalImpression/%s", id), payload)
}
func (r *repository) Patch(ctx context.Context, id string, req ClinicalImpressionPatchRequest) (*satusehat.FHIRResponse, error) {
return r.executeRequest(ctx, "PATCH", fmt.Sprintf("/ClinicalImpression/%s", id), req)
}
func (r *repository) GetByID(ctx context.Context, id string) (*satusehat.FHIRResponse, error) {
return r.executeRequest(ctx, "GET", fmt.Sprintf("/ClinicalImpression/%s", id), nil)
}
func (r *repository) Search(ctx context.Context, queryParams url.Values) (*satusehat.FHIRResponse, error) {
return r.executeRequest(ctx, "GET", fmt.Sprintf("/ClinicalImpression?%s", queryParams.Encode()), nil)
}
@@ -1,37 +0,0 @@
package clinicalimpression
import (
"context"
"net/url"
"service/internal/interfaces/satusehat"
"service/pkg/errors"
)
type Service interface {
Create(ctx context.Context, req ClinicalImpressionRequest) (*satusehat.FHIRResponse, error)
Update(ctx context.Context, id string, req ClinicalImpressionRequest) (*satusehat.FHIRResponse, error)
Patch(ctx context.Context, id string, req ClinicalImpressionPatchRequest) (*satusehat.FHIRResponse, error)
GetByID(ctx context.Context, id string) (*satusehat.FHIRResponse, error)
Search(ctx context.Context, queryParams url.Values) (*satusehat.FHIRResponse, error)
}
type service struct{ repo Repository }
func NewService(repo Repository) Service { return &service{repo: repo} }
func (s *service) Create(ctx context.Context, req ClinicalImpressionRequest) (*satusehat.FHIRResponse, error) {
return s.repo.Create(ctx, MapRequestToFHIR(req))
}
func (s *service) Update(ctx context.Context, id string, req ClinicalImpressionRequest) (*satusehat.FHIRResponse, error) {
if id == "" {
return nil, errors.NewValidationError().Message("ID is required").Build()
}
return s.repo.Update(ctx, id, MapRequestToFHIR(req).Set("id", id))
}
func (s *service) Patch(ctx context.Context, id string, req ClinicalImpressionPatchRequest) (*satusehat.FHIRResponse, error) {
return s.repo.Patch(ctx, id, req)
}
func (s *service) GetByID(ctx context.Context, id string) (*satusehat.FHIRResponse, error) {
return s.repo.GetByID(ctx, id)
}
func (s *service) Search(ctx context.Context, queryParams url.Values) (*satusehat.FHIRResponse, error) {
return s.repo.Search(ctx, queryParams)
}
@@ -1,17 +0,0 @@
package composition
import "time"
type CompositionRequest struct {
PatientID string `json:"patient_id" binding:"required"`
PatientName string `json:"patient_name" binding:"required"`
EncounterID string `json:"encounter_id" binding:"required"`
PractitionerID string `json:"practitioner_id" binding:"required"`
PractitionerName string `json:"practitioner_name" binding:"required"`
Title string `json:"title" binding:"required"`
Status string `json:"status" binding:"required,oneof=preliminary final amended entered-in-error"` // preliminary, final, amended, entered-in-error
Date time.Time `json:"date" binding:"required"`
}
// CompositionPatchRequest merepresentasikan payload operasi JSON Patch.
type CompositionPatchRequest []map[string]interface{}
@@ -1,38 +0,0 @@
package composition
import (
"time"
"service/internal/interfaces/satusehat"
)
func MapRequestToFHIR(req CompositionRequest) satusehat.FHIRPayload {
payload := satusehat.NewFHIRPayload("Composition").
Set("status", req.Status).
Set("type", map[string]interface{}{
"coding": []map[string]interface{}{
{
"system": "http://loinc.org",
"code": "11503-0", // Example: Medical records
"display": "Medical records",
},
},
}).
Set("subject", map[string]interface{}{
"reference": "Patient/" + req.PatientID,
"display": req.PatientName,
}).
Set("encounter", map[string]interface{}{
"reference": "Encounter/" + req.EncounterID,
}).
Set("date", req.Date.Format(time.RFC3339)).
Set("title", req.Title)
if req.PractitionerID != "" {
payload.Append("author", map[string]interface{}{
"reference": "Practitioner/" + req.PractitionerID,
"display": req.PractitionerName,
})
}
return payload
}
@@ -1,73 +0,0 @@
package composition
import (
"context"
"encoding/json"
"fmt"
"net/url"
"service/internal/interfaces/satusehat"
)
type Repository interface {
Create(ctx context.Context, payload interface{}) (*satusehat.FHIRResponse, error)
Update(ctx context.Context, id string, payload interface{}) (*satusehat.FHIRResponse, error)
Patch(ctx context.Context, id string, req CompositionPatchRequest) (*satusehat.FHIRResponse, error)
GetByID(ctx context.Context, id string) (*satusehat.FHIRResponse, error)
Search(ctx context.Context, queryParams url.Values) (*satusehat.FHIRResponse, error)
}
type repository struct {
client satusehat.SatuSehatClient
}
func NewRepository(client satusehat.SatuSehatClient) Repository {
return &repository{client: client}
}
func (r *repository) executeRequest(ctx context.Context, method, endpoint string, req interface{}) (*satusehat.FHIRResponse, error) {
resp, err := r.client.DoRequest(ctx, method, endpoint, req)
if err != nil {
return nil, err
}
var result map[string]interface{}
if err := json.Unmarshal(resp, &result); err != nil {
return nil, fmt.Errorf("failed to unmarshal response: %w", err)
}
var resourceID string
if id, ok := result["id"].(string); ok {
resourceID = id
}
return &satusehat.FHIRResponse{
ID: resourceID,
FullResponse: result,
RawResponse: resp,
}, nil
}
func (r *repository) Create(ctx context.Context, payload interface{}) (*satusehat.FHIRResponse, error) {
return r.executeRequest(ctx, "POST", "/Composition", payload)
}
func (r *repository) Update(ctx context.Context, id string, payload interface{}) (*satusehat.FHIRResponse, error) {
endpoint := fmt.Sprintf("/Composition/%s", id)
return r.executeRequest(ctx, "PUT", endpoint, payload)
}
func (r *repository) Patch(ctx context.Context, id string, req CompositionPatchRequest) (*satusehat.FHIRResponse, error) {
endpoint := fmt.Sprintf("/Composition/%s", id)
return r.executeRequest(ctx, "PATCH", endpoint, req)
}
func (r *repository) GetByID(ctx context.Context, id string) (*satusehat.FHIRResponse, error) {
endpoint := fmt.Sprintf("/Composition/%s", id)
return r.executeRequest(ctx, "GET", endpoint, nil)
}
func (r *repository) Search(ctx context.Context, queryParams url.Values) (*satusehat.FHIRResponse, error) {
endpoint := fmt.Sprintf("/Composition?%s", queryParams.Encode())
return r.executeRequest(ctx, "GET", endpoint, nil)
}

Some files were not shown because too many files have changed in this diff Show More