How to hide endpoints from Swagger documentation with Springfox - java

I have a Spring Boot project with next dependency of Springfox:
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
And I have my Interface:
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import springfox.documentation.annotations.ApiIgnore;
#RestController
#RequestMapping(value = "/cache")
#ApiIgnore
#Api(hidden = true)
public interface CacheController {
#RequestMapping(
value = "clear/",
method = RequestMethod.GET,
produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.TEXT_PLAIN_VALUE}
)
#ApiOperation(value = "", hidden = true)
ResponseEntity<String> clearToken();
}
The annotations #ApiIgnore and #Api(hidden = true) (I've tested them separately and they don't work either.) haven't effects to hide the documentation. It only works if the annotation is over the method, but I would like hide them all since I have other endpoints I'd like to hide.
Some ideas?
EDIT:
This is my Swagger configuration:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMethod;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.builders.ResponseMessageBuilder;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.ApiKey;
import springfox.documentation.service.AuthorizationScope;
import springfox.documentation.service.Contact;
import springfox.documentation.service.ResponseMessage;
import springfox.documentation.service.SecurityReference;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
#Configuration
#EnableSwagger2
public class SwaggerConfig {
public static String API_KEY_NAME;
#Bean
public Docket apiDocumentation() {
List<ResponseMessage> errorList = this.defineResponseMessages();
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("my.package.rest"))
.paths(PathSelectors.any())
.build()
.useDefaultResponseMessages(true)
.globalResponseMessage(RequestMethod.GET, errorList)
.securitySchemes(Arrays.asList(this.apiKey()))
.securityContexts(Arrays.asList(this.securityContext()))
.apiInfo(this.apiInfo());
}
#Value("${server.security.apiKeyName}")
public void setApiKeyName(final String apiKeyName) {
SwaggerConfig.API_KEY_NAME = apiKeyName;
}
private ApiKey apiKey() {
return new ApiKey("apiKey", API_KEY_NAME, "header");
}
private SecurityContext securityContext() {
return SecurityContext.builder()
.securityReferences(defaultAuth())
.forPaths(PathSelectors.any()).build();
}
private List<SecurityReference> defaultAuth() {
AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
return Arrays.asList(new SecurityReference("apiKey", authorizationScopes));
}
private List<ResponseMessage> defineResponseMessages() {
List<ResponseMessage> errorList = new ArrayList<ResponseMessage>();
ResponseMessage responseMessage = new ResponseMessageBuilder()
.code(HttpStatus.INTERNAL_SERVER_ERROR.value())
.message(HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase())
.build();
errorList.add(responseMessage);
responseMessage = new ResponseMessageBuilder()
.code(HttpStatus.UNAUTHORIZED.value())
.message(HttpStatus.UNAUTHORIZED.getReasonPhrase())
.build();
errorList.add(responseMessage);
responseMessage = new ResponseMessageBuilder()
.code(HttpStatus.NOT_FOUND.value())
.message(HttpStatus.NOT_FOUND.getReasonPhrase())
.build();
errorList.add(responseMessage);
return errorList;
}
private ApiInfo apiInfo() {
ApiInfoBuilder apiInfoBuilder = new ApiInfoBuilder();
return apiInfoBuilder
.title("My API")
.description("Description")
.version("1.0.0 Beta")
.build();
}
}

You have added the #ApiIgnore annotation on an interface. It looks like, this annotation doesn't work when added on an interface. (I really don't understand why #Api works on an interface and #ApiIgnore don't. 😕)
Add the annotation directly to your controller class. This should solve your problem.
The hidden property on the #Api annotation doesn't work currently. (See this GitHub issue.)
For Springdoc see: How to hide endpoints from OpenAPI documentation with Springdoc

For OpenAPI3 and SpringBoot:
I used #Hidden annotation on a method of a controller.
It seems to work both at method level and controller level.
#Hidden annotation was imported from using:
import io.swagger.v3.oas.annotations;

One more way is to use #ApiOperation(hidden = true)
This can be used at controller/handler level method.
E.g.
#RestController
public HomeController{
#ApiOperation(value = "<Your Message>", hidden = true)
public String getMessage(#RequestParam(value = "msg") final String msg){
return msg;
}
}

The scenario where we want to hide only a particular method(s) from the class. For swagger.v3 there is an annotation with name Hidden in io.swagger.core.v3:swagger-annotations:2.0.10 jar. Methods to be hidden can be annotated with Hidden annotation as shown below. The below method shows the method with DELETE operation which needs to be hidden from the swagger documentation.
#DELETE
#Hidden
public void deleteList(int id) {
//code goes here.
}

