Skip to content

Commit

Permalink
This code diff introduces a significant change by migrating the appli…
Browse files Browse the repository at this point in the history
…cation from a traditional Spring MVC architecture to a reactive Spring WebFlux architecture. This change is evident in the following modifications:

**1. Reactive Dependencies:**

- Removal of `spring-boot-starter-data-jpa` and addition of `spring-boot-starter-data-r2dbc` indicate a shift from blocking JPA to reactive R2DBC for database interactions.
- Inclusion of `org.postgresql:r2dbc-postgresql` and `io.r2dbc:r2dbc-h2` provides drivers for reactive database connections.

**2. Reactive Repositories:**

- Repositories like `ParameterRepository`, `AnsettelseLoggRepository`, and `LoggRepository` now extend reactive interfaces like `ReactiveCrudRepository` and `ReactiveSortingRepository`.
- Methods in these repositories now return reactive types like `Flux` and `Mono` instead of `List` and `Optional`.

**3. Reactive Services:**

- Service methods in `LoggService`, `ArbeidsforholdService`, `KodeverkService`, `AnsettelseLoggService`, and `ParameterService` are updated to return reactive types (`Flux`, `Mono`).
- Usage of reactive operators like `flatMap`, `map`, and `collectList` is introduced for asynchronous data processing.

**4. Reactive Controllers:**

- Controllers like `ParameterController` and `LoggController` now return reactive types (`Flux`, `Mono`) from their endpoint methods.

**5. Security Configuration:**

- The `SecurityConfig` class is updated to use `EnableWebFluxSecurity` and `EnableReactiveMethodSecurity` annotations, indicating a shift to reactive security configurations.
- The `springSecurityFilterChain` method now configures a `SecurityWebFilterChain` for reactive web requests.

**6. Other Changes:**

- Introduction of `NavHeaders` class for managing custom headers.
- Update to OpenApiConfig to support reactive endpoints.
- Removal of unused dependencies and code related to the previous Spring MVC architecture.

**Impact:**

This migration to a reactive architecture brings several benefits:

- **Improved Performance and Scalability:** Reactive applications can handle more requests with fewer resources compared to traditional blocking applications.
- **Non-Blocking Operations:** Asynchronous operations prevent threads from being blocked, leading to better resource utilization.
- **Enhanced Responsiveness:** Reactive applications can respond to user requests faster, even under heavy load.

**Considerations:**

- Developers need to be familiar with reactive programming concepts and libraries like Reactor to work with this updated codebase.
- Testing strategies need to be adapted for reactive components.
- Existing code that interacts with the application might need adjustments to handle reactive types.

This migration represents a significant architectural shift that can lead to a more performant and scalable application.
  • Loading branch information
krharum committed Oct 17, 2024
1 parent 69331eb commit 1c16526
Show file tree
Hide file tree
Showing 29 changed files with 252 additions and 189 deletions.
31 changes: 11 additions & 20 deletions apps/levende-arbeidsforhold-ansettelse/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,57 +2,48 @@ plugins {
id "dolly-apps"
}

test {
useJUnitPlatform()
}

sonarqube {
properties {
property "sonar.dynamicAnalysis", "reuseReports"
property "sonar.host.url", "https://sonarcloud.io"
property "sonar.java.coveragePlugin", "jacoco"
property "sonar.language", "java"
property "sonar.token", System.getenv("SONAR_TOKEN")
property "sonar.organization", "navikt"
property "sonar.project.monorepo.enabled", true
property "sonar.projectKey", "testnav-levende-arbeidsforhold-ansettelse"
property "sonar.projectName", "testnav-levende-arbeidsforhold-ansettelse"
property "sonar.sourceEncoding", "UTF-8"
}
}


