Skip to content

Commit

Permalink
Add mapping strategy and Swagger adjustments
Browse files Browse the repository at this point in the history
Implemented a MapperFacade configuration to handle custom mapping strategies and converters. Improved Swagger setup by refining security configurations and customizing views. Enhanced OppdragRequest DTO with additional schema information and data types for better validation and XML support. Additionally, added basic unit tests for mapping strategies and updated dependencies in the build configuration.
  • Loading branch information
krharum committed Oct 30, 2024
1 parent ca4fbdc commit bd567a1
Show file tree
Hide file tree
Showing 14 changed files with 345 additions and 84 deletions.
30 changes: 10 additions & 20 deletions apps/oppdrag-service/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -68,28 +68,18 @@ dependencies {
'jakarta.activation:jakarta.activation-api:2.0.0',
'com.sun.xml.ws:jaxws-rt:3.0.0'

implementation 'no.nav.testnav.libs:security-core'
implementation 'no.nav.testnav.libs:servlet-core'
implementation 'no.nav.testnav.libs:servlet-security'
implementation 'no.nav.testnav.libs:data-transfer-objects'
implementation "no.nav.testnav.libs:data-transfer-objects"
implementation "no.nav.testnav.libs:servlet-core"
implementation "no.nav.testnav.libs:servlet-security"
implementation "no.nav.testnav.libs:vault"

implementation 'org.springframework.cloud:spring-cloud-starter-vault-config'
implementation "org.springframework.boot:spring-boot-starter-web"
implementation "org.springframework.boot:spring-boot-starter-security"
implementation "org.springframework.boot:spring-boot-starter-oauth2-client"
implementation "org.springframework.boot:spring-boot-starter-web-services"

implementation 'net.logstash.logback:logstash-logback-encoder:7.4'
implementation "org.springdoc:springdoc-openapi-starter-webmvc-ui:$versions.springdoc"
implementation "io.swagger.core.v3:swagger-annotations-jakarta:$versions.swagger"

implementation 'org.springdoc:springdoc-openapi-starter-webflux-ui:2.3.0'
implementation 'io.swagger.core.v3:swagger-annotations-jakarta:2.2.20'
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-web-services'
implementation 'org.springframework.ws:spring-ws-security'

implementation 'ma.glasnost.orika:orika-core:1.5.4'
annotationProcessor 'org.projectlombok:lombok'
implementation 'org.projectlombok:lombok'

testImplementation 'org.springframework.boot:spring-boot-starter-test'
implementation "ma.glasnost.orika:orika-core:$versions.orika"
}
3 changes: 1 addition & 2 deletions apps/oppdrag-service/settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@ plugins {

rootProject.name = 'oppdrag-service'

includeBuild '../../libs/security-core'
includeBuild '../../libs/data-transfer-objects'
includeBuild '../../libs/servlet-core'
includeBuild '../../libs/servlet-security'
includeBuild '../../libs/data-transfer-objects'
includeBuild '../../libs/vault'

gradleEnterprise {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@
import org.springframework.context.annotation.Import;

@Configuration
@Import({
ApplicationCoreConfig.class,
@Import({ApplicationCoreConfig.class,
SecureOAuth2ServerToServerConfiguration.class
})
public class ApplicationConfig {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package no.nav.testnav.oppdragservice.config;

import ma.glasnost.orika.CustomConverter;
import ma.glasnost.orika.MapperFacade;
import ma.glasnost.orika.impl.DefaultMapperFactory;
import no.nav.testnav.oppdragservice.mapper.MappingStrategy;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.List;

import static java.util.Objects.nonNull;

@Configuration
@SuppressWarnings("java:S3740")
public class MapperFacadeConfig {

@Bean
MapperFacade mapperFacade(List<MappingStrategy> mappingStrategies, List<CustomConverter> customConverters) {
var mapperFactory = new DefaultMapperFactory.Builder().build();

if (nonNull(mappingStrategies)) {
for (var mapper : mappingStrategies) {
mapper.register(mapperFactory);
}
}

if (nonNull(customConverters)) {
for (var converter : customConverters) {
mapperFactory.getConverterFactory().registerConverter(converter);
}
}

return mapperFactory.getMapperFacade();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,15 @@
import no.nav.testnav.libs.servletcore.config.ApplicationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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 org.springframework.context.annotation.Import;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.Arrays;

@Configuration
public class OpenApiConfig implements WebFilter {
@Import(ApplicationProperties.class)
public class OpenApiConfig implements WebMvcConfigurer {

@Bean
public OpenAPI openApi(ApplicationProperties applicationProperties) {
Expand All @@ -29,7 +28,7 @@ public OpenAPI openApi(ApplicationProperties applicationProperties) {
.scheme("bearer")
.bearerFormat("JWT")
.in(SecurityScheme.In.HEADER)
.name(HttpHeaders.AUTHORIZATION)
.name("Authorization")
))
.addSecurityItem(
new SecurityRequirement().addList("bearer-jwt", Arrays.asList("read", "write")))
Expand All @@ -46,20 +45,11 @@ public OpenAPI openApi(ApplicationProperties applicationProperties) {
.license(new License()
.name("MIT License")
.url("https://opensource.org/licenses/MIT")
)
);
));
}

@Override
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);
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/swagger").setViewName("redirect:/swagger-ui.html");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,31 @@

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
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.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;

@EnableWebSecurity
@Configuration
@Profile({"prod", "dev"})
public class SecurityConfig {

@Bean
public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
public SecurityFilterChain filterChain(HttpSecurity httpSecurity, HandlerMappingIntrospector introspector) throws Exception {

MvcRequestMatcher.Builder mvcMatcherBuilder = new MvcRequestMatcher.Builder(introspector);

httpSecurity.sessionManagement(sessionConfig -> sessionConfig.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(authorizeConfig -> authorizeConfig.requestMatchers(
"/internal/**",
"/webjars/**",
"/swagger-resources/**",
"/v3/api-docs/**",
"/swagger-ui/**",
"/swagger",
"/error",
"/swagger-ui.html"
).permitAll().requestMatchers("/api/**").fullyAuthenticated())
.authorizeHttpRequests(authorizeConfig -> authorizeConfig
.requestMatchers(mvcMatcherBuilder.pattern("/api/**")).fullyAuthenticated()
.anyRequest().permitAll())
.oauth2ResourceServer(oauth2RSConfig -> oauth2RSConfig.jwt(Customizer.withDefaults()));

return httpSecurity.build();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,8 @@

@Service
public class OppdragConsumer {

public void sendOppdrag(String xmlPayload) {

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package no.nav.testnav.oppdragservice.mapper;

import ma.glasnost.orika.MapperFactory;

@FunctionalInterface
public interface MappingStrategy {

/**
* A callback for registering criteria on the provided {@link MapperFactory}.
* <p/>
* <pre>{@code
*
* @Override public void register(MapperFactory factory) {
* factory.registerMapper(arbeidsfordelingToRestArbeidsfordeling());
* }
* }</pre>
*/
void register(MapperFactory factory);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package no.nav.testnav.oppdragservice.mapper;

import ma.glasnost.orika.CustomMapper;
import ma.glasnost.orika.MapperFactory;
import ma.glasnost.orika.MappingContext;
import no.nav.testnav.libs.dto.oppdragservice.v1.OppdragRequest;
import no.nav.testnav.oppdragservice.wsdl.Oppdrag;
import no.nav.testnav.oppdragservice.wsdl.SendInnOppdragRequest;
import no.nav.testnav.oppdragservice.wsdl.SendInnOppdragRequest2;
import org.springframework.stereotype.Component;

@Component
public class OppdragRequestMappingStrategy implements MappingStrategy{

@Override
public void register(MapperFactory factory) {

factory.classMap(OppdragRequest.class, SendInnOppdragRequest.class)
.customize(new CustomMapper<>() {
@Override
public void mapAtoB(OppdragRequest source,
SendInnOppdragRequest destination,
MappingContext context) {

destination.setRequest(mapperFacade.map(source, SendInnOppdragRequest2.class, context));
}
})
.register();

factory.classMap(OppdragRequest.class, Oppdrag.class)
.customize(new CustomMapper<>() {
@Override
public void mapAtoB(OppdragRequest source,
Oppdrag destination,
MappingContext context) {


}
})
.byDefault()
.register();
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package no.nav.testnav.oppdragservice.provider;

import io.swagger.v3.oas.annotations.Operation;
import lombok.RequiredArgsConstructor;
import no.nav.testnav.libs.dto.oppdragservice.v1.OppdragRequest;
import no.nav.testnav.oppdragservice.service.OppdragService;
import no.nav.testnav.oppdragservice.wsdl.SendInnOppdragResponse;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

Expand All @@ -16,7 +18,8 @@ public class OppdragController {
private final OppdragService oppdragService;

@PostMapping
public SendInnOppdragResponse sendInnOppdrag(OppdragRequest oppdragRequest) {
@Operation(summary = "Send inn oppdrag")
public SendInnOppdragResponse sendInnOppdrag(@RequestBody OppdragRequest oppdragRequest) {

return oppdragService.sendInnOppdrag(oppdragRequest);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,46 @@
package no.nav.testnav.oppdragservice.service;

import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.JAXBException;
import lombok.SneakyThrows;
import ma.glasnost.orika.MapperFacade;
import no.nav.testnav.libs.dto.oppdragservice.v1.OppdragRequest;
import no.nav.testnav.oppdragservice.consumer.OppdragConsumer;
import no.nav.testnav.oppdragservice.wsdl.SendInnOppdragRequest;
import no.nav.testnav.oppdragservice.wsdl.SendInnOppdragResponse;
import org.springframework.stereotype.Service;

import java.io.StringWriter;

@Service
public class OppdragService {

private final JAXBContext jaxbContext;
private final OppdragConsumer oppdragConsumer;
private final MapperFacade mapperFacade;

public OppdragService(OppdragConsumer oppdragConsumer, MapperFacade mapperFacade) throws JAXBException {
this.oppdragConsumer = oppdragConsumer;
this.mapperFacade = mapperFacade;
this.jaxbContext = JAXBContext.newInstance(SendInnOppdragRequest.class);
}

public SendInnOppdragResponse sendInnOppdrag(OppdragRequest oppdragRequest) {

var request = mapperFacade.map(oppdragRequest, SendInnOppdragRequest.class);
var xmlRequest = marshallToXml(request);

oppdragConsumer.sendOppdrag(xmlRequest);
return null;
}

@SneakyThrows
private String marshallToXml(SendInnOppdragRequest melding) {

var marshaller = jaxbContext.createMarshaller();
var writer = new StringWriter();
marshaller.marshal(melding, writer);

return writer.toString();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package no.nav.testnav.oppdragservice.mapper;

import ma.glasnost.orika.CustomConverter;
import ma.glasnost.orika.MapperFacade;
import ma.glasnost.orika.impl.DefaultMapperFactory;

import static java.util.Objects.nonNull;

public class MapperTestUtils {

public static MapperFacade createMapperFacadeForMappingStrategy(MappingStrategy... strategies) {
return createMapperFacadeForMappingStrategy(null, strategies);
}

public static MapperFacade createMapperFacadeForMappingStrategy(CustomConverter<Object, Object> converter, MappingStrategy... strategies) {
DefaultMapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();

for (MappingStrategy strategy : strategies) {
strategy.register(mapperFactory);
}

if (nonNull(converter)) {
mapperFactory.getConverterFactory().registerConverter(converter);
}
return mapperFactory.getMapperFacade();
}
}
Loading

0 comments on commit bd567a1

Please sign in to comment.