Skip to content

Commit

Permalink
Add yrkesskade-proxy application #deploy-proxy-yrkesskade (#3640)
Browse files Browse the repository at this point in the history
* Add yrkesskade-proxy application #deploy-proxy-yrkesskade

Added configuration files and scripts to implement the `yrkesskade-proxy` application, including Gradle build scripts, Dockerfile, application properties, and CI/CD pipeline. This setup configures OAuth2 security, logging, and application routing.

* Rename workflow and disable ApplicationContextTest
#deploy-proxy-yrkesskade

Updated the workflow name from 'udistub-proxy' to 'yrkesskade-proxy' for consistency. Additionally, disabled the ApplicationContextTest in the yrkesskadeproxy module for now.

* Remove legacy Spring dependencies

Removed Spring Boot Starter WebFlux and Spring Cloud Starter dependencies to clean up deprecated bootstrap configurations. This change simplifies the build configuration and reduces potential conflicts.

* Deploy #deploy-proxy-yrkesskade

* Add YrkesskadeClient to handle yrkesskade bestilling

Introduces YrkesskadeClient and related classes to handle the creation and status tracking of yrkesskade (work injury) requests. This includes mappings, DTOs, and configurations necessary for the integration with the yrkesskade-proxy.

* Add support for Trygdeetaten Azure AD integration
#deploy-proxy-yrkesskade

Updated configuration to include a new Azure AD application for Trygdeetaten and modified the application starter to use TrygdeetatenAzureAdTokenService for authentication. These changes facilitate seamless integration of Testnav Yrkesskade Proxy with Trygdeetaten's Azure AD instance.

* Remove unused config and add dev environment setup

Removed obsolete port and webproxy settings from config.yml to clean up configuration. Added a new application-dev.yaml file to set up the development environment with the yrkesskade data generator service URL. Updated bean names for gateway filters to improve clarity and maintain consistency.

* Deploy #deploy-proxy-yrkesskade

* Add Swagger annotations to YrkesskadeRequest fields

Annotated various fields in the YrkesskadeRequest class with @Schema for better API documentation. Also added a nested Periode class with its own annotations and adjusted the getPerioder method to initialize the perioder list if null.

* Add FakedingsConsumer and switch to TokenX authentication

Introduced FakedingsConsumer for generating fake tokens. Replaced TrygdeetatenAzureAdTokenService with TokenXService for authentication. Cleaned up unused dependencies and old configuration references.

* Deploy #deploy-proxy-yrkesskade

* test push

* Add "ident" header and handle special "Innmelderrolletype"

Include "ident" header in YrkesskadePostCommand to enhance request authentication. Also, update YrkesskadeMappingStrategy to set "paaVegneAv" for specific Innmelderrolletype cases such as "denSkadelidte" and "vergeOgForesatt".

* deploy #deploy-test-dolly-backend

* Update Yrkesskade mappings and add logging improvements
#deploy-test-dolly-backend

Revised the BestillingYrkesskadeStatusMapper to handle Yrkesskade statuses instead of Skattekort. Enhanced YrkesskadeConsumer and YrkesskadeClient with detailed log statements for better traceability.

* Add new types and queries for pension and tax data
#deploy-test-dolly-backend

Updated `ElasticTyper` and `OpenSearchQueryBuilder` to include new enums and query cases for `PEN_AFP_OFFENTLIG`, `PEN_PENSJONSAVTALE`, `SKATTEKORT`, and `YRKESSKADE`. Also fixed a typo in the `SystemTyper` description for `YRKESSKADE`. This enhances the system's ability to handle additional pension and tax types.

* Add logging filter for Reactor Netty HTTP client
#deploy-proxy-yrkesskade

Configured application.yml to set logging levels for Reactor Netty and Spring Cloud Gateway. Created a new LogFilter configuration class to add wiretap logging for HTTP client at INFO level.

* Deploy
#deploy-test-dolly-backend

* Enable HTTP client wiretap for debugging
#deploy-proxy-yrkesskade

Added the wiretap configuration to the HTTP client section in the application.yml. This aids in capturing the raw HTTP communication, which is useful for debugging purposes.

* Log authentication token value in filter

Added logging to output the TokenX authentication token value in the request filter. This will assist in debugging and tracking token-related issues.

* Deploy #deploy-proxy-yrkesskade

* Remove redundant build includes and add local configuration #deploy-test-dolly-backend

Eliminated numerous unnecessary build includes from settings.gradle to streamline dependencies. Added LocalConfig for local environment setup including Flyway and Vault configurations. Incorporated 'vault' library into build.gradle to support secure database interactions.