Another different great way is to define the visible paths on the SpringFox Configuration
#Configuration
#EnableSwagger2
public class SpringFoxConfig {
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(Predicates.or(PathSelectors.ant("/rtm/**"), PathSelectors.ant("/appview/**")))
.build().apiInfo(apiEndPointsInfo());
}
}
This way you can define the visible paths centraly and avoid puting swagger annotations on many controllers.

Another option is to just remove the #Api completely, and your controller and its methods shouldn't be picked up by swagger.

Related

Configuration in Resilience4J CircuitBreaker not working

I'm using a circuit breaker of Resilience4J and I need to ignore some custom exceptions so I need to change the default configuration. I'm working with microservices so I have a microservice connected to a database which have some basic requests like get by id and I also have an edge service which use these requests. I need, for example, if the id doesn't exist, the microservice throws a custom exception and the circuitbreaker doesn't open in this case.
Microservice with the database:
Get request
#GetMapping("/sales-rep/{id}")
#ResponseStatus(HttpStatus.OK)
public SalesRepDTO getSalesRep(#PathVariable Integer id) {
return salesRepService.getSalesRep(id);
}
Service
public SalesRepDTO getSalesRep(Integer id) {
if(salesRepRepository.existsById(id)) {
SalesRep salesRep = salesRepRepository.findById(id).get();
return new SalesRepDTO(salesRep.getId(), salesRep.getName());
} else {
throw new SalesRepNotFoundException("Sales rep not found");
}
}
Edge service:
Service
import com.ironhack.manageAllservice.client.AccountClient;
import com.ironhack.manageAllservice.client.LeadClient;
import com.ironhack.manageAllservice.client.SalesRepClient;
import com.ironhack.manageAllservice.controller.dtos.*;
import com.ironhack.manageAllservice.controller.dtos.report.OpportunityBySalesRepDTO;
import com.ironhack.manageAllservice.controller.dtos.report.ReportDTO;
import com.ironhack.manageAllservice.service.exceptions.SalesRepNotFoundException;
import com.ironhack.manageAllservice.controller.dtos.report.*;
import com.ironhack.manageAllservice.service.interfaces.IManageAllService;
import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;
import io.github.resilience4j.timelimiter.TimeLimiterConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JCircuitBreakerFactory;
import org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JConfigBuilder;
import org.springframework.cloud.client.circuitbreaker.CircuitBreaker;
import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory;
import org.springframework.cloud.client.circuitbreaker.Customizer;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.web.server.ResponseStatusException;
import java.time.Duration;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
#Service
public class ManageAllService implements IManageAllService {
#Autowired
private CircuitBreakerFactory circuitBreakerFactory;
#Bean
public Customizer<Resilience4JCircuitBreakerFactory> globalCustomConfiguration() {
CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
.failureRateThreshold(50)
.waitDurationInOpenState(Duration.ofMillis(1000))
.slidingWindowSize(2)
.ignoreExceptions(SalesRepNotFoundException.class)
.build();
TimeLimiterConfig timeLimiterConfig = TimeLimiterConfig.custom()
.timeoutDuration(Duration.ofSeconds(4))
.build();
return factory -> factory.configureDefault(id -> new Resilience4JConfigBuilder(id)
.circuitBreakerConfig(circuitBreakerConfig)
.timeLimiterConfig(timeLimiterConfig)
.build());
}
public SalesRepDTO getSalesRepById(Integer id) {
CircuitBreaker circuitBreaker = circuitBreakerFactory.create("salesRep-service");
SalesRepDTO salesRepDTO = circuitBreaker.run(()->salesRepClient.getSalesRep(id),
throwable -> postSalesRepFallBack());
return salesRepDTO;
}
SalesRepNotFoundException.class is the exception I want to ignore, but the circuitbreaker isn't changing the configuration. Any suggestion?
I suggest that you have a look at our Spring Boot 2 starter: https://resilience4j.readme.io/docs/getting-started-3
Our Spring Boot starter allows you to extract the configuration into the config file and use annotations.

Request method 'GET' not supported] error for method using #DeleteMapping

