How to extract path variable from responseentity in Junit testing - spring-data

Searched but unfortunately I do not get similar questions. I've pasted my involved codes. It uses Spring DATA framework.
Entity EscalationPolicy with ID automatically generated
controller to hand POST request to create an new policy
update JUnit Test
What I'm trying to do in the test is that first create one new EscalationPolicy with the object set by initTest(). Then fetch and update it. However the ID is unknown and I suppose I need to extract it from the return URI. I don't know how to do it after Mockmvc perform and appreciate any help. Thanks!
#Entity
#Table(name = "T_ESCALATIONPOLICY")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class EscalationPolicy implements Serializable {
#Id
#GeneratedValue(generator = "uuid")
#GenericGenerator(name = "uuid", strategy = "uuid")
private String id;
#Column(name = "policy_name")
private String policy_name;
...
}
#RestController
#RequestMapping("/api")
public class EscalationPolicyResource {
...
/**
* POST /escalationPolicys -> Create a new escalationPolicy.
*/
#RequestMapping(value = "/escalationPolicys",
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE)
#Timed
public ResponseEntity<Void> create(#RequestBody EscalationPolicy escalationPolicy) throws URISyntaxException {
log.debug("REST request to save EscalationPolicy : {}", escalationPolicy);
if (escalationPolicy.getId() != null) {
return ResponseEntity.badRequest().header("Failure", "A new escalationPolicy cannot already have an ID").build();
}
escalationPolicyRepository.saveAndFlush(escalationPolicy);
return ResponseEntity.created(new URI("/api/escalationPolicys/" + escalationPolicy.getId())).build();
}
...
}
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
#IntegrationTest
public class EscalationPolicyResourceTest {
#Before
public void initTest() {
escalationPolicy = new EscalationPolicy();
escalationPolicy.setPolicy_name("Policy Test");
...
}
#Test
#Transactional
public void updatePolicy() throws Exception {
// Create the EscalationPolicy
restEscalationPolicyMockMvc.perform(post("/api/escalationPolicys")
.contentType(TestUtil.APPLICATION_JSON_UTF8)
.content(TestUtil.convertObjectToJsonBytes(escalationPolicy)))
.andExpect(status().isCreated());
// Get the created policy
EscalationPolicy e = escalationPolicyRepository.findOne(id);
~~need ID here
}
...
}

Though it may not be the most elegant way to deal with it, I think a way to bypass the problem. I save the id in the header map and in the test code to extract it.
#RestController
#RequestMapping("/api")
public class EscalationPolicyResource {
...
/**
* POST /escalationPolicys -> Create a new escalationPolicy.
*/
#RequestMapping(value = "/escalationPolicys",
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE)
#Timed
public ResponseEntity<Void> create(#RequestBody EscalationPolicy escalationPolicy) throws URISyntaxException {
log.debug("REST request to save EscalationPolicy : {}", escalationPolicy);
if (escalationPolicy.getId() != null) {
return ResponseEntity.badRequest().header("Failure", "A new escalationPolicy cannot already have an ID").build();
}
escalationPolicyRepository.saveAndFlush(escalationPolicy);
HttpHeaders headers = new HttpHeaders();
headers.set("policyID", escalationPolicy.getId());
return new ResponseEntity<Void>(headers, HttpStatus.CREATED);
}
...
}
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
#IntegrationTest
public class EscalationPolicyResourceTest {
#Before
public void initTest() {
escalationPolicy = new EscalationPolicy();
escalationPolicy.setPolicy_name("Policy Test");
...
}
#Test
#Transactional
public void updatePolicy() throws Exception {
// Create the EscalationPolicy
ResultActions action =
restEscalationPolicyMockMvc.perform(post("/api/escalationPolicys")
.contentType(TestUtil.APPLICATION_JSON_UTF8)
.content(TestUtil.convertObjectToJsonBytes(escalationPolicy)));
action.andExpect(status().isCreated());
id = (String)action.andReturn().getResponse().getHeaderValue("policyID");
// Get the created policy
EscalationPolicy e = escalationPolicyRepository.findOne(id);
}
...
}