* Change skadetidspunkt type to Instant in YrkesskadeRequest #deploy-test-dolly-backend

Updated skadetidspunkt from LocalDate to Instant for more precise timestamp handling. This change ensures consistency in capturing exact moments of injury occurrences.

* Update skattekort handling and add afpOffentlig mapping #deploy-test-dolly-backend

Revised logic to check non-null skattekort before updating arbeidsgiverSkatt. Added mapping for afpOffentlig field in the PensjonData class. These changes enhance the robustness and completeness of the data transformation logic.

* Update ident header in YrkesskadePostCommand
#deploy-test-dolly-backend

Changed the ident header source from getSkadelidtIdentifikator to getInnmelderIdentifikator. This ensures the correct identifier is used in the request headers.

* Refactor Yrkesskade commands to return standardized DTOs
#deploy-test-dolly-backend

Updated YrkesskadePostCommand, YrkesskadeClient, and related classes to return a new YrkesskadeResponseDTO instead of ResponseEntity. This change standardizes the response handling and improves clarity in the API’s error and success responses.

* Update identifier mapping for verge and representative roles
#deploy-test-dolly-backend

Corrected the call to `getInnmelderIdentifikator` for the `virksomhetsrepresentant` role instead of using `getSkadelidtIdentifikator`. Also, reformatted parts of the innmelder identifier logic for better readability.

* Update database credentials and improve Yrkesskade mapping strategy
#deploy-test-dolly-backend

Changed the database URL and username in the local environment configuration to use a test database. Updated YrkesskadeMappingStrategy to include default values for specific cases, ensuring proper handling of innmelder roles.

* Add testnav-yrkesskade-proxy application and update IDs #deploy-test-dolly-backend

Included the testnav-yrkesskade-proxy in config and config.test files. Updated specific placeholders with correct identifier strings in YrkesskadeMappingStrategy.

* Refactor error message handling in YrkesskadeClient
#deploy-test-dolly-backend

Improve message logic for successful and non-successful statuses by adding conditional checks. Use `StringUtils.isNotBlank` to ensure proper handling of empty or null messages.

* Improve yrkesskade handling and update local DB config
#deploy-test-dolly-backend

Added a new `SaksoversiktDTO` class to handle yrkesskade data responses. Implemented `YrkesskadeGetCommand` for fetching yrkesskade case overviews and adjusted `YrkesskadeClient` to handle new cases conditionally. Updated the local database configuration to use a test DB and default `postgres` user.

* Add transaction persistence for Yrkesskade status
#deploy-test-dolly-backend

Introduced code for persisting transaction status updates in `YrkesskadeClient`. Modified existing methods to utilize the new persistence logic, ensuring `YrkesskadeStatus` is updated effectively.

* Refactor Yrkesskade status formatting.
#deploy-test-dolly-backend

Updated `YrkesskadeClient` to use formatted string for `YrkesskadeStatus`. This enhances clarity and consistency in status reporting by embedding an identifier directly within the message format.

* Handle null Innmelderrolle in YrkesskadeMappingStrategy
#deploy-test-dolly-backend

Add a null check to ensure that the innmelderIdentifikator is only set if innmelderrolle is not null. This prevents potential NullPointerException and improves code robustness.
  • Loading branch information
krharum authored Oct 15, 2024
1 parent ca9aa47 commit 1a587c6
Show file tree
Hide file tree
Showing 58 changed files with 1,595 additions and 8 deletions.
24 changes: 24 additions & 0 deletions .github/workflows/proxy.yrkesskade-proxy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: yrkesskade-proxy

