351 lines
10 KiB
Go
351 lines
10 KiB
Go
package api
|
|
|
|
import (
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/google/uuid"
|
|
"holiday-api/domain/holiday"
|
|
"log/slog"
|
|
"net/http"
|
|
"strconv"
|
|
"time"
|
|
)
|
|
|
|
func NoMethod() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
c.AbortWithError(http.StatusNotFound, nil)
|
|
}
|
|
}
|
|
|
|
func NoRoute() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
c.AbortWithError(http.StatusNotFound, nil)
|
|
}
|
|
}
|
|
|
|
func GetIndex(holidayService holiday.HolidayService, countryService holiday.CountryService, yearService holiday.YearService) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
search := holiday.Search{Country: "HR", Year: nil}
|
|
if err := c.ShouldBindQuery(&search); err != nil {
|
|
c.AbortWithError(http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
years, _ := yearService.Find()
|
|
if search.Year == nil {
|
|
c.HTML(http.StatusOK, "index.gohtml", gin.H{"Year": time.Now().Year(), "Years": years})
|
|
return
|
|
}
|
|
countries, _ := countryService.Find()
|
|
holidays, _ := holidayService.Find(search, holiday.Paging{PageSize: 100})
|
|
c.HTML(http.StatusOK, "results.gohtml", gin.H{"Years": years, "Countries": countries, "Search": search, "Holidays": holiday.ToResponse(holidays).Holidays})
|
|
}
|
|
}
|
|
|
|
func GetDocumentation(countryService holiday.CountryService, yearService holiday.YearService) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
countries, _ := countryService.Find()
|
|
years, _ := yearService.Find()
|
|
c.HTML(http.StatusOK, "documentation.gohtml", gin.H{"Year": time.Now().Year(), "Years": years, "Countries": countries})
|
|
}
|
|
}
|
|
|
|
func GetHolidays(service holiday.HolidayService) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
paging := holiday.Paging{PageSize: 50}
|
|
if err := c.ShouldBindQuery(&paging); err != nil {
|
|
c.AbortWithError(http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
|
|
search := holiday.Search{}
|
|
if err := c.ShouldBindQuery(&search); err != nil {
|
|
c.AbortWithError(http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
|
|
holidays, err := service.Find(search, paging)
|
|
if err != nil {
|
|
render(c, http.StatusNotFound, holiday.ErrorResponse{Created: time.Now(), Message: "failed fetching holidays"}, nil)
|
|
} else {
|
|
render(c, http.StatusOK, holiday.ToResponse(holidays), search.Type)
|
|
}
|
|
}
|
|
}
|
|
|
|
func DeleteCountry(countryService holiday.CountryService) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
id := uuid.MustParse(c.Param("id"))
|
|
_, err := countryService.FindById(id)
|
|
if err != nil {
|
|
Abort(c, err, http.StatusNotFound, "couldn't find country")
|
|
return
|
|
}
|
|
if err := countryService.Delete(id); err != nil {
|
|
Abort(c, err, http.StatusInternalServerError, "couldn't delete country")
|
|
return
|
|
}
|
|
c.Redirect(http.StatusSeeOther, "/admin/countries")
|
|
}
|
|
}
|
|
|
|
func CreateOrUpdateCountry(countryService holiday.CountryService) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
request := struct {
|
|
Id *string `form:"id"`
|
|
IsoName string `form:"iso_name" binding:"required,min=2,max=2"`
|
|
Name string `form:"name" binding:"required,min=1,max=45"`
|
|
}{}
|
|
if err := c.ShouldBind(&request); err != nil {
|
|
Abort(c, err, http.StatusInternalServerError, "invalid request when creating or updating country")
|
|
return
|
|
}
|
|
|
|
country := holiday.Country{
|
|
IsoName: request.IsoName,
|
|
Name: request.Name,
|
|
}
|
|
|
|
var err error
|
|
if request.Id != nil {
|
|
country.Id = uuid.MustParse(*request.Id)
|
|
country, err = countryService.Update(country)
|
|
} else {
|
|
country, err = countryService.Create(country)
|
|
}
|
|
if err != nil {
|
|
Abort(c, err, http.StatusInternalServerError, "couldn't create or update country")
|
|
} else {
|
|
c.Redirect(http.StatusSeeOther, "/admin/countries")
|
|
}
|
|
}
|
|
}
|
|
|
|
func GetCountries(countryService holiday.CountryService) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
countries, _ := countryService.Find()
|
|
c.HTML(http.StatusOK, "countries.gohtml", gin.H{"Countries": countries})
|
|
}
|
|
}
|
|
|
|
func DeleteHoliday(holidayService holiday.HolidayService) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
id := uuid.MustParse(c.Param("id"))
|
|
hol, err := holidayService.FindById(id)
|
|
if err != nil {
|
|
Abort(c, err, http.StatusNotFound, "couldn't find holiday to delete")
|
|
return
|
|
}
|
|
if err := holidayService.Delete(id); err != nil {
|
|
Abort(c, err, http.StatusInternalServerError, "couldn't delete holiday")
|
|
return
|
|
}
|
|
c.Redirect(http.StatusSeeOther, "/admin?country="+hol.Country+"&year="+strconv.FormatInt(int64(hol.Date.Year()), 10))
|
|
}
|
|
}
|
|
|
|
func CopyYear(holidayService holiday.HolidayService) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
request := struct {
|
|
From int `form:"from"`
|
|
To int `form:"to"`
|
|
Country string `form:"country" binding:"len=2"`
|
|
}{}
|
|
if err := c.ShouldBind(&request); err != nil {
|
|
Abort(c, err, http.StatusBadRequest, "invalid request when copying holiday")
|
|
return
|
|
}
|
|
|
|
err := holidayService.Copy(request.Country, request.From, request.To)
|
|
if err != nil {
|
|
Abort(c, err, http.StatusInternalServerError, "couldn't copy holidays")
|
|
} else {
|
|
c.Redirect(http.StatusSeeOther, "/admin?country="+request.Country+"&year="+strconv.FormatInt(int64(request.To), 10))
|
|
}
|
|
}
|
|
}
|
|
|
|
func CreateOrUpdateHoliday(holidayService holiday.HolidayService) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
request := struct {
|
|
Id *string `form:"id"`
|
|
Name string `form:"name" binding:"required,min=1"`
|
|
Description string `form:"description"`
|
|
IsStateHoliday bool `form:"state_holiday"`
|
|
IsReligiousHoliday bool `form:"religious_holiday"`
|
|
Country string `form:"country" binding:"len=2"`
|
|
Date time.Time `form:"date" time_format:"2006-01-02"`
|
|
}{}
|
|
if err := c.ShouldBind(&request); err != nil {
|
|
Abort(c, err, http.StatusBadRequest, "invalid request when creating holiday")
|
|
return
|
|
}
|
|
|
|
hol := holiday.Holiday{
|
|
Country: request.Country,
|
|
Date: request.Date,
|
|
Name: request.Name,
|
|
Description: request.Description,
|
|
IsStateHoliday: request.IsStateHoliday,
|
|
IsReligiousHoliday: request.IsReligiousHoliday,
|
|
}
|
|
|
|
var err error
|
|
if request.Id != nil {
|
|
hol.Id = uuid.MustParse(*request.Id)
|
|
hol, err = holidayService.Update(hol)
|
|
} else {
|
|
hol, err = holidayService.Create(hol)
|
|
}
|
|
if err != nil {
|
|
Abort(c, err, 500, "couldn't create or update holiday")
|
|
} else {
|
|
c.Redirect(http.StatusSeeOther, "/admin?country="+request.Country+"&year="+strconv.FormatInt(int64(request.Date.Year()), 10))
|
|
}
|
|
}
|
|
}
|
|
|
|
func GetAdminHome(holidayService holiday.HolidayService, countryService holiday.CountryService, yearService holiday.YearService) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
search := holiday.Search{Country: "HR", Year: new(int)}
|
|
*search.Year = time.Now().Year()
|
|
if err := c.ShouldBindQuery(&search); err != nil {
|
|
Abort(c, err, http.StatusBadRequest, "invalid search parameters")
|
|
return
|
|
}
|
|
holidays, _ := holidayService.Find(search, holiday.Paging{PageSize: 100})
|
|
holidayResponse := holiday.ToResponse(holidays)
|
|
countries, _ := countryService.Find()
|
|
years, _ := yearService.Find()
|
|
|
|
response := map[string]any{}
|
|
response["Holidays"] = holidayResponse
|
|
response["Search"] = search
|
|
response["Countries"] = countries
|
|
response["Years"] = years
|
|
|
|
c.HTML(http.StatusOK, "admin_dashboard.gohtml", response)
|
|
}
|
|
}
|
|
|
|
func AddHolidayDialog(countryService holiday.CountryService) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
countries, _ := countryService.Find()
|
|
c.HTML(http.StatusOK, "add-holiday.gohtml", gin.H{"Countries": countries})
|
|
}
|
|
}
|
|
|
|
func EditHolidayDialog(holidayService holiday.HolidayService, countryService holiday.CountryService) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
id := uuid.MustParse(c.Query("id"))
|
|
hol, err := holidayService.FindById(id)
|
|
if err != nil {
|
|
Abort(c, err, http.StatusNotFound, "couldn't find holiday to edit")
|
|
return
|
|
}
|
|
countries, _ := countryService.Find()
|
|
c.HTML(http.StatusOK, "edit-holiday.gohtml", gin.H{"Countries": countries, "Holiday": hol})
|
|
}
|
|
}
|
|
|
|
func DeleteHolidayDialog(holidayService holiday.HolidayService) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
id := uuid.MustParse(c.Query("id"))
|
|
hol, err := holidayService.FindById(id)
|
|
if err != nil {
|
|
Abort(c, err, http.StatusNotFound, "couldn't find dialog")
|
|
return
|
|
}
|
|
c.HTML(http.StatusOK, "delete-holiday.gohtml", gin.H{"Holiday": hol})
|
|
}
|
|
}
|
|
|
|
func CopyYearDialog() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
country := c.Query("country")
|
|
year, err := strconv.ParseInt(c.Query("year"), 10, 32)
|
|
if err != nil {
|
|
Abort(c, err, http.StatusNotFound, "couldn't parse year")
|
|
return
|
|
}
|
|
c.HTML(http.StatusOK, "copy-year.gohtml", gin.H{"Country": country, "Year": year})
|
|
}
|
|
}
|
|
|
|
func AddCountryDialog() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
c.HTML(http.StatusOK, "add-country.gohtml", gin.H{})
|
|
}
|
|
}
|
|
|
|
func EditCountryDialog(countryService holiday.CountryService) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
id := uuid.MustParse(c.Query("id"))
|
|
country, err := countryService.FindById(id)
|
|
if err != nil {
|
|
Abort(c, err, http.StatusNotFound, "couldn't find country")
|
|
return
|
|
}
|
|
c.HTML(http.StatusOK, "edit-country.gohtml", gin.H{"Country": country})
|
|
}
|
|
}
|
|
|
|
func DeleteCountryDialog(countryService holiday.CountryService) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
id := uuid.MustParse(c.Query("id"))
|
|
country, err := countryService.FindById(id)
|
|
if err != nil {
|
|
Abort(c, err, http.StatusNotFound, "couldn't find dialog")
|
|
return
|
|
}
|
|
c.HTML(http.StatusOK, "delete-country.gohtml", gin.H{"Country": country})
|
|
}
|
|
}
|
|
|
|
type CSV interface {
|
|
CSV() []byte
|
|
}
|
|
|
|
func render(c *gin.Context, status int, response any, contentType *string) {
|
|
value := c.GetHeader("accept")
|
|
if contentType != nil {
|
|
switch *contentType {
|
|
case "xml":
|
|
value = "text/xml"
|
|
case "json":
|
|
value = "application/json"
|
|
case "csv":
|
|
value = "text/csv"
|
|
}
|
|
}
|
|
switch value {
|
|
case "text/csv":
|
|
if csvResponse, ok := response.(CSV); ok {
|
|
c.Data(200, value+"; charset=utf-8", csvResponse.CSV())
|
|
} else {
|
|
c.Header("content-type", "application/json; charset=utf-8")
|
|
c.JSON(status, response)
|
|
}
|
|
case "application/xml":
|
|
fallthrough
|
|
case "text/xml":
|
|
c.Header("content-type", value+"; charset=utf-8; header=present;")
|
|
c.XML(status, response)
|
|
case "application/json":
|
|
fallthrough
|
|
default:
|
|
c.Header("content-type", "application/json; charset=utf-8")
|
|
c.JSON(status, response)
|
|
}
|
|
}
|
|
|
|
func Abort(c *gin.Context, err error, statusCode int, message string) {
|
|
slog.Error(message, slog.String("err", errorMessage(err)))
|
|
c.AbortWithError(statusCode, err)
|
|
}
|
|
|
|
func errorMessage(err error) string {
|
|
if err != nil {
|
|
return err.Error()
|
|
}
|
|
return "-"
|
|
}
|