I'm getting a error like:
Request method 'GET' not supported for a method (deleteProductById)
using the #DeleteMapping annotation whenever I visit the URL mapped to said method (http://localhost:8083/deleteproductbyid/1). The app works when I change the annotation for the method to #GetMapping but that causes other issues.
Here's the relevant code:
package com.democrudexample.controller;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
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;
import com.democrudexample.model.Product;
import com.democrudexample.services.CrudService;
#RestController
#RequestMapping("/")
public class CrudRestController {
#Autowired
private CrudService service;
#GetMapping("/getproductlist")
#CrossOrigin(origins = "http://localhost:4200")
public List<Product> fetchProductList() {
List<Product> products = new ArrayList<Product>();
//logic to fetch list from database
products = service.fetchProductList();
return products;
}
#PostMapping("/addproduct")
#CrossOrigin(origins = "http://localhost:4200")
public Product saveProduct(#RequestBody Product product) {
return service.saveProductToDB(product);
}
#GetMapping("/getproductbyid/{id}")
#CrossOrigin(origins = "http://localhost:4200")
public Product fetchProductById(#PathVariable int id) {
return service.fetchProductById(id).get();
}
#DeleteMapping(value = "/deleteproductbyid/{id}")
#CrossOrigin(origins = "http://localhost:4200")
public String deleteProductById(#PathVariable int id) {
return service.deleteProductById(id);
}
}
package com.democrudexample.services;
import java.util.List;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.democrudexample.model.Product;
import com.democrudexample.repository.CrudRepo;
#Service
public class CrudService {
#Autowired
private CrudRepo repo;
public List<Product> fetchProductList(){
return repo.findAll();
}
public Product saveProductToDB(Product product) {
return repo.save(product);
}
public Optional<Product> fetchProductById(int id) {
return repo.findById(id);
}
public String deleteProductById(int id) {
String result;
try {
repo.deleteById(id);
result = "Product sucessfully deleted";
System.out.println(result);
}catch(Exception e) {
result = "Product id is not valid";
System.out.println(result);
}
return result;
}
}
EDIT: I commented out the result and everything related to it in the deleteProductById method and it seems to be working just fine now. After having looked at the console, the error seems to have been some issues with parsing the text.
By annotating the method as #DeleteMapping, you are making this a HTTP DELETE operation. Refer this documentation for more details about different HTTP requests.
However, when you access an URL in browser, browser always sends a GET request, whereas your Resource is expecting a DELETE request. Hence you are getting the error.
You can use tools like Postman or you can write a small code in Javascript to send a DELETE request to the server.

KeycloakSecurityContext is returning null in SpringBoot

