Customize endpoints of dockets with springfox Swagger - rest

I've searched on the internet how to customize endpoints of my multiple dockets, but haven't found the answer.
My module has several APIs. I want to generate Swagger documentation on different endpoints, each one positioned on the root of its corresponding API. For example :
localhost:8080/v1/subscriptions/doc
localhost:8080/v1/buckets/doc
I've found only one way to have different endpoints for my dockets, but the URL don't correspond to what I want. They are :
localhost:8080/doc?group=subscriptions
localhost:8080/doc?group=buckets
Here is my Swagger configuration file
#Configuration
#EnableSwagger2
public class SwaggerConfiguration {
#Value("${info.version}")
private String version;
#Bean
public Docket subscriptionsApi() {
return new Docket(DocumentationType.SWAGGER_2)
.groupName("subscriptions")
.apiInfo(subscriptionsApiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.mymodule"))
.paths(PathSelectors.ant("/v1/subscriptions/**"))
.build();
}
#Bean
public Docket bucketsApi() {
return new Docket(DocumentationType.SWAGGER_2)
.groupName("buckets")
.apiInfo(bucketsApiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.mymodule"))
.paths(PathSelectors.ant("/v1/buckets/**"))
.build();
}
private ApiInfo subscriptionsApiInfo() {
return new ApiInfoBuilder()
.title("Subscriptions Api definition")
.description("Subscriptions Api definition")
.version(version)
.build();
}
private ApiInfo bucketsApiInfo() {
return new ApiInfoBuilder()
.title("Bucket Api definition")
.description("Bucket Api definition")
.version(version)
.build();
}
}
And in my application.yml file, I've written :
springfox.documentation.swagger.v2.path: "/doc"
Do you know a way to define the endpoints on the way I want?
Thanks in advance

I've found the answer!
#Configuration
#EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addRedirectViewController("/v1/subscriptions/doc", "/doc?group=subscriptions");
}
}

Related

Spring Cloud Config - #ConditionalOnProperty and #Configuration behavior

I'm having some issues with #ConditionaOnProperty and #Configuration behavior not being updated based on the changes in the application properties (config file).
Here's what I have
Configuration
#Configuration
public class RandomRestConfig {
#Value("${external.message.root.uri}")
private String rootUri;
#Bean
#RefreshScope
public RestTemplate randomRestTemplate() {
return new RestTemplateBuilder()
.rootUri(rootUri)
.build();
}
}
Conditional service
#Service
#RefreshScope
#ConditionalOnProperty(value = "external.message.enabled", havingValue = "true")
public class RandomRestService {
#Autowired
#Qualifier("randomRestTemplate")
private RestTemplate restTemplate;
public String getMessageFromService() {
final var response = restTemplate.getForEntity("/trips/trip-text", String.class);
return response.getBody();
}
}
Usage via controller
#RefreshScope
#RestController
public class MessageRestController {
#Value("${message: No message found}")
private String message;
private RandomRestService randomRestService;
public MessageRestController(Optional<RandomRestService> optionalRestService) {
optionalRestService.ifPresent(service -> this.randomRestService = service);
}
#GetMapping("/external-message")
String getExternalMessage() {
if (randomRestService == null) {
throw new RuntimeException("Invalid request - rest is disabled");
}
return randomRestService.getMessageFromService();
}
}
Now, what I'm trying to achieve are
Change the value of the rootUri. I changed it in the config file but it didn't take effect, the old URI is still in effect.
Change the value of external.message.enabled from false to true, but the service is still null in MessageRestController. I was expecting that the bean will be updated.
Now, with both scenarios, I manually triggered the actuator /refresh endpoint and both properties were visible in the response.
[
"config.client.version",
"external.message.root.uri",
"external.message.enabled"
]
Am I missing something? Or is it possible at all?
Thank you!

SimpleBatchConfig not picking up my DefaultBatchConfigurer

I wrote a simple demo to overwrite default jobrepo. Instead of map based I wanted a H2 db to hold persistent metadata.
Therefore I wrote a CustomBatchConfigurer like this:
#Configuration
public class CustomBatchConfigurer extends DefaultBatchConfigurer {
#Autowired
#Qualifier("repo-db")
DataSource dataSource;
#Override
public void setDataSource(DataSource dataSource) {
super.setDataSource(dataSource);
}
#Bean(name = "repo-db")
public DataSource getJobRepoDataSource() {
return DataSourceBuilder
.create()
.url("jdbc:h2:tcp://localhost/~/src/spring-batch/batch_repo")
.driverClassName("org.h2.Driver")
.username("sa")
.password("test")
.type(HikariDataSource.class)
.build();
}
}
But Spring-Batch is not picking it up:
o.s.b.c.c.a.DefaultBatchConfigurer: No datasource was provided...using a Map based JobRepository
What am I doing wrong? I thought I had followed the instructions on spring doc ref.
Thanks and regards,
Jörg
You configuration should look more like this:
#Configuration
public class CustomBatchConfiguration {
#Bean
public BatchConfigurer batchConfigurer(#Qualifier("repo-db") DataSource dataSource) {
return new DefaultBatchConfigurer(dataSource);
}
#Bean(name = "repo-db")
public DataSource jobRepoDataSource() {
return DataSourceBuilder
.create()
.url("jdbc:h2:tcp://localhost/~/src/spring-batch/batch_repo")
.driverClassName("org.h2.Driver")
.username("sa")
.password("test")
.type(HikariDataSource.class)
.build();
}
}
If your bean methods are proxied (which is the default), you can also simplify the first bean method to
#Bean
public BatchConfigurer batchConfigurer() {
return new DefaultBatchConfigurer(jobRepoDataSource());
}
Please also have a second look at the official documentation: https://docs.spring.io/spring-batch/docs/4.3.x/reference/html/job.html#javaConfig

