diff --git a/pom.xml b/pom.xml index 81ba6a3..64c2842 100644 --- a/pom.xml +++ b/pom.xml @@ -41,10 +41,6 @@ - - org.springframework.boot - spring-boot-starter - org.springframework.boot spring-boot-starter-web @@ -106,6 +102,10 @@ ${allure.version} test + + org.springframework.boot + spring-boot-starter-webflux + diff --git a/src/main/java/com/goi/integration/IntegrationServiceApplication.java b/src/main/java/com/goi/integration/IntegrationServiceApplication.java new file mode 100644 index 0000000..290e8e1 --- /dev/null +++ b/src/main/java/com/goi/integration/IntegrationServiceApplication.java @@ -0,0 +1,11 @@ +package com.goi.integration; + +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.SpringApplication; + +@SpringBootApplication +public class IntegrationServiceApplication { + public static void main(String[] args) { + SpringApplication.run(IntegrationServiceApplication.class, args); + } +} \ No newline at end of file diff --git a/src/main/java/com/goi/integration/common/config/InMemoryScheduleJobConfigProvider.java b/src/main/java/com/goi/integration/common/config/InMemoryScheduleJobConfigProvider.java new file mode 100644 index 0000000..4cdedd1 --- /dev/null +++ b/src/main/java/com/goi/integration/common/config/InMemoryScheduleJobConfigProvider.java @@ -0,0 +1,29 @@ +package com.goi.integration.common.config; + +import com.goi.integration.common.config.ScheduleJobConfigDto; +import org.springframework.stereotype.Component; + +import java.util.Map; +import java.util.Optional; + +@Component +public class InMemoryScheduleJobConfigProvider implements ScheduleJobConfigProvider { + + private final Map configs = Map.of( + "SAMSARA_DVIR", + ScheduleJobConfigDto.builder() + .sjcJobCode("SAMSARA_DVIR") + .sjcEnabled(true) + .sjcCronExpression("0 */10 * * * *") + .sjcLookbackHours(24) + .sjcOverlapMinutes(10) + .sjcMaxRecords(252) + .sjcTimezone("UTC") + .build() + ); + + @Override + public Optional getJobConfig(String jobCode) { + return Optional.ofNullable(configs.get(jobCode)); + } +} \ No newline at end of file diff --git a/src/main/java/com/goi/integration/common/config/InternalWebClientFactory.java b/src/main/java/com/goi/integration/common/config/InternalWebClientFactory.java new file mode 100644 index 0000000..52eda94 --- /dev/null +++ b/src/main/java/com/goi/integration/common/config/InternalWebClientFactory.java @@ -0,0 +1,16 @@ +package com.goi.integration.common.config; + +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.client.WebClient; + +@Component +public class InternalWebClientFactory { + + public WebClient create(String baseUrl, String token) { + return WebClient.builder() + .baseUrl(baseUrl) + .defaultHeader("X-INTERNAL-SERVICE", "integration-service") + .defaultHeader("X-INTERNAL-TOKEN", token) + .build(); + } +} diff --git a/src/main/java/com/goi/integration/common/config/ScheduleJobConfigDto.java b/src/main/java/com/goi/integration/common/config/ScheduleJobConfigDto.java new file mode 100644 index 0000000..d88ab9c --- /dev/null +++ b/src/main/java/com/goi/integration/common/config/ScheduleJobConfigDto.java @@ -0,0 +1,20 @@ +package com.goi.integration.common.config; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class ScheduleJobConfigDto { + private String sjcJobCode; // SAMSARA_DVIR + private String sjcCronExpression; // "0 */10 * * * *" + private Integer sjcLookbackHours; // 24 + private Integer sjcOverlapMinutes;// 10 + private Integer sjcMaxRecords; // 252 + private Boolean sjcEnabled; // true + private String sjcTimezone; // "UTC" +} diff --git a/src/main/java/com/goi/integration/common/config/ScheduleJobConfigProvider.java b/src/main/java/com/goi/integration/common/config/ScheduleJobConfigProvider.java new file mode 100644 index 0000000..692c00d --- /dev/null +++ b/src/main/java/com/goi/integration/common/config/ScheduleJobConfigProvider.java @@ -0,0 +1,10 @@ +package com.goi.integration.common.config; + +import com.goi.integration.common.config.ScheduleJobConfigDto; + +import java.util.Optional; + +public interface ScheduleJobConfigProvider { + Optional getJobConfig(String jobCode); +} + diff --git a/src/main/java/com/goi/integration/common/dto/ExtIngestResult.java b/src/main/java/com/goi/integration/common/dto/ExtIngestResult.java new file mode 100644 index 0000000..ece3a8b --- /dev/null +++ b/src/main/java/com/goi/integration/common/dto/ExtIngestResult.java @@ -0,0 +1,30 @@ +package com.goi.integration.common.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class ExtIngestResult { + private String source; + private String recordType; + private int received; + private int inserted; + private int updated; + private int skipped; + + public static ExtIngestResult empty(String source, String recordType) { + return ExtIngestResult.builder() + .source(source) + .recordType(recordType) + .received(0) + .inserted(0) + .updated(0) + .skipped(0) + .build(); + } +} diff --git a/src/main/java/com/goi/integration/common/util/DateTimeUtil.java b/src/main/java/com/goi/integration/common/util/DateTimeUtil.java new file mode 100644 index 0000000..001dce8 --- /dev/null +++ b/src/main/java/com/goi/integration/common/util/DateTimeUtil.java @@ -0,0 +1,50 @@ +package com.goi.integration.common.util; + +import com.fasterxml.jackson.databind.JsonNode; + +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeParseException; + +public final class DateTimeUtil { + + private DateTimeUtil() {} + + /** + * JsonNode → LocalDateTime + * (ISO-8601, Z / offset 지원) + */ + public static LocalDateTime parse(JsonNode node) { + if (node == null || node.isMissingNode() || node.isNull()) { + return null; + } + return parse(node.asText(null)); + } + + /** + * String → LocalDateTime + * ex) 2025-12-22T13:30:24.365Z + * ex) 2025-12-22T13:30:24+00:00 + */ + public static LocalDateTime parse(String value) { + if (value == null || value.isBlank()) { + return null; + } + + try { + return OffsetDateTime.parse(value).toLocalDateTime(); + } catch (DateTimeParseException e) { + return null; // ingest 안정성 우선 + } + } + + public static LocalDateTime parseToToronto(String value) { + if (value == null || value.isBlank()) return null; + + return OffsetDateTime + .parse(value) + .atZoneSameInstant(ZoneId.of("America/Toronto")) + .toLocalDateTime(); + } +} diff --git a/src/main/java/com/goi/integration/common/util/ExtPayloadHashUtil.java b/src/main/java/com/goi/integration/common/util/ExtPayloadHashUtil.java new file mode 100644 index 0000000..8a63be2 --- /dev/null +++ b/src/main/java/com/goi/integration/common/util/ExtPayloadHashUtil.java @@ -0,0 +1,80 @@ +package com.goi.integration.common.util; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; + +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.util.TreeMap; + +public final class ExtPayloadHashUtil { + + private static final ObjectMapper mapper = new ObjectMapper(); + + private ExtPayloadHashUtil() {} + + /** record(JsonNode) → canonical hash */ + public static String sha256FromNode(JsonNode node) { + try { + JsonNode normalized = normalize(node); + String canonicalJson = mapper.writeValueAsString(normalized); + return sha256(canonicalJson); + } catch (Exception e) { + throw new RuntimeException("Failed to hash payload", e); + } + } + + /** raw JSON string → hash (fallback 용) */ + public static String sha256FromJson(String rawJson) { + try { + JsonNode node = mapper.readTree(rawJson); + return sha256FromNode(node); + } catch (Exception e) { + throw new RuntimeException("Failed to hash raw json", e); + } + } + + /* ---------- internal ---------- */ + + @SuppressWarnings({ "serial", "deprecation" }) + private static JsonNode normalize(JsonNode node) { + if (node.isObject()) { + ObjectNode obj = mapper.createObjectNode(); + new TreeMap() {{ + node.fields().forEachRemaining(e -> put(e.getKey(), normalize(e.getValue()))); + }}.forEach(obj::set); + return obj; + } + if (node.isArray()) { + ArrayNode arr = mapper.createArrayNode(); + node.forEach(n -> arr.add(normalize(n))); + return arr; + } + return node; + } + + private static String sha256(String value) { + try { + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + byte[] hash = digest.digest(value.getBytes(StandardCharsets.UTF_8)); + StringBuilder hex = new StringBuilder(); + for (byte b : hash) hex.append(String.format("%02x", b)); + return hex.toString(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static String sha256FromJsonNode(JsonNode node) { + try { + JsonNode normalized = normalize(node); + String canonicalJson = mapper.writeValueAsString(normalized); + return sha256(canonicalJson); + } catch (Exception e) { + throw new RuntimeException("Failed to hash JsonNode payload", e); + } + } + +} diff --git a/src/main/java/com/goi/integration/samsara/client/OprIngestClient.java b/src/main/java/com/goi/integration/samsara/client/OprIngestClient.java new file mode 100644 index 0000000..30880c1 --- /dev/null +++ b/src/main/java/com/goi/integration/samsara/client/OprIngestClient.java @@ -0,0 +1,102 @@ +package com.goi.integration.samsara.client; + +import com.goi.integration.common.dto.ExtIngestResult; +import com.goi.integration.samsara.dto.ExtSamsaraInspectionIngestCommand; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.client.WebClient; + +@Slf4j +@Component +@RequiredArgsConstructor +public class OprIngestClient { + + private static final String INTERNAL_TOKEN_HEADER = "X-INTERNAL-TOKEN"; + + @Qualifier("oprWebClient") + private final WebClient oprWebClient; + + @Value("${ext.opr.internal-token}") + private String token; + +// @Autowired +// private ObjectMapper objectMapper; + + public ExtIngestResult ingestInspection(ExtSamsaraInspectionIngestCommand command) { +// String json = objectMapper +// .writerWithDefaultPrettyPrinter() +// .writeValueAsString(command); +// + log.info( + "[OPR_INGEST][REQUEST] source={}, type={}, records={}, fetchedAt={}", + command.getSource(), + command.getRecordType(), + command.getRecords() != null ? command.getRecords().size() : 0, + command.getFetchedAt() + ); + + /* + try { + log.debug( + "[OPR_INGEST][REQUEST_BODY]\n{}", + new com.fasterxml.jackson.databind.ObjectMapper() + .writerWithDefaultPrettyPrinter() + .writeValueAsString(command) + ); + } catch (Exception e) { + log.debug("[OPR_INGEST][REQUEST_BODY] failed to serialize", e); + } + */ + + try { + ExtIngestResult result = oprWebClient.post() + .uri("/ext/samsara/inspections/ingest") + .header(INTERNAL_TOKEN_HEADER, token) + .bodyValue(command) + .retrieve() + .onStatus( + status -> status.is4xxClientError() || status.is5xxServerError(), + resp -> resp.bodyToMono(String.class) + .map(body -> { + if (resp.statusCode().value() == 403) { + return new IllegalStateException( + "OPR_AUTH_FAILED: " + body + ); + } + return new RuntimeException( + "OPR_INGEST_HTTP_ERROR: " + resp.statusCode() + " body=" + body + ); + }) + ) + .bodyToMono(ExtIngestResult.class) + .block(); + + // 성공 로그 + log.info( + "[OPR_INGEST][SUCCESS] received={}, inserted={}, updated={}, skipped={}", + result.getReceived(), + result.getInserted(), + result.getUpdated(), + result.getSkipped() + ); + + return result; + + } catch (Exception e) { + // 실패 로그 + log.error( + "[OPR_INGEST][FAILED] source={}, type={}, error={}", + command.getSource(), + command.getRecordType(), + e.getMessage(), + e + ); + throw e; + } + } +} diff --git a/src/main/java/com/goi/integration/samsara/client/SamsaraClient.java b/src/main/java/com/goi/integration/samsara/client/SamsaraClient.java new file mode 100644 index 0000000..fcfff35 --- /dev/null +++ b/src/main/java/com/goi/integration/samsara/client/SamsaraClient.java @@ -0,0 +1,41 @@ +package com.goi.integration.samsara.client; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpHeaders; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.client.WebClient; + +import java.time.Instant; + +@Component +public class SamsaraClient { + + private final WebClient webClient; + + public SamsaraClient( + @Value("${ext.samsara.base-url:https://api.samsara.com}") String baseUrl, + @Value("${ext.samsara.api-token}") String apiToken + ) { + this.webClient = WebClient.builder() + .baseUrl(baseUrl) + .defaultHeader(HttpHeaders.AUTHORIZATION, "Bearer " + apiToken) + .build(); + } + + /** + * DVIR history raw JSON (string)로 받아도 되고, DTO로 매핑해도 됨. + * 일단 초기엔 String으로 받아서 JsonNode로 파싱하는게 가장 유연함. + */ + public String getDvirHistory(int limit, Instant startTime, Instant endTime) { + return webClient.get() + .uri(uriBuilder -> uriBuilder + .path("/fleet/dvirs/history") + .queryParam("limit", limit) + .queryParam("startTime", startTime.toString()) + .queryParam("endTime", endTime.toString()) + .build()) + .retrieve() + .bodyToMono(String.class) + .block(); + } +} \ No newline at end of file diff --git a/src/main/java/com/goi/integration/samsara/config/OprClientConfig.java b/src/main/java/com/goi/integration/samsara/config/OprClientConfig.java new file mode 100644 index 0000000..b7197ce --- /dev/null +++ b/src/main/java/com/goi/integration/samsara/config/OprClientConfig.java @@ -0,0 +1,20 @@ +package com.goi.integration.samsara.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.reactive.function.client.WebClient; + +@Configuration +public class OprClientConfig { + + @Bean + public WebClient oprWebClient( + WebClient.Builder builder, + @Value("${ext.opr.base-url}") String baseUrl + ) { + return builder + .baseUrl(baseUrl) + .build(); + } +} diff --git a/src/main/java/com/goi/integration/samsara/controller/SamsaraTestController.java b/src/main/java/com/goi/integration/samsara/controller/SamsaraTestController.java new file mode 100644 index 0000000..c83beb9 --- /dev/null +++ b/src/main/java/com/goi/integration/samsara/controller/SamsaraTestController.java @@ -0,0 +1,19 @@ +package com.goi.integration.samsara.controller; + +import com.goi.integration.samsara.job.SamsaraDvirIngestJob; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/internal/test") +@RequiredArgsConstructor +public class SamsaraTestController { + + private final SamsaraDvirIngestJob dvirJob; + + @PostMapping("/samsara-dvir") + public String runDvirNow() { + dvirJob.run(); + return "SAMSARA_DVIR_TRIGGERED"; + } +} diff --git a/src/main/java/com/goi/integration/samsara/dto/ExtSamsaraInspectionIngestCommand.java b/src/main/java/com/goi/integration/samsara/dto/ExtSamsaraInspectionIngestCommand.java new file mode 100644 index 0000000..6907606 --- /dev/null +++ b/src/main/java/com/goi/integration/samsara/dto/ExtSamsaraInspectionIngestCommand.java @@ -0,0 +1,20 @@ +package com.goi.integration.samsara.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class ExtSamsaraInspectionIngestCommand { + private String source; + private String recordType; + private LocalDateTime fetchedAt; + private List records; +} \ No newline at end of file diff --git a/src/main/java/com/goi/integration/samsara/dto/ExtSamsaraInspectionRecordDto.java b/src/main/java/com/goi/integration/samsara/dto/ExtSamsaraInspectionRecordDto.java new file mode 100644 index 0000000..7358385 --- /dev/null +++ b/src/main/java/com/goi/integration/samsara/dto/ExtSamsaraInspectionRecordDto.java @@ -0,0 +1,26 @@ +package com.goi.integration.samsara.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +import com.fasterxml.jackson.databind.JsonNode; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class ExtSamsaraInspectionRecordDto { + private String externalId; // inspection id + private String vehicleExternalId; // vehicle.id + private String driverExternalId; // signatoryUser.id + private String inspectionType; // preTrip / postTrip + private LocalDateTime startTime; + private LocalDateTime endTime; + private LocalDateTime signedAt; + private String payloadHash; // SHA-256 + private JsonNode payloadJson; // raw inspection JSON +} diff --git a/src/main/java/com/goi/integration/samsara/job/SamsaraDvirIngestJob.java b/src/main/java/com/goi/integration/samsara/job/SamsaraDvirIngestJob.java new file mode 100644 index 0000000..64f5674 --- /dev/null +++ b/src/main/java/com/goi/integration/samsara/job/SamsaraDvirIngestJob.java @@ -0,0 +1,59 @@ +package com.goi.integration.samsara.job; + +import com.goi.integration.common.config.ScheduleJobConfigDto; +import com.goi.integration.common.config.ScheduleJobConfigProvider; +import com.goi.integration.common.dto.ExtIngestResult; +import com.goi.integration.samsara.client.SamsaraClient; +import com.goi.integration.samsara.service.SamsaraInspectionIngestService; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.time.Duration; +import java.time.Instant; + +@Slf4j +@Component +@RequiredArgsConstructor +public class SamsaraDvirIngestJob { + + private static final String JOB_CODE = "SAMSARA_DVIR"; + + private final ScheduleJobConfigProvider configProvider; + private final SamsaraClient samsaraClient; + private final SamsaraInspectionIngestService ingestService; + + @Scheduled(cron = "${ext.samsara.jobs.dvir.cron:0 */10 * * * *}") + public void run() { + + ScheduleJobConfigDto cfg = configProvider.getJobConfig(JOB_CODE) + .filter(ScheduleJobConfigDto::getSjcEnabled) + .orElse(null); + + if (cfg == null) { + log.info("[{}] disabled or config missing", JOB_CODE); + return; + } + + int limit = (cfg.getSjcMaxRecords() != null) ? cfg.getSjcMaxRecords() : 252; + + Instant now = Instant.now(); + long lookbackSec = Duration.ofHours(cfg.getSjcLookbackHours() != null ? cfg.getSjcLookbackHours() : 24).getSeconds(); + long overlapSec = Duration.ofMinutes(cfg.getSjcOverlapMinutes() != null ? cfg.getSjcOverlapMinutes() : 0).getSeconds(); + + Instant start = now.minusSeconds(lookbackSec + overlapSec); + Instant end = now; + + log.info("[{}] calling samsara dvir history start={} end={} limit={}", JOB_CODE, start, end, limit); + + String rawJson = samsaraClient.getDvirHistory(limit, start, end); + + // opr-rest-api ingest 호출 + ExtIngestResult result = ingestService.ingestFromRawJson(rawJson); + + log.info("[{}] ingest result received={}, inserted={}, updated={}, skipped={}", + JOB_CODE, result.getReceived(), result.getInserted(), result.getUpdated(), result.getSkipped()); + } +} \ No newline at end of file diff --git a/src/main/java/com/goi/integration/samsara/service/SamsaraInspectionIngestService.java b/src/main/java/com/goi/integration/samsara/service/SamsaraInspectionIngestService.java new file mode 100644 index 0000000..220d0e9 --- /dev/null +++ b/src/main/java/com/goi/integration/samsara/service/SamsaraInspectionIngestService.java @@ -0,0 +1,103 @@ +package com.goi.integration.samsara.service; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.goi.integration.common.dto.ExtIngestResult; +import com.goi.integration.common.util.DateTimeUtil; +import com.goi.integration.common.util.ExtPayloadHashUtil; +import com.goi.integration.samsara.client.OprIngestClient; +import com.goi.integration.samsara.dto.ExtSamsaraInspectionIngestCommand; +import com.goi.integration.samsara.dto.ExtSamsaraInspectionRecordDto; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +@Slf4j +@Service +@RequiredArgsConstructor +public class SamsaraInspectionIngestService { + + private static final String JOB_CODE = "SAMSARA_DVIR"; + + private final ObjectMapper objectMapper; + private final OprIngestClient oprIngestClient; + + /** + * Samsara DVIR raw JSON 응답을 받아 + * 1) record 단위로 분해 + * 2) hash 생성 + * 3) opr-rest-api ingest endpoint 호출 + */ + public ExtIngestResult ingestFromRawJson(String rawJson) { + + try { + // 전체 JSON 파싱 + JsonNode root = objectMapper.readTree(rawJson); + JsonNode data = root.path("data"); + + // data[] 없음 + if (!data.isArray()) { + log.warn("Samsara DVIR response has no data[] array"); + return ExtIngestResult.empty("SAMSARA", "DVIR"); + } + + // + List records = new ArrayList<>(); + log.info("[{}] data={}", JOB_CODE, data.size()); + + // data[] 각 node 가 하나의 inspection (preTrip / postTrip) + for (JsonNode node : data) { + // parsing + String externalId = node.path("id").asText(null); + String vehicleExtId = node.path("vehicle").path("id").asText(null); + String driverExtId = node.path("authorSignature") + .path("signatoryUser") + .path("id") + .asText(null); + String inspectionType = node.path("type").asText(null); + LocalDateTime startTime = DateTimeUtil.parseToToronto(node.path("startTime").asText(null)); + LocalDateTime endTime = DateTimeUtil.parseToToronto(node.path("endTime").asText(null)); + LocalDateTime signedAt = DateTimeUtil.parseToToronto( + node.path("authorSignature").path("signedAtTime").asText(null) + ); + // record 단위 hash 생성 (opr-rest-api 에서 idempotent ingest 판단용) + String hash = ExtPayloadHashUtil.sha256FromJsonNode(node); + + // record DTO + records.add( + ExtSamsaraInspectionRecordDto.builder() + .externalId(externalId) + .vehicleExternalId(vehicleExtId) + .driverExternalId(driverExtId) + .inspectionType(inspectionType) + .startTime(startTime) + .endTime(endTime) + .signedAt(signedAt) + .payloadJson(node) // 원본 payload (JSON 그대로 저장) + .payloadHash(hash) + .build() + ); + } + + // ingest command 생성 + ExtSamsaraInspectionIngestCommand command = + ExtSamsaraInspectionIngestCommand.builder() + .source("SAMSARA") + .recordType("DVIR") + .fetchedAt(LocalDateTime.now()) + .records(records) + .build(); + + // opr-rest-api ingest endpoint 호출 + return oprIngestClient.ingestInspection(command); + + } catch (Exception e) { + log.error("Failed to ingest samsara DVIR payload", e); + throw new RuntimeException(e); + } + } +} diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 9cf1edd..97acab4 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -14,5 +14,39 @@ management: exposure: include: health server: + port: 8091 servlet: - context-path: /integration-service \ No newline at end of file + context-path: /integration-service +# ============================ +# External Integrations +# ============================ +ext: + opr: + base-url: http://localhost:8083/opr-rest-api # opr-rest-api 주소 + ingest-path: /ext/samsara/inspections/ingest + internal-token: ${OPR_INTERNAL_TOKEN} + hcm: + base-url: http://localhost:8081/hcm-rest-api + internal-token: ${HCM_INTERNAL_TOKEN} + acc: + base-url: http://localhost:8084/acc-rest-api + internal-token: ${ACC_INTERNAL_TOKEN} + samsara: + base-url: https://api.samsara.com + api-token: ${SAMSARA_API_TOKEN} # 반드시 env 로 + timeout: + connect-ms: 5000 + read-ms: 10000 + + jobs: + dvir: + cron: "0 */10 * * * *" # 10분 +# ============================ +# logging +# ============================ +logging: + file: + name: logs/integration-service.log + level: + root: INFO + com.goi.integration: INFO \ No newline at end of file