Retrieving additional non DB information using Spring Data Rest - spring-data

I am using Spring Data Rest to expose a news feed REST API. I want to add an image (location) to the entity which will be retrieved by a separate web service API call.
What is the best way to do this using Spring Data Rest or would I have to create another separate REST API call/domain object etc.?
Any sample code would be fantastic.

You should use a ResourceProcessor
The Spring Data REST exporter executes any discovered ResourceProcessor's before it creates the output representation
#Bean
public ResourceProcessor<Resource<MyEntity>> myEntityProcessor() {
return new ResourceProcessor<Resource<MyEntity>>() {
#Override
public Resource<MyEntity> process(Resource<MyEntity> resource) {
resource.add(new Link("http://localhost:8080/images/images.jpg", "image"));
return resource;
}
};
}
Another example with access to the repository and EntityLinks object that helps to build links related to the entity..
#Component
class MyEntityResourceProcessor implements ResourceProcessor<Resource<MyEntity>> {
#Autoware
private MyEntityRepo repo;
#Autoware
private EntityLinks entityLinks;
#Override
public Resource<MyEntity> process(Resource<MyEntity> resource) {
MyEntity entity = resource.getContent();
// Some entity processing...
Link link entityLinks.linkForSingleResource(entity).slash("...").withRel("...")
resource.add(link);
return resource;
}
}
More examples of using ResourceProcessor you can find in RESTBucks project

Related

Rest Easy Swagger api documentation

In my application restful services created using Reast-Easy.
Am trying to document restful services using io.springfox:springfox-swagger2:2.9.2 and io.springfox:springfox-swagger-ui:2.9.2
Is this correct approach?
When i tried, am getting swagger UI page but getting "No Operations defined in Spec!"
From the logs, am getting below exception,
javax.ws.rs.NotFoundException: RESTEASY003210: Could not find resource for full path: http://localhost:8080/test/
at org.jboss.resteasy.core.registry.ClassNode.match(ClassNode.java:61)
at org.jboss.resteasy.core.registry.RootClassNode.match(RootClassNode.java:47)
Swagger Configuration:
#Configuration
#EnableSwagger2
public class SwaggerConfig extends WebMvcConfigurationSupport {
#Bean
public Docket apiMonitoramento() {
return new Docket(DocumentationType.SWAGGER_2).select().apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any()).build().apiInfo(apiInfo());
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder().title("REST API").description("REST API").build();
}
#Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
}
Is any issue in configuration ? Could you please help me out in this?
Springfox-swagger does not support RestEasy bindings as stated in this issue description
Its primarily intended to scan and build its model out of Spring Web MVC controllers.

Drilling down model objects on Swagger

My application is a restful api and its integrated with Swagger and OpenAPI.
I have generated all Java stubs using OpenAPI YAML file and everything is working fine.
But when i try o drill down model objects on Swagger then it cannot locate some of objects although there are part of project as project compiles fine.
As shown in below screenshot, drilldown fails to locate COnfiguration object.
Any ideas on how to resolve this.
Edit:
I have a restful webservice and i generate all the java stubs [Data transfer objects] from a YAML file using openapi-generator plugin. This plugin automatically generates a class OpenAPIDocumentationConfig and following are the details of the class. After this setup, models are automatically generated in Swagger UI.
Also want to add that I am using OpenAPI 3.0 but i need to split Object definitions into multiple files. So i am referring to them using definitions as i don't believe component schemas can be split into multiple files.
#Configuration
#EnableSwagger2
public class OpenAPIDocumentationConfig {
ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("ABC Service")
.description("ABC Service")
.license("")
.licenseUrl("http://unlicense.org")
.termsOfServiceUrl("")
.version("1.0.0")
.contact(new Contact("","", "xyz#abc.com"))
.build();
}
#Bean
public Docket customImplementation(ServletContext servletContext, #Value("${openapi.studioVALService.base-path:}") String basePath) {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("com.x.y.z"))
.build()
.pathProvider(new BasePathAwareRelativePathProvider(servletContext, basePath))
.directModelSubstitute(java.time.LocalDate.class, java.sql.Date.class)
.directModelSubstitute(java.time.OffsetDateTime.class, java.util.Date.class)
.apiInfo(apiInfo());
}
class BasePathAwareRelativePathProvider extends RelativePathProvider {
private String basePath;
public BasePathAwareRelativePathProvider(ServletContext servletContext, String basePath) {
super(servletContext);
this.basePath = basePath;
}
#Override
protected String applicationPath() {
return Paths.removeAdjacentForwardSlashes(UriComponentsBuilder.fromPath(super.applicationPath()).path(basePath).build().toString());
}
#Override
public String getOperationPath(String operationPath) {
UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromPath("/");
return Paths.removeAdjacentForwardSlashes(
uriComponentsBuilder.path(operationPath.replaceFirst("^" + basePath, "")).build().toString());
}
}
}
EDIT 2:
I moved all definitions to components and schemas but they are still split in multiple files and are referring to components across files but still i get the same error.
If you are using OpenAPI 3 you should put schemas that you want to reuse inside components. To refeer to it you must use refs like:
$ref: "#/components/schemas/EquityOptionConfigurationDO"

Using WebServiceGatewaySupport to handle requests to multiple webservices

