677 lines
19 KiB
Go
677 lines
19 KiB
Go
package services
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"api-service/internal/config"
|
|
"api-service/pkg/logger"
|
|
|
|
"github.com/mashingan/smapping"
|
|
"github.com/tidwall/gjson"
|
|
)
|
|
|
|
// SatuSehatService interface for SATUSEHAT operations
|
|
type SatuSehatService interface {
|
|
// Standard HTTP methods
|
|
Get(ctx context.Context, endpoint string, result interface{}) error
|
|
Post(ctx context.Context, endpoint string, payload interface{}, result interface{}) error
|
|
Put(ctx context.Context, endpoint string, payload interface{}, result interface{}) error
|
|
Delete(ctx context.Context, endpoint string, result interface{}) error
|
|
|
|
// Raw response methods
|
|
GetRawResponse(ctx context.Context, endpoint string) (*SatuSehatResponDTO, error)
|
|
PostRawResponse(ctx context.Context, endpoint string, payload interface{}) (*SatuSehatResponDTO, error)
|
|
|
|
// FHIR specific methods
|
|
PostBundle(ctx context.Context, bundle interface{}) (*SatuSehatResponDTO, error)
|
|
GetPatientByNIK(ctx context.Context, nik string) (*SatuSehatResponDTO, error)
|
|
GetPractitionerByNIK(ctx context.Context, nik string) (*SatuSehatResponDTO, error)
|
|
GetResourceByID(ctx context.Context, resourceType, id string) (*SatuSehatResponDTO, error)
|
|
|
|
// Token management
|
|
RefreshToken(ctx context.Context) error
|
|
IsTokenValid() bool
|
|
GenerateToken(ctx context.Context, clientID, clientSecret string) (*SatuSehatResponDTO, error)
|
|
}
|
|
|
|
// SatuSehatService struct for SATUSEHAT service
|
|
type SatuSehatServiceStruct struct {
|
|
config config.SatuSehatConfig
|
|
httpClient *http.Client
|
|
token TokenDetail
|
|
tokenMutex sync.RWMutex
|
|
}
|
|
|
|
// Token detail structure
|
|
type TokenDetail struct {
|
|
AccessToken string `json:"access_token"`
|
|
TokenType string `json:"token_type"`
|
|
ExpiresIn int64 `json:"expires_in"`
|
|
IssuedAt int64 `json:"issued_at"`
|
|
OrganizationName string `json:"organization_name"`
|
|
DeveloperEmail string `json:"developer.email"`
|
|
ClientID string `json:"client_id"`
|
|
ApplicationName string `json:"application_name"`
|
|
Status string `json:"status"`
|
|
ExpiryTime time.Time `json:"-"`
|
|
}
|
|
|
|
// Response structures
|
|
type SatuSehatResponMentahDTO struct {
|
|
StatusCode int `json:"status_code"`
|
|
Success bool `json:"success"`
|
|
Message string `json:"message"`
|
|
Data interface{} `json:"data"`
|
|
}
|
|
|
|
type SatuSehatResponDTO struct {
|
|
StatusCode int `json:"status_code"`
|
|
Success bool `json:"success"`
|
|
Message string `json:"message"`
|
|
Data interface{} `json:"data"`
|
|
Error *ErrorInfo `json:"error,omitempty"`
|
|
}
|
|
|
|
type ErrorInfo struct {
|
|
Code string `json:"code"`
|
|
Details string `json:"details"`
|
|
}
|
|
|
|
// Token methods
|
|
func (t *TokenDetail) IsExpired() bool {
|
|
if t.ExpiryTime.IsZero() {
|
|
return true
|
|
}
|
|
return time.Now().UTC().After(t.ExpiryTime.Add(-5 * time.Minute))
|
|
}
|
|
|
|
func (t *TokenDetail) SetExpired() {
|
|
t.ExpiryTime = time.Time{}
|
|
}
|
|
|
|
// NewSatuSehatService creates a new SATUSEHAT service instance
|
|
func NewSatuSehatService(cfg config.SatuSehatConfig) SatuSehatService {
|
|
service := &SatuSehatServiceStruct{
|
|
config: cfg,
|
|
httpClient: &http.Client{
|
|
Timeout: cfg.Timeout,
|
|
},
|
|
}
|
|
|
|
return service
|
|
}
|
|
|
|
// NewSatuSehatServiceFromConfig creates service from main config
|
|
func NewSatuSehatServiceFromConfig(cfg *config.Config) SatuSehatService {
|
|
return NewSatuSehatService(cfg.SatuSehat)
|
|
}
|
|
|
|
// NewSatuSehatServiceFromInterface creates service from interface (for backward compatibility)
|
|
func NewSatuSehatServiceFromInterface(cfg interface{}) (SatuSehatService, error) {
|
|
var satusehatConfig config.SatuSehatConfig
|
|
|
|
// Try to map from interface
|
|
err := smapping.FillStruct(&satusehatConfig, smapping.MapFields(&cfg))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to map config: %w", err)
|
|
}
|
|
|
|
if satusehatConfig.Timeout == 0 {
|
|
satusehatConfig.Timeout = 30 * time.Second
|
|
}
|
|
|
|
return NewSatuSehatService(satusehatConfig), nil
|
|
}
|
|
|
|
// SetHTTPClient allows custom http client configuration
|
|
func (s *SatuSehatServiceStruct) SetHTTPClient(client *http.Client) {
|
|
s.httpClient = client
|
|
}
|
|
|
|
// RefreshToken obtains new access token
|
|
func (s *SatuSehatServiceStruct) RefreshToken(ctx context.Context) error {
|
|
s.tokenMutex.Lock()
|
|
defer s.tokenMutex.Unlock()
|
|
|
|
// Double-check pattern
|
|
if !s.token.IsExpired() {
|
|
return nil
|
|
}
|
|
|
|
// Remove duplicate /oauth2/v1 from URL since AuthURL already contains it
|
|
tokenURL := fmt.Sprintf("%s/accesstoken?grant_type=client_credentials", s.config.AuthURL)
|
|
|
|
formData := fmt.Sprintf("client_id=%s&client_secret=%s", s.config.ClientID, s.config.ClientSecret)
|
|
|
|
req, err := http.NewRequestWithContext(ctx, "POST", tokenURL, bytes.NewBufferString(formData))
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create token request: %w", err)
|
|
}
|
|
|
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
|
|
res, err := s.httpClient.Do(req)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to execute token request: %w", err)
|
|
}
|
|
defer res.Body.Close()
|
|
|
|
body, err := io.ReadAll(res.Body)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to read token response: %w", err)
|
|
}
|
|
|
|
if res.StatusCode != 200 {
|
|
// Log the error response for debugging
|
|
fmt.Printf("DEBUG: Token request failed with status %d: %s\n", res.StatusCode, string(body))
|
|
return fmt.Errorf("token request failed with status %d: %s", res.StatusCode, string(body))
|
|
}
|
|
|
|
// Debug: log the raw response for troubleshooting
|
|
fmt.Printf("DEBUG: SATUSEHAT token response - Status: %d, Body: %s\n", res.StatusCode, string(body))
|
|
fmt.Printf("DEBUG: Request URL: %s\n", tokenURL)
|
|
fmt.Printf("DEBUG: Request Headers: %+v\n", req.Header)
|
|
|
|
return s.parseTokenResponse(body)
|
|
}
|
|
|
|
// parseTokenResponse parses token response from SATUSEHAT
|
|
func (s *SatuSehatServiceStruct) parseTokenResponse(body []byte) error {
|
|
// Debug: log the raw response for detailed analysis
|
|
fmt.Printf("DEBUG: Raw token response body: %s\n", string(body))
|
|
|
|
result := gjson.ParseBytes(body)
|
|
|
|
// Check if we have a valid access token
|
|
accessToken := result.Get("access_token").String()
|
|
if accessToken == "" {
|
|
return fmt.Errorf("no access token found in response: %s", string(body))
|
|
}
|
|
|
|
issuedAt := result.Get("issued_at").Int()
|
|
expiresIn := result.Get("expires_in").Int()
|
|
|
|
// Handle timestamp conversion (issued_at could be in milliseconds or seconds)
|
|
var expiryTime time.Time
|
|
if issuedAt > 1000000000000 { // If timestamp is in milliseconds
|
|
expiryTime = time.Unix(issuedAt/1000, 0).Add(time.Duration(expiresIn) * time.Second)
|
|
} else if issuedAt > 0 { // If timestamp is in seconds
|
|
expiryTime = time.Unix(issuedAt, 0).Add(time.Duration(expiresIn) * time.Second)
|
|
} else {
|
|
// If no issued_at, use current time + expires_in
|
|
expiryTime = time.Now().UTC().Add(time.Duration(expiresIn) * time.Second)
|
|
}
|
|
|
|
s.token = TokenDetail{
|
|
AccessToken: accessToken,
|
|
TokenType: result.Get("token_type").String(),
|
|
ExpiresIn: expiresIn,
|
|
IssuedAt: issuedAt,
|
|
OrganizationName: result.Get("organization_name").String(),
|
|
DeveloperEmail: result.Get("developer\\.email").String(),
|
|
ClientID: result.Get("client_id").String(),
|
|
ApplicationName: result.Get("application_name").String(),
|
|
Status: result.Get("status").String(),
|
|
ExpiryTime: expiryTime,
|
|
}
|
|
|
|
logger.Info("SATUSEHAT token refreshed successfully", map[string]interface{}{
|
|
"expires_at": s.token.ExpiryTime,
|
|
"organization": s.token.OrganizationName,
|
|
"token_type": s.token.TokenType,
|
|
"client_id": s.token.ClientID,
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
// IsTokenValid checks if current token is valid
|
|
func (s *SatuSehatServiceStruct) IsTokenValid() bool {
|
|
s.tokenMutex.RLock()
|
|
defer s.tokenMutex.RUnlock()
|
|
return !s.token.IsExpired()
|
|
}
|
|
|
|
// ensureValidToken ensures we have a valid token
|
|
func (s *SatuSehatServiceStruct) ensureValidToken(ctx context.Context) error {
|
|
s.tokenMutex.RLock()
|
|
needsRefresh := s.token.IsExpired()
|
|
s.tokenMutex.RUnlock()
|
|
|
|
if needsRefresh {
|
|
return s.RefreshToken(ctx)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// prepareRequest prepares HTTP request with required headers
|
|
func (s *SatuSehatServiceStruct) prepareRequest(ctx context.Context, method, endpoint string, body io.Reader) (*http.Request, error) {
|
|
// Ensure valid token
|
|
if err := s.ensureValidToken(ctx); err != nil {
|
|
return nil, fmt.Errorf("failed to ensure valid token: %w", err)
|
|
}
|
|
|
|
fullURL := s.config.BaseURL + endpoint
|
|
req, err := http.NewRequestWithContext(ctx, method, fullURL, body)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create request: %w", err)
|
|
}
|
|
|
|
// Set headers
|
|
s.tokenMutex.RLock()
|
|
token := s.token.AccessToken
|
|
s.tokenMutex.RUnlock()
|
|
|
|
req.Header.Set("Content-Type", "application/json")
|
|
req.Header.Set("Accept", "application/json")
|
|
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
|
|
|
|
return req, nil
|
|
}
|
|
|
|
// processResponse processes response from SATUSEHAT API
|
|
func (s *SatuSehatServiceStruct) processResponse(res *http.Response) (*SatuSehatResponDTO, error) {
|
|
defer res.Body.Close()
|
|
|
|
body, err := io.ReadAll(res.Body)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to read response body: %w", err)
|
|
}
|
|
|
|
// Create response
|
|
resp := &SatuSehatResponDTO{
|
|
StatusCode: res.StatusCode,
|
|
Success: res.StatusCode >= 200 && res.StatusCode < 300,
|
|
}
|
|
|
|
// Handle different status codes
|
|
switch {
|
|
case res.StatusCode == 401:
|
|
s.tokenMutex.Lock()
|
|
s.token.SetExpired()
|
|
s.tokenMutex.Unlock()
|
|
|
|
resp.Error = &ErrorInfo{
|
|
Code: "UNAUTHORIZED",
|
|
Details: "Token expired or invalid",
|
|
}
|
|
resp.Message = "Unauthorized access"
|
|
|
|
case res.StatusCode >= 400 && res.StatusCode < 500:
|
|
resp.Error = &ErrorInfo{
|
|
Code: "CLIENT_ERROR",
|
|
Details: string(body),
|
|
}
|
|
resp.Message = "Client error"
|
|
|
|
case res.StatusCode >= 500:
|
|
resp.Error = &ErrorInfo{
|
|
Code: "SERVER_ERROR",
|
|
Details: string(body),
|
|
}
|
|
resp.Message = "Server error"
|
|
|
|
default:
|
|
resp.Message = "Success"
|
|
}
|
|
|
|
// Parse JSON response if successful
|
|
if resp.Success && len(body) > 0 {
|
|
var jsonData interface{}
|
|
if err := json.Unmarshal(body, &jsonData); err != nil {
|
|
// If JSON unmarshal fails, store as string
|
|
resp.Data = string(body)
|
|
} else {
|
|
resp.Data = jsonData
|
|
}
|
|
}
|
|
|
|
return resp, nil
|
|
}
|
|
|
|
// Get performs HTTP GET request
|
|
func (s *SatuSehatServiceStruct) Get(ctx context.Context, endpoint string, result interface{}) error {
|
|
resp, err := s.GetRawResponse(ctx, endpoint)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return mapToResult(resp, result)
|
|
}
|
|
|
|
// Post performs HTTP POST request
|
|
func (s *SatuSehatServiceStruct) Post(ctx context.Context, endpoint string, payload interface{}, result interface{}) error {
|
|
resp, err := s.PostRawResponse(ctx, endpoint, payload)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return mapToResult(resp, result)
|
|
}
|
|
|
|
// Put performs HTTP PUT request
|
|
func (s *SatuSehatServiceStruct) Put(ctx context.Context, endpoint string, payload interface{}, result interface{}) error {
|
|
var buf bytes.Buffer
|
|
if payload != nil {
|
|
if err := json.NewEncoder(&buf).Encode(payload); err != nil {
|
|
return fmt.Errorf("failed to encode payload: %w", err)
|
|
}
|
|
}
|
|
|
|
req, err := s.prepareRequest(ctx, http.MethodPut, endpoint, &buf)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
res, err := s.httpClient.Do(req)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to execute PUT request: %w", err)
|
|
}
|
|
|
|
resp, err := s.processResponse(res)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return mapToResult(resp, result)
|
|
}
|
|
|
|
// Delete performs HTTP DELETE request
|
|
func (s *SatuSehatServiceStruct) Delete(ctx context.Context, endpoint string, result interface{}) error {
|
|
req, err := s.prepareRequest(ctx, http.MethodDelete, endpoint, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
res, err := s.httpClient.Do(req)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to execute DELETE request: %w", err)
|
|
}
|
|
|
|
resp, err := s.processResponse(res)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return mapToResult(resp, result)
|
|
}
|
|
|
|
// GetRawResponse returns raw response without mapping
|
|
func (s *SatuSehatServiceStruct) GetRawResponse(ctx context.Context, endpoint string) (*SatuSehatResponDTO, error) {
|
|
req, err := s.prepareRequest(ctx, http.MethodGet, endpoint, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
res, err := s.httpClient.Do(req)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to execute GET request: %w", err)
|
|
}
|
|
|
|
return s.processResponse(res)
|
|
}
|
|
|
|
// PostRawResponse returns raw response without mapping
|
|
func (s *SatuSehatServiceStruct) PostRawResponse(ctx context.Context, endpoint string, payload interface{}) (*SatuSehatResponDTO, error) {
|
|
var buf bytes.Buffer
|
|
if payload != nil {
|
|
if err := json.NewEncoder(&buf).Encode(payload); err != nil {
|
|
return nil, fmt.Errorf("failed to encode payload: %w", err)
|
|
}
|
|
}
|
|
|
|
req, err := s.prepareRequest(ctx, http.MethodPost, endpoint, &buf)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
res, err := s.httpClient.Do(req)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to execute POST request: %w", err)
|
|
}
|
|
|
|
return s.processResponse(res)
|
|
}
|
|
|
|
// FHIR-specific methods
|
|
|
|
// PostBundle posts FHIR bundle to SATUSEHAT
|
|
func (s *SatuSehatServiceStruct) PostBundle(ctx context.Context, bundle interface{}) (*SatuSehatResponDTO, error) {
|
|
return s.PostRawResponse(ctx, "", bundle)
|
|
}
|
|
|
|
// GetPatientByNIK retrieves patient by NIK
|
|
func (s *SatuSehatServiceStruct) GetPatientByNIK(ctx context.Context, nik string) (*SatuSehatResponDTO, error) {
|
|
endpoint := fmt.Sprintf("/Patient?identifier=https://fhir.kemkes.go.id/id/nik|%s", nik)
|
|
return s.GetRawResponse(ctx, endpoint)
|
|
}
|
|
|
|
// GetPractitionerByNIK retrieves practitioner by NIK
|
|
func (s *SatuSehatServiceStruct) GetPractitionerByNIK(ctx context.Context, nik string) (*SatuSehatResponDTO, error) {
|
|
endpoint := fmt.Sprintf("/Practitioner?identifier=https://fhir.kemkes.go.id/id/nik|%s", nik)
|
|
return s.GetRawResponse(ctx, endpoint)
|
|
}
|
|
|
|
// GetResourceByID retrieves any FHIR resource by ID
|
|
func (s *SatuSehatServiceStruct) GetResourceByID(ctx context.Context, resourceType, id string) (*SatuSehatResponDTO, error) {
|
|
endpoint := fmt.Sprintf("/%s/%s", resourceType, id)
|
|
return s.GetRawResponse(ctx, endpoint)
|
|
}
|
|
|
|
// GenerateToken generates a new access token with custom client credentials
|
|
func (s *SatuSehatServiceStruct) GenerateToken(ctx context.Context, clientID, clientSecret string) (*SatuSehatResponDTO, error) {
|
|
// Remove duplicate /oauth2/v1 from URL since AuthURL already contains it
|
|
tokenURL := fmt.Sprintf("%s/accesstoken?grant_type=client_credentials", s.config.AuthURL)
|
|
|
|
formData := fmt.Sprintf("client_id=%s&client_secret=%s", clientID, clientSecret)
|
|
|
|
req, err := http.NewRequestWithContext(ctx, "POST", tokenURL, strings.NewReader(formData))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create token request: %w", err)
|
|
}
|
|
|
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
|
|
res, err := s.httpClient.Do(req)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to execute token request: %w", err)
|
|
}
|
|
defer res.Body.Close()
|
|
|
|
body, err := io.ReadAll(res.Body)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to read token response: %w", err)
|
|
}
|
|
|
|
// Process the response using the existing response processor
|
|
resp := &SatuSehatResponDTO{
|
|
StatusCode: res.StatusCode,
|
|
Success: res.StatusCode >= 200 && res.StatusCode < 300,
|
|
}
|
|
|
|
// Handle different status codes
|
|
switch {
|
|
case res.StatusCode == 401:
|
|
resp.Error = &ErrorInfo{
|
|
Code: "UNAUTHORIZED",
|
|
Details: "Invalid client credentials",
|
|
}
|
|
resp.Message = "Unauthorized access"
|
|
|
|
case res.StatusCode >= 400 && res.StatusCode < 500:
|
|
resp.Error = &ErrorInfo{
|
|
Code: "CLIENT_ERROR",
|
|
Details: string(body),
|
|
}
|
|
resp.Message = "Client error"
|
|
|
|
case res.StatusCode >= 500:
|
|
resp.Error = &ErrorInfo{
|
|
Code: "SERVER_ERROR",
|
|
Details: string(body),
|
|
}
|
|
resp.Message = "Server error"
|
|
|
|
default:
|
|
resp.Message = "Success"
|
|
}
|
|
|
|
// Parse JSON response if successful
|
|
if resp.Success && len(body) > 0 {
|
|
var jsonData interface{}
|
|
if err := json.Unmarshal(body, &jsonData); err != nil {
|
|
// If JSON unmarshal fails, store as string
|
|
resp.Data = string(body)
|
|
} else {
|
|
resp.Data = jsonData
|
|
}
|
|
}
|
|
|
|
return resp, nil
|
|
}
|
|
|
|
// Helper functions
|
|
|
|
// mapToResult maps the final response to the result interface
|
|
func mapToResult(resp *SatuSehatResponDTO, result interface{}) error {
|
|
respBytes, err := json.Marshal(resp)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to marshal final response: %w", err)
|
|
}
|
|
|
|
if err := json.Unmarshal(respBytes, result); err != nil {
|
|
return fmt.Errorf("failed to unmarshal to result: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Backward compatibility functions
|
|
|
|
func SatuSehatGetRequest(endpoint string, cfg interface{}) interface{} {
|
|
service, err := NewSatuSehatServiceFromInterface(cfg)
|
|
if err != nil {
|
|
logger.Error("Failed to create SATUSEHAT service", map[string]interface{}{
|
|
"error": err.Error(),
|
|
})
|
|
return nil
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
defer cancel()
|
|
|
|
resp, err := service.GetRawResponse(ctx, endpoint)
|
|
if err != nil {
|
|
logger.Error("Failed to get SATUSEHAT response", map[string]interface{}{
|
|
"error": err.Error(),
|
|
})
|
|
return nil
|
|
}
|
|
|
|
return resp
|
|
}
|
|
|
|
func SatuSehatPostRequest(endpoint string, cfg interface{}, data interface{}) interface{} {
|
|
service, err := NewSatuSehatServiceFromInterface(cfg)
|
|
if err != nil {
|
|
logger.Error("Failed to create SATUSEHAT service", map[string]interface{}{
|
|
"error": err.Error(),
|
|
})
|
|
return nil
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
defer cancel()
|
|
|
|
resp, err := service.PostRawResponse(ctx, endpoint, data)
|
|
if err != nil {
|
|
logger.Error("Failed to post SATUSEHAT response", map[string]interface{}{
|
|
"error": err.Error(),
|
|
})
|
|
return nil
|
|
}
|
|
|
|
return resp
|
|
}
|
|
|
|
// FHIR helper functions
|
|
|
|
func SatuSehatGetPatient(nik string, cfg interface{}) interface{} {
|
|
service, err := NewSatuSehatServiceFromInterface(cfg)
|
|
if err != nil {
|
|
logger.Error("Failed to create SATUSEHAT service", map[string]interface{}{
|
|
"error": err.Error(),
|
|
})
|
|
return nil
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
defer cancel()
|
|
|
|
resp, err := service.GetPatientByNIK(ctx, nik)
|
|
if err != nil {
|
|
logger.Error("Failed to get patient", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"nik": nik,
|
|
})
|
|
return nil
|
|
}
|
|
|
|
return resp
|
|
}
|
|
|
|
func SatuSehatGetPractitioner(nik string, cfg interface{}) interface{} {
|
|
service, err := NewSatuSehatServiceFromInterface(cfg)
|
|
if err != nil {
|
|
logger.Error("Failed to create SATUSEHAT service", map[string]interface{}{
|
|
"error": err.Error(),
|
|
})
|
|
return nil
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
defer cancel()
|
|
|
|
resp, err := service.GetPractitionerByNIK(ctx, nik)
|
|
if err != nil {
|
|
logger.Error("Failed to get practitioner", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"nik": nik,
|
|
})
|
|
return nil
|
|
}
|
|
|
|
return resp
|
|
}
|
|
|
|
func SatuSehatPostBundle(bundle interface{}, cfg interface{}) interface{} {
|
|
service, err := NewSatuSehatServiceFromInterface(cfg)
|
|
if err != nil {
|
|
logger.Error("Failed to create SATUSEHAT service", map[string]interface{}{
|
|
"error": err.Error(),
|
|
})
|
|
return nil
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
defer cancel()
|
|
|
|
resp, err := service.PostBundle(ctx, bundle)
|
|
if err != nil {
|
|
logger.Error("Failed to post bundle", map[string]interface{}{
|
|
"error": err.Error(),
|
|
})
|
|
return nil
|
|
}
|
|
|
|
return resp
|
|
}
|