From 5855133b96506e1c199fc75d5cddc317b6bd8de9 Mon Sep 17 00:00:00 2001 From: Nakka Leela Prasanth Date: Mon, 19 Apr 2021 16:44:20 +0530 Subject: [PATCH 1/8] #Leela | Adds Jenkinsfile for Jenkins integration --- pipelines/Jenkinsfile | 103 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 pipelines/Jenkinsfile diff --git a/pipelines/Jenkinsfile b/pipelines/Jenkinsfile new file mode 100644 index 0000000..f50dc10 --- /dev/null +++ b/pipelines/Jenkinsfile @@ -0,0 +1,103 @@ +pipeline { + agent { + kubernetes { + yaml """ + apiVersion: v1 + kind: Pod + spec: + containers: + - name: docker + image: registry.nhadclmgm.tcl.com:5000/docker:dind + securityContext: + privileged: true + resources: + limits: + cpu: '300m' + memory: '500Mi' + requests: + cpu: '100m' + memory: '300Mi' + - name: java11 + image: adoptopenjdk/maven-openjdk11 + command: + - sleep + args: + - infinity + resources: + limits: + cpu: '1000m' + memory: '2000Mi' + requests: + cpu: '400m' + memory: '1200Mi' + """ + defaultContainer 'java11' + } + } + + options { + skipDefaultCheckout() + } + + environment { + def IMAGE_NAME= 'gateway' + def DOCKER_FILE_DIRECTORY = "Dockerfile" + def DOCKER_HARBOR_URL = "harbor.nhadclmgm.tatacommunications.com" + def HARBOR_REPO = "gateway" + def IMAGE_TAG = "latest" + def IMAGE_REPO = "${DOCKER_HARBOR_URL + '/' + HARBOR_REPO + '/'+ IMAGE_NAME}" + def IMAGE = "${IMAGE_REPO + ':' + IMAGE_TAG}" + } + + stages { + stage('Get latest version of code') { + steps { + script{ + def scmVar = checkout([ + $class: 'GitSCM', branches: scm.branches, + extensions: scm.extensions + [[$class: 'CleanBeforeCheckout', deleteUntrackedNestedRepositories: true]], + userRemoteConfigs: scm.userRemoteConfigs]) + sh "echo ${scmVar.GIT_COMMIT} | head -c7 >> commit_sha.txt" + if(env.TAG_NAME){ + sh "echo $TAG_NAME > commit_sha.txt" + sh "cat commit_sha.txt" + } + } + } + } + + stage("Run tests and Build"){ + steps{ + container('java11'){ + sh "./gradlew clean test" + sh "./gradlew clean bootJar" + } + } + } + + stage ('Build Docker Image and Push to NHA Harbor') { + when { + anyOf{ + branch 'master' + buildingTag() + } + } + steps { + withCredentials([ + usernamePassword(credentialsId: 'NDHM_HARBOR_HUB_PASSWORD_GATEWAY', usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD')] + ){ + container('docker'){ + script{ + def COMMIT_SHA=readFile(file: 'commit_sha.txt') + dockerImage = docker.build IMAGE + sh "docker login ${DOCKER_HARBOR_URL} -u ${USERNAME} -p ${PASSWORD}" + sh "docker push ${IMAGE}" + sh "docker tag ${IMAGE_REPO} ${IMAGE_REPO}:${COMMIT_SHA}" + sh "docker push ${IMAGE_REPO}:${COMMIT_SHA}" + } + } + } + } + } + } +} \ No newline at end of file From e0ce70c22ad56c9dcad266943ccb410c1f1c919b Mon Sep 17 00:00:00 2001 From: "richa.singh" Date: Thu, 22 Apr 2021 15:40:34 +0530 Subject: [PATCH 2/8] Richa | API to get govt programs as HIPs --- .../gateway/SecurityConfiguration.java | 4 ++- .../projecteka/gateway/common/Constants.java | 1 + .../gateway/registry/RegistryController.java | 7 +++++ .../gateway/registry/RegistryRepository.java | 30 +++++++++++++++++++ .../gateway/registry/RegistryService.java | 5 ++++ .../gateway/registry/model/GovtProgram.java | 13 ++++++++ 6 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 src/main/java/in/projecteka/gateway/registry/model/GovtProgram.java diff --git a/src/main/java/in/projecteka/gateway/SecurityConfiguration.java b/src/main/java/in/projecteka/gateway/SecurityConfiguration.java index c5fa092..5993f55 100644 --- a/src/main/java/in/projecteka/gateway/SecurityConfiguration.java +++ b/src/main/java/in/projecteka/gateway/SecurityConfiguration.java @@ -33,6 +33,7 @@ import static in.projecteka.gateway.common.Constants.HFR_BRIDGES_BRIDGE_ID_SERVICES; import static in.projecteka.gateway.common.Constants.INTERNAL_BRIDGES; import static in.projecteka.gateway.common.Constants.INTERNAL_BRIDGES_BRIDGE_ID_SERVICES; +import static in.projecteka.gateway.common.Constants.INTERNAL_PATH_GET_GOVT_PROGRAMS_LIST; import static in.projecteka.gateway.common.Constants.INTERNAL_CM; import static in.projecteka.gateway.common.Constants.INTERNAL_GET_FACILITY_BY_ID; import static in.projecteka.gateway.common.Constants.INTERNAL_SEARCH_FACILITY_BY_NAME; @@ -151,7 +152,8 @@ public class SecurityConfiguration { PATH_SUBSCRIPTION_REQUESTS_NOTIFY, INTERNAL_SEARCH_FACILITY_BY_NAME, INTERNAL_GET_FACILITY_BY_ID, - PATH_PATIENTS_SMS_ON_NOTIFY + PATH_PATIENTS_SMS_ON_NOTIFY, + INTERNAL_PATH_GET_GOVT_PROGRAMS_LIST }; protected static final String[] ALLOW_LIST_APIS = { diff --git a/src/main/java/in/projecteka/gateway/common/Constants.java b/src/main/java/in/projecteka/gateway/common/Constants.java index 6328693..6445f66 100644 --- a/src/main/java/in/projecteka/gateway/common/Constants.java +++ b/src/main/java/in/projecteka/gateway/common/Constants.java @@ -93,6 +93,7 @@ public class Constants { public static final String HFR_BRIDGES_BRIDGE_ID_SERVICES = "/bridges/{bridgeId}/services"; public static final String INTERNAL_SEARCH_FACILITY_BY_NAME = CURRENT_VERSION + "/internal/facilities"; public static final String INTERNAL_GET_FACILITY_BY_ID = CURRENT_VERSION + "/internal/facilities/{serviceId}"; + public static final String INTERNAL_PATH_GET_GOVT_PROGRAMS_LIST = CURRENT_VERSION + "/internal/govt-programs"; //API to be called on HIP/HIU bridge public static final String PATH_BRIDGE_ON_FETCH_AUTH_MODES = CURRENT_VERSION + "/users/auth/on-fetch-modes"; diff --git a/src/main/java/in/projecteka/gateway/registry/RegistryController.java b/src/main/java/in/projecteka/gateway/registry/RegistryController.java index b40011e..56502f3 100644 --- a/src/main/java/in/projecteka/gateway/registry/RegistryController.java +++ b/src/main/java/in/projecteka/gateway/registry/RegistryController.java @@ -5,6 +5,7 @@ import in.projecteka.gateway.registry.model.BridgeServiceRequest; import in.projecteka.gateway.registry.model.CMServiceRequest; import in.projecteka.gateway.registry.model.FacilityRepresentation; +import in.projecteka.gateway.registry.model.GovtProgram; import in.projecteka.gateway.registry.model.HFRBridgeResponse; import in.projecteka.gateway.registry.model.ServiceDetailsResponse; import in.projecteka.gateway.registry.model.ServiceProfileResponse; @@ -28,6 +29,7 @@ import static in.projecteka.gateway.common.Constants.INTERNAL_BRIDGES_BRIDGE_ID_SERVICES; import static in.projecteka.gateway.common.Constants.INTERNAL_CM; import static in.projecteka.gateway.common.Constants.INTERNAL_GET_FACILITY_BY_ID; +import static in.projecteka.gateway.common.Constants.INTERNAL_PATH_GET_GOVT_PROGRAMS_LIST; import static in.projecteka.gateway.common.Constants.INTERNAL_SEARCH_FACILITY_BY_NAME; import static in.projecteka.gateway.common.Constants.UNSPECIFIED_SERVICE_TYPE; @@ -79,4 +81,9 @@ public Mono> searchFacilityByName(@RequestParam Str public Mono searchFacilityByName(@PathVariable String serviceId) { return registryService.getFacilityById(serviceId); } + + @GetMapping(INTERNAL_PATH_GET_GOVT_PROGRAMS_LIST) + public Mono> fetchGovtPrograms() { + return registryService.fetchGovtPrograms(); + } } diff --git a/src/main/java/in/projecteka/gateway/registry/RegistryRepository.java b/src/main/java/in/projecteka/gateway/registry/RegistryRepository.java index 6062968..dc6eb48 100644 --- a/src/main/java/in/projecteka/gateway/registry/RegistryRepository.java +++ b/src/main/java/in/projecteka/gateway/registry/RegistryRepository.java @@ -10,6 +10,7 @@ import in.projecteka.gateway.registry.model.EndpointDetails; import in.projecteka.gateway.registry.model.Endpoints; import in.projecteka.gateway.registry.model.FacilityRepresentation; +import in.projecteka.gateway.registry.model.GovtProgram; import in.projecteka.gateway.registry.model.HFRBridgeResponse; import in.projecteka.gateway.registry.model.ServiceDetailsResponse; import in.projecteka.gateway.registry.model.ServiceProfile; @@ -72,6 +73,7 @@ public class RegistryRepository { private static final String SELECT_FACILITIES_BY_NAME = "SELECT service_id, name, is_hip, is_hiu, is_health_locker " + "FROM bridge_service WHERE UPPER(name) LIKE $1 AND is_hip = true"; + private static final String SELECT_GOVT_PROGRAMS = "SELECT hip_id, name FROM govt_programs"; private final PgPool readWriteClient; private final PgPool readOnlyClient; @@ -496,4 +498,32 @@ private FacilityRepresentation toFacilityRepresentation(Row row) { .facilityType(serviceTypes) .build(); } + + public Mono> fetchGovtPrograms() { + return Mono.create(monoSink -> this.readOnlyClient.preparedQuery(SELECT_GOVT_PROGRAMS) + .execute( + handler -> { + if (handler.failed()) { + logger.error(handler.cause().getMessage(), handler.cause()); + monoSink.error(new DbOperationError("Failed to fetch govt programs from govt_programs")); + return; + } + var iterator = handler.result().iterator(); + if (!iterator.hasNext()) { + monoSink.success(); + return; + } + RowSet rowSet = handler.result(); + List results = new ArrayList<>(); + if (rowSet.iterator().hasNext()) { + rowSet.forEach(row -> { + results.add(GovtProgram.builder() + .hipId(row.getString("hip_id")) + .name(row.getString("name")) + .build()); + }); + } + monoSink.success(results); + })); + } } diff --git a/src/main/java/in/projecteka/gateway/registry/RegistryService.java b/src/main/java/in/projecteka/gateway/registry/RegistryService.java index 837e5e7..678035a 100644 --- a/src/main/java/in/projecteka/gateway/registry/RegistryService.java +++ b/src/main/java/in/projecteka/gateway/registry/RegistryService.java @@ -15,6 +15,7 @@ import in.projecteka.gateway.registry.model.EndpointDetails; import in.projecteka.gateway.registry.model.Endpoints; import in.projecteka.gateway.registry.model.FacilityRepresentation; +import in.projecteka.gateway.registry.model.GovtProgram; import in.projecteka.gateway.registry.model.HFRBridgeResponse; import in.projecteka.gateway.registry.model.ServiceDetailsResponse; import in.projecteka.gateway.registry.model.ServiceProfileResponse; @@ -374,5 +375,9 @@ public Mono getFacilityById(String serviceId) { .build(); }); } + + public Mono> fetchGovtPrograms(){ + return registryRepository.fetchGovtPrograms(); + } } diff --git a/src/main/java/in/projecteka/gateway/registry/model/GovtProgram.java b/src/main/java/in/projecteka/gateway/registry/model/GovtProgram.java new file mode 100644 index 0000000..69410c5 --- /dev/null +++ b/src/main/java/in/projecteka/gateway/registry/model/GovtProgram.java @@ -0,0 +1,13 @@ +package in.projecteka.gateway.registry.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; + +@AllArgsConstructor +@Data +@Builder +public class GovtProgram { + private String hipId; + private String name; +} From 0ace8a81aa3e405f370e1d6791d923177f4553d9 Mon Sep 17 00:00:00 2001 From: "richa.singh" Date: Mon, 26 Apr 2021 15:56:22 +0530 Subject: [PATCH 3/8] Richa | Fetch govt programs api| change responsebody --- .../gateway/registry/RegistryController.java | 2 +- .../gateway/registry/RegistryRepository.java | 31 +++++++------------ .../gateway/registry/RegistryService.java | 4 +-- 3 files changed, 14 insertions(+), 23 deletions(-) diff --git a/src/main/java/in/projecteka/gateway/registry/RegistryController.java b/src/main/java/in/projecteka/gateway/registry/RegistryController.java index 56502f3..8bc2960 100644 --- a/src/main/java/in/projecteka/gateway/registry/RegistryController.java +++ b/src/main/java/in/projecteka/gateway/registry/RegistryController.java @@ -83,7 +83,7 @@ public Mono searchFacilityByName(@PathVariable String se } @GetMapping(INTERNAL_PATH_GET_GOVT_PROGRAMS_LIST) - public Mono> fetchGovtPrograms() { + public Mono> fetchGovtPrograms() { return registryService.fetchGovtPrograms(); } } diff --git a/src/main/java/in/projecteka/gateway/registry/RegistryRepository.java b/src/main/java/in/projecteka/gateway/registry/RegistryRepository.java index dc6eb48..01b9547 100644 --- a/src/main/java/in/projecteka/gateway/registry/RegistryRepository.java +++ b/src/main/java/in/projecteka/gateway/registry/RegistryRepository.java @@ -73,7 +73,9 @@ public class RegistryRepository { private static final String SELECT_FACILITIES_BY_NAME = "SELECT service_id, name, is_hip, is_hiu, is_health_locker " + "FROM bridge_service WHERE UPPER(name) LIKE $1 AND is_hip = true"; - private static final String SELECT_GOVT_PROGRAMS = "SELECT hip_id, name FROM govt_programs"; + private static final String SELECT_GOVT_PROGRAMS = "SELECT service_id, gp.name as name, is_hip, is_hiu, is_health_locker " + + " FROM bridge_service INNER JOIN govt_programs AS gp ON gp.hip_id = service_id" + + " WHERE active = true AND is_hip = true"; private final PgPool readWriteClient; private final PgPool readOnlyClient; @@ -499,31 +501,20 @@ private FacilityRepresentation toFacilityRepresentation(Row row) { .build(); } - public Mono> fetchGovtPrograms() { - return Mono.create(monoSink -> this.readOnlyClient.preparedQuery(SELECT_GOVT_PROGRAMS) - .execute( - handler -> { + public Flux fetchGovtPrograms() { + return Flux.create(fluxSink -> this.readOnlyClient.preparedQuery(SELECT_GOVT_PROGRAMS) + .execute(handler -> { if (handler.failed()) { logger.error(handler.cause().getMessage(), handler.cause()); - monoSink.error(new DbOperationError("Failed to fetch govt programs from govt_programs")); - return; - } - var iterator = handler.result().iterator(); - if (!iterator.hasNext()) { - monoSink.success(); + fluxSink.error(new DbOperationError("Failed to fetch govt programs from govt_programs")); return; } RowSet rowSet = handler.result(); - List results = new ArrayList<>(); - if (rowSet.iterator().hasNext()) { - rowSet.forEach(row -> { - results.add(GovtProgram.builder() - .hipId(row.getString("hip_id")) - .name(row.getString("name")) - .build()); - }); + for (var row: rowSet) { + fluxSink.next(toFacilityRepresentation(row)); } - monoSink.success(results); + + fluxSink.complete(); })); } } diff --git a/src/main/java/in/projecteka/gateway/registry/RegistryService.java b/src/main/java/in/projecteka/gateway/registry/RegistryService.java index 678035a..f63cce3 100644 --- a/src/main/java/in/projecteka/gateway/registry/RegistryService.java +++ b/src/main/java/in/projecteka/gateway/registry/RegistryService.java @@ -376,8 +376,8 @@ public Mono getFacilityById(String serviceId) { }); } - public Mono> fetchGovtPrograms(){ - return registryRepository.fetchGovtPrograms(); + public Mono> fetchGovtPrograms(){ + return registryRepository.fetchGovtPrograms().collectList(); } } From 92964947dd1c0e654e22346d501f54090079f76a Mon Sep 17 00:00:00 2001 From: "richa.singh" Date: Mon, 26 Apr 2021 16:03:38 +0530 Subject: [PATCH 4/8] Richa | Fetch govt programs api| Remove GovtProgram class --- .../gateway/registry/RegistryController.java | 1 - .../gateway/registry/RegistryRepository.java | 3 --- .../gateway/registry/RegistryService.java | 1 - .../gateway/registry/model/GovtProgram.java | 13 ------------- 4 files changed, 18 deletions(-) delete mode 100644 src/main/java/in/projecteka/gateway/registry/model/GovtProgram.java diff --git a/src/main/java/in/projecteka/gateway/registry/RegistryController.java b/src/main/java/in/projecteka/gateway/registry/RegistryController.java index 8bc2960..3fa69ba 100644 --- a/src/main/java/in/projecteka/gateway/registry/RegistryController.java +++ b/src/main/java/in/projecteka/gateway/registry/RegistryController.java @@ -5,7 +5,6 @@ import in.projecteka.gateway.registry.model.BridgeServiceRequest; import in.projecteka.gateway.registry.model.CMServiceRequest; import in.projecteka.gateway.registry.model.FacilityRepresentation; -import in.projecteka.gateway.registry.model.GovtProgram; import in.projecteka.gateway.registry.model.HFRBridgeResponse; import in.projecteka.gateway.registry.model.ServiceDetailsResponse; import in.projecteka.gateway.registry.model.ServiceProfileResponse; diff --git a/src/main/java/in/projecteka/gateway/registry/RegistryRepository.java b/src/main/java/in/projecteka/gateway/registry/RegistryRepository.java index 01b9547..bd4f30a 100644 --- a/src/main/java/in/projecteka/gateway/registry/RegistryRepository.java +++ b/src/main/java/in/projecteka/gateway/registry/RegistryRepository.java @@ -1,6 +1,5 @@ package in.projecteka.gateway.registry; -import in.projecteka.gateway.clients.ClientError; import in.projecteka.gateway.common.DbOperationError; import in.projecteka.gateway.registry.model.Bridge; import in.projecteka.gateway.registry.model.BridgeRegistryRequest; @@ -10,7 +9,6 @@ import in.projecteka.gateway.registry.model.EndpointDetails; import in.projecteka.gateway.registry.model.Endpoints; import in.projecteka.gateway.registry.model.FacilityRepresentation; -import in.projecteka.gateway.registry.model.GovtProgram; import in.projecteka.gateway.registry.model.HFRBridgeResponse; import in.projecteka.gateway.registry.model.ServiceDetailsResponse; import in.projecteka.gateway.registry.model.ServiceProfile; @@ -30,7 +28,6 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; -import java.util.Objects; import static in.projecteka.gateway.common.Serializer.from; import static in.projecteka.gateway.common.Serializer.to; diff --git a/src/main/java/in/projecteka/gateway/registry/RegistryService.java b/src/main/java/in/projecteka/gateway/registry/RegistryService.java index f63cce3..7807af3 100644 --- a/src/main/java/in/projecteka/gateway/registry/RegistryService.java +++ b/src/main/java/in/projecteka/gateway/registry/RegistryService.java @@ -15,7 +15,6 @@ import in.projecteka.gateway.registry.model.EndpointDetails; import in.projecteka.gateway.registry.model.Endpoints; import in.projecteka.gateway.registry.model.FacilityRepresentation; -import in.projecteka.gateway.registry.model.GovtProgram; import in.projecteka.gateway.registry.model.HFRBridgeResponse; import in.projecteka.gateway.registry.model.ServiceDetailsResponse; import in.projecteka.gateway.registry.model.ServiceProfileResponse; diff --git a/src/main/java/in/projecteka/gateway/registry/model/GovtProgram.java b/src/main/java/in/projecteka/gateway/registry/model/GovtProgram.java deleted file mode 100644 index 69410c5..0000000 --- a/src/main/java/in/projecteka/gateway/registry/model/GovtProgram.java +++ /dev/null @@ -1,13 +0,0 @@ -package in.projecteka.gateway.registry.model; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; - -@AllArgsConstructor -@Data -@Builder -public class GovtProgram { - private String hipId; - private String name; -} From 83a99e619de4873eb877cf5ebd2f9a0903cf0176 Mon Sep 17 00:00:00 2001 From: "richa.singh" Date: Mon, 26 Apr 2021 16:47:03 +0530 Subject: [PATCH 5/8] Richa | SHubhanshu | Minor log fix --- .../java/in/projecteka/gateway/registry/RegistryRepository.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/in/projecteka/gateway/registry/RegistryRepository.java b/src/main/java/in/projecteka/gateway/registry/RegistryRepository.java index bd4f30a..ae57911 100644 --- a/src/main/java/in/projecteka/gateway/registry/RegistryRepository.java +++ b/src/main/java/in/projecteka/gateway/registry/RegistryRepository.java @@ -503,7 +503,7 @@ public Flux fetchGovtPrograms() { .execute(handler -> { if (handler.failed()) { logger.error(handler.cause().getMessage(), handler.cause()); - fluxSink.error(new DbOperationError("Failed to fetch govt programs from govt_programs")); + fluxSink.error(new DbOperationError("Failed to fetch govt programs")); return; } RowSet rowSet = handler.result(); From 8f6d570c139d421917a029518e433227fb3739df Mon Sep 17 00:00:00 2001 From: MD Dubey Date: Thu, 29 Apr 2021 11:56:08 +0530 Subject: [PATCH 6/8] Dubey | Toggle to disable correlation-ids --- Dockerfile | 2 +- .../in/projecteka/gateway/MdcContextLifterConfiguration.java | 2 ++ src/main/resources/application.yaml | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 6a02f4b..b38cea0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,4 +2,4 @@ FROM adoptopenjdk/openjdk11:jre-11.0.8_10-alpine VOLUME /tmp COPY build/libs/* app.jar EXPOSE 8000 -ENTRYPOINT ["java", "-jar", "/app.jar"] \ No newline at end of file +ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "/app.jar"] \ No newline at end of file diff --git a/src/main/java/in/projecteka/gateway/MdcContextLifterConfiguration.java b/src/main/java/in/projecteka/gateway/MdcContextLifterConfiguration.java index 59d0896..ea6793a 100644 --- a/src/main/java/in/projecteka/gateway/MdcContextLifterConfiguration.java +++ b/src/main/java/in/projecteka/gateway/MdcContextLifterConfiguration.java @@ -1,5 +1,6 @@ package in.projecteka.gateway; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Configuration; import reactor.core.publisher.Hooks; import reactor.core.publisher.Operators; @@ -8,6 +9,7 @@ import javax.annotation.PreDestroy; @Configuration +@ConditionalOnProperty(value = "logging.correlation-enabled", havingValue = "true") public class MdcContextLifterConfiguration { private String MDC_CONTEXT_REACTOR_KEY = in.projecteka.gateway.MdcContextLifterConfiguration.class.getName(); diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index ea70a68..22ebe06 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -67,3 +67,4 @@ webclient: logging: level: ROOT: ${LOG_LEVEL:INFO} + correlation-enabled: ${LOG_CORRELATION_ID:true} From 7e46a036b7f1805f591097dd86e9b6086d0cc359 Mon Sep 17 00:00:00 2001 From: Dheeraj Pundir <1236dheeraj.ds@gmail.com> Date: Tue, 11 May 2021 10:43:48 +0530 Subject: [PATCH 7/8] Dheeraj | Add config to use rabbitMQ with SSL --- .../java/in/projecteka/gateway/GatewayConfiguration.java | 7 ++++++- .../projecteka/gateway/common/heartbeat/Heartbeat.java | 9 +++++++-- .../gateway/common/heartbeat/RabbitmqOptions.java | 1 + src/main/resources/application-local.yml | 1 + src/main/resources/application.yaml | 1 + 5 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/main/java/in/projecteka/gateway/GatewayConfiguration.java b/src/main/java/in/projecteka/gateway/GatewayConfiguration.java index e7d9743..f0dbbd5 100644 --- a/src/main/java/in/projecteka/gateway/GatewayConfiguration.java +++ b/src/main/java/in/projecteka/gateway/GatewayConfiguration.java @@ -98,6 +98,8 @@ import reactor.rabbitmq.ReceiverOptions; import reactor.rabbitmq.SenderOptions; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; import java.time.Duration; import java.util.concurrent.TimeUnit; @@ -907,12 +909,15 @@ public ResponseOrchestrator authConfirmResponseOrchestrator( } @Bean - public ConnectionFactory connectionFactory(RabbitmqOptions rabbitmqOptions) { + public ConnectionFactory connectionFactory(RabbitmqOptions rabbitmqOptions) throws KeyManagementException, NoSuchAlgorithmException { ConnectionFactory connectionFactory = new ConnectionFactory(); connectionFactory.setHost(rabbitmqOptions.getHost()); connectionFactory.setPort(rabbitmqOptions.getPort()); connectionFactory.setUsername(rabbitmqOptions.getUsername()); connectionFactory.setPassword(rabbitmqOptions.getPassword()); + if(rabbitmqOptions.isUseSSL()){ + connectionFactory.useSslProtocol(); + } connectionFactory.useNio(); return connectionFactory; } diff --git a/src/main/java/in/projecteka/gateway/common/heartbeat/Heartbeat.java b/src/main/java/in/projecteka/gateway/common/heartbeat/Heartbeat.java index 509b722..8496549 100644 --- a/src/main/java/in/projecteka/gateway/common/heartbeat/Heartbeat.java +++ b/src/main/java/in/projecteka/gateway/common/heartbeat/Heartbeat.java @@ -10,6 +10,8 @@ import java.io.IOException; import java.net.HttpURLConnection; import java.net.URL; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; import java.util.concurrent.TimeoutException; import static in.projecteka.gateway.clients.model.Error.of; @@ -34,17 +36,20 @@ public Mono getStatus() { return (cacheHealth.isUp() && isRabbitMQUp() && isKeycloakUp()) ? just(HeartbeatResponse.builder().timeStamp(now(UTC)).status(UP).build()) : just(HeartbeatResponse.builder().timeStamp(now(UTC)).status(DOWN).error(of(SERVICE_DOWN)).build()); - } catch (IOException | TimeoutException e) { + } catch (IOException | TimeoutException | KeyManagementException | NoSuchAlgorithmException e) { return just(HeartbeatResponse.builder().timeStamp(now(UTC)).status(DOWN).error(of(SERVICE_DOWN)).build()); } } - private boolean isRabbitMQUp() throws IOException, TimeoutException { + private boolean isRabbitMQUp() throws IOException, TimeoutException, KeyManagementException, NoSuchAlgorithmException { var factory = new ConnectionFactory(); factory.setHost(rabbitmqOptions.getHost()); factory.setPort(rabbitmqOptions.getPort()); factory.setUsername(rabbitmqOptions.getUsername()); factory.setPassword(rabbitmqOptions.getPassword()); + if(rabbitmqOptions.isUseSSL()){ + factory.useSslProtocol(); + } try (Connection connection = factory.newConnection()) { return connection.isOpen(); } diff --git a/src/main/java/in/projecteka/gateway/common/heartbeat/RabbitmqOptions.java b/src/main/java/in/projecteka/gateway/common/heartbeat/RabbitmqOptions.java index 8fe22af..fb9a36f 100644 --- a/src/main/java/in/projecteka/gateway/common/heartbeat/RabbitmqOptions.java +++ b/src/main/java/in/projecteka/gateway/common/heartbeat/RabbitmqOptions.java @@ -15,4 +15,5 @@ public class RabbitmqOptions { private final int channelPoolMaxCacheSize; private final String username; private final String password; + private final boolean useSSL; } diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml index f5f55b8..e816b90 100644 --- a/src/main/resources/application-local.yml +++ b/src/main/resources/application-local.yml @@ -6,6 +6,7 @@ spring: port: 5672 username: guest password: guest + useSSL: false identity: clientId: gateway clientSecret: ${CLIENT_SECRET} diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 22ebe06..ea99a4e 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -42,6 +42,7 @@ spring: username: ${RABBITMQ_USERNAME:guest} password: ${RABBITMQ_PASSWORD:guest} channelPoolMaxCacheSize: ${RABBITMQ_CHANNEL_POOL_SIZE:10} + useSSL: ${RABBITMQ_USE_SSL:true} identity: clientId: ${GATEWAY_CLIENT_ID} clientSecret: ${GATEWAY_CLIENT_SECRET} From a7e0fa1f077cb1064852548f0dc17f285e3fb2de Mon Sep 17 00:00:00 2001 From: Dheeraj Pundir <1236dheeraj.ds@gmail.com> Date: Fri, 21 May 2021 19:08:54 +0530 Subject: [PATCH 8/8] Dheeraj | Add routes for hip data notification and it's acknowledgment --- .../gateway/GatewayConfiguration.java | 43 +++++++++ .../gateway/SecurityConfiguration.java | 6 +- .../HipDataNotificationServiceClient.java | 35 +++++++ .../projecteka/gateway/common/Constants.java | 2 + .../DataNotificationController.java | 47 +++++++++ .../DataNotificationControllerTest.java | 96 +++++++++++++++++++ 6 files changed, 228 insertions(+), 1 deletion(-) create mode 100644 src/main/java/in/projecteka/gateway/clients/HipDataNotificationServiceClient.java create mode 100644 src/main/java/in/projecteka/gateway/data_notification/DataNotificationController.java create mode 100644 src/test/java/in/projecteka/gateway/data_notification/DataNotificationControllerTest.java diff --git a/src/main/java/in/projecteka/gateway/GatewayConfiguration.java b/src/main/java/in/projecteka/gateway/GatewayConfiguration.java index f0dbbd5..7557506 100644 --- a/src/main/java/in/projecteka/gateway/GatewayConfiguration.java +++ b/src/main/java/in/projecteka/gateway/GatewayConfiguration.java @@ -22,6 +22,7 @@ import in.projecteka.gateway.clients.HealthInfoNotificationServiceClient; import in.projecteka.gateway.clients.HipConsentNotifyServiceClient; import in.projecteka.gateway.clients.HipDataFlowServiceClient; +import in.projecteka.gateway.clients.HipDataNotificationServiceClient; import in.projecteka.gateway.clients.HipInitLinkServiceClient; import in.projecteka.gateway.clients.HiuConsentNotifyServiceClient; import in.projecteka.gateway.clients.HiuSubscriptionNotifyServiceClient; @@ -1426,4 +1427,46 @@ public ResponseOrchestrator patientSMSNotifyResponseOrchestrator( DefaultValidatedResponseAction patientSMSNotificationResponseAction) { return new ResponseOrchestrator(validator, patientSMSNotificationResponseAction); } + + @Bean("hipDataNotificationServiceClient") + public HipDataNotificationServiceClient hipDataNotificationServiceClient(ServiceOptions serviceOptions, + @Qualifier("customBuilder") WebClient.Builder builder, + CMRegistry cmRegistry, + IdentityService identityService, + BridgeRegistry bridgeRegistry) { + return new HipDataNotificationServiceClient(serviceOptions, builder, identityService, cmRegistry, bridgeRegistry); + } + + @Bean("hipDataNotificationRequestAction") + public DefaultValidatedRequestAction hipDataNotificationRequestAction( + HipDataNotificationServiceClient hipDataNotificationServiceClient) { + return new DefaultValidatedRequestAction<>(hipDataNotificationServiceClient); + } + + @Bean("hipDataNotificationRequestOrchestrator") + public RequestOrchestrator hipDataNotificationRequestOrchestrator( + @Qualifier("requestIdMappings") CacheAdapter requestIdMappings, + RedundantRequestValidator redundantRequestValidator, + Validator validator, + HipDataNotificationServiceClient hipDataNotificationServiceClient, + DefaultValidatedRequestAction hipDataNotificationRequestAction) { + return new RequestOrchestrator<>(requestIdMappings, + redundantRequestValidator, + validator, + hipDataNotificationServiceClient, + hipDataNotificationRequestAction); + } + + @Bean("hipDataNotificationResponseAction") + public DefaultValidatedResponseAction hipDataNotificationResponseAction( + HipDataNotificationServiceClient hipDataNotificationServiceClient) { + return new DefaultValidatedResponseAction<>(hipDataNotificationServiceClient); + } + + @Bean("hipDataNotificationResponseOrchestrator") + public ResponseOrchestrator hipDataNotificationResponseOrchestrator( + Validator validator, + DefaultValidatedResponseAction hipDataNotificationResponseAction) { + return new ResponseOrchestrator(validator, hipDataNotificationResponseAction); + } } diff --git a/src/main/java/in/projecteka/gateway/SecurityConfiguration.java b/src/main/java/in/projecteka/gateway/SecurityConfiguration.java index 5993f55..99ab626 100644 --- a/src/main/java/in/projecteka/gateway/SecurityConfiguration.java +++ b/src/main/java/in/projecteka/gateway/SecurityConfiguration.java @@ -58,6 +58,8 @@ import static in.projecteka.gateway.common.Constants.PATH_HEALTH_INFORMATION_HIP_REQUEST; import static in.projecteka.gateway.common.Constants.PATH_HEALTH_INFORMATION_NOTIFY; import static in.projecteka.gateway.common.Constants.PATH_HEARTBEAT; +import static in.projecteka.gateway.common.Constants.PATH_HIP_DATA_NOTIFICATION; +import static in.projecteka.gateway.common.Constants.PATH_HIP_DATA_NOTIFICATION_ACKNOWLEDGEMENT; import static in.projecteka.gateway.common.Constants.PATH_HIU_SUBSCRIPTION_NOTIFY; import static in.projecteka.gateway.common.Constants.PATH_HIU_SUBSCRIPTION_ON_NOTIFY; import static in.projecteka.gateway.common.Constants.PATH_LINK_CONFIRM; @@ -118,7 +120,8 @@ public class SecurityConfiguration { PATH_HEALTH_INFORMATION_HIP_ON_REQUEST, PATH_ADD_CARE_CONTEXTS, PATH_PATIENT_ON_SHARE, - PATH_PATIENTS_SMS_NOTIFY + PATH_PATIENTS_SMS_NOTIFY, + PATH_HIP_DATA_NOTIFICATION }; protected static final String[] HIU_HIP_APIS = new String[]{ @@ -148,6 +151,7 @@ public class SecurityConfiguration { PATH_CONSENT_REQUEST_ON_STATUS, PATH_PATIENT_SHARE, PATH_ON_FETCH_AUTH_MODES, + PATH_HIP_DATA_NOTIFICATION_ACKNOWLEDGEMENT, PATH_HIU_SUBSCRIPTION_NOTIFY, PATH_SUBSCRIPTION_REQUESTS_NOTIFY, INTERNAL_SEARCH_FACILITY_BY_NAME, diff --git a/src/main/java/in/projecteka/gateway/clients/HipDataNotificationServiceClient.java b/src/main/java/in/projecteka/gateway/clients/HipDataNotificationServiceClient.java new file mode 100644 index 0000000..541238a --- /dev/null +++ b/src/main/java/in/projecteka/gateway/clients/HipDataNotificationServiceClient.java @@ -0,0 +1,35 @@ +package in.projecteka.gateway.clients; + +import in.projecteka.gateway.common.Constants; +import in.projecteka.gateway.common.IdentityService; +import in.projecteka.gateway.common.cache.ServiceOptions; +import in.projecteka.gateway.registry.BridgeRegistry; +import in.projecteka.gateway.registry.CMRegistry; +import in.projecteka.gateway.registry.ServiceType; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Mono; + +public class HipDataNotificationServiceClient extends ServiceClient { + private final CMRegistry cmRegistry; + private final BridgeRegistry bridgeRegistry; + + public HipDataNotificationServiceClient(ServiceOptions serviceOptions, + WebClient.Builder webClientBuilder, + IdentityService identityService, + CMRegistry cmRegistry, + BridgeRegistry bridgeRegistry) { + super(serviceOptions, webClientBuilder, identityService); + this.cmRegistry = cmRegistry; + this.bridgeRegistry = bridgeRegistry; + } + + @Override + protected Mono getResponseUrl(String clientId, ServiceType serviceType) { + return bridgeRegistry.getHostFor(clientId, serviceType).map(host -> host + Constants.PATH_HIP_DATA_NOTIFICATION_ACKNOWLEDGEMENT); + } + + @Override + protected Mono getRequestUrl(String clientId, ServiceType serviceType) { + return cmRegistry.getHostFor(clientId).map(host -> host + Constants.PATH_HIP_DATA_NOTIFICATION); + } +} diff --git a/src/main/java/in/projecteka/gateway/common/Constants.java b/src/main/java/in/projecteka/gateway/common/Constants.java index 6445f66..2e0c804 100644 --- a/src/main/java/in/projecteka/gateway/common/Constants.java +++ b/src/main/java/in/projecteka/gateway/common/Constants.java @@ -86,6 +86,8 @@ public class Constants { public static final String PATH_HIU_SUBSCRIPTION_ON_NOTIFY = CURRENT_VERSION + "/subscriptions/hiu/on-notify"; public static final String PATH_PATIENTS_SMS_NOTIFY = CURRENT_VERSION + "/patients/sms/notify"; public static final String PATH_PATIENTS_SMS_ON_NOTIFY = CURRENT_VERSION + "/patients/sms/on-notify"; + public static final String PATH_HIP_DATA_NOTIFICATION = CURRENT_VERSION + "/links/context/notify"; + public static final String PATH_HIP_DATA_NOTIFICATION_ACKNOWLEDGEMENT = CURRENT_VERSION + "/links/context/on-notify"; public static final String ROUTE_PATH_CM_HIU_SUBSCRIPTION_ON_NOTIFY = CURRENT_VERSION + "/subscriptions/on-notify"; public static final String GW_PATH_HI_SERVICE_BY_ID = CURRENT_VERSION + "/hi-services/{service-id}"; public static final String GW_PATH_HI_SERVICES = CURRENT_VERSION + "/hi-services"; diff --git a/src/main/java/in/projecteka/gateway/data_notification/DataNotificationController.java b/src/main/java/in/projecteka/gateway/data_notification/DataNotificationController.java new file mode 100644 index 0000000..313be46 --- /dev/null +++ b/src/main/java/in/projecteka/gateway/data_notification/DataNotificationController.java @@ -0,0 +1,47 @@ +package in.projecteka.gateway.data_notification; + +import in.projecteka.gateway.clients.HipDataNotificationServiceClient; +import in.projecteka.gateway.common.Caller; +import in.projecteka.gateway.common.RequestOrchestrator; +import in.projecteka.gateway.common.ResponseOrchestrator; +import lombok.AllArgsConstructor; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpStatus; +import org.springframework.security.core.context.ReactiveSecurityContextHolder; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; +import reactor.core.publisher.Mono; + +import static in.projecteka.gateway.common.Constants.API_CALLED; +import static in.projecteka.gateway.common.Constants.PATH_HIP_DATA_NOTIFICATION; +import static in.projecteka.gateway.common.Constants.PATH_HIP_DATA_NOTIFICATION_ACKNOWLEDGEMENT; +import static in.projecteka.gateway.common.Constants.X_CM_ID; +import static in.projecteka.gateway.common.Constants.X_HIP_ID; +import static in.projecteka.gateway.common.Constants.bridgeId; + + +@RestController +@AllArgsConstructor +public class DataNotificationController { + RequestOrchestrator hipDataNotificationRequestOrchestrator; + ResponseOrchestrator hipDataNotificationResponseOrchestrator; + + @ResponseStatus(HttpStatus.ACCEPTED) + @PostMapping(PATH_HIP_DATA_NOTIFICATION) + public Mono hipDataNotification(HttpEntity requestEntity) { + return ReactiveSecurityContextHolder.getContext() + .map(securityContext -> (Caller) securityContext.getAuthentication().getPrincipal()) + .map(Caller::getClientId) + .flatMap(clientId -> hipDataNotificationRequestOrchestrator + .handleThis(requestEntity, X_CM_ID, X_HIP_ID, bridgeId(clientId)) + .subscriberContext(context -> context.put(API_CALLED, PATH_HIP_DATA_NOTIFICATION))); + } + + @ResponseStatus(HttpStatus.ACCEPTED) + @PostMapping(PATH_HIP_DATA_NOTIFICATION_ACKNOWLEDGEMENT) + public Mono hipDataNotificationAcknowledgement(HttpEntity requestEntity) { + return hipDataNotificationResponseOrchestrator.processResponse(requestEntity, X_HIP_ID) + .subscriberContext(context -> context.put(API_CALLED, PATH_HIP_DATA_NOTIFICATION_ACKNOWLEDGEMENT)); + } +} diff --git a/src/test/java/in/projecteka/gateway/data_notification/DataNotificationControllerTest.java b/src/test/java/in/projecteka/gateway/data_notification/DataNotificationControllerTest.java new file mode 100644 index 0000000..31e233b --- /dev/null +++ b/src/test/java/in/projecteka/gateway/data_notification/DataNotificationControllerTest.java @@ -0,0 +1,96 @@ +package in.projecteka.gateway.data_notification; + +import com.nimbusds.jose.jwk.JWKSet; +import in.projecteka.gateway.clients.HipDataNotificationServiceClient; +import in.projecteka.gateway.common.Authenticator; +import in.projecteka.gateway.common.Constants; +import in.projecteka.gateway.common.RequestOrchestrator; +import in.projecteka.gateway.common.ResponseOrchestrator; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.web.reactive.server.WebTestClient; +import reactor.core.publisher.Mono; + +import java.util.List; + +import static in.projecteka.gateway.common.Constants.BRIDGE_ID_PREFIX; +import static in.projecteka.gateway.common.Constants.X_CM_ID; +import static in.projecteka.gateway.common.Constants.X_HIP_ID; +import static in.projecteka.gateway.common.Role.CM; +import static in.projecteka.gateway.common.Role.HIP; +import static in.projecteka.gateway.testcommon.TestBuilders.caller; +import static in.projecteka.gateway.testcommon.TestBuilders.string; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; +import static org.springframework.http.HttpHeaders.AUTHORIZATION; +import static org.springframework.http.MediaType.APPLICATION_JSON; +import static reactor.core.publisher.Mono.empty; +import static reactor.core.publisher.Mono.just; + +@ExtendWith(SpringExtension.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@AutoConfigureWebTestClient +class DataNotificationControllerTest { + @MockBean + @Qualifier("hipDataNotificationRequestOrchestrator") + RequestOrchestrator hipDataNotificationRequestOrchestrator; + + @Autowired + WebTestClient webTestClient; + + @MockBean(name = "centralRegistryJWKSet") + JWKSet centralRegistryJWKSet; + + @MockBean + Authenticator authenticator; + + @Qualifier("hipDataNotificationResponseOrchestrator") + @MockBean + ResponseOrchestrator hipDataNotificationResponseOrchestrator; + + @Test + void shouldFireAndForgetForHipDataNotification() { + var token = string(); + var clientId = string(); + when(hipDataNotificationRequestOrchestrator + .handleThis(any(), eq(X_CM_ID), eq(X_HIP_ID), eq(BRIDGE_ID_PREFIX + clientId))) + .thenReturn(empty()); + when(authenticator.verify(token)) + .thenReturn(just(caller().clientId(clientId).roles(List.of(HIP)).build())); + + webTestClient + .post() + .uri(Constants.PATH_HIP_DATA_NOTIFICATION) + .header(AUTHORIZATION, token) + .contentType(APPLICATION_JSON) + .bodyValue("{}") + .exchange() + .expectStatus() + .isAccepted(); + } + + @Test + void shouldFireAndForgetForHipDataNotificationAcknowledgment() { + var token = string(); + when(authenticator.verify(token)).thenReturn(just(caller().roles(List.of(CM)).build())); + when(hipDataNotificationResponseOrchestrator.processResponse(any(), eq(X_HIP_ID))) + .thenReturn(Mono.empty()); + + webTestClient + .post() + .uri(Constants.PATH_HIP_DATA_NOTIFICATION_ACKNOWLEDGEMENT) + .contentType(APPLICATION_JSON) + .header(AUTHORIZATION, token) + .bodyValue("{}") + .exchange() + .expectStatus() + .isAccepted(); + } +}