on:
push:
paths:
- plugins/**
- libs/reactive-core/**
- libs/reactive-proxy/**
- libs/reactive-security/**
- libs/security-core/**
- proxies/yrkesskade-proxy/**
- .github/workflows/proxy.yrkesskade-proxy.yml

jobs:
workflow:
uses: ./.github/workflows/common.workflow.backend.yml
with:
cluster: "dev-gcp"
working-directory: "proxies/yrkesskade-proxy"
deploy-tag: "#deploy-proxy-yrkesskade"
permissions:
contents: read
id-token: write
secrets: inherit
1 change: 1 addition & 0 deletions apps/dolly-backend/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ dependencies {
implementation 'no.nav.testnav.libs:servlet-insecure-security'
implementation 'no.nav.testnav.libs:security-core'
implementation 'no.nav.testnav.libs:database'
implementation 'no.nav.testnav.libs:vault'
implementation 'no.nav.testnav.libs:data-transfer-objects'
implementation 'no.nav.testnav.libs:data-transfer-search-objects'
implementation 'no.nav.testnav.libs:reactive-core'
Expand Down
1 change: 1 addition & 0 deletions apps/dolly-backend/config.test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ spec:
- application: testnav-sykemelding-api
- application: testnav-synt-sykemelding-api
- application: testnav-tps-messaging-service
- application: testnav-yrkesskade-proxy
- application: testnorge-profil-api-dev
external:
- host: testnav-arena-forvalteren-proxy.dev-fss-pub.nais.io
Expand Down
1 change: 1 addition & 0 deletions apps/dolly-backend/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ spec:
- application: testnav-synt-sykemelding-api
- application: testnav-tps-messaging-service
- application: testnorge-profil-api
- application: testnav-yrkesskade-proxy
external:
- host: testnav-arena-forvalteren-proxy.dev-fss-pub.nais.io
- host: testnav-brregstub-proxy.dev-fss-pub.nais.io
Expand Down
1 change: 1 addition & 0 deletions apps/dolly-backend/settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ includeBuild '../../.github/workflows'
includeBuild '../../libs/data-transfer-objects'
includeBuild '../../libs/data-transfer-search-objects'
includeBuild '../../libs/database'
includeBuild '../../libs/vault'
includeBuild '../../libs/reactive-core'
includeBuild '../../libs/security-core'
includeBuild '../../libs/servlet-core'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,11 @@ private Flux<Map.Entry<String, Set<String>>> getIdentWithRelasjoner(DollyPerson
.map(PdlPerson.ForelderBarnRelasjon::getRelatertPersonsIdent)
.filter(Objects::nonNull),
person.getPerson().getFullmakt().stream()
.map(FullmaktDTO::getMotpartsPersonident))
.map(FullmaktDTO::getMotpartsPersonident),
person.getPerson().getVergemaalEllerFremtidsfullmakt().stream()
.map(PdlPerson.Vergemaal::getVergeEllerFullmektig)
.map(PdlPerson.VergeEllerFullmektig::getMotpartsPersonident)
.filter(Objects::nonNull))
.flatMap(Function.identity())))
.map(ident -> Map.entry(ident, new HashSet<>())));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package no.nav.dolly.bestilling.yrkesskade;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import ma.glasnost.orika.MapperFacade;
import no.nav.dolly.bestilling.ClientFuture;
import no.nav.dolly.bestilling.ClientRegister;
import no.nav.dolly.bestilling.personservice.PersonServiceConsumer;
import no.nav.dolly.bestilling.yrkesskade.dto.ResponseDTO;
import no.nav.dolly.bestilling.yrkesskade.dto.ResponseDTO.Status;
import no.nav.dolly.bestilling.yrkesskade.dto.YrkesskadeResponseDTO;
import no.nav.dolly.domain.jpa.BestillingProgress;
import no.nav.dolly.domain.jpa.TransaksjonMapping;
import no.nav.dolly.domain.resultset.RsDollyUtvidetBestilling;
import no.nav.dolly.domain.resultset.dolly.DollyPerson;
import no.nav.dolly.errorhandling.ErrorStatusDecoder;
import no.nav.dolly.mapper.MappingContextUtils;
import no.nav.dolly.service.TransaksjonMappingService;
import no.nav.dolly.util.TransactionHelperService;
import no.nav.testnav.libs.dto.yrkesskade.v1.YrkesskadeRequest;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

import static java.lang.Boolean.TRUE;
import static no.nav.dolly.domain.resultset.SystemTyper.YRKESSKADE;
import static org.apache.commons.lang3.StringUtils.isNotBlank;

@Slf4j
@Service
@RequiredArgsConstructor
public class YrkesskadeClient implements ClientRegister {

private final MapperFacade mapper;
private final ObjectMapper objectMapper;
private final YrkesskadeConsumer yrkesskadeConsumer;
private final TransactionHelperService transactionHelperService;
private final TransaksjonMappingService transaksjonMappingService;
private final PersonServiceConsumer personServiceConsumer;

@Override
public Flux<ClientFuture> gjenopprett(RsDollyUtvidetBestilling bestilling, DollyPerson dollyPerson, BestillingProgress progress, boolean isOpprettEndre) {

if (!bestilling.getYrkesskader().isEmpty()) {

var index = new AtomicInteger(0);
transactionHelperService.persister(progress, BestillingProgress::getYrkesskadeStatus,
BestillingProgress::setYrkesskadeStatus, "Yrkesskade#1:%s".formatted(ErrorStatusDecoder.getInfoVenter(
YRKESSKADE.getBeskrivelse())));

return Flux.from(yrkesskadeConsumer.hentSaksoversikt(dollyPerson.getIdent())
.map(resultat -> !resultat.getSaker().isEmpty())
.map(eksisterendeSak -> !eksisterendeSak || isOpprettEndre)
.flatMap(nysak -> TRUE.equals(nysak) ?
personServiceConsumer.getPdlPersoner(List.of(dollyPerson.getIdent()))
.doOnNext(personBolk -> log.info("Hentet pdlPersonBolk"))
.flatMap(personbolk -> Flux.fromIterable(bestilling.getYrkesskader())
.map(yrkesskade -> {
var context = MappingContextUtils.getMappingContext();
context.setProperty("ident", dollyPerson.getIdent());
context.setProperty("personBolk", personbolk);
return mapper.map(yrkesskade, YrkesskadeRequest.class, context);
})
.map(yrkesskade -> yrkesskadeConsumer.lagreYrkesskade(yrkesskade)
.map(status -> lagreTransaksjon(status, yrkesskade, progress.getBestilling().getId()))))
.flatMap(Flux::from)
.map(status -> encodeStatus(status, index.incrementAndGet()))
.collectList()
.map(resultat -> futurePersist(progress, resultat)) :

Mono.just(futurePersist(progress, List.of(ResponseDTO.builder()
.id(index.incrementAndGet())
.status(Status.OK)
.build())))
));
}

return Flux.empty();
}

@Override
public void release(List<String> identer) {
// Er ikke støttet
}

private ResponseDTO encodeStatus(YrkesskadeResponseDTO status, int index) {

String melding;

if (status.getStatus().is2xxSuccessful()) {
melding = null;
} else if (isNotBlank(status.getMelding())) {
melding = status.getMelding();
} else {
melding = status.getStatus().toString();
}

return ResponseDTO.builder()
.id(index)
.status(status.getStatus().is2xxSuccessful() ? Status.OK : Status.FEIL)
.melding(melding)
.build();
}

private ClientFuture futurePersist(BestillingProgress progress, String status) {

return () -> {
transactionHelperService.persister(progress, BestillingProgress::setYrkesskadeStatus, status);
return progress;
};
}

private ClientFuture futurePersist(BestillingProgress progress, List<ResponseDTO> response) {

return futurePersist(progress, response.stream()
.map(entry -> "Yrkesskade#%d:%s".formatted(entry.getId(),
entry.getStatus() == Status.OK ? "OK" :
ErrorStatusDecoder.encodeStatus("FEIL: %s".formatted(entry.getMelding()))))
.collect(Collectors.joining(",")));
}

private YrkesskadeResponseDTO lagreTransaksjon(YrkesskadeResponseDTO status, YrkesskadeRequest request,
Long bestillingId) {

if (status.getStatus().is2xxSuccessful()) {
transaksjonMappingService.save(TransaksjonMapping.builder()
.bestillingId(bestillingId)
.transaksjonId(toJson(request))
.ident(request.getSkadelidtIdentifikator())
.datoEndret(LocalDateTime.now())
.system(YRKESSKADE.name())
.build());
}
return status;
}

private String toJson(Object object) {

try {
return objectMapper.writeValueAsString(object);
} catch (JsonProcessingException e) {
log.error("Feilet å konvertere transaksjonsId for dokarkiv", e);
}
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package no.nav.dolly.bestilling.yrkesskade;

import lombok.extern.slf4j.Slf4j;
import no.nav.dolly.bestilling.yrkesskade.command.YrkesskadeGetCommand;
import no.nav.dolly.bestilling.yrkesskade.command.YrkesskadePostCommand;
import no.nav.dolly.bestilling.yrkesskade.dto.SaksoversiktDTO;
import no.nav.dolly.bestilling.yrkesskade.dto.YrkesskadeResponseDTO;
import no.nav.dolly.config.Consumers;
import no.nav.testnav.libs.dto.yrkesskade.v1.YrkesskadeRequest;
import no.nav.testnav.libs.securitycore.domain.ServerProperties;
import no.nav.testnav.libs.standalone.servletsecurity.exchange.TokenExchange;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

@Slf4j
@Service
public class YrkesskadeConsumer {

private final WebClient webClient;
private final TokenExchange tokenExchange;
private final ServerProperties serverProperties;

public YrkesskadeConsumer(
TokenExchange tokenExchange,
Consumers consumers,
WebClient.Builder webClientBuilder) {

this.tokenExchange = tokenExchange;
serverProperties = consumers.getYrkesskadeProxy();
this.webClient = webClientBuilder
.baseUrl(serverProperties.getUrl())
.build();
}

public Mono<YrkesskadeResponseDTO> lagreYrkesskade(YrkesskadeRequest request) {

log.info("Sender yrkesskade melding: {}", request);
return tokenExchange.exchange(serverProperties)
.flatMap(token -> new YrkesskadePostCommand(webClient, request, token.getTokenValue()).call())
.doOnNext(response ->
log.info("Mottatt response fra yrkesskade service {}", response));
}

public Mono<SaksoversiktDTO> hentSaksoversikt(String ident) {

log.info("Henter yrkeskade saksoversikt for ident {}", ident);
return tokenExchange.exchange(serverProperties)
.flatMap(token -> new YrkesskadeGetCommand(webClient, ident, token.getTokenValue()).call())
.doOnNext(response ->
log.info("Mottatt saksoversikt fra yrkesskade service {}", response));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package no.nav.dolly.bestilling.yrkesskade.command;

import lombok.RequiredArgsConstructor;
import no.nav.dolly.bestilling.yrkesskade.dto.SaksoversiktDTO;
import no.nav.testnav.libs.reactivecore.utils.WebClientFilter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
import reactor.util.retry.Retry;

import java.time.Duration;
import java.util.concurrent.Callable;

@RequiredArgsConstructor
public class YrkesskadeGetCommand implements Callable<Mono<SaksoversiktDTO>> {

private static final String YRKESSKADE_URL = "/api/v1/yrkesskader/{ident}";

private final WebClient webClient;
private final String ident;
private final String token;

@Override
public Mono<SaksoversiktDTO> call() {

return webClient
.get()
.uri(uriBuilder -> uriBuilder.path(YRKESSKADE_URL)
.build(ident))
.accept(MediaType.APPLICATION_JSON)
.header(HttpHeaders.AUTHORIZATION, "Bearer " + token)
.header("ident", ident)
.retrieve()
.bodyToMono(SaksoversiktDTO.class)
.map(resultat -> SaksoversiktDTO.builder()
.status(HttpStatusCode.valueOf(200))
.saker(resultat.getSaker())
.build())
.doOnError(WebClientFilter::logErrorMessage)
.onErrorResume(throwable -> Mono.just(SaksoversiktDTO.builder()
.status(WebClientFilter.getStatus(throwable))
.melding(WebClientFilter.getMessage(throwable))
.build()))
.retryWhen(Retry.backoff(3, Duration.ofSeconds(5))
.filter(WebClientFilter::is5xxException));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package no.nav.dolly.bestilling.yrkesskade.command;

import lombok.RequiredArgsConstructor;
import no.nav.dolly.bestilling.yrkesskade.dto.YrkesskadeResponseDTO;
import no.nav.testnav.libs.dto.yrkesskade.v1.YrkesskadeRequest;
import no.nav.testnav.libs.reactivecore.utils.WebClientFilter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
import reactor.util.retry.Retry;

import java.time.Duration;
import java.util.concurrent.Callable;

@RequiredArgsConstructor
public class YrkesskadePostCommand implements Callable<Mono<YrkesskadeResponseDTO>> {

private static final String YRKESSKADE_URL = "/api/v1/yrkesskader";

private final WebClient webClient;
private final YrkesskadeRequest yrkesskadeRequest;
private final String token;

@Override
public Mono<YrkesskadeResponseDTO> call() {

return webClient
.post()
.uri(uriBuilder -> uriBuilder.path(YRKESSKADE_URL).build())
.accept(MediaType.APPLICATION_JSON)
.header(HttpHeaders.AUTHORIZATION, "Bearer " + token)
.header("ident", yrkesskadeRequest.getInnmelderIdentifikator())
.bodyValue(yrkesskadeRequest)
.retrieve()
.toBodilessEntity()
.map(response -> YrkesskadeResponseDTO.builder()
.status(HttpStatusCode.valueOf(201))
.build())
.doOnError(WebClientFilter::logErrorMessage)
.onErrorResume(throwable -> Mono.just(YrkesskadeResponseDTO.builder()
.status(WebClientFilter.getStatus(throwable))
.melding(WebClientFilter.getMessage(throwable))
.build()))
.retryWhen(Retry.backoff(3, Duration.ofSeconds(5))
.filter(WebClientFilter::is5xxException));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package no.nav.dolly.bestilling.yrkesskade.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ResponseDTO {

public enum Status {OK, FEIL}

private Integer id;
private Status status;
private String melding;
}
Loading

0 comments on commit 1a587c6

Please sign in to comment.