dependencies {
implementation "no.nav.testnav.libs:data-transfer-objects"
implementation "no.nav.testnav.libs:data-transfer-search-objects"
implementation "no.nav.testnav.libs:database"
implementation "no.nav.testnav.libs:reactive-core"
implementation "no.nav.testnav.libs:security-core"
implementation "no.nav.testnav.libs:servlet-core"
implementation "no.nav.testnav.libs:reactive-security"
implementation "no.nav.testnav.libs:servlet-insecure-security"
implementation "no.nav.testnav.libs:vault"

implementation "org.springframework.boot:spring-boot-starter-oauth2-client"
implementation "org.springframework.boot:spring-boot-starter-data-jpa"
implementation "org.springframework.boot:spring-boot-starter-data-r2dbc"

implementation "org.springframework.cloud:spring-cloud-starter-vault-config"

implementation "org.postgresql:postgresql:42.7.3"
implementation "io.r2dbc:r2dbc-h2"
implementation "org.postgresql:r2dbc-postgresql"
implementation "org.flywaydb:flyway-core"
implementation "org.flywaydb:flyway-database-postgresql"

runtimeOnly "org.postgresql:postgresql"
runtimeOnly "com.h2database:h2"

implementation "io.micrometer:micrometer-registry-prometheus"
implementation "org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0"
implementation "io.swagger.core.v3:swagger-annotations-jakarta:2.2.21"
implementation "org.springdoc:springdoc-openapi-starter-webflux-ui:$versions.springdoc"
implementation "io.swagger.core.v3:swagger-annotations-jakarta:$versions.swagger"

implementation "org.hibernate.validator:hibernate-validator"

testImplementation "org.springframework.boot:spring-boot-starter-test"
testImplementation "org.springframework.cloud:spring-cloud-contract-wiremock"

implementation "org.projectlombok:lombok"
annotationProcessor "org.projectlombok:lombok"
testAnnotationProcessor "org.projectlombok:lombok"

implementation "com.zaxxer:HikariCP"
implementation "com.h2database:h2"
}

