Swagger endpoint not accessible - spring

I have a spring mvc project that uses gradle to build the project.
I used the steps described here in this project.
The build.gradle file has the following entry for swagger:
compile (libraries.swagger){
exclude group:'org.slf4j', module:'slf4j-log4j12'
exclude group:'org.slf4j', module:'slf4j-api'
exclude group:'junit', module:'junit'
}
The configuration for swagger is done in the project build.gradle as below:
swagger: "com.knappsack:swagger4spring-web:0.3.3"
My controller for the documetation end point is:
import com.knappsack.swagger4springweb.controller.ApiDocumentationController;
import com.wordnik.swagger.model.ApiInfo;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* This is an example of how you might extend the ApiDocumentationController in order to set your
* own RequestMapping (instead of the default "/api") among other possibilities. Going this route,
* you do not necessarily have to define the controller in your servlet context.
*/
#Controller
#RequestMapping(value = "/documentation")
public class SwaggerDocumentationController extends ApiDocumentationController {
public SwaggerDocumentationController() {
setBaseControllerPackage("com.controller");
setBaseModelPackage("com.domain");
setApiVersion("v1");
ApiInfo apiInfo = new ApiInfo("swagger",
"This is a basic web app for demonstrating swagger",
"http://localhost:9999/terms", "http://localhost:9999/contact", "MIT",
"http://opensource.org/licenses/MIT");
setApiInfo(apiInfo);
}
#RequestMapping(value = "/", method = RequestMethod.GET)
public String documentation() {
return "documentation";
}
}
And then I try to access the endpoints for documentation as: http://localhost:9999/myApp/documentation/test
test is the mapping mentioned inside the controller like below:
#Api(value = "test",
description = "A test controller to see if the services are up and running.")
#RequestMapping(value = {"/test"}, method = {RequestMethod.GET})
I get a 404 not found error while accessing the documentation URL above. Am I missing something?
Please do let me know if any more information is required.
Please help !!

I have user swagger-springmvc and its working fine. You can try it from following url
https://github.com/martypitt/swagger-springmvc

Related

#GetMapping method not calles

I am a beginner so please don't be mean.
I have got an html page index.html
And I want the method MainController::getListEmployee to be called.
In this method, I put a System.err to see if the method is called. And I see nothing.
Controller code
package com.cgi.listeemployes.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import com.cgi.listeemployes.model.User;
import com.cgi.listeemployes.repository.UserRepository;
#Controller // This means that this class is a Controller
#RequestMapping(path="/") // This means URL's start with /demo (after Application path)
public class MainController {
#Autowired // This means to get the bean called userRepository
// Which is auto-generated by Spring, we will use it to handle the data
private UserRepository userRepository;
#GetMapping(path="/index.html")
public #ResponseBody Iterable<User> getListEmployee() {
// This returns a JSON or XML with the users
System.err.println("getting ");
return userRepository.findAll();
}
#PostMapping(path="/add") // Map ONLY POST Requests
public #ResponseBody String addNewUser (#RequestParam String name
, #RequestParam String email) {
// #ResponseBody means the returned String is the response, not a view name
// #RequestParam means it is a parameter from the GET or POST request
User n = new User();
n.setName(name);
n.setEmail(email);
userRepository.save(n);
return "Saved";
}
#GetMapping(path="/all")
public #ResponseBody Iterable<User> getAllUsers() {
// This returns a JSON or XML with the users
return userRepository.findAll();
}
}
thanks for your help
When you want to return a html, just return a string with the name of the html file, it could be "Index" (without the .html).
In your #GetMapping(path="/index.html"), you are returning an object instead a html.
If you want to load data from database and render it at your html, then add the attribute "Model model" in your parameters, like this:
#GetMapping(path="/index.html")
public String getListEmployee(Model model) {
List<User> users = userRepository.findAll();
model.addAttribute("yourUsers", users); // this gonna inject the list of users in your html
System.err.println("getting ");
return "Index"
}
Then in your html, you can get the users with ${yourUsers} and do whatever you want.
I saw your project, it is missing the template engine. Template engine is what gonna get the data of your backend and show in your front/html. I added the Thymeleaf template engine into your pom.xml, and it worked. Here is the thymeleaf dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
To work with thymeleaf, you have to put all your html into a new folder called "templates" in the "resources", same level of "static". You cannot use html in the static folder, this folder should have only css, javascripts and assets.

