Skip to content

Commit

Permalink
refactor: implement holiday
Browse files Browse the repository at this point in the history
  • Loading branch information
mohsenk committed Aug 15, 2024
1 parent 2e3b6b6 commit b52bfee
Show file tree
Hide file tree
Showing 15 changed files with 315 additions and 0 deletions.
20 changes: 20 additions & 0 deletions src/main/java/app/teamwize/api/holiday/config/HolidayConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package app.teamwize.api.holiday.config;

import app.teamwize.api.holiday.provider.OpenHolidayProvider;
import app.teamwize.api.holiday.provider.PublicHolidayProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestClient;


@Configuration
public class HolidayConfig {

@Bean
PublicHolidayProvider openHolidayProvider() {
var restClient = RestClient.builder().baseUrl("https://openholidaysapi.org")
.build();
return new OpenHolidayProvider(restClient);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package app.teamwize.api.holiday.controller;

import app.teamwize.api.auth.service.SecurityService;
import app.teamwize.api.holiday.domain.exception.PublicHolidayProviderConnectionBrokenException;
import app.teamwize.api.holiday.domain.response.FetchedPublicHoliday;
import app.teamwize.api.holiday.domain.request.HolidayCreateRequest;
import app.teamwize.api.holiday.domain.response.HolidayResponse;
import app.teamwize.api.holiday.mapper.HolidayMapper;
import app.teamwize.api.holiday.service.HolidayService;
import app.teamwize.api.organization.exception.OrganizationNotFoundException;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("holidays")
@RequiredArgsConstructor
public class HolidayController {

private final HolidayService holidayService;
private final SecurityService securityService;
private final HolidayMapper holidayMapper;

@PostMapping("batch")
public List<HolidayResponse> createHolidays(@RequestBody List<HolidayCreateRequest> requests) throws OrganizationNotFoundException {
var holidays = holidayService.createHolidays(securityService.getUserOrganizationId(), requests);
return holidayMapper.toHolidayResponses(holidays);
}

@GetMapping("fetch")
public List<FetchedPublicHoliday> fetchPublicHoliday(Integer year) throws OrganizationNotFoundException, PublicHolidayProviderConnectionBrokenException {
return holidayService.fetchPublicHolidays(securityService.getUserOrganizationId(), year);
}

@GetMapping
public List<HolidayResponse> getHolidays(Integer year, String countryCode) {
var holidays = holidayService.getHolidays(securityService.getUserOrganizationId(), year, countryCode);
return holidayMapper.toHolidayResponses(holidays);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package app.teamwize.api.holiday.domain;

public enum HolidayStatus {
ACTIVE,
ARCHIVED
}
34 changes: 34 additions & 0 deletions src/main/java/app/teamwize/api/holiday/domain/entity/Holiday.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package app.teamwize.api.holiday.domain.entity;

import app.teamwize.api.base.domain.entity.BaseAuditEntity;
import app.teamwize.api.holiday.domain.HolidayStatus;
import app.teamwize.api.organization.domain.entity.Organization;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;

import java.time.LocalDate;

@Getter
@Setter
@Entity
@Table(name = "holidays")
public class Holiday extends BaseAuditEntity {
@Id
@GeneratedValue(generator = "holiday_id_seq_generator")
@SequenceGenerator(name = "holiday_id_seq_generator", sequenceName = "holiday_id_seq", allocationSize = 10)
protected Long id;

private LocalDate date;

private String countryCode;

private String description;

@Enumerated(EnumType.STRING)
private HolidayStatus status;

@ManyToOne(fetch = FetchType.LAZY)
private Organization organization;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package app.teamwize.api.holiday.domain.exception;

public class PublicHolidayProviderConnectionBrokenException extends Exception {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package app.teamwize.api.holiday.domain.request;

import java.time.LocalDate;

public record HolidayCreateRequest(String description, LocalDate date,String countryCode) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package app.teamwize.api.holiday.domain.response;

import java.time.LocalDate;

public record FetchedPublicHoliday(LocalDate date, String type, String name){}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package app.teamwize.api.holiday.domain.response;

import java.time.LocalDate;

public record HolidayResponse(Long id, String description, LocalDate date, String countryCode) {
}
13 changes: 13 additions & 0 deletions src/main/java/app/teamwize/api/holiday/mapper/HolidayMapper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package app.teamwize.api.holiday.mapper;

import app.teamwize.api.base.config.DefaultMapperConfig;
import app.teamwize.api.holiday.domain.entity.Holiday;
import app.teamwize.api.holiday.domain.response.HolidayResponse;
import org.mapstruct.Mapper;

import java.util.List;

@Mapper(config = DefaultMapperConfig.class)
public interface HolidayMapper {
List<HolidayResponse> toHolidayResponses(List<Holiday> holidays);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package app.teamwize.api.holiday.provider;

import app.teamwize.api.holiday.domain.response.FetchedPublicHoliday;
import lombok.RequiredArgsConstructor;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.web.client.RestClient;

import java.time.LocalDate;
import java.util.List;

import static org.springframework.http.MediaType.APPLICATION_JSON;

@RequiredArgsConstructor
public class OpenHolidayProvider implements PublicHolidayProvider {

private final RestClient restClient;

record OpenHolidayResponse(
String id,
LocalDate startDate,
String endDate,
String type,
List<OpenHolidayResponseName> name,
boolean nationwide) {
}

record OpenHolidayResponseName(
String language,
String text) {
}


@Override
public List<FetchedPublicHoliday> getPublicHolidays(String countryCode, Integer year) {
List<OpenHolidayResponse> publicHolidays = this.restClient.get().uri(uriBuilder -> uriBuilder
.path("/PublicHolidays")
.queryParam("countryIsoCode", countryCode)
.queryParam("languageIsoCode", "EN")
.queryParam("validFrom", LocalDate.of(year, 1, 1).toString())
.queryParam("validTo", LocalDate.of(year + 1, 1, 1).toString())
.build())

.accept(APPLICATION_JSON).retrieve()
.body(new ParameterizedTypeReference<>() {
});

return publicHolidays
.stream()
.map(this::toFetchedPublicHoliday)
.toList();
}


private FetchedPublicHoliday toFetchedPublicHoliday(OpenHolidayResponse response) {
return new FetchedPublicHoliday(response.startDate, response.type, response.name.get(0).text);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package app.teamwize.api.holiday.provider;


import app.teamwize.api.holiday.domain.response.FetchedPublicHoliday;

import java.util.List;

public interface PublicHolidayProvider {
List<FetchedPublicHoliday> getPublicHolidays(String countryCode, Integer year);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package app.teamwize.api.holiday.repository;

import app.teamwize.api.holiday.domain.entity.Holiday;
import io.hypersistence.utils.spring.repository.BaseJpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.web.bind.annotation.RequestMapping;

import java.time.LocalDate;
import java.util.List;

@RequestMapping
public interface HolidayRepository extends BaseJpaRepository<Holiday, Long>, JpaSpecificationExecutor<Holiday> {
List<Holiday> findByOrganizationIdAndCountryCodeAndDateIsBetween(Long organizationId, String countryCode, LocalDate startDate,LocalDate endDate);
}
56 changes: 56 additions & 0 deletions src/main/java/app/teamwize/api/holiday/service/HolidayService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package app.teamwize.api.holiday.service;

import app.teamwize.api.holiday.domain.exception.PublicHolidayProviderConnectionBrokenException;
import app.teamwize.api.holiday.domain.response.FetchedPublicHoliday;
import app.teamwize.api.holiday.domain.entity.Holiday;
import app.teamwize.api.holiday.domain.request.HolidayCreateRequest;
import app.teamwize.api.holiday.provider.PublicHolidayProvider;
import app.teamwize.api.holiday.repository.HolidayRepository;
import app.teamwize.api.organization.exception.OrganizationNotFoundException;
import app.teamwize.api.organization.service.OrganizationService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.util.List;

@Service
@RequiredArgsConstructor
public class HolidayService {

private final HolidayRepository holidayRepository;
private final OrganizationService organizationService;
private final PublicHolidayProvider publicHolidayProvider;

@Transactional
public List<Holiday> createHolidays(Long organizationId, List<HolidayCreateRequest> requests) throws OrganizationNotFoundException {
var organization = organizationService.getOrganization(organizationId);
var holidays = requests.stream().map(holidayCreateRequest -> new Holiday()
.setDescription(holidayCreateRequest.description())
.setDate(holidayCreateRequest.date())
.setCountryCode(holidayCreateRequest.countryCode())
.setOrganization(organization)
).toList();
return holidayRepository.persistAll(holidays);
}

@Transactional
public List<FetchedPublicHoliday> fetchPublicHolidays(Long organizationId, Integer year) throws OrganizationNotFoundException, PublicHolidayProviderConnectionBrokenException {
if (year == null) {
year = LocalDate.now().getYear();
}
var organization = organizationService.getOrganization(organizationId);
try {
return publicHolidayProvider.getPublicHolidays(organization.getCountry(), year);
} catch (Exception ex) {
throw new PublicHolidayProviderConnectionBrokenException();
}
}

public List<Holiday> getHolidays(Long organizationId, Integer year, String countryCode) {
var startDate = LocalDate.of(year, 1, 1);
var endDate = startDate.plusYears(1);
return holidayRepository.findByOrganizationIdAndCountryCodeAndDateIsBetween(organizationId, countryCode, startDate, endDate);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>

<databaseChangeLog
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">

<changeSet id="20240815-1430-create-holidays-table" author="M.Karimi">
<createSequence sequenceName="holiday_id_seq" startValue="1" minValue="1" incrementBy="10" cycle="false"/>

<createTable tableName="holidays">
<column name="id" type="BIGINT" defaultValue="nextval('holiday_id_seq')">
<constraints nullable="false" primaryKey="true" primaryKeyName="holiday_key_pkey"/>
</column>
<column name="created_at" type="TIMESTAMP WITHOUT TIME ZONE"/>
<column name="updated_at" type="TIMESTAMP WITHOUT TIME ZONE"/>


<column name="description" type="VARCHAR"/>

<column name="status" type="VARCHAR"/>

<column name="country_code" type="VARCHAR"/>

<column name="date" type="DATE"/>

<column name="organization_id" type="BIGINT">
<constraints nullable="false"/>
</column>

<column name="metadata" type="JSONB"/>
</createTable>

<addForeignKeyConstraint baseTableName="holidays" baseColumnNames="organization_id"
constraintName="holidays_organization_id_fkey"
referencedTableName="organizations" referencedColumnNames="id"/>
</changeSet>
</databaseChangeLog>
3 changes: 3 additions & 0 deletions src/main/resources/db/changelog/db.changelog-master.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,7 @@ databaseChangeLog:
relativeToChangelogFile: true
- include:
file: 20240714-1651-add-timezone-workingdays-to-organizations-table.xml
relativeToChangelogFile: true
- include:
file: 20240815-1430-create-holidays-table.xml
relativeToChangelogFile: true

0 comments on commit b52bfee

Please sign in to comment.