{
"id": 109433,
"name": "test",
"value": [
"c"
]
}
Having the node above I execute this post to modify my object so the value contains "a","b" instead of "c":
curl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' -d ' {
"id": 109433,
"name": "test",
"value": [
"a","b"
]
}' 'http://localhost:8080/configs'
Then I execute a get to get all nodes
curl -X GET --header 'Accept: application/json' 'http://localhost:8080/configs'
For some weird reason it will return
{
"id": 109433,
"name": "test",
"value": [
"a",
"b",
"c"
]
}
If I go to neo4j outside my web application and query it I will get
{
"id": 109433,
"name": "test",
"value": [
"a","b"
]
}
So just to be sure I have some sort of data pollution like a cache running somewhere I stop the server and turn it on again, then I do the get again
curl -X GET --header 'Accept: application/json' 'http://localhost:8080/configs'
returns:
{
"id": 109433,
"name": "test",
"value": [
"a",
"b",
]
}
So now "c" is nowhere to be seen.
Here is the code I'm using
api
package io.swagger.api;
import io.swagger.annotations.*;
import io.swagger.model.Config;
import io.swagger.service.ConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
#RestController
#RequestMapping(value = "/configs", produces = {APPLICATION_JSON_VALUE})
#Api(value = "/configs", description = "the configs API")
public class ConfigsApi {
#Autowired
private ConfigService configService;
#ApiOperation(value = "", notes = "Gets `Config` objects. ", response = Config.class, responseContainer = "List")
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Successful response", response = Config.class) })
#RequestMapping(value = "", produces = { "application/json" }, method = RequestMethod.GET)
public ResponseEntity<List<Config>> getAllConfigs() throws NotFoundException {
return new ResponseEntity<List<Config>>(configService.getAll(),HttpStatus.OK);
}
#ApiOperation(value = "", notes = "Creates/updates `Config` object. ", response = Void.class)
#ApiResponses(value = {
#ApiResponse(code = 200, message = "successful operation", response = Void.class),
#ApiResponse(code = 400, message = "Invalid Config", response = Void.class) })
#RequestMapping(value = "", produces = { "application/json" }, method = RequestMethod.POST)
public ResponseEntity<Void> createConfigByKey( #ApiParam(value = "Updated user object" ,required=true ) #RequestBody Config body ) throws NotFoundException {
configService.merge(body);
return new ResponseEntity<Void>(HttpStatus.OK);
}
}
Service
package io.swagger.service;
import com.google.common.collect.Lists;
import io.swagger.model.Config;
import io.swagger.repository.ConfigRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
#Slf4j
#Service
public class ConfigService {
#Autowired
private ConfigRepository configRepository;
public List<Config> getAll() {return Lists.newArrayList(configRepository.findAll());}
public void merge(Config c){configRepository.save(c);}
}
Repository
package io.swagger.repository;
import io.swagger.model.Config;
import org.springframework.data.neo4j.annotation.Query;
import org.springframework.data.neo4j.repository.GraphRepository;
public interface ConfigRepository extends GraphRepository<Config> {
}
Neo4j Configuration
package io.swagger.configuration;
import org.neo4j.ogm.session.SessionFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.neo4j.config.Neo4jConfiguration;
import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories;
#Configuration
#EnableNeo4jRepositories("io.swagger.*")
public class InventoryApiNeo4jConfiguration extends Neo4jConfiguration {
#Value("${neo4j.ogm.driver}")
private String driver;
#Value("${neo4j.ogm.URI}")
private String uri;
#Value("${neo4j.ogm.connection.pool.size}")
private int connectionPoolSize;
#Value("${neo4j.ogm.encryption.level}")
private String encryptionLevel;
#Bean
public org.neo4j.ogm.config.Configuration getConfiguration() {
org.neo4j.ogm.config.Configuration configuration = new org.neo4j.ogm.config.Configuration();
configuration.driverConfiguration()
.setDriverClassName(driver)
.setURI(uri)
.setConnectionPoolSize(connectionPoolSize)
.setEncryptionLevel(encryptionLevel);
return configuration;
}
#Override
#Bean
public SessionFactory getSessionFactory() {
return new SessionFactory(getConfiguration(), "io.swagger");
}
}
Pom
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>io.swagger</groupId>
<artifactId>swagger-springboot-server</artifactId>
<packaging>jar</packaging>
<name>swagger-springboot-server</name>
<version>1.0.0</version>
<properties>
<springfox-version>2.4.0</springfox-version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.0.RC1</version>
</parent>
<build>
<sourceDirectory>src/main/java</sourceDirectory>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--SpringFox dependencies -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${springfox-version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${springfox-version}</version>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.10</version>
<scope>provided</scope>
</dependency>
<!-- Neo4J -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-neo4j</artifactId>
<version>4.1.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-ogm-bolt-driver</artifactId>
<version>2.0.3</version>
</dependency>
</dependencies><repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/libs-milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>
Updated Spring boot from 1.4.0.RC1 to 1.4.0.RELEASE and now it does not occur.
Related
In the OpenApi30Config configuration file I have:
import org.springframework.context.annotation.Configuration;
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.enums.SecuritySchemeType;
import io.swagger.v3.oas.annotations.info.Info;
import io.swagger.v3.oas.annotations.security.SecurityScheme;
#Configuration
#OpenAPIDefinition(info = #Info(title = "Users API", version = "2.0", description = "Users Information"))
#SecurityScheme(name = "bearerAuth", type = SecuritySchemeType.HTTP, bearerFormat = "JWT", scheme = "bearer")
public class OpenApi30Config {
}
In the WebSecurityConfig
#EnableWebSecurity
#Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private Environment environment;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.addFilterAfter(new JWTAuthorizationFilter(environment), UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/auth").permitAll()
.antMatchers("/open-api/**").permitAll()
.anyRequest().authenticated();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("someUser")
.password(passwordEncoder().encode("somePassword"))
.authorities("ADMIN");
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Controllers:
AuthController
#RestController
public class AuthController implements EnvironmentAware {
private Environment environment;
#PostMapping("auth")
public User login(#RequestParam("user") String name, #RequestParam("password") String pwd) {
String token = getJWTToken(name);
User user = new User();
user.setName(name);
user.setToken(token);
return user;
}
private String getJWTToken(String username) {
List<GrantedAuthority> grantedAuthorities = AuthorityUtils
.commaSeparatedStringToAuthorityList("ROLE_USER");
String token = Jwts
.builder()
.setId("softtekJWT")
.setSubject(username)
.claim("authorities",
grantedAuthorities.stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.toList()))
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + Long.parseLong(environment.getProperty("millisecond.expiration"))))
.signWith(SignatureAlgorithm.HS512,
environment.getProperty("secret.key").getBytes()).compact();
return "Bearer " + token;
}
#Override
public void setEnvironment(final Environment environment) {
this.environment = environment;
}
}
The UserController:
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
#RestController
#RequestMapping("/api/users")
public class UserController {
#Autowired
private UserServiceIface userService;
#Autowired UserPasswordValidator userPasswordValidator;
#Operation(summary = "list Users", security = #SecurityRequirement(name = "bearerAuth"))
#GetMapping
public ResponseEntity<?> list(){
return ResponseEntity.ok().body(userService.findAll());
}
#Operation(summary = "view User", security = #SecurityRequirement(name = "bearerAuth"))
#GetMapping("/{id}")
public ResponseEntity<?> view(#PathVariable Long id) {
Optional<User> optionalStoredUser = userService.findById(id);
if (!optionalStoredUser.isPresent()) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok().body(optionalStoredUser.get());
}
#Operation(summary = "create User", security = #SecurityRequirement(name = "bearerAuth"))
#PostMapping
public ResponseEntity<?> create(#RequestHeader(value="Authorization") String token,
#Valid #RequestBody User user, BindingResult result) {
userPasswordValidator.validate(user, result);
throwExceptionIfErrors(result);
Optional<User> optionalStoredUser = userService.findByEmail(user.getEmail());
if (optionalStoredUser.isPresent()) {
throw new ExistingMailException(user.getEmail());
}
user.setIsactive(true);
user.setToken(token);
User createdUser = userService.save(user);
return ResponseEntity.status(HttpStatus.CREATED).body(createdUser);
}
#Operation(summary = "edit User", security = #SecurityRequirement(name = "bearerAuth"))
#PutMapping("/{id}")
public ResponseEntity<?> edit(#RequestHeader(value="Authorization") String token,
#Valid #RequestBody User user, BindingResult result, #PathVariable Long id) {
userPasswordValidator.validate(user, result);
throwExceptionIfErrors(result);
if (user.getEmail() != null && !user.getEmail().isEmpty()) {
boolean emailUsed = userService.findByEmailAndIdNot(user.getEmail(), id).size() > 0;
if (emailUsed) {
throw new ExistingMailException(user.getEmail());
}
}
Optional<User> optionalStoredUser = userService.findById(id);
if (!optionalStoredUser.isPresent()) {
return ResponseEntity.notFound().build();
}
User editedUser = optionalStoredUser.get();
editedUser.setName(user.getName());
editedUser.setEmail(user.getEmail());
editedUser.setPassword(user.getPassword());
editedUser.setPhones(user.getPhones());
editedUser.setModified(new Date());
editedUser.setIsactive(user.isIsactive());
editedUser.setToken(token);
try {
User updatedUser = userService.save(editedUser);
return ResponseEntity.status(HttpStatus.CREATED).body(updatedUser);
} catch (Exception exp) {
throw new DefaultException(exp.getLocalizedMessage());
}
}
#Operation(summary = "delete User", security = #SecurityRequirement(name = "bearerAuth"))
#DeleteMapping("/{id}")
public ResponseEntity<?> delete(#PathVariable Long id) {
userService.deleteById(id);
return ResponseEntity.noContent().build();
}
private void throwExceptionIfErrors(BindingResult result) {
if (result.hasErrors()) {
FieldError passwordFieldError = result.getAllErrors()
.stream().map(e -> (FieldError) e)
.filter(f -> f.getField().equals("password"))
.findFirst().orElse(null);
if (passwordFieldError != null) {
throw new PatternPasswordException(passwordFieldError.getDefaultMessage());
}
FieldError emailFieldError = result.getAllErrors()
.stream().map(e -> (FieldError) e)
.filter(f -> f.getField().equals("email"))
.findFirst().orElse(null);
if (emailFieldError != null) {
throw new PatternEmailException(emailFieldError.getDefaultMessage());
}
}
}
}
In my pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.5.13</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<!-- <version>5.8.2</version> -->
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<!-- <version>5.8.2</version> -->
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<!-- <version>5.8.2</version> -->
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<!-- <version>4.2.0</version> -->
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<!-- <version>4.2.0</version> -->
</dependency>
</dependencies>
Now UserControllerTest in the test:
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
#WebMvcTest(UserController.class)
class UserControllerTest {
#Autowired
private MockMvc mvc;
#MockBean
private UserServiceIface userService;
#MockBean
UserPasswordValidator userPasswordValidator;
private List<User> users;
#SuppressWarnings("serial")
#BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
users = new ArrayList<User>() {
{
User user = new User();
user.setId(1L);
user.setName("First");
user.setEmail("i#country.com");
user.setPassword("abc1");
add(user);
user = new User();
user.setId(2L);
user.setName("Second");
user.setEmail("you#country.com");
user.setPassword("abc2");
add(user);
user = new User();
user.setId(3L);
user.setName("Third");
user.setEmail("he#country.com");
user.setPassword("abc3");
add(user);
}
};
}
#Test
void testList() throws Exception {
// given
when(userService.findAll()).thenReturn(users);
// then
mvc.perform(MockMvcRequestBuilders.get("/api/users").contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$.[0].name").value("First"))
;
}
Running I get:
MockHttpServletResponse:
Status = 403
Error message = Access Denied
Headers = [X-Content-Type-Options:"nosniff", X-XSS-Protection:"1; mode=block", Cache-Control:"no-cache, no-store, max-age=0, must-revalidate", Pragma:"no-cache", Expires:"0", X-Frame-Options:"DENY"]
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
EDIT 1:
I changed my test class!
package org.bz.ms.app.usuarios.controllers;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.when;
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import org.junit.jupiter.api.*;
import org.mockito.MockitoAnnotations;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.security.test.context.support.WithMockUser;
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;
#WebMvcTest(UserController.class)
class UserControllerTest {
#Autowired
private WebApplicationContext context;
#Autowired
private MockMvc mvc;
#MockBean
private UserServiceIface userService;
#MockBean
UserPasswordValidator userPasswordValidator;
private List<User> users;
#SuppressWarnings("serial")
#BeforeEach
void setUp() {
this.mvc = MockMvcBuilders.webAppContextSetup(this.context).apply(springSecurity()).build();
// userDao = mock(UserDao.class); // Manually
MockitoAnnotations.openMocks(this);
users = new ArrayList<User>() {
{
User user = new User();
user.setId(1L);
user.setName("First");
user.setEmail("i#country.com");
user.setPassword("abc1");
add(user);
user = new User();
user.setId(2L);
user.setName("Second");
user.setEmail("you#country.com");
user.setPassword("abc2");
add(user);
user = new User();
user.setId(3L);
user.setName("Third");
user.setEmail("he#country.com");
user.setPassword("abc3");
add(user);
}
};
}
#Test
#WithMockUser
void testList() throws Exception {
// given
when(userService.findAll()).thenReturn(users);
// then
mvc.perform(MockMvcRequestBuilders.get("/api/users").contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
// .andExpect(content().contentType(MediaType.APPLICATION_JSON))
// .andExpect(jsonPath("$.[0].name").value("First"))
;
}
}
I have doubts about this injection, but I think that I have: WebSecurityConfig and OpenApi30Config.
#Autowired
private WebApplicationContext context;
and this on setUp method:
this.mvc = MockMvcBuilders.webAppContextSetup(this.context).apply(springSecurity()).build();
How to test my controller including the security?
It is because you configure the API can only be accessed by an authenticated user but now you are sending a request as an anonymous user and hence it return 403 access denied error.
The most simplest way is to add #WithMockUser to the test method. It will emulate the test are running with an authenticated user . It also allows to configure what the authorities or role do this user has :
#Test
#WithMockUser
void testList() throws Exception {
// given
when(userService.findAll()).thenReturn(users);
// then
mvc.perform(MockMvcRequestBuilders.get("/api/users").contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$.[0].name").value("First"))
;
}
Please note that #WithMockUser requires to add the spring-security-test dependency :
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
I have an entity class that refers to itself as the parent class.
#NodeEntity
public class Config {
#Id
#GeneratedValue
private Long id;
#Relationship(type = "OVERRIDES", direction="INCOMING")
private Config parentConfig;
#Properties(allowCast=true, prefix="properties", delimiter=".")
private Map<String, String> properties;
....
}
When I run the built-in findById() method in my java code, the "parentConfig" is always the same as the child object. I never see the other end of the relationship. What am I doing wrong?
this works as expected but depends on how you want to model your config.
Assuming you want to express "Child overwrites parent config", which I would model as
CREATE (c:Config {name: 'Parent'}) <- [:OVERRIDES] - (c2:Config {name: 'Child'})
This gives you:
Then you must model the association as outgoing from the child object like this:
import org.neo4j.ogm.annotation.GeneratedValue;
import org.neo4j.ogm.annotation.Id;
import org.neo4j.ogm.annotation.Relationship;
public class Config {
#Id
#GeneratedValue
private Long id;
private String name;
#Relationship(type = "OVERRIDES", direction = Relationship.OUTGOING)
private Config parent;
public Long getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Config getParent() {
return parent;
}
#Override public String toString() {
return "Config{" +
"name='" + name + '\'' +
'}';
}
}
Turning this into a full example:
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.neo4j.repository.Neo4jRepository;
import org.springframework.stereotype.Component;
interface ConfigRepo extends Neo4jRepository<Config, Long> {
Optional<Config> findOneByName(String name);
}
#Component
class Example implements CommandLineRunner {
#Autowired
private ConfigRepo configRepo;
#Override
public void run(String... args) throws Exception {
configRepo.findOneByName("Parent").ifPresent(c -> {
System.out.println("Config " + c + " has parent " + c.getParent());
});
configRepo.findOneByName("Child").ifPresent(c -> {
System.out.println("Config " + c + " has parent " + c.getParent());
});
}
}
#SpringBootApplication
public class SorecassocApplication {
public static void main(String[] args) {
SpringApplication.run(SorecassocApplication.class, args);
}
}
Will return
2018-10-10 09:20:21.960 INFO 3832 --- [ main] o.n.o.drivers.bolt.request.BoltRequest : Request: MATCH (n:`Config`) WHERE n.`name` = { `name_0` } WITH n RETURN n,[ [ (n)-[r_o1:`OVERRIDES`]->(c1:`Config`) | [ r_o1, c1 ] ] ], ID(n) with params {name_0=Parent}
Config Config{name='Parent'} has parent null
2018-10-10 09:20:21.984 INFO 3832 --- [ main] o.n.o.drivers.bolt.request.BoltRequest : Request: MATCH (n:`Config`) WHERE n.`name` = { `name_0` } WITH n RETURN n,[ [ (n)-[r_o1:`OVERRIDES`]->(c1:`Config`) | [ r_o1, c1 ] ] ], ID(n) with params {name_0=Child}
Config Config{name='Child'} has parent Config{name='Parent'}
If you model the relationship as INCOMING from the perspective of the child, than you have to inverse the relation ship in the nodes. Otherwise the output is as:
2018-10-10 09:22:54.929 INFO 3837 --- [ main] o.n.o.drivers.bolt.request.BoltRequest : Request: MATCH (n:`Config`) WHERE n.`name` = { `name_0` } WITH n RETURN n,[ [ (n)<-[r_o1:`OVERRIDES`]-(c1:`Config`) | [ r_o1, c1 ] ] ], ID(n) with params {name_0=Parent}
Config Config{name='Parent'} has parent Config{name='Child'}
2018-10-10 09:22:54.951 INFO 3837 --- [ main] o.n.o.drivers.bolt.request.BoltRequest : Request: MATCH (n:`Config`) WHERE n.`name` = { `name_0` } WITH n RETURN n,[ [ (n)<-[r_o1:`OVERRIDES`]-(c1:`Config`) | [ r_o1, c1 ] ] ], ID(n) with params {name_0=Child}
Config Config{name='Child'} has parent null
Here are my dependencies to turn the examples into a running application:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>sorecassoc</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>sorecassoc</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-neo4j</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Please let me know if this solves your problem.
I am using spring-data-cassandra to connect to our cassandra db which is in aws cluster. Using CassandraTemplate I am able to connect locally and get the data but while deploying application in docker container I am getting below error
UnsatisfiedDependencyException: Error creating bean with name 'cassandraTemplate' defined in class path resource
[org/springframework/boot/autoconfigure/data/cassandra/CassandraDataAutoConfiguration.class]:
Unsatisfied dependency expressed through method 'cassandraTemplate' parameter 0;
nested exception is org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'session' defined in class path resource [utils/CassandraConfig.class]:
Invocation of init method failed; nested exception is com.datastax.driver.core.exceptions.NoHostAvailableException: All host(s) tried for query failed
(tried: /IP:9042 (com.datastax.driver.core.TransportException: [/IP:9042] Error writing))
Here is my pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>mus-cassadra-read</artifactId>
<groupId>com.rogs.mus</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.rogs.mus</groupId>
<artifactId>external-endpoints</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
<version>1.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>com.rogs.mus</groupId>
<artifactId>model-cassadra-read</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
<version>1.3.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
<version>1.2.0.RELEASE</version>
</dependency>
<!-- cassandra dependency -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-cassandra</artifactId>
<version>1.4.5.RELEASE</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.6.Final</version>
</dependency>
</dependencies>
</project>
CassandraConfig.java
#Configuration
#EnableCassandraRepositories(basePackages = { "org.spring.cassandra.example.repo" })
public class CassandraConfig {
private final Logger LOGGER = LoggerFactory.getLogger(this.getClass());
#Value("${cassandra.hostnames}")
private String hostnames ;
#Value("${cassandra.username}")
private String username ;
#Value("${cassandra.password}")
private String password ;
#Value("${cassandra.keyspace}")
private String keyspace ;
#Value("${cassandra.isSsl}")
private boolean isSsl ;
#Value("${cassandra.port}")
private int port;
#Bean
Public CassandraClusterFactoryBean cluster() {
CassandraClusterFactoryBean cluster = new CassandraClusterFactoryBean();
PlainTextAuthProvider authProvider = new PlainTextAuthProvider(username, password);
cluster.setContactPoints(hostnames);
cluster.setPort(port);
cluster.setUsername(username);
cluster.setPassword(password);
return cluster;
}
#Bean
Public CassandraMappingContext mappingContext() {
return new BasicCassandraMappingContext();
}
#Bean
Public CassandraConverter converter() {
return new MappingCassandraConverter(mappingContext());
}
#Bean
Public CassandraSessionFactoryBean session() throws Exception {
CassandraSessionFactoryBean session = new CassandraSessionFactoryBean();
session.setCluster(cluster().getObject());
session.setKeyspaceName(keyspace);
session.setConverter(converter());
session.setSchemaAction(SchemaAction.NONE);
return session;
}
#Bean
Public CassandraOperations cassandraTemplate() throws Exception {
return new CassandraTemplate(session().getObject());
}
}
Please let me know if I am doing anything wrong here.
I have a JPA web application with some integration tests against the JPA repositories. There are no integration tests against the Neo4J repositories yet.
Now, I have added some Neo4J functionality to this existing JPA web application.
I'm this now using Neo4J repositories, alongside JPA repositories. My entities and repositories are named differently and are sitting in different packages.
My tests all extend the following class:
#WebAppConfiguration
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { ApplicationConfiguration.class, WebSecurityTestConfiguration.class, WebConfiguration.class })
#Transactional
public abstract class AbstractControllerTest {
...
}
The tests run fine when the application configuration does not have any
Neo4J configuration:
#Configuration
#ComponentScan(basePackages = { "it.robot.rest.config" })
#Import({ DatabaseConfiguration.class, Log4jWeb.class })
public class ApplicationConfiguration {
}
But they error on an exception when adding the Neo4J configuration:
#Configuration
#ComponentScan(basePackages = { "it.robot.rest.config" })
#Import({ DatabaseConfiguration.class, Neo4JRepositoryConfiguration.class, Log4jWeb.class })
public class ApplicationConfiguration {
}
The exception is:
javax.persistence.TransactionRequiredException: no transaction is in progress
Here is the Neo4J configuration (I tried both Neo4jConfiguration and CrossStoreNeo4jConfiguration classes and I get the same exception):
#Configuration
#EnableNeo4jRepositories(basePackages = { "it.robot.data.neo4j.repository" } )
#EnableTransactionManagement
#ComponentScan(basePackages = { "it.robot.data.neo4j.service" })
public class Neo4JRepositoryConfiguration extends Neo4jConfiguration {
public static final String URL = "http://localhost:7474/db/data/";
public static final String LOGIN = "neo4j";
public static final String PASSWORD = "mypassword";
Neo4JRepositoryConfiguration() {
setBasePackage("it.robot.data.neo4j.domain");
}
#Bean
GraphDatabaseService graphDatabaseService() {
return new SpringCypherRestGraphDatabase(URL, LOGIN, PASSWORD);
}
}
Here is the JPA configuration is:
#Configuration
#Import({ JpaService.class, Log4j.class })
#EnableTransactionManagement
#ComponentScan(basePackages = { "it.robot.data.config" })
#EnableJpaRepositories(basePackages = { "it.robot.data.jpa" }, repositoryFactoryBeanClass = it.robot.data.jpa.repository.GenericRepositoryFactoryBean.class)
public class DatabaseConfiguration {
...
}
It looks like the Neo4jConfiguration class transaction manager has the same name ("transactionManager") as the JPA transaction manager, and overrides it.
I would content myself with Neo4J using the JPA transaction manager provided by Spring but I wonder if that is possible.
Some additional information...
I'm using spring-data-neo4j and spring-data-neo4j-rest version 3.3.2.RELEASE
I'm using a server Neo4J database and not an embedded one and of course the Neo4J server is started.
I disabled the authentication on the database since it was standing in my way and my curl request didn't seem to update the password:
curl -H "Accept:application/json"
-H "Content-Type: application/json"
"http://localhost:7474/user/neo4j/password"
-X POST -d "{ \"password\" : \"myownpassword\" }"
The only user I know of doesn't seem to be too vocal:
stephane#stephane-ThinkPad-X301:~> curl -H "Accept:application/json" -H "Content-Type: application/json" "http://localhost:7474/user/neo4j"
stephane#stephane-ThinkPad-X301:~>
stephane#stephane-ThinkPad-X301:~>
I have not created any "schema/structure" in the graph and am not sure if I should do.
The Neo4J entities:
#NodeEntity
#SequenceGenerator(name = "id_generator", sequenceName = "sq_id_part")
public class Neo4JPart extends BaseEntity {
#Column(nullable = false)
private String name;
#Column(nullable = false, unique = true)
private String serialNumber;
private Integer weight;
#ManyToOne
#JoinColumn(name = "manufacturer_id", nullable = false)
private Neo4JManufacturer manufacturer;
#Fetch
#RelatedTo(type = "part", direction = Direction.BOTH)
public Set<Neo4JPart> parts;
public Neo4JPart() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSerialNumber() {
return serialNumber;
}
public void setSerialNumber(String serialNumber) {
this.serialNumber = serialNumber;
}
public Integer getWeight() {
return weight;
}
public void setWeight(Integer weight) {
this.weight = weight;
}
public Neo4JManufacturer getManufacturer() {
return manufacturer;
}
public void setManufacturer(Neo4JManufacturer manufacturer) {
this.manufacturer = manufacturer;
}
public Set<Neo4JPart> getParts() {
return parts;
}
public void setParts(Set<Neo4JPart> parts) {
this.parts = parts;
}
public String toString() {
String results = name + "'s compatible parts include\n";
if (parts != null) {
for (Neo4JPart part : parts) {
results += "\t- " + part.name + "\n";
}
}
return results;
}
}
#MappedSuperclass
public class BaseEntity {
#GraphId
#GeneratedValue(strategy = GenerationType.AUTO, generator = "id_generator")
#Column(name = "id")
private Long id;
#Version
#Column(nullable = false)
private int version;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public int getVersion() {
return this.version;
}
public void setVersion(int version) {
this.version = version;
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (this.id == null || obj == null || !(this.getClass().equals(obj.getClass()))) {
return false;
}
BaseEntity that = (BaseEntity) obj;
return this.id.equals(that.getId());
}
#Override
public int hashCode() {
return id == null ? 0 : id.hashCode();
}
}
And the Neo4J repositories:
public interface Neo4JPartRepository extends GraphRepository<Neo4JPart> {
public Neo4JPart findByName(String name);
public Neo4JPart findBySerialNumber(String serialNumber);
public Page<Neo4JPart> findByManufacturer(#Param("manufacturer") Neo4JManufacturer manufacturer, Pageable page);
public List<Neo4JPart> findByManufacturer(#Param("manufacturer") Neo4JManufacturer manufacturer);
public Page<Neo4JPart> findByPartsName(String name, Pageable page);
}
public interface Neo4JManufacturerRepository extends GraphRepository<Neo4JManufacturer> {
Neo4JManufacturer findByName(String name);
}
The Maven dependencies are:
<org.springframework.version>4.1.2.RELEASE</org.springframework.version>
<hibernate.version>4.3.6.Final</hibernate.version>
<dependencies>
<dependency>
<groupId>com.thalasoft</groupId>
<artifactId>toolbox</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.2.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.34</version>
</dependency>
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0.3</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.3.172</version>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>2.3.2</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.1-api</artifactId>
<version>1.0.0.Final</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>1.6.2.RELEASE</version>
</dependency>
<dependency>
<groupId>com.mysema.querydsl</groupId>
<artifactId>querydsl-core</artifactId>
<version>3.5.0</version>
</dependency>
<dependency>
<groupId>com.mysema.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<version>3.5.0</version>
</dependency>
<dependency>
<groupId>com.mysema.querydsl</groupId>
<artifactId>querydsl-jpa</artifactId>
<version>3.5.0</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
<version>1.10.2.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.6.4</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.6.4</version>
</dependency>
<dependency>
<groupId>org.lazyluke</groupId>
<artifactId>log4jdbc-remix</artifactId>
<version>0.2.7</version>
</dependency>
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>org.jadira.usertype</groupId>
<artifactId>usertype.jodatime</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>javax.transaction</groupId>
<artifactId>jta</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>org.jasypt</groupId>
<artifactId>jasypt</artifactId>
<version>1.7</version>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>1.6.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${org.springframework.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-oxm</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-neo4j</artifactId>
<version>3.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-neo4j-rest</artifactId>
<version>3.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-neo4j-cross-store</artifactId>
<version>3.3.2.RELEASE</version>
</dependency>
</dependencies>
I tried to upgrade to the 3.4.0 version of the newly available release on search.maven.org but the build now gives the following exception:
AnnotationFormatError: Invalid default: public abstract java.lang.Class org.springframework.data.neo4j.config.EnableNeo4jRepositories.repositoryBaseClass()
I could see nothing about that repositoryBaseClass in the reference documentation http://docs.spring.io/spring-data/neo4j/docs/3.4.0.RELEASE/reference/pdf/spring-data-neo4j-reference.pdf
The source code Javadoc only says:
Configure the repository base class to be used to create repository proxies for this particular configuration.
And that left me scratching my head wondering what is a repository proxy and if one is required in my case.
I could solve the issue with the solution provided at Implementing Spring ChainedTransactionManager according to the "best efforts 1PC" pattern with a chained transaction manager, following a tip by Simon at How do I properly set up cross-store persistence using Spring Data JPA + Neo4j?
I just had to change my Neo4j configuration. I didn't even have to touch anything in the other JPA transaction manager.
Here is my Neo4j configuration:
#EnableNeo4jRepositories(basePackages = { "it.robot.data.neo4j.repository" })
#EnableTransactionManagement
#ComponentScan(basePackages = { "it.robot.data.neo4j.service" })
public class Neo4JRepositoryConfiguration extends Neo4jConfiguration {
private static Logger logger = LoggerFactory.getLogger(Neo4JRepositoryConfiguration.class);
public static final String URL = "http://localhost:7474/db/data/";
public static final String LOGIN = "neo4j";
public static final String PASSWORD = "xxxxx";
Neo4JRepositoryConfiguration() {
setBasePackage("it.robot.data.neo4j.domain");
}
#Bean
GraphDatabaseService graphDatabaseService() {
return new SpringCypherRestGraphDatabase(URL, LOGIN, PASSWORD);
}
#Autowired
LocalContainerEntityManagerFactoryBean entityManagerFactory;
#Override
public PlatformTransactionManager neo4jTransactionManager(
GraphDatabaseService graphDatabaseService) {
return new ChainedTransactionManager(
new JpaTransactionManager(entityManagerFactory.getObject()),
new JtaTransactionManagerFactoryBean(graphDatabaseService).getObject());
}
}
I'm having a hard time getting Swagger UI to work with my dropwizard application. The annotated dropwizard app seems to generate the correct json documents but swagger UI is unabel to navigate it properly.
Main API page, generated at http://localhost:8080/v1/TimeService/api-docs:
{"apiVersion":"0.0","swaggerVersion":"1.2","apis":[{"path":"/times"}]}
Swagger displays that page correctly.
Detail information for the time resource generated at http://localhost:8080/v1/TimeService/api-docs/times:
{"apiVersion":"0.0","swaggerVersion":"1.2","basePath":"/v1/TimeService/*","resourcePath":"/times","produces":["application/json"],"apis":[{"path":"/times/","operations":[{"method":"GET","summary":"Retrieves time for given","notes":"timezone parameter is optional, defaults to GMT","type":"array","items":{"$ref":"Time"},"nickname":"getTime","parameters":[{"name":"timezone","description":"timezone","required":false,"items":{"type":"string"},"paramType":"query","allowMultiple":false}]}]}],"models":{"Time":{"id":"Time","properties":{"time":{"type":"string"}}}}}
Which looks good to me. But whenever, in the swagger UI, I click on any "times" hyperlink to display the details, nothing happens. It looks like swagger ui cannot resolve the link correctly.
My annotated resource, TimeResource.java:
package com.cgi.saas.bluprint;
import com.google.common.base.Optional;
import com.wordnik.swagger.annotations.Api;
import com.wordnik.swagger.annotations.ApiOperation;
import com.wordnik.swagger.annotations.ApiParam;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
#Path("/times")
#Produces(MediaType.APPLICATION_JSON)
#Api("/times")
public class TimeResource {
private final String defaultTimezone;
public TimeResource(String defaultTimezone) {
this.defaultTimezone = defaultTimezone;
}
#GET
#ApiOperation(value = "Retrieves time for given",
notes = "timezone parameter is optional, defaults to GMT",
response = Time.class,
responseContainer = "List")
#Path("/")
public Time getTime(#ApiParam(value="timezone") #QueryParam("timezone") Optional<String> timezone) {
DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
TimeZone timeZone = TimeZone.getTimeZone(timezone.or(defaultTimezone));
formatter.setTimeZone(timeZone);
String formatted = formatter.format(new Date());
return new Time(formatted);
}
}
The main service class, TimeService.java:
package com.cgi.saas.bluprint;
import java.util.EnumSet;
import javax.servlet.DispatcherType;
import javax.servlet.FilterRegistration.Dynamic;
import org.eclipse.jetty.servlets.CrossOriginFilter;
import com.wordnik.swagger.config.ConfigFactory;
import com.wordnik.swagger.config.ScannerFactory;
import com.wordnik.swagger.config.SwaggerConfig;
import com.wordnik.swagger.jaxrs.config.DefaultJaxrsScanner;
import com.wordnik.swagger.jaxrs.listing.ApiDeclarationProvider;
import com.wordnik.swagger.jaxrs.listing.ApiListingResourceJSON;
import com.wordnik.swagger.jaxrs.listing.ResourceListingProvider;
import com.wordnik.swagger.jaxrs.reader.DefaultJaxrsApiReader;
import com.wordnik.swagger.reader.ClassReaders;
import io.dropwizard.Application;
import io.dropwizard.assets.AssetsBundle;
import io.dropwizard.server.DefaultServerFactory;
import io.dropwizard.setup.Bootstrap;
import io.dropwizard.setup.Environment;
public class TimeService extends Application<TimezoneConfiguration> {
private void configureCors(Environment environment) {
final Dynamic filter = environment.servlets().addFilter("CORS",
CrossOriginFilter.class);
filter.addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), true, "/*");
filter.setInitParameter(CrossOriginFilter.ALLOWED_METHODS_PARAM, "GET,PUT,POST,DELETE,OPTIONS");
filter.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM, "*");
filter.setInitParameter(CrossOriginFilter.ACCESS_CONTROL_ALLOW_ORIGIN_HEADER, "*");
filter.setInitParameter("allowedHeaders",
"Content-Type,Authorization,X-Requested-With,Content-Length,Accept,Origin");
filter.setInitParameter("allowCredentials", "true");
}
public static void main(String[] args) throws Exception {
new TimeService().run(args);
}
public void initialize(Bootstrap<TimezoneConfiguration> timezoneConfigurationBootstrap) {
timezoneConfigurationBootstrap.addBundle(new AssetsBundle("/assets/", "/", "index.html"));
timezoneConfigurationBootstrap.addBundle(new AssetsBundle("/assets/swagger", "/swagger", "index.html", "swagger"));
}
#Override
public void run(TimezoneConfiguration appConfig, Environment environment) throws Exception {
DefaultServerFactory sf = (DefaultServerFactory) appConfig.getServerFactory();
String rootPath = "/v1/" + appConfig.getDeployName() + "/*";
sf.setJerseyRootPath(rootPath);
configureCors(environment);
String defaultTimezone = appConfig.getDefaultTimezone();
TimeResource timeResource = new TimeResource(defaultTimezone);
environment.jersey().register(timeResource);
// Swagger Resource
environment.jersey().register(new ApiListingResourceJSON());
// Swagger providers
environment.jersey().register(new ApiDeclarationProvider());
environment.jersey().register(new ResourceListingProvider());
// Swagger Scanner, which finds all the resources for #Api Annotations
ScannerFactory.setScanner(new DefaultJaxrsScanner());
// Add the reader, which scans the resources and extracts the resource information
ClassReaders.setReader(new DefaultJaxrsApiReader());
// Set the swaggeonfigurationBootstrap
final SwaggerConfig swConfig = ConfigFactory.config();
swConfig.setBasePath(rootPath);
}
}
My pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.cgi.saas.bluprint</groupId>
<artifactId>Application2</artifactId>
<version>1.0</version>
<repositories>
<repository>
<id>sonatype-nexus-snapshots</id>
<name>Sonatype Nexus Snapshots</name>
<url>http://oss.sonatype.org/content/repositories/snapshots</url>
</repository>
</repositories>
<properties>
<dropwizard.version>0.9.0-SNAPSHOT</dropwizard.version>
</properties>
<dependencies>
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-core</artifactId>
<version>${dropwizard.version}</version>
</dependency>
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-assets</artifactId>
<version>${dropwizard.version}</version>
</dependency>
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-jersey</artifactId>
<version>${dropwizard.version}</version>
</dependency>
<dependency>
<groupId>com.wordnik</groupId>
<artifactId>swagger-jaxrs_2.10</artifactId>
<version>1.3.10</version>
<exclusions>
<exclusion>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-scala_2.10</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.wordnik</groupId>
<artifactId>swagger-annotations</artifactId>
<version>1.3.10</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.2</version>
<configuration>
<createDependencyReducedPom>true</createDependencyReducedPom>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.cgi.saas.bluprint.TimeService</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Rewrote it as follows:
Cloned the latest and greatest version of swagger-ui at https://github.com/swagger-api/swagger-ui.git
Copied dist under src/main/resources/swagger/assets
Updated pom.xml:
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-core</artifactId>
<version>${dropwizard.version}</version>
</dependency>
<dependency>
<groupId>io.federecio</groupId>
<artifactId>dropwizard-swagger</artifactId>
<version>0.6</version>
</dependency>
Updated TimeService.java:
import java.util.EnumSet;
import javax.servlet.DispatcherType;
import javax.servlet.FilterRegistration.Dynamic;
import org.eclipse.jetty.servlets.CrossOriginFilter;
import io.dropwizard.Application;
import io.dropwizard.server.DefaultServerFactory;
import io.dropwizard.setup.Bootstrap;
import io.dropwizard.setup.Environment;
import io.federecio.dropwizard.swagger.SwaggerDropwizard;
public class TimeService extends Application<TimezoneConfiguration> {
private final SwaggerDropwizard swaggerDropwizard = new SwaggerDropwizard();
private void configureCors(Environment environment) {
final Dynamic filter = environment.servlets().addFilter("CORS",
CrossOriginFilter.class);
filter.addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), true, "/*");
filter.setInitParameter(CrossOriginFilter.ALLOWED_METHODS_PARAM, "GET,PUT,POST,DELETE,OPTIONS");
filter.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM, "*");
filter.setInitParameter(CrossOriginFilter.ACCESS_CONTROL_ALLOW_ORIGIN_HEADER, "*");
filter.setInitParameter("allowedHeaders",
"Content-Type,Authorization,X-Requested-With,Content-Length,Accept,Origin");
filter.setInitParameter("allowCredentials", "true");
}
public static void main(String[] args) throws Exception {
new TimeService().run(args);
}
public void initialize(Bootstrap<TimezoneConfiguration> timezoneConfigurationBootstrap) {
swaggerDropwizard.onInitialize(timezoneConfigurationBootstrap);
}
#Override
public void run(TimezoneConfiguration appConfig, Environment environment) throws Exception {
DefaultServerFactory sf = (DefaultServerFactory) appConfig.getServerFactory();
String rootPath = "/v1/" + appConfig.getDeployName() + "/*";
sf.setJerseyRootPath(rootPath);
configureCors(environment);
String defaultTimezone = appConfig.getDefaultTimezone();
TimeResource timeResource = new TimeResource(defaultTimezone);
environment.jersey().register(timeResource);
swaggerDropwizard.onRun(appConfig, environment, "localhost");
}
}
The swagger ui is available at http://localhost:8080/v1/TimeService/swagger