Overriding repository endpoints automatically created by Spring Data Rest

I have a Spring project with spring-data-rest as a dependency. I have quite a number of repositories in my project, which spring-data-rest automatically created REST API endpoints for. This suited my needs pretty well until now. Now I have a requirement to change the default functionality of one endpoint for all my repositories, specifically, /BASE_PATH/REPOSITORY. This path responds with a paged list of all records of my db.
Now I want to reimplement this endpoint for all my repositories. This is where I am hitting a roadblock. I tried
#RestController
public class MyTableResource {
private MyTableService myTableService;
#Autowired
public MyTableResource(MyTableService myTableService) {
this.myTableService = myTableService;
}
#GetMapping(value = "/api/v1/myTables", produces = MediaTypes.HAL_JSON_VALUE)
public ResponseEntity getMyTables(#QuerydslPredicate(root = MyTable.class) Predicate predicate) throws NoSuchMethodException {
// My custom implementation
}
}
Now this somewhat works but the problem is I need to write almost the same code for all my repositories. I tried #GetMapping(value = "/api/v1/{repository}", produces = MediaTypes.HAL_JSON_VALUE) but this is also matching /api/v1/notarepository which I have implemented separately.
Also, even if I do #GetMapping(value = "/api/v1/{repository}", produces = MediaTypes.HAL_JSON_VALUE) I would like to get a handle to a repository object (MyTable) using {repository} path variable, which would be myTables in this case.
In short, I want to write a single custom controller for all my repositories, since the logic would be the same for each of them, while making sure the correct repository is called based on the path called also making sure that any path variables I introduce does not hide other controller classes I have written.
More things I have tried
I was attempting to get paged HATEOAS resource objects automatically from my list of entities. For this I found that I can use PagedResourceAssembler
#RestController
public class MyTableResource {
private MyTableService myTableService;
#Autowired
public MyTableResource(MyTableService myTableService) {
this.myTableService = myTableService;
}
#GetMapping(value = "/api/v1/myTables", produces = MediaTypes.HAL_JSON_VALUE)
public ResponseEntity getMyTables(#QuerydslPredicate(root = MyTable.class) Predicate predicate, PagedResourcesAssembler<Object> pagedResourcesAssembler) throws NoSuchMethodException {
// My custom implementation
return ResponseEntity.ok(pagedResourcesAssembler.toResource(myTableList);
}
}
This gives me a good response with the required links for the page but does not give links per entity. Then I found I can hook up PersistentEntityResourceAssembler and pass it to toResource above so I did
#RestController
public class MyTableResource {
private MyTableService myTableService;
#Autowired
public MyTableResource(MyTableService myTableService) {
this.myTableService = myTableService;
}
#GetMapping(value = "/api/v1/myTables", produces = MediaTypes.HAL_JSON_VALUE)
public ResponseEntity getMyTables(#QuerydslPredicate(root = MyTable.class) Predicate predicate, PagedResourcesAssembler<Object> pagedResourcesAssembler, PersistentEntityResourceAssembler assembler) throws NoSuchMethodException {
// My custom implementation
return ResponseEntity.ok(pagedResourcesAssembler.toResource(myTableList, assembler);
}
}
This does not work as reported in How to have PersistentEntityResourceAssembler injected into request methods of custom #RepositoryRestController in a #WebMvcTest unit test .
It kind of works if I replace #RestController with RepositoryRestController but then Predicate stops working as mentioned in https://jira.spring.io/browse/DATAREST-838 .
So, I tried using #QuerydslPredicate RootResourceInformation resourceInformation instead of #QuerydslPredicate(root = MyTable.class) Predicate predicate. This also did not work as my controller endpoint does not have /{repository} in it.
Then I tried setting #GetMapping(value = "/{repository}" produces = MediaTypes.HAL_JSON_VALUE). This threw a mapping conflict error.
So I am completely stuck as to what to do next.
You can extend the default behavior provided by Spring Data Rest by extending RepositoryRestMvcConfiguration.
RepositoryRestMvcConfiguration has a DelegatingHandlerMapping bean which holds a list of HandlerMapping. Spring iterates over this list and tries to find a handler for the request. The order of this list is important. The first one gets picked up first for the execution. So if we add a new handler in front of the ones we already have then our HandlerMapping will be called.
You can use whatever logic you want to find the handler for the request. In your case, this would be if the path variable is a repository name.
The following code adds a new handler:
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration;
import org.springframework.data.rest.webmvc.support.DelegatingHandlerMapping;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.HandlerMapping;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
#Configuration
public class CustomRestMvcConfiguration extends RepositoryRestMvcConfiguration {
public CustomRestMvcConfiguration(ApplicationContext context,
ObjectFactory<ConversionService> conversionService) {
super(context, conversionService);
}
#Override public DelegatingHandlerMapping restHandlerMapping() {
DelegatingHandlerMapping delegatingHandlerMapping = super.restHandlerMapping();
List<HandlerMapping> delegates = delegatingHandlerMapping.getDelegates();
delegates.add(0, new HandlerMapping() {
#Override public HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//Your custom logic to decide if you should handle the request
//If you don't want to handle the request return null
return null;
}
});
return new DelegatingHandlerMapping(delegates);
}
}
Hope this helps!
Note: RepositoryRestHandlerMapping is the default one you can check it while writing your logic. It might be helpful.