Related

MongoRepository Save method does not insert in database

I have created a SpringBoot project with Jhipster. The database I am using is MongoDB.
In the application-dev.yml I have the following configuration:
data:
mongodb:
uri: mongodb://<user>:<pass>#<ip>:<port>
database: gateway
The user, password, ip Address, and port, in my application-dev are real values.
The DatabaseConfiguration.java is:
#Configuration
#EnableMongoRepositories("es.second.cdti.repository")
#Profile("!" + JHipsterConstants.SPRING_PROFILE_CLOUD)
#Import(value = MongoAutoConfiguration.class)
#EnableMongoAuditing(auditorAwareRef = "springSecurityAuditorAware")
public class DatabaseConfiguration {
private final Logger log = LoggerFactory.getLogger(DatabaseConfiguration.class);
#Bean
public ValidatingMongoEventListener validatingMongoEventListener() {
return new ValidatingMongoEventListener(validator());
}
#Bean
public LocalValidatorFactoryBean validator() {
return new LocalValidatorFactoryBean();
}
#Bean
public MongoCustomConversions customConversions() {
List<Converter<?, ?>> converters = new ArrayList<>();
converters.add(DateToZonedDateTimeConverter.INSTANCE);
converters.add(ZonedDateTimeToDateConverter.INSTANCE);
return new MongoCustomConversions(converters);
}
#Bean
public Mongobee mongobee(MongoClient mongoClient, MongoTemplate mongoTemplate, MongoProperties mongoProperties) {
log.debug("Configuring Mongobee");
Mongobee mongobee = new Mongobee(mongoClient);
mongobee.setDbName(mongoProperties.getMongoClientDatabase());
mongobee.setMongoTemplate(mongoTemplate);
// package to scan for migrations
mongobee.setChangeLogsScanPackage("es.second.cdti.config.dbmigrations");
mongobee.setEnabled(true);
return mongobee;
}}
The CloudDatabaseConfiguration is:
#Configuration
#EnableMongoRepositories("es.second.cdti.repository")
#Profile(JHipsterConstants.SPRING_PROFILE_CLOUD)
public class CloudDatabaseConfiguration extends AbstractCloudConfig {
private final Logger log = LoggerFactory.getLogger(CloudDatabaseConfiguration.class);
#Bean
public MongoDbFactory mongoFactory() {
return connectionFactory().mongoDbFactory();
}
#Bean
public LocalValidatorFactoryBean validator() {
return new LocalValidatorFactoryBean();
}
#Bean
public ValidatingMongoEventListener validatingMongoEventListener() {
return new ValidatingMongoEventListener(validator());
}
#Bean
public MongoCustomConversions customConversions() {
List<Converter<?, ?>> converterList = new ArrayList<>();
converterList.add(DateToZonedDateTimeConverter.INSTANCE);
converterList.add(ZonedDateTimeToDateConverter.INSTANCE);
converterList.add(DurationToLongConverter.INSTANCE);
return new MongoCustomConversions(converterList);
}
#Bean
public Mongobee mongobee(MongoDbFactory mongoDbFactory, MongoTemplate mongoTemplate, Cloud cloud) {
log.debug("Configuring Cloud Mongobee");
List<ServiceInfo> matchingServiceInfos = cloud.getServiceInfos(MongoDbFactory.class);
if (matchingServiceInfos.size() != 1) {
throw new CloudException("No unique service matching MongoDbFactory found. Expected 1, found "
+ matchingServiceInfos.size());
}
MongoServiceInfo info = (MongoServiceInfo) matchingServiceInfos.get(0);
Mongobee mongobee = new Mongobee(info.getUri());
mongobee.setDbName(mongoDbFactory.getDb().getName());
mongobee.setMongoTemplate(mongoTemplate);
// package to scan for migrations
mongobee.setChangeLogsScanPackage("es.second.cdti.config.dbmigrations");
mongobee.setEnabled(true);
return mongobee;
}
}
The cdtiApp.java is:
#SpringBootApplication
#EnableConfigurationProperties({ApplicationProperties.class})
public class CdtiApp implements InitializingBean{
private static final Logger log = LoggerFactory.getLogger(CdtiApp.class);
private final Environment env;
public CdtiApp(Environment env) {
this.env = env;
}
/**
* Initializes cdti.
* <p>
* Spring profiles can be configured with a program argument --spring.profiles.active=your-active-profile
* <p>
* You can find more information on how profiles work with JHipster on https://www.jhipster.tech/profiles/.
*/
#PostConstruct
public void initApplication() {
Collection<String> activeProfiles = Arrays.asList(env.getActiveProfiles());
if (activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT) && activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_PRODUCTION)) {
log.error("You have misconfigured your application! It should not run " +
"with both the 'dev' and 'prod' profiles at the same time.");
}
if (activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT) && activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_CLOUD)) {
log.error("You have misconfigured your application! It should not " +
"run with both the 'dev' and 'cloud' profiles at the same time.");
}
}
/**
* Main method, used to run the application.
*
* #param args the command line arguments.
*/
public static void main(String[] args) {
SpringApplication app = new SpringApplication(CdtiApp.class);
DefaultProfileUtil.addDefaultProfile(app);
Environment env = app.run(args).getEnvironment();
logApplicationStartup(env);
}
private static void logApplicationStartup(Environment env) {
String protocol = "http";
if (env.getProperty("server.ssl.key-store") != null) {
protocol = "https";
}
String serverPort = env.getProperty("server.port");
String contextPath = env.getProperty("server.servlet.context-path");
if (StringUtils.isBlank(contextPath)) {
contextPath = "/";
}
String hostAddress = "localhost";
try {
hostAddress = InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
log.warn("The host name could not be determined, using `localhost` as fallback");
}
log.info("\n----------------------------------------------------------\n\t" +
"Application '{}' is running! Access URLs:\n\t" +
"Local: \t\t{}://localhost:{}{}\n\t" +
"External: \t{}://{}:{}{}\n\t" +
"Profile(s): \t{}\n----------------------------------------------------------",
env.getProperty("spring.application.name"),
protocol,
serverPort,
contextPath,
protocol,
hostAddress,
serverPort,
contextPath,
env.getActiveProfiles());
String configServerStatus = env.getProperty("configserver.status");
if (configServerStatus == null) {
configServerStatus = "Not found or not setup for this application";
}
log.info("\n----------------------------------------------------------\n\t" +
"Config Server: \t{}\n----------------------------------------------------------", configServerStatus);
}
#Override
public void afterPropertiesSet() throws Exception {
// TODO Auto-generated method stub
}
}
The Vehicle entity:
#org.springframework.data.mongodb.core.mapping.Document(collection = "vehicle")
public class Vehicle implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private String id;
#NotNull
#Field("plate")
private String plate;
#NotNull
#Field("registrationDate")
private Instant registrationDate;
#NotNull
#Field("brand")
private String brand;
#NotNull
#Field("model")
private String model;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPlate() {
return plate;
}
public void setPlate(String plate) {
this.plate = plate;
}
public Instant getRegistrationDate() {
return registrationDate;
}
public void setRegistrationDate(Instant registrationDate) {
this.registrationDate = registrationDate;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
}
The VehicleDTO is:
public class VehicleDTO {
private String id;
private String plate;
private Instant registrationDate;
private String brand;
private String model;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPlate() {
return plate;
}
public void setPlate(String plate) {
this.plate = plate;
}
public Instant getRegistrationDate() {
return registrationDate;
}
public void setRegistrationDate(Instant registrationDate) {
this.registrationDate = registrationDate;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
}
The VehicleMapper is:
#Mapper(componentModel = "spring")
public interface VehicleMapper{
Vehicle toEntity(VehicleDTO source);
VehicleDTO toDto(Vehicle target);
}
The VehicleResource is:
#RestController
#RequestMapping("/api")
#CrossOrigin(origins = "*", methods = { RequestMethod.GET, RequestMethod.POST })
public class VehicleResource {
private final Logger log = LoggerFactory.getLogger(VehicleResource.class);
#Value("${jhipster.clientApp.name}")
private String applicationName;
#Autowired
private final VehicleService vehicleService;
public VehicleResource(VehicleService vehicleService) {
this.vehicleService = vehicleService;
}
#PostMapping("/vehicle")
#PreAuthorize("hasAuthority(\"" + AuthoritiesConstants.ADMIN + "\")")
public ResponseEntity<Vehicle> createVehicle(#Valid #RequestBody VehicleDTO vehicleDTO) throws URISyntaxException {
log.debug("REST request to save Vehicle : {}", vehicleDTO);
Vehicle newVehicle = vehicleService.createVehicle(vehicleDTO);
return ResponseEntity.created(new URI("/api/vehicle/" + newVehicle.getPlate()))
.headers(HeaderUtil.createAlert(applicationName, "vehicleManagement.created", newVehicle.getPlate()))
.body(newVehicle);
}
}
The VehicleService interface is:
public interface VehicleService {
Vehicle createVehicle(VehicleDTO vehicleDTO);
}
The VehicleServiceImpl is:
#Service
public class VehicleServiceImpl implements VehicleService{
#Autowired
private final VehicleRepository vehicleRepository;
#Autowired
private final VehicleMapper mapper;
public VehicleServiceImpl(VehicleRepository vehicleRepository, VehicleMapper mapper) {
this.vehicleRepository = vehicleRepository;
this.mapper = mapper;
}
private final Logger log = LoggerFactory.getLogger(VehicleServiceImpl.class);
#Override
public Vehicle createVehicle(VehicleDTO vehicleDTO) {
Vehicle vehicle = vehicleRepository.save(mapper.toEntity(vehicleDTO));
log.debug("Created Information for vehicle: {}", vehicle);
return vehicle;
}
}
The VehicleRepository interface is:
/**
* Spring Data MongoDB repository for the {#link Vehicle} entity.
*/
#Repository
public interface VehicleRepository extends MongoRepository<Vehicle, String> {
}
From the Swagger console I access the Vehicle-Resource:
Swagger console
Click on the button and write in the text box the json with the vehicle data:
enter JSON data
As we can see in the following image, the answer is 201. Initially the vehicle was saved with the identifier "id": "60e740935ed5a10e2c2ed19e".
Send request
I access the database to check that the vehicle has been correctly stored in the vehicle table. To my surprise ... there is no vehicle in the vehicle table:
show database
I can make sure that the data in the database application-dev is OK. I don't have any other databases.
I suspect that transactions with the database are not actually being made. This data is somehow stored in memory because if I do a findAllVehicles from Swagger it does return the vehicle.
I have a eureka server running (jhipster-registry) and two microservices that synchronize with it. The Gateway, which acts as a reverse proxy and the Vehiculos microservice. The Swagger console is the gateway, from where I make the request to insert vehicles. Everything seems to work, but as I say in bbdd does not save anything.

Spring-Boot RestController: Passing Id as String not working

I connected my Spring-Boot-Application to a MongoDB. The application is nothing serious, just for getting into working with spring and MongoDB.
The problem it, that my id is a String and I get an Internal Server Error, when I pass the id of a database entry, in order to get it byId...
This is my domain class:
#Data
#NoArgsConstructor
#AllArgsConstructor
#Builder
#Document(collection = "songinfo")
public class SongInfo {
#Id
private String id;
private int songId;
private String songName;
private String description;
}
The Controller-Method:
#RequiredArgsConstructor
#RestController
#RequestMapping("/songsinfo")
public class SongsInfoController {
private final SongInfoService songInfoService;
#GetMapping(value = "/{id}", headers = "Accept=application/json", produces =
{MediaType.APPLICATION_JSON_VALUE})
public ResponseEntity<SongInfo> getSongInfoById(#PathVariable(value = "id") String id) {
SongInfo songInfo = songInfoService.getSongInfoById(id);
if (songInfo == null)
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
return new ResponseEntity<>(songInfo, HttpStatus.OK);
}
The SongInfoServiceImpl:*
#Override
public SongInfo getSongInfoById(String id) {
return songInfoRepository.findById(id).orElseThrow(NotFoundException::new);
}
This is the SongsInfoRepository:
public interface SongInfoRepository extends MongoRepository<SongInfo, String> {
}
Getting all songinfos from the database is working fine:
But when is pass the id from one of these entries, I get this:
What is wrong here with my implementation?
You're throwing the exception in SongInfoServiceImpl which is not handled in your SongsInfoController Class.
Solution 1: Instead of throwing the exception return null.
SongInfoServiceImpl.java
#Override
public SongInfo getSongInfoById(String id) {
return songInfoRepository.findById(id).orElse(null);
}
Solution 2: Add try catch block
SongsInfoController.java
#RequiredArgsConstructor
#RestController
#RequestMapping("/songsinfo")
public class SongsInfoController {
private final SongInfoService songInfoService;
#GetMapping(value = "/{id}",
headers = "Accept=application/json",
produces = {MediaType.APPLICATION_JSON_VALUE}
)
public ResponseEntity<SongInfo> getSongInfoById(#PathVariable(value = "id") String id) {
SongInfo songInfo = null;
try {
songInfo = songInfoService.getSongInfoById(id);
} catch(Exception e) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
return new ResponseEntity<>(songInfo, HttpStatus.OK);
}
}
I think you need to divide two problem.
Check id parameter SongsInfoController
Inside controller check your parameter is valid through log or sysout
Check getSongInfoById method in SongInfoServiceImpl
Simply getSongInfoById(8752); is get error?
I want to add comment but my reputation is under 50.
If you comment above two solution check result, then I will add additional answer.

