major: séparer commande de librairie importable
Bump major version à 9 package main déplacé vers cmd/bottin/ pour garder `go install` qui nomme l'exécutable `bottin`, sans empêcher d'importer le code à l'extérieur du projet avec pkg/bottin/. Déplacer fichiers SQL vers queries/ Déplacer fichiers html vers templates/ Ajouter scripts/ avec génération et injection de certificats x509 (https) et les ajouter au Makefile Ajouter début d'exemple de manifests dans deployments/kubernetes/
This commit is contained in:
parent
a17d6bf06c
commit
b419a5b260
25 changed files with 513 additions and 451 deletions
170
pkg/bottin/client.go
Normal file
170
pkg/bottin/client.go
Normal file
|
@ -0,0 +1,170 @@
|
|||
package bottin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"codeberg.org/vlbeaudoin/voki/v3"
|
||||
)
|
||||
|
||||
type APIClient struct {
|
||||
Voki *voki.Voki
|
||||
}
|
||||
|
||||
func (c APIClient) GetHealth() (health string, err error) {
|
||||
var request HealthGETRequest
|
||||
response, err := request.Request(c.Voki)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if code, message := response.StatusCode(), response.Message; code >= 400 {
|
||||
err = fmt.Errorf("%d: %s", code, message)
|
||||
return
|
||||
}
|
||||
|
||||
return response.Message, nil
|
||||
}
|
||||
|
||||
func (c APIClient) InsertProgrammes(programmes ...Programme) (amountInserted int64, err error) {
|
||||
var request ProgrammesPOSTRequest
|
||||
request.Data.Programmes = programmes
|
||||
|
||||
response, err := request.Request(c.Voki)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if code, message := response.StatusCode(), response.Message; code >= 400 {
|
||||
err = fmt.Errorf("%d: %s", code, message)
|
||||
return
|
||||
}
|
||||
|
||||
return response.Data.ProgrammesInserted, nil
|
||||
}
|
||||
|
||||
func (c APIClient) InsertMembres(membres ...Membre) (amountInserted int64, err error) {
|
||||
var request MembresPOSTRequest
|
||||
request.Data.Membres = membres
|
||||
|
||||
response, err := request.Request(c.Voki)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if code, message := response.StatusCode(), response.Message; code >= 400 {
|
||||
err = fmt.Errorf("%d: %s", code, message)
|
||||
return
|
||||
}
|
||||
|
||||
return response.Data.MembresInserted, nil
|
||||
}
|
||||
|
||||
func (c APIClient) GetMembre(membreID string) (membre Membre, err error) {
|
||||
var request MembreGETRequest
|
||||
request.Param.MembreID = membreID
|
||||
|
||||
response, err := request.Request(c.Voki)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if code, message := response.StatusCode(), response.Message; code >= 400 {
|
||||
err = fmt.Errorf("%d: %s", code, message)
|
||||
return
|
||||
}
|
||||
|
||||
return response.Data.Membre, nil
|
||||
}
|
||||
|
||||
func (c APIClient) GetMembres(limit int) (membres []Membre, err error) {
|
||||
var request MembresGETRequest
|
||||
|
||||
request.Query.Limit = limit
|
||||
|
||||
response, err := request.Request(c.Voki)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if code, message := response.StatusCode(), response.Message; code >= 400 {
|
||||
err = fmt.Errorf("%d: %s", code, message)
|
||||
return
|
||||
}
|
||||
|
||||
return response.Data.Membres, nil
|
||||
}
|
||||
|
||||
func (c APIClient) GetProgrammes(limit int) (programmes []Programme, err error) {
|
||||
var request ProgrammesGETRequest
|
||||
|
||||
request.Query.Limit = limit
|
||||
|
||||
response, err := request.Request(c.Voki)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if code, message := response.StatusCode(), response.Message; code >= 400 {
|
||||
err = fmt.Errorf("%d: %s", code, message)
|
||||
return
|
||||
}
|
||||
|
||||
return response.Data.Programmes, nil
|
||||
}
|
||||
|
||||
func (c APIClient) UpdateMembrePreferedName(membreID string, name string) (err error) {
|
||||
var request MembrePreferedNamePUTRequest
|
||||
|
||||
if !IsMembreID(membreID) {
|
||||
return fmt.Errorf("Numéro étudiant '%s' invalide", membreID)
|
||||
}
|
||||
request.Param.MembreID = membreID
|
||||
request.Data.PreferedName = name
|
||||
|
||||
response, err := request.Request(c.Voki)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if code, message := response.StatusCode(), response.Message; code >= 400 {
|
||||
err = fmt.Errorf("%d: %s", code, message)
|
||||
return
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c APIClient) GetMembreForDisplay(membreID string) (membre MembreForDisplay, err error) {
|
||||
var request MembreDisplayGETRequest
|
||||
request.Param.MembreID = membreID
|
||||
|
||||
response, err := request.Request(c.Voki)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if code, message := response.StatusCode(), response.Message; code >= 400 {
|
||||
err = fmt.Errorf("%d: %s", code, message)
|
||||
return
|
||||
}
|
||||
|
||||
return response.Data.Membre, nil
|
||||
}
|
||||
|
||||
func (c APIClient) GetMembresForDisplay(limit int) (membres []MembreForDisplay, err error) {
|
||||
var request MembresDisplayGETRequest
|
||||
|
||||
request.Query.Limit = limit
|
||||
|
||||
response, err := request.Request(c.Voki)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if code, message := response.StatusCode(), response.Message; code >= 400 {
|
||||
err = fmt.Errorf("%d: %s", code, message)
|
||||
return
|
||||
}
|
||||
|
||||
return response.Data.Membres, nil
|
||||
}
|
53
pkg/bottin/config.go
Normal file
53
pkg/bottin/config.go
Normal file
|
@ -0,0 +1,53 @@
|
|||
package bottin
|
||||
|
||||
type Config struct {
|
||||
Client struct {
|
||||
API struct {
|
||||
Host string `yaml:"host"`
|
||||
Key string `yaml:"key"`
|
||||
Port int `yaml:"port"`
|
||||
Protocol string `yaml:"protocol"`
|
||||
} `yaml:"api"`
|
||||
} `yaml:"client"`
|
||||
|
||||
Server struct {
|
||||
API struct {
|
||||
DB struct {
|
||||
Database string `yaml:"database"`
|
||||
Host string `yaml:"host"`
|
||||
Password string `yaml:"password"`
|
||||
Port int `yaml:"port"`
|
||||
SSLMode string `yaml:"sslmode"`
|
||||
User string `yaml:"user"`
|
||||
} `yaml:"db"`
|
||||
Host string `yaml:"host"`
|
||||
Key string `yaml:"key"`
|
||||
Port int `yaml:"port"`
|
||||
TLS struct {
|
||||
Enabled bool `yaml:"enabled"`
|
||||
Certfile string `yaml:"certfile"`
|
||||
Keyfile string `yaml:"keyfile"`
|
||||
} `yaml:"tls"`
|
||||
} `yaml:"api"`
|
||||
UI struct {
|
||||
API struct {
|
||||
Host string `yaml:"host"`
|
||||
Key string `yaml:"key"`
|
||||
Port int `yaml:"port"`
|
||||
Protocol string `yaml:"protocol"`
|
||||
TLS struct {
|
||||
SkipVerify bool `yaml:"skipverify"`
|
||||
} `yaml:"tls"`
|
||||
} `yaml:"api"`
|
||||
Host string `yaml:"host"`
|
||||
Password string `yaml:"password"`
|
||||
Port int `yaml:"port"`
|
||||
TLS struct {
|
||||
Enabled bool `yaml:"enabled"`
|
||||
Certfile string `yaml:"certfile"`
|
||||
Keyfile string `yaml:"keyfile"`
|
||||
} `yaml:"tls"`
|
||||
User string `yaml:"user"`
|
||||
} `yaml:"ui"`
|
||||
} `yaml:"server"`
|
||||
}
|
353
pkg/bottin/db.go
Normal file
353
pkg/bottin/db.go
Normal file
|
@ -0,0 +1,353 @@
|
|||
package bottin
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
|
||||
"git.agecem.com/agecem/bottin/v9/queries"
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
type PostgresClient struct {
|
||||
//TODO move context out of client
|
||||
Ctx context.Context
|
||||
Pool *pgxpool.Pool
|
||||
}
|
||||
|
||||
func (db *PostgresClient) CreateOrReplaceSchema() error {
|
||||
_, err := db.Pool.Exec(db.Ctx, queries.SQLSchema())
|
||||
return err
|
||||
}
|
||||
|
||||
func (db *PostgresClient) CreateOrReplaceViews() error {
|
||||
_, err := db.Pool.Exec(db.Ctx, queries.SQLViews())
|
||||
return err
|
||||
}
|
||||
|
||||
// InsertMembres inserts a slice of Membre into a database, returning the amount inserted and any error encountered
|
||||
func (d *PostgresClient) InsertMembres(membres ...Membre) (inserted int64, err error) {
|
||||
select {
|
||||
case <-d.Ctx.Done():
|
||||
return inserted, fmt.Errorf("PostgresClient.Ctx closed: %s", d.Ctx.Err())
|
||||
default:
|
||||
tx, err := d.Pool.Begin(d.Ctx)
|
||||
if err != nil {
|
||||
return inserted, err
|
||||
}
|
||||
defer tx.Rollback(d.Ctx)
|
||||
|
||||
for i, membre := range membres {
|
||||
if membre.ID == "" {
|
||||
return inserted, fmt.Errorf("insertion ligne %d: membre requiert numéro étudiant valide", i)
|
||||
}
|
||||
|
||||
result, err := tx.Exec(d.Ctx, `
|
||||
INSERT INTO membres
|
||||
(id, last_name, first_name, prefered_name, programme_id)
|
||||
VALUES
|
||||
($1, $2, $3, $4, $5)
|
||||
ON CONFLICT (id) DO NOTHING;`,
|
||||
membre.ID,
|
||||
membre.LastName,
|
||||
membre.FirstName,
|
||||
membre.PreferedName,
|
||||
membre.ProgrammeID,
|
||||
)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
inserted += result.RowsAffected()
|
||||
}
|
||||
|
||||
if err = tx.Commit(d.Ctx); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return inserted, err
|
||||
}
|
||||
}
|
||||
|
||||
func (d *PostgresClient) InsertProgrammes(programmes ...Programme) (inserted int64, err error) {
|
||||
select {
|
||||
case <-d.Ctx.Done():
|
||||
return inserted, fmt.Errorf("PostgresClient.Ctx closed: %s", d.Ctx.Err())
|
||||
default:
|
||||
tx, err := d.Pool.Begin(d.Ctx)
|
||||
if err != nil {
|
||||
return inserted, err
|
||||
}
|
||||
defer tx.Rollback(d.Ctx)
|
||||
|
||||
for _, programme := range programmes {
|
||||
if programme.ID == "" {
|
||||
return 0, fmt.Errorf("Cannot insert programme with no programme_id")
|
||||
}
|
||||
|
||||
result, err := tx.Exec(d.Ctx, `
|
||||
INSERT INTO programmes
|
||||
(id, name)
|
||||
VALUES ($1, $2) ON CONFLICT DO NOTHING;`,
|
||||
programme.ID,
|
||||
programme.Name)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
inserted += result.RowsAffected()
|
||||
}
|
||||
|
||||
if err := tx.Commit(d.Ctx); err != nil {
|
||||
return inserted, err
|
||||
}
|
||||
|
||||
return inserted, err
|
||||
}
|
||||
}
|
||||
|
||||
func (d *PostgresClient) GetMembre(membreID string) (membre Membre, err error) {
|
||||
select {
|
||||
case <-d.Ctx.Done():
|
||||
err = fmt.Errorf("PostgresClient.Ctx closed: %s", d.Ctx.Err())
|
||||
return
|
||||
default:
|
||||
if err = d.Pool.QueryRow(d.Ctx, `
|
||||
SELECT
|
||||
"membres".id,
|
||||
"membres".last_name,
|
||||
"membres".first_name,
|
||||
"membres".prefered_name,
|
||||
"membres".programme_id
|
||||
FROM
|
||||
"membres"
|
||||
WHERE
|
||||
"membres".id = $1
|
||||
LIMIT
|
||||
1;
|
||||
`, membreID).Scan(
|
||||
&membre.ID,
|
||||
&membre.LastName,
|
||||
&membre.FirstName,
|
||||
&membre.PreferedName,
|
||||
&membre.ProgrammeID,
|
||||
); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if membre.ID == "" {
|
||||
return membre, fmt.Errorf("Aucun membre trouvé avec numéro '%s'", membre.ID)
|
||||
}
|
||||
|
||||
return membre, nil
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
func (d *PostgresClient) UpdateMembreName(membreID, newName string) (int64, error) {
|
||||
result, err := d.Pool.Exec("UPDATE membres SET prefered_name = $1 WHERE id = $2;", newName, membreID)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
rows, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return rows, err
|
||||
}
|
||||
|
||||
return rows, nil
|
||||
}
|
||||
*/
|
||||
|
||||
func (d *PostgresClient) GetMembres(limit int) (membres []Membre, err error) {
|
||||
select {
|
||||
case <-d.Ctx.Done():
|
||||
return nil, fmt.Errorf("PostgresClient.Ctx closed: %s", d.Ctx.Err())
|
||||
default:
|
||||
rows, err := d.Pool.Query(d.Ctx, `
|
||||
SELECT
|
||||
"membres".id,
|
||||
"membres".last_name,
|
||||
"membres".first_name,
|
||||
"membres".prefered_name,
|
||||
"membres".programme_id
|
||||
FROM
|
||||
"membres"
|
||||
ORDER BY
|
||||
"membres".id
|
||||
LIMIT
|
||||
$1;
|
||||
`, limit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var membre Membre
|
||||
|
||||
if err = rows.Scan(
|
||||
&membre.ID,
|
||||
&membre.LastName,
|
||||
&membre.FirstName,
|
||||
&membre.PreferedName,
|
||||
&membre.ProgrammeID,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
membres = append(membres, membre)
|
||||
}
|
||||
if rows.Err() != nil {
|
||||
return membres, rows.Err()
|
||||
}
|
||||
|
||||
return membres, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (d *PostgresClient) GetProgrammes(limit int) (programmes []Programme, err error) {
|
||||
select {
|
||||
case <-d.Ctx.Done():
|
||||
return nil, fmt.Errorf("PostgresClient.Ctx closed: %s", d.Ctx.Err())
|
||||
default:
|
||||
rows, err := d.Pool.Query(d.Ctx, `
|
||||
SELECT
|
||||
"programmes".id,
|
||||
"programmes".name
|
||||
FROM
|
||||
"programmes"
|
||||
ORDER BY
|
||||
"programmes".id
|
||||
LIMIT
|
||||
$1;
|
||||
`, limit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var programme Programme
|
||||
|
||||
if err = rows.Scan(
|
||||
&programme.ID,
|
||||
&programme.Name,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
programmes = append(programmes, programme)
|
||||
}
|
||||
if rows.Err() != nil {
|
||||
return programmes, rows.Err()
|
||||
}
|
||||
|
||||
return programmes, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (d *PostgresClient) UpdateMembrePreferedName(membreID string, name string) (err error) {
|
||||
select {
|
||||
case <-d.Ctx.Done():
|
||||
return fmt.Errorf("PostgresClient.Ctx closed: %s", d.Ctx.Err())
|
||||
default:
|
||||
if !IsMembreID(membreID) {
|
||||
return fmt.Errorf("Numéro étudiant '%s' invalide", membreID)
|
||||
}
|
||||
|
||||
_, err = d.Pool.Exec(d.Ctx, `
|
||||
UPDATE
|
||||
"membres"
|
||||
SET
|
||||
prefered_name = $1
|
||||
WHERE
|
||||
"membres".id = $2;
|
||||
`, name, membreID)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (d *PostgresClient) GetMembreForDisplay(membreID string) (membre MembreForDisplay, err error) {
|
||||
select {
|
||||
case <-d.Ctx.Done():
|
||||
err = fmt.Errorf("PostgresClient.Ctx closed: %s", d.Ctx.Err())
|
||||
return
|
||||
default:
|
||||
if err = d.Pool.QueryRow(d.Ctx, `
|
||||
SELECT
|
||||
"membres_for_display".id,
|
||||
"membres_for_display".name,
|
||||
"membres_for_display".programme_id,
|
||||
"membres_for_display".programme_name
|
||||
FROM
|
||||
"membres_for_display"
|
||||
WHERE
|
||||
"membres_for_display".id = $1
|
||||
LIMIT
|
||||
1;
|
||||
`, membreID).Scan(
|
||||
&membre.ID,
|
||||
&membre.Name,
|
||||
&membre.ProgrammeID,
|
||||
&membre.ProgrammeName,
|
||||
); err != nil {
|
||||
if err == pgx.ErrNoRows {
|
||||
err = fmt.Errorf("Numéro étudiant valide mais aucun·e membre trouvé·e")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if membre.ID == "" {
|
||||
return membre, fmt.Errorf("Aucun membre trouvé avec numéro '%s'", membre.ID)
|
||||
}
|
||||
|
||||
return membre, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (d *PostgresClient) GetMembresForDisplay(limit int) (membres []MembreForDisplay, err error) {
|
||||
select {
|
||||
case <-d.Ctx.Done():
|
||||
return nil, fmt.Errorf("PostgresClient.Ctx closed: %s", d.Ctx.Err())
|
||||
default:
|
||||
rows, err := d.Pool.Query(d.Ctx, `
|
||||
SELECT
|
||||
"membres_for_display".id,
|
||||
"membres_for_display".name,
|
||||
"membres_for_display".programme_id,
|
||||
"membres_for_display".programme_name
|
||||
FROM
|
||||
"membres_for_display"
|
||||
ORDER BY
|
||||
"membres_for_display".id
|
||||
LIMIT
|
||||
$1;
|
||||
`, limit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var membre MembreForDisplay
|
||||
|
||||
if err = rows.Scan(
|
||||
&membre.ID,
|
||||
&membre.Name,
|
||||
&membre.ProgrammeID,
|
||||
&membre.ProgrammeName,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
membres = append(membres, membre)
|
||||
}
|
||||
if rows.Err() != nil {
|
||||
return membres, rows.Err()
|
||||
}
|
||||
|
||||
return membres, nil
|
||||
}
|
||||
}
|
38
pkg/bottin/entity.go
Normal file
38
pkg/bottin/entity.go
Normal file
|
@ -0,0 +1,38 @@
|
|||
package bottin
|
||||
|
||||
import "unicode"
|
||||
|
||||
type Programme struct {
|
||||
ID string `db:"id" json:"programme_id" csv:"programme_id"`
|
||||
Name string `db:"name" json:"nom_programme" csv:"nom_programme"`
|
||||
}
|
||||
|
||||
type Membre struct {
|
||||
ID string `db:"id" json:"membre_id" csv:"membre_id"`
|
||||
LastName string `db:"last_name" json:"last_name" csv:"last_name"`
|
||||
FirstName string `db:"first_name" json:"first_name" csv:"first_name"`
|
||||
PreferedName string `db:"prefered_name" json:"prefered_name" csv:"prefered_name"`
|
||||
ProgrammeID string `db:"programme_id" json:"programme_id" csv:"programme_id"`
|
||||
}
|
||||
|
||||
// MembreForDisplay maps to the `membres_for_display` view declared in `sql/views.sql`
|
||||
type MembreForDisplay struct {
|
||||
ID string `db:"id" json:"membre_id" csv:"membre_id"`
|
||||
Name string `db:"name" json:"name" csv:"name"`
|
||||
ProgrammeID string `db:"programme_id" json:"programme_id" csv:"programme_id"`
|
||||
ProgrammeName string `db:"programme_name" json:"programme_name" csv:"programme_name"`
|
||||
}
|
||||
|
||||
func IsMembreID(membre_id string) bool {
|
||||
if len(membre_id) != 7 {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, character := range membre_id {
|
||||
if !unicode.IsDigit(character) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
350
pkg/bottin/request.go
Normal file
350
pkg/bottin/request.go
Normal file
|
@ -0,0 +1,350 @@
|
|||
package bottin
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"codeberg.org/vlbeaudoin/voki/v3"
|
||||
)
|
||||
|
||||
var _ voki.Requester[HealthGETResponse] = HealthGETRequest{}
|
||||
|
||||
type HealthGETRequest struct{}
|
||||
|
||||
func (request HealthGETRequest) Complete() bool { return true }
|
||||
|
||||
func (request HealthGETRequest) Request(v *voki.Voki) (response HealthGETResponse, err error) {
|
||||
if !request.Complete() {
|
||||
err = fmt.Errorf("Incomplete HealthGET request")
|
||||
return
|
||||
}
|
||||
|
||||
statusCode, body, err := v.CallAndParse(
|
||||
http.MethodGet,
|
||||
"/api/v9/health/",
|
||||
nil,
|
||||
true,
|
||||
)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("%d: %s", statusCode, err)
|
||||
return
|
||||
}
|
||||
response.SetStatusCode(statusCode)
|
||||
|
||||
if err = json.Unmarshal(body, &response); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
var _ voki.Requester[ProgrammesPOSTResponse] = ProgrammesPOSTRequest{}
|
||||
|
||||
type ProgrammesPOSTRequest struct {
|
||||
Data struct {
|
||||
Programmes []Programme
|
||||
}
|
||||
}
|
||||
|
||||
func (request ProgrammesPOSTRequest) Complete() bool {
|
||||
return len(request.Data.Programmes) != 0
|
||||
}
|
||||
func (request ProgrammesPOSTRequest) Request(v *voki.Voki) (response ProgrammesPOSTResponse, err error) {
|
||||
if !request.Complete() {
|
||||
err = fmt.Errorf("Incomplete ProgrammesPOSTRequest")
|
||||
return
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err = json.NewEncoder(&buf).Encode(request.Data); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
statusCode, body, err := v.CallAndParse(
|
||||
http.MethodPost,
|
||||
"/api/v9/programme/",
|
||||
&buf,
|
||||
true,
|
||||
)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("code=%d err=%s", statusCode, err)
|
||||
return
|
||||
}
|
||||
response.SetStatusCode(statusCode)
|
||||
if err = json.Unmarshal(body, &response); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
var _ voki.Requester[MembresPOSTResponse] = MembresPOSTRequest{}
|
||||
|
||||
type MembresPOSTRequest struct {
|
||||
Data struct {
|
||||
Membres []Membre
|
||||
}
|
||||
}
|
||||
|
||||
func (request MembresPOSTRequest) Complete() bool {
|
||||
return len(request.Data.Membres) != 0
|
||||
}
|
||||
|
||||
func (request MembresPOSTRequest) Request(v *voki.Voki) (response MembresPOSTResponse, err error) {
|
||||
if !request.Complete() {
|
||||
err = fmt.Errorf("Incomplete MembresPOSTRequest")
|
||||
return
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err = json.NewEncoder(&buf).Encode(request.Data); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
statusCode, body, err := v.CallAndParse(
|
||||
http.MethodPost,
|
||||
"/api/v9/membre/",
|
||||
&buf,
|
||||
true,
|
||||
)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("code=%d err=%s", statusCode, err)
|
||||
return
|
||||
}
|
||||
response.SetStatusCode(statusCode)
|
||||
if err = json.Unmarshal(body, &response); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
var _ voki.Requester[MembreGETResponse] = MembreGETRequest{}
|
||||
|
||||
type MembreGETRequest struct {
|
||||
Param struct {
|
||||
MembreID string `json:"membre_id" param:"membre_id"`
|
||||
}
|
||||
}
|
||||
|
||||
func (request MembreGETRequest) Complete() bool {
|
||||
return request.Param.MembreID != ""
|
||||
}
|
||||
|
||||
func (request MembreGETRequest) Request(v *voki.Voki) (response MembreGETResponse, err error) {
|
||||
if !request.Complete() {
|
||||
err = fmt.Errorf("Incomplete MembreGETRequest")
|
||||
return
|
||||
}
|
||||
|
||||
if id := request.Param.MembreID; !IsMembreID(id) {
|
||||
err = fmt.Errorf("MembreID '%s' invalide", id)
|
||||
return
|
||||
}
|
||||
|
||||
statusCode, body, err := v.CallAndParse(
|
||||
http.MethodGet,
|
||||
fmt.Sprintf("/api/v9/membre/%s/", request.Param.MembreID),
|
||||
nil,
|
||||
true,
|
||||
)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("code=%d err=%s", statusCode, err)
|
||||
return
|
||||
}
|
||||
response.SetStatusCode(statusCode)
|
||||
if err = json.Unmarshal(body, &response); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
var _ voki.Requester[MembresGETResponse] = MembresGETRequest{}
|
||||
|
||||
type MembresGETRequest struct {
|
||||
Query struct {
|
||||
Limit int `json:"limit" query:"limit"`
|
||||
}
|
||||
}
|
||||
|
||||
func (request MembresGETRequest) Complete() bool { return true }
|
||||
|
||||
func (request MembresGETRequest) Request(v *voki.Voki) (response MembresGETResponse, err error) {
|
||||
if !request.Complete() {
|
||||
err = fmt.Errorf("Incomplete MembresGETRequest")
|
||||
return
|
||||
}
|
||||
|
||||
statusCode, body, err := v.CallAndParse(
|
||||
http.MethodGet,
|
||||
fmt.Sprintf("/api/v9/membre/?limit=%d", request.Query.Limit),
|
||||
nil,
|
||||
true,
|
||||
)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("code=%d err=%s", statusCode, err)
|
||||
return
|
||||
}
|
||||
response.SetStatusCode(statusCode)
|
||||
if err = json.Unmarshal(body, &response); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
var _ voki.Requester[MembrePreferedNamePUTResponse] = MembrePreferedNamePUTRequest{}
|
||||
|
||||
type MembrePreferedNamePUTRequest struct {
|
||||
Data struct {
|
||||
PreferedName string `json:"prefered_name"`
|
||||
} `json:"data"`
|
||||
Param struct {
|
||||
MembreID string `json:"membre_id" param:"membre_id"`
|
||||
} `json:"param"`
|
||||
}
|
||||
|
||||
func (request MembrePreferedNamePUTRequest) Complete() bool {
|
||||
return IsMembreID(request.Param.MembreID) && len(request.Data.PreferedName) != 0
|
||||
}
|
||||
|
||||
func (request MembrePreferedNamePUTRequest) Request(v *voki.Voki) (response MembrePreferedNamePUTResponse, err error) {
|
||||
if !request.Complete() {
|
||||
err = fmt.Errorf("Incomplete MembrePreferedNamePUTRequest")
|
||||
return
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err = json.NewEncoder(&buf).Encode(request.Data); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
statusCode, body, err := v.CallAndParse(
|
||||
http.MethodPut,
|
||||
fmt.Sprintf("/api/v9/membre/%s/prefered_name/", request.Param.MembreID),
|
||||
&buf,
|
||||
true,
|
||||
)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("code=%d err=%s", statusCode, err)
|
||||
return
|
||||
}
|
||||
response.SetStatusCode(statusCode)
|
||||
if err = json.Unmarshal(body, &response); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
var _ voki.Requester[ProgrammesGETResponse] = ProgrammesGETRequest{}
|
||||
|
||||
type ProgrammesGETRequest struct {
|
||||
Query struct {
|
||||
Limit int `json:"limit" query:"limit"`
|
||||
}
|
||||
}
|
||||
|
||||
func (request ProgrammesGETRequest) Complete() bool { return true }
|
||||
|
||||
func (request ProgrammesGETRequest) Request(v *voki.Voki) (response ProgrammesGETResponse, err error) {
|
||||
if !request.Complete() {
|
||||
err = fmt.Errorf("Incomplete ProgrammesGETRequest")
|
||||
return
|
||||
}
|
||||
|
||||
statusCode, body, err := v.CallAndParse(
|
||||
http.MethodGet,
|
||||
fmt.Sprintf("/api/v9/programme/?limit=%d", request.Query.Limit),
|
||||
nil,
|
||||
true,
|
||||
)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("code=%d err=%s", statusCode, err)
|
||||
return
|
||||
}
|
||||
response.SetStatusCode(statusCode)
|
||||
if err = json.Unmarshal(body, &response); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
var _ voki.Requester[MembresDisplayGETResponse] = MembresDisplayGETRequest{}
|
||||
|
||||
type MembresDisplayGETRequest struct {
|
||||
Query struct {
|
||||
Limit int `json:"limit" query:"limit"`
|
||||
}
|
||||
}
|
||||
|
||||
func (request MembresDisplayGETRequest) Complete() bool { return true }
|
||||
|
||||
func (request MembresDisplayGETRequest) Request(v *voki.Voki) (response MembresDisplayGETResponse, err error) {
|
||||
if !request.Complete() {
|
||||
err = fmt.Errorf("Incomplete MembresDisplayGETRequest")
|
||||
return
|
||||
}
|
||||
|
||||
statusCode, body, err := v.CallAndParse(
|
||||
http.MethodGet,
|
||||
fmt.Sprintf("/api/v9/membre/display/?limit=%d", request.Query.Limit),
|
||||
nil,
|
||||
true,
|
||||
)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("code=%d err=%s", statusCode, err)
|
||||
return
|
||||
}
|
||||
response.SetStatusCode(statusCode)
|
||||
if err = json.Unmarshal(body, &response); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
var _ voki.Requester[MembreDisplayGETResponse] = MembreDisplayGETRequest{}
|
||||
|
||||
type MembreDisplayGETRequest struct {
|
||||
Param struct {
|
||||
MembreID string `json:"membre_id" param:"membre_id"`
|
||||
}
|
||||
}
|
||||
|
||||
func (request MembreDisplayGETRequest) Complete() bool {
|
||||
return request.Param.MembreID != ""
|
||||
}
|
||||
|
||||
func (request MembreDisplayGETRequest) Request(v *voki.Voki) (response MembreDisplayGETResponse, err error) {
|
||||
if !request.Complete() {
|
||||
err = fmt.Errorf("Incomplete MembreDisplayGETRequest")
|
||||
return
|
||||
}
|
||||
|
||||
if id := request.Param.MembreID; !IsMembreID(id) {
|
||||
err = fmt.Errorf("MembreID '%s' invalide", id)
|
||||
return
|
||||
}
|
||||
|
||||
statusCode, body, err := v.CallAndParse(
|
||||
http.MethodGet,
|
||||
fmt.Sprintf("/api/v9/membre/%s/display/", request.Param.MembreID),
|
||||
nil,
|
||||
true,
|
||||
)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("code=%d err=%s", statusCode, err)
|
||||
return
|
||||
}
|
||||
response.SetStatusCode(statusCode)
|
||||
if err = json.Unmarshal(body, &response); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
91
pkg/bottin/response.go
Normal file
91
pkg/bottin/response.go
Normal file
|
@ -0,0 +1,91 @@
|
|||
package bottin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"codeberg.org/vlbeaudoin/voki/v3"
|
||||
)
|
||||
|
||||
type APIResponse struct {
|
||||
voki.MessageResponse
|
||||
statusCode int
|
||||
}
|
||||
|
||||
func (R APIResponse) StatusCode() int { return R.statusCode }
|
||||
|
||||
func (R *APIResponse) SetStatusCode(code int) error {
|
||||
if code <= 0 {
|
||||
return fmt.Errorf("Cannot set status code to %d", code)
|
||||
}
|
||||
R.statusCode = code
|
||||
return nil
|
||||
}
|
||||
|
||||
type HealthGETResponse struct {
|
||||
APIResponse
|
||||
}
|
||||
|
||||
type MembreGETResponse struct {
|
||||
APIResponse
|
||||
Data MembreGETResponseData `json:"data"`
|
||||
}
|
||||
type MembreGETResponseData struct {
|
||||
Membre Membre `json:"membre"`
|
||||
}
|
||||
|
||||
type MembrePreferedNamePUTResponse struct {
|
||||
APIResponse
|
||||
}
|
||||
|
||||
type MembresGETResponse struct {
|
||||
APIResponse
|
||||
Data MembresGETResponseData `json:"data"`
|
||||
}
|
||||
|
||||
type MembresGETResponseData struct {
|
||||
Membres []Membre `json:"membres"`
|
||||
}
|
||||
|
||||
type MembreDisplayGETResponse struct {
|
||||
APIResponse
|
||||
Data MembreDisplayGETResponseData `json:"data"`
|
||||
}
|
||||
type MembreDisplayGETResponseData struct {
|
||||
Membre MembreForDisplay `json:"membre_for_display"`
|
||||
}
|
||||
|
||||
type MembresDisplayGETResponse struct {
|
||||
APIResponse
|
||||
Data MembresDisplayGETResponseData `json:"data"`
|
||||
}
|
||||
|
||||
type MembresDisplayGETResponseData struct {
|
||||
Membres []MembreForDisplay `json:"membres_for_display"`
|
||||
}
|
||||
|
||||
type MembresPOSTResponse struct {
|
||||
APIResponse
|
||||
Data MembresPOSTResponseData `json:"data"`
|
||||
}
|
||||
|
||||
type MembresPOSTResponseData struct {
|
||||
MembresInserted int64 `json:"membres_inserted"`
|
||||
}
|
||||
|
||||
type ProgrammesPOSTResponse struct {
|
||||
APIResponse
|
||||
Data ProgrammesPOSTResponseData `json:"data"`
|
||||
}
|
||||
|
||||
type ProgrammesPOSTResponseData struct {
|
||||
ProgrammesInserted int64 `json:"programmes_inserted"`
|
||||
}
|
||||
|
||||
type ProgrammesGETResponse struct {
|
||||
APIResponse
|
||||
Data ProgrammesGETResponseData `json:"data"`
|
||||
}
|
||||
|
||||
type ProgrammesGETResponseData struct {
|
||||
Programmes []Programme `json:"programmes"`
|
||||
}
|
465
pkg/bottin/routes.go
Normal file
465
pkg/bottin/routes.go
Normal file
|
@ -0,0 +1,465 @@
|
|||
package bottin
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"codeberg.org/vlbeaudoin/pave/v2"
|
||||
"codeberg.org/vlbeaudoin/voki/v3"
|
||||
"github.com/gocarina/gocsv"
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
func AddRoutes(e *echo.Echo, db *PostgresClient, cfg Config) error {
|
||||
_ = db
|
||||
_ = cfg
|
||||
|
||||
apiPath := "/api/v9"
|
||||
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
|
||||
}
|
||||
|
||||
if err := pave.EchoRegister[ProgrammesPOSTRequest](
|
||||
apiGroup,
|
||||
&p,
|
||||
apiPath,
|
||||
http.MethodPost,
|
||||
"/programme/",
|
||||
"Insert programmes",
|
||||
"ProgrammesPOST", func(c echo.Context) error {
|
||||
var request, response = ProgrammesPOSTRequest{}, ProgrammesPOSTResponse{}
|
||||
|
||||
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:
|
||||
var response voki.ResponseBadRequest
|
||||
response.Message = fmt.Sprintf("cannot parse body with content-type: %s", contentType)
|
||||
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
|
||||
}
|
||||
|
||||
if err := pave.EchoRegister[MembresPOSTRequest](
|
||||
apiGroup,
|
||||
&p,
|
||||
apiPath,
|
||||
http.MethodPost,
|
||||
"/membre/",
|
||||
"Insert membres",
|
||||
"MembresPOST", func(c echo.Context) error {
|
||||
var request, response = MembresPOSTRequest{}, MembresPOSTResponse{}
|
||||
|
||||
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:
|
||||
var response voki.ResponseBadRequest
|
||||
response.Message = fmt.Sprintf("cannot parse body with content-type: %s", contentType)
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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{}
|
||||
|
||||
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.Server.API.DefaultLimit
|
||||
//TODO cfg.Client.API.Limit
|
||||
request.Query.Limit = 1000
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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{}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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{}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
Reference in a new issue