I'm using spring-ws-core to build a SOAP client. For this I'm extending WebServiceGatewaySupport to make the service calls.
public class WeatherClient extends WebServiceGatewaySupport {
...
public WeatherResponse getCityForecastByZip(String zipCode) {
GetCityForecastByZIP request = new GetCityForecastByZIP();
request.setZIP(zipCode);
GetCityForecastByZIPResponse response = (GetCityForecastByZIPResponse) this.getWebServiceTemplate().marshalSendAndReceive(request,
new SoapActionCallback("http://ws.cdyne.com/WeatherWS/GetCityForecastByZIP"));
return response;
}
...
}
Spring configuration is pretty straightforward
#Configuration
public class WebServicesConfiguration {
private static final String WEATHER_SERVICE_DEFAULT_URI = "...";
#Bean(name = "servicesMarshaller")
public Jaxb2Marshaller servicesMarshaller() {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setContextPath("some.package");
return marshaller;
}
#Bean
public WeatherClient weatherService(#Qualifier("servicesMarshaller") Jaxb2Marshaller marshaller) {
WeatherClient client = new WeatherClient(WEATHER_SERVICE_DEFAULT_URI);
client.setMarshaller(marshaller);
client.setUnmarshaller(marshaller);
return client;
}
}
This works just fine for a single web service. Now, suppose that I have many similar web services, but each one has it's own .wsdl specification and URI. I know that I can make a service call through the spring WebServiceTemplate and specify the URI to use. So my idea was to use a single WebServiceGatewaySupport to handle all the calls to the different services. In each call, I would pass the soap action, the corresponding request, if any, and the web service URL. My application is suppose to run in a multi-threaded environment.
Is this a good practice to use a single WebServiceGatewaySupport to handle concurrent calls to different URIs?
Looking to the WebServiceGatewaySupport source code, the short asnwer: yes, it is OK to use it for different URLs, as well as the underlying WebServiceTemplate is thread-safe.
Your implementation will be thread-safe too, if you don't save some state between requests.

New REST request in JHipster returning "Not Found - 404"

Error Description
Hey all,
I'm having trouble getting a response from my manually added controllers in a JHipster-based project. I scaffolded up the original project, and then hand-wrote my own services and controllers.
When I execute the call, the error result I get from SoapUI (which I am using for initial validation) is at the following url: http://imgur.com/04FpmEZ,Havk1EL#0
And if I look at my Eclipse console error, I see the following: http://imgur.com/04FpmEZ,Havk1EL#1
Controller
/**
* GET /courses/json -> get all the courses.
*/
#RequestMapping(value = "/json",
method = RequestMethod.GET,
produces = "application/json")
#Timed
public List<Course> getAll() {
log.debug("REST request to get all Courses");
return courseService.findAllCourses();
}
Service
package com.testapp.myapp.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.testapp.myapp.domain.Course;
import com.testapp.myapp.repository.CourseRepository;
#Service
#Transactional
public class CourseServiceImpl implements CourseService {
#Autowired
CourseRepository courseRepository;
public long countAllCourses() {
return courseRepository.count();
}
public void deleteCourse(Course course) {
courseRepository.delete(course);
}
public Course findCourse(Integer id) {
return courseRepository.findOne(id);
}
public List<Course> findAllCourses() {
return courseRepository.findAll();
}
public List<Course> findCourseEntries(int firstResult, int maxResults) {
return courseRepository.findAll(new org.springframework.data.domain.PageRequest(firstResult / maxResults, maxResults)).getContent();
}
public void saveCourse(Course course) {
courseRepository.save(course);
}
public Course updateCourse(Course course) {
return courseRepository.save(course);
}
}
What is confusing about this is that I ran the query provided by hibernate directly against my DB, and it returns the record set just fine. Is it possible that the service is being blocked due to some security or authentication constraint auto-loaded by JHipster?
A few issues existed, all related to migrating from Roo into JHipster:
I had built my new Controller class with org.sprinframework.stereotype.Controller's #Controller annotation, rather than #RestController... The original controller annotation was scaffolded up by Spring Roo (which is highly effective at generating services from an existing DB using their DBRE addon, I might add).
After switching over to #RestController, I ran into the second hurdle, which I had originally expected as a JHipster implementation : the service was being blocked due to authentication constraints.
This was fixed by going into com.[projectname].config and updating the SecurityConfiguration.java file, exposing specifically the APIs that I wanted.
Then, I had to make sure Hibernate was getting the full collection of the objects being requested (I had a lot of complex relational entities being built by Roo)... failed to lazily initialize a collection of role...
In the Domain entity, change your #OneToMany annotation as follows:
#OneToMany(fetch = FetchType.EAGER, mappedBy = "courseId", cascade = CascadeType.REMOVE)
Source of answer: Solve "failed to lazily initialize a collection of role" exception
Voila! Functioning, secure-able JSON-based APIs, fully reverse engineered from an existing Postgresql DB, loaded into a prescaffolded Angular front-end.

Custom Logic and Proxy Classes in ADO.NET Data Services

I've just read "Injecting Custom Logic in ADO.NET Data Services" and my next question is, How do you get your [WebGet] method to show up in the client-side proxy classes? Sure, I can call this directly (RESTfully) with, say, WebClient but I thought the strong typing features in ADO.NET Data Services would "hide" this from me auto-magically.
So here we have:
public class MyService : DataService<MyDataSource>
{
// This method is called only once to initialize service-wide policies.
public static void InitializeService(IDataServiceConfiguration config)
{
config.SetEntitySetAccessRule("Customers", EntitySetRights.AllRead);
config.SetServiceOperationAccessRule("CustomersInCity", ServiceOperationRights.All);
}
[WebGet]
public IQueryable<MyDataSource.Customers> CustomersInCity(string city)
{
return from c in this.CurrentDataSource.Customers
where c.City == city
select c;
}
}
How can I get CustomersInCity() to show up in my client-side class defintions?
When you see your Odata in browser, you will see link ...
e.g. http://localhost:1234/odataService.svc
just write your method name after the link
for your method it will be something like this...
http://localhost:1234/odataService.svc/CustomersInCity?city="London"