Dynamic config Spring batch execution time and parameters via database configured parameters

I am a fresh spring batch user, pls help me. Here is my requirement:
I have fulfilled several spring batch jobs with different names. I want to execute these jobs with different job parameters, and I hope these parameters can be configured dynamically in database, so I can add new job execution with different job names and different parameters.
Also, I want to schedule my job execution in different time, and the crontab expression can also be configured.
Maybe the database structure is like:
id
task_name
spring_batch_job_name
cron_expression
Wonder if some guys can guide me. Very thanks!
Here is my job setting entity:
#Entity
#Table(name = "report_tasks_manager", schema = "reconciliation", catalog = "")
public class ReportTasksManager {
private int id;
private String taskDesc;
private String taskName;
// crontab expression
private String cronExpression;
// class name to execute job logic
private String methodName;
private int state;
private Integer conCurrent;
private String reserved1;
private String reserved2;
private String reserved3;
private Timestamp startTime;
private Timestamp endTime;
private Timestamp createTime;
}
I defined a class which implemented Job interface, and the execute() method in this class executes the business logic, like launching a spring batch job:
public class QuartzJobFactory implements Job {
public QuartzJobFactory() {
}
#Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
System.out.println("time ={" + new Date() + "}");
System.out.println("starting job build factory");
ReportTasksManager reportTasksManager = (ReportTasksManager) jobExecutionContext.getMergedJobDataMap().get("scheduleJob");
System.out.println("job name = {" + reportTasksManager.getTaskName() + "}");
}
}
For registering a cron trigger, I defined a rest controller to checkout job parameters in database and configure scheduler
#RestController
#RequestMapping(path = "test")
public class QuartzManager {
private SchedulerFactory schedulerFactory = new StdSchedulerFactory();
#Autowired
private ReportTaskManagerDAO reportTaskManagerDAO;
#GetMapping(value = "schedule")
public void scheduleJob() {
// Read settings from database
List<ReportTasksManager> quartzList = reportTaskManagerDAO.findAll();
if (quartzList.size() > 0) {
quartzList.forEach(reportTasksManager -> {
try {
configQuartz(reportTasksManager, schedulerFactory.getScheduler());
} catch (SchedulerException | ClassNotFoundException e) {
e.printStackTrace();
}
});
}
}
#SuppressWarnings("unchecked")
private void configQuartz(ReportTasksManager reportTasksManager, Scheduler scheduler) throws SchedulerException, ClassNotFoundException {
TriggerKey triggerKey = TriggerKey.triggerKey(reportTasksManager.getTaskName(), Scheduler.DEFAULT_GROUP);
// check if triggers already defined in scheduler
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
if (null == trigger) {
// not define——new trigger&jobDetail
JobDetail jobDetail =
JobBuilder.newJob((Class<? extends Job>) Class.forName(reportTasksManager.getMethodName()))
.withIdentity(reportTasksManager.getTaskName(), Scheduler.DEFAULT_GROUP)
.build();
jobDetail.getJobDataMap().put("scheduleJob", reportTasksManager);
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(reportTasksManager.getCronExpression());
trigger = TriggerBuilder.newTrigger()
.withIdentity(reportTasksManager.getTaskName(), Scheduler.DEFAULT_GROUP)
.withSchedule(scheduleBuilder)
.build();
scheduler.scheduleJob(jobDetail, trigger);
scheduler.start();
} else {
// already defined——update
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(reportTasksManager.getCronExpression());
trigger = trigger.getTriggerBuilder()
.withIdentity(triggerKey)
.withSchedule(scheduleBuilder)
.build();
scheduler.rescheduleJob(triggerKey, trigger);
}
}
}
You can use create some Util class( on #PostConstruct) which loads your job config from DB.
For example:
#Entity
public class Configuration{
#Id
private long id;
private String field;
private String value;
// getter and setter
}
#Component
public interface ConfigurationRepo extends JpaRepository<Configuration, Long> {
}
public final class ConfigurationUtil {
private ConfigurationUtil() {
}
private static List<Configuration> defaultConfiguration;
/**
* #return the defaultConfiguration
*/
public static List<Configuration> getDefaultConfiguration() {
return defaultConfiguration;
}
/**
* #param defaultConfiguration the defaultConfiguration to set
*/
public static void setDefaultConfiguration(List<Configuration> defaultConfiguration) {
ConfigurationUtil.defaultConfiguration = defaultConfiguration;
}
public static String getValueByField(String field) {
return defaultConfiguration.stream()
.filter(s -> s.getField()
.equalsIgnoreCase(field))
.findFirst()
.get()
.getValue();
}
}
#Component
public class ConfigurationContextInitializer {
#Autowired
ConfigurationRepo configurationRepo;
#PostConstruct
public void init() {
ConfigurationUtil.setDefaultConfiguration(configurationRepo.findAll());
}
}
//To access DB value
ConfigurationUtil.getValueByField("JOB_NAME"); // depends on your DB key

Feign with Hystrix Fallback throwing bean error on startup

I am working on an example from here about Feign and Hystrix. Without the Feign fallback property, everything works okay. But when I add the fallback property and create the fallback class that implements the feign clients interface, I get the following error
Description:
Field customerClient in com.feign.demo.controllers.CustomerController required a single bean, but 2 were found:
- customerClientFallback: defined in file [../ApplicationFeign/target/classes/com/feign/demo/clients/fallback/CustomerClientFallback.class]
- com.feign.demo.clients.CustomerClient: defined in null
Action:
Consider marking one of the beans as #Primary, updating the consumer to accept multiple beans, or using #Qualifier to identify the bean that should be consumed
Below is my Feign Client Interface :
#FeignClient(name = "CUSTOMERSERVICE", fallback = CustomerClientFallback.class, primary = false)
#RequestMapping(value = "customer")
public interface CustomerClient {
#RequestMapping(method = RequestMethod.GET, value = "/getAllCustomers")
List<Customer> getAllCustomers();
#RequestMapping(method = RequestMethod.PATCH, value = "/{customerId}", consumes = "application/json")
Customer update(#PathVariable("customerId") long customerId, #RequestBody Customer customer);
#RequestMapping(method = RequestMethod.GET, value = "/{customerId}")
Customer getCustomerById(#PathVariable("customerId") long customerId);
#RequestMapping(method = RequestMethod.POST, value = "/", consumes = "application/json")
Customer saveCustomer(#RequestBody Customer customer);
}
CustomerClientFallback implementation:
#Component
public class CustomerClientFallback implements CustomerClient {
#Override
public List<Customer> getAllCustomers() {
return new ArrayList<Customer>();
}
#Override
public Customer update(long customerId, Customer customer) {
// TODO Auto-generated method stub
return null;
}
#Override
public Customer getCustomerById(long customerId) {
// TODO Auto-generated method stub
return null;
}
#Override
public Customer saveCustomer(Customer customer) {
// TODO Auto-generated method stub
return null;
}
}
Application Class:
#SpringBootApplication
#EnableFeignClients
#EnableDiscoveryClient
#EnableHystrix
#EnableHystrixDashboard
public class ApplicationFeignApplication {
public static void main(String[] args) {
SpringApplication.run(ApplicationFeignApplication.class, args);
}
}
Spring cloud version :
Greenwich.SR1
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
Bellow is a modification but it does not work as well.
#RestController
public class CustomerController {
#Autowired
private CustomerClient customerClient;
#Autowired
public CustomerController(#Qualifier("customerClientFallback") CustomerClient customerClient) {
this.customerClient = customerClient;
}
#RequestMapping(path = "/getAllCustomers", method = RequestMethod.GET)
public ResponseEntity<Object> getAllCustomers() {
List<Customer> customers = customerClient.getAllCustomers();
return new ResponseEntity<>(customers, HttpStatus.OK);
}
#RequestMapping(path = "/{customerId}", method = RequestMethod.GET)
public ResponseEntity<Object> get(#PathVariable() long customerId) {
try {
Customer c = customerClient.getCustomerById(customerId);
if (c != null) {
return new ResponseEntity<>(c, HttpStatus.OK);
} else {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body("Customer Not Found");
}
} catch (Exception e) {
e.printStackTrace();
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.getMessage());
}
}
#RequestMapping(path = "/{customerId}", method = RequestMethod.PATCH)
public ResponseEntity<Object> UpdateCustomer(#PathVariable() Long customerId, #RequestBody Customer customer) {
Customer c;
try {
c = customerClient.update(customerId, customer);
if (c != null) {
return new ResponseEntity<>(c, HttpStatus.OK);
} else {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body("Customer Not Found");
}
} catch (Exception e) {
e.printStackTrace();
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.getMessage());
}
}
#RequestMapping(path = "", method = RequestMethod.POST)
public ResponseEntity<Object> saveCustomer(#RequestBody Customer customer) {
Customer c;
try {
c = customerClient.saveCustomer(customer);
return new ResponseEntity<>(c, HttpStatus.OK);
} catch (Exception e) {
e.printStackTrace();
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.getMessage());
}
}
}
It seems there is a problem due to using CustomerClient.java feign client in your controller class.
Please make sure you are adding qualifier
#Autowired
private CustomerClient customerClient;
#Autowired
public CustomerController(#Qualifier("customerClientFallback") CustomerClient customerClient ) {
this.customerClient= customerClient;
}
This should works now.
I will suggest you to look into FallBackFactory for more power on feign exception handling,
This is known bug in Spring Cloud, see:
https://github.com/spring-cloud/spring-cloud-netflix/issues/2677
Remove the autowired annotation from the field, you are already injecting the dependency in the constructor.
private CustomerClient customerClient;
#Autowired
public CustomerController(#Qualifier("customerClientFallback") CustomerClient customerClient) {
this.customerClient = customerClient;
}
Its also safer to use constructor dependency injection instead of field injection - with field injection you allow anyone to create an instance of your class in an invalid state. In the constructor the dependencies are clearly specified and also it is easier to test your code (mock the dependencies and use them in the constructor)
Also, when you annotate an Interface or Class with #RequestMapping Spring will register a handler even if you have a #FeignClient annotation - and since you have an implementation of this interface you should remove it to avoid any issues with ambiguous mapping.
Like so
#FeignClient(name = "CUSTOMERSERVICE", fallback = CustomerClientFallback.class, primary = false)
public interface CustomerClient {
#RequestMapping(method = RequestMethod.GET, value = "/getAllCustomers")
List<Customer> getAllCustomers();
#RequestMapping(method = RequestMethod.PATCH, value = "/{customerId}", consumes = "application/json")
Customer update(#PathVariable("customerId") long customerId, #RequestBody Customer customer);
#RequestMapping(method = RequestMethod.GET, value = "/{customerId}")
Customer getCustomerById(#PathVariable("customerId") long customerId);
#RequestMapping(method = RequestMethod.POST, value = "/", consumes = "application/json")
Customer saveCustomer(#RequestBody Customer customer);
}
The Error seems to be because of the #RequestMapping provided at the class/interface level.
In your case because of #RequestMapping(value = "customer") in CustomerClient.java

Spring restful service test case fail HTTP status is 500

I want to implement test case for spring restful web services which return a json
MY controller test class is :
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {WebAppContext.class,JpaTestConfiguration.class
})
#WebAppConfiguration
public class DominProfileRestControllerTest {
private MockMvc mockMvc;
#Autowired
private WebApplicationContext webApplicationContext;
private MediaType contentType = new MediaType(MediaType.APPLICATION_JSON.getType(),
MediaType.APPLICATION_JSON.getSubtype(),
Charset.forName("utf8"));
#Before
public void setUp() {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
#Test
public void testGetDomainProfile() throws Exception {
String profileId = domainProfile.getId().toString();
System.out.print("testing restful"+profileId);
mockMvc.perform(get("/service/domainprofile/get/{id}", profileId) )
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$.city", is("Chandigrah")));
/* mockMvc.perform(get("/service/domainprofile/get/{id}",profileId).accept(MediaType.TEXT_PLAIN))
.andExpect(status().isOk())
.andExpect(content().contentType("text/plain;charset=ISO-8859-1"))
.andExpect(content().string("hello Prashant"));
*/
}
My Controller class Is :
#RestController
#RequestMapping("/service/domainprofile")
public class DominProfileRestController {
#Autowired
private JpaDomainProfileRepository jpaDomainProfileRepository;
#RequestMapping(value = "/get/{id}", method = RequestMethod.GET)
public DomainProfileResource getDomainProfile(#PathVariable String id) {
JpaDomainProfile domainProfile = jpaDomainProfileRepository.findOne(Long.valueOf(id));
DomainProfileResource domainProfileResource = new DomainProfileResource();
System.out.println("domainProfile.getCity()*************" + domainProfile.getCity());
System.out.println("domainProfile.getAddress()*************" + domainProfile.getAddress());
domainProfileResource.setCity(domainProfile.getCity());
domainProfileResource.setAddress(domainProfile.getAddress());
// return new ResponseEntity<DomainProfileResource>(domainProfileResource, HttpStatus.OK);
return domainProfileResource;
// return domainProfile;
}
}
When I run test case I got An error while we got values in domainprofile.city and domainprofile.address.
Error Is :
java.lang.AssertionError: Status
Expected :200
Actual :500
It Is Working Fine When I return a plain text
can you do this
mockMvc.perform(get("/service/domainprofile/get/{id}", profileId) )
.andDo(print());
this will print the full response with exception , now if you see HttpMessageNotWritableException which was the issue I was facing , you should try to serialize your object using jackson and see if it works (spring internally uses Jackson ). For example , If any of your fields are null the Serialization will fail.