2024-06-11 17:28:20 -04:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2024-07-03 17:34:18 -04:00
|
|
|
"encoding/csv"
|
2024-06-11 17:28:20 -04:00
|
|
|
"fmt"
|
2024-07-03 17:34:18 -04:00
|
|
|
"io"
|
2024-06-11 17:28:20 -04:00
|
|
|
"net/http"
|
2024-06-18 21:21:30 -04:00
|
|
|
"strconv"
|
2024-06-11 17:28:20 -04:00
|
|
|
|
|
|
|
"codeberg.org/vlbeaudoin/pave/v2"
|
|
|
|
"codeberg.org/vlbeaudoin/voki/v3"
|
2024-07-03 17:34:18 -04:00
|
|
|
"github.com/gocarina/gocsv"
|
2024-06-11 17:28:20 -04:00
|
|
|
"github.com/labstack/echo/v4"
|
|
|
|
)
|
|
|
|
|
2024-07-03 17:34:18 -04:00
|
|
|
func addRoutes(e *echo.Echo, db *PostgresClient, cfg Config) error {
|
2024-06-11 17:28:20 -04:00
|
|
|
_ = db
|
2024-07-03 17:37:29 -04:00
|
|
|
_ = cfg
|
2024-06-11 17:28:20 -04:00
|
|
|
|
2024-06-18 19:44:20 -04:00
|
|
|
apiPath := "/api/v7"
|
2024-06-11 17:28:20 -04:00
|
|
|
apiGroup := e.Group(apiPath)
|
|
|
|
p := pave.New()
|
|
|
|
if err := pave.EchoRegister[HealthGETRequest](
|
|
|
|
apiGroup,
|
|
|
|
&p,
|
|
|
|
apiPath,
|
|
|
|
http.MethodGet,
|
|
|
|
"/health/",
|
|
|
|
"Get API server health",
|
|
|
|
"HealthGET", func(c echo.Context) error {
|
|
|
|
var request, response = HealthGETRequest{}, HealthGETResponse{}
|
|
|
|
if !request.Complete() {
|
|
|
|
var response voki.ResponseBadRequest
|
|
|
|
response.Message = "Incomplete HealthGET request received"
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
|
|
}
|
|
|
|
if err := response.SetStatusCode(http.StatusOK); err != nil {
|
|
|
|
var response voki.ResponseInternalServerError
|
|
|
|
response.Message = fmt.Sprintf("handler: %s", err)
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
|
|
}
|
|
|
|
|
|
|
|
response.Message = "ok"
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
|
|
|
|
|
|
}); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2024-06-17 14:07:49 -04:00
|
|
|
|
|
|
|
if err := pave.EchoRegister[ProgrammesPOSTRequest](
|
|
|
|
apiGroup,
|
|
|
|
&p,
|
|
|
|
apiPath,
|
|
|
|
http.MethodPost,
|
2024-06-18 19:44:20 -04:00
|
|
|
"/programme/",
|
2024-06-17 17:25:53 -04:00
|
|
|
"Insert programmes",
|
2024-06-17 14:07:49 -04:00
|
|
|
"ProgrammesPOST", func(c echo.Context) error {
|
|
|
|
var request, response = ProgrammesPOSTRequest{}, ProgrammesPOSTResponse{}
|
|
|
|
|
2024-07-03 17:34:18 -04:00
|
|
|
switch contentType := c.Request().Header.Get("Content-Type"); contentType {
|
|
|
|
case "application/json":
|
|
|
|
if err := c.Bind(&request.Data); err != nil {
|
|
|
|
var response voki.ResponseBadRequest
|
|
|
|
response.Message = fmt.Sprintf("parse request body: %s", err)
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
|
|
}
|
|
|
|
case "text/csv":
|
|
|
|
body := c.Request().Body
|
|
|
|
if body == nil {
|
|
|
|
var response voki.ResponseBadRequest
|
|
|
|
response.Message = "empty request body cannot be parsed"
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
|
|
}
|
|
|
|
defer body.Close()
|
|
|
|
|
|
|
|
gocsv.SetCSVReader(func(in io.Reader) gocsv.CSVReader {
|
|
|
|
r := csv.NewReader(in)
|
|
|
|
r.Comma = ';'
|
|
|
|
return r // Allows use ; as delimiter
|
|
|
|
})
|
|
|
|
|
|
|
|
// Parse CSV data using gocsv
|
|
|
|
if err := gocsv.Unmarshal(body, &request.Data.Programmes); err != nil {
|
|
|
|
var response voki.ResponseBadRequest
|
|
|
|
response.Message = fmt.Sprintf("parse programmes from csv: %s", err)
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
|
|
}
|
|
|
|
default:
|
2024-06-17 14:07:49 -04:00
|
|
|
var response voki.ResponseBadRequest
|
2024-07-03 17:34:18 -04:00
|
|
|
response.Message = fmt.Sprintf("cannot parse body with content-type: %s", contentType)
|
2024-06-17 14:07:49 -04:00
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !request.Complete() {
|
|
|
|
var response voki.ResponseBadRequest
|
|
|
|
response.Message = "Incomplete ProgrammesPOST request received"
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
|
|
}
|
|
|
|
|
|
|
|
amountInserted, err := db.InsertProgrammes(request.Data.Programmes...)
|
|
|
|
if err != nil {
|
|
|
|
var response voki.ResponseBadRequest
|
|
|
|
response.Message = fmt.Sprintf("db: %s", err)
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
|
|
}
|
|
|
|
response.Data.ProgrammesInserted = amountInserted
|
|
|
|
|
|
|
|
if err := response.SetStatusCode(http.StatusOK); err != nil {
|
|
|
|
var response voki.ResponseInternalServerError
|
|
|
|
response.Message = fmt.Sprintf("handler: %s", err)
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
|
|
}
|
|
|
|
|
|
|
|
response.Message = "ok"
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
|
|
|
|
|
|
}); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2024-06-17 17:25:53 -04:00
|
|
|
|
|
|
|
if err := pave.EchoRegister[MembresPOSTRequest](
|
|
|
|
apiGroup,
|
|
|
|
&p,
|
|
|
|
apiPath,
|
|
|
|
http.MethodPost,
|
2024-06-18 19:44:20 -04:00
|
|
|
"/membre/",
|
2024-06-17 17:25:53 -04:00
|
|
|
"Insert membres",
|
|
|
|
"MembresPOST", func(c echo.Context) error {
|
|
|
|
var request, response = MembresPOSTRequest{}, MembresPOSTResponse{}
|
|
|
|
|
2024-07-03 17:34:18 -04:00
|
|
|
switch contentType := c.Request().Header.Get("Content-Type"); contentType {
|
|
|
|
case "application/json":
|
|
|
|
if err := c.Bind(&request.Data); err != nil {
|
|
|
|
var response voki.ResponseBadRequest
|
|
|
|
response.Message = fmt.Sprintf("parse request body: %s", err)
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
|
|
}
|
|
|
|
case "text/csv":
|
|
|
|
body := c.Request().Body
|
|
|
|
if body == nil {
|
|
|
|
var response voki.ResponseBadRequest
|
|
|
|
response.Message = "empty request body cannot be parsed"
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
|
|
}
|
|
|
|
defer body.Close()
|
|
|
|
|
|
|
|
gocsv.SetCSVReader(func(in io.Reader) gocsv.CSVReader {
|
|
|
|
r := csv.NewReader(in)
|
|
|
|
r.Comma = ';'
|
|
|
|
return r // Allows use ; as delimiter
|
|
|
|
})
|
|
|
|
|
|
|
|
// Parse CSV data using gocsv
|
|
|
|
if err := gocsv.Unmarshal(body, &request.Data.Membres); err != nil {
|
|
|
|
var response voki.ResponseBadRequest
|
|
|
|
response.Message = fmt.Sprintf("parse membres from csv: %s", err)
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
|
|
}
|
|
|
|
default:
|
2024-06-17 17:25:53 -04:00
|
|
|
var response voki.ResponseBadRequest
|
2024-07-03 17:34:18 -04:00
|
|
|
response.Message = fmt.Sprintf("cannot parse body with content-type: %s", contentType)
|
2024-06-17 17:25:53 -04:00
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !request.Complete() {
|
|
|
|
var response voki.ResponseBadRequest
|
|
|
|
response.Message = "Incomplete MembresPOST request received"
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
|
|
}
|
|
|
|
|
|
|
|
amountInserted, err := db.InsertMembres(request.Data.Membres...)
|
|
|
|
if err != nil {
|
|
|
|
var response voki.ResponseBadRequest
|
|
|
|
response.Message = fmt.Sprintf("db: %s", err)
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
|
|
}
|
|
|
|
response.Data.MembresInserted = amountInserted
|
|
|
|
|
|
|
|
if err := response.SetStatusCode(http.StatusOK); err != nil {
|
|
|
|
var response voki.ResponseInternalServerError
|
|
|
|
response.Message = fmt.Sprintf("handler: %s", err)
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
|
|
}
|
|
|
|
|
|
|
|
response.Message = "ok"
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
|
|
|
|
|
|
}); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2024-06-18 19:44:20 -04:00
|
|
|
|
|
|
|
if err := pave.EchoRegister[MembreGETRequest](
|
|
|
|
apiGroup,
|
|
|
|
&p,
|
|
|
|
apiPath,
|
|
|
|
http.MethodGet,
|
|
|
|
"/membre/:membre_id/",
|
|
|
|
"Get membre",
|
|
|
|
"MembreGET", func(c echo.Context) error {
|
|
|
|
var request, response = MembreGETRequest{}, MembreGETResponse{}
|
|
|
|
|
|
|
|
request.Param.MembreID = c.Param("membre_id")
|
|
|
|
|
|
|
|
if !request.Complete() {
|
|
|
|
var response voki.ResponseBadRequest
|
|
|
|
response.Message = "Incomplete MembreGET request received"
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
|
|
}
|
|
|
|
|
|
|
|
membre, err := db.GetMembre(request.Param.MembreID)
|
|
|
|
if err != nil {
|
|
|
|
var response voki.ResponseBadRequest
|
|
|
|
response.Message = fmt.Sprintf("db: %s", err)
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
|
|
}
|
|
|
|
response.Data.Membre = membre
|
|
|
|
|
|
|
|
if err := response.SetStatusCode(http.StatusOK); err != nil {
|
|
|
|
var response voki.ResponseInternalServerError
|
|
|
|
response.Message = fmt.Sprintf("handler: %s", err)
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
|
|
}
|
|
|
|
|
|
|
|
response.Message = "ok"
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
|
|
|
|
|
|
}); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2024-06-18 21:21:30 -04:00
|
|
|
|
|
|
|
if err := pave.EchoRegister[MembresGETRequest](
|
|
|
|
apiGroup,
|
|
|
|
&p,
|
|
|
|
apiPath,
|
|
|
|
http.MethodGet,
|
|
|
|
"/membre/",
|
|
|
|
"Get membres",
|
|
|
|
"MembresGET", func(c echo.Context) (err error) {
|
|
|
|
var request, response = MembresGETRequest{}, MembresGETResponse{}
|
|
|
|
|
2024-07-03 17:34:18 -04:00
|
|
|
queryLimit := c.QueryParam("limit")
|
|
|
|
if queryLimit != "" {
|
|
|
|
request.Query.Limit, err = strconv.Atoi(queryLimit)
|
|
|
|
if err != nil {
|
|
|
|
var response voki.ResponseBadRequest
|
|
|
|
response.Message = fmt.Sprintf("parsing limit: %s", err)
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
//TODO cfg.API.DefaultLimit
|
|
|
|
request.Query.Limit = 1000
|
2024-06-18 21:21:30 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if !request.Complete() {
|
|
|
|
var response voki.ResponseBadRequest
|
|
|
|
response.Message = "Incomplete MembresGET request received"
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
|
|
}
|
|
|
|
|
|
|
|
response.Data.Membres, err = db.GetMembres(request.Query.Limit)
|
|
|
|
if err != nil {
|
|
|
|
var response voki.ResponseBadRequest
|
|
|
|
response.Message = fmt.Sprintf("db: %s", err)
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := response.SetStatusCode(http.StatusOK); err != nil {
|
|
|
|
var response voki.ResponseInternalServerError
|
|
|
|
response.Message = fmt.Sprintf("handler: %s", err)
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
|
|
}
|
|
|
|
|
|
|
|
response.Message = "ok"
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
|
|
|
|
|
|
}); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2024-06-19 00:04:19 -04:00
|
|
|
|
|
|
|
if err := pave.EchoRegister[ProgrammesGETRequest](
|
|
|
|
apiGroup,
|
|
|
|
&p,
|
|
|
|
apiPath,
|
|
|
|
http.MethodGet,
|
|
|
|
"/programme/",
|
|
|
|
"Get programmes",
|
|
|
|
"ProgrammesGET", func(c echo.Context) (err error) {
|
|
|
|
var request, response = ProgrammesGETRequest{}, ProgrammesGETResponse{}
|
|
|
|
|
2024-07-03 17:34:18 -04:00
|
|
|
queryLimit := c.QueryParam("limit")
|
|
|
|
if queryLimit != "" {
|
|
|
|
request.Query.Limit, err = strconv.Atoi(queryLimit)
|
|
|
|
if err != nil {
|
|
|
|
var response voki.ResponseBadRequest
|
|
|
|
response.Message = fmt.Sprintf("parsing limit: %s", err)
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
//TODO cfg.API.DefaultLimit
|
|
|
|
request.Query.Limit = 1000
|
2024-06-19 00:04:19 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if !request.Complete() {
|
|
|
|
var response voki.ResponseBadRequest
|
|
|
|
response.Message = "Incomplete ProgrammesGET request received"
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
|
|
}
|
|
|
|
|
|
|
|
response.Data.Programmes, err = db.GetProgrammes(request.Query.Limit)
|
|
|
|
if err != nil {
|
|
|
|
var response voki.ResponseBadRequest
|
|
|
|
response.Message = fmt.Sprintf("db: %s", err)
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := response.SetStatusCode(http.StatusOK); err != nil {
|
|
|
|
var response voki.ResponseInternalServerError
|
|
|
|
response.Message = fmt.Sprintf("handler: %s", err)
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
|
|
}
|
|
|
|
|
|
|
|
response.Message = "ok"
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
|
|
|
|
|
|
}); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2024-06-19 00:28:26 -04:00
|
|
|
|
|
|
|
if err := pave.EchoRegister[MembrePreferedNamePUTRequest](
|
|
|
|
apiGroup,
|
|
|
|
&p,
|
|
|
|
apiPath,
|
|
|
|
http.MethodPut,
|
|
|
|
"/membre/:membre_id/prefered_name/",
|
|
|
|
"Update membre prefered name, which is prioritized in the membres_for_display view",
|
|
|
|
"MembrePreferedNamePUT", func(c echo.Context) error {
|
|
|
|
var request, response = MembrePreferedNamePUTRequest{}, MembrePreferedNamePUTResponse{}
|
|
|
|
|
|
|
|
request.Param.MembreID = c.Param("membre_id")
|
|
|
|
|
|
|
|
if err := c.Bind(&request.Data); err != nil {
|
|
|
|
var response voki.ResponseBadRequest
|
|
|
|
response.Message = fmt.Sprintf("parse request body: %s", err)
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !request.Complete() {
|
|
|
|
var response voki.ResponseBadRequest
|
|
|
|
response.Message = "Incomplete MembrePreferedNamePUT request received"
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := db.UpdateMembrePreferedName(request.Param.MembreID, request.Data.PreferedName); err != nil {
|
|
|
|
var response voki.ResponseBadRequest
|
|
|
|
response.Message = fmt.Sprintf("db: %s", err)
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := response.SetStatusCode(http.StatusOK); err != nil {
|
|
|
|
var response voki.ResponseInternalServerError
|
|
|
|
response.Message = fmt.Sprintf("handler: %s", err)
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
|
|
}
|
|
|
|
|
|
|
|
response.Message = fmt.Sprintf("Updated membre %s name to %s", request.Param.MembreID, request.Data.PreferedName)
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
|
|
|
|
|
|
}); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2024-06-20 18:51:38 -04:00
|
|
|
if err := pave.EchoRegister[MembresDisplayGETRequest](
|
|
|
|
apiGroup,
|
|
|
|
&p,
|
|
|
|
apiPath,
|
|
|
|
http.MethodGet,
|
|
|
|
"/membre/display/",
|
|
|
|
"Get membres",
|
|
|
|
"MembresDisplayGET", func(c echo.Context) (err error) {
|
|
|
|
var request, response = MembresDisplayGETRequest{}, MembresDisplayGETResponse{}
|
|
|
|
|
2024-07-03 17:34:18 -04:00
|
|
|
queryLimit := c.QueryParam("limit")
|
|
|
|
if queryLimit != "" {
|
|
|
|
request.Query.Limit, err = strconv.Atoi(queryLimit)
|
|
|
|
if err != nil {
|
|
|
|
var response voki.ResponseBadRequest
|
|
|
|
response.Message = fmt.Sprintf("parsing limit: %s", err)
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
//TODO cfg.API.DefaultLimit
|
|
|
|
request.Query.Limit = 1000
|
2024-06-20 18:51:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if !request.Complete() {
|
|
|
|
var response voki.ResponseBadRequest
|
|
|
|
response.Message = "Incomplete MembresDisplayGET request received"
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
|
|
}
|
|
|
|
|
|
|
|
response.Data.Membres, err = db.GetMembresForDisplay(request.Query.Limit)
|
|
|
|
if err != nil {
|
|
|
|
var response voki.ResponseBadRequest
|
|
|
|
response.Message = fmt.Sprintf("db: %s", err)
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := response.SetStatusCode(http.StatusOK); err != nil {
|
|
|
|
var response voki.ResponseInternalServerError
|
|
|
|
response.Message = fmt.Sprintf("handler: %s", err)
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
|
|
}
|
|
|
|
|
|
|
|
response.Message = "ok"
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
|
|
|
|
|
|
}); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := pave.EchoRegister[MembreDisplayGETRequest](
|
|
|
|
apiGroup,
|
|
|
|
&p,
|
|
|
|
apiPath,
|
|
|
|
http.MethodGet,
|
|
|
|
"/membre/:membre_id/display/",
|
|
|
|
"Get membre",
|
|
|
|
"MembreDisplayGET", func(c echo.Context) error {
|
|
|
|
var request, response = MembreDisplayGETRequest{}, MembreDisplayGETResponse{}
|
|
|
|
|
|
|
|
request.Param.MembreID = c.Param("membre_id")
|
|
|
|
|
|
|
|
if !request.Complete() {
|
|
|
|
var response voki.ResponseBadRequest
|
|
|
|
response.Message = "Incomplete MembreDisplayGET request received"
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
|
|
}
|
|
|
|
|
|
|
|
membre, err := db.GetMembreForDisplay(request.Param.MembreID)
|
|
|
|
if err != nil {
|
|
|
|
var response voki.ResponseBadRequest
|
|
|
|
response.Message = fmt.Sprintf("db: %s", err)
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
|
|
}
|
|
|
|
response.Data.Membre = membre
|
|
|
|
|
|
|
|
if err := response.SetStatusCode(http.StatusOK); err != nil {
|
|
|
|
var response voki.ResponseInternalServerError
|
|
|
|
response.Message = fmt.Sprintf("handler: %s", err)
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
|
|
}
|
|
|
|
|
|
|
|
response.Message = "ok"
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
|
|
|
|
|
|
}); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2024-06-11 17:28:20 -04:00
|
|
|
return nil
|
|
|
|
}
|