Spring Boot : Apache CXF SOAP with #RestController

I am making Spring Boot rest service using #RestController, in same project I am also exposing the Apache CXF SOAP service like
#RestController Code
#RestController
#RequestMapping(value = "/mobileTopUpService")
public class TopUpRestService {
#RequestMapping(value="/processTopUpRequest", method=RequestMethod.POST,
consumes = MediaType.APPLICATION_JSON_VALUE, produces=MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<TopUpRequestDTO> processTopUpRequest(HttpServletRequest httpServletRequest, #Valid RequestEntity<TopUpRequestDTO> _requestEntity) {
return new ResponseEntity<>(new exampleDTO("hi"), HttpStatus.OK);
}
}
Apache CXF SOAP
#Configuration
#Import(ApplicationConfiguration.class)
public class WebServiceConfig
{
public static final String SERVLET_MAPPING_URL_PATH = "/*";
public static final String SERVICE_NAME_URL_PATH = "/services";
#Autowired
private ApplicationConfiguration applicationConfiguration;
#Bean
public ServletRegistrationBean dispatcherServlet()
{
return new ServletRegistrationBean(new CXFServlet(), SERVLET_MAPPING_URL_PATH);
}
#Bean(name = Bus.DEFAULT_BUS_ID)
public SpringBus springBus()
{
return new SpringBus();
}
#Bean
public ERSBackendService ersBackendServiceImpl()
{
return new ERSBackendServiceImpl();
}
#Bean
public Endpoint endpoint()
{
EndpointImpl endpoint = new EndpointImpl(springBus(), ersBackendServiceImpl());
endpoint.publish(SERVICE_NAME_URL_PATH);
AutomaticWorkQueue executorQueue = createThreadPoolExecutorQueue();
endpoint.setExecutor(executorQueue);
return endpoint;
}
#Bean
public EmbeddedServletContainerFactory embeddedServletContainerFactory()
{
TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory("/backend-service", Integer.valueOf(applicationConfiguration.getPort()));
return factory;
}
}
SOAP Service are running fine after change but REST (#RestController) stop working, but if I disables the methods
// #Bean
// public ServletRegistrationBean dispatcherServlet()
// {
// return new ServletRegistrationBean(new CXFServlet(), SERVLET_MAPPING_URL_PATH);
// }
and
#Bean
// public EmbeddedServletContainerFactory embeddedServletContainerFactory()
// {
// TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory("/backend-service", Integer.valueOf("8007"));
// return factory;
// }
//}
and run
http://localhost:8007/mobileTopUpService/processTopUpRequest/
the #RestController runs fine but not soap.
I need to run both #RestController and CXF SOAP, kindly suggest.
thanks
I've just working with SOAP and REST servicies together. Here's my configuration: (At the end of the answer, I included a sample project)
application.properties
cxf.path=/services
cxf.servlet.load-on-startup=-1
WebServiceConfig
#Configuration
#ConditionalOnWebApplication
public class WebServiceConfig {
private static final Logger LOGGER = LoggerFactory.getLogger(WsEndpointsConfiguration.class);
#Autowired
private Bus bus;
#Value("${cxf.path}")
private String cxfServletPath;
#Autowired
private YourServiceInterface yourService;
public Logger getLOGGER() {
return LOGGER;
}
public Bus getBus() {
return bus;
}
public String getCxfServletPath() {
return cxfServletPath;
}
public void setCxfServletPath(String cxfServletPath) {
this.cxfServletPath = cxfServletPath;
}
public YourServiceInterface getYourServiceInterface() {
return yourService;
}
#Bean
public Endpoint yourWebServiceEndpoint() {
EndpointImpl endpoint = new EndpointImpl(getBus(), new YourWebServiceEndpoint(getYourServiceInterface()));
endpoint.publish("/YourWebService");
return endpoint;
}
#Bean
public FilterRegistrationBean openEntityManagerInViewFilter() {
FilterRegistrationBean filterRegBean = new FilterRegistrationBean();
filterRegBean.setFilter(new OpenEntityManagerInViewFilter());
List<String> urlPatterns = new ArrayList<String>();
urlPatterns.add(getCxfServletPath() + "/*");
filterRegBean.setUrlPatterns(urlPatterns);
if (getLOGGER().isDebugEnabled()) {
getLOGGER().debug("Registering the 'OpenEntityManagerInViewFilter' filter for the '"
.concat(getCxfServletPath() + "/*").concat("' URL."));
}
return filterRegBean;
}
}
Replace the #Autowired service with your own service interface.
You could check a full example here:
https://github.com/jcagarcia/proofs/tree/master/spring-security-and-formatters
Related classes from the sample provided above:
Configuration class
WebService API
WebService Implementation
application.properties
Hope it helps,
I resolved it as #EnableWebMvc in class where starting boot app
i.e SpringApplication.run(ApplicationStartup.class, args);
Moved ServletRegistrationBean in spring boot class too,
disbaled method
#Bean
public EmbeddedServletContainerFactory embeddedServletContainerFactory() {...}

Implementing RequestMethod.PATCH in Spring RestController

I am creating a Rest API for a MongoDB database using MongoRepository. I want to create an endpoint that uses "RequestMethod.PATCH" and implements the "PATCH" functionality: delta update with fields provided in the #RequestBody.
The functionality that I want already exists in "Spring Data Rest" by using the "#RepositoryRestResource" annotation on my Repository class as described here https://spring.io/guides/gs/accessing-data-rest/
But I don't want to expose my Repository class like that. I like the classic Controller->Service->Repository lineage. My controller looks like this:
#RestController
public class ActivitiesController {
#Autowired
ActivitiesService activitiesService;
#RequestMapping(value="activities", method=RequestMethod.PATCH)
public ActivityModel updateActivity(
#RequestBody ActivityModel activityModel
){
//Input ActivityModel will only have subset of fields that have been changed, aka the delta
return activitiesService.update(activityModel);
}
#RequestMapping(value="activities", method=RequestMethod.PUT)
public ActivityModel updateActivity(
#RequestBody ActivityModel activityModel
){
//Input ActivityModel will have all fields populated
return activitiesService.save(activityModel);
}
}
And my repository is here:
#Repository
public interface ActivitiesRepo extends MongoRepository<ActivityModel, String> {
//out of the box implementation
}
My problem is that, from what I can tell, MongoRepository does not provide delta updates out of the box the way that Spring Data Rest does. How can I implement that functionality in the Service layer here?:
#Service
public class ActivitiesService {
#Autowired
ActivitiesRepo activitiesRepo;
public ActivityModel update(ActivityModel activityModel){
//delta update implementation, aka PATCH implementation
}
//method that should only be used with RequestMethod.PUT
public ActivityModel save(ActivityModel activityModel){
return activitiesRepo.save(activityModel);
}
}
What do you think of this solution for a manual "PATCH" implementation:
public class ModelUtil {
public static <T> Object update(Object origModel, Object dirtyModel, Class<T> clazz){
ObjectMapper m = new ObjectMapper();
HashMap<String, Object> origModelAsMap = m.convertValue(origModel, new TypeReference<Map<String, Object>>() {});
HashMap<String, Object> dirtyModelAsMap = m.convertValue(dirtyModel, new TypeReference<Map<String, Object>>() {});
dirtyModelAsMap.forEach((k, v)-> {
origModelAsMap.put(k, v);
});
return m.convertValue(origModelAsMap, clazz);
}
}

Spring Java config message convertor priority

I have defined two convertors like this using Spring Java config. I always get a XML response unless I specified the 'Accept=applicaiton/json' in the HTTP header. Is there a way to set the default convertor to be JSON instead of XML convertor.
#EnableWebMvc
#Configuration
#ComponentScan(basePackages = {"foo.bar"})
public class WebMvcConfig extends WebMvcConfigurerAdapter {
...
#Bean
public MappingJackson2HttpMessageConverter jsonConverter() {
MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
ObjectMapper objectMapper = new ObjectMapper();
jsonConverter.setObjectMapper(objectMapper);
return jsonConverter;
}
#Bean
public MappingJackson2XmlHttpMessageConverter xmlConverter() {
MappingJackson2XmlHttpMessageConverter xmlConverter = new MappingJackson2XmlHttpMessageConverter();
return xmlConverter;
}
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(jsonConverter());
converters.add(xmlConverter());
super.configureMessageConverters(converters);
}
Here is my controller.
#RequestMapping(value = "/product")
public
#ResponseBody
BSONObject getProducts(#RequestParam String ids,
#RequestParam(required = false) String types) {
List<BSONObject> products = commonDataService.getData(ids, types);
return products;
}
Try the following configuration, it sets up the default Content negotiation strategy(based on article here):
#Configuration
#EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.defaultContentType(MediaType.APPLICATION_JSON);
}
}
Another option will be to provide other ways of specifying the content format, if Accept header is not feasible, an option could be to specify an extension /myuri/sample.json which would be returned as a json.