4 changes: 2 additions & 2 deletions apps/levende-arbeidsforhold-ansettelse/settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ plugins {

rootProject.name = 'levende-arbeidsforhold-ansettelse'

includeBuild '../../libs/security-core'
includeBuild '../../libs/servlet-core'
includeBuild '../../libs/reactive-security'
includeBuild '../../libs/reactive-core'
includeBuild '../../libs/security-core'
includeBuild '../../libs/servlet-insecure-security'
includeBuild '../../libs/data-transfer-objects'
includeBuild '../../libs/data-transfer-search-objects'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
package no.nav.testnav.levendearbeidsforholdansettelse.config;

import no.nav.testnav.libs.servletcore.config.ApplicationCoreConfig;
import no.nav.testnav.libs.reactivecore.config.CoreConfig;
import no.nav.testnav.libs.reactivesecurity.config.SecureOAuth2ServerToServerConfiguration;
import no.nav.testnav.libs.standalone.servletsecurity.config.InsecureJwtServerToServerConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.scheduling.annotation.EnableAsync;

@Configuration
@Import({
ApplicationCoreConfig.class,
CoreConfig.class,
SecureOAuth2ServerToServerConfiguration.class,
InsecureJwtServerToServerConfiguration.class
})
@EnableAsync
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package no.nav.testnav.levendearbeidsforholdansettelse.config;

import io.r2dbc.h2.H2ConnectionFactory;
import io.r2dbc.spi.ConnectionFactory;
import lombok.RequiredArgsConstructor;
import org.flywaydb.core.Flyway;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.core.env.Environment;
import org.springframework.data.r2dbc.config.AbstractR2dbcConfiguration;
import org.springframework.data.r2dbc.config.EnableR2dbcAuditing;
import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories;

@Configuration
@EnableR2dbcAuditing
@EnableR2dbcRepositories
@RequiredArgsConstructor
class DatabaseR2dbcConfiguration extends AbstractR2dbcConfiguration {

private final Environment env;

@Bean
@Profile("dev")
public ConnectionFactory connectionFactory() {
return H2ConnectionFactory.inMemory("testdb");
}

@Bean(initMethod = "migrate")
public Flyway flyway() {
return new Flyway(Flyway.configure()
.baselineOnMigrate(true)
.dataSource(
env.getRequiredProperty("spring.datasource.url"),
env.getRequiredProperty("spring.datasource.username"),
env.getRequiredProperty("spring.datasource.password"))
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,19 @@
import io.swagger.v3.oas.models.info.License;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
import no.nav.testnav.libs.servletcore.config.ApplicationProperties;
import no.nav.testnav.libs.reactivecore.config.ApplicationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.http.HttpHeaders;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;

import java.util.Arrays;

@Configuration
public class OpenApiConfig implements WebMvcConfigurer {
public class OpenApiConfig implements WebFilter {

@Bean
public OpenAPI openApi(ApplicationProperties applicationProperties) {
Expand All @@ -26,7 +29,7 @@ public OpenAPI openApi(ApplicationProperties applicationProperties) {
.scheme("bearer")
.bearerFormat("JWT")
.in(SecurityScheme.In.HEADER)
.name("Authorization")
.name(HttpHeaders.AUTHORIZATION)
))
.addSecurityItem(
new SecurityRequirement().addList("bearer-jwt", Arrays.asList("read", "write")))
Expand All @@ -48,7 +51,15 @@ public OpenAPI openApi(ApplicationProperties applicationProperties) {
}

@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/swagger").setViewName("redirect:/swagger-ui.html");
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
if (exchange.getRequest().getURI().getPath().equals("/swagger")) {
return chain
.filter(exchange.mutate()
.request(exchange.getRequest()
.mutate().path("/swagger-ui.html").build())
.build());
}

return chain.filter(exchange);
}
}
Original file line number Diff line number Diff line change
@@ -1,26 +1,28 @@
package no.nav.testnav.levendearbeidsforholdansettelse.config;

import lombok.RequiredArgsConstructor;
import no.nav.testnav.libs.reactivesecurity.manager.JwtReactiveAuthenticationManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;

@EnableWebSecurity
@Configuration
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
@RequiredArgsConstructor
public class SecurityConfig {

private final JwtReactiveAuthenticationManager jwtReactiveAuthenticationManager;

@Bean
@SuppressWarnings("java:S4502")
public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {

httpSecurity.sessionManagement(sessionConfig -> sessionConfig.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(authorizeConfig -> authorizeConfig.requestMatchers(
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity httpSecurity) {
return httpSecurity
.csrf(ServerHttpSecurity.CsrfSpec::disable)
.authorizeExchange(authorizeConfig -> authorizeConfig.pathMatchers(
"/internal/**",
"/webjars/**",
"/swagger-resources/**",
Expand All @@ -31,13 +33,8 @@ public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Excepti
"/swagger-ui.html",
"/h2/**",
"/member/**")
.permitAll()
.requestMatchers("/api/**")
.fullyAuthenticated())
.headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable))
.oauth2ResourceServer(oauth2RSConfig -> oauth2RSConfig.jwt(Customizer.withDefaults()));

return httpSecurity.build();
.permitAll().anyExchange().authenticated())
.oauth2ResourceServer(oauth2RSConfig -> oauth2RSConfig.jwt(jwtSpec -> jwtSpec.authenticationManager(jwtReactiveAuthenticationManager)))
.build();
}
}

}
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package no.nav.testnav.levendearbeidsforholdansettelse.consumers;

import io.netty.channel.ChannelOption;
import io.netty.channel.epoll.EpollChannelOption;
import lombok.extern.slf4j.Slf4j;
import no.nav.testnav.levendearbeidsforholdansettelse.config.Consumers;
import no.nav.testnav.levendearbeidsforholdansettelse.consumers.command.aareg.HentArbeidsforholdCommand;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.ExchangeStrategies;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

import java.util.ArrayList;
import java.util.List;
Expand Down Expand Up @@ -39,12 +40,11 @@ public KodeverkServiceConsumer(
objectMapper = new ObjectMapper();
}

public List<String> hentKodeverk(String kodeverk) {
public Mono<List<String>> hentKodeverk(String kodeverk) {

return tokenExchange.exchange(serverProperties)
.flatMap(token -> new KodeverkServiceCommand(webClient, token.getTokenValue(), kodeverk, objectMapper).call())
.map(Map::keySet)
.map(ArrayList::new)
.block();
.map(ArrayList::new);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import no.nav.testnav.levendearbeidsforholdansettelse.domain.NavHeaders;
import no.nav.testnav.libs.dto.levendearbeidsforhold.v1.Arbeidsforhold;
import no.nav.testnav.libs.reactivecore.utils.WebClientFilter;
import no.nav.testnav.libs.servletcore.headers.NavHeaders;
import org.springframework.http.HttpHeaders;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.WebClientResponseException;
Expand All @@ -23,7 +23,7 @@
@RequiredArgsConstructor
public class HentArbeidsforholdCommand implements Callable<Flux<Arbeidsforhold>> {

private static final String miljoe = "q2";
private static final String MILJOE = "q2";
private static final String NAV_PERSON_IDENT = "Nav-Personident";
private static final String CONSUMER = "Dolly";

Expand All @@ -45,7 +45,7 @@ public Flux<Arbeidsforhold> call() {
.queryParam("arbeidsforholdtype", "forenkletOppgjoersordning",
"frilanserOppdragstakerHonorarPersonerMm", "maritimtArbeidsforhold",
"ordinaertArbeidsforhold")
.build(miljoe))
.build(MILJOE))
.header(HttpHeaders.AUTHORIZATION, "Bearer " + token)
.header(NAV_PERSON_IDENT, ident)
.header(NavHeaders.NAV_CONSUMER_ID, CONSUMER)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
import reactor.core.publisher.Flux;

@RestController
@RequestMapping("/api/v1/logg")
Expand All @@ -22,21 +21,21 @@ public class LoggController {

@GetMapping
@Operation(description = "Henter logger i hht forespørsel")
public Page<AnsettelseLogg> getAnsettelser(Pageable pageable) {
public Flux<Page<AnsettelseLogg>> getAnsettelser(Pageable pageable) {

return loggService.getAnsettelseLogg(pageable);
}

@GetMapping("/ident/{ident}")
@Operation(description = "Henter logg i hht forespørsel")
public List<AnsettelseLogg> getIdent(@PathVariable String ident) {
public Flux<AnsettelseLogg> getIdent(@PathVariable String ident) {

return loggService.getIdent(ident);
}

@GetMapping("/organisasjon/{orgnummer}")
@Operation(description = "Henter logg i hht forespørsel")
public List<AnsettelseLogg> getOrganisasjon(@PathVariable String orgnummer) {
public Flux<AnsettelseLogg> getOrganisasjon(@PathVariable String orgnummer) {

return loggService.getOrgnummer(orgnummer);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import no.nav.testnav.levendearbeidsforholdansettelse.domain.dto.ParameterDTO;
import no.nav.testnav.levendearbeidsforholdansettelse.entity.JobbParameter;
import no.nav.testnav.levendearbeidsforholdansettelse.service.ParameterService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
Expand All @@ -15,16 +15,15 @@
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@Slf4j
@RestController
@RequestMapping("/api/v1/parameter")
@RequiredArgsConstructor
public class ParameterController {

@Autowired
private final ParameterService parameterService;

/**
Expand All @@ -33,7 +32,7 @@ public class ParameterController {
*/
@GetMapping
@Operation(description = "Henter alle parametre for oppretting av arbeidsforhold")
public List<ParameterDTO> hentAlleParametere() {
public Flux<ParameterDTO> hentAlleParametere() {

return parameterService.hentAlleParametere();
}
Expand All @@ -46,8 +45,8 @@ public List<ParameterDTO> hentAlleParametere() {
@PutMapping("/{parameternavn}")
@Operation(description = "Legg inn ny verdi på en parameter")
@ResponseStatus(HttpStatus.OK)
public void oppdatereVerdier(@PathVariable("parameternavn") String parameternavn, @RequestBody String verdi){
public Mono<JobbParameter> oppdatereVerdier(@PathVariable("parameternavn") String parameternavn, @RequestBody String verdi){

parameterService.updateVerdi(parameternavn, verdi);
return parameterService.updateVerdi(parameternavn, verdi);
}
}
Loading

0 comments on commit 1c16526

Please sign in to comment.