I am creating an SPA with Keycloak 5, Spring Boot 2 and Angular 7.
Everything was fine, even keycloack configuration in application.properties and roles securing. But when I tried to create a Bean to get the User Token Data, I am receiving a null bean of it. Can't understand why, it is just like the code in Keycloak documentation...
import javax.servlet.http.HttpServletRequest;
import org.keycloak.KeycloakSecurityContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
#Configuration
public class KeycloakConfig {
/**
* Retorna o contexto de segurança do Keycloak.
*
* #return
*/
#Bean
#Scope(scopeName = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public KeycloakSecurityContext accessToken() {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
return (KeycloakSecurityContext) request.getAttribute(KeycloakSecurityContext.class.getName());
}
}
Boot Config:
keycloak.enabled = true
keycloak.auth-server-url = http://acesso.tre-pa.jus.br/auth
keycloak.realm = TRE-PA
keycloak.resource = acesso-sistemas-service
keycloak.credentials.secret = ca70294a-af51-4abb-81f9-234566de2c7c
keycloak.ssl-required = external
keycloak.use-resource-role-mappings = false
keycloak.bearer-only = true
keycloak.autodetect-bearer-only = true
keycloak.principal-attribute = preferred_username
logging.level.org.keycloak = DEBUG
spring.main.allow-bean-definition-overriding = true
# spring.autoconfigure.exclude = org.keycloak.adapters.springboot.KeycloakSpringBootConfiguration
keycloak.securityConstraints[0].securityCollections[0].name = secured-area
keycloak.securityConstraints[0].securityCollections[0].patterns[0] = /secured/*
keycloak.securityConstraints[1].securityCollections[0].patterns[1] = /admin/*
keycloak.securityConstraints[1].authRoles[0] = DEVELOPER
keycloak.securityConstraints[1].securityCollections[0].name = service-area
keycloak.securityConstraints[1].securityCollections[0].patterns[0] = /service/*
I did something similar in my application using Keycloak 4.8.1.Final where I could access the AccessToken and expose it as a bean for injection into other classes:
#Bean
#Scope(scopeName = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public AccessToken getAccessToken() {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
KeycloakPrincipal<RefreshableKeycloakSecurityContext> keycloakPrincipal = (KeycloakPrincipal) request.getUserPrincipal();
if (keycloakPrincipal == null) {
throw new NotAuthenticatedException("KeycloakPrincipal not set");
}
KeycloakSecurityContext keycloakSecurityContext = keycloakPrincipal.getKeycloakSecurityContext();
return keycloakSecurityContext.getToken();
}
After upgrading Keycloak to 5 (and above all the way to 15.0.1) the code above no longer works. The only way I managed to get a reference to the AccessToken was by using Spring Security as described (mostly) here
Basically adding spring security deps to gradle
implementation('org.springframework.boot:spring-boot-starter-security')
Adding this config class...
import org.keycloak.adapters.KeycloakConfigResolver
import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver
import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper
import org.springframework.security.core.session.SessionRegistryImpl
import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy
import javax.inject.Inject
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(jsr250Enabled = true)
class KeycloakConfig : KeycloakWebSecurityConfigurerAdapter() {
override fun configure(http: HttpSecurity) {
super.configure(http)
http.authorizeRequests()
.anyRequest()
.permitAll()
http.csrf().disable()
}
#Inject
fun configureGlobal(authManager: AuthenticationManagerBuilder) {
val authProvider = keycloakAuthenticationProvider()
authProvider.setGrantedAuthoritiesMapper(SimpleAuthorityMapper())
authManager.authenticationProvider(authProvider)
}
#Bean
override fun sessionAuthenticationStrategy(): SessionAuthenticationStrategy {
return RegisterSessionAuthenticationStrategy(SessionRegistryImpl())
}
}
And then updating my method in my other config class:
import org.springframework.amqp.rabbit.annotation.EnableRabbit
import org.springframework.cache.annotation.EnableCaching
import org.springframework.context.annotation.EnableAspectJAutoProxy
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder
import com.fasterxml.jackson.module.kotlin.KotlinModule
import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.ObjectMapper
import org.springframework.web.context.WebApplicationContext
import org.springframework.context.annotation.ScopedProxyMode
import org.keycloak.representations.AccessToken
import javax.servlet.http.HttpServletRequest
import org.springframework.web.context.request.RequestContextHolder
import org.springframework.web.context.request.ServletRequestAttributes
import org.keycloak.KeycloakPrincipal
import org.keycloak.adapters.RefreshableKeycloakSecurityContext
import org.keycloak.KeycloakSecurityContext
import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Scope
import javax.inject.Inject
import javax.ws.rs.NotAuthorizedException
import javax.ws.rs.core.Context
import javax.ws.rs.core.SecurityContext
#Configuration
class ApplicationConfig {
//needed for keycloak adaptor 7.0.1+
// added in this here rather than in KeycloakConfig above due to issue here:
// https://stackoverflow.com/questions/57957006/unable-to-build-spring-based-project-for-authentication-using-keycloak
#Bean
fun keycloakConfigResolver(): KeycloakSpringBootConfigResolver {
return KeycloakSpringBootConfigResolver()
}
#Bean
#Scope(scopeName = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
fun accessToken(): AccessToken {
val request = (RequestContextHolder.currentRequestAttributes() as ServletRequestAttributes).request
val token = request.userPrincipal as KeycloakAuthenticationToken? ?: throw NotAuthorizedException("KeycloakPrincipal not set")
val principal = token.principal as KeycloakPrincipal<*>
val keycloakSecurityContext = principal.keycloakSecurityContext
return keycloakSecurityContext.token
}
}
The above codes works for me with Springboot versions 2.1.2.RELEASE and 2.5.3

NoSuchMessageException in Spring boot 2.0.1

I am trying to implement internationalization in my website but keep getting this exception :
org.springframework.context.NoSuchMessageException: No message found under code 'nav_home' for locale 'en'
My resource bundle is located here :
src/main/resources/translations
Project structure [imgur]
Here is my configuration file :
package com.spring.henallux.FlourishBlotts.configuration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.validation.DefaultMessageCodesResolver;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.i18n.CookieLocaleResolver;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
import java.util.Locale;
#Configuration
public class MainConfiguration implements WebMvcConfigurer {
#Bean
public DefaultMessageCodesResolver defaultMessageCodesResolver() {
DefaultMessageCodesResolver defaultMessageCodesResolver = new DefaultMessageCodesResolver();
return defaultMessageCodesResolver;
}
#Bean
public ResourceBundleMessageSource resourceBundleMessageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setDefaultEncoding("UTF-8");
messageSource.setBasenames("translations/general", "translations/errors");
messageSource.setUseCodeAsDefaultMessage(true);
return messageSource;
}
#Bean
public LocaleResolver localeResolver() {
CookieLocaleResolver resolver = new CookieLocaleResolver();
resolver.setDefaultLocale(new Locale("en"));
resolver.setCookieName("flourish-blotts_localeCookie");
resolver.setCookieMaxAge(-1);
return resolver;
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
LocaleChangeInterceptor interceptor = new LocaleChangeInterceptor();
interceptor.setParamName("locale");
registry.addInterceptor(interceptor);
}
}
My controller :
package com.spring.henallux.FlourishBlotts.controller;
import com.spring.henallux.FlourishBlotts.model.LoginForm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import java.util.Locale;
#Controller
#RequestMapping(value="/home")
public class HomeController {
private final MessageSource messageSource;
#Autowired
public HomeController(MessageSource messageSource) {
this.messageSource = messageSource;
}
#RequestMapping(method = RequestMethod.GET)
public String home (Model model, Locale locale){
model.addAttribute("title", messageSource.getMessage("nav_home", null, locale));
model.addAttribute("path", "home");
model.addAttribute("loginForm", new LoginForm());
return "integrated:home";
}
#RequestMapping(value="/login", method = RequestMethod.POST)
public String login (#ModelAttribute(value="loginForm") LoginForm form){
//TODO log user here
return "redirect:/home";
}
}
And my general_en.properties file :
nav_home=Home
nav_books=All books
nav_about=About us
nav_account=Account
nav_cart=Cart
I've been searching for hours but cannot find anything that helps me
I've modifying my basenames in my configuration class by adding
-> classpath:
-> classpath*:
-> / before translations, with and without classpath(*)
I get the same error when using the fr locale.
I still get the same error when I try to access any message from template.jsp instead of HomeController.
Please help me stackoverflow, you're my only hope ;)
Nevermind, I found my error.
In my configuration file, my ResourceBundleMessageSource bean wasn't named messageSource()
That's it --'

Spring Test - Mock #ModelAttribute

I have a RestController that I'm attempting to test via Spring MVC Test. It has the following ModelAttribute in it:
#ModelAttribute("authUser")
public User authUser(#AuthenticationPrincipal SpringAuthUser springAuthUser) throws Exception {
User user = ConstantsHome.userprofileMgr.getUserByUserId(springAuthUser.getUsername(), true, true);
user.updateRights(null);
request.getSession().setAttribute(ConstantsHome.USEROBJECT_KEY, user);
return user;
}
When I run a test against this RestController, I'm getting a NullPointerException inside this authUser method.
Is there a way to mock this method such that the mocked method gets used instead of this one for testing? I've read other posts on this and thought I could just pass an "authUser" param in my test but that's not working. Ultimately trying to make this "authUser" not throw an NPE...here is my test...
#Test
public void testGetAllUsers() throws Exception {
String userJson = AvadaObjectMapper.getMapper().writeValueAsString(new User());
System.out.println("userJson=" + userJson);
MvcResult result = this.mockMvc.perform(get("/").param("authUser", userJson).accept(MediaType.parseMediaType("application/json;charset=UTF-8")))
.andExpect(status().isOk())
.andExpect(content().contentType("application/json"))
.andDo(print())
.andReturn();
String content = result.getResponse().getContentAsString();
assertTrue(content.contains("Hello"));
}
I was able to get my authUser to work via the test configuration class below. Pay attention to the .defaultRequest... line in particular as that is where the auth user gets established. I use the class below as the #ContextConfiguration class on all of my tests.
import com.avada.rest.api.users.UserService;
import com.avada.rest.api.users.TestUserService;
import com.avada.rest.security.SpringAuthUser;
import java.util.HashSet;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors;
import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
#Configuration
#ComponentScan(excludeFilters={
#ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE, value=UserService.class)
})
public class TestSpringRestConfig {
public static final SpringAuthUser AUTH_USER =
new SpringAuthUser("test", "test",
new HashSet<GrantedAuthority>() {{
add(new SimpleGrantedAuthority("ROLE_ADMIN"));
}}
);
#Bean
public UserService userService() {
return new TestUserService();
}
#Bean
public MockMvc mockMvc(WebApplicationContext wac) {
return MockMvcBuilders
.webAppContextSetup(wac)
.defaultRequest(MockMvcRequestBuilders.get("/").with(SecurityMockMvcRequestPostProcessors.user(AUTH_USER)))
.apply(SecurityMockMvcConfigurers.springSecurity())
.build();
}
}

Categories