Spring CLI (v2.1.3) can't grab spring depenencies using Grab annotation in a simple groovy file

Bellow I'm trying to create a simple groovy file that will expose a simple service saying hello, and also handle 404 errors by implementing ErrorController ( To avoid whitelabel error page, I could inject an HttpServletRequest and retrieve the status, but this isn't my concern for now )
package org.test
#Grab("spring-boot-autoconfigure")
#Grab("spring-boot-autoconfigure-processor")
#RestController
public class HelloController implements ErrorController{
#GetMapping(value="/hello")
public String sayHello(){
return "Hey Joker";
}
#Override
public String getErrorPath(){
return "/error";
}
#GetMapping(value="/error")
public String notFoundException(){
return "{error:'NOT_FOUND', message:'Resource Not Found'}";
}
}
So when I run > spring run restController.groovy I get :
file:/Users/xxxx/yyyy/restController.groovy: 3: unable to resolve class ErrorController
# line 3, column 1.
#Grab("spring-boot-autoconfigure")
^
1 error
I was able to start your controller and calling the hello endpoint, by adding the following import statement (don't know why only the ErrorController import is required)
import org.springframework.boot.web.servlet.error.ErrorController

Spring cloud config server. Environment variables in properties

I configured Spring Cloud Config server like this:
#SpringBootApplication
#EnableAutoConfiguration
#EnableConfigServer
public class ConfigServer {
public static void main(String[] args) {
SpringApplication.run(ConfigServer.class, args);
}
}
I'm using 'native' profile so properties are picked up from the file system:
server.port=8888
spring.profiles.active=native
spring.cloud.config.server.native.search-locations: classpath:/global
Now the tricky part is that some properties contain environmental variables. Properties in 'global/application-production.properties' are configured like this:
test=${DOCKER_HOST}
When I start up Config Server - everything works fine. However when I access http://localhost:8888/testapp/production I see this:
{
name: "testapp",
profiles: [
"production"
],
label: null,
version: null,
propertySources: [
{
name: "classpath:/global/application-production.properties",
source: {
test: "${DOCKER_HOST}"
}
}
]
}
So value from ENV variable is not replacing ${DOCKER_HOST} put rather returned as is.
But if I access http://localhost:8888/application-production.properties then result is non JSON but rather plain text:
test: tcp://192.168.99.100:2376
Spring documentation says:
The YAML and properties representations have an additional flag (provided as a boolean query parameter resolvePlaceholders) to signal that placeholders in the source documents, in the standard Spring ${…​} form, should be resolved in the output where possible before rendering. This is a useful feature for consumers that don’t know about the Spring placeholder conventions.
For some reason resolvePlaceholders is not working for JSON representation thus server config clients need to be aware of all ENV variables configured on server.
Is it possible to force JSON representation resolvePlaceholders same way as plain text (properties) representation?
I faced the same issue. After looking into Spring Cloud Config Repository I have found the following commit:
Omit system properties and env vars from placeholders in config
It looks like such behavior is not supported.
You can try the Property Overrides feature to override properties from git Environment Repository.
To override property foo at runtime, just set a system property or an environment variable spring.cloud.config.server.overrides.foo before starting the config server.
There was an update in order to accomplish this, in the following merge. 1 I found an implementation for resolvePlaceholders. Which gave me the idea of just creating a new controller which uses the EnvironmentController. This will allow you to resolve configuration, this is a good bootstrap.
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.config.server.environment.EnvironmentController;
import org.springframework.cloud.config.server.environment.EnvironmentRepository;
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;
#RestController
#RequestMapping(method = RequestMethod.GET, path = "resolved/${spring.cloud.config.server.prefix:}")
public class ReplacedEnvironmentController {
private EnvironmentController environmentController;
#Autowired
public ReplacedEnvironmentController(EnvironmentRepository repository) {
environmentController = new EnvironmentController(repository, new ObjectMapper());
}
public ReplacedEnvironmentController(EnvironmentRepository repository,
ObjectMapper objectMapper) {
environmentController = new EnvironmentController(repository, objectMapper);
}
#RequestMapping("/{name}/{profiles:.*[^-].*}")
public ResponseEntity<String> resolvedDefaultLabel(#PathVariable String name,
#PathVariable String profiles) throws Exception {
return resolvedLabelled(name, profiles, null);
}
#RequestMapping("/{name}/{profiles}/{label:.*}")
public ResponseEntity<String> resolvedLabelled(#PathVariable String name, #PathVariable String profiles,
#PathVariable String label) throws Exception {
return environmentController.labelledJsonProperties(name, profiles, label, true);
}
}

Spring Data Rest - Add link to search endpoint

In our Spring-Data-Rest Project we have a custom (fuzzy) search on a /buergers/search/findBuergerFuzzy?searchString="..." endpoint.
Is it possible to add a link for it on the /buergers/search endpoint (Without overriding the automatically exposed Repository findBy Methods)?
The Controller exposing the search:
#BasePathAwareController
#RequestMapping("/buergers/search/")
public class BuergerSearchController {
#Autowired
QueryService service;
#RequestMapping(value = "/findBuergerFuzzy", method = RequestMethod.GET)
public
#ResponseBody
ResponseEntity<?> findBuergerFuzzy(PersistentEntityResourceAssembler assembler, #Param("searchString") String searchString) {
if (searchString.length() < 3)
throw new IllegalArgumentException("Search String must be at least 3 chars long.");
List<Buerger> list = service.query(searchString, Buerger.class, new String[]{"vorname", "nachname", "geburtsdatum", "augenfarbe"});
final List<PersistentEntityResource> collect = list.stream().map(assembler::toResource).collect(Collectors.toList());
return new ResponseEntity<Object>(new Resources<>(collect), HttpStatus.OK);
}
}
UPDATE: This is an outdated workaround answer. Upgrade to Spring HATEOAS 1.0.
Old Workaround:
Digging the spring-data-rest source i found the RepositorySearchesResource which seems to solve the problem.
#Component
public class SearchResourcesProcessor implements ResourceProcessor<RepositorySearchesResource> {
#Override
public RepositorySearchesResource process(RepositorySearchesResource repositorySearchesResource) {
final String search = repositorySearchesResource.getId().getHref();
final Link findFullTextFuzzy = new Link(search + "/findFullTextFuzzy{?q}").withRel("findFullTextFuzzy");
repositorySearchesResource.add(findFullTextFuzzy);
return repositorySearchesResource;
}
}
Because we generate this code by templates, this is sufficient and fullfills our needs. Make sure to check the comments for the right and safe way.
Version
migrate-to-1.0.changes
ResourceSupport is now RepresentationModel
Resource is now EntityModel
Resources is now CollectionModel
PagedResources is now PagedModel
Code
The code for new version:
import org.springframework.data.rest.webmvc.RepositorySearchesResource;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.server.RepresentationModelProcessor;
import org.springframework.stereotype.Component;
#Component
public class RepositorySearchesProcessor implements RepresentationModelProcessor<RepositorySearchesResource> {
#Override
public RepositorySearchesResource process(RepositorySearchesResource model) {
System.out.println(model.getDomainType());
model.add(Link.of(model.getRequiredLink("self").getHref() + "/findFullTextFuzzy{?q}").withRel("findFullTextFuzzy"));
return model;
}
}
How
About how to find what resource or model you use, after setting breakpoints in each method of RepresentationModel, you maybe find something useful :

Resources