Skip to content

Commit c3d8c86

Browse files
authored
Feilhåndtering når bestllinger henger (#3673)
* Handle timeouts in WebClientFilter and ArenaForvalterClient Add TimeoutException handling in WebClientFilter for improved error messaging and status codes. Update ArenaForvalterClient to include timeout on WebClient calls with a 30-second limit, and provide detailed error messages on timeout exceptions. * Add Pensjonforvalter integration and enhance configuration Introduce automated tests for PensjonforvalterHelper. Create PensjonPdlPersonService for fetching extended person data from PDL. Update ApplicationConfig to include a configurable client timeout. Expand RsDollyBestilling to include a check for Pensjon data. Consolidate logic in PensjonforvalterClient by leveraging new services and managing timeouts effectively. Ensure proper handling of related entities and integrate error handling for improved resilience. * Refactor service clients to use ApplicationConfig for timeouts Replaced hardcoded timeout values with dynamic values from ApplicationConfig in PersonServiceClient. Additionally, restructured PensjonforvalterClient to include ApplicationConfig and reordered dependencies for consistency. * Add ApplicationConfig mock to ArenaForvalterClientTest Previously, the ApplicationConfig was not being mocked in tests. This addition ensures that the ApplicationConfig dependencies are properly handled during test runs, improving the accuracy and coverage of the unit tests. * Fix TPS messaging status handling and client timeout Refine logic for filtering TPS messaging status to exclude specific messages. Add application-configured timeout and error handling in TpsMessagingClient for better fault tolerance. * Add error handling and timeout to AaregClient Introduce a timeout mechanism to limit client response time and add error handling to manage potential issues gracefully. Updated dependencies and refactored code for improved readability and maintenance. * Add client timeout mock to `ArenaForvalterClientTest` Included mock setup for `applicationConfig.getClientTimeout()` returning 30L, ensuring consistent behavior in tests. Refactored test `gjenopprett_TekniskFeil` to use `StepVerifier` for reactive stream verification. * Refactor Dokarkiv interface to use Mono instead of Flux This commit refactors the Dokarkiv modules to shift from using Flux to Mono for single response handling. This change simplifies the code, improves performance, and ensures proper error handling with the introduction of a timeout mechanism. The refactor also introduces better logging and error reporting functionalities. * Refactor InntektsmeldingClient to improve error handling Revised the gjenopprett method in InntektsmeldingClient to enhance error handling and timeout management. Introduced filtering and mapping for environments, and added a new isExistInntekstsmelding method in RsDollyBestilling to streamline checks. * Add timeout and error handling in SykemeldingClient Configured a timeout feature based on the presence of synthetic sykemelding and added error handling to provide more informative error responses. This improves client resilience and clarity in case of failures.
1 parent fb48421 commit c3d8c86

31 files changed

+1229
-1057
lines changed

apps/dolly-backend/src/main/java/no/nav/dolly/bestilling/aareg/AaregClient.java

+15-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import no.nav.dolly.bestilling.ClientRegister;
99
import no.nav.dolly.bestilling.aareg.amelding.AmeldingService;
1010
import no.nav.dolly.bestilling.aareg.domain.ArbeidsforholdRespons;
11+
import no.nav.dolly.config.ApplicationConfig;
1112
import no.nav.dolly.domain.jpa.BestillingProgress;
1213
import no.nav.dolly.domain.jpa.Bruker;
1314
import no.nav.dolly.domain.resultset.RsDollyUtvidetBestilling;
@@ -16,11 +17,13 @@
1617
import no.nav.dolly.errorhandling.ErrorStatusDecoder;
1718
import no.nav.dolly.util.TransactionHelperService;
1819
import no.nav.testnav.libs.dto.aareg.v1.Arbeidsforhold;
20+
import no.nav.testnav.libs.reactivecore.utils.WebClientFilter;
1921
import no.nav.testnav.libs.securitycore.domain.AccessToken;
2022
import org.springframework.stereotype.Service;
2123
import reactor.core.publisher.Flux;
2224
import reactor.core.publisher.Mono;
2325

26+
import java.time.Duration;
2427
import java.time.YearMonth;
2528
import java.util.List;
2629
import java.util.Set;
@@ -49,9 +52,10 @@ public class AaregClient implements ClientRegister {
4952
private static final String SYSTEM = "AAREG";
5053

5154
private final AaregConsumer aaregConsumer;
55+
private final AmeldingService ameldingService;
56+
private final ApplicationConfig applicationConfig;
5257
private final ErrorStatusDecoder errorStatusDecoder;
5358
private final MapperFacade mapperFacade;
54-
private final AmeldingService ameldingService;
5559
private final TransactionHelperService transactionHelperService;
5660

5761
@Override
@@ -66,7 +70,7 @@ public Flux<ClientFuture> gjenopprett(RsDollyUtvidetBestilling bestilling, Dolly
6670
var miljoerTrygg = new AtomicReference<>(miljoer);
6771

6872
var initStatus = miljoer.stream()
69-
.map(miljo -> String.format("%s:%s", miljo, getInfoVenter(SYSTEM)))
73+
.map(miljo -> "%s:%s".formatted(miljo, getInfoVenter(SYSTEM)))
7074
.collect(Collectors.joining(","));
7175

7276
transactionHelperService.persister(progress, BestillingProgress::getAaregStatus,
@@ -83,11 +87,20 @@ public Flux<ClientFuture> gjenopprett(RsDollyUtvidetBestilling bestilling, Dolly
8387
return ameldingService.sendAmelding(bestilling, dollyPerson, miljoerTrygg.get());
8488
}
8589
})
90+
.timeout(Duration.ofSeconds(applicationConfig.getClientTimeout()))
91+
.onErrorResume(error -> getErrors(error, miljoerTrygg.get()))
8692
.map(status -> futurePersist(progress, status));
8793
}
8894
return Flux.empty();
8995
}
9096

