From e1b99f8f38faf6254fefd41c4fc35559b15a1e91 Mon Sep 17 00:00:00 2001 From: gigihshs Date: Mon, 24 Nov 2025 09:13:08 +0700 Subject: [PATCH] mastering bridging --- .env-example | 9 + .gitignore | 2 + Dockerfile | 32 + README.md | 1 + builder-api.sh | 5 + builder-swag.sh | 6 + cmd/main.go | 146 + docker-compose.yaml | 10 + go.mod | 37 + go.sum | 93 + internal/config/akses_satusehat.go | 18 + internal/constant/const.go | 29 + internal/constant/error.go | 5 + internal/handler/allergancytoleran_handler.go | 58 + internal/handler/careplan_handler.go | 58 + .../handler/clinicalImpression_handler.go | 62 + internal/handler/composition_handler.go | 62 + internal/handler/condition_handler.go | 75 + internal/handler/diagnosisreport_handler.go | 62 + internal/handler/encounter_handler.go | 81 + internal/handler/episodeofcare_handler.go | 80 + internal/handler/goal_handler.go | 58 + internal/handler/imagingstudy_handler.go | 97 + internal/handler/immunization_handler.go | 58 + .../handler/medicationdispense_handler.go | 58 + internal/handler/medicationrequest_handler.go | 58 + .../handler/medicationstatement_handler.go | 60 + internal/handler/medicine_handler.go | 40 + internal/handler/oauth_handler.go | 33 + internal/handler/observation_handler.go | 78 + internal/handler/organization_handler.go | 54 + internal/handler/patient_handler.go | 47 + internal/handler/practitioner_handler.go | 37 + internal/handler/procedure_handler.go | 71 + .../handler/questionnaireresponse_handler.go | 58 + internal/handler/servicerequest_handler.go | 77 + internal/handler/specimen_handler.go | 77 + .../allergancytoleran_integration.go | 262 ++ internal/integration/careplan_integration.go | 224 ++ .../clinicalImpression_integration.go | 326 ++ .../integration/composition_integration.go | 835 +++++ internal/integration/condition_integration.go | 230 ++ .../diagnosisreport_integration.go | 363 +++ internal/integration/encounter_integration.go | 183 ++ .../integration/episodeofcare_integration.go | 353 ++ internal/integration/goal_integration.go | 221 ++ .../integration/imagingstudy_integration.go | 284 ++ .../integration/immunization_integration.go | 130 + .../medicationdispense_integration.go | 240 ++ .../medicationrequest_integration.go | 260 ++ .../medicationstatement_integration.go | 173 + internal/integration/medicine_integration.go | 196 ++ internal/integration/oauth_integration.go | 54 + .../integration/observation_intergration.go | 382 +++ .../integration/organization_integration.go | 164 + internal/integration/patient_integration.go | 146 + .../integration/practitioner_integration.go | 105 + internal/integration/procedure_integration.go | 236 ++ .../questionnaireresponse_integration.go | 181 ++ .../integration/servicerequest_integration.go | 288 ++ internal/integration/specimen_integration.go | 336 ++ internal/midleware/middleware.go | 4 + internal/model/allergancytoleran.go | 71 + internal/model/careplan.go | 16 + internal/model/clinicalImpression.go | 56 + internal/model/composition.go | 40 + internal/model/condition.go | 11 + internal/model/diagnosisreport.go | 20 + internal/model/encounter.go | 82 + internal/model/episodeofcare.go | 30 + internal/model/goal.go | 20 + internal/model/imagingstudy.go | 31 + internal/model/immunization.go | 30 + internal/model/medicationdispense.go | 46 + internal/model/medicationrequest.go | 44 + internal/model/medicationstatement.go | 60 + internal/model/medicine.go | 33 + internal/model/oauth_model.go | 23 + internal/model/observation.go | 24 + internal/model/organization.go | 38 + internal/model/patient.go | 90 + internal/model/practitioner.go | 1 + internal/model/procedure.go | 24 + internal/model/questionnaireresponse.go | 24 + internal/model/satusehat_akses.go | 10 + internal/model/servicerequest.go | 40 + internal/model/specimen.go | 33 + internal/routes/allergancytoleran.go | 14 + internal/routes/careplan.go | 14 + internal/routes/clinicalimpression.go | 15 + internal/routes/composition.go | 13 + internal/routes/condition.go | 16 + internal/routes/diagnosisreport.go | 14 + internal/routes/encounter.go | 15 + internal/routes/episodeofcare.go | 15 + internal/routes/goal.go | 14 + internal/routes/imagingstudy.go | 14 + internal/routes/immunization.go | 14 + internal/routes/medicationdispense.go | 14 + internal/routes/medicationrequest.go | 14 + internal/routes/medicationstatement.go | 14 + internal/routes/medicine.go | 14 + internal/routes/oauth.go | 13 + internal/routes/observation.go | 16 + internal/routes/organization.go | 14 + internal/routes/patient.go | 14 + internal/routes/practitioner.go | 13 + internal/routes/procedure.go | 13 + internal/routes/questionnaireresponse.go | 14 + internal/routes/servicerequest.go | 15 + internal/routes/specimen.go | 15 + openapi/Dockerfile | 10 + openapi/openapi_satusehat.yaml | 2866 +++++++++++++++++ pkg/common/master_data.go | 14 + pkg/httputil/request.go | 102 + 115 files changed, 12298 insertions(+) create mode 100644 .env-example create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 README.md create mode 100644 builder-api.sh create mode 100644 builder-swag.sh create mode 100644 cmd/main.go create mode 100644 docker-compose.yaml create mode 100644 go.mod create mode 100644 go.sum create mode 100644 internal/config/akses_satusehat.go create mode 100644 internal/constant/const.go create mode 100644 internal/constant/error.go create mode 100644 internal/handler/allergancytoleran_handler.go create mode 100644 internal/handler/careplan_handler.go create mode 100644 internal/handler/clinicalImpression_handler.go create mode 100644 internal/handler/composition_handler.go create mode 100644 internal/handler/condition_handler.go create mode 100644 internal/handler/diagnosisreport_handler.go create mode 100644 internal/handler/encounter_handler.go create mode 100644 internal/handler/episodeofcare_handler.go create mode 100644 internal/handler/goal_handler.go create mode 100644 internal/handler/imagingstudy_handler.go create mode 100644 internal/handler/immunization_handler.go create mode 100644 internal/handler/medicationdispense_handler.go create mode 100644 internal/handler/medicationrequest_handler.go create mode 100644 internal/handler/medicationstatement_handler.go create mode 100644 internal/handler/medicine_handler.go create mode 100644 internal/handler/oauth_handler.go create mode 100644 internal/handler/observation_handler.go create mode 100644 internal/handler/organization_handler.go create mode 100644 internal/handler/patient_handler.go create mode 100644 internal/handler/practitioner_handler.go create mode 100644 internal/handler/procedure_handler.go create mode 100644 internal/handler/questionnaireresponse_handler.go create mode 100644 internal/handler/servicerequest_handler.go create mode 100644 internal/handler/specimen_handler.go create mode 100644 internal/integration/allergancytoleran_integration.go create mode 100644 internal/integration/careplan_integration.go create mode 100644 internal/integration/clinicalImpression_integration.go create mode 100644 internal/integration/composition_integration.go create mode 100644 internal/integration/condition_integration.go create mode 100644 internal/integration/diagnosisreport_integration.go create mode 100644 internal/integration/encounter_integration.go create mode 100644 internal/integration/episodeofcare_integration.go create mode 100644 internal/integration/goal_integration.go create mode 100644 internal/integration/imagingstudy_integration.go create mode 100644 internal/integration/immunization_integration.go create mode 100644 internal/integration/medicationdispense_integration.go create mode 100644 internal/integration/medicationrequest_integration.go create mode 100644 internal/integration/medicationstatement_integration.go create mode 100644 internal/integration/medicine_integration.go create mode 100644 internal/integration/oauth_integration.go create mode 100644 internal/integration/observation_intergration.go create mode 100644 internal/integration/organization_integration.go create mode 100644 internal/integration/patient_integration.go create mode 100644 internal/integration/practitioner_integration.go create mode 100644 internal/integration/procedure_integration.go create mode 100644 internal/integration/questionnaireresponse_integration.go create mode 100644 internal/integration/servicerequest_integration.go create mode 100644 internal/integration/specimen_integration.go create mode 100644 internal/midleware/middleware.go create mode 100644 internal/model/allergancytoleran.go create mode 100644 internal/model/careplan.go create mode 100644 internal/model/clinicalImpression.go create mode 100644 internal/model/composition.go create mode 100644 internal/model/condition.go create mode 100644 internal/model/diagnosisreport.go create mode 100644 internal/model/encounter.go create mode 100644 internal/model/episodeofcare.go create mode 100644 internal/model/goal.go create mode 100644 internal/model/imagingstudy.go create mode 100644 internal/model/immunization.go create mode 100644 internal/model/medicationdispense.go create mode 100644 internal/model/medicationrequest.go create mode 100644 internal/model/medicationstatement.go create mode 100644 internal/model/medicine.go create mode 100644 internal/model/oauth_model.go create mode 100644 internal/model/observation.go create mode 100644 internal/model/organization.go create mode 100644 internal/model/patient.go create mode 100644 internal/model/practitioner.go create mode 100644 internal/model/procedure.go create mode 100644 internal/model/questionnaireresponse.go create mode 100644 internal/model/satusehat_akses.go create mode 100644 internal/model/servicerequest.go create mode 100644 internal/model/specimen.go create mode 100644 internal/routes/allergancytoleran.go create mode 100644 internal/routes/careplan.go create mode 100644 internal/routes/clinicalimpression.go create mode 100644 internal/routes/composition.go create mode 100644 internal/routes/condition.go create mode 100644 internal/routes/diagnosisreport.go create mode 100644 internal/routes/encounter.go create mode 100644 internal/routes/episodeofcare.go create mode 100644 internal/routes/goal.go create mode 100644 internal/routes/imagingstudy.go create mode 100644 internal/routes/immunization.go create mode 100644 internal/routes/medicationdispense.go create mode 100644 internal/routes/medicationrequest.go create mode 100644 internal/routes/medicationstatement.go create mode 100644 internal/routes/medicine.go create mode 100644 internal/routes/oauth.go create mode 100644 internal/routes/observation.go create mode 100644 internal/routes/organization.go create mode 100644 internal/routes/patient.go create mode 100644 internal/routes/practitioner.go create mode 100644 internal/routes/procedure.go create mode 100644 internal/routes/questionnaireresponse.go create mode 100644 internal/routes/servicerequest.go create mode 100644 internal/routes/specimen.go create mode 100644 openapi/Dockerfile create mode 100644 openapi/openapi_satusehat.yaml create mode 100644 pkg/common/master_data.go create mode 100644 pkg/httputil/request.go diff --git a/.env-example b/.env-example new file mode 100644 index 0000000..899c792 --- /dev/null +++ b/.env-example @@ -0,0 +1,9 @@ +AUTH_URL= +BASE_URL= +CONSENT_URL= +KFA_URL= +CLIENT_ID= +CLIENT_SECRET= +ORGANIZATION_ID= + +HTTP_PORT=8080 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c2111fc --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.env +.vscode \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..40c1832 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,32 @@ +# Build stage +FROM golang:1.21 AS build-env +ENV GO111MODULE=on + +# Set /app as the working directory +WORKDIR /app + +# Copy all files to /app +COPY . . +COPY .env .env + +# Install dependencies +RUN go mod download && go mod verify +RUN go mod tidy -v + +# Build the Go application +RUN CGO_ENABLED=0 GOOS=linux go build -v -installsuffix cgo -o app cmd/main.go + +# final +FROM ubuntu:20.04 + +RUN apt-get update -y +RUN apt-get -y install wget + + +COPY --from=build-env /app/app . + + +EXPOSE 8080 + +# Define the command to run the application +CMD ["./app"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..f0f4712 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# satusehat-bridge \ No newline at end of file diff --git a/builder-api.sh b/builder-api.sh new file mode 100644 index 0000000..7e8f49f --- /dev/null +++ b/builder-api.sh @@ -0,0 +1,5 @@ +sudo docker build -t satusehat-bridge:1.0 . + +sudo docker stop satusehat-bridge_satusehat-bridge_1 +sudo docker-compose down --volumes --remove-orphans +sudo docker-compose --env-file .env up --build diff --git a/builder-swag.sh b/builder-swag.sh new file mode 100644 index 0000000..eece1fb --- /dev/null +++ b/builder-swag.sh @@ -0,0 +1,6 @@ +cd openapi/ +sudo docker stop swagger-satusehat +sudo docker rm swagger-satusehat + +sudo docker build -t openapi:1.0 . +sudo docker run --name swagger-satusehat -p 8081:8080 openapi:1.0 \ No newline at end of file diff --git a/cmd/main.go b/cmd/main.go new file mode 100644 index 0000000..a029c59 --- /dev/null +++ b/cmd/main.go @@ -0,0 +1,146 @@ +package main + +import ( + "fmt" + "log" + "net/http" + "os" + "satusehat-rssa/internal/config" + "satusehat-rssa/internal/handler" + "satusehat-rssa/internal/integration" + "satusehat-rssa/internal/midleware" + "satusehat-rssa/internal/routes" + + "github.com/gin-gonic/gin" + "github.com/joho/godotenv" +) + +func main() { + var err error + + // load environment + if err := godotenv.Load(); err != nil { + log.Println("No .env file found or error loading it") + } + + // init gin engine + e := gin.Default() + + err = e.SetTrustedProxies(nil) + if err != nil { + panic(err) + } + + // cors middleware + e.Use(func(c *gin.Context) { + c.Writer.Header().Set("Access-Control-Allow-Origin", "*") + c.Writer.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, PATCH") + c.Writer.Header().Set("Access-Control-Allow-Headers", "Origin, Content-Type, Accept, Authorization") + + // log to ensure the middleware is being executed + c.Writer.Header().Set("Access-Control-Expose-Headers", "Access-Control-Allow-Origin, Access-Control-Allow-Methods, Access-Control-Allow-Headers, Content-Disposition") + if c.Request.Method == "OPTIONS" { + c.Writer.Header().Set("Access-Control-Max-Age", "86400") + c.AbortWithStatus(http.StatusOK) + return + } + + c.Next() + }) + + // health check + e.GET("/health", func(c *gin.Context) { + c.JSON(200, gin.H{ + "message": "ok", + }) + }) + + // route for handling 404 + e.NoRoute(func(c *gin.Context) { + c.JSON(http.StatusNotFound, gin.H{"message": "Hello in API Satu Sehat Integration"}) + }) + + //Load Akses + akses := config.LoadConfig() + authMiddleware := midleware.AuthMiddleware{} + + oa := integration.NewOauthRequestRepo(akses) + at := integration.NewAllergancyToleranRepo(akses) + cp := integration.NewCarePlanRepo(akses) + qr := integration.NewQuestionnaireResponseRepo(akses) + ms := integration.NewMedicationStatementRepo(akses) + im := integration.NewImmunizationRepo(akses) + is := integration.NewImagingStudyRepo(akses) + mr := integration.NewMedicationRequestRepo(akses) + md := integration.NewMedicationDispenseRepo(akses) + gl := integration.NewGoalRepo(akses) + medic := integration.NewMedicineKfaRepo(akses) + patient := integration.NewPatientRepo(akses) + practioner := integration.NewPracticionerRepo(akses) + encounter := integration.NewEncounterRepo(akses) + clinicalImpression := integration.NewClinicalImpressionRepo(akses) + observation := integration.NewObservationRepo(akses) + organization := integration.NewOrganizationRepo(akses) + condition := integration.NewConditionRepo(akses) + dianosisReport := integration.NewDiagnosisReportRepo(akses) + serviceRequest := integration.NewServiceRequestRepository(akses) + specimen := integration.NewSpecimenRepository(akses) + episodeOfCare := integration.NewEpisodeOfCareRepo(akses) + procedure := integration.NewProcedureRepo(akses) + composition := integration.NewCompositionRepo(akses) + + oahandler := handler.NewOuathHandler(oa) + athandler := handler.NewAllergancyToleranHandler(at) + cphandler := handler.NewCarePlanHandler(cp) + qrhandler := handler.NewQuestionnaireResponseHandler(qr) + mshandler := handler.NewMedicationStatementHandler(ms) + imhandler := handler.NewImmunizationHandler(im) + ishandler := handler.NewImagingStudyHandler(is) + mrhandler := handler.NewMedicationRequestHandler(mr) + mdhandler := handler.NewMedicationDispenseHandler(md) + glhandler := handler.NewGoalHandler(gl) + medicineHandler := handler.NewMedicineHandler(medic) + patientHandler := handler.NewPatientHandler(patient) + practicionerHandler := handler.NewPracticionerHandler(practioner) + encounterHandler := handler.NewEncounterHandler(encounter) + clinicalImpressionHandler := handler.NewClinicalImpressionHandler(clinicalImpression) + observationHandler := handler.NewObservationHandler(observation) + organizationHandler := handler.NewOrganizationHandler(organization) + conditionHandler := handler.NewConditionHandler(condition) + diagnosisReportHandler := handler.NewDiagnosisReportHandler(dianosisReport) + serviceRequestHandler := handler.NewServiceRequestHandler(serviceRequest) + specimenHandler := handler.NewSpecimenHandler(specimen) + episodeOfCareHandler := handler.NewEpisodeOfCareHandler(episodeOfCare) + procedureHandler := handler.NewProcedureHandler(procedure) + compositionHandler := handler.NewCompositionHandler(composition) + + routes.Oauth(e, oahandler, authMiddleware) + routes.AllergancyToleran(e, athandler, authMiddleware) + routes.CarePlan(e, cphandler, authMiddleware) + routes.QuestionnaireResponse(e, qrhandler, authMiddleware) + routes.MedicationStatement(e, mshandler, authMiddleware) + routes.Immunization(e, imhandler, authMiddleware) + routes.ImagingStudy(e, ishandler, authMiddleware) + routes.MedicationRequest(e, mrhandler, authMiddleware) + routes.MedicationDispense(e, mdhandler, authMiddleware) + routes.Goal(e, glhandler, authMiddleware) + routes.MedicineKfa(e, medicineHandler, authMiddleware) + routes.Patient(e, patientHandler, authMiddleware) + routes.Practicioner(e, practicionerHandler, authMiddleware) + routes.Encounter(e, encounterHandler, authMiddleware) + routes.ClinicalImpression(e, clinicalImpressionHandler, authMiddleware) + routes.Observation(e, observationHandler, authMiddleware) + routes.Organization(e, organizationHandler, authMiddleware) + routes.Condition(e, conditionHandler, authMiddleware) + routes.DiagnosisReport(e, diagnosisReportHandler, authMiddleware) + routes.ServiceRequest(e, serviceRequestHandler, authMiddleware) + routes.Specimen(e, specimenHandler, authMiddleware) + routes.EpisodeOfCare(e, episodeOfCareHandler, authMiddleware) + routes.Procedure(e, procedureHandler, authMiddleware) + routes.Composition(e, compositionHandler, authMiddleware) + + port := fmt.Sprintf(":%s", os.Getenv("HTTP_PORT")) + if err = e.Run(port); err != nil { + panic(err) + } +} diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..a208b58 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,10 @@ +version: '3.8' + +services: + satusehat-bridge: + image: satusehat-bridge:1.0 + ports: + - "8080:8080" + env_file: + - .env + restart: unless-stopped diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..616aee9 --- /dev/null +++ b/go.mod @@ -0,0 +1,37 @@ +module satusehat-rssa + +go 1.21.12 + +require ( + github.com/gin-gonic/gin v1.10.1 + github.com/go-playground/validator/v10 v10.26.0 + github.com/joho/godotenv v1.5.1 +) + +require ( + github.com/bytedance/sonic v1.11.6 // indirect + github.com/bytedance/sonic/loader v0.1.1 // indirect + github.com/cloudwego/base64x v0.1.4 // indirect + github.com/cloudwego/iasm v0.2.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.8 // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/goccy/go-json v0.10.2 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/cpuid/v2 v2.2.7 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.2.12 // indirect + golang.org/x/arch v0.8.0 // indirect + golang.org/x/crypto v0.33.0 // indirect + golang.org/x/net v0.34.0 // indirect + golang.org/x/sys v0.30.0 // indirect + golang.org/x/text v0.22.0 // indirect + google.golang.org/protobuf v1.34.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..7fb7084 --- /dev/null +++ b/go.sum @@ -0,0 +1,93 @@ +github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0= +github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= +github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= +github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= +github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= +github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= +github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= +github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ= +github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k= +github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo= +github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4= +github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= +github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= +github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= +golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= +golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= +golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= +golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= +golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/internal/config/akses_satusehat.go b/internal/config/akses_satusehat.go new file mode 100644 index 0000000..3c694ab --- /dev/null +++ b/internal/config/akses_satusehat.go @@ -0,0 +1,18 @@ +package config + +import ( + "os" + "satusehat-rssa/internal/model" +) + +func LoadConfig() *model.Akses { + + return &model.Akses{ + AuthUrl: os.Getenv("AUTH_URL"), + BaseUrl: os.Getenv("BASE_URL"), + ConsentUrl: os.Getenv("CONSENT_URL"), + KfaUrl: os.Getenv("KFA_URL"), + ClientId: os.Getenv("CLIENT_ID"), + ClientSecret: os.Getenv("CLIENT_SECRET"), + } +} diff --git a/internal/constant/const.go b/internal/constant/const.go new file mode 100644 index 0000000..7b248ed --- /dev/null +++ b/internal/constant/const.go @@ -0,0 +1,29 @@ +package constant + +const ( + AllergyIntoleranceResourceType = "AllergyIntolerance" + ClinicalImpressionResourceType = "ClinicalImpression" + ServiceRequestResourceType = "ServiceRequest" + EpisodeOfCareResourceType = "EpisodeOfCare" + SpecimenResourceType = "Specimen" + DiagnosticReportResourceType = "DiagnosticReport" + ObservationResourceType = "Observation" + OrganizationResourceType = "Organization" + CarePlanResourceType = "CarePlan" + QuestionnaireResponseResourceType = "QuestionnaireResponse" + MedicationStatementResourceType = "MedicationStatement" + ImmunizationResourceType = "Immunization" + ImagingStudyResourceType = "ImagingStudy" + MedicationRequestResourceType = "MedicationRequest" + MedicationDispenseResourceType = "MedicationDispense" + ProcedureResourceType = "Procedure" + GoalResourceType = "Goal" + PatientResourceType = "Patient" + EncounterResourceType = "Encounter" +) + +const ( + ContentTypeFHIRJSON = "application/fhir+json" + PatientProfile = "https://fhir.kemkes.go.id/r4/StructureDefinition/Patient" + FHIRMedicationProfile = "https://fhir.kemkes.go.id/r4/StructureDefinition/Medication" +) diff --git a/internal/constant/error.go b/internal/constant/error.go new file mode 100644 index 0000000..9daacb2 --- /dev/null +++ b/internal/constant/error.go @@ -0,0 +1,5 @@ +package constant + +const ( + ErrGenerateToken = "failed to generate access token" +) diff --git a/internal/handler/allergancytoleran_handler.go b/internal/handler/allergancytoleran_handler.go new file mode 100644 index 0000000..e64a69a --- /dev/null +++ b/internal/handler/allergancytoleran_handler.go @@ -0,0 +1,58 @@ +package handler + +import ( + "net/http" + "satusehat-rssa/internal/integration" + "satusehat-rssa/internal/model" + + "github.com/gin-gonic/gin" +) + +type AllergancyToleranHandler struct { + AllergancyToleran integration.AllergancyToleranInterface +} + +func NewAllergancyToleranHandler(AllergancyToleran integration.AllergancyToleranInterface) *AllergancyToleranHandler { + return &AllergancyToleranHandler{AllergancyToleran: AllergancyToleran} +} + +func (a AllergancyToleranHandler) CreateAllergancyToleran(c *gin.Context) { + var bodyParam model.AllergancyToleranRequest + if err := c.ShouldBindJSON(&bodyParam); err != nil { + c.JSON(http.StatusBadRequest, err) + return + } + + res, err := a.AllergancyToleran.CreateAllergancyToleran(bodyParam) + if err != nil { + if res != nil { + c.JSON(http.StatusInternalServerError, res) + return + } + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, res) +} + +func (a AllergancyToleranHandler) UpdateAllergancyToleran(c *gin.Context) { + var bodyParam model.AllergancyToleranRequest + if err := c.ShouldBindJSON(&bodyParam); err != nil { + c.JSON(http.StatusBadRequest, err) + return + } + + bodyParam.Id = c.Param("id") + res, err := a.AllergancyToleran.UpdateAllergancyToleran(bodyParam) + if err != nil { + if res != nil { + c.JSON(http.StatusInternalServerError, res) + return + } + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, res) +} diff --git a/internal/handler/careplan_handler.go b/internal/handler/careplan_handler.go new file mode 100644 index 0000000..82d165f --- /dev/null +++ b/internal/handler/careplan_handler.go @@ -0,0 +1,58 @@ +package handler + +import ( + "net/http" + "satusehat-rssa/internal/integration" + "satusehat-rssa/internal/model" + + "github.com/gin-gonic/gin" +) + +type CarePlanHandler struct { + CarePlan integration.CarePlanInterface +} + +func NewCarePlanHandler(CarePlan integration.CarePlanInterface) *CarePlanHandler { + return &CarePlanHandler{CarePlan: CarePlan} +} + +func (h CarePlanHandler) CreateCarePlan(c *gin.Context) { + var req model.CarePlanRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + res, err := h.CarePlan.CreateCarePlan(req) + if err != nil { + if res != nil { + c.JSON(http.StatusInternalServerError, res) + return + } + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, res) +} + +func (h CarePlanHandler) UpdateCarePlan(c *gin.Context) { + var req model.CarePlanRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + req.Id = c.Param("id") + res, err := h.CarePlan.CreateCarePlan(req) + if err != nil { + if res != nil { + c.JSON(http.StatusInternalServerError, res) + return + } + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, res) +} diff --git a/internal/handler/clinicalImpression_handler.go b/internal/handler/clinicalImpression_handler.go new file mode 100644 index 0000000..097a44f --- /dev/null +++ b/internal/handler/clinicalImpression_handler.go @@ -0,0 +1,62 @@ +package handler + +import ( + "net/http" + "satusehat-rssa/internal/integration" + "satusehat-rssa/internal/model" + "satusehat-rssa/pkg/common" + + "github.com/gin-gonic/gin" +) + +type ClinicalImpressionHandler struct { + // Define fields for ClinicalImpressionHandler + ClinicalImpression integration.ClinicalImppressionInterface +} + +func NewClinicalImpressionHandler(ClinicalImpression integration.ClinicalImppressionInterface) *ClinicalImpressionHandler { + return &ClinicalImpressionHandler{ClinicalImpression: ClinicalImpression} +} + +func (c *ClinicalImpressionHandler) CreateClinicalImpression(ctx *gin.Context) { + var bodyParam model.ClinicalImpressionRequest + if err := ctx.ShouldBindJSON(&bodyParam); err != nil { + ctx.JSON(http.StatusBadRequest, err) + return + } + + bodyParam.Identifier = append(bodyParam.Identifier, common.GetIdentifier("clinicalImpression")) + + res, err := c.ClinicalImpression.CreateClinicalImpression(bodyParam) + if err != nil { + if res != nil { + ctx.JSON(http.StatusInternalServerError, res) + return + } + ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + ctx.JSON(http.StatusOK, res) +} + +func (c *ClinicalImpressionHandler) UpdateClinicalImpression(ctx *gin.Context) { + var bodyParam model.ClinicalImpressionRequest + if err := ctx.ShouldBindJSON(&bodyParam); err != nil { + ctx.JSON(http.StatusBadRequest, err) + return + } + + bodyParam.Id = ctx.Param("id") + bodyParam.Identifier = append(bodyParam.Identifier, common.GetIdentifier("clinicalImpression")) + + res, err := c.ClinicalImpression.UpdateClinicalImpression(bodyParam) + if err != nil { + if res != nil { + ctx.JSON(http.StatusInternalServerError, res) + return + } + ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + ctx.JSON(http.StatusOK, res) +} diff --git a/internal/handler/composition_handler.go b/internal/handler/composition_handler.go new file mode 100644 index 0000000..fdf0081 --- /dev/null +++ b/internal/handler/composition_handler.go @@ -0,0 +1,62 @@ +package handler + +import ( + "net/http" + "satusehat-rssa/internal/integration" + "satusehat-rssa/internal/model" + "satusehat-rssa/pkg/common" + + "github.com/gin-gonic/gin" +) + +type CompositionHandler struct { + Composition integration.CompositionInterface +} + +func NewCompositionHandler(composition integration.CompositionInterface) *CompositionHandler { + return &CompositionHandler{ + Composition: composition, + } +} + +func (ch *CompositionHandler) CreateComposition(c *gin.Context) { + var req model.CompositionRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + req.Identifier = common.GetIdentifier("composition") + resp, err := ch.Composition.CreateComposition(req) + if err != nil { + if resp != nil { + c.JSON(http.StatusInternalServerError, resp) + return + } + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, resp) +} + +func (ch *CompositionHandler) UpdateComposition(c *gin.Context) { + var req model.CompositionRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + req.Identifier = common.GetIdentifier("composition") + req.Id = c.Param("id") + resp, err := ch.Composition.UpdateComposition(req) + if err != nil { + if resp != nil { + c.JSON(http.StatusInternalServerError, resp) + return + } + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, resp) +} diff --git a/internal/handler/condition_handler.go b/internal/handler/condition_handler.go new file mode 100644 index 0000000..8d5d092 --- /dev/null +++ b/internal/handler/condition_handler.go @@ -0,0 +1,75 @@ +package handler + +import ( + "net/http" + "satusehat-rssa/internal/integration" + "satusehat-rssa/internal/model" + + "github.com/gin-gonic/gin" +) + +type ConditionHandler struct { + // Define fields for ConditionHandler + Condition integration.ConditionInterface +} + +func NewConditionHandler(condition integration.ConditionInterface) *ConditionHandler { + return &ConditionHandler{Condition: condition} +} +func (c *ConditionHandler) CreateCondition(ctx *gin.Context) { + var bodyParam model.ConditionRequest + if err := ctx.ShouldBindJSON(&bodyParam); err != nil { + ctx.JSON(http.StatusBadRequest, err) + return + } + res, err := c.Condition.CreateCondition(bodyParam) + if err != nil { + if res != nil { + ctx.JSON(http.StatusInternalServerError, res) + return + } + ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + ctx.JSON(http.StatusOK, res) +} + +func (c *ConditionHandler) GetConditionByPatient(ctx *gin.Context) { + patientId := ctx.Query("subject") + if patientId == "" { + ctx.JSON(http.StatusBadRequest, "patientId is required") + return + } + + res, err := c.Condition.GetConditionByPatient(patientId) + if err != nil { + if res != nil { + ctx.JSON(http.StatusInternalServerError, res) + return + } + ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + ctx.JSON(http.StatusOK, res) +} + +func (c *ConditionHandler) UpdateCondition(ctx *gin.Context) { + var bodyParam model.ConditionRequest + if err := ctx.ShouldBindJSON(&bodyParam); err != nil { + ctx.JSON(http.StatusBadRequest, err) + return + } + + bodyParam.Id = ctx.Param("id") + res, err := c.Condition.UpdateCondition(bodyParam) + if err != nil { + if res != nil { + ctx.JSON(http.StatusInternalServerError, res) + return + } + ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + ctx.JSON(http.StatusOK, res) +} diff --git a/internal/handler/diagnosisreport_handler.go b/internal/handler/diagnosisreport_handler.go new file mode 100644 index 0000000..d72a59f --- /dev/null +++ b/internal/handler/diagnosisreport_handler.go @@ -0,0 +1,62 @@ +package handler + +import ( + "net/http" + "satusehat-rssa/internal/integration" + "satusehat-rssa/internal/model" + "satusehat-rssa/pkg/common" + + "github.com/gin-gonic/gin" +) + +type DiagnosisReportHandler struct { + DiagnosisReport integration.DiagnosisReportInterface +} + +func NewDiagnosisReportHandler(diagnosisReport integration.DiagnosisReportInterface) *DiagnosisReportHandler { + return &DiagnosisReportHandler{DiagnosisReport: diagnosisReport} +} + +func (d *DiagnosisReportHandler) CreateDiagnosisReport(c *gin.Context) { + var req model.DiagnosticReportRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + identifier := common.GetIdentifier("diagnostic") + identifier.System += "/lab" + req.Identifier = append(req.Identifier, identifier) + res, err := d.DiagnosisReport.CreateDiagnosisReport(req) + if err != nil { + if res != nil { + c.JSON(http.StatusInternalServerError, res) + return + } + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + c.JSON(http.StatusOK, res) +} + +func (d *DiagnosisReportHandler) UpdateDiagnosisReport(c *gin.Context) { + var req model.DiagnosticReportRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + req.Id = c.Param("id") + identifier := common.GetIdentifier("diagnostic") + identifier.System += "/lab" + req.Identifier = append(req.Identifier, identifier) + res, err := d.DiagnosisReport.UpdateDiagnosisReport(req) + if err != nil { + if res != nil { + c.JSON(http.StatusInternalServerError, res) + return + } + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + c.JSON(http.StatusOK, res) +} diff --git a/internal/handler/encounter_handler.go b/internal/handler/encounter_handler.go new file mode 100644 index 0000000..fded6cc --- /dev/null +++ b/internal/handler/encounter_handler.go @@ -0,0 +1,81 @@ +package handler + +import ( + "net/http" + "satusehat-rssa/internal/integration" + "satusehat-rssa/internal/model" + + "github.com/gin-gonic/gin" +) + +type EncounterHandler struct { + // Define any dependencies needed for the handler + Encounter integration.EncounterInterface +} + +func NewEncounterHandler(encounter integration.EncounterInterface) *EncounterHandler { + return &EncounterHandler{Encounter: encounter} +} + +// Add methods for handling encounter-related requests here +func (e *EncounterHandler) CreateEncounter(c *gin.Context) { + var req model.EncounterRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + res, err := e.Encounter.CreateEncounter(req) + if err != nil { + if res != nil { + c.JSON(http.StatusInternalServerError, res) + return + } + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + c.JSON(http.StatusOK, res) +} + +func (e *EncounterHandler) GetEncounterByPatient(c *gin.Context) { + id := c.Query("subject") + if id == "" { + c.JSON(http.StatusBadRequest, gin.H{"error": "Subject is required"}) + return + } + + encounter, err := e.Encounter.GetEncounterByPatient(id) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + if encounter == nil { + c.JSON(http.StatusNotFound, gin.H{"message": "Encounter not found"}) + return + } + + c.JSON(http.StatusOK, encounter) +} + +func (e *EncounterHandler) UpdateEncounter(c *gin.Context) { + var req model.EncounterUpdateRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + // set id for param + id := c.Param("id") + req.ID = id + + res, err := e.Encounter.UpdateEncounter(req) + if err != nil { + if res != nil { + c.JSON(http.StatusInternalServerError, res) + return + } + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + c.JSON(http.StatusOK, res) +} diff --git a/internal/handler/episodeofcare_handler.go b/internal/handler/episodeofcare_handler.go new file mode 100644 index 0000000..0f935fc --- /dev/null +++ b/internal/handler/episodeofcare_handler.go @@ -0,0 +1,80 @@ +package handler + +import ( + "net/http" + "satusehat-rssa/internal/integration" + "satusehat-rssa/internal/model" + "satusehat-rssa/pkg/common" + + "github.com/gin-gonic/gin" +) + +type EpisodeOfCareHandler struct { + // Define any dependencies needed for the handler + EpisodeOfCare integration.EpisodeOfCareInterface +} + +func NewEpisodeOfCareHandler(episodeOfCare integration.EpisodeOfCareInterface) *EpisodeOfCareHandler { + return &EpisodeOfCareHandler{EpisodeOfCare: episodeOfCare} +} + +// Add methods for handling episode of care-related requests here +func (e *EpisodeOfCareHandler) CreateEpisodeOfCare(c *gin.Context) { + var req model.EpisodeOfCareRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + req.Identifier = append(req.Identifier, common.GetIdentifier("episode-of-care")) + res, err := e.EpisodeOfCare.CreateEpisodeOfCare(req) + if err != nil { + if res != nil { + c.JSON(http.StatusInternalServerError, res) + return + } + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + c.JSON(http.StatusOK, res) +} + +func (e *EpisodeOfCareHandler) GetEpisodeOfCareByPatient(c *gin.Context) { + id := c.Query("patient") + if id == "" { + c.JSON(http.StatusBadRequest, gin.H{"error": "Patient is required"}) + return + } + + episodeOfCare, err := e.EpisodeOfCare.GetEpisodeOfCareByPatient(id) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + if episodeOfCare == nil { + c.JSON(http.StatusNotFound, gin.H{"message": "Episode of care not found"}) + return + } + + c.JSON(http.StatusOK, episodeOfCare) +} + +func (e *EpisodeOfCareHandler) UpdateEpisodeOfCare(c *gin.Context) { + var req model.EpisodeOfCareRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + req.Id = c.Param("id") + req.Identifier = append(req.Identifier, common.GetIdentifier("episode-of-care")) + res, err := e.EpisodeOfCare.UpdateEpisodeOfCare(req) + if err != nil { + if res != nil { + c.JSON(http.StatusInternalServerError, res) + return + } + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + c.JSON(http.StatusOK, res) +} diff --git a/internal/handler/goal_handler.go b/internal/handler/goal_handler.go new file mode 100644 index 0000000..1ccf683 --- /dev/null +++ b/internal/handler/goal_handler.go @@ -0,0 +1,58 @@ +package handler + +import ( + "net/http" + "satusehat-rssa/internal/integration" + "satusehat-rssa/internal/model" + + "github.com/gin-gonic/gin" +) + +type GoalHandler struct { + Goal integration.GoalInterface +} + +func NewGoalHandler(Goal integration.GoalInterface) *GoalHandler { + return &GoalHandler{Goal: Goal} +} + +func (h GoalHandler) CreateGoal(c *gin.Context) { + var req model.GoalRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + res, err := h.Goal.CreateGoal(req) + if err != nil { + if res != nil { + c.JSON(http.StatusInternalServerError, res) + return + } + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, res) +} + +func (h GoalHandler) UpdateGoal(c *gin.Context) { + var req model.GoalRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + req.Id = c.Param("id") + res, err := h.Goal.CreateGoal(req) + if err != nil { + if res != nil { + c.JSON(http.StatusInternalServerError, res) + return + } + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, res) +} diff --git a/internal/handler/imagingstudy_handler.go b/internal/handler/imagingstudy_handler.go new file mode 100644 index 0000000..5ebaf02 --- /dev/null +++ b/internal/handler/imagingstudy_handler.go @@ -0,0 +1,97 @@ +package handler + +import ( + "fmt" + "net/http" + "satusehat-rssa/internal/integration" + "satusehat-rssa/internal/model" + + "github.com/gin-gonic/gin" +) + +type ImagingStudyHandler struct { + ImagingStudy integration.ImagingStudyInterface +} + +func NewImagingStudyHandler(ImagingStudy integration.ImagingStudyInterface) *ImagingStudyHandler { + return &ImagingStudyHandler{ImagingStudy: ImagingStudy} +} + +func (h ImagingStudyHandler) CreateImagingStudy(c *gin.Context) { + var req model.ImagingStudyRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + if len(req.Identifier) < 1 { + c.JSON(http.StatusBadRequest, gin.H{"error": "identifier is required."}) + return + } + + if len(req.Identifier) > 0 { + for k, i := range req.Identifier { + if k == 0 && i.Value == "" { + c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("identifier[%d].value (ACSN) required", k)}) + return + } + + // if k == 1 && i.Value == "" { + // c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("identifier[%d].value (urn:dicom:uid) required", k)}) + // return + // } + } + } + + res, err := h.ImagingStudy.CreateImagingStudy(req) + if err != nil { + if res != nil { + c.JSON(http.StatusInternalServerError, res) + return + } + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, res) +} + +func (h ImagingStudyHandler) UpdateImagingStudy(c *gin.Context) { + var req model.ImagingStudyRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + if len(req.Identifier) < 1 { + c.JSON(http.StatusBadRequest, gin.H{"error": "identifier is required."}) + return + } + + if len(req.Identifier) > 0 { + for k, i := range req.Identifier { + if k == 0 && i.Value == "" { + c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("identifier[%d].value (ACSN) required", k)}) + return + } + + // if k == 1 && i.Value == "" { + // c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("identifier[%d].value (urn:dicom:uid) required", k)}) + // return + // } + } + } + + req.Id = c.Param("id") + res, err := h.ImagingStudy.CreateImagingStudy(req) + if err != nil { + if res != nil { + c.JSON(http.StatusInternalServerError, res) + return + } + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, res) +} diff --git a/internal/handler/immunization_handler.go b/internal/handler/immunization_handler.go new file mode 100644 index 0000000..6301496 --- /dev/null +++ b/internal/handler/immunization_handler.go @@ -0,0 +1,58 @@ +package handler + +import ( + "net/http" + "satusehat-rssa/internal/integration" + "satusehat-rssa/internal/model" + + "github.com/gin-gonic/gin" +) + +type ImmunizationHandler struct { + Immunization integration.ImmunizationInterface +} + +func NewImmunizationHandler(Immunization integration.ImmunizationInterface) *ImmunizationHandler { + return &ImmunizationHandler{Immunization: Immunization} +} + +func (h ImmunizationHandler) CreateImmunization(c *gin.Context) { + var req model.ImmunizationRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + res, err := h.Immunization.CreateImmunization(req) + if err != nil { + if res != nil { + c.JSON(http.StatusInternalServerError, res) + return + } + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, res) +} + +func (h ImmunizationHandler) UpdateImmunization(c *gin.Context) { + var req model.ImmunizationRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + req.Id = c.Param("id") + res, err := h.Immunization.CreateImmunization(req) + if err != nil { + if res != nil { + c.JSON(http.StatusInternalServerError, res) + return + } + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, res) +} diff --git a/internal/handler/medicationdispense_handler.go b/internal/handler/medicationdispense_handler.go new file mode 100644 index 0000000..b8908c9 --- /dev/null +++ b/internal/handler/medicationdispense_handler.go @@ -0,0 +1,58 @@ +package handler + +import ( + "net/http" + "satusehat-rssa/internal/integration" + "satusehat-rssa/internal/model" + + "github.com/gin-gonic/gin" +) + +type MedicationDispenseHandler struct { + MedicationDispense integration.MedicationDispenseInterface +} + +func NewMedicationDispenseHandler(MedicationDispense integration.MedicationDispenseInterface) *MedicationDispenseHandler { + return &MedicationDispenseHandler{MedicationDispense: MedicationDispense} +} + +func (h MedicationDispenseHandler) CreateMedicationDispense(c *gin.Context) { + var req model.MedicationDispenseRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + res, err := h.MedicationDispense.CreateMedicationDispense(req) + if err != nil { + if res != nil { + c.JSON(http.StatusInternalServerError, res) + return + } + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, res) +} + +func (h MedicationDispenseHandler) UpdateMedicationDispense(c *gin.Context) { + var req model.MedicationDispenseRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + req.Id = c.Param("id") + res, err := h.MedicationDispense.CreateMedicationDispense(req) + if err != nil { + if res != nil { + c.JSON(http.StatusInternalServerError, res) + return + } + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, res) +} diff --git a/internal/handler/medicationrequest_handler.go b/internal/handler/medicationrequest_handler.go new file mode 100644 index 0000000..c7157ab --- /dev/null +++ b/internal/handler/medicationrequest_handler.go @@ -0,0 +1,58 @@ +package handler + +import ( + "net/http" + "satusehat-rssa/internal/integration" + "satusehat-rssa/internal/model" + + "github.com/gin-gonic/gin" +) + +type MedicationRequestHandler struct { + MedicationRequest integration.MedicationRequestInterface +} + +func NewMedicationRequestHandler(MedicationRequest integration.MedicationRequestInterface) *MedicationRequestHandler { + return &MedicationRequestHandler{MedicationRequest: MedicationRequest} +} + +func (h MedicationRequestHandler) CreateMedicationRequest(c *gin.Context) { + var req model.MedicationRequestRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + res, err := h.MedicationRequest.CreateMedicationRequest(req) + if err != nil { + if res != nil { + c.JSON(http.StatusInternalServerError, res) + return + } + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, res) +} + +func (h MedicationRequestHandler) UpdateMedicationRequest(c *gin.Context) { + var req model.MedicationRequestRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + req.Id = c.Param("id") + res, err := h.MedicationRequest.CreateMedicationRequest(req) + if err != nil { + if res != nil { + c.JSON(http.StatusInternalServerError, res) + return + } + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, res) +} diff --git a/internal/handler/medicationstatement_handler.go b/internal/handler/medicationstatement_handler.go new file mode 100644 index 0000000..206e171 --- /dev/null +++ b/internal/handler/medicationstatement_handler.go @@ -0,0 +1,60 @@ +package handler + +import ( + "fmt" + "net/http" + "satusehat-rssa/internal/integration" + "satusehat-rssa/internal/model" + + "github.com/gin-gonic/gin" +) + +type MedicationStatementHandler struct { + MedicationStatement integration.MedicationStatementInterface +} + +func NewMedicationStatementHandler(MedicationStatement integration.MedicationStatementInterface) *MedicationStatementHandler { + return &MedicationStatementHandler{MedicationStatement: MedicationStatement} +} + +func (h MedicationStatementHandler) CreateMedicationStatement(c *gin.Context) { + var req model.MedicationStatementRequest + if err := c.ShouldBindJSON(&req); err != nil { + fmt.Println(err) + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + res, err := h.MedicationStatement.CreateMedicationStatement(req) + if err != nil { + if res != nil { + c.JSON(http.StatusInternalServerError, res) + return + } + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, res) +} + +func (h MedicationStatementHandler) UpdateMedicationStatement(c *gin.Context) { + var req model.MedicationStatementRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + req.Id = c.Param("id") + res, err := h.MedicationStatement.CreateMedicationStatement(req) + if err != nil { + if res != nil { + c.JSON(http.StatusInternalServerError, res) + return + } + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, res) +} diff --git a/internal/handler/medicine_handler.go b/internal/handler/medicine_handler.go new file mode 100644 index 0000000..f987c5b --- /dev/null +++ b/internal/handler/medicine_handler.go @@ -0,0 +1,40 @@ +package handler + +import ( + "net/http" + "satusehat-rssa/internal/integration" + "satusehat-rssa/internal/model" + + "github.com/gin-gonic/gin" +) + +type MedicineHandler struct { + Medicine integration.MedicineIntegrationInterface +} + +func NewMedicineHandler(medicine integration.MedicineIntegrationInterface) *MedicineHandler { + return &MedicineHandler{Medicine: medicine} +} +func (m MedicineHandler) GetMedicineKfa(c *gin.Context) { + var req model.MedicineKfaRequest + if err := c.ShouldBindQuery(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + res, err := m.Medicine.GetMedicineKfa(req) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + c.JSON(http.StatusOK, res) +} + +func (m MedicineHandler) GetMedicineByKfaCode(c *gin.Context) { + kfaCode := c.Param("kfa_code") + res, err := m.Medicine.GetMedicineByKfaCode(kfaCode) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + c.JSON(http.StatusOK, res) +} diff --git a/internal/handler/oauth_handler.go b/internal/handler/oauth_handler.go new file mode 100644 index 0000000..56d01ac --- /dev/null +++ b/internal/handler/oauth_handler.go @@ -0,0 +1,33 @@ +package handler + +import ( + "net/http" + "satusehat-rssa/internal/integration" + "satusehat-rssa/internal/model" + + "github.com/gin-gonic/gin" +) + +type OauthHandler struct { + Oauth integration.OauthInterface +} + +func NewOuathHandler(Oauth integration.OauthInterface) *OauthHandler { + return &OauthHandler{Oauth: Oauth} +} + +func (oh OauthHandler) GenerateToken(c *gin.Context) { + var bodyParam model.OauthRequest + if err := c.ShouldBindJSON(&bodyParam); err != nil { + c.JSON(http.StatusBadRequest, err) + return + } + + res, err := oh.Oauth.GenerateToken(bodyParam) + if err != nil { + c.JSON(http.StatusInternalServerError, err) + return + } + + c.JSON(http.StatusOK, res) +} diff --git a/internal/handler/observation_handler.go b/internal/handler/observation_handler.go new file mode 100644 index 0000000..78c1970 --- /dev/null +++ b/internal/handler/observation_handler.go @@ -0,0 +1,78 @@ +package handler + +import ( + "net/http" + "satusehat-rssa/internal/integration" + "satusehat-rssa/internal/model" + + "github.com/gin-gonic/gin" +) + +type ObservationHandler struct { + // Define any dependencies needed for the handler + Observation integration.ObservationInterface +} + +func NewObservationHandler(observation integration.ObservationInterface) *ObservationHandler { + return &ObservationHandler{Observation: observation} +} + +// Add methods for handling observation-related requests here +func (o *ObservationHandler) CreateObservation(c *gin.Context) { + var req model.ObservationRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + res, err := o.Observation.CreateObservation(req) + if err != nil { + if res != nil { + c.JSON(http.StatusInternalServerError, res) + return + } + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + c.JSON(http.StatusOK, res) +} + +func (o *ObservationHandler) GetObservationByPatient(c *gin.Context) { + id := c.Query("subject") + if id == "" { + c.JSON(http.StatusBadRequest, gin.H{"error": "Subject is required"}) + return + } + + observation, err := o.Observation.GetObservationByPatient(id) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + if observation == nil { + c.JSON(http.StatusNotFound, gin.H{"message": "Observation not found"}) + return + } + + c.JSON(http.StatusOK, observation) +} + +func (o *ObservationHandler) UpdateObservation(c *gin.Context) { + var req model.ObservationRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + req.Id = c.Param("id") + res, err := o.Observation.UpdateObservation(req) + if err != nil { + if res != nil { + c.JSON(http.StatusInternalServerError, res) + return + } + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + c.JSON(http.StatusOK, res) +} diff --git a/internal/handler/organization_handler.go b/internal/handler/organization_handler.go new file mode 100644 index 0000000..4441a1c --- /dev/null +++ b/internal/handler/organization_handler.go @@ -0,0 +1,54 @@ +package handler + +import ( + "net/http" + "satusehat-rssa/internal/integration" + "satusehat-rssa/internal/model" + "satusehat-rssa/pkg/common" + + "github.com/gin-gonic/gin" +) + +type OrganizationHandler struct { + Organization integration.OrganizationInterface +} + +func NewOrganizationHandler(organization integration.OrganizationInterface) *OrganizationHandler { + return &OrganizationHandler{Organization: organization} +} + +func (o OrganizationHandler) CreateOrganization(c *gin.Context) { + var bodyParam model.OrganizationRequest + if err := c.ShouldBindJSON(&bodyParam); err != nil { + c.JSON(http.StatusBadRequest, err) + return + } + bodyParam.Identifier = append(bodyParam.Identifier, common.GetIdentifier("organization")) + res, err := o.Organization.CreateOrganization(bodyParam) + if err != nil { + if res != nil { + c.JSON(http.StatusInternalServerError, res) + return + } + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, res) +} + +func (o OrganizationHandler) GetOrganizationPatient(c *gin.Context) { + id := c.Query("subject") + if id == "" { + c.JSON(http.StatusBadRequest, "Patient ID is required") + return + } + + res, err := o.Organization.GetOrganizationPatient(id) + if err != nil { + c.JSON(http.StatusInternalServerError, err) + return + } + + c.JSON(http.StatusOK, res) +} diff --git a/internal/handler/patient_handler.go b/internal/handler/patient_handler.go new file mode 100644 index 0000000..f42e236 --- /dev/null +++ b/internal/handler/patient_handler.go @@ -0,0 +1,47 @@ +package handler + +import ( + "net/http" + "satusehat-rssa/internal/integration" + "satusehat-rssa/internal/model" + + "github.com/gin-gonic/gin" +) + +type PatientHandler struct { + // Define any dependencies or services needed for the PatientHandler + Patient integration.PatientInterface +} + +func NewPatientHandler(patient integration.PatientInterface) *PatientHandler { + return &PatientHandler{Patient: patient} +} + +// Add methods for handling patient-related requests here +func (p *PatientHandler) GetPatientByNIK(c *gin.Context) { + param := c.Query("identifier") + res, err := p.Patient.GetPatientByNIK(param) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + c.JSON(http.StatusOK, res) +} + +func (p *PatientHandler) CreatePatient(c *gin.Context) { + var req model.PatientRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + res, err := p.Patient.CreataPatient(req) + if err != nil { + if res != nil { + c.JSON(http.StatusInternalServerError, res) + return + } + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + c.JSON(http.StatusOK, res) +} diff --git a/internal/handler/practitioner_handler.go b/internal/handler/practitioner_handler.go new file mode 100644 index 0000000..34a770e --- /dev/null +++ b/internal/handler/practitioner_handler.go @@ -0,0 +1,37 @@ +package handler + +import ( + "net/http" + "satusehat-rssa/internal/integration" + + "github.com/gin-gonic/gin" +) + +type PracticionerHandler struct { + Practicioner integration.PracticionerInterface +} + +func NewPracticionerHandler(practicioner integration.PracticionerInterface) *PracticionerHandler { + return &PracticionerHandler{Practicioner: practicioner} +} + +func (p PracticionerHandler) GetPracticionerByNik(c *gin.Context) { + nik := c.Query("identifier") + if nik == "" { + c.JSON(http.StatusBadRequest, gin.H{"error": "identifier is required"}) + return + } + + practicioner, err := p.Practicioner.GetPracticionerByNIK(nik) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + if practicioner == nil { + c.JSON(http.StatusNotFound, gin.H{"message": "Practicioner not found"}) + return + } + + c.JSON(http.StatusOK, practicioner) +} diff --git a/internal/handler/procedure_handler.go b/internal/handler/procedure_handler.go new file mode 100644 index 0000000..75dbc16 --- /dev/null +++ b/internal/handler/procedure_handler.go @@ -0,0 +1,71 @@ +package handler + +import ( + "net/http" + "satusehat-rssa/internal/integration" + "satusehat-rssa/internal/model" + + "github.com/gin-gonic/gin" +) + +type ProcedureHandler struct { + Procedure integration.ProcedureInterface +} + +func NewProcedureHandler(procedure integration.ProcedureInterface) *ProcedureHandler { + return &ProcedureHandler{ + Procedure: procedure, + } +} + +func (p *ProcedureHandler) CreateProcedure(c *gin.Context) { + var req model.ProcedureRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + result, err := p.Procedure.CreateProcedure(req) + if err != nil { + if result != nil { + c.JSON(http.StatusInternalServerError, result) + return + } + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, result) +} + +func (p *ProcedureHandler) GetProcedure(c *gin.Context) { + id := c.Param("id") + + result, err := p.Procedure.GetProcedure(id) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, result) +} + +func (p *ProcedureHandler) UpdateProcedure(c *gin.Context) { + var req model.ProcedureRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + result, err := p.Procedure.UpdateProcedure(req) + if err != nil { + if result != nil { + c.JSON(http.StatusInternalServerError, result) + return + } + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, result) +} diff --git a/internal/handler/questionnaireresponse_handler.go b/internal/handler/questionnaireresponse_handler.go new file mode 100644 index 0000000..c2e56e2 --- /dev/null +++ b/internal/handler/questionnaireresponse_handler.go @@ -0,0 +1,58 @@ +package handler + +import ( + "net/http" + "satusehat-rssa/internal/integration" + "satusehat-rssa/internal/model" + + "github.com/gin-gonic/gin" +) + +type QuestionnaireResponseHandler struct { + QuestionnaireResponse integration.QuestionnaireResponseInterface +} + +func NewQuestionnaireResponseHandler(QuestionnaireResponse integration.QuestionnaireResponseInterface) *QuestionnaireResponseHandler { + return &QuestionnaireResponseHandler{QuestionnaireResponse: QuestionnaireResponse} +} + +func (h QuestionnaireResponseHandler) CreateQuestionnaireResponse(c *gin.Context) { + var req model.QuestionnaireResponseRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + res, err := h.QuestionnaireResponse.CreateQuestionnaireResponse(req) + if err != nil { + if res != nil { + c.JSON(http.StatusInternalServerError, res) + return + } + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, res) +} + +func (h QuestionnaireResponseHandler) UpdateQuestionnaireResponse(c *gin.Context) { + var req model.QuestionnaireResponseRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + req.Id = c.Param("id") + res, err := h.QuestionnaireResponse.CreateQuestionnaireResponse(req) + if err != nil { + if res != nil { + c.JSON(http.StatusInternalServerError, res) + return + } + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, res) +} diff --git a/internal/handler/servicerequest_handler.go b/internal/handler/servicerequest_handler.go new file mode 100644 index 0000000..7917a53 --- /dev/null +++ b/internal/handler/servicerequest_handler.go @@ -0,0 +1,77 @@ +package handler + +import ( + "net/http" + "satusehat-rssa/internal/integration" + "satusehat-rssa/internal/model" + "satusehat-rssa/pkg/common" + + "github.com/gin-gonic/gin" +) + +type ServiceRequestHandler struct { + repo integration.ServiceRequestInterface +} + +func NewServiceRequestHandler(repo integration.ServiceRequestInterface) *ServiceRequestHandler { + return &ServiceRequestHandler{repo: repo} +} + +func (h *ServiceRequestHandler) CreateServiceRequest(c *gin.Context) { + var req model.ServiceRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + req.Identifier = append(req.Identifier, common.GetIdentifier("servicerequest")) + data, err := h.repo.CreateServiceRequest(req) + if err != nil { + if data != nil { + c.JSON(http.StatusInternalServerError, data) + return + } + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, data) +} + +func (h *ServiceRequestHandler) GetServiceRequestByPatient(c *gin.Context) { + id := c.Query("subject") + if id == "" { + c.JSON(http.StatusBadRequest, gin.H{"error": "Patient ID is required"}) + return + } + + data, err := h.repo.GetServiceRequestByPatient(id) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, data) +} + +func (h *ServiceRequestHandler) UpdateServiceRequest(c *gin.Context) { + var req model.ServiceRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + req.Id = c.Param("id") + req.Identifier = append(req.Identifier, common.GetIdentifier("servicerequest")) + data, err := h.repo.UpdateServiceRequest(req) + if err != nil { + if data != nil { + c.JSON(http.StatusInternalServerError, data) + return + } + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, data) +} diff --git a/internal/handler/specimen_handler.go b/internal/handler/specimen_handler.go new file mode 100644 index 0000000..8afc46f --- /dev/null +++ b/internal/handler/specimen_handler.go @@ -0,0 +1,77 @@ +package handler + +import ( + "net/http" + "satusehat-rssa/internal/integration" + "satusehat-rssa/internal/model" + "satusehat-rssa/pkg/common" + + "github.com/gin-gonic/gin" +) + +type SpecimenHandler struct { + Specimen integration.SpecimenInterface +} + +func NewSpecimenHandler(specimen integration.SpecimenInterface) *SpecimenHandler { + return &SpecimenHandler{Specimen: specimen} +} +func (s *SpecimenHandler) CreateSpecimen(c *gin.Context) { + var req model.SpecimenRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + req.Identifier = append(req.Identifier, common.GetIdentifier("specimen")) + res, err := s.Specimen.CreateSpecimen(req) + if err != nil { + if res != nil { + c.JSON(http.StatusInternalServerError, res) + return + } + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + c.JSON(http.StatusOK, res) +} + +func (s *SpecimenHandler) GetSpecimenByPatient(c *gin.Context) { + id := c.Query("subject") + if id == "" { + c.JSON(http.StatusBadRequest, gin.H{"error": "Subject is required"}) + return + } + + specimen, err := s.Specimen.GetSpecimenByPatient(id) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + if specimen == nil { + c.JSON(http.StatusNotFound, gin.H{"message": "Specimen not found"}) + return + } + + c.JSON(http.StatusOK, specimen) +} + +func (s *SpecimenHandler) UpdateSpecimen(c *gin.Context) { + var req model.SpecimenRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + req.Id = c.Param("id") + req.Identifier = append(req.Identifier, common.GetIdentifier("specimen")) + res, err := s.Specimen.UpdateSpecimen(req) + if err != nil { + if res != nil { + c.JSON(http.StatusInternalServerError, res) + return + } + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + c.JSON(http.StatusOK, res) +} diff --git a/internal/integration/allergancytoleran_integration.go b/internal/integration/allergancytoleran_integration.go new file mode 100644 index 0000000..70d078d --- /dev/null +++ b/internal/integration/allergancytoleran_integration.go @@ -0,0 +1,262 @@ +package integration + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "net/http" + "os" + "satusehat-rssa/internal/constant" + "satusehat-rssa/internal/model" + "satusehat-rssa/pkg/httputil" +) + +type AllergancyToleranInterface interface { + // Define methods for AllergancyToleranInterface + CreateAllergancyToleran(req model.AllergancyToleranRequest) (*model.AllergancyToleranResponse, error) + UpdateAllergancyToleran(req model.AllergancyToleranRequest) (map[string]interface{}, error) + GetAllergytoleranByPatient(id string) (map[string]interface{}, error) + HandleCheckAllergancyToleran(id string) ([]string, bool, error) +} + +type AllergancyToleranRepository struct { + // Define fields for AllergancyToleranRepository + akses *model.Akses +} + +// GetAllergytoleranByPatient implements AllergancyToleranInterface. +func (a *AllergancyToleranRepository) GetAllergytoleranByPatient(id string) (map[string]interface{}, error) { + var data map[string]interface{} + + url := a.akses.BaseUrl + "/AllergyIntolerance?patient=" + id + method := "GET" + + client := &http.Client{} + request, err := http.NewRequest(method, url, nil) + if err != nil { + return nil, err + } + oauth := model.OauthRequest{ + ClientId: a.akses.ClientId, + ClientSecret: a.akses.ClientSecret, + } + token, err := NewOauthRequestRepo(a.akses).GenerateToken(oauth) + if err != nil { + return nil, err + } + if token == nil { + return nil, errors.New(constant.ErrGenerateToken) + } + request.Header.Add("Authorization", "Bearer "+token.AccessToken) + request.Header.Set("Content-Type", constant.ContentTypeFHIRJSON) + request.Header.Set("Accept", constant.ContentTypeFHIRJSON) + + res, err := client.Do(request) + if err != nil { + return nil, err + } + defer res.Body.Close() + + err = json.NewDecoder(res.Body).Decode(&data) + if err != nil { + return nil, err + } + + return data, nil +} + +// HandleCheckAllergancyToleran implements AllergancyToleranInterface. +func (a *AllergancyToleranRepository) HandleCheckAllergancyToleran(id string) ([]string, bool, error) { + allergy, err := a.GetAllergytoleranByPatient(id) + if err != nil { + return nil, false, err + } + + var ids []string + if entries, ok := allergy["entry"].([]interface{}); ok && len(entries) != 0 { + if entries, ok := (allergy)["entry"].([]interface{}); ok && len(entries) > 0 { + if entryMap, ok := entries[0].(map[string]interface{}); ok { + if resource, ok := entryMap["resource"].(map[string]interface{}); ok { + if id, ok := resource["id"].(string); ok { + //fmt.Println("resource.id:", id) + ids = append(ids, id) + return ids, true, nil + } + } + } + } + return nil, true, nil + } + + return nil, false, nil +} + +// CreateAllergancyToleran implements AllergancyToleranInterface. +func (a *AllergancyToleranRepository) CreateAllergancyToleran(req model.AllergancyToleranRequest) (*model.AllergancyToleranResponse, error) { + var data *model.AllergancyToleranResponse + + req.ResourceType = constant.AllergyIntoleranceResourceType + req.Identifier = append(req.Identifier, &model.IdentifierObject{ + Use: "official", + System: "http://sys-ids.kemkes.go.id/allergy/" + os.Getenv("ORGANIZATION_ID"), // Set this if needed, or remove if not present in IdentifierObject + Value: os.Getenv("ORGANIZATION_ID"), + }) + + patient, err := a.setupPatient(&req) + if err != nil { + return nil, err + } + + err = a.setupEncounter(&req, patient) + if err != nil { + return nil, err + } + + // err = a.setupPractitioner(&req) + // if err != nil { + // return nil, err + // } + + url := a.akses.BaseUrl + "/AllergyIntolerance" + method := "POST" + + payload, err := json.Marshal(req) + if err != nil { + return nil, err + } + client := &http.Client{} + request, err := http.NewRequest(method, url, bytes.NewBuffer(payload)) + if err != nil { + return nil, err + } + + oauth := model.OauthRequest{ + ClientId: a.akses.ClientId, + ClientSecret: a.akses.ClientSecret, + } + OauthInterface := NewOauthRequestRepo(a.akses) + token, err := OauthInterface.GenerateToken(oauth) + if err != nil { + return nil, err + } + if token == nil { + return nil, errors.New(constant.ErrGenerateToken) + } + request.Header.Add("Authorization", "Bearer "+token.AccessToken) + request.Header.Set("Content-Type", constant.ContentTypeFHIRJSON) + request.Header.Set("Accept", constant.ContentTypeFHIRJSON) + + res, err := client.Do(request) + if err != nil { + return nil, err + } + defer res.Body.Close() + + err = json.NewDecoder(res.Body).Decode(&data) + if err != nil { + return nil, err + } + + return data, nil +} + +func (a *AllergancyToleranRepository) UpdateAllergancyToleran(req model.AllergancyToleranRequest) (map[string]interface{}, error) { + req.ResourceType = constant.AllergyIntoleranceResourceType + req.Identifier = append(req.Identifier, &model.IdentifierObject{ + Use: "official", + System: "http://sys-ids.kemkes.go.id/allergy/" + os.Getenv("ORGANIZATION_ID"), // Set this if needed, or remove if not present in IdentifierObject + Value: os.Getenv("ORGANIZATION_ID"), + }) + + patient, err := a.setupPatient(&req) + if err != nil { + return nil, err + } + + err = a.setupEncounter(&req, patient) + if err != nil { + return nil, err + } + + // err = a.setupPractitioner(&req) + // if err != nil { + // return nil, err + // } + + oauth := model.OauthRequest{ + ClientId: a.akses.ClientId, + ClientSecret: a.akses.ClientSecret, + } + token, err := NewOauthRequestRepo(a.akses).GenerateToken(oauth) + if err != nil { + return nil, err + } + if token == nil { + return nil, errors.New(constant.ErrGenerateToken) + } + + url := a.akses.BaseUrl + fmt.Sprintf("/AllergyIntolerance/%s", req.Id) + return httputil.DoRequest(httputil.RequestOption{ + Method: "PUT", + URL: url, + Body: req, + BearerToken: token.AccessToken, + Headers: httputil.DefaultFHIRHeaders(), + }) +} + +// setupPatient handles patient reference logic. +func (a *AllergancyToleranRepository) setupPatient(req *model.AllergancyToleranRequest) (string, error) { + if req.Patient.Reference == "" { + return "", nil + } + patientInterface := NewPatientRepo(a.akses) + patient, err := patientInterface.HandleCheckPatient(req.Patient.Reference) + if err != nil { + return "", err + } + if patient != "" { + req.Patient.Reference = "Patient/" + patient + } + return patient, nil +} + +// setupEncounter handles encounter reference logic. +func (a *AllergancyToleranRepository) setupEncounter(req *model.AllergancyToleranRequest, patient string) error { + if patient == "" { + return nil + } + encounterInterface := NewEncounterRepo(a.akses) + encounterId, encounterExist, err := encounterInterface.HandleCheckEncounter(patient) + if err != nil { + return err + } + if encounterExist { + req.Encounter.Reference = "Encounter/" + encounterId + } + return nil +} + +// setupPractitioner handles practitioner reference logic. +func (a *AllergancyToleranRepository) setupPractitioner(req *model.AllergancyToleranRequest) error { + if req.Recorder.Reference == "" { + return nil + } + practicionerInterface := NewPracticionerRepo(a.akses) + ref, display, err := practicionerInterface.HandleCheckPartitioner(req.Recorder.Reference) + if err != nil { + return err + } + if ref != "" { + req.Recorder.Reference = "Practitioner/" + ref + req.Recorder.Display = display + } + return nil +} + +func NewAllergancyToleranRepo(akses *model.Akses) AllergancyToleranInterface { + return &AllergancyToleranRepository{ + akses: akses, + } +} diff --git a/internal/integration/careplan_integration.go b/internal/integration/careplan_integration.go new file mode 100644 index 0000000..bb93025 --- /dev/null +++ b/internal/integration/careplan_integration.go @@ -0,0 +1,224 @@ +package integration + +import ( + "encoding/json" + "errors" + "fmt" + "net/http" + "satusehat-rssa/internal/constant" + "satusehat-rssa/internal/model" + "satusehat-rssa/pkg/httputil" +) + +type CarePlanInterface interface { + // Define methods for CarePlanInterface + CreateCarePlan(req model.CarePlanRequest) (map[string]interface{}, error) + UpdateCarePlan(req model.CarePlanRequest) (map[string]interface{}, error) + GetCareplanByPatient(id string) (map[string]interface{}, error) + HandleCheckCarePlan(id string) ([]string, bool, error) +} + +type CarePlanRepository struct { + // Define fields for CarePlanRepository + akses *model.Akses +} + +// GetCareplanByPatient implements CarePlanInterface. +func (c *CarePlanRepository) GetCareplanByPatient(id string) (map[string]interface{}, error) { + var data map[string]interface{} + + url := c.akses.BaseUrl + "/CarePlan?subject=" + id + method := "GET" + + client := &http.Client{} + request, err := http.NewRequest(method, url, nil) + if err != nil { + return nil, err + } + oauth := model.OauthRequest{ + ClientId: c.akses.ClientId, + ClientSecret: c.akses.ClientSecret, + } + token, err := NewOauthRequestRepo(c.akses).GenerateToken(oauth) + if err != nil { + return nil, err + } + if token == nil { + return nil, errors.New(constant.ErrGenerateToken) + } + request.Header.Add("Authorization", "Bearer "+token.AccessToken) + request.Header.Set("Content-Type", constant.ContentTypeFHIRJSON) + request.Header.Set("Accept", constant.ContentTypeFHIRJSON) + + res, err := client.Do(request) + if err != nil { + return nil, err + } + defer res.Body.Close() + + err = json.NewDecoder(res.Body).Decode(&data) + if err != nil { + return nil, err + } + return data, nil +} + +// HandleCheckCarePlan implements CarePlanInterface. +func (c *CarePlanRepository) HandleCheckCarePlan(id string) ([]string, bool, error) { + careplan, err := c.GetCareplanByPatient(id) + if err != nil { + return nil, false, err + } + var ids []string + if entries, ok := careplan["entry"].([]interface{}); ok && len(entries) != 0 { + if entries, ok := (careplan)["entry"].([]interface{}); ok && len(entries) > 0 { + if entryMap, ok := entries[0].(map[string]interface{}); ok { + if resource, ok := entryMap["resource"].(map[string]interface{}); ok { + if id, ok := resource["id"].(string); ok { + //fmt.Println("resource.id:", id) + ids = append(ids, id) + return ids, true, nil + } + } + } + } + return nil, true, nil + } + + return nil, false, nil +} + +func NewCarePlanRepo(akses *model.Akses) CarePlanInterface { + return &CarePlanRepository{ + akses: akses, + } +} + +// CreateCarePlan implements CarePlanInterface. +func (c *CarePlanRepository) CreateCarePlan(req model.CarePlanRequest) (map[string]interface{}, error) { + req.ResourceType = constant.CarePlanResourceType + + patient, err := c.setupPatient(&req) + if err != nil { + return nil, err + } + + err = c.setupEncounter(&req, patient) + if err != nil { + return nil, err + } + + // err = c.setupPractitioner(&req) + // if err != nil { + // return nil, err + // } + + oauth := model.OauthRequest{ + ClientId: c.akses.ClientId, + ClientSecret: c.akses.ClientSecret, + } + token, err := NewOauthRequestRepo(c.akses).GenerateToken(oauth) + if err != nil { + return nil, err + } + if token == nil { + return nil, errors.New(constant.ErrGenerateToken) + } + + url := c.akses.BaseUrl + "/CarePlan" + return httputil.DoRequest(httputil.RequestOption{ + Method: "POST", + URL: url, + Body: req, + BearerToken: token.AccessToken, + Headers: httputil.DefaultFHIRHeaders(), + }) +} + +func (c *CarePlanRepository) UpdateCarePlan(req model.CarePlanRequest) (map[string]interface{}, error) { + req.ResourceType = constant.CarePlanResourceType + + patient, err := c.setupPatient(&req) + if err != nil { + return nil, err + } + + err = c.setupEncounter(&req, patient) + if err != nil { + return nil, err + } + + err = c.setupPractitioner(&req) + if err != nil { + return nil, err + } + + oauth := model.OauthRequest{ + ClientId: c.akses.ClientId, + ClientSecret: c.akses.ClientSecret, + } + token, err := NewOauthRequestRepo(c.akses).GenerateToken(oauth) + if err != nil { + return nil, err + } + if token == nil { + return nil, errors.New(constant.ErrGenerateToken) + } + + url := c.akses.BaseUrl + fmt.Sprintf("/CarePlan/%s", req.Id) + return httputil.DoRequest(httputil.RequestOption{ + Method: "PUT", + URL: url, + Body: req, + BearerToken: token.AccessToken, + Headers: httputil.DefaultFHIRHeaders(), + }) +} + +func (c *CarePlanRepository) setupPatient(req *model.CarePlanRequest) (string, error) { + if req.Subject.Reference == "" { + return "", nil + } + patientInterface := NewPatientRepo(c.akses) + patient, err := patientInterface.HandleCheckPatient(req.Subject.Reference) + if err != nil { + return "", err + } + + if patient != "" { + req.Subject.Reference = "Patient/" + patient + } + return patient, nil +} + +func (c *CarePlanRepository) setupEncounter(req *model.CarePlanRequest, patient string) error { + if patient == "" { + return nil + } + encounterInterface := NewEncounterRepo(c.akses) + encounterId, encounterExist, err := encounterInterface.HandleCheckEncounter(patient) + if err != nil { + return err + } + if encounterExist { + req.Encounter.Reference = "Encounter/" + encounterId + } + return nil +} + +// setupPractitioner handles practitioner reference logic. +func (c *CarePlanRepository) setupPractitioner(req *model.CarePlanRequest) error { + if req.Author.Reference == "" { + return nil + } + practicionerInterface := NewPracticionerRepo(c.akses) + ref, display, err := practicionerInterface.HandleCheckPartitioner(req.Author.Reference) + if err != nil { + return err + } + if ref != "" { + req.Author.Reference = "Practitioner/" + ref + req.Author.Display = display + } + return nil +} diff --git a/internal/integration/clinicalImpression_integration.go b/internal/integration/clinicalImpression_integration.go new file mode 100644 index 0000000..feba396 --- /dev/null +++ b/internal/integration/clinicalImpression_integration.go @@ -0,0 +1,326 @@ +package integration + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "net/http" + "satusehat-rssa/internal/constant" + "satusehat-rssa/internal/model" + "satusehat-rssa/pkg/httputil" +) + +type ClinicalImppressionInterface interface { + // Define methods for ClinicalImpressionInterface + CreateClinicalImpression(req model.ClinicalImpressionRequest) (map[string]interface{}, error) + UpdateClinicalImpression(req model.ClinicalImpressionRequest) (map[string]interface{}, error) + GetClinicalImpressionByPatient(id string) (map[string]interface{}, error) + HandleCheckClinicalImpression(id string) ([]string, bool, error) +} + +type ClinicalImpressionRepository struct { + akses *model.Akses +} + +// GetClinicalImpressionByPatient implements ClinicalImppressionInterface. +func (c *ClinicalImpressionRepository) GetClinicalImpressionByPatient(id string) (map[string]interface{}, error) { + var data map[string]interface{} + url := c.akses.BaseUrl + "/ClinicalImpression?subject=" + id + method := "GET" + + client := &http.Client{} + request, err := http.NewRequest(method, url, nil) + if err != nil { + return nil, err + } + oauth := model.OauthRequest{ + ClientId: c.akses.ClientId, + ClientSecret: c.akses.ClientSecret, + } + token, err := NewOauthRequestRepo(c.akses).GenerateToken(oauth) + if err != nil { + return nil, err + } + if token == nil { + return nil, errors.New(constant.ErrGenerateToken) + } + request.Header.Add("Authorization", "Bearer "+token.AccessToken) + request.Header.Set("Content-Type", constant.ContentTypeFHIRJSON) + request.Header.Set("Accept", constant.ContentTypeFHIRJSON) + res, err := client.Do(request) + if err != nil { + return nil, err + } + defer res.Body.Close() + + err = json.NewDecoder(res.Body).Decode(&data) + if err != nil { + return nil, err + } + + return data, nil +} + +// HandleCheckClinicalImpression implements ClinicalImppressionInterface. +func (c *ClinicalImpressionRepository) HandleCheckClinicalImpression(id string) ([]string, bool, error) { + clinical, err := c.GetClinicalImpressionByPatient(id) + if err != nil { + return nil, false, err + } + var ids []string + if entries, ok := clinical["entry"].([]interface{}); ok && len(entries) != 0 { + if entries, ok := (clinical)["entry"].([]interface{}); ok && len(entries) > 0 { + if entryMap, ok := entries[0].(map[string]interface{}); ok { + if resource, ok := entryMap["resource"].(map[string]interface{}); ok { + if id, ok := resource["id"].(string); ok { + //fmt.Println("resource.id:", id) + ids = append(ids, id) + return ids, true, nil + } + } + } + } + return nil, true, nil + } + + return nil, false, nil +} + +// CreateClinicalImpression implements ClinicalImppressionInterface. +func (c *ClinicalImpressionRepository) CreateClinicalImpression(req model.ClinicalImpressionRequest) (map[string]interface{}, error) { + var ( + data map[string]interface{} + ) + req.ResourceType = constant.ClinicalImpressionResourceType + + // Setup Patient + var patient string + if req.Subject.Reference != "" { + patientInterface := NewPatientRepo(c.akses) + var err error + patient, err = patientInterface.HandleCheckPatient(req.Subject.Reference) + if err != nil { + return nil, err + } + if patient == "" { // Belum ada di satu sehat + return nil, errors.New("patient not found") + } else { + req.Subject.Reference = "Patient/" + patient + } + } + // Setup Encounter + if patient != "" { + encounterInterface := NewEncounterRepo(c.akses) + encounterId, encounterExist, err := encounterInterface.HandleCheckEncounter(patient) + if err != nil { + return nil, err + } + if encounterExist { + req.Encounter.Reference = "Encounter/" + encounterId + } else { + return nil, errors.New("encounter not found") + } + } + // Setup Practitioner + // if req.Assessor.Reference != "" { + // practitionerInterface := NewPracticionerRepo(c.akses) + // practitionerId, _, err := practitionerInterface.HandleCheckPartitioner(req.Assessor.Reference) + // if err != nil { + // return nil, err + // } + // if practitionerId != "" { + // req.Assessor.Reference = "Practitioner/" + practitionerId + // } else { + // return nil, errors.New("practitioner not found") + // } + // } + + if len(req.Investigation) > 0 { + //Setup Condition + if patient != "" { + conditionInterface := NewConditionRepo(c.akses) + conditionId, conditionExist, err := conditionInterface.HandleCheckCondition(patient) + if err != nil { + return nil, err + } + req.Problem = append(req.Problem, model.Reference{ + Reference: "Condition/" + conditionId, + }) + if !conditionExist { + return nil, errors.New("condition not found") + } + } + + //Setup Observation + if patient != "" { + observationInterface := NewObservationRepo(c.akses) + observationId, observationExist, err := observationInterface.HandleCheckObservation(patient) + if err != nil { + return nil, err + } + for _, id := range observationId { + req.Investigation = append(req.Investigation, model.Investigation{ + Item: []model.InvestigationItem{ + {Reference: "Observation/" + id}, + }, + }) + } + + if !observationExist { + return nil, errors.New("observation not found") + } + } + } + + url := c.akses.BaseUrl + "/ClinicalImpression" + method := "POST" + + payload, err := json.Marshal(req) + if err != nil { + return nil, err + } + client := &http.Client{} + request, err := http.NewRequest(method, url, bytes.NewBuffer(payload)) + if err != nil { + return nil, err + } + + // Assuming the access token is stored in a field of the akses struct + oauth := model.OauthRequest{ + ClientId: c.akses.ClientId, + ClientSecret: c.akses.ClientSecret, + } + OauthInterface := NewOauthRequestRepo(c.akses) + token, err := OauthInterface.GenerateToken(oauth) + + if err != nil { + return nil, err + + } + if token != nil { + request.Header.Add("Authorization", "Bearer "+token.AccessToken) + } else { + return nil, errors.New(constant.ErrGenerateToken) + } + + request.Header.Set("Content-Type", "application/json") + + response, err := client.Do(request) + if err != nil { + return nil, err + } + defer response.Body.Close() + + err = json.NewDecoder(response.Body).Decode(&data) + if err != nil { + return nil, err + } + + return data, nil +} + +func (c *ClinicalImpressionRepository) UpdateClinicalImpression(req model.ClinicalImpressionRequest) (map[string]interface{}, error) { + req.ResourceType = constant.ClinicalImpressionResourceType + + // Setup Patient + var patient string + if req.Subject.Reference != "" { + patientInterface := NewPatientRepo(c.akses) + var err error + patient, err = patientInterface.HandleCheckPatient(req.Subject.Reference) + if err != nil { + return nil, err + } + if patient == "" { // Belum ada di satu sehat + return nil, errors.New("patient not found") + } else { + req.Subject.Reference = "Patient/" + patient + } + } + // Setup Encounter + if patient != "" { + encounterInterface := NewEncounterRepo(c.akses) + encounterId, encounterExist, err := encounterInterface.HandleCheckEncounter(patient) + if err != nil { + return nil, err + } + if encounterExist { + req.Encounter.Reference = "Encounter/" + encounterId + } else { + return nil, errors.New("encounter not found") + } + } + // Setup Practitioner + if req.Assessor.Reference != "" { + practitionerInterface := NewPracticionerRepo(c.akses) + practitionerId, _, err := practitionerInterface.HandleCheckPartitioner(req.Assessor.Reference) + if err != nil { + return nil, err + } + if practitionerId != "" { + req.Assessor.Reference = "Practitioner/" + practitionerId + } else { + return nil, errors.New("practitioner not found") + } + + //Setup Condition + if patient != "" { + conditionInterface := NewConditionRepo(c.akses) + conditionId, conditionExist, err := conditionInterface.HandleCheckCondition(patient) + if err != nil { + return nil, err + } + req.Problem = append(req.Problem, model.Reference{ + Reference: "Condition/" + conditionId, + }) + if !conditionExist { + return nil, errors.New("condition not found") + } + } + + //Setup Observation + if patient != "" { + observationInterface := NewObservationRepo(c.akses) + observationId, observationExist, err := observationInterface.HandleCheckObservation(patient) + if err != nil { + return nil, err + } + for _, id := range observationId { + req.Investigation = append(req.Investigation, model.Investigation{ + Item: []model.InvestigationItem{ + {Reference: "Observation/" + id}, + }, + }) + } + + if !observationExist { + return nil, errors.New("observation not found") + } + } + } + + oauth := model.OauthRequest{ + ClientId: c.akses.ClientId, + ClientSecret: c.akses.ClientSecret, + } + token, err := NewOauthRequestRepo(c.akses).GenerateToken(oauth) + if err != nil { + return nil, err + } + if token == nil { + return nil, errors.New(constant.ErrGenerateToken) + } + + url := c.akses.BaseUrl + fmt.Sprintf("/ClinicalImpression/%s", req.Id) + return httputil.DoRequest(httputil.RequestOption{ + Method: "PUT", + URL: url, + Body: req, + BearerToken: token.AccessToken, + }) +} + +func NewClinicalImpressionRepo(akses *model.Akses) ClinicalImppressionInterface { + return &ClinicalImpressionRepository{akses: akses} +} diff --git a/internal/integration/composition_integration.go b/internal/integration/composition_integration.go new file mode 100644 index 0000000..53a33e3 --- /dev/null +++ b/internal/integration/composition_integration.go @@ -0,0 +1,835 @@ +package integration + +import ( + "errors" + "os" + "satusehat-rssa/internal/constant" + "satusehat-rssa/internal/model" + "satusehat-rssa/pkg/httputil" +) + +type CompositionInterface interface { + CreateComposition(req model.CompositionRequest) (map[string]interface{}, error) + UpdateComposition(req model.CompositionRequest) (map[string]interface{}, error) + GetCompositionByID(id string) (map[string]interface{}, error) +} + +type CompositionRepository struct { + akses *model.Akses +} + +// CreateComposition implements CompositionInterface. +func (c *CompositionRepository) CreateComposition(req model.CompositionRequest) (map[string]interface{}, error) { + req.ResourceType = "Composition" + req.Identifier = model.Identifier{ + Use: "official", + System: "http://sys-ids.kemkes.go.id/composition/" + os.Getenv("ORGANIZATION_ID"), + Value: os.Getenv("ORGANIZATION_ID"), + } + + patient, err := c.setupPatient(&req) + if err != nil { + return nil, err + } + + err = c.setupEncounter(&req, patient) + if err != nil { + return nil, err + } + + err = c.setupOrganization(&req) + if err != nil { + return nil, err + } + + // section setup + if req.Anamnesis == "" { + req.Anamnesis = "default anamnesis" + } + if req.PemeriksaanFisik == "" { + req.PemeriksaanFisik = "default pemeriksaan fisik" + } + if req.PemeriksaanPenunjang == "" { + req.PemeriksaanPenunjang = "default pemeriksaan penunjang" + } + if req.Medikamentosa == "" { + req.Medikamentosa = "default medikamentosa" + } + if req.LanjutanPenatalaksanaan == "" { + req.LanjutanPenatalaksanaan = "default lanjutan penatalaksanaan" + } + req.Section = append(req.Section, model.SectionRequest{ + Title: "Anamnesis", + Code: model.CodeableConcept{ + Coding: []model.Coding{ + { + System: "http://terminology.kemkes.go.id", + Code: "TK000003", + Display: "Anamnesis", + }, + }, + }, + Text: &model.SectionText{ + Status: "additional", + Div: req.Anamnesis, + }, + }) + allergyId := c.setupAllergyIntollerance(patient) + section := model.SectionRequest{ + Title: "Riwayat Alergi", + Code: model.CodeableConcept{ + Coding: []model.Coding{ + { + System: "http://loinc.org", + Code: "18776-5", + Display: "Riwayat Alergi", + }, + }, + }, + } + if allergyId != "" { + section.Entry = []model.Reference{ + {Reference: "AllergyIntolerance/" + allergyId}, + } + } + req.Section = append(req.Section, section) + + /* Observation disabled for Now + req.Section = append(req.Section, model.SectionRequest{ + Title: "Pemeriksaan Fisik", + Code: model.CodeableConcept{ + Coding: []model.Coding{ + { + System: "http://terminology.kemkes.go.id", + Code: "TK000007", + Display: "Pemeriksaan Fisik", + }, + }, + }, + Section: []model.SectionRequest{ + { + Title: "Tanda Vital", + Code: model.CodeableConcept{ + Coding: []model.Coding{ + { + System: "http://loinc.org", + Code: "8716-3", + Display: "Vital signs", + }, + }, + }, + Entry: c.setupObservation(patient), + }, + { + Title: "Hasil Pemeriksaan", + Code: model.CodeableConcept{ + Coding: []model.Coding{ + { + System: "http://terminology.kemkes.go.id", + Code: "TK000007", + Display: "Pemeriksaan Fisik", + }, + }, + }, + Text: &model.SectionText{ + Status: "additional", + Div: req.PemeriksaanFisik, + }, + }, + }, + }) + */ + var entry []model.Reference + clinicalId := c.setupClinicalImpression(patient) + goalId := c.setupGoal(patient) + carePlanId := c.setupCarePlan(patient) + + if clinicalId != "" { + entry = append(entry, model.Reference{Reference: "ClinicalImpression/" + clinicalId}) + } + if goalId != "" { + entry = append(entry, model.Reference{Reference: "Goal/" + goalId}) + } + if carePlanId != "" { + entry = append(entry, model.Reference{Reference: "CarePlan/" + carePlanId}) + } + + req.Section = append(req.Section, model.SectionRequest{ + Title: "Perencanaan Perawatan", + Code: model.CodeableConcept{ + Coding: []model.Coding{ + { + System: "http://loinc.org", + Code: "18776-5", + Display: "Plan of care note", + }, + }, + }, + Entry: entry, + }) + + var entryLab, entryRad []model.Reference + serviceRequestIdLab := c.setupServiceRequestCategory(patient, "LAB") + specimenIdLab := c.setupSpecimenCategory(patient) + diagnosticReportIdLab := c.setupDiagnosticCategory(patient, "LAB") + observationIdLab := c.setupObservationCategory(patient, "LAB") + + serviceRequestIdRad := c.setupServiceRequestCategory(patient, "RAD") + imagingStudyRad := c.setupImagingStudy(patient) + diagnosticReportIdRad := c.setupDiagnosticCategory(patient, "RAD") + observationIdRad := c.setupObservationCategory(patient, "RAD") + + if serviceRequestIdLab != "" { + entryLab = append(entryLab, model.Reference{Reference: "ServiceRequest/" + serviceRequestIdLab}) + } + if specimenIdLab != "" { + entryLab = append(entryLab, model.Reference{Reference: "Specimen/" + specimenIdLab}) + } + if diagnosticReportIdLab != "" { + entryLab = append(entryLab, model.Reference{Reference: "DiagnosticReport/" + diagnosticReportIdLab}) + } + if observationIdLab != "" { + entryLab = append(entryLab, model.Reference{Reference: "Observation/" + observationIdLab}) + } + + if serviceRequestIdRad != "" { + entryRad = append(entryRad, model.Reference{Reference: "ServiceRequest/" + serviceRequestIdRad}) + } + if imagingStudyRad != "" { + entryRad = append(entryRad, model.Reference{Reference: "ImagingStudy/" + imagingStudyRad}) + } + if diagnosticReportIdRad != "" { + entryRad = append(entryRad, model.Reference{Reference: "DiagnosticReport/" + diagnosticReportIdRad}) + } + if observationIdRad != "" { + entryRad = append(entryRad, model.Reference{Reference: "Observation/" + observationIdRad}) + } + + if len(entryLab) != 0 && len(entryRad) != 0 { + req.Section = append(req.Section, model.SectionRequest{ + Title: "Pemeriksaan Penunjang", + Code: model.CodeableConcept{ + Coding: []model.Coding{ + { + System: "http://terminology.kemkes.go.id", + Code: "TK000009", + Display: "Hasil Pemeriksaan Penunjang", + }, + }, + }, + Text: &model.SectionText{ + Status: "additional", + Div: req.PemeriksaanPenunjang, + }, + Section: []model.SectionRequest{ + { + Title: "Hasil Pemeriksaan Laboratorium", + Code: model.CodeableConcept{ + Coding: []model.Coding{ + { + System: "http://loinc.org", + Code: "11502-2", + Display: "Laboratory report", + }, + }, + }, + Entry: entryLab, + }, + { + Title: "Hasil Pemeriksaan Radiologi", + Code: model.CodeableConcept{ + Coding: []model.Coding{ + { + System: "http://loinc.org", + Code: "18782-3", + Display: "Radiology Study observation (narrative)", + }, + }, + }, + Entry: entryRad, + }, + }, + }) + } else if len(entryLab) != 0 && len(entryRad) == 0 { + req.Section = append(req.Section, model.SectionRequest{ + Title: "Pemeriksaan Penunjang", + Code: model.CodeableConcept{ + Coding: []model.Coding{ + { + System: "http://terminology.kemkes.go.id", + Code: "TK000009", + Display: "Hasil Pemeriksaan Penunjang", + }, + }, + }, + Text: &model.SectionText{ + Status: "additional", + Div: req.PemeriksaanPenunjang, + }, + Section: []model.SectionRequest{ + { + Title: "Hasil Pemeriksaan Laboratorium", + Code: model.CodeableConcept{ + Coding: []model.Coding{ + { + System: "http://loinc.org", + Code: "11502-2", + Display: "Laboratory report", + }, + }, + }, + Entry: entryLab, + }, + }, + }) + } else if len(entryLab) == 0 && len(entryRad) != 0 { + req.Section = append(req.Section, model.SectionRequest{ + Title: "Pemeriksaan Penunjang", + Code: model.CodeableConcept{ + Coding: []model.Coding{ + { + System: "http://terminology.kemkes.go.id", + Code: "TK000009", + Display: "Hasil Pemeriksaan Penunjang", + }, + }, + }, + Text: &model.SectionText{ + Status: "additional", + Div: req.PemeriksaanPenunjang, + }, + Section: []model.SectionRequest{ + { + Title: "Hasil Pemeriksaan Radiologi", + Code: model.CodeableConcept{ + Coding: []model.Coding{ + { + System: "http://loinc.org", + Code: "18782-3", + Display: "Radiology Study observation (narrative)", + }, + }, + }, + Entry: entryRad, + }, + }, + }) + } + + var entryCondition []model.Reference + conditionId := c.setupCondition(patient) + if conditionId != "" { + entryCondition = append(entryCondition, model.Reference{Reference: "Condition/" + conditionId}) + } + req.Section = append(req.Section, model.SectionRequest{ + Title: "Diagnosa", + Code: model.CodeableConcept{ + Coding: []model.Coding{ + { + System: "http://terminology.kemkes.go.id", + Code: "TK000004", + Display: "Diagnosis", + }, + }, + }, + Section: []model.SectionRequest{ + { + Title: "Diagnosa Utama", + Code: model.CodeableConcept{ + Coding: []model.Coding{ + { + System: "http://loinc.org", + Code: "78375-3", + Display: "Discharge diagnosis Narrative", + }, + }, + }, + Entry: entryCondition, + }, + }, + }) + + var entryProcedure []model.Reference + procedureId := c.setupProcedure(patient) + if procedureId != "" { + entryProcedure = append(entryProcedure, model.Reference{Reference: "Procedure/" + procedureId}) + } + req.Section = append(req.Section, model.SectionRequest{ + Title: "Tindakan Operatif/Non Operatif Utama", + Code: model.CodeableConcept{ + Coding: []model.Coding{ + { + System: "http://terminology.kemkes.go.id", + Code: "TK000005", + Display: "Tindakan/Prosedur Medis", + }, + }, + }, + Entry: entryProcedure, + }) + var medication []model.Reference + medicationRequestId := c.setupMedicationRequest(patient) + if medicationRequestId != "" { + medication = append(medication, model.Reference{Reference: "MedicationRequest/" + medicationRequestId}) + } + medicationDispenseId := c.setupMedicationDispense(patient) + if medicationDispenseId != "" { + medication = append(medication, model.Reference{Reference: "MedicationDispense/" + medicationDispenseId}) + } + /* Farmasi section disabled for Now + req.Section = append(req.Section, model.SectionRequest{ + Title: "Farmasi", + Code: model.CodeableConcept{ + Coding: []model.Coding{ + { + System: "http://terminology.kemkes.go.id", + Code: "TK000013", + Display: "Obat", + }, + }, + }, + Section: []model.SectionRequest{ + /*{ + Title: "Obat Saat Kunjungan", + Code: model.CodeableConcept{ + Coding: []model.Coding{ + { + System: "http://loinc.org", + Code: "42346-7", + Display: "Medications on admission (narrative)", + }, + }, + }, + Entry: []model.Reference{ + {Reference: "MedicationAdministration/"}, + {Reference: "MedicationRequest/"}, + {Reference: "MedicationDispense/"}, + }, + },*/ + /* { + Title: "Obat Pulang", + Code: model.CodeableConcept{ + Coding: []model.Coding{ + { + System: "http://loinc.org", + Code: "75311-1", + Display: "Discharge medications Narrative", + }, + }, + }, + Entry: medication, + }, + }, + }) + */ + req.Section = append(req.Section, model.SectionRequest{ + Title: "Medikamentosa", + Code: model.CodeableConcept{ + Coding: []model.Coding{ + { + System: "http://loinc.org", + Code: "18776-5", + Display: "Plan of treatment (narrative)", + }, + }, + }, + Text: &model.SectionText{ + Status: "additional", + Div: req.Medikamentosa, + }, + }) + req.Section = append(req.Section, model.SectionRequest{ + Title: "Lanjutan Penatalaksanaan", + Code: model.CodeableConcept{ + Coding: []model.Coding{ + { + System: "http://loinc.org", + Code: "8653-8", + Display: "Hospital Discharge instructions", + }, + }, + }, + Text: &model.SectionText{ + Status: "additional", + Div: req.LanjutanPenatalaksanaan, + }, + }) + + //remove field unused + req.Anamnesis = "" + req.PemeriksaanFisik = "" + req.PemeriksaanPenunjang = "" + req.Medikamentosa = "" + req.LanjutanPenatalaksanaan = "" + + oauth := model.OauthRequest{ + ClientId: c.akses.ClientId, + ClientSecret: c.akses.ClientSecret, + } + token, err := NewOauthRequestRepo(c.akses).GenerateToken(oauth) + if err != nil { + return nil, err + } + + if token == nil { + return nil, errors.New(constant.ErrGenerateToken) + } + + // b, err := json.MarshalIndent(req, "", " ") + // if err != nil { + // log.Println("error marshal:", err) + // } else { + // log.Println(string(b)) + // } + + url := c.akses.BaseUrl + "/Composition" + return httputil.DoRequest(httputil.RequestOption{ + Method: "POST", + URL: url, + Body: req, + BearerToken: token.AccessToken, + }) + +} + +// GetCompositionByID implements CompositionInterface. +func (c *CompositionRepository) GetCompositionByID(id string) (map[string]interface{}, error) { + panic("unimplemented") +} + +// UpdateComposition implements CompositionInterface. +func (c *CompositionRepository) UpdateComposition(req model.CompositionRequest) (map[string]interface{}, error) { + req.ResourceType = "Composition" + + patient, err := c.setupPatient(&req) + if err != nil { + return nil, err + } + + err = c.setupEncounter(&req, patient) + if err != nil { + return nil, err + } + + err = c.setupOrganization(&req) + if err != nil { + return nil, err + } + + oauth := model.OauthRequest{ + ClientId: c.akses.ClientId, + ClientSecret: c.akses.ClientSecret, + } + token, err := NewOauthRequestRepo(c.akses).GenerateToken(oauth) + if err != nil { + return nil, err + } + + if token == nil { + return nil, errors.New(constant.ErrGenerateToken) + } + url := c.akses.BaseUrl + "/Composition/" + req.Id + return httputil.DoRequest(httputil.RequestOption{ + Method: "PUT", + URL: url, + Body: req, + BearerToken: token.AccessToken, + }) +} + +func (c *CompositionRepository) setupPatient(req *model.CompositionRequest) (string, error) { + if req.Subject.Reference == "" { + return "", nil + } + patientInterface := NewPatientRepo(c.akses) + patient, err := patientInterface.HandleCheckPatient(req.Subject.Reference) + if err != nil { + return "", err + } + if patient != "" { + req.Subject.Reference = "Patient/" + patient + } + return patient, nil +} + +// setupEncounter extracts encounter setup logic from CreateDiagnosisReport. +func (c *CompositionRepository) setupEncounter(req *model.CompositionRequest, patient string) error { + if patient == "" { + return nil + } + encounterInterface := NewEncounterRepo(c.akses) + encounterId, encounterExist, err := encounterInterface.HandleCheckEncounter(patient) + if err != nil { + return err + } + if encounterExist { + req.Encounter.Reference = "Encounter/" + encounterId + } else { + //Buat dulu encounter + } + return nil +} + +func (c *CompositionRepository) setupOrganization(req *model.CompositionRequest) error { + + req.Custodian.Reference = "Organization/" + os.Getenv("ORGANIZATION_ID") + return nil +} + +func (c *CompositionRepository) setupPractitioner(req *model.CompositionRequest) error { + if len(req.Author) == 0 { + return nil + } + practicionerInterface := NewPracticionerRepo(c.akses) + ref, display, err := practicionerInterface.HandleCheckPartitioner(req.Author[0].Reference) + if err != nil { + return err + } + if ref != "" { + req.Author[0].Reference = "Practitioner/" + ref + req.Author[0].Display = display + } + return nil +} + +func (c *CompositionRepository) setupAllergyIntollerance(patient string) string { + if patient == "" { + return "" + } + allergyInterface := NewAllergancyToleranRepo(c.akses) + allergyId, allergyExist, err := allergyInterface.HandleCheckAllergancyToleran(patient) + if err != nil { + return "" + } + if allergyExist { + return allergyId[0] + } + return "" +} + +func (c *CompositionRepository) setupObservation(patient string) []model.Reference { + if patient == "" { + return nil + } + observationInterface := NewObservationRepo(c.akses) + observationId, observationExist, err := observationInterface.HandleCheckObservation(patient) + if err != nil { + return nil + } + if observationExist { + var references []model.Reference + + for _, id := range observationId { + references = append(references, model.Reference{Reference: "Observation/" + id}) + } + return references + } + return nil +} + +func (c *CompositionRepository) setupClinicalImpression(patient string) string { + if patient == "" { + return "" + } + clinicalInterface := NewClinicalImpressionRepo(c.akses) + clinicalId, clinicalExist, err := clinicalInterface.HandleCheckClinicalImpression(patient) + if err != nil { + return "" + } + if clinicalExist { + return clinicalId[0] + } + return "" +} + +func (c *CompositionRepository) setupGoal(patient string) string { + if patient == "" { + return "" + } + goalInterface := NewGoalRepo(c.akses) + goalId, goalExist, err := goalInterface.HandleCheckGoal(patient) + if err != nil { + return "" + } + if goalExist { + return goalId[0] + } + return "" +} + +func (c *CompositionRepository) setupCarePlan(patient string) string { + if patient == "" { + return "" + } + careplanInterface := NewCarePlanRepo(c.akses) + careplanId, careplanExist, err := careplanInterface.HandleCheckCarePlan(patient) + if err != nil { + return "" + } + if careplanExist { + return careplanId[0] + } + return "" +} + +func (c *CompositionRepository) setupServiceRequestCategory(patient string, category string) string { + if patient == "" { + return "" + } + if category == "LAB" { + category = "108252007" + } else if category == "RAD" { + category = "363679005" + } + serviceRequestInterface := NewServiceRequestRepository(c.akses) + serviceRequestId, serviceRequestExist, err := serviceRequestInterface.HandleCheckServiceRequestByCategory(patient, category) + if err != nil { + return "" + } + if serviceRequestExist { + return serviceRequestId[0] + } + return "" +} + +func (c *CompositionRepository) setupSpecimenCategory(patient string) string { + if patient == "" { + return "" + } + + specimenInterface := NewSpecimenRepository(c.akses) + specimenId, specimenExist, err := specimenInterface.HandleCheckSpecimen(patient) + if err != nil { + return "" + } + if specimenExist { + return specimenId[0] + } + return "" +} + +func (c *CompositionRepository) setupImagingStudy(patient string) string { + if patient == "" { + return "" + } + + imagingInterface := NewImagingStudyRepo(c.akses) + imagingId, imagingExist, err := imagingInterface.HandleCheckImagingStudy(patient) + if err != nil { + return "" + } + if imagingExist { + return imagingId[0] + } + return "" +} + +func (c *CompositionRepository) setupObservationCategory(patient string, category string) string { + if patient == "" { + return "" + } + if category == "LAB" { + category = "laboratory" + } else if category == "RAD" { + category = "imaging" + } + observationInterface := NewObservationRepo(c.akses) + observationId, observationExist, err := observationInterface.HandleCheckObservationCategory(patient, category) + if err != nil { + return "" + } + if observationExist { + return observationId[0] + } + return "" +} + +func (c *CompositionRepository) setupDiagnosticCategory(patient string, category string) string { + if patient == "" { + return "" + } + if category == "LAB" { + category = "LAB" + } else if category == "RAD" { + category = "RAD" + } + observationInterface := NewObservationRepo(c.akses) + observationId, observationExist, err := observationInterface.HandleCheckObservationCategory(patient, category) + if err != nil { + return "" + } + if observationExist { + return observationId[0] + } + return "" +} + +func (c *CompositionRepository) setupCondition(patient string) string { + if patient == "" { + return "" + } + + conditionInterface := NewConditionRepo(c.akses) + conditionId, conditionExist, err := conditionInterface.HandleCheckCondition(patient) + if err != nil { + return "" + } + if conditionExist { + return conditionId + } + return "" +} + +func (c *CompositionRepository) setupProcedure(patient string) string { + if patient == "" { + return "" + } + + procedureInterface := NewProcedureRepo(c.akses) + procedureId, procedureExist, err := procedureInterface.HandleCheckProcedure(patient) + if err != nil { + return "" + } + if procedureExist { + return procedureId[0] + } + return "" +} + +func (c *CompositionRepository) setupMedicationRequest(patient string) string { + if patient == "" { + return "" + } + + medicationRequestInterface := NewMedicationRequestRepo(c.akses) + medicationRequestId, medicationRequestExist, err := medicationRequestInterface.HandleCheckMedicationRequest(patient, "") + if err != nil { + return "" + } + if medicationRequestExist { + return medicationRequestId[0] + } + return "" +} + +func (c *CompositionRepository) setupMedicationDispense(patient string) string { + if patient == "" { + return "" + } + + medicationDispenseInterface := NewMedicationDispenseRepo(c.akses) + medicationDispenseId, medicationDispenseExist, err := medicationDispenseInterface.HandleCheckMedicationDispense(patient, "") + if err != nil { + return "" + } + if medicationDispenseExist { + return medicationDispenseId[0] + } + return "" +} + +func NewCompositionRepo(akses *model.Akses) CompositionInterface { + return &CompositionRepository{ + akses: akses, + } +} diff --git a/internal/integration/condition_integration.go b/internal/integration/condition_integration.go new file mode 100644 index 0000000..62fe098 --- /dev/null +++ b/internal/integration/condition_integration.go @@ -0,0 +1,230 @@ +package integration + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "net/http" + "satusehat-rssa/internal/constant" + "satusehat-rssa/internal/model" + "satusehat-rssa/pkg/httputil" +) + +type ConditionInterface interface { + CreateCondition(request model.ConditionRequest) (map[string]interface{}, error) + GetConditionByPatient(id string) (map[string]interface{}, error) + HandleCheckCondition(id string) (string, bool, error) + UpdateCondition(request model.ConditionRequest) (map[string]interface{}, error) +} + +type ConditionRepository struct { + akses *model.Akses +} + +// HandleCheckCondition implements ConditionInterface. +func (c *ConditionRepository) HandleCheckCondition(id string) (string, bool, error) { + condition, err := c.GetConditionByPatient(id) + if err != nil { + return "", false, err + } + + // You can process 'encounter' here if needed + if entries, ok := condition["entry"].([]interface{}); ok && len(entries) != 0 { + if entries, ok := (condition)["entry"].([]interface{}); ok && len(entries) > 0 { + if entryMap, ok := entries[0].(map[string]interface{}); ok { + if resource, ok := entryMap["resource"].(map[string]interface{}); ok { + if id, ok := resource["id"].(string); ok { + //fmt.Println("resource.id:", id) + return id, true, nil + } + } + } + } + return "", true, nil + } + + return "", false, nil +} + +func NewConditionRepo(akses *model.Akses) ConditionInterface { + return &ConditionRepository{akses: akses} +} + +// Implement the ConditionInterface methods for ConditionRepository +func (c *ConditionRepository) CreateCondition(req model.ConditionRequest) (map[string]interface{}, error) { + // TODO: implement the logic here + var ( + data map[string]interface{} + ) + + req.ClinicalStatus.Coding = append(req.ClinicalStatus.Coding, model.Coding{ + System: "http://terminology.hl7.org/CodeSystem/condition-clinical", + Code: "active", + Display: "Active", + }) + + var patient string + if req.Subject.Reference != "" { + patientInterface := NewPatientRepo(c.akses) + var err error + patient, err = patientInterface.HandleCheckPatient(req.Subject.Reference) + if err != nil { + return nil, err + } + if patient == "" { // Belum ada di satu sehat + return nil, errors.New("patient not found") + } else { + req.Subject.Reference = "Patient/" + patient + } + } + + // Setup Encounter + if patient != "" { + encounterInterface := NewEncounterRepo(c.akses) + encounterId, encounterExist, err := encounterInterface.HandleCheckEncounter(patient) + if err != nil { + return nil, err + } + + if encounterExist { + req.Encounter.Reference = "Encounter/" + encounterId + } else { + return nil, errors.New("encounter not found") + } + } + + url := c.akses.BaseUrl + "/Condition" + method := "POST" + payload, err := json.Marshal(req) + if err != nil { + return nil, err + } + client := &http.Client{} + request, err := http.NewRequest(method, url, bytes.NewBuffer(payload)) + if err != nil { + return nil, err + } + oauth := model.OauthRequest{ + ClientId: c.akses.ClientId, + ClientSecret: c.akses.ClientSecret, + } + OauthInterface := NewOauthRequestRepo(c.akses) + token, err := OauthInterface.GenerateToken(oauth) + if err != nil { + return nil, err + } + + if token != nil { + request.Header.Add("Authorization", "Bearer "+token.AccessToken) + } else { + return nil, errors.New(constant.ErrGenerateToken) + } + request.Header.Set("Content-Type", constant.ContentTypeFHIRJSON) + res, err := client.Do(request) + if err != nil { + return nil, err + } + defer res.Body.Close() + + err = json.NewDecoder(res.Body).Decode(&data) + if err != nil { + return nil, err + } + + return data, nil +} + +func (c *ConditionRepository) GetConditionByPatient(id string) (map[string]interface{}, error) { + // TODO: implement the logic here + var ( + data map[string]interface{} + ) + url := c.akses.BaseUrl + "/Condition?subject=" + id + method := "GET" + client := &http.Client{} + request, err := http.NewRequest(method, url, nil) + if err != nil { + return nil, err + } + oauth := model.OauthRequest{ + ClientId: c.akses.ClientId, + ClientSecret: c.akses.ClientSecret, + } + OauthInterface := NewOauthRequestRepo(c.akses) + token, err := OauthInterface.GenerateToken(oauth) + if err != nil { + return nil, err + } + if token != nil { + request.Header.Add("Authorization", "Bearer "+token.AccessToken) + } else { + return nil, errors.New(constant.ErrGenerateToken) + } + request.Header.Set("Content-Type", constant.ContentTypeFHIRJSON) + res, err := client.Do(request) + if err != nil { + return nil, err + } + defer res.Body.Close() + + err = json.NewDecoder(res.Body).Decode(&data) + if err != nil { + return nil, err + } + + return data, nil +} + +func (c *ConditionRepository) UpdateCondition(req model.ConditionRequest) (map[string]interface{}, error) { + var patient string + if req.Subject.Reference != "" { + patientInterface := NewPatientRepo(c.akses) + var err error + patient, err = patientInterface.HandleCheckPatient(req.Subject.Reference) + if err != nil { + return nil, err + } + if patient == "" { // Belum ada di satu sehat + return nil, errors.New("patient not found") + } else { + req.Subject.Reference = "Patient/" + patient + } + } + + // Setup Encounter + if patient != "" { + encounterInterface := NewEncounterRepo(c.akses) + encounterId, encounterExist, err := encounterInterface.HandleCheckEncounter(patient) + if err != nil { + return nil, err + } + + if encounterExist { + req.Encounter.Reference = "Encounter/" + encounterId + } else { + return nil, errors.New("encounter not found") + } + } + + oauth := model.OauthRequest{ + ClientId: c.akses.ClientId, + ClientSecret: c.akses.ClientSecret, + } + token, err := NewOauthRequestRepo(c.akses).GenerateToken(oauth) + if err != nil { + return nil, err + } + if token == nil { + return nil, errors.New(constant.ErrGenerateToken) + } + + url := c.akses.BaseUrl + fmt.Sprintf("/Condition/%s", req.Id) + return httputil.DoRequest(httputil.RequestOption{ + Method: "PUT", + URL: url, + Body: req, + BearerToken: token.AccessToken, + Headers: httputil.DefaultFHIRHeaders(), + }) +} diff --git a/internal/integration/diagnosisreport_integration.go b/internal/integration/diagnosisreport_integration.go new file mode 100644 index 0000000..6e8937a --- /dev/null +++ b/internal/integration/diagnosisreport_integration.go @@ -0,0 +1,363 @@ +package integration + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "net/http" + "os" + "satusehat-rssa/internal/constant" + "satusehat-rssa/internal/model" + "satusehat-rssa/pkg/httputil" + "strings" +) + +type DiagnosisReportInterface interface { + // Define methods for DiagnosisReportInterface + CreateDiagnosisReport(req model.DiagnosticReportRequest) (map[string]interface{}, error) + GetDiagnosisReportByPatient(id string) (map[string]interface{}, error) + GetDiagnosisReportByPatientCategory(id string, category string) (map[string]interface{}, error) + HandleCheckDiagnosisReport(id string) (string, bool, error) + HandleCheckDiagnosisReportCategory(id string, category string) ([]string, bool, error) + UpdateDiagnosisReport(req model.DiagnosticReportRequest) (map[string]interface{}, error) +} +type DiagnosisReportRepository struct { + akses *model.Akses +} + +// GetDiagnosisReportByPatientCategory implements DiagnosisReportInterface. +func (d *DiagnosisReportRepository) GetDiagnosisReportByPatientCategory(id string, category string) (map[string]interface{}, error) { + var data map[string]interface{} + + url := d.akses.BaseUrl + "/DiagnosticReport?subject=" + id + "&category=" + category + method := "GET" + + client := &http.Client{} + request, err := http.NewRequest(method, url, nil) + if err != nil { + return nil, err + } + oauth := model.OauthRequest{ + ClientId: d.akses.ClientId, + ClientSecret: d.akses.ClientSecret, + } + OauthInterface := NewOauthRequestRepo(d.akses) + token, err := OauthInterface.GenerateToken(oauth) + if err != nil { + return nil, err + } + if token != nil { + request.Header.Set("Authorization", "Bearer "+token.AccessToken) + } + request.Header.Set("Content-Type", constant.ContentTypeFHIRJSON) + res, err := client.Do(request) + if err != nil { + return nil, err + } + defer res.Body.Close() + err = json.NewDecoder(res.Body).Decode(&data) + if err != nil { + return nil, err + } + return data, nil +} + +// HandleCheckDiagnosisReportCategory implements DiagnosisReportInterface. +func (d *DiagnosisReportRepository) HandleCheckDiagnosisReportCategory(id string, category string) ([]string, bool, error) { + diagnosis, err := d.GetDiagnosisReportByPatientCategory(id, category) + if err != nil { + return nil, false, err + } + var ids []string + if entries, ok := diagnosis["entry"].([]interface{}); ok && len(entries) != 0 { + if entries, ok := (diagnosis)["entry"].([]interface{}); ok && len(entries) > 0 { + if entryMap, ok := entries[0].(map[string]interface{}); ok { + if resource, ok := entryMap["resource"].(map[string]interface{}); ok { + if id, ok := resource["id"].(string); ok { + //fmt.Println("resource.id:", id) + ids = append(ids, id) + return ids, true, nil + } + } + } + } + return nil, true, nil + } + + return nil, false, nil +} + +// CreateDiagnosisReport implements DiagnosisReportInterface. +func (d *DiagnosisReportRepository) CreateDiagnosisReport(req model.DiagnosticReportRequest) (map[string]interface{}, error) { + var data map[string]interface{} + req.ResourceType = constant.DiagnosticReportResourceType + req.Identifier = append(req.Identifier, model.Identifier{ + Use: "official", + System: "http://sys-ids.kemkes.go.id/diagnostic/" + os.Getenv("ORGANIZATION_ID") + "/lab", + Value: os.Getenv("ORGANIZATION_ID"), + }) + + patient, err := d.setupPatient(&req) + if err != nil { + return nil, err + } + + err = d.setupEncounter(&req, patient) + if err != nil { + return nil, err + } + + //Setup Performer + err = d.setupPractitioner(&req) + if err != nil { + return nil, err + } + err = d.setupOrganization(&req) + if err != nil { + return nil, err + } + + //Setup Result + err = d.setupObserver(&req, patient) + if err != nil { + return nil, err + } + + //Setup Specimen + err = d.setupSpecimen(&req, patient) + if err != nil { + return nil, err + } + + //Setup BasedOn + + url := d.akses.BaseUrl + "/DiagnosticReport" + method := "POST" + payload, err := json.Marshal(req) + if err != nil { + return nil, err + } + + client := &http.Client{} + request, err := http.NewRequest(method, url, bytes.NewBuffer(payload)) + if err != nil { + return nil, err + } + oauth := model.OauthRequest{ + ClientId: d.akses.ClientId, + ClientSecret: d.akses.ClientSecret, + } + OauthInterface := NewOauthRequestRepo(d.akses) + token, err := OauthInterface.GenerateToken(oauth) + if err != nil { + return nil, err + } + if token != nil { + request.Header.Add("Authorization", "Bearer "+token.AccessToken) + } else { + return nil, errors.New(constant.ErrGenerateToken) + } + + request.Header.Set("Content-Type", "application/json") + response, err := client.Do(request) + if err != nil { + return nil, err + } + defer response.Body.Close() + + err = json.NewDecoder(response.Body).Decode(&data) + if err != nil { + return nil, err + } + + return data, nil +} + +// CreateDiagnosisReport implements DiagnosisReportInterface. +func (d *DiagnosisReportRepository) UpdateDiagnosisReport(req model.DiagnosticReportRequest) (map[string]interface{}, error) { + patient, err := d.setupPatient(&req) + if err != nil { + return nil, err + } + + err = d.setupEncounter(&req, patient) + if err != nil { + return nil, err + } + + //Setup Performer + err = d.setupPractitioner(&req) + if err != nil { + return nil, err + } + err = d.setupOrganization(&req) + if err != nil { + return nil, err + } + + //Setup Result + err = d.setupObserver(&req, patient) + if err != nil { + return nil, err + } + + //Setup Specimen + err = d.setupSpecimen(&req, patient) + if err != nil { + return nil, err + } + + //Setup BasedOn + oauth := model.OauthRequest{ + ClientId: d.akses.ClientId, + ClientSecret: d.akses.ClientSecret, + } + token, err := NewOauthRequestRepo(d.akses).GenerateToken(oauth) + if err != nil { + return nil, err + } + if token == nil { + return nil, errors.New(constant.ErrGenerateToken) + } + + url := d.akses.BaseUrl + fmt.Sprintf("/DiagnosticReport/%s", req.Id) + return httputil.DoRequest(httputil.RequestOption{ + Method: "PUT", + URL: url, + Body: req, + BearerToken: token.AccessToken, + }) +} + +// setupPatient extracts patient setup logic from CreateDiagnosisReport. +func (d *DiagnosisReportRepository) setupPatient(req *model.DiagnosticReportRequest) (string, error) { + if req.Subject.Reference == "" { + return "", nil + } + patientInterface := NewPatientRepo(d.akses) + patient, err := patientInterface.HandleCheckPatient(req.Subject.Reference) + if err != nil { + return "", err + } + if patient != "" { + req.Subject.Reference = "Patient/" + patient + } + return patient, nil +} + +// setupEncounter extracts encounter setup logic from CreateDiagnosisReport. +func (d *DiagnosisReportRepository) setupEncounter(req *model.DiagnosticReportRequest, patient string) error { + if patient == "" { + return nil + } + encounterInterface := NewEncounterRepo(d.akses) + encounterId, encounterExist, err := encounterInterface.HandleCheckEncounter(patient) + if err != nil { + return err + } + if encounterExist { + req.Encounter.Reference = "Encounter/" + encounterId + } else { + //Buat dulu encounter + } + return nil +} + +func (d *DiagnosisReportRepository) setupSpecimen(req *model.DiagnosticReportRequest, patient string) error { + if patient == "" { + return nil + } + specimenInterface := NewSpecimenRepository(d.akses) + ids, exist, err := specimenInterface.HandleCheckSpecimen(patient) + if err != nil { + return err + } + if exist { + for i := range req.Specimen { + req.Specimen[i].Reference = "Specimen/" + ids[i] + } + } + return nil +} + +func (d *DiagnosisReportRepository) setupBasedOn(req *model.DiagnosticReportRequest, patient string) error { + if patient == "" { + return nil + } + basedOnInterface := NewServiceRequestRepository(d.akses) + ids, exist, err := basedOnInterface.HandleCheckServiceRequest(patient) + if err != nil { + return err + } + if exist { + for i := range req.BasedOn { + req.BasedOn[i].Reference = "ServiceRequest/" + ids[i] + } + } + return nil +} + +// GetDiagnosisReportByPatient implements DiagnosisReportInterface. +func (d *DiagnosisReportRepository) GetDiagnosisReportByPatient(id string) (map[string]interface{}, error) { + panic("unimplemented") +} + +// HandleCheckDiagnosisReport implements DiagnosisReportInterface. +func (d *DiagnosisReportRepository) HandleCheckDiagnosisReport(id string) (string, bool, error) { + panic("unimplemented") +} + +// setupPractitioner handles practitioner reference logic. +func (d *DiagnosisReportRepository) setupPractitioner(req *model.DiagnosticReportRequest) error { + if len(req.Performer) == 0 { + return nil + } + + for _, performer := range req.Performer { + if strings.Contains(performer.Reference, "Practitioner/") { + temp := strings.Split(performer.Reference, "/") + practicionerInterface := NewPracticionerRepo(d.akses) + ref, _, err := practicionerInterface.HandleCheckPartitioner(temp[1]) + if err != nil { + return err + } + if ref != "" { + performer.Reference = "Practitioner/" + ref + } + } + } + return nil +} + +func (d *DiagnosisReportRepository) setupOrganization(req *model.DiagnosticReportRequest) error { + if len(req.Performer) == 0 { + return nil + } + + for _, performer := range req.Performer { + if strings.Contains(performer.Reference, "Organization/") { + performer.Reference = "Organization/" + os.Getenv("ORGANIZATION_ID") + } + } + return nil +} + +func (d *DiagnosisReportRepository) setupObserver(req *model.DiagnosticReportRequest, patient string) error { + observerInterface := NewObservationRepo(d.akses) + //temp := strings.Split(req.Subject.Reference, "/") + ids, valid, err := observerInterface.HandleCheckObservation(patient) + if err != nil { + return err + } + if len(ids) > 0 && valid { + for _, id := range ids { + req.Result = append(req.Result, model.Reference{Reference: "Observation/" + id}) + } + } + return nil +} + +func NewDiagnosisReportRepo(akses *model.Akses) DiagnosisReportInterface { + return &DiagnosisReportRepository{akses: akses} +} diff --git a/internal/integration/encounter_integration.go b/internal/integration/encounter_integration.go new file mode 100644 index 0000000..7789f43 --- /dev/null +++ b/internal/integration/encounter_integration.go @@ -0,0 +1,183 @@ +package integration + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "net/http" + "os" + "satusehat-rssa/internal/constant" + "satusehat-rssa/internal/model" + "satusehat-rssa/pkg/httputil" +) + +type EncounterInterface interface { + // Define methods for encounter integration + CreateEncounter(req model.EncounterRequest) (map[string]interface{}, error) + GetEncounterByPatient(id string) (map[string]interface{}, error) + HandleCheckEncounter(patientID string) (string, bool, error) + UpdateEncounter(req model.EncounterUpdateRequest) (map[string]interface{}, error) +} + +type EncounterRepository struct { + akses *model.Akses +} + +// HandleCheckEncounter implements EncounterInterface. +func (e *EncounterRepository) HandleCheckEncounter(patientID string) (string, bool, error) { + encounter, err := e.GetEncounterByPatient(patientID) + if err != nil { + return "", false, err + } + + // You can process 'encounter' here if needed + if entries, ok := encounter["entry"].([]interface{}); ok && len(entries) != 0 { + if entries, ok := (encounter)["entry"].([]interface{}); ok && len(entries) > 0 { + if entryMap, ok := entries[0].(map[string]interface{}); ok { + if resource, ok := entryMap["resource"].(map[string]interface{}); ok { + if id, ok := resource["id"].(string); ok { + //fmt.Println("resource.id:", id) + return id, true, nil + } + } + } + } + return "", true, nil + } + + return "", false, nil +} + +// GetEncounterByPatient implements EncounterInterface. +func (e *EncounterRepository) GetEncounterByPatient(id string) (map[string]interface{}, error) { + var ( + data *map[string]interface{} + ) + + url := e.akses.BaseUrl + "/Encounter?subject=" + id + method := "GET" + + client := &http.Client{} + request, err := http.NewRequest(method, url, nil) + if err != nil { + return nil, err + } + oauth := model.OauthRequest{ + ClientId: e.akses.ClientId, + ClientSecret: e.akses.ClientSecret, + } + OauthInterface := NewOauthRequestRepo(e.akses) + token, err := OauthInterface.GenerateToken(oauth) + if err != nil { + return nil, err + } + if token != nil { + request.Header.Add("Authorization", "Bearer "+token.AccessToken) + } else { + return nil, errors.New(constant.ErrGenerateToken) + } + request.Header.Set("Content-Type", constant.ContentTypeFHIRJSON) + res, err := client.Do(request) + if err != nil { + return nil, err + } + defer res.Body.Close() + + if res.StatusCode != http.StatusOK { + return nil, errors.New("failed to get encounter by patient: " + res.Status) + } + err = json.NewDecoder(res.Body).Decode(&data) + if err != nil { + return nil, err + } + if data == nil { + return nil, errors.New("no encounter found for patient") + } + return *data, nil +} + +func NewEncounterRepo(akses *model.Akses) EncounterInterface { + return &EncounterRepository{akses: akses} +} + +// CreateEncounter implements EncounterInterface. +func (e *EncounterRepository) CreateEncounter(req model.EncounterRequest) (map[string]interface{}, error) { + var ( + data *map[string]interface{} + ) + req.ResourceType = constant.EncounterResourceType + req.Identifier = append(req.Identifier, model.IdentifierEncounter{ + System: "http://sys-ids.kemkes.go.id/encounter/" + os.Getenv("ORGANIZATION_ID"), + Value: os.Getenv("ORGANIZATION_ID"), + }) + + req.ServiceProvider.Reference = "Organization/" + os.Getenv("ORGANIZATION_ID") + + url := e.akses.BaseUrl + "/Encounter" + method := "POST" + + payload, err := json.Marshal(req) + if err != nil { + return nil, err + } + client := &http.Client{} + request, err := http.NewRequest(method, url, bytes.NewBuffer(payload)) + if err != nil { + return nil, err + } + oauth := model.OauthRequest{ + ClientId: e.akses.ClientId, + ClientSecret: e.akses.ClientSecret, + } + OauthInterface := NewOauthRequestRepo(e.akses) + token, err := OauthInterface.GenerateToken(oauth) + if err != nil { + return nil, err + } + if token != nil { + request.Header.Add("Authorization", "Bearer "+token.AccessToken) + } else { + return nil, errors.New(constant.ErrGenerateToken) + } + + request.Header.Set("Content-Type", constant.ContentTypeFHIRJSON) + request.Header.Set("Accept", constant.ContentTypeFHIRJSON) + + res, err := client.Do(request) + if err != nil { + return nil, err + } + defer res.Body.Close() + + err = json.NewDecoder(res.Body).Decode(&data) + if err != nil { + return nil, err + } + + return *data, nil +} + +// UpdateEncounter implements EncounterInterface. +func (e *EncounterRepository) UpdateEncounter(req model.EncounterUpdateRequest) (map[string]interface{}, error) { + oauth := model.OauthRequest{ + ClientId: e.akses.ClientId, + ClientSecret: e.akses.ClientSecret, + } + token, err := NewOauthRequestRepo(e.akses).GenerateToken(oauth) + if err != nil { + return nil, err + } + if token == nil { + return nil, errors.New(constant.ErrGenerateToken) + } + + url := e.akses.BaseUrl + fmt.Sprintf("/Encounter/%s", req.ID) + return httputil.DoRequest(httputil.RequestOption{ + Method: "PUT", + URL: url, + Body: req, + BearerToken: token.AccessToken, + Headers: httputil.DefaultFHIRHeaders(), + }) +} diff --git a/internal/integration/episodeofcare_integration.go b/internal/integration/episodeofcare_integration.go new file mode 100644 index 0000000..d9729c9 --- /dev/null +++ b/internal/integration/episodeofcare_integration.go @@ -0,0 +1,353 @@ +package integration + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "net/http" + "os" + "satusehat-rssa/internal/constant" + "satusehat-rssa/internal/model" + "satusehat-rssa/pkg/httputil" +) + +type EpisodeOfCareInterface interface { + // Define methods for EpisodeOfCareInterface + CreateEpisodeOfCare(req model.EpisodeOfCareRequest) (map[string]interface{}, error) + GetEpisodeOfCareByPatient(id string) (map[string]interface{}, error) + HandleCheckEpisodeOfCare(id string) ([]string, bool, error) + UpdateEpisodeOfCare(req model.EpisodeOfCareRequest) (map[string]interface{}, error) +} + +type EpisodeOfCareRepository struct { + akses *model.Akses +} + +// CreateEpisodeOfCare implements EpisodeOfCareInterface. +func (e *EpisodeOfCareRepository) CreateEpisodeOfCare(req model.EpisodeOfCareRequest) (map[string]interface{}, error) { + var data map[string]interface{} + req.ResourceType = constant.EpisodeOfCareResourceType + req.Identifier = []model.Identifier{ + { + System: "http://sys-ids.kemkes.go.id/episode-of-care/" + os.Getenv("ORGANIZATION_ID"), + Value: os.Getenv("ORGANIZATION_ID"), + }, + } + + // Setup Patient + var patient string + if req.Patient.Reference != "" { + patientInterface := NewPatientRepo(e.akses) + var err error + patient, err = patientInterface.HandleCheckPatient(req.Patient.Reference) + if err != nil { + return nil, err + } + if patient != "" { + req.Patient.Reference = "Patient/" + patient + } + } + + // Setup Organization + req.ManagingOrganization.Reference = "Organization/" + os.Getenv("ORGANIZATION_ID") + req.ManagingOrganization.Display = os.Getenv("ORGANIZATION_NAME") + + var conditionReference model.Reference + condition := e.setupCondition(patient) + if condition != "" { + conditionReference = model.Reference{ + Reference: "Condition/" + condition, + } + } else { + return nil, errors.New("condition not found for the patient") + } + req.Diagnosis = append(req.Diagnosis, model.Diagnosis{ + Condition: conditionReference, + }) + + // Setup CareManager + // if req.CareManager.Reference != "" { + // careManagerInterface := NewPracticionerRepo(e.akses) + // careManagerId, _, err := careManagerInterface.HandleCheckPartitioner(req.CareManager.Reference) + // if err != nil { + // return nil, err + // } + // req.CareManager.Reference = "Practitioner/" + careManagerId + // } + + // Setup Type + url := e.akses.BaseUrl + "/EpisodeOfCare" + method := "POST" + jsonData, err := json.Marshal(req) + if err != nil { + return nil, err + } + + client := &http.Client{} + request, err := http.NewRequest(method, url, bytes.NewBuffer(jsonData)) + if err != nil { + return nil, err + } + oauth := model.OauthRequest{ + ClientId: e.akses.ClientId, + ClientSecret: e.akses.ClientSecret, + } + OauthInterface := NewOauthRequestRepo(e.akses) + token, err := OauthInterface.GenerateToken(oauth) + if err != nil { + return nil, err + } + request.Header.Set("Authorization", "Bearer "+token.AccessToken) + request.Header.Set("Content-Type", "application/json") + response, err := client.Do(request) + if err != nil { + return nil, err + + } + defer response.Body.Close() + + if err := json.NewDecoder(response.Body).Decode(&data); err != nil { + return nil, err + } + + if !hasOperationOutcomeIssue(data) { + // Setup Encounter + if patient != "" { + encounterInterface := NewEncounterRepo(e.akses) + encounterId, encounterExist, err := encounterInterface.HandleCheckEncounter(patient) + if err != nil { + return nil, err + } + + if encounterExist { + data["encounter"] = map[string]interface{}{ + "reference": "Encounter/" + encounterId, + } + } else { + return nil, errors.New("encounter not found") + } + } + } + + return data, nil +} + +// GetEpisodeOfCareByPatient implements EpisodeOfCareInterface. +func (e *EpisodeOfCareRepository) GetEpisodeOfCareByPatient(id string) (map[string]interface{}, error) { + var data map[string]interface{} + + // Validate the patient ID + if id == "" { + return nil, errors.New("patient ID cannot be empty") + } + + // Construct the URL with query parameters + url := e.akses.BaseUrl + "/EpisodeOfCare?patient=" + id + method := "GET" + + client := &http.Client{} + request, err := http.NewRequest(method, url, nil) + if err != nil { + return nil, err + } + + oauth := model.OauthRequest{ + ClientId: e.akses.ClientId, + ClientSecret: e.akses.ClientSecret, + } + OauthInterface := NewOauthRequestRepo(e.akses) + token, err := OauthInterface.GenerateToken(oauth) + if err != nil { + return nil, err + } + request.Header.Set("Authorization", "Bearer "+token.AccessToken) + + response, err := client.Do(request) + if err != nil { + return nil, err + } + defer response.Body.Close() + + if err := json.NewDecoder(response.Body).Decode(&data); err != nil { + return nil, err + } + + return data, nil +} + +// HandleCheckEpisodeOfCare implements EpisodeOfCareInterface. +func (e *EpisodeOfCareRepository) HandleCheckEpisodeOfCare(id string) ([]string, bool, error) { + eoc, err := e.GetEpisodeOfCareByPatient(id) + if err != nil { + return nil, false, err + } + + // You can process 'eoc' here if needed + var ids []string + if entries, ok := eoc["entry"].([]interface{}); ok && len(entries) != 0 { + if entries, ok := (eoc)["entry"].([]interface{}); ok && len(entries) > 0 { + if entryMap, ok := entries[0].(map[string]interface{}); ok { + if resource, ok := entryMap["resource"].(map[string]interface{}); ok { + if id, ok := resource["id"].(string); ok { + //fmt.Println("resource.id:", id) + ids = append(ids, id) + return ids, true, nil + } + } + } + } + return nil, true, nil + } + + return nil, false, nil +} + +func (e *EpisodeOfCareRepository) UpdateEpisodeOfCare(req model.EpisodeOfCareRequest) (map[string]interface{}, error) { + req.ResourceType = constant.EpisodeOfCareResourceType + + // Setup Patient + var patient string + if req.Patient.Reference != "" { + patientInterface := NewPatientRepo(e.akses) + var err error + patient, err = patientInterface.HandleCheckPatient(req.Patient.Reference) + if err != nil { + return nil, err + } + if patient != "" { + req.Patient.Reference = "Patient/" + patient + } + } + + // Setup Organization + if req.ManagingOrganization.Reference != "" { + orgInterface := NewOrganizationRepo(e.akses) + orgName, _, err := orgInterface.HandleCheckOrganization(req.ManagingOrganization.Reference) + if err != nil { + return nil, err + } + req.ManagingOrganization.Reference = "Organization/" + req.ManagingOrganization.Reference + req.ManagingOrganization.Display = orgName + } + + // Get id existing EpisodeOfCare + data, err := e.setupPutEpisodeOfCare(&req, patient) + if err != nil { + return nil, err + } + + if len(data["entry"].([]interface{})) == 0 { + return nil, errors.New("episode of care not found") + } + + // Setup CareManager + // if req.CareManager.Reference != "" { + // careManagerInterface := NewPracticionerRepo(e.akses) + // careManagerId, _, err := careManagerInterface.HandleCheckPartitioner(req.CareManager.Reference) + // if err != nil { + // return nil, err + // } + // req.CareManager.Reference = "Practitioner/" + careManagerId + // } + + // Setup Type + oauth := model.OauthRequest{ + ClientId: e.akses.ClientId, + ClientSecret: e.akses.ClientSecret, + } + token, err := NewOauthRequestRepo(e.akses).GenerateToken(oauth) + if err != nil { + return nil, err + } + if token == nil { + return nil, errors.New(constant.ErrGenerateToken) + } + + url := e.akses.BaseUrl + fmt.Sprintf("/EpisodeOfCare/%s", req.Id) + return httputil.DoRequest(httputil.RequestOption{ + Method: "PUT", + URL: url, + Body: req, + BearerToken: token.AccessToken, + }) +} +func (e *EpisodeOfCareRepository) setupCondition(patient string) string { + if patient == "" { + return "" + } + + conditionInterface := NewConditionRepo(e.akses) + conditionId, conditionExist, err := conditionInterface.HandleCheckCondition(patient) + if err != nil { + return "" + } + if conditionExist { + return conditionId + } + return "" +} + +func (e *EpisodeOfCareRepository) setupPutEpisodeOfCare(req *model.EpisodeOfCareRequest, patient string) (map[string]interface{}, error) { + data, err := e.GetEpisodeOfCareByPatient(patient) + if err != nil { + return nil, err + } + + if entries, ok := data["entry"].([]interface{}); ok && len(entries) != 0 { + if entries, ok := (data)["entry"].([]interface{}); ok && len(entries) > 0 { + if entryMap, ok := entries[0].(map[string]interface{}); ok { + if resource, ok := entryMap["resource"].(map[string]interface{}); ok { + if id, ok := resource["id"].(string); ok { + req.Id = id + } + } + } + } + } + + return data, nil +} + +// hasOperationOutcomeIssue returns true when the response is an OperationOutcome +// with at least one issue that includes diagnostics or details.text. +func hasOperationOutcomeIssue(resp map[string]interface{}) bool { + if resp == nil { + return false + } + + rt, ok := resp["resourceType"].(string) + if !ok || rt != "OperationOutcome" { + return false + } + + issues, ok := resp["issue"].([]interface{}) + if !ok || len(issues) == 0 { + return false + } + + for _, it := range issues { + m, ok := it.(map[string]interface{}) + if !ok { + continue + } + + if diag, _ := m["diagnostics"].(string); diag != "" { + return true + } + + if details, ok := m["details"].(map[string]interface{}); ok { + if txt, _ := details["text"].(string); txt != "" { + return true + } + } + } + + return false +} + +func NewEpisodeOfCareRepo(akses *model.Akses) EpisodeOfCareInterface { + return &EpisodeOfCareRepository{ + akses: akses, + } +} diff --git a/internal/integration/goal_integration.go b/internal/integration/goal_integration.go new file mode 100644 index 0000000..94322f5 --- /dev/null +++ b/internal/integration/goal_integration.go @@ -0,0 +1,221 @@ +package integration + +import ( + "encoding/json" + "errors" + "fmt" + "net/http" + "satusehat-rssa/internal/constant" + "satusehat-rssa/internal/model" + "satusehat-rssa/pkg/httputil" +) + +type GoalInterface interface { + // Define methods for GoalInterface + CreateGoal(req model.GoalRequest) (map[string]interface{}, error) + UpdateGoal(req model.GoalRequest) (map[string]interface{}, error) + GetGoalByPatient(id string) (map[string]interface{}, error) + HandleCheckGoal(id string) ([]string, bool, error) +} + +type GoalRepository struct { + // Define fields for GoalRepository + akses *model.Akses +} + +// GetGoalByPatient implements GoalInterface. +func (c *GoalRepository) GetGoalByPatient(id string) (map[string]interface{}, error) { + var data map[string]interface{} + + url := c.akses.BaseUrl + "/Goal?subject=" + id + method := "GET" + + client := &http.Client{} + request, err := http.NewRequest(method, url, nil) + if err != nil { + return nil, err + } + oauth := model.OauthRequest{ + ClientId: c.akses.ClientId, + ClientSecret: c.akses.ClientSecret, + } + token, err := NewOauthRequestRepo(c.akses).GenerateToken(oauth) + if err != nil { + return nil, err + } + if token == nil { + return nil, errors.New(constant.ErrGenerateToken) + } + request.Header.Add("Authorization", "Bearer "+token.AccessToken) + request.Header.Set("Content-Type", constant.ContentTypeFHIRJSON) + request.Header.Set("Accept", constant.ContentTypeFHIRJSON) + res, err := client.Do(request) + if err != nil { + return nil, err + } + defer res.Body.Close() + + err = json.NewDecoder(res.Body).Decode(&data) + if err != nil { + return nil, err + } + return data, nil +} + +// HandleCheckGoal implements GoalInterface. +func (c *GoalRepository) HandleCheckGoal(id string) ([]string, bool, error) { + goal, err := c.GetGoalByPatient(id) + if err != nil { + return nil, false, err + } + var ids []string + if entries, ok := goal["entry"].([]interface{}); ok && len(entries) != 0 { + if entries, ok := (goal)["entry"].([]interface{}); ok && len(entries) > 0 { + if entryMap, ok := entries[0].(map[string]interface{}); ok { + if resource, ok := entryMap["resource"].(map[string]interface{}); ok { + if id, ok := resource["id"].(string); ok { + //fmt.Println("resource.id:", id) + ids = append(ids, id) + return ids, true, nil + } + } + } + } + return nil, true, nil + } + + return nil, false, nil +} + +func NewGoalRepo(akses *model.Akses) GoalInterface { + return &GoalRepository{ + akses: akses, + } +} + +// CreateGoal implements GoalInterface. +func (c *GoalRepository) CreateGoal(req model.GoalRequest) (map[string]interface{}, error) { + req.ResourceType = constant.GoalResourceType + + patient, err := c.setupPatient(&req) + if err != nil { + return nil, err + } + + // err = c.setupPractitioner(&req) + // if err != nil { + // return nil, err + // } + + oauth := model.OauthRequest{ + ClientId: c.akses.ClientId, + ClientSecret: c.akses.ClientSecret, + } + token, err := NewOauthRequestRepo(c.akses).GenerateToken(oauth) + if err != nil { + return nil, err + } + if token == nil { + return nil, errors.New(constant.ErrGenerateToken) + } + + url := c.akses.BaseUrl + "/Goal" + + data, err := httputil.DoRequest(httputil.RequestOption{ + Method: "POST", + URL: url, + Body: req, + BearerToken: token.AccessToken, + Headers: httputil.DefaultFHIRHeaders(), + }) + if err != nil { + return nil, err + } + if !isOperationOutcomeError(data) { + // Setup Encounter + if patient != "" { + encounterInterface := NewEncounterRepo(c.akses) + encounterId, encounterExist, err := encounterInterface.HandleCheckEncounter(patient) + if err != nil { + return nil, err + } + + if encounterExist { + data["encounter"] = map[string]interface{}{ + "reference": "Encounter/" + encounterId, + } + } else { + return nil, errors.New("encounter not found") + } + } + } + return data, nil +} + +func (c *GoalRepository) UpdateGoal(req model.GoalRequest) (map[string]interface{}, error) { + req.ResourceType = constant.GoalResourceType + + _, err := c.setupPatient(&req) + if err != nil { + return nil, err + } + + // err = c.setupPractitioner(&req) + // if err != nil { + // return nil, err + // } + + oauth := model.OauthRequest{ + ClientId: c.akses.ClientId, + ClientSecret: c.akses.ClientSecret, + } + token, err := NewOauthRequestRepo(c.akses).GenerateToken(oauth) + if err != nil { + return nil, err + } + if token == nil { + return nil, errors.New(constant.ErrGenerateToken) + } + + url := c.akses.BaseUrl + fmt.Sprintf("/Goal/%s", req.Id) + return httputil.DoRequest(httputil.RequestOption{ + Method: "PUT", + URL: url, + Body: req, + BearerToken: token.AccessToken, + Headers: httputil.DefaultFHIRHeaders(), + }) +} + +func (c *GoalRepository) setupPatient(req *model.GoalRequest) (string, error) { + if req.Subject.Reference == "" { + return "", nil + } + patientInterface := NewPatientRepo(c.akses) + patient, err := patientInterface.HandleCheckPatient(req.Subject.Reference) + if err != nil { + return "", err + } + + if patient != "" { + req.Subject.Reference = "Patient/" + patient + } + return patient, nil +} + +// setupPractitioner handles practitioner reference logic. +func (c *GoalRepository) setupPractitioner(req *model.GoalRequest) error { + if req.ExpressedBy.Reference == "" { + return nil + } + practicionerInterface := NewPracticionerRepo(c.akses) + ref, display, err := practicionerInterface.HandleCheckPartitioner(req.ExpressedBy.Reference) + if err != nil { + return err + } + if ref != "" { + req.ExpressedBy.Reference = "Practitioner/" + ref + req.ExpressedBy.Display = display + } + return nil +} diff --git a/internal/integration/imagingstudy_integration.go b/internal/integration/imagingstudy_integration.go new file mode 100644 index 0000000..a198c4c --- /dev/null +++ b/internal/integration/imagingstudy_integration.go @@ -0,0 +1,284 @@ +package integration + +import ( + "encoding/json" + "errors" + "fmt" + "net/http" + "os" + "satusehat-rssa/internal/constant" + "satusehat-rssa/internal/model" + "satusehat-rssa/pkg/httputil" +) + +type ImagingStudyInterface interface { + // Define methods for ImagingStudyInterface + CreateImagingStudy(req model.ImagingStudyRequest) (map[string]interface{}, error) + UpdateImagingStudy(req model.ImagingStudyRequest) (map[string]interface{}, error) + GetImagingStudyByPatient(id string) (map[string]interface{}, error) + HandleCheckImagingStudy(id string) ([]string, bool, error) +} + +type ImagingStudyRepository struct { + // Define fields for ImagingStudyRepository + akses *model.Akses +} + +func NewImagingStudyRepo(akses *model.Akses) ImagingStudyInterface { + return &ImagingStudyRepository{ + akses: akses, + } +} + +// GetImagingStudyByPatient implements ImagingStudyInterface. +func (c *ImagingStudyRepository) GetImagingStudyByPatient(id string) (map[string]interface{}, error) { + var data map[string]interface{} + + url := c.akses.BaseUrl + "/ImagingStudy?subject=" + id + method := "GET" + + client := &http.Client{} + request, err := http.NewRequest(method, url, nil) + if err != nil { + return nil, err + } + oauth := model.OauthRequest{ + ClientId: c.akses.ClientId, + ClientSecret: c.akses.ClientSecret, + } + token, err := NewOauthRequestRepo(c.akses).GenerateToken(oauth) + if err != nil { + return nil, err + } + if token == nil { + return nil, errors.New(constant.ErrGenerateToken) + } + request.Header.Add("Authorization", "Bearer "+token.AccessToken) + request.Header.Set("Content-Type", constant.ContentTypeFHIRJSON) + request.Header.Set("Accept", constant.ContentTypeFHIRJSON) + res, err := client.Do(request) + if err != nil { + return nil, err + } + defer res.Body.Close() + + err = json.NewDecoder(res.Body).Decode(&data) + if err != nil { + return nil, err + } + return data, nil + +} + +// HandleCheckImagingStudy implements ImagingStudyInterface. +func (c *ImagingStudyRepository) HandleCheckImagingStudy(id string) ([]string, bool, error) { + imaging, err := c.GetImagingStudyByPatient(id) + if err != nil { + return nil, false, err + } + var ids []string + if entries, ok := imaging["entry"].([]interface{}); ok && len(entries) != 0 { + if entries, ok := (imaging)["entry"].([]interface{}); ok && len(entries) > 0 { + if entryMap, ok := entries[0].(map[string]interface{}); ok { + if resource, ok := entryMap["resource"].(map[string]interface{}); ok { + if id, ok := resource["id"].(string); ok { + //fmt.Println("resource.id:", id) + ids = append(ids, id) + return ids, true, nil + } + } + } + } + return nil, true, nil + } + + return nil, false, nil +} + +// CreateImagingStudy implements ImagingStudyInterface. +func (c *ImagingStudyRepository) CreateImagingStudy(req model.ImagingStudyRequest) (map[string]interface{}, error) { + req.ResourceType = constant.ImagingStudyResourceType + + if len(req.Identifier) > 0 { + identifier := []model.Identifier{} + for k, i := range req.Identifier { + if k == 0 { + identifier = append(identifier, model.Identifier{ + Use: "official", + System: "http://sys-ids.kemkes.go.id/acsn/" + os.Getenv("ORGANIZATION_ID"), // Set this if needed, or remove if not present in IdentifierObject + Value: i.Value, + Type: &model.CodeableConcept{ + Coding: []model.Coding{ + { + System: "http://terminology.hl7.org/CodeSystem/v2-0203", + Code: "ACSN", + }, + }, + }, + }) + } + + // if k == 1 { + // identifier = append(identifier, model.Identifier{ + // System: "urn:dicom:uid", + // Value: i.Value, + // }) + // } + } + req.Identifier = identifier + } + + patient, err := c.setupPatient(&req) + if err != nil { + return nil, err + } + + // Setup Service Request + err = c.setupServiceRequest(&req, patient) + if err != nil { + return nil, err + } + + oauth := model.OauthRequest{ + ClientId: c.akses.ClientId, + ClientSecret: c.akses.ClientSecret, + } + token, err := NewOauthRequestRepo(c.akses).GenerateToken(oauth) + if err != nil { + return nil, err + } + if token == nil { + return nil, errors.New(constant.ErrGenerateToken) + } + + url := c.akses.BaseUrl + "/ImagingStudy" + data, err := httputil.DoRequest(httputil.RequestOption{ + Method: "POST", + URL: url, + Body: req, + BearerToken: token.AccessToken, + Headers: httputil.DefaultFHIRHeaders(), + }) + if err != nil { + return nil, err + } + if !isOperationOutcomeError(data) { + // Setup Encounter + if patient != "" { + encounterInterface := NewEncounterRepo(c.akses) + encounterId, encounterExist, err := encounterInterface.HandleCheckEncounter(patient) + if err != nil { + return nil, err + } + + if encounterExist { + data["encounter"] = map[string]interface{}{ + "reference": "Encounter/" + encounterId, + } + } else { + return nil, errors.New("encounter not found") + } + } + } + return data, nil +} + +func (c *ImagingStudyRepository) UpdateImagingStudy(req model.ImagingStudyRequest) (map[string]interface{}, error) { + req.ResourceType = constant.ImagingStudyResourceType + + if len(req.Identifier) > 0 { + identifier := []model.Identifier{} + for k, i := range req.Identifier { + if k == 0 { + identifier = append(identifier, model.Identifier{ + Use: "official", + System: "http://sys-ids.kemkes.go.id/acsn/" + os.Getenv("ORGANIZATION_ID"), // Set this if needed, or remove if not present in IdentifierObject + Value: i.Value, + Type: &model.CodeableConcept{ + Coding: []model.Coding{ + { + System: "http://terminology.hl7.org/CodeSystem/v2-0203", + Code: "ACSN", + }, + }, + }, + }) + } + + // if k == 1 { + // identifier = append(identifier, model.Identifier{ + // System: "urn:dicom:uid", + // Value: i.Value, + // }) + // } + } + req.Identifier = identifier + } + + patient, err := c.setupPatient(&req) + if err != nil { + return nil, err + } + + // Setup Service Request + err = c.setupServiceRequest(&req, patient) + if err != nil { + return nil, err + } + + oauth := model.OauthRequest{ + ClientId: c.akses.ClientId, + ClientSecret: c.akses.ClientSecret, + } + token, err := NewOauthRequestRepo(c.akses).GenerateToken(oauth) + if err != nil { + return nil, err + } + if token == nil { + return nil, errors.New(constant.ErrGenerateToken) + } + + url := c.akses.BaseUrl + fmt.Sprintf("/ImagingStudy/%s", req.Id) + return httputil.DoRequest(httputil.RequestOption{ + Method: "PUT", + URL: url, + Body: req, + BearerToken: token.AccessToken, + Headers: httputil.DefaultFHIRHeaders(), + }) +} + +func (c *ImagingStudyRepository) setupPatient(req *model.ImagingStudyRequest) (string, error) { + if req.Subject.Reference == "" { + return "", nil + } + patientInterface := NewPatientRepo(c.akses) + patient, err := patientInterface.HandleCheckPatient(req.Subject.Reference) + if err != nil { + return "", err + } + + if patient != "" { + req.Subject.Reference = "Patient/" + patient + } + return patient, nil +} + +func (c *ImagingStudyRepository) setupServiceRequest(req *model.ImagingStudyRequest, patient string) error { + if patient == "" { + return nil + } + serviceReq := NewServiceRequestRepository(c.akses) + ids, _, err := serviceReq.HandleCheckServiceRequest(patient) + if err != nil { + return err + } + if len(ids) > 0 { + req.BasedOn = []model.Reference{ + { + Reference: "ServiceRequest/" + ids[0], + }, + } + } + return nil +} diff --git a/internal/integration/immunization_integration.go b/internal/integration/immunization_integration.go new file mode 100644 index 0000000..8d0fdd7 --- /dev/null +++ b/internal/integration/immunization_integration.go @@ -0,0 +1,130 @@ +package integration + +import ( + "errors" + "fmt" + "satusehat-rssa/internal/constant" + "satusehat-rssa/internal/model" + "satusehat-rssa/pkg/httputil" +) + +type ImmunizationInterface interface { + // Define methods for ImmunizationInterface + CreateImmunization(req model.ImmunizationRequest) (map[string]interface{}, error) + UpdateImmunization(req model.ImmunizationRequest) (map[string]interface{}, error) +} + +type ImmunizationRepository struct { + // Define fields for ImmunizationRepository + akses *model.Akses +} + +func NewImmunizationRepo(akses *model.Akses) ImmunizationInterface { + return &ImmunizationRepository{ + akses: akses, + } +} + +// CreateImmunization implements ImmunizationInterface. +func (c *ImmunizationRepository) CreateImmunization(req model.ImmunizationRequest) (map[string]interface{}, error) { + req.ResourceType = constant.ImmunizationResourceType + //setup primarySource default true karena vaksin bersumber dari orang yg melakukan vaksin + req.PrimarySource = true + + patient, err := c.setupPatient(&req) + if err != nil { + return nil, err + } + + err = c.setupEncounter(&req, patient) + if err != nil { + return nil, err + } + + oauth := model.OauthRequest{ + ClientId: c.akses.ClientId, + ClientSecret: c.akses.ClientSecret, + } + token, err := NewOauthRequestRepo(c.akses).GenerateToken(oauth) + if err != nil { + return nil, err + } + if token == nil { + return nil, errors.New(constant.ErrGenerateToken) + } + + url := c.akses.BaseUrl + "/Immunization" + return httputil.DoRequest(httputil.RequestOption{ + Method: "POST", + URL: url, + Body: req, + BearerToken: token.AccessToken, + Headers: httputil.DefaultFHIRHeaders(), + }) +} + +func (c *ImmunizationRepository) UpdateImmunization(req model.ImmunizationRequest) (map[string]interface{}, error) { + req.ResourceType = constant.ImmunizationResourceType + + patient, err := c.setupPatient(&req) + if err != nil { + return nil, err + } + + err = c.setupEncounter(&req, patient) + if err != nil { + return nil, err + } + + oauth := model.OauthRequest{ + ClientId: c.akses.ClientId, + ClientSecret: c.akses.ClientSecret, + } + token, err := NewOauthRequestRepo(c.akses).GenerateToken(oauth) + if err != nil { + return nil, err + } + if token == nil { + return nil, errors.New(constant.ErrGenerateToken) + } + + url := c.akses.BaseUrl + fmt.Sprintf("/Immunization/%s", req.Id) + return httputil.DoRequest(httputil.RequestOption{ + Method: "PUT", + URL: url, + Body: req, + BearerToken: token.AccessToken, + Headers: httputil.DefaultFHIRHeaders(), + }) +} + +func (c *ImmunizationRepository) setupPatient(req *model.ImmunizationRequest) (string, error) { + if req.Patient.Reference == "" { + return "", nil + } + patientInterface := NewPatientRepo(c.akses) + patient, err := patientInterface.HandleCheckPatient(req.Patient.Reference) + if err != nil { + return "", err + } + + if patient != "" { + req.Patient.Reference = "Patient/" + patient + } + return patient, nil +} + +func (c *ImmunizationRepository) setupEncounter(req *model.ImmunizationRequest, patient string) error { + if patient == "" { + return nil + } + encounterInterface := NewEncounterRepo(c.akses) + encounterId, encounterExist, err := encounterInterface.HandleCheckEncounter(patient) + if err != nil { + return err + } + if encounterExist { + req.Encounter.Reference = "Encounter/" + encounterId + } + return nil +} diff --git a/internal/integration/medicationdispense_integration.go b/internal/integration/medicationdispense_integration.go new file mode 100644 index 0000000..04dde64 --- /dev/null +++ b/internal/integration/medicationdispense_integration.go @@ -0,0 +1,240 @@ +package integration + +import ( + "encoding/json" + "errors" + "fmt" + "net/http" + "satusehat-rssa/internal/constant" + "satusehat-rssa/internal/model" + "satusehat-rssa/pkg/common" + "satusehat-rssa/pkg/httputil" + "strings" +) + +type MedicationDispenseInterface interface { + // Define methods for MedicationDispenseInterface + CreateMedicationDispense(req model.MedicationDispenseRequest) (map[string]interface{}, error) + UpdateMedicationDispense(req model.MedicationDispenseRequest) (map[string]interface{}, error) + GetMedicationDispenseByPatient(id string, category string) (map[string]interface{}, error) + HandleCheckMedicationDispense(id string, category string) ([]string, bool, error) +} + +type MedicationDispenseRepository struct { + // Define fields for MedicationDispenseRepository + akses *model.Akses +} + +func NewMedicationDispenseRepo(akses *model.Akses) MedicationDispenseInterface { + return &MedicationDispenseRepository{ + akses: akses, + } +} + +// GetMedicationDispenseByPatient implements MedicationDispenseInterface. +func (c *MedicationDispenseRepository) GetMedicationDispenseByPatient(id string, category string) (map[string]interface{}, error) { + var data map[string]interface{} + + url := c.akses.BaseUrl + "/MedicationDispense?subject=" + id + method := "GET" + + client := &http.Client{} + request, err := http.NewRequest(method, url, nil) + if err != nil { + return nil, err + } + oauth := model.OauthRequest{ + ClientId: c.akses.ClientId, + ClientSecret: c.akses.ClientSecret, + } + token, err := NewOauthRequestRepo(c.akses).GenerateToken(oauth) + if err != nil { + return nil, err + } + if token == nil { + return nil, errors.New(constant.ErrGenerateToken) + } + request.Header.Add("Authorization", "Bearer "+token.AccessToken) + request.Header.Set("Content-Type", constant.ContentTypeFHIRJSON) + request.Header.Set("Accept", constant.ContentTypeFHIRJSON) + res, err := client.Do(request) + + if err != nil { + return nil, + err + } + defer res.Body.Close() + err = json.NewDecoder(res.Body).Decode(&data) + if err != nil { + return nil, err + } + return data, nil +} + +// HandleCheckMedicationDispense implements MedicationDispenseInterface. +func (c *MedicationDispenseRepository) HandleCheckMedicationDispense(id string, category string) ([]string, bool, error) { + medicationDispense, err := c.GetMedicationDispenseByPatient(id, category) + if err != nil { + return nil, false, err + } + + var ids []string + if entries, ok := medicationDispense["entry"].([]interface{}); ok && len(entries) != 0 { + if entries, ok := (medicationDispense)["entry"].([]interface{}); ok && len(entries) > 0 { + if entryMap, ok := entries[0].(map[string]interface{}); ok { + if resource, ok := entryMap["resource"].(map[string]interface{}); ok { + if id, ok := resource["id"].(string); ok { + //fmt.Println("resource.id:", id) + ids = append(ids, id) + return ids, true, nil + } + } + } + } + return nil, true, nil + } + + return nil, false, nil +} + +// CreateMedicationDispense implements MedicationDispenseInterface. +func (c *MedicationDispenseRepository) CreateMedicationDispense(req model.MedicationDispenseRequest) (map[string]interface{}, error) { + req.ResourceType = constant.MedicationDispenseResourceType + req.Identifier = append(req.Identifier, common.GetIdentifier("prescription")) + identifierItem := common.GetIdentifier("prescription-item") + identifierItem.Value += "-1" + req.Identifier = append(req.Identifier, identifierItem) + + patient, err := c.setupPatient(&req) + if err != nil { + return nil, err + } + + err = c.setupEncounter(&req, patient) + if err != nil { + return nil, err + } + + // err = c.setupPractitioner(&req) + // if err != nil { + // return nil, err + // } + + oauth := model.OauthRequest{ + ClientId: c.akses.ClientId, + ClientSecret: c.akses.ClientSecret, + } + token, err := NewOauthRequestRepo(c.akses).GenerateToken(oauth) + if err != nil { + return nil, err + } + if token == nil { + return nil, errors.New(constant.ErrGenerateToken) + } + + url := c.akses.BaseUrl + "/MedicationDispense" + return httputil.DoRequest(httputil.RequestOption{ + Method: "POST", + URL: url, + Body: req, + BearerToken: token.AccessToken, + Headers: httputil.DefaultFHIRHeaders(), + }) +} + +func (c *MedicationDispenseRepository) UpdateMedicationDispense(req model.MedicationDispenseRequest) (map[string]interface{}, error) { + req.ResourceType = constant.MedicationDispenseResourceType + req.Identifier = append(req.Identifier, common.GetIdentifier("prescription")) + identifierItem := common.GetIdentifier("prescription-item") + identifierItem.Value += "-1" + req.Identifier = append(req.Identifier, identifierItem) + + patient, err := c.setupPatient(&req) + if err != nil { + return nil, err + } + + err = c.setupEncounter(&req, patient) + if err != nil { + return nil, err + } + + // err = c.setupPractitioner(&req) + // if err != nil { + // return nil, err + // } + + oauth := model.OauthRequest{ + ClientId: c.akses.ClientId, + ClientSecret: c.akses.ClientSecret, + } + token, err := NewOauthRequestRepo(c.akses).GenerateToken(oauth) + if err != nil { + return nil, err + } + if token == nil { + return nil, errors.New(constant.ErrGenerateToken) + } + + url := c.akses.BaseUrl + fmt.Sprintf("/MedicationDispense/%s", req.Id) + return httputil.DoRequest(httputil.RequestOption{ + Method: "PUT", + URL: url, + Body: req, + BearerToken: token.AccessToken, + Headers: httputil.DefaultFHIRHeaders(), + }) +} + +func (c *MedicationDispenseRepository) setupPatient(req *model.MedicationDispenseRequest) (string, error) { + if req.Subject.Reference == "" { + return "", nil + } + patientInterface := NewPatientRepo(c.akses) + patient, err := patientInterface.HandleCheckPatient(req.Subject.Reference) + if err != nil { + return "", err + } + + if patient != "" { + req.Subject.Reference = "Patient/" + patient + } + return patient, nil +} + +func (c *MedicationDispenseRepository) setupEncounter(req *model.MedicationDispenseRequest, patient string) error { + if patient == "" { + return nil + } + encounterInterface := NewEncounterRepo(c.akses) + encounterId, encounterExist, err := encounterInterface.HandleCheckEncounter(patient) + if err != nil { + return err + } + if encounterExist { + req.Context.Reference = "Encounter/" + encounterId + } + return nil +} + +// setupPractitioner handles practitioner reference logic. +func (c *MedicationDispenseRepository) setupPractitioner(req *model.MedicationDispenseRequest) error { + if len(req.Performer) == 0 { + return nil + } + + for k, performer := range req.Performer { + if strings.Contains(performer.Actor.Reference, "Practitioner/") { + temp := strings.Split(performer.Actor.Reference, "/") + practicionerInterface := NewPracticionerRepo(c.akses) + ref, _, err := practicionerInterface.HandleCheckPartitioner(temp[1]) + if err != nil { + return err + } + if ref != "" { + req.Performer[k].Actor.Reference = "Practitioner/" + ref + } + } + } + return nil +} diff --git a/internal/integration/medicationrequest_integration.go b/internal/integration/medicationrequest_integration.go new file mode 100644 index 0000000..e1601cd --- /dev/null +++ b/internal/integration/medicationrequest_integration.go @@ -0,0 +1,260 @@ +package integration + +import ( + "encoding/json" + "errors" + "fmt" + "net/http" + "os" + "satusehat-rssa/internal/constant" + "satusehat-rssa/internal/model" + "satusehat-rssa/pkg/common" + "satusehat-rssa/pkg/httputil" +) + +type MedicationRequestInterface interface { + // Define methods for MedicationRequestInterface + CreateMedicationRequest(req model.MedicationRequestRequest) (map[string]interface{}, error) + UpdateMedicationRequest(req model.MedicationRequestRequest) (map[string]interface{}, error) + GetMedicationRequestByPatient(id string, category string) (map[string]interface{}, error) + HandleCheckMedicationRequest(id string, category string) ([]string, bool, error) +} + +type MedicationRequestRepository struct { + // Define fields for MedicationRequestRepository + akses *model.Akses +} + +func NewMedicationRequestRepo(akses *model.Akses) MedicationRequestInterface { + return &MedicationRequestRepository{ + akses: akses, + } +} + +// GetMedicationRequestByPatient implements MedicationRequestInterface. +func (c *MedicationRequestRepository) GetMedicationRequestByPatient(id string, category string) (map[string]interface{}, error) { + var data map[string]interface{} + + url := c.akses.BaseUrl + "/MedicationRequest?subject=" + id + method := "GET" + + client := &http.Client{} + request, err := http.NewRequest(method, url, nil) + if err != nil { + return nil, err + } + oauth := model.OauthRequest{ + ClientId: c.akses.ClientId, + ClientSecret: c.akses.ClientSecret, + } + token, err := NewOauthRequestRepo(c.akses).GenerateToken(oauth) + if err != nil { + return nil, err + } + if token == nil { + return nil, errors.New(constant.ErrGenerateToken) + } + request.Header.Add("Authorization", "Bearer "+token.AccessToken) + request.Header.Set("Content-Type", constant.ContentTypeFHIRJSON) + request.Header.Set("Accept", constant.ContentTypeFHIRJSON) + res, err := client.Do(request) + if err != nil { + return nil, err + } + defer res.Body.Close() + + err = json.NewDecoder(res.Body).Decode(&data) + if err != nil { + return nil, err + } + + return data, nil +} + +// HandleCheckMedicationRequest implements MedicationRequestInterface. +func (c *MedicationRequestRepository) HandleCheckMedicationRequest(id string, category string) ([]string, bool, error) { + medicationReq, err := c.GetMedicationRequestByPatient(id, category) + if err != nil { + return nil, false, err + } + var ids []string + if entries, ok := medicationReq["entry"].([]interface{}); ok && len(entries) != 0 { + if entries, ok := (medicationReq)["entry"].([]interface{}); ok && len(entries) > 0 { + if entryMap, ok := entries[0].(map[string]interface{}); ok { + if resource, ok := entryMap["resource"].(map[string]interface{}); ok { + if id, ok := resource["id"].(string); ok { + //fmt.Println("resource.id:", id) + ids = append(ids, id) + return ids, true, nil + } + } + } + } + return nil, true, nil + } + + return nil, false, nil +} + +// CreateMedicationRequest implements MedicationRequestInterface. +func (c *MedicationRequestRepository) CreateMedicationRequest(req model.MedicationRequestRequest) (map[string]interface{}, error) { + req.ResourceType = constant.MedicationRequestResourceType + req.Identifier = append(req.Identifier, common.GetIdentifier("prescription")) + identifierItem := common.GetIdentifier("prescription-item") + identifierItem.Value += "-1" + req.Identifier = append(req.Identifier, identifierItem) + + patient, err := c.setupPatient(&req) + if err != nil { + return nil, err + } + + err = c.setupEncounter(&req, patient) + if err != nil { + return nil, err + } + + err = c.setupMedication(&req) + if err != nil { + return nil, err + } + req.DispenseRequest.Performer.Reference = "Organization/" + os.Getenv("ORGANIZATION_ID") + + req.MedicationRequest = nil + + // err = c.setupPractitioner(&req) + // if err != nil { + // return nil, err + // } + + oauth := model.OauthRequest{ + ClientId: c.akses.ClientId, + ClientSecret: c.akses.ClientSecret, + } + token, err := NewOauthRequestRepo(c.akses).GenerateToken(oauth) + if err != nil { + return nil, err + } + if token == nil { + return nil, errors.New(constant.ErrGenerateToken) + } + + url := c.akses.BaseUrl + "/MedicationRequest" + return httputil.DoRequest(httputil.RequestOption{ + Method: "POST", + URL: url, + Body: req, + BearerToken: token.AccessToken, + Headers: httputil.DefaultFHIRHeaders(), + }) +} + +func (c *MedicationRequestRepository) UpdateMedicationRequest(req model.MedicationRequestRequest) (map[string]interface{}, error) { + req.ResourceType = constant.MedicationRequestResourceType + req.Identifier = append(req.Identifier, common.GetIdentifier("prescription")) + identifierItem := common.GetIdentifier("prescription-item") + identifierItem.Value += "-1" + req.Identifier = append(req.Identifier, identifierItem) + + patient, err := c.setupPatient(&req) + if err != nil { + return nil, err + } + + err = c.setupEncounter(&req, patient) + if err != nil { + return nil, err + } + + // err = c.setupPractitioner(&req) + // if err != nil { + // return nil, err + // } + + oauth := model.OauthRequest{ + ClientId: c.akses.ClientId, + ClientSecret: c.akses.ClientSecret, + } + token, err := NewOauthRequestRepo(c.akses).GenerateToken(oauth) + if err != nil { + return nil, err + } + if token == nil { + return nil, errors.New(constant.ErrGenerateToken) + } + + url := c.akses.BaseUrl + fmt.Sprintf("/MedicationRequest/%s", req.Id) + return httputil.DoRequest(httputil.RequestOption{ + Method: "PUT", + URL: url, + Body: req, + BearerToken: token.AccessToken, + Headers: httputil.DefaultFHIRHeaders(), + }) +} + +func (c *MedicationRequestRepository) setupPatient(req *model.MedicationRequestRequest) (string, error) { + if req.Subject.Reference == "" { + return "", nil + } + patientInterface := NewPatientRepo(c.akses) + patient, err := patientInterface.HandleCheckPatient(req.Subject.Reference) + if err != nil { + return "", err + } + + if patient != "" { + req.Subject.Reference = "Patient/" + patient + } + return patient, nil +} + +func (c *MedicationRequestRepository) setupEncounter(req *model.MedicationRequestRequest, patient string) error { + if patient == "" { + return nil + } + encounterInterface := NewEncounterRepo(c.akses) + encounterId, encounterExist, err := encounterInterface.HandleCheckEncounter(patient) + if err != nil { + return err + } + if encounterExist { + req.Encounter.Reference = "Encounter/" + encounterId + } + return nil +} + +// setupPractitioner handles practitioner reference logic. +func (c *MedicationRequestRepository) setupPractitioner(req *model.MedicationRequestRequest) error { + if req.Requester.Reference == "" { + return nil + } + practicionerInterface := NewPracticionerRepo(c.akses) + ref, display, err := practicionerInterface.HandleCheckPartitioner(req.Requester.Reference) + if err != nil { + return err + } + if ref != "" { + req.Requester.Reference = "Practitioner/" + ref + req.Requester.Display = display + } + return nil +} + +func (c *MedicationRequestRepository) setupMedication(req *model.MedicationRequestRequest) error { + if req.MedicationRequest == nil { + return errors.New("Need Medication Request") + } + MedicationInterface := NewMedicineKfaRepo(c.akses) + medication, err := MedicationInterface.MedicationCreate(*req.MedicationRequest) + if err != nil { + return err + } + if idRaw, ok := medication["id"].(string); ok { + if idRaw == "" { + return errors.New("medication id is empty") + } + req.MedicationReference.Reference = "Medication/" + idRaw + } + return nil +} diff --git a/internal/integration/medicationstatement_integration.go b/internal/integration/medicationstatement_integration.go new file mode 100644 index 0000000..69ede4f --- /dev/null +++ b/internal/integration/medicationstatement_integration.go @@ -0,0 +1,173 @@ +package integration + +import ( + "errors" + "fmt" + "satusehat-rssa/internal/constant" + "satusehat-rssa/internal/model" + "satusehat-rssa/pkg/common" + "satusehat-rssa/pkg/httputil" +) + +type MedicationStatementInterface interface { + // Define methods for MedicationStatementInterface + CreateMedicationStatement(req model.MedicationStatementRequest) (map[string]interface{}, error) + UpdateMedicationStatement(req model.MedicationStatementRequest) (map[string]interface{}, error) +} + +type MedicationStatementRepository struct { + // Define fields for MedicationStatementRepository + akses *model.Akses +} + +func NewMedicationStatementRepo(akses *model.Akses) MedicationStatementInterface { + return &MedicationStatementRepository{ + akses: akses, + } +} + +// CreateMedicationStatement implements MedicationStatementInterface. +func (c *MedicationStatementRepository) CreateMedicationStatement(req model.MedicationStatementRequest) (map[string]interface{}, error) { + req.ResourceType = constant.MedicationStatementResourceType + + // inject identifier to contained + if len(req.Contained) > 0 { + for k, c := range req.Contained { + identifier := common.GetIdentifier("medication") + identifier.Value = c.ID + req.Contained[k].Identifier = append(req.Contained[k].Identifier, identifier) + } + } + + patient, err := c.setupPatient(&req) + if err != nil { + return nil, err + } + + err = c.setupEncounter(&req, patient) + if err != nil { + return nil, err + } + + err = c.setupSource(&req) + if err != nil { + return nil, err + } + + oauth := model.OauthRequest{ + ClientId: c.akses.ClientId, + ClientSecret: c.akses.ClientSecret, + } + token, err := NewOauthRequestRepo(c.akses).GenerateToken(oauth) + if err != nil { + return nil, err + } + if token == nil { + return nil, errors.New(constant.ErrGenerateToken) + } + + url := c.akses.BaseUrl + "/MedicationStatement" + return httputil.DoRequest(httputil.RequestOption{ + Method: "POST", + URL: url, + Body: req, + BearerToken: token.AccessToken, + Headers: httputil.DefaultFHIRHeaders(), + }) +} + +func (c *MedicationStatementRepository) UpdateMedicationStatement(req model.MedicationStatementRequest) (map[string]interface{}, error) { + req.ResourceType = constant.MedicationStatementResourceType + + // inject identifier to contained + if len(req.Contained) > 0 { + for k, c := range req.Contained { + identifier := common.GetIdentifier("medication") + identifier.Value = c.ID + req.Contained[k].Identifier = append(req.Contained[k].Identifier, identifier) + } + } + + patient, err := c.setupPatient(&req) + if err != nil { + return nil, err + } + + err = c.setupEncounter(&req, patient) + if err != nil { + return nil, err + } + + err = c.setupSource(&req) + if err != nil { + return nil, err + } + + oauth := model.OauthRequest{ + ClientId: c.akses.ClientId, + ClientSecret: c.akses.ClientSecret, + } + token, err := NewOauthRequestRepo(c.akses).GenerateToken(oauth) + if err != nil { + return nil, err + } + if token == nil { + return nil, errors.New(constant.ErrGenerateToken) + } + + url := c.akses.BaseUrl + fmt.Sprintf("/MedicationStatement/%s", req.Id) + return httputil.DoRequest(httputil.RequestOption{ + Method: "PUT", + URL: url, + Body: req, + BearerToken: token.AccessToken, + Headers: httputil.DefaultFHIRHeaders(), + }) +} + +func (c *MedicationStatementRepository) setupPatient(req *model.MedicationStatementRequest) (string, error) { + if req.Subject.Reference == "" { + return "", nil + } + patientInterface := NewPatientRepo(c.akses) + patient, err := patientInterface.HandleCheckPatient(req.Subject.Reference) + if err != nil { + return "", err + } + + if patient != "" { + req.Subject.Reference = "Patient/" + patient + } + return patient, nil +} + +func (c *MedicationStatementRepository) setupSource(req *model.MedicationStatementRequest) error { + if req.InformationSource.Reference == "" { + return nil + } + patientInterface := NewPatientRepo(c.akses) + patient, err := patientInterface.HandleCheckPatient(req.InformationSource.Reference) + if err != nil { + return err + } + + if patient != "" { + req.InformationSource.Reference = "Patient/" + patient + } + return nil +} + +func (c *MedicationStatementRepository) setupEncounter(req *model.MedicationStatementRequest, patient string) error { + if patient == "" { + return nil + } + encounterInterface := NewEncounterRepo(c.akses) + encounterId, encounterExist, err := encounterInterface.HandleCheckEncounter(patient) + if err != nil { + return err + } + if encounterExist { + req.Context.Reference = "Encounter/" + encounterId + } + return nil +} diff --git a/internal/integration/medicine_integration.go b/internal/integration/medicine_integration.go new file mode 100644 index 0000000..14a743e --- /dev/null +++ b/internal/integration/medicine_integration.go @@ -0,0 +1,196 @@ +package integration + +import ( + "encoding/json" + "errors" + "net/http" + "os" + "satusehat-rssa/internal/constant" + "satusehat-rssa/internal/model" + "satusehat-rssa/pkg/httputil" + "strconv" +) + +type MedicineIntegrationInterface interface { + GetMedicineKfa(req model.MedicineKfaRequest) ([]model.MedicineKfaResponse, error) + MedicationCreate(req model.MedicationRequest) (map[string]interface{}, error) + GetMedicineByKfaCode(kfaCode string) (map[string]interface{}, error) +} + +type MedicineKfaRepository struct { + akses *model.Akses +} + +// GetMedicineByKfaCode implements MedicineIntegrationInterface. +func (m *MedicineKfaRepository) GetMedicineByKfaCode(kfaCode string) (map[string]interface{}, error) { + var data map[string]interface{} + url := m.akses.KfaUrl + "/products" + method := "GET" + client := &http.Client{} + request, err := http.NewRequest(method, url, nil) + + // Assuming the access token is stored in a field of the akses struct + oauth := model.OauthRequest{ + ClientId: m.akses.ClientId, + ClientSecret: m.akses.ClientSecret, + } + OauthInterface := NewOauthRequestRepo(m.akses) + token, err := OauthInterface.GenerateToken(oauth) + if err != nil { + return nil, err + } + if token != nil { + request.Header.Add("Authorization", "Bearer "+token.AccessToken) + } else { + return nil, errors.New(constant.ErrGenerateToken) + } + request.Header.Set("Content-Type", "application/json") + + q := request.URL.Query() + q.Add("code", kfaCode) + q.Add("identifier", "kfa") + request.URL.RawQuery = q.Encode() + res, err := client.Do(request) + if err != nil { + return nil, err + } + defer res.Body.Close() + + if err := json.NewDecoder(res.Body).Decode(&data); err != nil { + return nil, err + } + return data, nil +} + +// MedicationCreate implements MedicineIntegrationInterface. +func (m *MedicineKfaRepository) MedicationCreate(req model.MedicationRequest) (map[string]interface{}, error) { + req.ResourceType = "Medication" + req.Meta = model.Meta{ + Profile: []string{constant.FHIRMedicationProfile}, + } + req.Identifier = []model.Identifier{ + { + System: "http://sys-ids.kemkes.go.id/medication/" + os.Getenv("ORGANIZATION_ID"), + Value: os.Getenv("ORGANIZATION_ID"), + Use: "official", + }, + } + req.Manufacturer = model.Reference{ + Reference: "Organization/" + os.Getenv("ORGANIZATION_ID"), + } + req.Extension = []model.ExtensionMedication{ + { + URL: "https://fhir.kemkes.go.id/r4/StructureDefinition/MedicationType", + ValueCodeableConcept: model.CodeableConcept{ + Coding: []model.Coding{ + { + System: "http://terminology.kemkes.go.id/CodeSystem/medication-type", + Code: "NC", + Display: "Non-compound", + }, + }, + }, + }, + } + + url := m.akses.BaseUrl + "/Medication" + + oauth := model.OauthRequest{ + ClientId: m.akses.ClientId, + ClientSecret: m.akses.ClientSecret, + } + OauthInterface := NewOauthRequestRepo(m.akses) + token, err := OauthInterface.GenerateToken(oauth) + if err != nil { + return nil, err + } + + data, err := httputil.DoRequest(httputil.RequestOption{ + Method: "POST", + URL: url, + Body: req, + BearerToken: token.AccessToken, + Headers: httputil.DefaultFHIRHeaders(), + }) + if err != nil { + return nil, err + } + return data, nil +} + +// GetMedicineKfa implements MedicineIntegrationInterface. +func (m *MedicineKfaRepository) GetMedicineKfa(req model.MedicineKfaRequest) ([]model.MedicineKfaResponse, error) { + var ( + data map[string]interface{} + ) + url := m.akses.KfaUrl + "/products/all" + method := "GET" + client := &http.Client{} + request, err := http.NewRequest(method, url, nil) + + // Assuming the access token is stored in a field of the akses struct + oauth := model.OauthRequest{ + ClientId: m.akses.ClientId, + ClientSecret: m.akses.ClientSecret, + } + OauthInterface := NewOauthRequestRepo(m.akses) + token, err := OauthInterface.GenerateToken(oauth) + + if err != nil { + return nil, err + + } + if token != nil { + request.Header.Add("Authorization", "Bearer "+token.AccessToken) + } else { + return nil, errors.New(constant.ErrGenerateToken) + } + + request.Header.Set("Content-Type", "application/json") + + q := request.URL.Query() + q.Add("page", strconv.Itoa(req.Page)) + q.Add("size", strconv.Itoa(req.Size)) + q.Add("product_type", req.ProdustType) + q.Add("keyword", req.Keyword) + request.URL.RawQuery = q.Encode() + + res, err := client.Do(request) + if err != nil { + return nil, err + } + defer res.Body.Close() + if res.StatusCode != http.StatusOK { + return nil, errors.New("failed to fetch data: " + res.Status) + } + var response []model.MedicineKfaResponse + if err := json.NewDecoder(res.Body).Decode(&data); err != nil { + return nil, err + } + if items, ok := data["items"].(map[string]interface{}); ok { + for _, item := range items { + if itemMap, ok := item.([]interface{}); ok { + for _, med := range itemMap { + if medMap, ok := med.(map[string]interface{}); ok { + medicine := model.MedicineKfaResponse{ + Name: medMap["name"].(string), + KfaCode: medMap["kfa_code"].(string), + } + response = append(response, medicine) + } else { + return nil, errors.New("invalid item format") + } + } + } + } + } else { + return nil, errors.New("invalid response format") + } + return response, nil +} + +func NewMedicineKfaRepo(akses *model.Akses) MedicineIntegrationInterface { + return &MedicineKfaRepository{ + akses: akses, + } +} diff --git a/internal/integration/oauth_integration.go b/internal/integration/oauth_integration.go new file mode 100644 index 0000000..af84d2d --- /dev/null +++ b/internal/integration/oauth_integration.go @@ -0,0 +1,54 @@ +package integration + +import ( + "encoding/json" + "net/http" + "satusehat-rssa/internal/model" + "strings" +) + +type OauthInterface interface { + GenerateToken(req model.OauthRequest) (*model.OauthResponse, error) +} + +type OauthRepository struct { + akses *model.Akses +} + +// GenerateToken implements OauthInterface. +func (o *OauthRepository) GenerateToken(req model.OauthRequest) (*model.OauthResponse, error) { + var ( + data *model.OauthResponse + ) + url := o.akses.AuthUrl + "/accesstoken?grant_type=client_credentials" + method := "POST" + + req_data := "client_id=" + req.ClientId + "&client_secret=" + req.ClientSecret + payload := strings.NewReader(req_data) + + client := &http.Client{} + request, err := http.NewRequest(method, url, payload) + + if err != nil { + return nil, err + } + request.Header.Add("Content-Type", "application/x-www-form-urlencoded") + + res, err := client.Do(request) + if err != nil { + return nil, err + } + defer res.Body.Close() + + err = json.NewDecoder(res.Body).Decode(&data) + if err != nil { + return nil, err + } + return data, nil +} + +func NewOauthRequestRepo(akses *model.Akses) OauthInterface { + return &OauthRepository{ + akses: akses, + } +} diff --git a/internal/integration/observation_intergration.go b/internal/integration/observation_intergration.go new file mode 100644 index 0000000..17c7132 --- /dev/null +++ b/internal/integration/observation_intergration.go @@ -0,0 +1,382 @@ +package integration + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "net/http" + "satusehat-rssa/internal/constant" + "satusehat-rssa/internal/model" + "satusehat-rssa/pkg/httputil" +) + +type ObservationInterface interface { + CreateObservation(req model.ObservationRequest) (map[string]interface{}, error) + GetObservationByPatient(id string) (map[string]interface{}, error) + GetObservationByPatientCategory(id string, category string) (map[string]interface{}, error) + HandleCheckObservation(id string) ([]string, bool, error) + HandleCheckObservationCategory(id string, category string) ([]string, bool, error) + UpdateObservation(req model.ObservationRequest) (map[string]interface{}, error) +} + +type ObservationRepository struct { + akses *model.Akses +} + +// GetObservationByPatientCategory implements ObservationInterface. +func (o *ObservationRepository) GetObservationByPatientCategory(id string, category string) (map[string]interface{}, error) { + var data map[string]interface{} + + url := o.akses.BaseUrl + "/Observation?subject=" + id + "&category=" + category + method := "GET" + + client := &http.Client{} + request, err := http.NewRequest(method, url, nil) + if err != nil { + return nil, err + } + oauth := model.OauthRequest{ + ClientId: o.akses.ClientId, + ClientSecret: o.akses.ClientSecret, + } + OauthInterface := NewOauthRequestRepo(o.akses) + token, err := OauthInterface.GenerateToken(oauth) + if err != nil { + return nil, err + } + if token != nil { + request.Header.Set("Authorization", "Bearer "+token.AccessToken) + } + request.Header.Set("Content-Type", constant.ContentTypeFHIRJSON) + res, err := client.Do(request) + if err != nil { + return nil, err + } + defer res.Body.Close() + + err = json.NewDecoder(res.Body).Decode(&data) + if err != nil { + return nil, err + } + + return data, nil +} + +// HandleCheckObservationCategory implements ObservationInterface. +func (o *ObservationRepository) HandleCheckObservationCategory(id string, category string) ([]string, bool, error) { + observation, err := o.GetObservationByPatientCategory(id, category) + if err != nil { + return nil, false, err + } + var ids []string + if entries, ok := observation["entry"].([]interface{}); ok && len(entries) != 0 { + if entries, ok := (observation)["entry"].([]interface{}); ok && len(entries) > 0 { + if entryMap, ok := entries[0].(map[string]interface{}); ok { + if resource, ok := entryMap["resource"].(map[string]interface{}); ok { + if id, ok := resource["id"].(string); ok { + //fmt.Println("resource.id:", id) + ids = append(ids, id) + return ids, true, nil + } + } + } + } + return nil, true, nil + } + + return nil, false, nil +} + +// HandleCheckObservation implements ObservationInterface. +func (o *ObservationRepository) HandleCheckObservation(id string) ([]string, bool, error) { + observation, err := o.GetObservationByPatient(id) + if err != nil { + return nil, false, err + } + + var ids []string + if entries, ok := observation["entry"].([]interface{}); ok && len(entries) != 0 { + if entries, ok := (observation)["entry"].([]interface{}); ok && len(entries) > 0 { + if entryMap, ok := entries[0].(map[string]interface{}); ok { + if resource, ok := entryMap["resource"].(map[string]interface{}); ok { + if id, ok := resource["id"].(string); ok { + //fmt.Println("resource.id:", id) + ids = append(ids, id) + return ids, true, nil + } + } + } + } + return nil, true, nil + } + + return nil, false, nil +} + +// GetObservationByPatient implements ObservationInterface. +func (o *ObservationRepository) GetObservationByPatient(id string) (map[string]interface{}, error) { + var ( + data map[string]interface{} + ) + url := o.akses.BaseUrl + "/Observation?subject=" + id + method := "GET" + + client := http.Client{} + request, err := http.NewRequest(method, url, nil) + if err != nil { + return nil, err + } + oauth := model.OauthRequest{ + ClientId: o.akses.ClientId, + ClientSecret: o.akses.ClientSecret, + } + OauthInterface := NewOauthRequestRepo(o.akses) + token, err := OauthInterface.GenerateToken(oauth) + if err != nil { + return nil, err + } + if token != nil { + request.Header.Add("Authorization", "Bearer "+token.AccessToken) + } else { + return nil, errors.New(constant.ErrGenerateToken) + } + request.Header.Set("Accept", constant.ContentTypeFHIRJSON) + res, err := client.Do(request) + if err != nil { + return nil, err + } + + defer res.Body.Close() + + err = json.NewDecoder(res.Body).Decode(&data) + if err != nil { + return nil, err + } + + return data, nil +} + +// CreateObservation implements ObservationInterface. +func (o *ObservationRepository) CreateObservation(req model.ObservationRequest) (map[string]interface{}, error) { + var ( + data = make(map[string]interface{}) + ) + req.ResourceType = constant.ObservationResourceType + + // Setup Patient + var patient string + if req.Subject.Reference != "" { + patientInterface := NewPatientRepo(o.akses) + var err error + patient, err = patientInterface.HandleCheckPatient(req.Subject.Reference) + if err != nil { + return nil, err + } + + if patient == "" { // Belum ada di satu sehat + return nil, errors.New("patient not found") + } else { + req.Subject.Reference = "Patient/" + patient + } + } + + //Setup Practitioner + // if req.Performer != nil && len(req.Performer) > 0 { + // practitionerInterface := NewPracticionerRepo(o.akses) + // practitionerId, _, err := practitionerInterface.HandleCheckPartitioner(req.Performer[0].Reference) + // if err != nil { + // return nil, err + // } + // if practitionerId != "" { + // req.Performer = []model.Reference{ + // {Reference: "Practitioner/" + practitionerId}, + // } + // } else { + // return nil, errors.New("practitioner not found") + // } + // } + + // Setup Encounter + if patient != "" { + encounterInterface := NewEncounterRepo(o.akses) + encounterId, encounterExist, err := encounterInterface.HandleCheckEncounter(patient) + if err != nil { + return nil, err + } + + if encounterExist { + req.Encounter.Reference = "Encounter/" + encounterId + } else { + return nil, errors.New("encounter not found") + } + } + + //setup service request + err := o.setupServiceRequest(&req, patient) + if err != nil { + return nil, errors.New("service request not found") + } + + if req.Category[0].Coding[0].Code == "laboratory" { + //setup specimen + err = o.setupSpecimen(&req, patient) + if err != nil { + return nil, errors.New("specimen not found") + } + } + + url := o.akses.BaseUrl + "/Observation" + method := "POST" + + payload, err := json.Marshal(req) + if err != nil { + return nil, err + } + client := http.Client{} + request, err := http.NewRequest(method, url, bytes.NewBuffer(payload)) + if err != nil { + return nil, err + } + + oauth := model.OauthRequest{ + ClientId: o.akses.ClientId, + ClientSecret: o.akses.ClientSecret, + } + OauthInterface := NewOauthRequestRepo(o.akses) + NewOauthRequestRepo(o.akses) + token, err := OauthInterface.GenerateToken(oauth) + + if err != nil { + return nil, err + } + request.Header.Set("Authorization", "Bearer "+token.AccessToken) + request.Header.Set("Content-Type", "application/json") + response, err := client.Do(request) + if err != nil { + return nil, err + } + defer response.Body.Close() + + err = json.NewDecoder(response.Body).Decode(&data) + if err != nil { + return nil, err + } + + return data, nil + +} + +func (o *ObservationRepository) UpdateObservation(req model.ObservationRequest) (map[string]interface{}, error) { + req.ResourceType = constant.ObservationResourceType + + // Setup Patient + var patient string + if req.Subject.Reference != "" { + patientInterface := NewPatientRepo(o.akses) + var err error + patient, err = patientInterface.HandleCheckPatient(req.Subject.Reference) + if err != nil { + return nil, err + } + + if patient == "" { // Belum ada di satu sehat + return nil, errors.New("patient not found") + } else { + req.Subject.Reference = "Patient/" + patient + } + } + + //Setup Practitioner + // if req.Performer != nil && len(req.Performer) > 0 { + // practitionerInterface := NewPracticionerRepo(o.akses) + // practitionerId, _, err := practitionerInterface.HandleCheckPartitioner(req.Performer[0].Reference) + // if err != nil { + // return nil, err + // } + // if practitionerId != "" { + // req.Performer = []model.Reference{ + // {Reference: "Practitioner/" + practitionerId}, + // } + // } else { + // return nil, errors.New("practitioner not found") + // } + // } + + // Setup Encounter + if patient != "" { + encounterInterface := NewEncounterRepo(o.akses) + encounterId, encounterExist, err := encounterInterface.HandleCheckEncounter(patient) + if err != nil { + return nil, err + } + + if encounterExist { + req.Encounter.Reference = "Encounter/" + encounterId + } else { + return nil, errors.New("encounter not found") + } + } + + oauth := model.OauthRequest{ + ClientId: o.akses.ClientId, + ClientSecret: o.akses.ClientSecret, + } + token, err := NewOauthRequestRepo(o.akses).GenerateToken(oauth) + if err != nil { + return nil, err + } + if token == nil { + return nil, errors.New(constant.ErrGenerateToken) + } + + url := o.akses.BaseUrl + fmt.Sprintf("/Observation/%s", req.Id) + return httputil.DoRequest(httputil.RequestOption{ + Method: "PUT", + URL: url, + Body: req, + BearerToken: token.AccessToken, + }) + +} + +func (o *ObservationRepository) setupServiceRequest(req *model.ObservationRequest, patient string) error { + if patient == "" { + return nil + } + serviceReq := NewServiceRequestRepository(o.akses) + ids, _, err := serviceReq.HandleCheckServiceRequest(patient) + if err != nil { + return err + } + if len(ids) > 0 { + req.BasedOn = []model.Reference{ + { + Reference: "ServiceRequest/" + ids[0], + }, + } + } + return nil +} + +func (o *ObservationRepository) setupSpecimen(req *model.ObservationRequest, patient string) error { + if patient == "" { + return nil + } + specimenInterface := NewSpecimenRepository(o.akses) + ids, exist, err := specimenInterface.HandleCheckSpecimen(patient) + if err != nil { + return err + } + if exist { + req.Specimen = &model.Reference{} + req.Specimen.Reference = "Specimen/" + ids[0] + } else { + return errors.New("specimen not found") + } + return nil +} + +func NewObservationRepo(akses *model.Akses) ObservationInterface { + return &ObservationRepository{akses: akses} +} diff --git a/internal/integration/organization_integration.go b/internal/integration/organization_integration.go new file mode 100644 index 0000000..ea5b436 --- /dev/null +++ b/internal/integration/organization_integration.go @@ -0,0 +1,164 @@ +package integration + +import ( + "bytes" + "encoding/json" + "errors" + "net/http" + "satusehat-rssa/internal/constant" + "satusehat-rssa/internal/model" +) + +type OrganizationInterface interface { + // Define methods for OrganizationInterface + CreateOrganization(req model.OrganizationRequest) (map[string]interface{}, error) + GetOrganizationPatient(id string) (map[string]interface{}, error) + HandleCheckOrganization(id string) (string, bool, error) +} + +type OrganizationRepository struct { + akses *model.Akses +} + +// CreateOrganization implements OrganizationInterface. +func (o *OrganizationRepository) CreateOrganization(req model.OrganizationRequest) (map[string]interface{}, error) { + var data map[string]interface{} + req.ResourceType = constant.OrganizationResourceType + + url := o.akses.BaseUrl + "/Organization" + method := "POST" + + payload, err := json.Marshal(req) + if err != nil { + return nil, err + } + + client := &http.Client{} + request, err := http.NewRequest(method, url, bytes.NewBuffer(payload)) + if err != nil { + return nil, err + } + + oauth := model.OauthRequest{ + ClientId: o.akses.ClientId, + ClientSecret: o.akses.ClientSecret, + } + OauthInterface := NewOauthRequestRepo(o.akses) + token, err := OauthInterface.GenerateToken(oauth) + if err != nil { + return nil, err + } + + request.Header.Set("Authorization", "Bearer "+token.AccessToken) + request.Header.Set("Content-Type", "application/json") + + response, err := client.Do(request) + if err != nil { + return nil, err + } + defer response.Body.Close() + + err = json.NewDecoder(response.Body).Decode(&data) + if err != nil { + return nil, err + } + + return data, nil +} + +// GetDiagnosisReportByPatient implements OrganizationInterface. +func (o *OrganizationRepository) GetOrganizationPatient(id string) (map[string]interface{}, error) { + url := o.akses.BaseUrl + "/DiagnosticReport?patient=" + id + method := "GET" + + client := &http.Client{} + request, err := http.NewRequest(method, url, nil) + if err != nil { + return nil, err + } + + oauth := model.OauthRequest{ + ClientId: o.akses.ClientId, + ClientSecret: o.akses.ClientSecret, + } + OauthInterface := NewOauthRequestRepo(o.akses) + token, err := OauthInterface.GenerateToken(oauth) + if err != nil { + return nil, err + } + + request.Header.Set("Authorization", "Bearer "+token.AccessToken) + request.Header.Set("Accept", constant.ContentTypeFHIRJSON) + + response, err := client.Do(request) + if err != nil { + return nil, err + } + defer response.Body.Close() + + if response.StatusCode != http.StatusOK { + return nil, errors.New("failed to get diagnosis report") + } + + var data map[string]interface{} + err = json.NewDecoder(response.Body).Decode(&data) + if err != nil { + return nil, err + } + + return data, nil +} + +// HandleCheckOrganization implements OrganizationInterface. +func (o *OrganizationRepository) HandleCheckOrganization(id string) (string, bool, error) { + url := o.akses.BaseUrl + "/Organization/" + id + method := "GET" + + client := &http.Client{} + request, err := http.NewRequest(method, url, nil) + if err != nil { + return "", false, err + } + + oauth := model.OauthRequest{ + ClientId: o.akses.ClientId, + ClientSecret: o.akses.ClientSecret, + } + OauthInterface := NewOauthRequestRepo(o.akses) + token, err := OauthInterface.GenerateToken(oauth) + if err != nil { + return "", false, err + } + + request.Header.Set("Authorization", "Bearer "+token.AccessToken) + request.Header.Set("Accept", constant.ContentTypeFHIRJSON) + + response, err := client.Do(request) + if err != nil { + return "", false, err + } + defer response.Body.Close() + + if response.StatusCode == http.StatusNotFound { + return "", false, nil + } else if response.StatusCode != http.StatusOK { + return "", false, errors.New("failed to check organization") + } + + var data map[string]interface{} + err = json.NewDecoder(response.Body).Decode(&data) + if err != nil { + return "", false, err + } + + name, ok := data["name"].(string) + if !ok { + return "", false, errors.New("organization name not found") + } + + return name, true, nil +} + +func NewOrganizationRepo(akses *model.Akses) OrganizationInterface { + return &OrganizationRepository{akses: akses} +} diff --git a/internal/integration/patient_integration.go b/internal/integration/patient_integration.go new file mode 100644 index 0000000..82f17a1 --- /dev/null +++ b/internal/integration/patient_integration.go @@ -0,0 +1,146 @@ +package integration + +import ( + "bytes" + "encoding/json" + "errors" + "net/http" + "satusehat-rssa/internal/constant" + "satusehat-rssa/internal/model" +) + +type PatientInterface interface { + // Define methods for PartientInterface + CreataPatient(req model.PatientRequest) (*map[string]interface{}, error) + GetPatientByNIK(identifier string) (*map[string]interface{}, error) + HandleCheckPatient(nik string) (string, error) +} + +type PatientRepository struct { + akses *model.Akses +} + +// HandleCheckPatient implements PatientInterface. +func (p *PatientRepository) HandleCheckPatient(nik string) (string, error) { + nik = "https://fhir.kemkes.go.id/id/nik|" + nik + patient, err := p.GetPatientByNIK(nik) + if err != nil { + return "", err + } + + if entries, ok := (*patient)["entry"].([]interface{}); ok && len(entries) > 0 { + if entryMap, ok := entries[0].(map[string]interface{}); ok { + if resource, ok := entryMap["resource"].(map[string]interface{}); ok { + if id, ok := resource["id"].(string); ok { + //fmt.Println("resource.id:", id) + return id, nil + } + } + } + } + + return "", nil +} + +// CreataPatient implements PatientInterface. +func (p *PatientRepository) CreataPatient(req model.PatientRequest) (*map[string]interface{}, error) { + var ( + data *map[string]interface{} + ) + req.ResourceType = constant.PatientResourceType + req.Meta = model.Meta{ + Profile: []string{constant.PatientProfile}, + } + + url := p.akses.BaseUrl + "/Patient" + method := "POST" + + payload, err := json.Marshal(req) + if err != nil { + return nil, err + } + client := &http.Client{} + request, err := http.NewRequest(method, url, bytes.NewBuffer(payload)) + if err != nil { + return nil, err + } + // Assuming the access token is stored in a field of the akses struct + oauth := model.OauthRequest{ + ClientId: p.akses.ClientId, + ClientSecret: p.akses.ClientSecret, + } + OauthInterface := NewOauthRequestRepo(p.akses) + token, err := OauthInterface.GenerateToken(oauth) + if err != nil { + return nil, err + } + if token != nil { + request.Header.Add("Authorization", "Bearer "+token.AccessToken) + } else { + return nil, errors.New(constant.ErrGenerateToken) + } + request.Header.Set("Content-Type", constant.ContentTypeFHIRJSON) + request.Header.Set("Accept", constant.ContentTypeFHIRJSON) + res, err := client.Do(request) + if err != nil { + return nil, err + } + defer res.Body.Close() + + err = json.NewDecoder(res.Body).Decode(&data) + if err != nil { + return nil, err + } + return data, nil + +} + +// GetPatientByNIK implements PatientInterface. +func (p *PatientRepository) GetPatientByNIK(identifier string) (*map[string]interface{}, error) { + var ( + data *map[string]interface{} + ) + url := p.akses.BaseUrl + "/Patient?identifier=" + identifier + method := "GET" + client := &http.Client{} + request, err := http.NewRequest(method, url, nil) + if err != nil { + return nil, err + } + // Assuming the access token is stored in a field of the akses struct + oauth := model.OauthRequest{ + ClientId: p.akses.ClientId, + ClientSecret: p.akses.ClientSecret, + } + OauthInterface := NewOauthRequestRepo(p.akses) + token, err := OauthInterface.GenerateToken(oauth) + + if err != nil { + return nil, err + } + if token != nil { + request.Header.Add("Authorization", "Bearer "+token.AccessToken) + } else { + return nil, errors.New(constant.ErrGenerateToken) + } + + request.Header.Set("Content-Type", constant.ContentTypeFHIRJSON) + request.Header.Set("Accept", constant.ContentTypeFHIRJSON) + + res, err := client.Do(request) + if err != nil { + return nil, err + } + defer res.Body.Close() + + err = json.NewDecoder(res.Body).Decode(&data) + if err != nil { + return nil, err + } + return data, nil +} + +// CreataPatient implements PartientInterface. +func NewPatientRepo(akses *model.Akses) PatientInterface { + return &PatientRepository{akses: akses} +} diff --git a/internal/integration/practitioner_integration.go b/internal/integration/practitioner_integration.go new file mode 100644 index 0000000..f877e98 --- /dev/null +++ b/internal/integration/practitioner_integration.go @@ -0,0 +1,105 @@ +package integration + +import ( + "encoding/json" + "errors" + "net/http" + "satusehat-rssa/internal/constant" + "satusehat-rssa/internal/model" +) + +type PracticionerInterface interface { + // Add fields as necessary for the integration + GetPracticionerByNIK(nik string) (*map[string]interface{}, error) + HandleCheckPartitioner(nik string) (string, string, error) +} + +type PracticionerRepository struct { + akses *model.Akses +} + +// HandleCheckPartitioner implements PracticionerInterface. +func (p *PracticionerRepository) HandleCheckPartitioner(nik string) (string, string, error) { + practitioner, err := p.GetPracticionerByNIK("https://fhir.kemkes.go.id/id/nik|" + nik) + if err != nil { + return "", "", err + } + if practitioner != nil { + id := "" + nameText := "" + if entriesRaw, ok := (*practitioner)["entry"]; ok { + if entries, ok := entriesRaw.([]interface{}); ok && len(entries) > 0 { + if entryMap, ok := entries[0].(map[string]interface{}); ok { + if resource, ok := entryMap["resource"].(map[string]interface{}); ok { + // Ambil id + if idRaw, ok := resource["id"].(string); ok { + id = idRaw + } + + // Ambil name[0].text + if namesRaw, ok := resource["name"]; ok { + if names, ok := namesRaw.([]interface{}); ok && len(names) > 0 { + if nameObj, ok := names[0].(map[string]interface{}); ok { + if text, ok := nameObj["text"].(string); ok { + nameText = text + } + } + } + } + } + } + } + } + return id, nameText, nil + } else { + return "", "", errors.New("practitioner not found") + } +} + +func NewPracticionerRepo(akses *model.Akses) PracticionerInterface { + return &PracticionerRepository{akses: akses} +} + +func (p *PracticionerRepository) GetPracticionerByNIK(nik string) (*map[string]interface{}, error) { + var ( + data *map[string]interface{} + ) + url := p.akses.BaseUrl + "/Practitioner?identifier=" + nik + method := "GET" + + client := &http.Client{} + request, err := http.NewRequest(method, url, nil) + if err != nil { + return nil, err + } + + oauth := model.OauthRequest{ + ClientId: p.akses.ClientId, + ClientSecret: p.akses.ClientSecret, + } + OauthInterface := NewOauthRequestRepo(p.akses) + token, err := OauthInterface.GenerateToken(oauth) + if err != nil { + return nil, err + } + if token != nil { + request.Header.Add("Authorization", "Bearer "+token.AccessToken) + } else { + return nil, errors.New(constant.ErrGenerateToken) + } + + request.Header.Set("Accept", constant.ContentTypeFHIRJSON) + + res, err := client.Do(request) + if err != nil { + return nil, err + } + defer res.Body.Close() + + err = json.NewDecoder(res.Body).Decode(&data) + if err != nil { + return nil, err + } + + return data, nil +} diff --git a/internal/integration/procedure_integration.go b/internal/integration/procedure_integration.go new file mode 100644 index 0000000..68ede61 --- /dev/null +++ b/internal/integration/procedure_integration.go @@ -0,0 +1,236 @@ +package integration + +import ( + "encoding/json" + "errors" + "net/http" + "satusehat-rssa/internal/constant" + "satusehat-rssa/internal/model" + "satusehat-rssa/pkg/httputil" + "strings" +) + +type ProcedureInterface interface { + CreateProcedure(req model.ProcedureRequest) (map[string]interface{}, error) + GetProcedure(id string) (map[string]interface{}, error) + UpdateProcedure(req model.ProcedureRequest) (map[string]interface{}, error) + GetProcedureByPatient(id string) (map[string]interface{}, error) + HandleCheckProcedure(id string) ([]string, bool, error) +} + +type ProcedureRepository struct { + akses *model.Akses +} + +// GetProcedureByPatient implements ProcedureInterface. +func (p *ProcedureRepository) GetProcedureByPatient(id string) (map[string]interface{}, error) { + var data map[string]interface{} + url := p.akses.BaseUrl + "/Procedure?subject=" + id + method := "GET" + + client := &http.Client{} + oauth := model.OauthRequest{ + ClientId: p.akses.ClientId, + ClientSecret: p.akses.ClientSecret, + } + OauthInterface := NewOauthRequestRepo(p.akses) + token, err := OauthInterface.GenerateToken(oauth) + if err != nil { + return nil, err + } + if token == nil { + return nil, errors.New(constant.ErrGenerateToken) + } + request, err := http.NewRequest(method, url, nil) + if err != nil { + return nil, err + } + request.Header.Set("Authorization", "Bearer "+token.AccessToken) + request.Header.Set("Content-Type", constant.ContentTypeFHIRJSON) + res, err := client.Do(request) + if err != nil { + return nil, err + } + defer res.Body.Close() + + err = json.NewDecoder(res.Body).Decode(&data) + if err != nil { + return nil, err + } + + return data, nil +} + +// HandleCheckProcedure implements ProcedureInterface. +func (p *ProcedureRepository) HandleCheckProcedure(id string) ([]string, bool, error) { + procedure, err := p.GetProcedureByPatient(id) + if err != nil { + return nil, false, err + } + var ids []string + if entries, ok := procedure["entry"].([]interface{}); ok && len(entries) != 0 { + if entries, ok := (procedure)["entry"].([]interface{}); ok && len(entries) > 0 { + if entryMap, ok := entries[0].(map[string]interface{}); ok { + if resource, ok := entryMap["resource"].(map[string]interface{}); ok { + if id, ok := resource["id"].(string); ok { + //fmt.Println("resource.id:", id) + ids = append(ids, id) + return ids, true, nil + } + } + } + } + return nil, true, nil + } + + return nil, false, nil +} + +// CreateProcedure implements ProcedureInterface. +func (p *ProcedureRepository) CreateProcedure(req model.ProcedureRequest) (map[string]interface{}, error) { + req.ResourceType = constant.ProcedureResourceType + + patient, err := p.setupPatient(&req) + if err != nil { + return nil, err + } + + err = p.setupEncounter(&req, patient) + if err != nil { + return nil, err + } + + //Setup Performer + err = p.setupPractitioner(&req) + if err != nil { + return nil, err + } + + oauth := model.OauthRequest{ + ClientId: p.akses.ClientId, + ClientSecret: p.akses.ClientSecret, + } + + token, err := NewOauthRequestRepo(p.akses).GenerateToken(oauth) + if err != nil { + return nil, err + } + if token == nil { + return nil, errors.New(constant.ErrGenerateToken) + } + + url := p.akses.BaseUrl + "/Procedure" + + return httputil.DoRequest(httputil.RequestOption{ + Method: "POST", + URL: url, + Body: req, + BearerToken: token.AccessToken, + }) +} + +// GetProcedure implements ProcedureInterface. +func (p *ProcedureRepository) GetProcedure(id string) (map[string]interface{}, error) { + panic("unimplemented") +} + +// UpdateProcedure implements ProcedureInterface. +func (p *ProcedureRepository) UpdateProcedure(req model.ProcedureRequest) (map[string]interface{}, error) { + patient, err := p.setupPatient(&req) + if err != nil { + return nil, err + } + + err = p.setupEncounter(&req, patient) + if err != nil { + return nil, err + } + + //Setup Performer + err = p.setupPractitioner(&req) + if err != nil { + return nil, err + } + + oauth := model.OauthRequest{ + ClientId: p.akses.ClientId, + ClientSecret: p.akses.ClientSecret, + } + + token, err := NewOauthRequestRepo(p.akses).GenerateToken(oauth) + if err != nil { + return nil, err + } + if token == nil { + return nil, errors.New(constant.ErrGenerateToken) + } + + url := p.akses.BaseUrl + "/Procedure/" + req.ID + + return httputil.DoRequest(httputil.RequestOption{ + Method: "PUT", + URL: url, + BearerToken: token.AccessToken, + Body: req, + }) +} + +func (p *ProcedureRepository) setupPatient(req *model.ProcedureRequest) (string, error) { + if req.Subject.Reference == "" { + return "", nil + } + patientInterface := NewPatientRepo(p.akses) + patient, err := patientInterface.HandleCheckPatient(req.Subject.Reference) + if err != nil { + return "", err + } + if patient != "" { + req.Subject.Reference = "Patient/" + patient + } + return patient, nil +} + +// setupEncounter extracts encounter setup logic from CreateDiagnosisReport. +func (p *ProcedureRepository) setupEncounter(req *model.ProcedureRequest, patient string) error { + if patient == "" { + return nil + } + encounterInterface := NewEncounterRepo(p.akses) + encounterId, encounterExist, err := encounterInterface.HandleCheckEncounter(patient) + if err != nil { + return err + } + if encounterExist { + req.Encounter.Reference = "Encounter/" + encounterId + } else { + //Buat dulu encounter + } + return nil +} + +func (p *ProcedureRepository) setupPractitioner(req *model.ProcedureRequest) error { + if len(req.Performer) == 0 { + return nil + } + + for _, performer := range req.Performer { + if strings.Contains(performer.Actor.Reference, "Practitioner/") { + temp := strings.Split(performer.Actor.Reference, "/") + practicionerInterface := NewPracticionerRepo(p.akses) + ref, _, err := practicionerInterface.HandleCheckPartitioner(temp[1]) + if err != nil { + return err + } + if ref != "" { + performer.Actor.Reference = "Practitioner/" + ref + } + } + } + return nil +} + +func NewProcedureRepo(akses *model.Akses) ProcedureInterface { + return &ProcedureRepository{ + akses: akses, + } +} diff --git a/internal/integration/questionnaireresponse_integration.go b/internal/integration/questionnaireresponse_integration.go new file mode 100644 index 0000000..2ae75ce --- /dev/null +++ b/internal/integration/questionnaireresponse_integration.go @@ -0,0 +1,181 @@ +package integration + +import ( + "errors" + "fmt" + "satusehat-rssa/internal/constant" + "satusehat-rssa/internal/model" + "satusehat-rssa/pkg/httputil" +) + +type QuestionnaireResponseInterface interface { + // Define methods for QuestionnaireResponseInterface + CreateQuestionnaireResponse(req model.QuestionnaireResponseRequest) (map[string]interface{}, error) + UpdateQuestionnaireResponse(req model.QuestionnaireResponseRequest) (map[string]interface{}, error) +} + +type QuestionnaireResponseRepository struct { + // Define fields for QuestionnaireResponseRepository + akses *model.Akses +} + +func NewQuestionnaireResponseRepo(akses *model.Akses) QuestionnaireResponseInterface { + return &QuestionnaireResponseRepository{ + akses: akses, + } +} + +// CreateQuestionnaireResponse implements QuestionnaireResponseInterface. +func (c *QuestionnaireResponseRepository) CreateQuestionnaireResponse(req model.QuestionnaireResponseRequest) (map[string]interface{}, error) { + req.ResourceType = constant.QuestionnaireResponseResourceType + + patient, err := c.setupPatient(&req) + if err != nil { + return nil, err + } + + err = c.setupEncounter(&req, patient) + if err != nil { + return nil, err + } + + err = c.setupSource(&req) + if err != nil { + return nil, err + } + + // err = c.setupPractitioner(&req) + // if err != nil { + // return nil, err + // } + + oauth := model.OauthRequest{ + ClientId: c.akses.ClientId, + ClientSecret: c.akses.ClientSecret, + } + token, err := NewOauthRequestRepo(c.akses).GenerateToken(oauth) + if err != nil { + return nil, err + } + if token == nil { + return nil, errors.New(constant.ErrGenerateToken) + } + + url := c.akses.BaseUrl + "/QuestionnaireResponse" + return httputil.DoRequest(httputil.RequestOption{ + Method: "POST", + URL: url, + Body: req, + BearerToken: token.AccessToken, + Headers: httputil.DefaultFHIRHeaders(), + }) +} + +func (c *QuestionnaireResponseRepository) UpdateQuestionnaireResponse(req model.QuestionnaireResponseRequest) (map[string]interface{}, error) { + req.ResourceType = constant.QuestionnaireResponseResourceType + + patient, err := c.setupPatient(&req) + if err != nil { + return nil, err + } + + err = c.setupEncounter(&req, patient) + if err != nil { + return nil, err + } + + err = c.setupSource(&req) + if err != nil { + return nil, err + } + + // err = c.setupPractitioner(&req) + // if err != nil { + // return nil, err + // } + + oauth := model.OauthRequest{ + ClientId: c.akses.ClientId, + ClientSecret: c.akses.ClientSecret, + } + token, err := NewOauthRequestRepo(c.akses).GenerateToken(oauth) + if err != nil { + return nil, err + } + if token == nil { + return nil, errors.New(constant.ErrGenerateToken) + } + + url := c.akses.BaseUrl + fmt.Sprintf("/QuestionnaireResponse/%s", req.Id) + return httputil.DoRequest(httputil.RequestOption{ + Method: "PUT", + URL: url, + Body: req, + BearerToken: token.AccessToken, + Headers: httputil.DefaultFHIRHeaders(), + }) +} + +func (c *QuestionnaireResponseRepository) setupPatient(req *model.QuestionnaireResponseRequest) (string, error) { + if req.Subject.Reference == "" { + return "", nil + } + patientInterface := NewPatientRepo(c.akses) + patient, err := patientInterface.HandleCheckPatient(req.Subject.Reference) + if err != nil { + return "", err + } + + if patient != "" { + req.Subject.Reference = "Patient/" + patient + } + return patient, nil +} + +func (c *QuestionnaireResponseRepository) setupSource(req *model.QuestionnaireResponseRequest) error { + if req.Source.Reference == "" { + return nil + } + patientInterface := NewPatientRepo(c.akses) + patient, err := patientInterface.HandleCheckPatient(req.Source.Reference) + if err != nil { + return err + } + + if patient != "" { + req.Source.Reference = "Patient/" + patient + } + return nil +} + +func (c *QuestionnaireResponseRepository) setupEncounter(req *model.QuestionnaireResponseRequest, patient string) error { + if patient == "" { + return nil + } + encounterInterface := NewEncounterRepo(c.akses) + encounterId, encounterExist, err := encounterInterface.HandleCheckEncounter(patient) + if err != nil { + return err + } + if encounterExist { + req.Encounter.Reference = "Encounter/" + encounterId + } + return nil +} + +// setupPractitioner handles practitioner reference logic. +func (c *QuestionnaireResponseRepository) setupPractitioner(req *model.QuestionnaireResponseRequest) error { + if req.Author.Reference == "" { + return nil + } + practicionerInterface := NewPracticionerRepo(c.akses) + ref, display, err := practicionerInterface.HandleCheckPartitioner(req.Author.Reference) + if err != nil { + return err + } + if ref != "" { + req.Author.Reference = "Practitioner/" + ref + req.Author.Display = display + } + return nil +} diff --git a/internal/integration/servicerequest_integration.go b/internal/integration/servicerequest_integration.go new file mode 100644 index 0000000..600afef --- /dev/null +++ b/internal/integration/servicerequest_integration.go @@ -0,0 +1,288 @@ +package integration + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "net/http" + "satusehat-rssa/internal/constant" + "satusehat-rssa/internal/model" + "satusehat-rssa/pkg/httputil" +) + +type ServiceRequestInterface interface { + CreateServiceRequest(req model.ServiceRequest) (map[string]interface{}, error) + GetServiceRequestByPatient(id string) (map[string]interface{}, error) + GetServiceRequestByPatientCategory(id string, category string) (map[string]interface{}, error) + HandleCheckServiceRequest(id string) ([]string, bool, error) + HandleCheckServiceRequestByCategory(id string, category string) ([]string, bool, error) + UpdateServiceRequest(req model.ServiceRequest) (map[string]interface{}, error) +} + +type ServiceRequestRepository struct { + akses *model.Akses +} + +// GetServiceRequestByPatientCategory implements ServiceRequestInterface. +func (s *ServiceRequestRepository) GetServiceRequestByPatientCategory(id string, category string) (map[string]interface{}, error) { + var data map[string]interface{} + url := s.akses.BaseUrl + "/ServiceRequest?subject=" + id + "&category=" + category + method := "GET" + client := &http.Client{} + request, err := http.NewRequest(method, url, nil) + if err != nil { + return nil, err + } + oauth := model.OauthRequest{ + ClientId: s.akses.ClientId, + ClientSecret: s.akses.ClientSecret, + } + OauthInterface := NewOauthRequestRepo(s.akses) + token, err := OauthInterface.GenerateToken(oauth) + if err != nil { + return nil, err + } + if token != nil { + request.Header.Set("Authorization", "Bearer "+token.AccessToken) + } + request.Header.Set("Content-Type", constant.ContentTypeFHIRJSON) + res, err := client.Do(request) + if err != nil { + return nil, err + } + defer res.Body.Close() + + err = json.NewDecoder(res.Body).Decode(&data) + if err != nil { + return nil, err + } + return data, nil +} + +// HandleCheckServiceRequestByCategory implements ServiceRequestInterface. +func (s *ServiceRequestRepository) HandleCheckServiceRequestByCategory(id string, category string) ([]string, bool, error) { + servicereq, err := s.GetServiceRequestByPatientCategory(id, category) + if err != nil { + return nil, false, err + } + var ids []string + if entries, ok := servicereq["entry"].([]interface{}); ok && len(entries) != 0 { + if entries, ok := (servicereq)["entry"].([]interface{}); ok && len(entries) > 0 { + if entryMap, ok := entries[0].(map[string]interface{}); ok { + if resource, ok := entryMap["resource"].(map[string]interface{}); ok { + if id, ok := resource["id"].(string); ok { + //fmt.Println("resource.id:", id) + ids = append(ids, id) + return ids, true, nil + } + } + } + } + return nil, true, nil + } + + return nil, false, nil +} + +// CreateServiceRequest implements ServiceRequestInterface. +func (s *ServiceRequestRepository) CreateServiceRequest(req model.ServiceRequest) (map[string]interface{}, error) { + var data map[string]interface{} + + req.ResourceType = constant.ServiceRequestResourceType + // Setup Patient + var patient string + if req.Subject.Reference != "" { + patientInterface := NewPatientRepo(s.akses) + var err error + patient, err = patientInterface.HandleCheckPatient(req.Subject.Reference) + if err != nil { + return nil, err + } + + if patient == "" { // Belum ada di satu sehat + return nil, errors.New("patient not found") + } else { + req.Subject.Reference = "Patient/" + patient + } + } + + // Setup Encounter + if patient != "" { + encounterInterface := NewEncounterRepo(s.akses) + encounterId, encounterExist, err := encounterInterface.HandleCheckEncounter(patient) + if err != nil { + return nil, err + } + + if encounterExist { + req.Encounter.Reference = "Encounter/" + encounterId + } else { + return nil, errors.New("encounter not found") + } + } + + url := s.akses.BaseUrl + "/ServiceRequest" + method := "POST" + payload, err := json.Marshal(req) + if err != nil { + return nil, err + } + client := &http.Client{} + request, err := http.NewRequest(method, url, bytes.NewBuffer(payload)) + if err != nil { + return nil, err + } + oauth := model.OauthRequest{ + ClientId: s.akses.ClientId, + ClientSecret: s.akses.ClientSecret, + } + OauthInterface := NewOauthRequestRepo(s.akses) + token, err := OauthInterface.GenerateToken(oauth) + if err != nil { + return nil, err + } + if token != nil { + request.Header.Set("Authorization", "Bearer "+token.AccessToken) + } + request.Header.Set("Content-Type", constant.ContentTypeFHIRJSON) + res, err := client.Do(request) + + if err != nil { + return nil, err + } + defer res.Body.Close() + + err = json.NewDecoder(res.Body).Decode(&data) + if err != nil { + return nil, err + } + + return data, nil + +} + +func (s *ServiceRequestRepository) UpdateServiceRequest(req model.ServiceRequest) (map[string]interface{}, error) { + req.ResourceType = constant.ServiceRequestResourceType + // Setup Patient + var patient string + if req.Subject.Reference != "" { + patientInterface := NewPatientRepo(s.akses) + var err error + patient, err = patientInterface.HandleCheckPatient(req.Subject.Reference) + if err != nil { + return nil, err + } + + if patient == "" { // Belum ada di satu sehat + return nil, errors.New("patient not found") + } else { + req.Subject.Reference = "Patient/" + patient + } + } + + // Setup Encounter + if patient != "" { + encounterInterface := NewEncounterRepo(s.akses) + encounterId, encounterExist, err := encounterInterface.HandleCheckEncounter(patient) + if err != nil { + return nil, err + } + + if encounterExist { + req.Encounter.Reference = "Encounter/" + encounterId + } else { + return nil, errors.New("encounter not found") + } + } + + oauth := model.OauthRequest{ + ClientId: s.akses.ClientId, + ClientSecret: s.akses.ClientSecret, + } + token, err := NewOauthRequestRepo(s.akses).GenerateToken(oauth) + if err != nil { + return nil, err + } + if token == nil { + return nil, errors.New(constant.ErrGenerateToken) + } + + url := s.akses.BaseUrl + fmt.Sprintf("/ServiceRequest/%s", req.Id) + return httputil.DoRequest(httputil.RequestOption{ + Method: "PUT", + URL: url, + Body: req, + BearerToken: token.AccessToken, + }) + +} + +// GetServiceRequestByPatient implements ServiceRequestInterface. +func (s *ServiceRequestRepository) GetServiceRequestByPatient(id string) (map[string]interface{}, error) { + var data map[string]interface{} + url := s.akses.BaseUrl + "/ServiceRequest?subject=" + id + method := "GET" + client := &http.Client{} + request, err := http.NewRequest(method, url, nil) + if err != nil { + return nil, err + } + oauth := model.OauthRequest{ + ClientId: s.akses.ClientId, + ClientSecret: s.akses.ClientSecret, + } + OauthInterface := NewOauthRequestRepo(s.akses) + token, err := OauthInterface.GenerateToken(oauth) + if err != nil { + return nil, err + } + if token != nil { + request.Header.Set("Authorization", "Bearer "+token.AccessToken) + } + request.Header.Set("Content-Type", constant.ContentTypeFHIRJSON) + res, err := client.Do(request) + + if err != nil { + return nil, err + } + defer res.Body.Close() + + err = json.NewDecoder(res.Body).Decode(&data) + if err != nil { + return nil, err + } + + return data, nil + +} + +// HandleCheckServiceRequest implements ServiceRequestInterface. +func (s *ServiceRequestRepository) HandleCheckServiceRequest(id string) ([]string, bool, error) { + servicereq, err := s.GetServiceRequestByPatient(id) + if err != nil { + return nil, false, err + } + + var ids []string + if entries, ok := servicereq["entry"].([]interface{}); ok && len(entries) != 0 { + if entries, ok := (servicereq)["entry"].([]interface{}); ok && len(entries) > 0 { + if entryMap, ok := entries[0].(map[string]interface{}); ok { + if resource, ok := entryMap["resource"].(map[string]interface{}); ok { + if id, ok := resource["id"].(string); ok { + //fmt.Println("resource.id:", id) + ids = append(ids, id) + return ids, true, nil + } + } + } + } + return nil, true, nil + } + + return nil, false, nil +} + +func NewServiceRequestRepository(akses *model.Akses) ServiceRequestInterface { + return &ServiceRequestRepository{akses: akses} +} diff --git a/internal/integration/specimen_integration.go b/internal/integration/specimen_integration.go new file mode 100644 index 0000000..9378500 --- /dev/null +++ b/internal/integration/specimen_integration.go @@ -0,0 +1,336 @@ +package integration + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "net/http" + "satusehat-rssa/internal/constant" + "satusehat-rssa/internal/model" + "satusehat-rssa/pkg/httputil" +) + +type SpecimenInterface interface { + CreateSpecimen(req model.SpecimenRequest) (map[string]interface{}, error) + GetSpecimenByPatient(id string) (map[string]interface{}, error) + GetSpecimenByPatientCategory(id string, category string) (map[string]interface{}, error) + HandleCheckSpecimen(id string) ([]string, bool, error) + HandleCheckSpecimenCategory(id string, category string) ([]string, bool, error) + UpdateSpecimen(req model.SpecimenRequest) (map[string]interface{}, error) +} + +type SpecimenRepository struct { + akses *model.Akses +} + +// GetSpecimenByPatientCategory implements SpecimenInterface. +func (s *SpecimenRepository) GetSpecimenByPatientCategory(id string, category string) (map[string]interface{}, error) { + var data map[string]interface{} + + url := s.akses.BaseUrl + "/Specimen?subject=" + id + "&category=" + category + method := "GET" + + client := &http.Client{} + request, err := http.NewRequest(method, url, nil) + if err != nil { + return nil, err + } + + oauth := model.OauthRequest{ + ClientId: s.akses.ClientId, + ClientSecret: s.akses.ClientSecret, + } + OauthInterface := NewOauthRequestRepo(s.akses) + token, err := OauthInterface.GenerateToken(oauth) + if err != nil { + return nil, err + } + if token != nil { + request.Header.Set("Authorization", "Bearer "+token.AccessToken) + } + request.Header.Set("Content-Type", constant.ContentTypeFHIRJSON) + res, err := client.Do(request) + if err != nil { + return nil, err + } + defer res.Body.Close() + + err = json.NewDecoder(res.Body).Decode(&data) + if err != nil { + return nil, err + } + return data, nil +} + +// HandleCheckSpecimenCategory implements SpecimenInterface. +func (s *SpecimenRepository) HandleCheckSpecimenCategory(id string, category string) ([]string, bool, error) { + specimen, err := s.GetSpecimenByPatientCategory(id, category) + if err != nil { + return nil, false, err + } + var ids []string + if entries, ok := specimen["entry"].([]interface{}); ok && len(entries) != 0 { + if entries, ok := (specimen)["entry"].([]interface{}); ok && len(entries) > 0 { + if entryMap, ok := entries[0].(map[string]interface{}); ok { + if resource, ok := entryMap["resource"].(map[string]interface{}); ok { + if id, ok := resource["id"].(string); ok { + //fmt.Println("resource.id:", id) + ids = append(ids, id) + return ids, true, nil + } + } + } + } + return nil, true, nil + } + + return nil, false, nil +} + +// CreateSpecimen implements SpecimenInterface. +func (s *SpecimenRepository) CreateSpecimen(req model.SpecimenRequest) (map[string]interface{}, error) { + var data map[string]interface{} + + req.ResourceType = constant.SpecimenResourceType + // Setup Patient + var patient string + if req.Subject.Reference != "" { + patientInterface := NewPatientRepo(s.akses) + var err error + patient, err = patientInterface.HandleCheckPatient(req.Subject.Reference) + if err != nil { + return nil, err + } + + if patient == "" { // Belum ada di satu sehat + return nil, errors.New("patient not found") + } else { + req.Subject.Reference = "Patient/" + patient + } + } + + // Setup Service Request + err := s.setupServiceRequest(&req, patient) + if err != nil { + return nil, err + } + + url := s.akses.BaseUrl + "/Specimen" + method := "POST" + payload, err := json.Marshal(req) + if err != nil { + return nil, err + } + + client := &http.Client{} + request, err := http.NewRequest(method, url, bytes.NewBuffer(payload)) + if err != nil { + return nil, err + } + + request.Header.Set("Content-Type", "application/json") + + oauth := model.OauthRequest{ + ClientId: s.akses.ClientId, + ClientSecret: s.akses.ClientSecret, + } + OauthInterface := NewOauthRequestRepo(s.akses) + token, err := OauthInterface.GenerateToken(oauth) + if err != nil { + return nil, err + } + if token != nil { + request.Header.Set("Authorization", "Bearer "+token.AccessToken) + } + + response, err := client.Do(request) + if err != nil { + return nil, err + } + defer response.Body.Close() + err = json.NewDecoder(response.Body).Decode(&data) + if err != nil { + return nil, err + } + if !isOperationOutcomeError(data) { + // Setup Encounter + if patient != "" { + encounterInterface := NewEncounterRepo(s.akses) + encounterId, encounterExist, err := encounterInterface.HandleCheckEncounter(patient) + if err != nil { + return nil, err + } + + if encounterExist { + data["encounter"] = map[string]interface{}{ + "reference": "Encounter/" + encounterId, + } + } else { + return nil, errors.New("encounter not found") + } + } + } + + return data, nil +} + +// GetSpecimenByPatient implements SpecimenInterface. +func (s *SpecimenRepository) GetSpecimenByPatient(id string) (map[string]interface{}, error) { + var data map[string]interface{} + url := s.akses.BaseUrl + "/Specimen?subject=" + id + method := "GET" + client := &http.Client{} + request, err := http.NewRequest(method, url, nil) + if err != nil { + return nil, err + } + oauth := model.OauthRequest{ + ClientId: s.akses.ClientId, + ClientSecret: s.akses.ClientSecret, + } + OauthInterface := NewOauthRequestRepo(s.akses) + token, err := OauthInterface.GenerateToken(oauth) + if err != nil { + return nil, err + } + if token != nil { + request.Header.Set("Authorization", "Bearer "+token.AccessToken) + } + + response, err := client.Do(request) + if err != nil { + return nil, err + } + defer response.Body.Close() + err = json.NewDecoder(response.Body).Decode(&data) + if err != nil { + return nil, err + } + + return data, nil +} + +// isOperationOutcomeError returns true when the response is an OperationOutcome +// with at least one issue that includes diagnostics or details.text. +func isOperationOutcomeError(resp map[string]interface{}) bool { + if resp == nil { + return false + } + if rt, ok := resp["resourceType"].(string); !ok || rt != "OperationOutcome" { + return false + } + issues, ok := resp["issue"].([]interface{}) + if !ok || len(issues) == 0 { + return false + } + for _, it := range issues { + if m, ok := it.(map[string]interface{}); ok { + if diag, ok := m["diagnostics"].(string); ok && diag != "" { + return true + } + if details, ok := m["details"].(map[string]interface{}); ok { + if txt, ok := details["text"].(string); ok && txt != "" { + return true + } + } + } + } + return false +} + +func (s *SpecimenRepository) setupServiceRequest(req *model.SpecimenRequest, patient string) error { + if patient == "" { + return nil + } + serviceReq := NewServiceRequestRepository(s.akses) + ids, _, err := serviceReq.HandleCheckServiceRequest(patient) + if err != nil { + return err + } + if len(ids) > 0 { + req.Request = []model.Reference{ + { + Reference: "ServiceRequest/" + ids[0], + }, + } + } + return nil +} + +// HandleCheckSpecimen implements SpecimenInterface. +func (s *SpecimenRepository) HandleCheckSpecimen(id string) ([]string, bool, error) { + specimen, err := s.GetSpecimenByPatient(id) + if err != nil { + return nil, false, err + } + + // You can process 'specimen' here if needed + var ids []string + if entries, ok := specimen["entry"].([]interface{}); ok && len(entries) != 0 { + if entries, ok := (specimen)["entry"].([]interface{}); ok && len(entries) > 0 { + if entryMap, ok := entries[0].(map[string]interface{}); ok { + if resource, ok := entryMap["resource"].(map[string]interface{}); ok { + if id, ok := resource["id"].(string); ok { + //fmt.Println("resource.id:", id) + ids = append(ids, id) + return ids, true, nil + } + } + } + } + return nil, true, nil + } + + return nil, false, nil +} + +func (s *SpecimenRepository) UpdateSpecimen(req model.SpecimenRequest) (map[string]interface{}, error) { + req.ResourceType = constant.SpecimenResourceType + // Setup Patient + var patient string + if req.Subject.Reference != "" { + patientInterface := NewPatientRepo(s.akses) + var err error + patient, err = patientInterface.HandleCheckPatient(req.Subject.Reference) + if err != nil { + return nil, err + } + + if patient == "" { // Belum ada di satu sehat + return nil, errors.New("patient not found") + } else { + req.Subject.Reference = "Patient/" + patient + } + } + + // Setup Service Request + err := s.setupServiceRequest(&req, patient) + if err != nil { + return nil, err + } + + oauth := model.OauthRequest{ + ClientId: s.akses.ClientId, + ClientSecret: s.akses.ClientSecret, + } + token, err := NewOauthRequestRepo(s.akses).GenerateToken(oauth) + if err != nil { + return nil, err + } + if token == nil { + return nil, errors.New(constant.ErrGenerateToken) + } + + url := s.akses.BaseUrl + fmt.Sprintf("/Specimen/%s", req.Id) + return httputil.DoRequest(httputil.RequestOption{ + Method: "PUT", + URL: url, + Body: req, + BearerToken: token.AccessToken, + }) +} + +func NewSpecimenRepository(akses *model.Akses) SpecimenInterface { + return &SpecimenRepository{akses: akses} +} diff --git a/internal/midleware/middleware.go b/internal/midleware/middleware.go new file mode 100644 index 0000000..ab619f4 --- /dev/null +++ b/internal/midleware/middleware.go @@ -0,0 +1,4 @@ +package midleware + +type AuthMiddleware struct { +} diff --git a/internal/model/allergancytoleran.go b/internal/model/allergancytoleran.go new file mode 100644 index 0000000..57ce43c --- /dev/null +++ b/internal/model/allergancytoleran.go @@ -0,0 +1,71 @@ +package model + +type AllergancyToleranRequest struct { + Id string `json:"id,omitempty"` + ResourceType string `json:"resourceType"` + Identifier []*IdentifierObject `json:"identifier"` + ClinicalStatus *ClinicalStatusObject `json:"clinicalStatus"` + VerificationStatus *ClinicalStatusObject `json:"verificationStatus"` + Category []string `json:"category"` + Code *CodeObject `json:"code"` + Patient *PatientObject `json:"patient"` + Encounter *PatientObject `json:"encounter"` + RecordedDate string `json:"recordedDate"` + Recorder *PatientObject `json:"recorder"` +} + +type AllergancyToleranResponse struct { + ResourceType string `json:"resourceType"` + Identifier []*IdentifierObject `json:"identifier"` + ClinicalStatus *ClinicalStatusObject `json:"clinicalStatus"` + VerificationStatus *ClinicalStatusObject `json:"verificationStatus"` + Category []string `json:"category"` + Code *CodeObject `json:"code"` + Patient *PatientObject `json:"patient"` + Encounter *PatientObject `json:"encounter"` + RecordedDate string `json:"recordedDate"` + Recorder *PatientObject `json:"recorder"` + ID string `json:"id"` + Meta *MetaObject `json:"meta"` + Issue []*IssuerObject `json:"issue` +} + +type IdentifierObject struct { + System string `json:"system"` + Use string `json:"use"` + Value string `json:"value"` +} + +type ClinicalStatusObject struct { + Coding []*CodingObject `json:"coding"` +} + +type CodeObject struct { + Coding []*CodingObject `json:"coding"` + Text string `json:"text"` +} + +type PatientObject struct { + Reference string `json:"reference"` + Display string `json:"display"` +} + +type CodingObject struct { + System string `json:"system"` + Code string `json:"code"` + Display string `json:"display"` +} + +type MetaObject struct { + VersionId string `json:"versionId"` + LastUpdated string `json:"lastUpdated"` +} + +type IssuerObject struct { + Saverity string `json:"severity"` + Code string `json:"code"` + Details *DetailObject `json:"details"` +} +type DetailObject struct { + Text string `json:"text"` +} diff --git a/internal/model/careplan.go b/internal/model/careplan.go new file mode 100644 index 0000000..b8f4e45 --- /dev/null +++ b/internal/model/careplan.go @@ -0,0 +1,16 @@ +package model + +type CarePlanRequest struct { + Id string `json:"id,omitempty"` + ResourceType string `json:"resourceType"` + Status string `json:"status" binding:"required"` + Intent string `json:"intent" binding:"required"` + Category []Category `json:"category" binding:"required"` + Title string `json:"title" binding:"required"` + Description string `json:"description" binding:"required"` + Subject Reference `json:"subject" binding:"required"` + Encounter Reference `json:"encounter"` + Created string `json:"created" binding:"required"` + Author Reference `json:"author" binding:"required"` + Goal []Reference `json:"goal"` +} diff --git a/internal/model/clinicalImpression.go b/internal/model/clinicalImpression.go new file mode 100644 index 0000000..6b70170 --- /dev/null +++ b/internal/model/clinicalImpression.go @@ -0,0 +1,56 @@ +package model + +// Reference to another FHIR resource +type Reference struct { + Reference string `json:"reference"` + Display string `json:"display,omitempty"` +} + +// Coding for codeable concepts +type Coding struct { + System string `json:"system"` + Code string `json:"code"` + Display string `json:"display"` +} + +// CodeableConcept for coded fields +type CodeableConcept struct { + Coding []Coding `json:"coding"` + Text string `json:"text,omitempty"` +} + +// Investigation item reference +type InvestigationItem struct { + Reference string `json:"reference"` +} + +// Investigation structure +type Investigation struct { + Code CodeableConcept `json:"code"` + Item []InvestigationItem `json:"item"` +} + +// Finding structure +type Finding struct { + ItemCodeableConcept *CodeableConcept `json:"itemCodeableConcept,omitempty"` + ItemReference *Reference `json:"itemReference,omitempty"` +} + +// ClinicalImpressionRequest for the request body +type ClinicalImpressionRequest struct { + Id string `json:"id,omitempty"` + ResourceType string `json:"resourceType"` + Identifier []Identifier `json:"identifier"` + Status string `json:"status"` + Description string `json:"description"` + Subject Reference `json:"subject"` + Encounter Reference `json:"encounter"` + EffectiveDateTime string `json:"effectiveDateTime"` + Date string `json:"date"` + Assessor Reference `json:"assessor"` + Problem []Reference `json:"problem"` + Investigation []Investigation `json:"investigation"` + Summary string `json:"summary"` + Finding []Finding `json:"finding"` + PrognosisCodeableConcept []CodeableConcept `json:"prognosisCodeableConcept"` +} diff --git a/internal/model/composition.go b/internal/model/composition.go new file mode 100644 index 0000000..ae976df --- /dev/null +++ b/internal/model/composition.go @@ -0,0 +1,40 @@ +package model + +type CompositionRequest struct { + ResourceType string `json:"resourceType"` + Identifier Identifier `json:"identifier"` + Status string `json:"status"` + Category []CodeableConcept `json:"category"` + Type CodeableConcept `json:"type"` + Subject Reference `json:"subject"` + Date string `json:"date"` + Title string `json:"title"` + Author []Reference `json:"author"` + Custodian Reference `json:"custodian"` + Encounter Reference `json:"encounter"` + Section []SectionRequest `json:"section"` + Id string `json:"id,omitempty"` + Anamnesis string `json:"anamnesis,omitempty"` + PemeriksaanFisik string `json:"pemeriksaanFisik,omitempty"` + PemeriksaanPenunjang string `json:"pemeriksaanPenunjang,omitempty"` + Medikamentosa string `json:"medikamentosa,omitempty"` + LanjutanPenatalaksanaan string `json:"lanjutanPenatalaksanaan,omitempty"` +} + +type SectionRequest struct { + Title string `json:"title,omitempty"` + Code CodeableConcept `json:"code"` + Text *SectionText `json:"text,omitempty"` + Entry []Reference `json:"entry,omitempty"` + Section []SectionRequest `json:"section,omitempty"` +} + +type Section struct { + Code CodeableConcept `json:"code"` + Text SectionText `json:"text"` +} + +type SectionText struct { + Status string `json:"status"` + Div string `json:"div"` +} diff --git a/internal/model/condition.go b/internal/model/condition.go new file mode 100644 index 0000000..1db0189 --- /dev/null +++ b/internal/model/condition.go @@ -0,0 +1,11 @@ +package model + +type ConditionRequest struct { + Id string `json:"id,omitempty"` + ResourceType string `json:"resourceType"` + ClinicalStatus CodeableConcept `json:"clinicalStatus"` + Category []CodeableConcept `json:"category"` + Code CodeableConcept `json:"code"` + Subject Reference `json:"subject"` + Encounter Reference `json:"encounter"` +} diff --git a/internal/model/diagnosisreport.go b/internal/model/diagnosisreport.go new file mode 100644 index 0000000..a7c8d71 --- /dev/null +++ b/internal/model/diagnosisreport.go @@ -0,0 +1,20 @@ +package model + +// DiagnosticReportRequest defines the structure for a diagnostic report request +type DiagnosticReportRequest struct { + Id string `json:"id,omitempty"` + ResourceType string `json:"resourceType"` + Identifier []Identifier `json:"identifier"` + Status string `json:"status"` + Category []CodeableConcept `json:"category"` + Code CodeableConcept `json:"code"` + Subject Reference `json:"subject"` + Encounter Reference `json:"encounter"` + EffectiveDateTime string `json:"effectiveDateTime"` + Issued string `json:"issued"` + Performer []Reference `json:"performer"` + Result []Reference `json:"result"` + Specimen []Reference `json:"specimen"` + BasedOn []Reference `json:"basedOn"` + ConclusionCode []CodeableConcept `json:"conclusionCode"` +} diff --git a/internal/model/encounter.go b/internal/model/encounter.go new file mode 100644 index 0000000..3966aa7 --- /dev/null +++ b/internal/model/encounter.go @@ -0,0 +1,82 @@ +package model + +type EncounterRequest struct { + ResourceType string `json:"resourceType"` + Status string `json:"status"` + Class Class `json:"class"` + Subject Subject `json:"subject"` + Participant []Participant `json:"participant"` + Period struct { + Start string `json:"start"` + } `json:"end"` + Location []Location `json:"location"` + StatusHistory []StatusHistory `json:"statusHistory"` + ServiceProvider struct { + Reference string `json:"reference"` + } `json:"serviceProvider"` + Identifier []IdentifierEncounter `json:"identifier"` +} + +type Class struct { + System string `json:"system"` + Code string `json:"code"` + Display string `json:"display"` +} +type Subject struct { + Reference string `json:"reference"` + Display string `json:"display"` +} + +type Participant struct { + Type []struct { + Coding []Class `json:"coding"` + } `json:"type"` + Individual Subject `json:"individual"` +} + +type Location struct { + Location Reference `json:"location"` +} + +type StatusHistory struct { + Status string `json:"status"` + Period struct { + Start string `json:"start"` + } `json:"period"` +} + +type EncounterUpdateRequest struct { + ResourceType string `json:"resourceType"` + ID string `json:"id"` + Status string `json:"status"` + Class Class `json:"class"` + Subject Subject `json:"subject"` + Participant []Participant `json:"participant"` + Period Period `json:"period"` + Location []Location `json:"location"` + StatusHistory []StatusHistory `json:"statusHistory"` + ServiceProvider struct { + Reference string `json:"reference"` + } `json:"serviceProvider"` + Identifier []IdentifierEncounter `json:"identifier"` + Hospitalization Hospitalization `json:"hospitalization"` + Diagnosis []DiagnosisEncounter `json:"diagnosis"` +} + +type Hospitalization struct { + DischargeDisposition struct { + Coding []Class `json:"coding"` + Text string `json:"text"` + } `json:"dischargeDisposition"` +} + +type DiagnosisEncounter struct { + Condition Reference `json:"condition"` + Use Type `json:"use"` + Rank int `json:"rank"` +} + +type IdentifierEncounter struct { + System string `json:"system"` + Value string `json:"value"` +} diff --git a/internal/model/episodeofcare.go b/internal/model/episodeofcare.go new file mode 100644 index 0000000..5dcd1b1 --- /dev/null +++ b/internal/model/episodeofcare.go @@ -0,0 +1,30 @@ +package model + +type EpisodeOfCareRequest struct { + Id string `json:"id,omitempty"` + ResourceType string `json:"resourceType"` + Identifier []Identifier `json:"identifier"` + Status string `json:"status"` + StatusHistory []StatusHistory `json:"statusHistory,omitempty"` + Type []Type `json:"type"` + Diagnosis []Diagnosis `json:"diagnosis"` + Patient Reference `json:"patient"` + ManagingOrganization Reference `json:"managingOrganization"` + Period Period `json:"period"` + CareManager Reference `json:"careManager"` +} + +type Period struct { + Start string `json:"start"` + End string `json:"end"` +} + +type Type struct { + Coding []Coding `json:"coding"` +} + +type Diagnosis struct { + Condition Reference `json:"condition"` + Role Type `json:"role,omitempty"` + Rank int `json:"rank,omitempty"` +} diff --git a/internal/model/goal.go b/internal/model/goal.go new file mode 100644 index 0000000..399ee90 --- /dev/null +++ b/internal/model/goal.go @@ -0,0 +1,20 @@ +package model + +type GoalRequest struct { + Id string `json:"id,omitempty"` + ResourceType string `json:"resourceType"` + LifecycleStatus string `json:"lifecycleStatus"` + Category []Category `json:"category,omitempty"` + Description Category `json:"description"` + Subject Reference `json:"subject"` + Target []GoalTarget `json:"target,omitempty"` + StatusDate string `json:"statusDate,omitempty"` + ExpressedBy Reference `json:"expressedBy,omitempty"` + Addresses []Reference `json:"addresses,omitempty"` +} + +type GoalTarget struct { + Measure Category `json:"measure,omitempty"` + DetailCodeableConcept Category `json:"detailCodeableConcept,omitempty"` + DueDate string `json:"dueDate,omitempty"` +} diff --git a/internal/model/imagingstudy.go b/internal/model/imagingstudy.go new file mode 100644 index 0000000..0d92bfc --- /dev/null +++ b/internal/model/imagingstudy.go @@ -0,0 +1,31 @@ +package model + +type ImagingStudyRequest struct { + Id string `json:"id,omitempty"` + ResourceType string `json:"resourceType"` + Identifier []Identifier `json:"identifier" binding:"required"` + Status string `json:"status" binding:"required"` + Modality []Coding `json:"modality"` + Subject Reference `json:"subject" binding:"required"` + Started string `json:"started" binding:"required"` + BasedOn []Reference `json:"basedOn"` //binding:"required" + NumberOfSeries int `json:"numberOfSeries,omitempty"` + NumberOfInstances int `json:"numberOfInstances,omitempty"` + Series []ImagingStudySeries `json:"series,omitempty"` //binding:"required" +} + +type ImagingStudySeries struct { + UID string `json:"uid"` + Number int `json:"number"` + Modality Coding `json:"modality"` + NumberOfInstances int `json:"numberOfInstances"` + Started string `json:"started"` + Instance []ImagingStudySeriesInstance `json:"instance"` +} + +type ImagingStudySeriesInstance struct { + UID string `json:"uid"` + SOPClass Coding `json:"sopClass"` + Number int `json:"number"` + Title string `json:"title"` +} diff --git a/internal/model/immunization.go b/internal/model/immunization.go new file mode 100644 index 0000000..b417f8a --- /dev/null +++ b/internal/model/immunization.go @@ -0,0 +1,30 @@ +package model + +type ImmunizationRequest struct { + Id string `json:"id,omitempty"` + ResourceType string `json:"resourceType"` + Status string `json:"status" binding:"required"` + VaccineCode CodeableConcept `json:"vaccineCode" binding:"required"` + Patient Reference `json:"patient" binding:"required"` + Encounter Reference `json:"encounter"` + OccurrenceDateTime string `json:"occurrenceDateTime" binding:"required"` + ExpirationDate string `json:"expirationDate" binding:"required"` + Recorded string `json:"recorded" binding:"required"` + PrimarySource bool `json:"primarySource"` + Location Reference `json:"location" binding:"required"` + LotNumber string `json:"lotNumber" binding:"required"` + Route CodeableConcept `json:"route" binding:"required"` + DoseQuantity Quantity `json:"doseQuantity" binding:"required"` + Performer []Performers `json:"performer" binding:"required"` + ReasonCode []CodeableConcept `json:"reasonCode" binding:"required"` + ProtocolApplied []ProtocolApplied `json:"protocolApplied" binding:"required"` +} + +type Performers struct { + Function CodeableConcept `json:"function"` + Actor Reference `json:"actor"` +} + +type ProtocolApplied struct { + DoseNumberPositiveInt int `json:"doseNumberPositiveInt"` +} diff --git a/internal/model/medicationdispense.go b/internal/model/medicationdispense.go new file mode 100644 index 0000000..12a0db1 --- /dev/null +++ b/internal/model/medicationdispense.go @@ -0,0 +1,46 @@ +package model + +type MedicationDispenseRequest struct { + Id string `json:"id,omitempty"` + ResourceType string `json:"resourceType"` + Identifier []Identifier `json:"identifier"` + Status string `json:"status" binding:"required"` + Category Category `json:"category"` + MedicationReference Reference `json:"medicationReference" binding:"required"` + Subject Reference `json:"subject" binding:"required"` + Context Reference `json:"context"` + Performer []Performer `json:"performer"` + Location Reference `json:"location" binding:"required"` + AuthorizingPrescription []Reference `json:"authorizingPrescription" binding:"required"` + Quantity Quantity `json:"quantity" binding:"required"` + DaysSupply DaysSupply `json:"daysSupply" binding:"required"` + WhenPrepared string `json:"whenPrepared" binding:"required"` + WhenHandedOver string `json:"whenHandedOver" binding:"required"` + DosageInstruction []Dosage `json:"dosageInstruction"` +} + +type DaysSupply struct { + Value float64 `json:"value"` + Unit string `json:"unit"` + System string `json:"system"` + Code string `json:"code"` +} + +type Dosage struct { + Sequence *int `json:"sequence,omitempty"` + Text string `json:"text"` + Timing Timing `json:"timing"` + DoseAndRate []DoseAndRate `json:"doseAndRate,omitempty"` +} + +type DoseAndRate struct { + Type Category `json:"type"` + DoseQuantity DoseQuantity `json:"doseQuantity"` +} + +type DoseQuantity struct { + Value float64 `json:"value"` + Unit string `json:"unit"` + System string `json:"system"` + Code string `json:"code"` +} diff --git a/internal/model/medicationrequest.go b/internal/model/medicationrequest.go new file mode 100644 index 0000000..a130cd1 --- /dev/null +++ b/internal/model/medicationrequest.go @@ -0,0 +1,44 @@ +package model + +type MedicationRequestRequest struct { + Id string `json:"id,omitempty"` + MedicationRequest *MedicationRequest `json:"medicationRequest,omitempty"` + ResourceType string `json:"resourceType"` + Identifier []Identifier `json:"identifier"` + Status string `json:"status" binding:"required"` + Intent string `json:"intent" binding:"required"` + Category []Category `json:"category"` + Priority string `json:"priority" binding:"required"` + MedicationReference Reference `json:"medicationReference" binding:"required"` + Subject Reference `json:"subject" binding:"required"` + Encounter Reference `json:"encounter"` + AuthoredOn string `json:"authoredOn"` + Requester Reference `json:"requester"` + ReasonCode []Category `json:"reasonCode"` + CourseOfTherapyType Category `json:"courseOfTherapyType"` + DosageInstruction []DosageMR `json:"dosageInstruction"` + DispenseRequest DispenseRequest `json:"dispenseRequest" binding:"required"` +} + +type DosageMR struct { + Sequence int `json:"sequence"` + Text string `json:"text"` + AdditionalInstruction []AdditionalText `json:"additionalInstruction,omitempty"` + PatientInstruction string `json:"patientInstruction,omitempty"` + Timing Timing `json:"timing"` + Route Category `json:"route,omitempty"` + DoseAndRate []DoseAndRate `json:"doseAndRate,omitempty"` +} + +type AdditionalText struct { + Text string `json:"text"` +} + +type DispenseRequest struct { + DispenseInterval Quantity `json:"dispenseInterval"` + ValidityPeriod Period `json:"validityPeriod"` + NumberOfRepeatsAllowed int `json:"numberOfRepeatsAllowed"` + Quantity Quantity `json:"quantity"` + ExpectedSupplyDuration Quantity `json:"expectedSupplyDuration"` + Performer Reference `json:"performer"` +} diff --git a/internal/model/medicationstatement.go b/internal/model/medicationstatement.go new file mode 100644 index 0000000..ceafb02 --- /dev/null +++ b/internal/model/medicationstatement.go @@ -0,0 +1,60 @@ +package model + +type MedicationStatementRequest struct { + Id string `json:"id,omitempty"` + ResourceType string `json:"resourceType"` + Contained []Medication `json:"contained" binding:"required"` + Status string `json:"status" binding:"required"` + Category Category `json:"category"` + MedicationReference Reference `json:"medicationReference" binding:"required"` + Subject Reference `json:"subject" binding:"required"` + Dosage []Dosage `json:"dosage"` + EffectiveDateTime string `json:"effectiveDateTime" binding:"required"` + DateAsserted string `json:"dateAsserted" binding:"required"` + InformationSource Reference `json:"informationSource" binding:"required"` + Context Reference `json:"context"` +} + +type Medication struct { + Code CodeableConcept `json:"code"` + Extension []MedicationExtension `json:"extension"` + Form CodeableConcept `json:"form"` + ID string `json:"id"` + Identifier []Identifier `json:"identifier"` + Ingredient []MedicationIngredient `json:"ingredient"` + Batch MedicationBatch `json:"batch"` + Meta Meta `json:"meta"` + ResourceType string `json:"resourceType"` + Status string `json:"status"` +} + +type MedicationExtension struct { + URL string `json:"url"` + ValueCodeableConcept CodeableConcept `json:"valueCodeableConcept"` +} + +type MedicationIngredient struct { + IsActive bool `json:"isActive"` + ItemCodeableConcept CodeableConcept `json:"itemCodeableConcept"` + Strength MedicationStrength `json:"strength"` +} + +type MedicationStrength struct { + Denominator Quantity `json:"denominator"` + Numerator Quantity `json:"numerator"` +} + +type MedicationBatch struct { + LotNumber string `json:"lotNumber"` + ExpirationDate string `json:"expirationDate"` +} + +type Timing struct { + Repeat TimingRepeat `json:"repeat"` +} + +type TimingRepeat struct { + Frequency int `json:"frequency"` + Period float64 `json:"period"` + PeriodUnit string `json:"periodUnit"` +} diff --git a/internal/model/medicine.go b/internal/model/medicine.go new file mode 100644 index 0000000..55ae1d0 --- /dev/null +++ b/internal/model/medicine.go @@ -0,0 +1,33 @@ +package model + +type MedicineKfaRequest struct { + Page int `form:"page"` + Size int `form:"size"` + ProdustType string `form:"product_type"` + Keyword string `form:"keyword"` +} + +type MedicineKfaResponse struct { + Name string `json:"name"` + KfaCode string `json:"kfa_code"` +} + +type MedicationRequest struct { + ResourceType string `json:"resourceType"` + Meta Meta `json:"meta"` + Identifier []Identifier `json:"identifier"` + Code CodeableConcept `json:"code"` + Status string `json:"status"` + Manufacturer Reference `json:"manufacturer"` + Form CodeableConcept `json:"form"` + Extension []ExtensionMedication `json:"extension"` +} + +type ExtensionMedication struct { + URL string `json:"url"` + ValueCodeableConcept CodeableConcept `json:"valueCodeableConcept"` +} + +// Shared types Meta, Identifier, Coding, CodeableConcept, Reference, and Extension +// are defined in other model files (e.g. patient.go and clinicalImpression.go). +// This file intentionally references those types to avoid redeclaration. diff --git a/internal/model/oauth_model.go b/internal/model/oauth_model.go new file mode 100644 index 0000000..485413c --- /dev/null +++ b/internal/model/oauth_model.go @@ -0,0 +1,23 @@ +package model + +type OauthRequest struct { + ClientId string `json:"client_id"` + ClientSecret string `json:"client_secret"` +} + +type OauthResponse struct { + RefreshTokenExpiresIn string `json:"refresh_token_expires_in"` + APIProductList string `json:"api_product_list"` + APIProductListJson []string `json:"api_product_list_json"` + OrganizationName string `json:"organization_name"` + DeveloperEmail string `json:"developer.email"` + TokenType string `json:"token_type"` + IssuedAt string `json:"issued_at"` + ClientID string `json:"client_id"` + AccessToken string `json:"access_token"` + ApplicationName string `json:"application_name"` + Scope string `json:"scope"` + ExpiresIn string `json:"expires_in"` + RefreshCount string `json:"refresh_count"` + Status string `json:"status"` +} diff --git a/internal/model/observation.go b/internal/model/observation.go new file mode 100644 index 0000000..d508756 --- /dev/null +++ b/internal/model/observation.go @@ -0,0 +1,24 @@ +package model + +type Quantity struct { + Value float64 `json:"value"` + Unit string `json:"unit"` + System string `json:"system"` + Code string `json:"code"` +} + +type ObservationRequest struct { + Id string `json:"id,omitempty"` + ResourceType string `json:"resourceType"` + Status string `json:"status"` + Category []CodeableConcept `json:"category"` + Code CodeableConcept `json:"code"` + Subject Reference `json:"subject"` + Performer []Reference `json:"performer"` + Specimen *Reference `json:"specimen,omitempty"` + BasedOn []Reference `json:"basedOn"` + Encounter Reference `json:"encounter"` + EffectiveDateTime string `json:"effectiveDateTime"` + Issued string `json:"issued"` + ValueQuantity *Quantity `json:"valueQuantity,omitempty"` +} diff --git a/internal/model/organization.go b/internal/model/organization.go new file mode 100644 index 0000000..6e9e99f --- /dev/null +++ b/internal/model/organization.go @@ -0,0 +1,38 @@ +package model + +type Telecom struct { + System string `json:"system"` + Value string `json:"value"` + Use string `json:"use"` +} + +type AddressExtensionValue struct { + Url string `json:"url"` + ValueCode string `json:"valueCode,omitempty"` +} + +type AddressExtension struct { + Url string `json:"url"` + Extension []AddressExtensionValue `json:"extension"` +} + +type Address struct { + Use string `json:"use"` + Type string `json:"type"` + Line []string `json:"line"` + City string `json:"city"` + PostalCode string `json:"postalCode"` + Country string `json:"country"` + Extension []AddressExtension `json:"extension"` +} + +type OrganizationRequest struct { + ResourceType string `json:"resourceType"` + Active bool `json:"active"` + Identifier []Identifier `json:"identifier"` + Type []CodeableConcept `json:"type"` + Name string `json:"name"` + Telecom []Telecom `json:"telecom"` + Address []Address `json:"address"` + PartOf Reference `json:"partOf"` +} diff --git a/internal/model/patient.go b/internal/model/patient.go new file mode 100644 index 0000000..40c6dad --- /dev/null +++ b/internal/model/patient.go @@ -0,0 +1,90 @@ +package model + +type PatientRequest struct { + ResourceType string `json:"resourceType"` + Meta Meta `json:"meta"` + Identifier []Identifier `json:"identifier"` + Active bool `json:"active"` + Name []HumanName `json:"name"` + Telecom []Identifier `json:"telecom"` + Gender string `json:"gender"` + BirthDate string `json:"birthDate"` + DeceasedBoolean bool `json:"deceasedBoolean"` + Address []AddressObject `json:"address"` + MaritalStatus MaritalStatus `json:"maritalStatus"` + MultipleBirthInteger int `json:"multipleBirthInteger"` + Contact []ContactObject `json:"contact"` + Communication []CommunicationObject `json:"communication"` + Extension []Extension `json:"extension"` +} + +type Meta struct { + Profile []string `json:"profile"` +} +type Identifier struct { + Use string `json:"use,omitempty"` + Type *CodeableConcept `json:"type,omitempty"` + System string `json:"system,omitempty"` + Value string `json:"value"` +} +type HumanName struct { + Use string `json:"use"` + Text string `json:"text"` +} +type AddressObject struct { + Use string `json:"use"` + Line []string `json:"line"` + City string `json:"city"` + PostalCode string `json:"postalCode"` + Country string `json:"country"` + Extension []ExtensionObject `json:"extension"` +} + +type Extension struct { + URL string `json:"url"` + Extension []struct { + URL string `json:"url"` + ValueCode string `json:"valueCode"` + } `json:"extension"` +} + +type MaritalStatus struct { + Coding []struct { + System string `json:"system"` + Code string `json:"code"` + Display string `json:"display"` + } `json:"coding"` + Text string `json:"text"` +} + +type ContactObject struct { + Relationship []struct { + Coding []struct { + System string `json:"system"` + Code string `json:"code"` + } `json:"coding"` + } + Name HumanName `json:"name"` + Telecom []Identifier `json:"telecom"` +} + +type CommunicationObject struct { + Language struct { + Coding []struct { + System string `json:"system"` + Code string `json:"code"` + Display string `json:"display"` + } `json:"coding"` + Text string `json:"text"` + } `json:"language"` + Preferred bool `json:"preferred"` +} + +type ExtensionObject struct { + URL string `json:"url"` + ValueCode *string `json:"valueCode"` + ValueAddress *struct { + Country string `json:"country"` + City string `json:"city"` + } `json:"valueAddress"` +} diff --git a/internal/model/practitioner.go b/internal/model/practitioner.go new file mode 100644 index 0000000..8b53790 --- /dev/null +++ b/internal/model/practitioner.go @@ -0,0 +1 @@ +package model diff --git a/internal/model/procedure.go b/internal/model/procedure.go new file mode 100644 index 0000000..c43a479 --- /dev/null +++ b/internal/model/procedure.go @@ -0,0 +1,24 @@ +package model + +type ProcedureRequest struct { + ID string `json:"id"` + ResourceType string `json:"resourceType"` + Status string `json:"status"` + Category CodeableConcept `json:"category"` + Code CodeableConcept `json:"code"` + Subject Reference `json:"subject"` + Encounter Reference `json:"encounter"` + PerformedPeriod Period `json:"performedPeriod"` + Performer []Performer `json:"performer"` + ReasonCode []CodeableConcept `json:"reasonCode"` + BodySite []CodeableConcept `json:"bodySite"` + Note []Annotation `json:"note"` +} + +type Performer struct { + Actor Reference `json:"actor"` +} + +type Annotation struct { + Text string `json:"text"` +} diff --git a/internal/model/questionnaireresponse.go b/internal/model/questionnaireresponse.go new file mode 100644 index 0000000..bd62cdf --- /dev/null +++ b/internal/model/questionnaireresponse.go @@ -0,0 +1,24 @@ +package model + +type QuestionnaireResponseRequest struct { + Id string `json:"id,omitempty"` + ResourceType string `json:"resourceType"` + Questionnaire string `json:"questionnaire" binding:"required"` + Status string `json:"status" binding:"required"` + Subject Reference `json:"subject" binding:"required"` + Encounter Reference `json:"encounter"` + Authored string `json:"authored" binding:"required"` + Author Reference `json:"author" binding:"required"` + Source Reference `json:"source" binding:"required"` + Item []QRItem `json:"item" binding:"required"` +} + +type QRItem struct { + LinkID string `json:"linkId"` + Text string `json:"text"` + Answer []QRAnswer `json:"answer"` +} + +type QRAnswer struct { + ValueCoding Coding `json:"valueCoding"` +} diff --git a/internal/model/satusehat_akses.go b/internal/model/satusehat_akses.go new file mode 100644 index 0000000..b38b730 --- /dev/null +++ b/internal/model/satusehat_akses.go @@ -0,0 +1,10 @@ +package model + +type Akses struct { + AuthUrl string + BaseUrl string + ConsentUrl string + KfaUrl string + ClientId string + ClientSecret string +} diff --git a/internal/model/servicerequest.go b/internal/model/servicerequest.go new file mode 100644 index 0000000..64bca12 --- /dev/null +++ b/internal/model/servicerequest.go @@ -0,0 +1,40 @@ +package model + +type ServiceRequest struct { + Id string `json:"id,omitempty"` + ResourceType string `json:"resourceType"` + Identifier []Identifier `json:"identifier"` + Status string `json:"status"` + Intent string `json:"intent"` + Priority string `json:"priority"` + Category []Category `json:"category"` + Code Code `json:"code"` + Subject Reference `json:"subject"` + Encounter Reference `json:"encounter"` + OccurrenceDateTime string `json:"occurrenceDateTime"` + AuthoredOn string `json:"authoredOn"` + Requester Reference `json:"requester"` + Performer []Reference `json:"performer"` + ReasonCode []ReasonCode `json:"reasonCode"` + LocationCode []LocationCode `json:"locationCode"` + LocationReference []Reference `json:"locationReference"` + PatientInstruction string `json:"patientInstruction"` +} + +type Category struct { + Coding []Coding `json:"coding"` +} + +type Code struct { + Coding []Coding `json:"coding"` + Text string `json:"text"` +} + +type ReasonCode struct { + Coding []Coding `json:"coding"` + Text string `json:"text"` +} + +type LocationCode struct { + Coding []Coding `json:"coding"` +} diff --git a/internal/model/specimen.go b/internal/model/specimen.go new file mode 100644 index 0000000..519dab9 --- /dev/null +++ b/internal/model/specimen.go @@ -0,0 +1,33 @@ +package model + +type SpecimenRequest struct { + Id string `json:"id,omitempty"` + ResourceType string `json:"resourceType"` + Identifier []Identifier `json:"identifier"` + Status string `json:"status"` + Type SpecimenType `json:"type"` + Collection SpecimenCollection `json:"collection"` + Subject Reference `json:"subject"` + Request []Reference `json:"request"` + ReceivedTime string `json:"receivedTime"` + Extension []SpecimenExtension `json:"extension"` +} + +type SpecimenType struct { + Coding []Coding `json:"coding"` +} + +type SpecimenCollection struct { + CollectedDateTime string `json:"collectedDateTime"` + Extension []SpecimenCollectionExtension `json:"extension"` +} + +type SpecimenCollectionExtension struct { + URL string `json:"url"` + ValueReference Reference `json:"valueReference"` +} + +type SpecimenExtension struct { + URL string `json:"url"` + ValueDateTime string `json:"valueDateTime,omitempty"` +} diff --git a/internal/routes/allergancytoleran.go b/internal/routes/allergancytoleran.go new file mode 100644 index 0000000..a1a6425 --- /dev/null +++ b/internal/routes/allergancytoleran.go @@ -0,0 +1,14 @@ +package routes + +import ( + "satusehat-rssa/internal/handler" + "satusehat-rssa/internal/midleware" + + "github.com/gin-gonic/gin" +) + +func AllergancyToleran(e *gin.Engine, allergancyToleranHandler *handler.AllergancyToleranHandler, m midleware.AuthMiddleware) { + r := e.Group("AllergyIntolerance") + r.POST("/", allergancyToleranHandler.CreateAllergancyToleran) + r.PUT("/:id", allergancyToleranHandler.UpdateAllergancyToleran) +} diff --git a/internal/routes/careplan.go b/internal/routes/careplan.go new file mode 100644 index 0000000..df30b35 --- /dev/null +++ b/internal/routes/careplan.go @@ -0,0 +1,14 @@ +package routes + +import ( + "satusehat-rssa/internal/handler" + "satusehat-rssa/internal/midleware" + + "github.com/gin-gonic/gin" +) + +func CarePlan(e *gin.Engine, cphandler *handler.CarePlanHandler, m midleware.AuthMiddleware) { + r := e.Group("CarePlan") + r.POST("/", cphandler.CreateCarePlan) + r.PUT("/:id", cphandler.UpdateCarePlan) +} diff --git a/internal/routes/clinicalimpression.go b/internal/routes/clinicalimpression.go new file mode 100644 index 0000000..11e9d31 --- /dev/null +++ b/internal/routes/clinicalimpression.go @@ -0,0 +1,15 @@ +package routes + +import ( + "satusehat-rssa/internal/handler" + "satusehat-rssa/internal/midleware" + + "github.com/gin-gonic/gin" +) + +func ClinicalImpression(e *gin.Engine, clinicalImpressionHandler *handler.ClinicalImpressionHandler, m midleware.AuthMiddleware) { + r := e.Group("ClinicalImpression") + r.POST("/", clinicalImpressionHandler.CreateClinicalImpression) + r.PUT("/:id", clinicalImpressionHandler.UpdateClinicalImpression) + // Add more routes as needed +} diff --git a/internal/routes/composition.go b/internal/routes/composition.go new file mode 100644 index 0000000..3dddd8e --- /dev/null +++ b/internal/routes/composition.go @@ -0,0 +1,13 @@ +package routes + +import ( + "satusehat-rssa/internal/handler" + "satusehat-rssa/internal/midleware" + + "github.com/gin-gonic/gin" +) + +func Composition(r *gin.Engine, h *handler.CompositionHandler, m midleware.AuthMiddleware) { + r.POST("/compositions/", h.CreateComposition) + r.PUT("/compositions/:id", h.UpdateComposition) +} diff --git a/internal/routes/condition.go b/internal/routes/condition.go new file mode 100644 index 0000000..df0d608 --- /dev/null +++ b/internal/routes/condition.go @@ -0,0 +1,16 @@ +package routes + +import ( + "satusehat-rssa/internal/handler" + "satusehat-rssa/internal/midleware" + + "github.com/gin-gonic/gin" +) + +func Condition(e *gin.Engine, conditionHandler *handler.ConditionHandler, m midleware.AuthMiddleware) { + r := e.Group("Condition") + r.POST("/", conditionHandler.CreateCondition) + r.GET("/", conditionHandler.GetConditionByPatient) + r.PUT("/:id", conditionHandler.UpdateCondition) + // Add more routes as needed +} diff --git a/internal/routes/diagnosisreport.go b/internal/routes/diagnosisreport.go new file mode 100644 index 0000000..29f5cde --- /dev/null +++ b/internal/routes/diagnosisreport.go @@ -0,0 +1,14 @@ +package routes + +import ( + "satusehat-rssa/internal/handler" + "satusehat-rssa/internal/midleware" + + "github.com/gin-gonic/gin" +) + +func DiagnosisReport(e *gin.Engine, h *handler.DiagnosisReportHandler, m midleware.AuthMiddleware) { + group := e.Group("/diagnosis-report") + group.POST("/", h.CreateDiagnosisReport) + group.PUT("/:id", h.UpdateDiagnosisReport) +} diff --git a/internal/routes/encounter.go b/internal/routes/encounter.go new file mode 100644 index 0000000..16a8f44 --- /dev/null +++ b/internal/routes/encounter.go @@ -0,0 +1,15 @@ +package routes + +import ( + "satusehat-rssa/internal/handler" + "satusehat-rssa/internal/midleware" + + "github.com/gin-gonic/gin" +) + +func Encounter(e *gin.Engine, encounterHandler *handler.EncounterHandler, m midleware.AuthMiddleware) { + r := e.Group("Encounter") + r.POST("/", encounterHandler.CreateEncounter) + r.GET("/", encounterHandler.GetEncounterByPatient) + r.PUT("/:id", encounterHandler.UpdateEncounter) +} diff --git a/internal/routes/episodeofcare.go b/internal/routes/episodeofcare.go new file mode 100644 index 0000000..451e3cb --- /dev/null +++ b/internal/routes/episodeofcare.go @@ -0,0 +1,15 @@ +package routes + +import ( + "satusehat-rssa/internal/handler" + "satusehat-rssa/internal/midleware" + + "github.com/gin-gonic/gin" +) + +func EpisodeOfCare(e *gin.Engine, episodeOfCareHandler *handler.EpisodeOfCareHandler, m midleware.AuthMiddleware) { + r := e.Group("EpisodeOfCare") + r.POST("/", episodeOfCareHandler.CreateEpisodeOfCare) + r.GET("/", episodeOfCareHandler.GetEpisodeOfCareByPatient) + r.PUT("/:id", episodeOfCareHandler.UpdateEpisodeOfCare) +} diff --git a/internal/routes/goal.go b/internal/routes/goal.go new file mode 100644 index 0000000..ab134f1 --- /dev/null +++ b/internal/routes/goal.go @@ -0,0 +1,14 @@ +package routes + +import ( + "satusehat-rssa/internal/handler" + "satusehat-rssa/internal/midleware" + + "github.com/gin-gonic/gin" +) + +func Goal(e *gin.Engine, cphandler *handler.GoalHandler, m midleware.AuthMiddleware) { + r := e.Group("Goal") + r.POST("/", cphandler.CreateGoal) + r.PUT("/:id", cphandler.UpdateGoal) +} diff --git a/internal/routes/imagingstudy.go b/internal/routes/imagingstudy.go new file mode 100644 index 0000000..eb8e535 --- /dev/null +++ b/internal/routes/imagingstudy.go @@ -0,0 +1,14 @@ +package routes + +import ( + "satusehat-rssa/internal/handler" + "satusehat-rssa/internal/midleware" + + "github.com/gin-gonic/gin" +) + +func ImagingStudy(e *gin.Engine, handler *handler.ImagingStudyHandler, m midleware.AuthMiddleware) { + r := e.Group("ImagingStudy") + r.POST("/", handler.CreateImagingStudy) + r.PUT("/:id", handler.UpdateImagingStudy) +} diff --git a/internal/routes/immunization.go b/internal/routes/immunization.go new file mode 100644 index 0000000..9636ac1 --- /dev/null +++ b/internal/routes/immunization.go @@ -0,0 +1,14 @@ +package routes + +import ( + "satusehat-rssa/internal/handler" + "satusehat-rssa/internal/midleware" + + "github.com/gin-gonic/gin" +) + +func Immunization(e *gin.Engine, cphandler *handler.ImmunizationHandler, m midleware.AuthMiddleware) { + r := e.Group("Immunization") + r.POST("/", cphandler.CreateImmunization) + r.PUT("/:id", cphandler.UpdateImmunization) +} diff --git a/internal/routes/medicationdispense.go b/internal/routes/medicationdispense.go new file mode 100644 index 0000000..8b60895 --- /dev/null +++ b/internal/routes/medicationdispense.go @@ -0,0 +1,14 @@ +package routes + +import ( + "satusehat-rssa/internal/handler" + "satusehat-rssa/internal/midleware" + + "github.com/gin-gonic/gin" +) + +func MedicationDispense(e *gin.Engine, cphandler *handler.MedicationDispenseHandler, m midleware.AuthMiddleware) { + r := e.Group("MedicationDispense") + r.POST("/", cphandler.CreateMedicationDispense) + r.PUT("/:id", cphandler.UpdateMedicationDispense) +} diff --git a/internal/routes/medicationrequest.go b/internal/routes/medicationrequest.go new file mode 100644 index 0000000..46bf9c1 --- /dev/null +++ b/internal/routes/medicationrequest.go @@ -0,0 +1,14 @@ +package routes + +import ( + "satusehat-rssa/internal/handler" + "satusehat-rssa/internal/midleware" + + "github.com/gin-gonic/gin" +) + +func MedicationRequest(e *gin.Engine, cphandler *handler.MedicationRequestHandler, m midleware.AuthMiddleware) { + r := e.Group("MedicationRequest") + r.POST("/", cphandler.CreateMedicationRequest) + r.PUT("/:id", cphandler.UpdateMedicationRequest) +} diff --git a/internal/routes/medicationstatement.go b/internal/routes/medicationstatement.go new file mode 100644 index 0000000..5108741 --- /dev/null +++ b/internal/routes/medicationstatement.go @@ -0,0 +1,14 @@ +package routes + +import ( + "satusehat-rssa/internal/handler" + "satusehat-rssa/internal/midleware" + + "github.com/gin-gonic/gin" +) + +func MedicationStatement(e *gin.Engine, cphandler *handler.MedicationStatementHandler, m midleware.AuthMiddleware) { + r := e.Group("MedicationStatement") + r.POST("/", cphandler.CreateMedicationStatement) + r.PUT("/:id", cphandler.UpdateMedicationStatement) +} diff --git a/internal/routes/medicine.go b/internal/routes/medicine.go new file mode 100644 index 0000000..4657317 --- /dev/null +++ b/internal/routes/medicine.go @@ -0,0 +1,14 @@ +package routes + +import ( + "satusehat-rssa/internal/handler" + "satusehat-rssa/internal/midleware" + + "github.com/gin-gonic/gin" +) + +func MedicineKfa(e *gin.Engine, medicineHandler *handler.MedicineHandler, m midleware.AuthMiddleware) { + r := e.Group("MedicineKfa") + r.GET("", medicineHandler.GetMedicineKfa) + r.GET("/:kfa_code", medicineHandler.GetMedicineByKfaCode) +} diff --git a/internal/routes/oauth.go b/internal/routes/oauth.go new file mode 100644 index 0000000..b8bc9b2 --- /dev/null +++ b/internal/routes/oauth.go @@ -0,0 +1,13 @@ +package routes + +import ( + "satusehat-rssa/internal/handler" + "satusehat-rssa/internal/midleware" + + "github.com/gin-gonic/gin" +) + +func Oauth(e *gin.Engine, oauthHandler *handler.OauthHandler, m midleware.AuthMiddleware) { + r := e.Group("accesstoken") + r.POST("/", oauthHandler.GenerateToken) +} diff --git a/internal/routes/observation.go b/internal/routes/observation.go new file mode 100644 index 0000000..2977d5d --- /dev/null +++ b/internal/routes/observation.go @@ -0,0 +1,16 @@ +package routes + +import ( + "satusehat-rssa/internal/handler" + "satusehat-rssa/internal/midleware" + + "github.com/gin-gonic/gin" +) + +func Observation(e *gin.Engine, observationHandler *handler.ObservationHandler, m midleware.AuthMiddleware) { + // Define the routes for observation + router := e.Group("/observation") + router.POST("/", observationHandler.CreateObservation) + router.GET("/", observationHandler.GetObservationByPatient) + router.PUT("/:id", observationHandler.UpdateObservation) +} diff --git a/internal/routes/organization.go b/internal/routes/organization.go new file mode 100644 index 0000000..c72dfb9 --- /dev/null +++ b/internal/routes/organization.go @@ -0,0 +1,14 @@ +package routes + +import ( + "satusehat-rssa/internal/handler" + "satusehat-rssa/internal/midleware" + + "github.com/gin-gonic/gin" +) + +func Organization(e *gin.Engine, handler *handler.OrganizationHandler, m midleware.AuthMiddleware) { + r := e.Group("Organization") + r.POST("/", handler.CreateOrganization) + r.GET("/", handler.GetOrganizationPatient) +} diff --git a/internal/routes/patient.go b/internal/routes/patient.go new file mode 100644 index 0000000..b92d064 --- /dev/null +++ b/internal/routes/patient.go @@ -0,0 +1,14 @@ +package routes + +import ( + "satusehat-rssa/internal/handler" + "satusehat-rssa/internal/midleware" + + "github.com/gin-gonic/gin" +) + +func Patient(e *gin.Engine, patientHandler *handler.PatientHandler, m midleware.AuthMiddleware) { + r := e.Group("Patient") + r.GET("/", patientHandler.GetPatientByNIK) + r.POST("/", patientHandler.CreatePatient) +} diff --git a/internal/routes/practitioner.go b/internal/routes/practitioner.go new file mode 100644 index 0000000..c0d8ce7 --- /dev/null +++ b/internal/routes/practitioner.go @@ -0,0 +1,13 @@ +package routes + +import ( + "satusehat-rssa/internal/handler" + "satusehat-rssa/internal/midleware" + + "github.com/gin-gonic/gin" +) + +func Practicioner(e *gin.Engine, practicionerHandler *handler.PracticionerHandler, m midleware.AuthMiddleware) { + r := e.Group("Practitioner") + r.GET("/", practicionerHandler.GetPracticionerByNik) +} diff --git a/internal/routes/procedure.go b/internal/routes/procedure.go new file mode 100644 index 0000000..1672198 --- /dev/null +++ b/internal/routes/procedure.go @@ -0,0 +1,13 @@ +package routes + +import ( + "github.com/gin-gonic/gin" + "satusehat-rssa/internal/handler" + "satusehat-rssa/internal/midleware" +) + +func Procedure(r *gin.Engine, h *handler.ProcedureHandler, m midleware.AuthMiddleware) { + r.POST("/procedures", h.CreateProcedure) + r.GET("/procedures/:id", h.GetProcedure) + r.PUT("/procedures/:id", h.UpdateProcedure) +} diff --git a/internal/routes/questionnaireresponse.go b/internal/routes/questionnaireresponse.go new file mode 100644 index 0000000..792b76a --- /dev/null +++ b/internal/routes/questionnaireresponse.go @@ -0,0 +1,14 @@ +package routes + +import ( + "satusehat-rssa/internal/handler" + "satusehat-rssa/internal/midleware" + + "github.com/gin-gonic/gin" +) + +func QuestionnaireResponse(e *gin.Engine, cphandler *handler.QuestionnaireResponseHandler, m midleware.AuthMiddleware) { + r := e.Group("QuestionnaireResponse") + r.POST("/", cphandler.CreateQuestionnaireResponse) + r.PUT("/:id", cphandler.UpdateQuestionnaireResponse) +} diff --git a/internal/routes/servicerequest.go b/internal/routes/servicerequest.go new file mode 100644 index 0000000..d29107a --- /dev/null +++ b/internal/routes/servicerequest.go @@ -0,0 +1,15 @@ +package routes + +import ( + "satusehat-rssa/internal/handler" + "satusehat-rssa/internal/midleware" + + "github.com/gin-gonic/gin" +) + +func ServiceRequest(e *gin.Engine, h *handler.ServiceRequestHandler, m midleware.AuthMiddleware) { + group := e.Group("/service-request") + group.POST("/", h.CreateServiceRequest) + group.GET("/", h.GetServiceRequestByPatient) + group.PUT("/:id", h.UpdateServiceRequest) +} diff --git a/internal/routes/specimen.go b/internal/routes/specimen.go new file mode 100644 index 0000000..12f339d --- /dev/null +++ b/internal/routes/specimen.go @@ -0,0 +1,15 @@ +package routes + +import ( + "satusehat-rssa/internal/handler" + "satusehat-rssa/internal/midleware" + + "github.com/gin-gonic/gin" +) + +func Specimen(e *gin.Engine, h *handler.SpecimenHandler, m midleware.AuthMiddleware) { + group := e.Group("/specimen") + group.POST("/", h.CreateSpecimen) + group.GET("/", h.GetSpecimenByPatient) + group.PUT("/:id", h.UpdateSpecimen) +} diff --git a/openapi/Dockerfile b/openapi/Dockerfile new file mode 100644 index 0000000..49a8e00 --- /dev/null +++ b/openapi/Dockerfile @@ -0,0 +1,10 @@ +# Gunakan image resmi Swagger UI +FROM swaggerapi/swagger-ui:v5.11.8 + +RUN mkdir swag +# Copy file .yaml ke dalam image +COPY ./openapi_satusehat.yaml /swag/openapi_satusehat.yaml + + +# Konfigurasi Swagger UI untuk membaca file .yaml Anda +ENV SWAGGER_JSON=/swag/openapi_satusehat.yaml \ No newline at end of file diff --git a/openapi/openapi_satusehat.yaml b/openapi/openapi_satusehat.yaml new file mode 100644 index 0000000..5112b95 --- /dev/null +++ b/openapi/openapi_satusehat.yaml @@ -0,0 +1,2866 @@ +openapi: 3.0.3 +info: + title: Integrasi-API-Satusehat + version: 1.0.0 + description: Dokumentasi Integrasi Satusehat ke OpenAPI 3.0 +servers: +- url: https://satusehat-api.multy.chat +paths: + /compositions: + post: + summary: Composition - Create + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Composition' + responses: + '200': + description: OK + /compositions/{id}: + put: + summary: Composition - Update + parameters: + - name: id + in: path + required: true + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Composition' + responses: + '200': + description: OK + /procedures: + post: + summary: Procedure - Create + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Procedure' + responses: + '200': + description: OK + /procedures/{id}: + put: + summary: Procedure - Update + parameters: + - name: id + in: path + required: true + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Procedure' + responses: + '200': + description: OK + /MedicineKfa: + get: + summary: Medicine-Kfa + responses: + '200': + description: OK + parameters: + - name: page + in: query + required: false + schema: + type: string + example: '1' + - name: size + in: query + required: false + schema: + type: string + example: '1000' + - name: product_type + in: query + required: false + schema: + type: string + example: farmasi + - name: keyword + in: query + required: false + schema: + type: string + example: ibuprofen + /MedicineKfa/{kfa_code}: + get: + summary: Medicine-Kfa - Get by KFA code + operationId: satusehat-rssa/internal/handler.MedicineHandler.GetMedicineByKfaCode-fm + parameters: + - name: kfa_code + in: path + required: true + schema: + type: string + description: KFA code of the medicine + responses: + '200': + description: OK + /ClinicalImpression: + post: + summary: ClinicalImpression + responses: + '200': + description: OK + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ClinicalImpression' + /ClinicalImpression/{id}: + put: + summary: ClinicalImpression - Update + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ClinicalImpression' + /AllergyIntolerance: + post: + summary: Alergi Makanan - Obat + responses: + '200': + description: OK + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AllergyIntolerance' + /AllergyIntolerance/{id}: + put: + summary: AllergyIntolerance - Update + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AllergyIntolerance' + /Encounter: + post: + summary: Encounter - Create + responses: + '200': + description: OK + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Encounter' + get: + summary: Encounter - by Subject + responses: + '200': + description: OK + parameters: + - name: subject + in: query + required: false + schema: + type: string + example: P20395297822 + /Encounter/{id}: + put: + summary: Encounter - Update + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/EncounterUpdateRequest' + /Practitioner: + get: + summary: Practitioner - Search NIK + responses: + '200': + description: OK + parameters: + - name: identifier + in: query + required: false + schema: + type: string + example: https://fhir.kemkes.go.id/id/nik|3322071302900002 + /Patient: + get: + summary: Patient - Search NIK + responses: + '200': + description: OK + parameters: + - name: identifier + in: query + required: false + schema: + type: string + example: https://fhir.kemkes.go.id/id/nik|3509087611003004 + post: + summary: Patient - Create by NIK + responses: + '200': + description: OK + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Patient' + /Organization: + post: + summary: Organization - Create + responses: + '200': + description: OK + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Organization' + /observation: + post: + summary: Observation - Create + responses: + '200': + description: OK + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Observation' + /observation/{id}: + put: + summary: Observation - Update + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Observation' + /Condition: + post: + summary: Condition - Diagnosis + responses: + '200': + description: OK + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Condition' + /Condition/{id}: + put: + summary: Condition - Update + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Condition' + /service-request: + post: + summary: ServiceRequest - Laboratorium Non Rujukan Eksternal + responses: + '200': + description: OK + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ServiceRequest' + /service-request/{id}: + put: + summary: ServiceRequest - Update + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ServiceRequest' + /EpisodeOfCare: + get: + summary: EpisodeOfCare - Search by Patient + responses: + '200': + description: OK + parameters: + - name: patient + in: query + required: false + schema: + type: string + example: 100000030009 + post: + summary: EpisodeOfCare - Create + responses: + '200': + description: OK + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/EpisodeOfCare' + /EpisodeOfCare/{id}: + put: + summary: EpisodeOfCare - Update + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/EpisodeOfCare' + /specimen: + post: + summary: Specimen - Create (Non Rujukan) + responses: + '200': + description: OK + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Specimen' + /specimen/{id}: + put: + summary: Specimen - Update + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Specimen' + /diagnosis-report: + post: + summary: DiagnosticReport - Create + responses: + '200': + description: OK + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/DiagnosticReport' + /diagnosis-report/{id}: + put: + summary: DiagnosticReport - Update + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/DiagnosticReport' + /CarePlan: + post: + summary: CarePlan - Create + responses: + '200': + description: OK + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CarePlan' + /CarePlan/{id}: + put: + summary: CarePlan - Update + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CarePlan' + /QuestionnaireResponse: + post: + summary: QuestionnaireResponse - Create + responses: + '200': + description: OK + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/QuestionnaireResponse' + /QuestionnaireResponse/{id}: + put: + summary: QuestionnaireResponse - Update + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/QuestionnaireResponse' + /MedicationStatement: + post: + summary: MedicationStatement - Create + responses: + '200': + description: OK + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/MedicationStatement' + /MedicationStatement/{id}: + put: + summary: MedicationStatement - Update + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/MedicationStatement' + /Immunization: + post: + summary: Immunization - Create + responses: + '200': + description: OK + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Immunization' + /Immunization/{id}: + put: + summary: Immunization - Update + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Immunization' + /ImagingStudy: + post: + summary: ImagingStudy - Create + responses: + '200': + description: OK + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ImagingStudy' + /ImagingStudy/{id}: + put: + summary: ImagingStudy - Update + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ImagingStudy' + /MedicationRequest: + post: + summary: MedicationRequest - Create + responses: + '200': + description: OK + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/MedicationRequest' + /MedicationRequest/{id}: + put: + summary: MedicationRequest - Update + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/MedicationRequest' + /MedicationDispense: + post: + summary: MedicationDispense - Create + responses: + '200': + description: OK + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/MedicationDispense' + /MedicationDispense/{id}: + put: + summary: MedicationDispense - Update + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/MedicationDispense' + /Goal: + post: + summary: Goal - Create + responses: + '200': + description: OK + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Goal' + /Goal/{id}: + put: + summary: Goal - Update + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Goal' +components: + securitySchemes: + ApiKeyAuth: + type: apiKey + in: header + name: x-api-key + schemas: + Composition: + type: object + required: + - status + - type + - category + - subject + - date + - author + - title + properties: + status: + type: string + type: + $ref: '#/components/schemas/CodeableConcept' + category: + type: array + items: + $ref: '#/components/schemas/CodeableConcept' + subject: + $ref: '#/components/schemas/Reference' + encounter: + $ref: '#/components/schemas/Reference' + date: + type: string + author: + type: array + items: + $ref: '#/components/schemas/Reference' + title: + type: string + custodian: + $ref: '#/components/schemas/Reference' + section: + type: array + items: + $ref: '#/components/schemas/Section' + anamnesis: + type: string + pemeriksaanFisik: + type: string + pemeriksaanPenunjang: + type: string + medikamentosa: + type: string + lanjutanPenatalaksanaan: + type: string + Identifier: + type: object + properties: + system: + type: string + value: + type: string + Section: + type: object + properties: + code: + $ref: '#/components/schemas/CodeableConcept' + text: + $ref: '#/components/schemas/SectionText' + SectionText: + type: object + properties: + status: + type: string + div: + type: string + Procedure: + type: object + required: + - status + - category + - code + - subject + - performedPeriod + - performer + - reasonCode + - bodySite + - note + properties: + status: + type: string + category: + $ref: '#/components/schemas/CodeableConcept' + code: + $ref: '#/components/schemas/CodeableConcept' + subject: + $ref: '#/components/schemas/Reference' + encounter: + $ref: '#/components/schemas/Reference' + performedPeriod: + $ref: '#/components/schemas/Period' + performer: + type: array + items: + $ref: '#/components/schemas/Performer' + reasonCode: + type: array + items: + $ref: '#/components/schemas/CodeableConcept' + bodySite: + type: array + items: + $ref: '#/components/schemas/CodeableConcept' + note: + type: array + items: + $ref: '#/components/schemas/Annotation' + CodeableConcept: + type: object + properties: + coding: + type: array + items: + $ref: '#/components/schemas/Coding' + text: + type: string + Coding: + type: object + properties: + system: + type: string + code: + type: string + display: + type: string + Reference: + type: object + properties: + reference: + type: string + display: + type: string + Period: + type: object + properties: + start: + type: string + end: + type: string + Performer: + type: object + properties: + actor: + $ref: '#/components/schemas/Reference' + Annotation: + type: object + properties: + text: + type: string + ClinicalImpression: + type: object + required: + - status + - description + - subject + - effectiveDateTime + - date + - assessor + - problem + properties: + status: + type: string + description: + type: string + subject: + type: object + properties: + reference: + type: string + display: + type: string + encounter: + type: object + properties: + reference: + type: string + display: + type: string + effectiveDateTime: + type: string + date: + type: string + assessor: + type: object + properties: + reference: + type: string + problem: + type: array + items: + type: object + properties: + reference: + type: string + investigation: + type: array + items: + type: object + properties: + code: + type: object + properties: + text: + type: string + item: + type: array + items: + type: object + properties: + reference: + type: string + summary: + type: string + finding: + type: array + items: + type: object + properties: + itemCodeableConcept: + type: object + properties: + coding: + type: array + items: + type: object + properties: + system: + type: string + code: + type: string + display: + type: string + itemReference: + type: object + properties: + reference: + type: string + prognosisCodeableConcept: + type: array + items: + type: object + properties: + coding: + type: array + items: + type: object + properties: + system: + type: string + code: + type: string + display: + type: string + AllergyIntolerance: + type: object + required: + - clinicalStatus + - verificationStatus + - category + - code + - patient + - recordedDate + - recorder + properties: + clinicalStatus: + type: object + properties: + coding: + type: array + items: + type: object + properties: + system: + type: string + code: + type: string + display: + type: string + verificationStatus: + type: object + properties: + coding: + type: array + items: + type: object + properties: + system: + type: string + code: + type: string + display: + type: string + category: + type: array + items: + type: string + code: + type: object + properties: + coding: + type: array + items: + type: object + properties: + system: + type: string + code: + type: string + display: + type: string + text: + type: string + patient: + type: object + properties: + reference: + type: string + recordedDate: + type: string + recorder: + type: object + properties: + reference: + type: string + Encounter: + type: object + required: + - status + - class + - subject + - participant + - period + properties: + status: + type: string + class: + type: object + properties: + system: + type: string + code: + type: string + display: + type: string + subject: + type: object + properties: + reference: + type: string + display: + type: string + participant: + type: array + items: + type: object + properties: + type: + type: array + items: + type: object + properties: + coding: + type: array + items: + type: object + properties: + system: + type: string + code: + type: string + display: + type: string + individual: + type: object + properties: + reference: + type: string + display: + type: string + period: + type: object + properties: + start: + type: string + location: + type: array + items: + type: object + properties: + location: + type: object + properties: + reference: + type: string + display: + type: string + statusHistory: + type: array + items: + type: object + properties: + status: + type: string + period: + type: object + properties: + start: + type: string + serviceProvider: + type: object + properties: + reference: + type: string + EncounterUpdateRequest: + type: object + properties: + resourceType: + type: string + identifier: + type: array + items: + type: object + properties: + system: + type: string + value: + type: string + status: + type: string + class: + type: object + properties: + system: + type: string + code: + type: string + display: + type: string + subject: + type: object + properties: + reference: + type: string + display: + type: string + participant: + type: array + items: + type: object + properties: + type: + type: array + items: + type: object + properties: + coding: + type: array + items: + type: object + properties: + system: + type: string + code: + type: string + display: + type: string + individual: + type: object + properties: + reference: + type: string + display: + type: string + period: + type: object + properties: + start: + type: string + format: date-time + end: + type: string + format: date-time + location: + type: array + items: + type: object + properties: + location: + type: object + properties: + reference: + type: string + display: + type: string + statusHistory: + type: array + items: + type: object + properties: + status: + type: string + period: + type: object + properties: + start: + type: string + format: date-time + end: + type: string + format: date-time + serviceProvider: + type: object + properties: + reference: + type: string + Condition: + type: object + required: + - category + - code + - subject + properties: + clinicalStatus: + type: object + properties: + coding: + type: array + items: + type: object + properties: + system: + type: string + code: + type: string + display: + type: string + category: + type: array + items: + type: object + properties: + coding: + type: array + items: + type: object + properties: + system: + type: string + code: + type: string + display: + type: string + code: + type: object + properties: + coding: + type: array + items: + type: object + properties: + system: + type: string + code: + type: string + display: + type: string + subject: + type: object + properties: + reference: + type: string + display: + type: string + encounter: + type: object + properties: + reference: + type: string + display: + type: string + ServiceRequest: + type: object + required: + - status + - intent + - code + - subject + - authoredOn + - requester + - performer + properties: + status: + type: string + intent: + type: string + priority: + type: string + category: + type: array + items: + type: object + properties: + coding: + type: array + items: + type: object + properties: + system: + type: string + code: + type: string + display: + type: string + code: + type: object + properties: + coding: + type: array + items: + type: object + properties: + system: + type: string + code: + type: string + display: + type: string + text: + type: string + subject: + type: object + properties: + reference: + type: string + occurrenceDateTime: + type: string + authoredOn: + type: string + requester: + type: object + properties: + reference: + type: string + display: + type: string + performer: + type: array + items: + type: object + properties: + reference: + type: string + display: + type: string + reasonCode: + type: array + items: + type: object + properties: + text: + type: string + EpisodeOfCare: + type: object + required: + - status + - statusHistory + - diagnosis + - type + - patient + - managingOrganization + - period + - careManager + properties: + status: + type: string + statusHistory: + type: array + items: + type: object + properties: + status: + type: string + period: + type: object + properties: + start: + type: string + end: + type: string + type: + type: array + items: + type: object + properties: + coding: + type: array + items: + type: object + properties: + system: + type: string + code: + type: string + display: + type: string + diagnosis: + type: array + items: + type: object + properties: + condition: + type: object + properties: + reference: + type: string + display: + type: string + role: + type: object + properties: + coding: + type: array + items: + type: object + properties: + system: + type: string + code: + type: string + display: + type: string + rank: + type: integer + patient: + type: object + properties: + reference: + type: string + display: + type: string + managingOrganization: + type: object + properties: + reference: + type: string + period: + type: object + properties: + start: + type: string + end: + type: string + careManager: + type: object + properties: + reference: + type: string + display: + type: string + Specimen: + type: object + required: + - status + - type + - subject + - collection + properties: + status: + type: string + type: + type: object + properties: + coding: + type: array + items: + type: object + properties: + system: + type: string + code: + type: string + display: + type: string + collection: + type: object + properties: + collectedDateTime: + type: string + extension: + type: array + items: + type: object + properties: + url: + type: string + valueReference: + type: object + properties: + reference: + type: string + subject: + type: object + properties: + reference: + type: string + display: + type: string + request: + type: array + items: + type: object + properties: + reference: + type: string + receivedTime: + type: string + extension: + type: array + items: + type: object + properties: + url: + type: string + valueDateTime: + type: string + DiagnosticReport: + type: object + required: + - status + - category + - code + - subject + - performer + - result + - basedOn + properties: + status: + type: string + category: + type: array + items: + type: object + properties: + coding: + type: array + items: + type: object + properties: + system: + type: string + code: + type: string + display: + type: string + code: + type: object + properties: + coding: + type: array + items: + type: object + properties: + system: + type: string + code: + type: string + display: + type: string + subject: + type: object + properties: + reference: + type: string + encounter: + type: object + properties: + reference: + type: string + effectiveDateTime: + type: string + issued: + type: string + performer: + type: array + items: + type: object + properties: + reference: + type: string + result: + type: array + items: + type: object + specimen: + type: array + items: + type: object + basedOn: + type: array + items: + type: object + conclusionCode: + type: array + items: + type: object + properties: + coding: + type: array + items: + type: object + properties: + system: + type: string + code: + type: string + display: + type: string + Observation: + type: object + required: + - resourceType + - status + - category + - code + - subject + - performer + - effectiveDateTime + - valueQuantity + properties: + status: + type: string + category: + type: array + items: + type: object + properties: + coding: + type: array + items: + type: object + properties: + system: + type: string + code: + type: string + display: + type: string + code: + type: object + properties: + coding: + type: array + items: + type: object + properties: + system: + type: string + code: + type: string + display: + type: string + subject: + type: object + properties: + reference: + type: string + performer: + type: array + items: + type: object + properties: + reference: + type: string + encounter: + type: object + properties: + reference: + type: string + display: + type: string + effectiveDateTime: + type: string + issued: + type: string + valueQuantity: + type: object + properties: + value: + type: number + unit: + type: string + system: + type: string + code: + type: string + Organization: + type: object + properties: + resourceType: + type: string + active: + type: boolean + identifier: + type: array + items: + type: object + properties: + use: + type: string + system: + type: string + value: + type: string + type: + type: array + items: + type: object + properties: + coding: + type: array + items: + type: object + properties: + system: + type: string + code: + type: string + display: + type: string + name: + type: string + telecom: + type: array + items: + type: object + properties: + system: + type: string + value: + type: string + use: + type: string + address: + type: array + items: + type: object + properties: + use: + type: string + type: + type: string + line: + type: array + items: + type: string + city: + type: string + postalCode: + type: string + country: + type: string + extension: + type: array + items: + type: object + properties: + url: + type: string + extension: + type: array + items: + type: object + properties: + url: + type: string + valueCode: + type: string + partOf: + type: object + properties: + reference: + type: string + Patient: + type: object + required: + - name + - gender + - birthDate + - address + - telecom + properties: + resourceType: + type: string + identifier: + type: array + items: + type: object + properties: + use: + type: string + system: + type: string + value: + type: string + name: + type: array + items: + type: object + properties: + use: + type: string + text: + type: string + family: + type: string + given: + type: array + items: + type: string + gender: + type: string + birthDate: + type: string + address: + type: array + items: + type: object + properties: + use: + type: string + type: + type: string + line: + type: array + items: + type: string + city: + type: string + postalCode: + type: string + country: + type: string + telecom: + type: array + items: + type: object + properties: + system: + type: string + value: + type: string + use: + type: string + CarePlan: + type: object + properties: + status: + type: string + intent: + type: string + category: + type: array + items: + type: object + properties: + coding: + type: array + items: + type: object + properties: + system: + type: string + code: + type: string + display: + type: string + title: + type: string + description: + type: string + subject: + type: object + properties: + reference: + type: string + display: + type: string + encounter: + type: object + properties: + reference: + type: string + created: + type: string + format: date-time + author: + type: object + properties: + reference: + type: string + goal: + type: array + items: + type: object + properties: + reference: + type: string + required: + - status + - intent + - category + - title + - description + - subject + - created + - author + QuestionnaireResponse: + type: object + properties: + questionnaire: + type: string + status: + type: string + subject: + type: object + properties: + reference: + type: string + display: + type: string + encounter: + type: object + properties: + reference: + type: string + authored: + type: string + format: date-time + author: + type: object + properties: + reference: + type: string + source: + type: object + properties: + reference: + type: string + item: + type: array + items: + type: object + properties: + linkId: + type: string + text: + type: string + answer: + type: array + items: + type: object + properties: + valueCoding: + type: object + properties: + system: + type: string + code: + type: string + display: + type: string + required: + - questionnaire + - status + - subject + - authored + - author + - source + - item + MedicationStatement: + type: object + properties: + contained: + type: array + description: Bisa multi obat, 1 id medication + items: + type: object + properties: + id: + type: string + code: + type: object + properties: + coding: + type: array + items: + type: object + properties: + code: + type: string + display: + type: string + system: + type: string + extension: + type: array + items: + type: object + properties: + url: + type: string + valueCodeableConcept: + type: object + properties: + coding: + type: array + items: + type: object + properties: + code: + type: string + display: + type: string + system: + type: string + form: + type: object + properties: + coding: + type: array + items: + type: object + properties: + code: + type: string + display: + type: string + system: + type: string + ingredient: + type: array + items: + type: object + properties: + isActive: + type: boolean + itemCodeableConcept: + type: object + properties: + coding: + type: array + items: + type: object + properties: + code: + type: string + display: + type: string + system: + type: string + strength: + type: object + properties: + denominator: + type: object + properties: + code: + type: string + system: + type: string + unit: + type: string + value: + type: number + numerator: + type: object + properties: + code: + type: string + system: + type: string + value: + type: number + batch: + type: object + properties: + lotNumber: + type: string + expirationDate: + type: string + format: date + meta: + type: object + properties: + profile: + type: array + items: + type: string + resourceType: + type: string + status: + type: string + status: + type: string + category: + type: object + properties: + coding: + type: array + items: + type: object + properties: + system: + type: string + code: + type: string + display: + type: string + medicationReference: + type: object + properties: + reference: + type: string + display: + type: string + subject: + type: object + properties: + reference: + type: string + display: + type: string + dosage: + type: array + items: + type: object + properties: + text: + type: string + timing: + type: object + properties: + repeat: + type: object + properties: + frequency: + type: integer + period: + type: number + periodUnit: + type: string + effectiveDateTime: + type: string + format: date-time + dateAsserted: + type: string + format: date-time + informationSource: + type: object + properties: + reference: + type: string + display: + type: string + context: + type: object + properties: + reference: + type: string + required: + - contained + - status + - medicationReference + - subject + - effectiveDateTime + - dateAsserted + - informationSource + Immunization: + type: object + required: + - status + - vaccineCode + - patient + - occurrenceDateTime + - primarySource + - location + - lotNumber + - route + - doseQuantity + - performer + - reasonCode + - protocolApplied + properties: + status: + type: string + enum: [completed, entered-in-error, not-done] + vaccineCode: + type: object + properties: + coding: + type: array + items: + type: object + properties: + system: + type: string + code: + type: string + display: + type: string + patient: + type: object + properties: + reference: + type: string + display: + type: string + encounter: + type: object + properties: + reference: + type: string + occurrenceDateTime: + type: string + format: date-time + expirationDate: + type: string + format: date + recorded: + type: string + format: date-time + primarySource: + type: boolean + location: + type: object + properties: + reference: + type: string + display: + type: string + lotNumber: + type: string + route: + type: object + properties: + coding: + type: array + items: + type: object + properties: + system: + type: string + code: + type: string + display: + type: string + doseQuantity: + type: object + properties: + value: + type: number + unit: + type: string + system: + type: string + code: + type: string + performer: + type: array + items: + type: object + properties: + function: + type: object + properties: + coding: + type: array + items: + type: object + properties: + system: + type: string + code: + type: string + display: + type: string + actor: + type: object + properties: + reference: + type: string + reasonCode: + type: array + items: + type: object + properties: + coding: + type: array + items: + type: object + properties: + system: + type: string + code: + type: string + display: + type: string + protocolApplied: + type: array + items: + type: object + properties: + doseNumberPositiveInt: + type: integer + ImagingStudy: + type: object + properties: + identifier: + type: array + items: + type: object + properties: + value: + type: string + status: + type: string + modality: + type: array + items: + type: object + properties: + system: + type: string + code: + type: string + subject: + type: object + properties: + reference: + type: string + started: + type: string + format: date-time + basedOn: + type: array + items: + type: object + properties: + reference: + type: string + numberOfSeries: + type: integer + numberOfInstances: + type: integer + series: + type: array + items: + type: object + properties: + uid: + type: string + number: + type: integer + modality: + type: object + properties: + system: + type: string + code: + type: string + numberOfInstances: + type: integer + started: + type: string + format: date-time + instance: + type: array + items: + type: object + properties: + uid: + type: string + sopClass: + type: object + properties: + system: + type: string + code: + type: string + number: + type: integer + title: + type: string + required: + - identifier + - status + - subject + - started + - series + MedicationRequest: + type: object + properties: + status: + type: string + intent: + type: string + category: + type: array + items: + type: object + properties: + coding: + type: array + items: + type: object + properties: + system: + type: string + code: + type: string + display: + type: string + priority: + type: string + medicationReference: + type: object + properties: + reference: + type: string + display: + type: string + subject: + type: object + properties: + reference: + type: string + display: + type: string + encounter: + type: object + properties: + reference: + type: string + authoredOn: + type: string + format: date-time + requester: + type: object + properties: + reference: + type: string + display: + type: string + reasonCode: + type: array + items: + type: object + properties: + coding: + type: array + items: + type: object + properties: + system: + type: string + code: + type: string + display: + type: string + courseOfTherapyType: + type: object + properties: + coding: + type: array + items: + type: object + properties: + system: + type: string + code: + type: string + display: + type: string + dosageInstruction: + type: array + items: + type: object + properties: + sequence: + type: integer + text: + type: string + additionalInstruction: + type: array + items: + type: object + properties: + text: + type: string + patientInstruction: + type: string + timing: + type: object + properties: + repeat: + type: object + properties: + frequency: + type: integer + period: + type: number + periodUnit: + type: string + route: + type: object + properties: + coding: + type: array + items: + type: object + properties: + system: + type: string + code: + type: string + display: + type: string + doseAndRate: + type: array + items: + type: object + properties: + type: + type: object + properties: + coding: + type: array + items: + type: object + properties: + system: + type: string + code: + type: string + display: + type: string + doseQuantity: + type: object + properties: + value: + type: number + unit: + type: string + system: + type: string + code: + type: string + dispenseRequest: + type: object + properties: + dispenseInterval: + type: object + properties: + value: + type: number + unit: + type: string + system: + type: string + code: + type: string + validityPeriod: + type: object + properties: + start: + type: string + format: date-time + end: + type: string + format: date-time + numberOfRepeatsAllowed: + type: integer + quantity: + type: object + properties: + value: + type: number + unit: + type: string + system: + type: string + code: + type: string + expectedSupplyDuration: + type: object + properties: + value: + type: number + unit: + type: string + system: + type: string + code: + type: string + performer: + type: object + properties: + reference: + type: string + required: + - status + - intent + - priority + - medicationReference + - subject + - authoredOn + - requester + - dispenseRequest + MedicationDispense: + type: object + required: + - status + - medicationReference + - subject + - location + - authorizingPrescription + - quantity + - daysSupply + - whenPrepared + - whenHandedOver + properties: + status: + type: string + category: + type: object + properties: + coding: + type: array + items: + type: object + properties: + system: + type: string + code: + type: string + display: + type: string + medicationReference: + type: object + properties: + reference: + type: string + display: + type: string + subject: + type: object + properties: + reference: + type: string + display: + type: string + context: + type: object + properties: + reference: + type: string + performer: + type: array + items: + type: object + properties: + actor: + type: object + properties: + reference: + type: string + display: + type: string + location: + type: object + properties: + reference: + type: string + display: + type: string + authorizingPrescription: + type: array + items: + type: object + properties: + reference: + type: string + quantity: + type: object + properties: + system: + type: string + code: + type: string + value: + type: number + daysSupply: + type: object + properties: + value: + type: number + unit: + type: string + system: + type: string + code: + type: string + whenPrepared: + type: string + format: date-time + whenHandedOver: + type: string + format: date-time + dosageInstruction: + type: array + items: + type: object + properties: + sequence: + type: integer + text: + type: string + timing: + type: object + properties: + repeat: + type: object + properties: + frequency: + type: integer + period: + type: integer + periodUnit: + type: string + doseAndRate: + type: array + items: + type: object + properties: + type: + type: object + properties: + coding: + type: array + items: + type: object + properties: + system: + type: string + code: + type: string + display: + type: string + doseQuantity: + type: object + properties: + value: + type: number + unit: + type: string + system: + type: string + code: + type: string + Goal: + type: object + properties: + lifecycleStatus: + type: string + category: + type: array + items: + type: object + properties: + coding: + type: array + items: + type: object + properties: + system: + type: string + code: + type: string + display: + type: string + description: + type: object + properties: + text: + type: string + subject: + type: object + properties: + reference: + type: string + target: + type: array + items: + type: object + properties: + measure: + type: object + properties: + coding: + type: array + items: + type: object + properties: + system: + type: string + code: + type: string + display: + type: string + detailCodeableConcept: + type: object + properties: + coding: + type: array + items: + type: object + properties: + system: + type: string + code: + type: string + display: + type: string + dueDate: + type: string + format: date + statusDate: + type: string + format: date + expressedBy: + type: object + properties: + reference: + type: string + addresses: + type: array + items: + type: object + properties: + reference: + type: string + required: + - lifecycleStatus + - category + - subject + - target + - statusDate + - expressedBy + +security: +- ApiKeyAuth: [] diff --git a/pkg/common/master_data.go b/pkg/common/master_data.go new file mode 100644 index 0000000..1c98049 --- /dev/null +++ b/pkg/common/master_data.go @@ -0,0 +1,14 @@ +package common + +import ( + "os" + "satusehat-rssa/internal/model" +) + +func GetIdentifier(typeName string) model.Identifier { + return model.Identifier{ + Use: "official", + System: "http://sys-ids.kemkes.go.id/" + typeName + "/" + os.Getenv("ORGANIZATION_ID"), + Value: os.Getenv("ORGANIZATION_ID"), + } +} diff --git a/pkg/httputil/request.go b/pkg/httputil/request.go new file mode 100644 index 0000000..33d5f2b --- /dev/null +++ b/pkg/httputil/request.go @@ -0,0 +1,102 @@ +package httputil + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + "satusehat-rssa/internal/constant" + "time" +) + +// RequestOption for request configuration +type RequestOption struct { + Method string + URL string + Body interface{} + Headers map[string]string + BearerToken string + Timeout time.Duration +} + +// DoRequest sends an HTTP request and returns the result as map[string]interface{} +func DoRequest(opt RequestOption) (map[string]interface{}, error) { + if opt.Method == "" { + opt.Method = http.MethodGet + } + + var bodyReader io.Reader + if opt.Body != nil { + jsonData, err := json.Marshal(opt.Body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewBuffer(jsonData) + } + + req, err := http.NewRequest(opt.Method, opt.URL, bodyReader) + if err != nil { + return nil, err + } + + // Set default Content-Type untuk JSON + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Accept", "application/json") + + // Set custom headers + for k, v := range opt.Headers { + req.Header.Set(k, v) + } + + // Set Bearer token kalau ada + if opt.BearerToken != "" { + req.Header.Set("Authorization", "Bearer "+opt.BearerToken) + } + + // Set timeout + client := &http.Client{ + Timeout: opt.Timeout, + } + if client.Timeout == 0 { + client.Timeout = 30 * time.Second + } + + resp, err := client.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + // Read all body (so we can decode or forward) + bodyBytes, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + // Forward error response directly + if resp.StatusCode >= http.StatusBadRequest { + // Try to decode as JSON first + var errResult map[string]interface{} + if json.Unmarshal(bodyBytes, &errResult) == nil { + return errResult, fmt.Errorf("http error: %s", resp.Status) + } + + return nil, fmt.Errorf("http error: %s", resp.Status) + } + + // Decode JSON success response + var result map[string]interface{} + if err := json.Unmarshal(bodyBytes, &result); err != nil { + return nil, err + } + + return result, nil +} + +func DefaultFHIRHeaders() map[string]string { + return map[string]string{ + "Content-Type": constant.ContentTypeFHIRJSON, + "Accept": constant.ContentTypeFHIRJSON, + } +}