diff --git a/client.go b/client.go index 6221ce0..d47a723 100644 --- a/client.go +++ b/client.go @@ -58,3 +58,20 @@ func (c APIClient) InsertMembres(membres ...Membre) (amountInserted int64, err e 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 +} diff --git a/client_test.go b/client_test.go index 9d1212d..7a847ad 100644 --- a/client_test.go +++ b/client_test.go @@ -40,6 +40,7 @@ func TestAPI(t *testing.T) { }) //TODO create or replace schema + t.Run("insert programmes", func(t *testing.T) { programmes := []Programme{ @@ -52,31 +53,71 @@ func TestAPI(t *testing.T) { t.Error(err) } }) - //TODO insert membres + + testMembres := []Membre{ + { + ID: "0000000", + FirstName: "Test", + LastName: "User", + ProgrammeID: "404.42", + }, + { + ID: "1234567", + FirstName: "Deadname", + LastName: "User", + PreferedName: "User, Test-Name", + ProgrammeID: "200.10", + }, + } + t.Run("insert membres", func(t *testing.T) { - membres := []Membre{ - { - ID: "0000000", - FirstName: "Test", - LastName: "User", - ProgrammeID: "404.42", - }, - { - ID: "1234567", - FirstName: "Deadname", - LastName: "User", - PreferedName: "User, Test-Name", - ProgrammeID: "200.10", - }, - } - _, err := apiClient.InsertMembres(membres...) + _, err := apiClient.InsertMembres(testMembres...) if err != nil { t.Error(err) } }) - //TODO get membre - //TODO update membre prefered name - //TODO get membres + t.Run("get membre", + func(t *testing.T) { + membre, err := apiClient.GetMembre(testMembres[0].ID) + if err != nil { + t.Error(err) + } + + want := testMembres[0].LastName + got := membre.LastName + + if want != got { + t.Errorf("want=%s got=%s", want, got) + } + }) + + t.Run("get invalid membre", + func(t *testing.T) { + _, err := apiClient.GetMembre("invalid") + if err == nil { + t.Error("`invalid` should not have been accepted as value to GetMembre, but did") + } + }) + + //TODO update membre prefered name + /* + t.Run("", + func(t *testing.T) { + if err := apiClient.UpdateMembrePreferedName(testMembres[0].ID, "User, Galaxy"); err != nil { + t.Error(err) + } + }) + */ + //TODO get membres + /* + t.Run("get membres, max 50", + func(t *testing.T) { + membres, err := apiClient.GetMembres(50) + if err != nil { + t.Error(err) + } + }) + */ } diff --git a/db.go b/db.go index c2394e4..bb401ae 100644 --- a/db.go +++ b/db.go @@ -111,29 +111,42 @@ VALUES ($1, $2) ON CONFLICT DO NOTHING;`, } } -/* -func (d *PostgresClient) GetMembre(membreID string) (Membre, error) { - var membre Membre - - rows, err := d.Pool.Queryx("SELECT * FROM membres WHERE id = $1 LIMIT 1;", membreID) - if err != nil { - return membre, err - } - - for rows.Next() { - err := rows.StructScan(&membre) - if err != nil { - return membre, 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("No membre by that id was found") - } + if membre.ID == "" { + return membre, fmt.Errorf("Aucun membre trouvé avec numéro '%s'", membre.ID) + } - return membre, nil + return membre, nil + } } -*/ /* func (d *PostgresClient) UpdateMembreName(membreID, newName string) (int64, error) { diff --git a/entity.go b/entity.go index 385acde..b919c29 100644 --- a/entity.go +++ b/entity.go @@ -1,5 +1,7 @@ package main +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"` @@ -12,3 +14,17 @@ type Membre struct { PreferedName string `db:"prefered_name" json:"prefered_name" csv:"prefered_name"` ProgrammeID string `db:"programme_id" json:"programme_id" csv:"programme_id"` } + +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 +} diff --git a/request.go b/request.go index 720561b..277e6f4 100644 --- a/request.go +++ b/request.go @@ -23,7 +23,7 @@ func (request HealthGETRequest) Request(v *voki.Voki) (response HealthGETRespons statusCode, body, err := v.CallAndParse( http.MethodGet, - "/api/health/", + "/api/v7/health/", nil, true, ) @@ -64,7 +64,7 @@ func (request ProgrammesPOSTRequest) Request(v *voki.Voki) (response ProgrammesP statusCode, body, err := v.CallAndParse( http.MethodPost, - "/api/programmes/", + "/api/v7/programme/", &buf, true, ) @@ -105,7 +105,7 @@ func (request MembresPOSTRequest) Request(v *voki.Voki) (response MembresPOSTRes statusCode, body, err := v.CallAndParse( http.MethodPost, - "/api/membres/", + "/api/v7/membre/", &buf, true, ) @@ -120,3 +120,44 @@ func (request MembresPOSTRequest) Request(v *voki.Voki) (response MembresPOSTRes 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/v7/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 +} diff --git a/routes.go b/routes.go index 05c62b5..c5b3083 100644 --- a/routes.go +++ b/routes.go @@ -12,7 +12,7 @@ import ( func addRoutes(e *echo.Echo, db *PostgresClient) error { _ = db - apiPath := "/api" + apiPath := "/api/v7" apiGroup := e.Group(apiPath) p := pave.New() if err := pave.EchoRegister[HealthGETRequest]( @@ -47,7 +47,7 @@ func addRoutes(e *echo.Echo, db *PostgresClient) error { &p, apiPath, http.MethodPost, - "/programmes/", + "/programme/", "Insert programmes", "ProgrammesPOST", func(c echo.Context) error { var request, response = ProgrammesPOSTRequest{}, ProgrammesPOSTResponse{} @@ -90,7 +90,7 @@ func addRoutes(e *echo.Echo, db *PostgresClient) error { &p, apiPath, http.MethodPost, - "/membres/", + "/membre/", "Insert membres", "MembresPOST", func(c echo.Context) error { var request, response = MembresPOSTRequest{}, MembresPOSTResponse{} @@ -127,5 +127,44 @@ func addRoutes(e *echo.Echo, db *PostgresClient) error { }); 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 + } return nil }