97+
private Flux<String> getErrors(Throwable error, Set<String> miljoer) {
98+
99+
return Flux.just(miljoer.stream()
100+
.map(miljoe -> "%s:Feil= %s".formatted(miljoe, ErrorStatusDecoder.encodeStatus(WebClientFilter.getMessage(error))))
101+
.collect(Collectors.joining(",")));
102+
}
103+
91104
@Override
92105
public void release(List<String> identer) {
93106

apps/dolly-backend/src/main/java/no/nav/dolly/bestilling/arenaforvalter/ArenaForvalterClient.java

+12-4
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,27 @@
1010
import no.nav.dolly.bestilling.arenaforvalter.service.ArenaDagpengerService;
1111
import no.nav.dolly.bestilling.arenaforvalter.service.ArenaStansYtelseService;
1212
import no.nav.dolly.bestilling.arenaforvalter.utils.ArenaEksisterendeVedtakUtil;
13+
import no.nav.dolly.config.ApplicationConfig;
1314
import no.nav.dolly.domain.jpa.BestillingProgress;
1415
import no.nav.dolly.domain.resultset.RsDollyUtvidetBestilling;
1516
import no.nav.dolly.domain.resultset.arenaforvalter.Arenadata;
1617
import no.nav.dolly.domain.resultset.dolly.DollyPerson;
1718
import no.nav.dolly.util.IdentTypeUtil;
1819
import no.nav.dolly.util.TransactionHelperService;
20+
import no.nav.testnav.libs.reactivecore.utils.WebClientFilter;
1921
import org.apache.commons.lang3.StringUtils;
2022
import org.springframework.stereotype.Service;
2123
import reactor.core.publisher.Flux;
2224
import reactor.core.publisher.Mono;
2325

26+
import java.time.Duration;
2427
import java.util.List;
2528
import java.util.stream.Collectors;
2629

2730
import static java.util.Objects.nonNull;
2831
import static no.nav.dolly.bestilling.arenaforvalter.utils.ArenaStatusUtil.AAP;
2932
import static no.nav.dolly.bestilling.arenaforvalter.utils.ArenaStatusUtil.AAP115;
33+
import static no.nav.dolly.bestilling.arenaforvalter.utils.ArenaStatusUtil.ANDREFEIL;
3034
import static no.nav.dolly.bestilling.arenaforvalter.utils.ArenaStatusUtil.BRUKER;
3135
import static no.nav.dolly.bestilling.arenaforvalter.utils.ArenaStatusUtil.DAGPENGER;
3236
import static no.nav.dolly.bestilling.arenaforvalter.utils.ArenaStatusUtil.fmtResponse;
@@ -41,13 +45,14 @@ public class ArenaForvalterClient implements ClientRegister {
4145
private static final String MILJOE_FMT = "%s$BRUKER= %s";
4246
private static final String SYSTEM = "Arena";
4347

44-
private final ArenaForvalterConsumer arenaForvalterConsumer;
45-
private final TransactionHelperService transactionHelperService;
46-
private final ArenaBrukerService arenaBrukerService;
48+
private final ApplicationConfig applicationConfig;
4749
private final ArenaAap115Service arenaAap115Service;
4850
private final ArenaAapService arenaAapService;
51+
private final ArenaBrukerService arenaBrukerService;
4952
private final ArenaDagpengerService arenaDagpengerService;
53+
private final ArenaForvalterConsumer arenaForvalterConsumer;
5054
private final ArenaStansYtelseService arenaStansYtelseService;
55+
private final TransactionHelperService transactionHelperService;
5156

5257
@Override
5358
public Flux<ClientFuture> gjenopprett(RsDollyUtvidetBestilling bestilling, DollyPerson dollyPerson, BestillingProgress progress, boolean isOpprettEndre) {
@@ -66,6 +71,9 @@ public Flux<ClientFuture> gjenopprett(RsDollyUtvidetBestilling bestilling, Dolly
6671
BestillingProgress::setArenaforvalterStatus, initStatus);
6772
})
6873
.flatMap(miljoer -> doArenaOpprett(ordre, dollyPerson.getIdent(), miljoer)
74+
.timeout(Duration.ofSeconds(applicationConfig.getClientTimeout()))
75+
.onErrorResume(error ->
76+
Mono.just(fmtResponse(miljoer, ANDREFEIL, WebClientFilter.getMessage(error))))
6977
.map(status -> futurePersist(progress, status))));
7078
}
7179

