Get response from external API in Spring Reactive WebFlux

1.3k views Asked by At

Hi I'm new to reactive programming. I want to get a response from external API which has holiday calendar and count working days between given two days. Here is what I implement up to now. But I have no idea how to get holidays from it.

WebClientConfiguration.java

@Configuration
public class WebClientConfiguration  {
    @Bean
    public WebClient webclient() {
        final int size = 16 * 1024 * 1024;
        final ExchangeStrategies strategies = ExchangeStrategies.builder()
                .codecs(codecs -> codecs.defaultCodecs().maxInMemorySize(size))
                .build();

        WebClient webClient = WebClient
                .builder()
                .baseUrl("http://222.165.138.144:5015/api/worklog/leaves")
                .defaultCookie("cookieKey", "cookieValue")
                .exchangeStrategies(strategies)
                .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                .build();
        return webClient;
    }

}
@Service
public class CompanyCalandarServiceImpl implements CompanyCalendarService {

    private static final Logger LOGGER = LoggerFactory.getLogger(CompanyCalandarServiceImpl.class);

    @Autowired
    WebClient webClient;

    String Calandar = "http://222.165.138.144:5015/api/worklog/leaves";

    @Override
    public Flux<CompanyHolidayCalendar> getCompanyHolidays() {
        return webClient.get()
                .uri(Calandar)
                .retrieve()
                .bodyToFlux(CompanyHolidayCalendar.class)
                .doOnError(throwable -> LOGGER.error("Failed for some reason", throwable));
    }

}

companyHolidayCalanderDTO.java

@Data
public class CompanyHolidayCalendar {

    private String date ;
    private String summery;
    private String description;
    private String type;
    
}

controller

    @GetMapping("/days-count")
    public Mono<ResponseEntity<LmsApiResponse>> getDaysCount(@RequestParam("start_date") String startDate,
            @RequestParam("end_date") String endDate) {

        long startTime = System.currentTimeMillis();

        LOGGER.info("getDaysCountRequest : StartDate={}|EndDate={}", startDate, endDate);

        return leaveRequestService.getDaysCount(startDate, endDate).map(
                response -> {

                    LOGGER.info("getDaysCountResponse : timeTaken={}|response={}", CommonUtil.getTimeTaken(startTime),
                            CommonUtil.convertToString(response));

                    return new ResponseEntity<>(
                            new LmsApiResponse(200, "Done!", response), HttpStatus.OK);
                });

    }

*Here I calculate working days without Saturday , Sunday and Holidays(coming from External API)

@Override
    public Mono<Integer> getDaysCount(String startDate, String endDate) {
        LOGGER.info("getDaysCount");

        Flux<CompanyHolidayCalendar> holidays = companyCalendarService.getCompanyHolidays();
/*This return FluxPeek. How to get date from this and calculate working days?*/

        System.out.println(holidays);



        int workingDays = 0;
        SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
        try {
            Calendar start = Calendar.getInstance();
            start.setTime(sdf.parse(startDate));
            Calendar end = Calendar.getInstance();
            end.setTime(sdf.parse(endDate));
            while (!start.after(end)) {
                int day = start.get(Calendar.DAY_OF_WEEK);

                day = day + 3;
                if (day > 7) {
                    day = day - 7;
                }

                if ((day != Calendar.SATURDAY) && (day != Calendar.SUNDAY))
                    workingDays++;
                start.add(Calendar.DATE, 1);
            }
            System.out.println(workingDays);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return Mono.just(workingDays);

    }

P.S : I'm using Java 18 to develop this. And I get Start date and End Date from FrontEnd as String (dd/mm/yyyy) format and this holiday API response coming as a String (2022-12-15).

3

There are 3 answers

1
lapadets On

Well, I am just guessing here and trying to give you some directions without a sample project and I have not run the code, but I would try to do something like this in the getDaysCount method:

@Override
public Mono<Integer> getDaysCount(String startDate, String endDate) {
    LOGGER.info("getDaysCount");

    SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");

    // Collect the holidays emitted by the Flux into a List
    return companyCalendarService.getCompanyHolidays()
        .collectList()
        .flatMap(holidays -> {

            Calendar start = Calendar.getInstance();
            try {
                start.setTime(sdf.parse(startDate));
            } catch (ParseException e) {
                e.printStackTrace();
            }
            Calendar end = Calendar.getInstance();
            try {
                end.setTime(sdf.parse(endDate));
            } catch (ParseException e) {
                e.printStackTrace();
            }

            int workingDays = 0;

            while (!start.after(end)) {
                int day = start.get(Calendar.DAY_OF_WEEK);
                day = day + 3;
                if (day > 7) {
                    day = day - 7;
                }

                if ((day != Calendar.SATURDAY) && (day != Calendar.SUNDAY)) {
                    boolean isHoliday = false;
                    for (CompanyHolidayCalendar holiday : holidays) {
                        // Compare the date of the holiday with the current date
                        if (sdf.format(start.getTime()).equals(holiday.getDate())) {
                            isHoliday = true;
                            break;
                        }
                    }
                    // If the date is not a holiday, increment the workingDays count
                    if (!isHoliday) {
                        workingDays++;
                    }
                }
                start.add(Calendar.DATE, 1);
            }

            return Mono.just(workingDays);
        });
}

in other words, I would try to do something like this where I get the companyHolidays from your service and then convert them into a list. Then you can do the rest of the calculations as you already do.

Also, I would suggest you to think about exception handling. Generally, it is not a good idea to catch an exception and print the stack. You could return a Mono.error instead:

} catch (ParseException e) {
   // maybe also log some meaningfull message
   return Mono.error(e);
}
0
Alex On

You need to build reactive flow in order to process result from getCompanyHolidays.

public Mono<Integer> getDaysCount(String startDate, String endDate) {
    return companyCalendarService.getCompanyHolidays()
            .collectList()
            .map(calendar -> calculateWorkingDays(startDate, endDate, calendar));
}

Now calculateWorkingDays is not reactive and you just need to calculate working days correctly.

int calculateWorkingDays(String startDate, String endDate, List<CompanyHolidayCalendar> calendar) {
   ...
}
0
Kunal Varpe On

We can do something like below. I have tried this with some test and it is working for me.

@Service
public class LeaveRequestServiceImpl implements LeaveRequestService {

    private final Predicate<DayOfWeek> weekendsPredicate = dayOfWeek -> DayOfWeek.SATURDAY == dayOfWeek
                                                                        || DayOfWeek.SUNDAY == dayOfWeek;
    private final CompanyCalendarService companyCalendarService;

    public LeaveRequestServiceImpl(CompanyCalendarService companyCalendarService) {
        this.companyCalendarService = companyCalendarService;
    }

    @Override
    public Mono<Integer> getDaysCount(String startDate, String endDate) {

        LocalDate start = parseDate(startDate);
        LocalDate end = parseDate(endDate);

        long daysWithoutWeekends = start.datesUntil(end.plusDays(1))
                                       .map(LocalDate::getDayOfWeek)
                                       .filter(weekendsPredicate.negate())
                                       .count();

        return companyCalendarService.getCompanyHolidays()
                                     .map(CompanyHoliday::date)
                                     .map(this::parseDate)
                                     .filter(date -> date.isAfter(start) && date.isBefore(end))
                                     .count()
                                     .map(days -> daysWithoutWeekends - days)
                                     .map(Long::intValue);


    }

    private LocalDate parseDate(String date) {
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
        return LocalDate.parse(date, dateTimeFormatter);
    }
}