From e4ff1013d04288a8fbb3710506cebfb3b9b7cd8a Mon Sep 17 00:00:00 2001 From: Victor Lacasse-Beaudoin Date: Thu, 20 Jun 2024 18:51:38 -0400 Subject: [PATCH] feature: ajouter et tester GetMembre[s]ForDisplay MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Priorisent le prefered_name ("nom d'usage") et devraient être utilisés aux endroits où l'affichage est important. --- client.go | 35 +++++++++++++++++++++ client_test.go | 18 +++++++++++ db.go | 80 ++++++++++++++++++++++++++++++++++++++++++++++++ entity.go | 8 +++++ request.go | 75 +++++++++++++++++++++++++++++++++++++++++++++ response.go | 17 +++++++++++ routes.go | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 315 insertions(+) diff --git a/client.go b/client.go index 91cdbe7..e4958a6 100644 --- a/client.go +++ b/client.go @@ -133,3 +133,38 @@ func (c APIClient) UpdateMembrePreferedName(membreID string, name string) (err e 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 +} diff --git a/client_test.go b/client_test.go index 1a02a15..a2aea93 100644 --- a/client_test.go +++ b/client_test.go @@ -122,4 +122,22 @@ func TestAPI(t *testing.T) { } t.Log(membres) }) + + t.Run("get membre for display", + func(t *testing.T) { + membre, err := apiClient.GetMembreForDisplay(testMembres[0].ID) + if err != nil { + t.Error(err) + } + t.Log(membre) + }) + + t.Run("get membres for display, max 5", + func(t *testing.T) { + membres, err := apiClient.GetMembresForDisplay(5) + if err != nil { + t.Error(err) + } + t.Log(membres) + }) } diff --git a/db.go b/db.go index 2608940..915ddab 100644 --- a/db.go +++ b/db.go @@ -272,3 +272,83 @@ WHERE } 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 { + 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 + } +} diff --git a/entity.go b/entity.go index b919c29..672f54c 100644 --- a/entity.go +++ b/entity.go @@ -15,6 +15,14 @@ type Membre struct { 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 diff --git a/request.go b/request.go index 97f51de..8f47f10 100644 --- a/request.go +++ b/request.go @@ -273,3 +273,78 @@ func (request ProgrammesGETRequest) Request(v *voki.Voki) (response ProgrammesGE 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/v7/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/v7/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 +} diff --git a/response.go b/response.go index 9f0d076..19818df 100644 --- a/response.go +++ b/response.go @@ -46,6 +46,23 @@ 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"` diff --git a/routes.go b/routes.go index 355dd8f..a5aaed6 100644 --- a/routes.go +++ b/routes.go @@ -297,5 +297,87 @@ func addRoutes(e *echo.Echo, db *PostgresClient) error { 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{} + + request.Query.Limit, err = strconv.Atoi(c.QueryParam("limit")) + if err != nil { + var response voki.ResponseBadRequest + response.Message = fmt.Sprintf("parsing limit: %s", err) + return c.JSON(response.StatusCode(), response) + } + + 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 }