@@ -94,7 +102,7 @@ private Mono<String> doArenaOpprett(Arenadata arenadata, String ident, List<Stri
94102
arenaDagpengerService.sendDagpenger(arenadata, arenaOperasjoner, ident, miljoe)
95103
.map(dagpengerStatus -> fmtResponse(miljoe, DAGPENGER, dagpengerStatus))
96104
));
97-
} else {
105+
} else {
98106
return Flux.just(fmtResponse(miljoe, BRUKER, NOT_SUPPORTED));
99107
}
100108
})

apps/dolly-backend/src/main/java/no/nav/dolly/bestilling/arenaforvalter/utils/ArenaStatusUtil.java

+11-2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import reactor.core.publisher.Flux;
1212
import reactor.core.publisher.Mono;
1313

14+
import java.util.Collection;
1415
import java.util.Map;
1516
import java.util.stream.Collectors;
1617

@@ -29,6 +30,7 @@ public class ArenaStatusUtil {
2930
public static final String AAP115 = "AAP115";
3031
public static final String AAP = "AAP";
3132
public static final String DAGPENGER = "DAGP";
33+
public static final String ANDREFEIL = "ARENA Oppretting Feil=";
3234

3335
public static Mono<String> getDagpengerStatus(ArenaNyeDagpengerResponse response, ErrorStatusDecoder errorStatusDecoder) {
3436

@@ -37,7 +39,7 @@ public static Mono<String> getDagpengerStatus(ArenaNyeDagpengerResponse response
3739
.map(status -> errorStatusDecoder.getErrorText(response.getStatus(), getMessage(response.getFeilmelding()))),
3840
Flux.fromIterable(response.getNyeDagp())
3941
.filter(nyDagP -> nonNull(nyDagP.getNyeDagpResponse()))
40-
.map(nyDagP -> "JA".equals(nyDagP.getNyeDagpResponse().getUtfall()) ?
42+
.map(nyDagP -> "JA".equals(nyDagP.getNyeDagpResponse().getUtfall()) ?
4143
"OK" :
4244
encodeStatus(ArenaUtils.AVSLAG + nyDagP.getNyeDagpResponse().getBegrunnelse()))
4345
.collect(Collectors.joining()),
@@ -78,9 +80,16 @@ public static Mono<String> getAapStatus(AapResponse response, ErrorStatusDecoder
7880
}
7981
}
8082

83+
public static String fmtResponse(Collection<String> miljoer, String system, String status) {
84+
85+
return miljoer.stream()
86+
.map(miljo -> fmtResponse(miljo, system, status))
87+
.collect(Collectors.joining(","));
88+
}
89+
8190
public static String fmtResponse(String miljoe, String system, String status) {
8291

83-
return String.format(MILJOE_FMT, miljoe, system, encodeStatus(status));
92+
return MILJOE_FMT.formatted(miljoe, system, encodeStatus(status));
8493
}
8594

8695
public static String getMessage(String jsonFeilmelding) {

apps/dolly-backend/src/main/java/no/nav/dolly/bestilling/dokarkiv/DokarkivClient.java

+47-27
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import no.nav.dolly.bestilling.dokarkiv.domain.DokarkivResponse;
1313
import no.nav.dolly.bestilling.dokarkiv.domain.JoarkTransaksjon;
1414
import no.nav.dolly.bestilling.personservice.PersonServiceConsumer;
15+
import no.nav.dolly.config.ApplicationConfig;
1516
import no.nav.dolly.domain.PdlPersonBolk;
1617
import no.nav.dolly.domain.jpa.BestillingProgress;
1718
import no.nav.dolly.domain.jpa.TransaksjonMapping;
@@ -21,17 +22,21 @@
2122
import no.nav.dolly.errorhandling.ErrorStatusDecoder;
2223
import no.nav.dolly.service.TransaksjonMappingService;
2324
import no.nav.dolly.util.TransactionHelperService;
25+
import no.nav.testnav.libs.reactivecore.utils.WebClientFilter;
2426
import org.springframework.stereotype.Service;
2527
import reactor.core.publisher.Flux;
2628
import reactor.core.publisher.Mono;
2729

30+
import java.time.Duration;
2831
import java.time.LocalDateTime;
2932
import java.util.List;
33+
import java.util.Set;
3034
import java.util.stream.Collectors;
3135

3236
import static java.util.Objects.isNull;
3337
import static java.util.Objects.nonNull;
3438
import static no.nav.dolly.domain.resultset.SystemTyper.DOKARKIV;
39+
import static no.nav.dolly.errorhandling.ErrorStatusDecoder.encodeStatus;
3540
import static org.apache.commons.lang3.StringUtils.isBlank;
3641
import static org.apache.commons.lang3.StringUtils.isNotBlank;
3742

@@ -40,40 +45,47 @@
4045
@RequiredArgsConstructor
4146
public class DokarkivClient implements ClientRegister {
4247

48+
private final ApplicationConfig applicationConfig;
4349
private final DokarkivConsumer dokarkivConsumer;
50+
private final ErrorStatusDecoder errorStatusDecoder;
4451
private final MapperFacade mapperFacade;
45-
private final TransaksjonMappingService transaksjonMappingService;
4652
private final ObjectMapper objectMapper;
47-
private final TransactionHelperService transactionHelperService;
4853
private final PersonServiceConsumer personServiceConsumer;
49-
private final ErrorStatusDecoder errorStatusDecoder;
54+
private final TransactionHelperService transactionHelperService;
55+
private final TransaksjonMappingService transaksjonMappingService;
5056

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

5460
return Flux.just(bestilling)
5561
.filter(bestilling1 -> nonNull(bestilling1.getDokarkiv()))
5662
.map(RsDollyUtvidetBestilling::getDokarkiv)
57-
.flatMap(dokarkiv -> Flux.from(getPersonData(List.of(dollyPerson.getIdent()))
58-
.map(person -> buildRequest(dokarkiv, person))
59-
.flatMap(request -> dokarkivConsumer.getEnvironments()
60-
.flatMapIterable(env -> env)
61-
.filter(env -> bestilling.getEnvironments().contains(env))
62-
.flatMap(env ->
63-
!transaksjonMappingService.existAlready(DOKARKIV,
64-
dollyPerson.getIdent(), env, bestilling.getId()) || isOpprettEndre ?
65-
66-
dokarkivConsumer.postDokarkiv(env, request)
67-
.map(status ->
68-
getStatus(dollyPerson.getIdent(),
69-
bestilling.getId(), status)) :
70-
71-
Mono.just(env + ":OK")
72-
))
73-
.collect(Collectors.joining(",")))
63+
.flatMap(dokarkiv -> Flux.from(getPersonData(dollyPerson.getIdent())
64+
.flatMap(person -> getFilteredMiljoer(bestilling.getEnvironments())
65+
.flatMapMany(miljoer -> Flux.fromIterable(miljoer)
66+
.flatMap(env -> buildRequest(dokarkiv, person)
67+
.flatMap(request ->
68+
!transaksjonMappingService.existAlready(DOKARKIV,
69+
dollyPerson.getIdent(), env, bestilling.getId()) || isOpprettEndre ?
70+
dokarkivConsumer.postDokarkiv(env, request)
71+
.map(status ->
72+
getStatus(dollyPerson.getIdent(),
73+
bestilling.getId(), status)) :
74+
Mono.just(env + ":OK")
75+
))
76+
.timeout(Duration.ofSeconds(applicationConfig.getClientTimeout()))
77+
.onErrorResume(error -> getErrors(error, miljoer))
78+
)))
79+
.collect(Collectors.joining(","))
7480
.map(status -> futurePersist(progress, status)));
7581
}
7682

83+
private Flux<String> getErrors(Throwable error, List<String> miljoer) {
84+
85+
return Flux.fromIterable(miljoer)
86+
.map(miljoe -> "%s:%s".formatted(miljoe, encodeStatus(WebClientFilter.getMessage(error))));
87+
}
88+
7789
private ClientFuture futurePersist(BestillingProgress progress, String status) {
7890

7991
return () -> {
@@ -84,12 +96,20 @@ private ClientFuture futurePersist(BestillingProgress progress, String status) {
8496
};
8597
}
8698

99+
private Mono<List<String>> getFilteredMiljoer(Set<String> miljoer) {
100+
101+
return dokarkivConsumer.getEnvironments()
102+
.flatMapMany(Flux::fromIterable)
103+
.filter(miljoer::contains)
104+
.collectList();
105+
}
106+
87107
private String getStatus(String ident, Long bestillingId, DokarkivResponse response) {
88108

89109
log.info("Dokarkiv response {} for ident {}", response, ident);
90110

91111
if (isNull(response)) {
92-
return null;
112+
return "UKJENT:Intet svar";
93113
}
94114

95115
if (isBlank(response.getFeilmelding())) {
@@ -101,7 +121,7 @@ private String getStatus(String ident, Long bestillingId, DokarkivResponse respo
101121

102122
return String.format("%s:FEIL=Teknisk feil se logg! %s", response.getMiljoe(),
103123
isNotBlank(response.getFeilmelding()) ?
104-
ErrorStatusDecoder.encodeStatus(errorStatusDecoder.getStatusMessage(response.getFeilmelding())) :
124+
encodeStatus(errorStatusDecoder.getStatusMessage(response.getFeilmelding())) :
105125
"UKJENT");
106126
}
107127
}
@@ -112,22 +132,22 @@ public void release(List<String> identer) {
112132
// Sletting er ikke støttet
113133
}
114134

115-
private Flux<PdlPersonBolk.PersonBolk> getPersonData(List<String> identer) {
135+
private Flux<PdlPersonBolk.PersonBolk> getPersonData(String ident) {
116136

117-
return personServiceConsumer.getPdlPersoner(identer)
137+
return personServiceConsumer.getPdlPersoner(List.of(ident))
118138
.filter(pdlPersonBolk -> nonNull(pdlPersonBolk.getData()))
119139
.map(PdlPersonBolk::getData)
120140
.map(PdlPersonBolk.Data::getHentPersonBolk)
121141
.flatMap(Flux::fromIterable)
122142
.filter(personBolk -> nonNull(personBolk.getPerson()));
123143
}
124144

125-
private DokarkivRequest buildRequest(RsDokarkiv rsDokarkiv, PdlPersonBolk.PersonBolk personBolk) {
145+
private Mono<DokarkivRequest> buildRequest(RsDokarkiv rsDokarkiv, PdlPersonBolk.PersonBolk personBolk) {
126146

127147
var context = new MappingContext.Factory().getContext();
128148
context.setProperty("personBolk", personBolk);
129149

130-
return mapperFacade.map(rsDokarkiv, DokarkivRequest.class, context);
150+
return Mono.just(mapperFacade.map(rsDokarkiv, DokarkivRequest.class, context));
131151
}
132152

133153
private void saveTransaksjonId(DokarkivResponse response, String ident, Long bestillingId, String miljoe) {
@@ -140,7 +160,7 @@ private void saveTransaksjonId(DokarkivResponse response, String ident, Long bes
140160
.bestillingId(bestillingId)
141161
.transaksjonId(toJson(JoarkTransaksjon.builder()
142162
.journalpostId(response.getJournalpostId())
143-
.dokumentInfoId(response.getDokumenter().get(0).getDokumentInfoId())
163+
.dokumentInfoId(response.getDokumenter().getFirst().getDokumentInfoId())
144164
.build()))
145165
.datoEndret(LocalDateTime.now())
146166
.miljoe(miljoe)

apps/dolly-backend/src/main/java/no/nav/dolly/bestilling/dokarkiv/DokarkivConsumer.java

+4-5
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
import no.nav.testnav.libs.standalone.servletsecurity.exchange.TokenExchange;
1414
import org.springframework.stereotype.Service;
1515
import org.springframework.web.reactive.function.client.WebClient;
16-
import reactor.core.publisher.Flux;
1716
import reactor.core.publisher.Mono;
1817

1918
import java.util.List;
@@ -41,18 +40,18 @@ public DokarkivConsumer(
4140
.build();
4241
}
4342

44-
@Timed(name = "providers", tags = { "operation", "dokarkiv-opprett" })
45-
public Flux<DokarkivResponse> postDokarkiv(String environment, DokarkivRequest dokarkivRequest) {
43+
@Timed(name = "providers", tags = {"operation", "dokarkiv-opprett"})
44+
public Mono<DokarkivResponse> postDokarkiv(String environment, DokarkivRequest dokarkivRequest) {
4645

4746
log.info("Dokarkiv sender melding for ident {} miljoe {} request {}",
4847
dokarkivRequest.getBruker().getId(), environment, dokarkivRequest);
4948

5049
return tokenService.exchange(serverProperties)
51-
.flatMapMany(token -> new DokarkivPostCommand(webClient, environment, dokarkivRequest,
50+
.flatMap(token -> new DokarkivPostCommand(webClient, environment, dokarkivRequest,
5251
token.getTokenValue()).call());
5352
}
5453

55-
@Timed(name = "providers", tags = { "operation", "dokarkiv_getEnvironments" })
54+
@Timed(name = "providers", tags = {"operation", "dokarkiv_getEnvironments"})
5655
public Mono<List<String>> getEnvironments() {
5756

5857
return tokenService.exchange(serverProperties)

0 commit comments

Comments
 (0)