From c850b221a1029eaf0bfd5ef34b1f0fba0c9debdb Mon Sep 17 00:00:00 2001 From: Victor Lacasse-Beaudoin Date: Fri, 9 Jun 2023 01:09:02 -0400 Subject: [PATCH 1/4] =?UTF-8?q?Impl=C3=A9menter=20client=20web=20de=20base?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Déplacer tous les flags vers rootCmd.PersistentFlags() Ajouter config struct types à models/ Ajouter data/apiclient.go#ApiClient.GetHealth() Ajouter webCmd avec viper.Unmarshal() pour valeurs de config Ajouter package web depuis agecem/bottin --- cmd/api.go | 56 ----------------- cmd/root.go | 98 ++++++++++++++++++++++++++++++ cmd/web.go | 90 +++++++++++++++++++++++++++ data/apiclient.go | 32 ++++++++++ models/models.go | 41 +++++++++++++ web/embed.go | 10 +++ web/templates/index.html | 117 ++++++++++++++++++++++++++++++++++++ web/webhandlers/handlers.go | 43 +++++++++++++ 8 files changed, 431 insertions(+), 56 deletions(-) create mode 100644 cmd/web.go create mode 100644 web/embed.go create mode 100644 web/templates/index.html create mode 100644 web/webhandlers/handlers.go diff --git a/cmd/api.go b/cmd/api.go index 7297270..224df3c 100644 --- a/cmd/api.go +++ b/cmd/api.go @@ -93,60 +93,4 @@ var apiCmd = &cobra.Command{ func init() { rootCmd.AddCommand(apiCmd) - - // api.key - apiCmd.Flags().String( - "api-key", "bottin-agenda", - "API server key. Leave empty for no key auth. (config: 'api.key')") - viper.BindPFlag("api.key", apiCmd.Flags().Lookup("api-key")) - - // api.port - apiCmd.Flags().Int( - "api-port", 1313, - "API server port (config:'api.port')") - viper.BindPFlag("api.port", apiCmd.Flags().Lookup("api-port")) - - // bottin.api.host - apiCmd.Flags().String( - "bottin-api-host", "api", - "Remote bottin API server host (config:'bottin.api.host')") - viper.BindPFlag("bottin.api.host", apiCmd.Flags().Lookup("bottin-api-host")) - - // bottin.api.key - apiCmd.Flags().String( - "bottin-api-key", "bottin", - "Remote bottin API server key (config:'bottin.api.key')") - viper.BindPFlag("bottin.api.key", apiCmd.Flags().Lookup("bottin-api-key")) - - // bottin.api.protocol - apiCmd.Flags().String( - "bottin-api-protocol", "http", - "Remote bottin API server protocol (config:'bottin.api.protocol')") - viper.BindPFlag("bottin.api.protocol", apiCmd.Flags().Lookup("bottin-api-protocol")) - - // bottin.api.port - apiCmd.Flags().Int( - "bottin-api-port", 1312, - "Remote bottin API server port (config:'bottin.api.port')") - viper.BindPFlag("bottin.api.port", apiCmd.Flags().Lookup("bottin-api-port")) - - // db.database - apiCmd.Flags().String("db-database", "bottin-agenda", "Postgres database (config:'db.database')") - viper.BindPFlag("db.database", apiCmd.Flags().Lookup("db-database")) - - // db.host - apiCmd.Flags().String("db-host", "db", "Postgres host (config:'db.host')") - viper.BindPFlag("db.host", apiCmd.Flags().Lookup("db-host")) - - // db.password - apiCmd.Flags().String("db-password", "bottin-agenda", "Postgres password (config:'db.password')") - viper.BindPFlag("db.password", apiCmd.Flags().Lookup("db-password")) - - // db.port - apiCmd.Flags().Int("db-port", 5432, "Postgres port (config:'db.port')") - viper.BindPFlag("db.port", apiCmd.Flags().Lookup("db-port")) - - // db.user - apiCmd.Flags().String("db-user", "bottin-agenda", "Postgres user (config:'db.user')") - viper.BindPFlag("db.user", apiCmd.Flags().Lookup("db-user")) } diff --git a/cmd/root.go b/cmd/root.go index 7fa76ca..b75fdbc 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -29,6 +29,104 @@ func init() { cobra.OnInitialize(initConfig) rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.bottin-agenda.yaml)") + + // web.api.host + rootCmd.PersistentFlags().String( + "web-api-host", "api", + "Remote API server host (config:'web.api.host')") + viper.BindPFlag("web.api.host", rootCmd.PersistentFlags().Lookup("web-api-host")) + + // web.api.key + rootCmd.PersistentFlags().String( + "web-api-key", "bottin-agenda", + "Remote API server key (config:'web.api.key')") + viper.BindPFlag("web.api.key", rootCmd.PersistentFlags().Lookup("web-api-key")) + + // web.api.protocol + rootCmd.PersistentFlags().String( + "web-api-protocol", "http", + "Remote API server protocol (config:'web.api.protocol')") + viper.BindPFlag("web.api.protocol", rootCmd.PersistentFlags().Lookup("web-api-protocol")) + + // web.api.port + rootCmd.PersistentFlags().Int( + "web-api-port", 1313, + "Remote API server port (config:'web.api.port')") + viper.BindPFlag("web.api.port", rootCmd.PersistentFlags().Lookup("web-api-port")) + + // web.password + rootCmd.PersistentFlags().String( + "web-password", "bottin-agenda", + "Web client password (config:'web.password')") + viper.BindPFlag("web.password", rootCmd.PersistentFlags().Lookup("web-password")) + + // web.port + rootCmd.PersistentFlags().Int( + "web-port", 2313, + "Web client port (config:'web.port')") + viper.BindPFlag("web.port", rootCmd.PersistentFlags().Lookup("web-port")) + + // web.user + rootCmd.PersistentFlags().String( + "web-user", "bottin-agenda", + "Web client user (config:'web.user')") + viper.BindPFlag("web.user", rootCmd.PersistentFlags().Lookup("web-user")) + + // api.key + rootCmd.PersistentFlags().String( + "api-key", "bottin-agenda", + "API server key. Leave empty for no key auth. (config: 'api.key')") + viper.BindPFlag("api.key", rootCmd.PersistentFlags().Lookup("api-key")) + + // api.port + rootCmd.PersistentFlags().Int( + "api-port", 1313, + "API server port (config:'api.port')") + viper.BindPFlag("api.port", rootCmd.PersistentFlags().Lookup("api-port")) + + // bottin.api.host + rootCmd.PersistentFlags().String( + "bottin-api-host", "api", + "Remote bottin API server host (config:'bottin.api.host')") + viper.BindPFlag("bottin.api.host", rootCmd.PersistentFlags().Lookup("bottin-api-host")) + + // bottin.api.key + rootCmd.PersistentFlags().String( + "bottin-api-key", "bottin", + "Remote bottin API server key (config:'bottin.api.key')") + viper.BindPFlag("bottin.api.key", rootCmd.PersistentFlags().Lookup("bottin-api-key")) + + // bottin.api.protocol + rootCmd.PersistentFlags().String( + "bottin-api-protocol", "http", + "Remote bottin API server protocol (config:'bottin.api.protocol')") + viper.BindPFlag("bottin.api.protocol", rootCmd.PersistentFlags().Lookup("bottin-api-protocol")) + + // bottin.api.port + rootCmd.PersistentFlags().Int( + "bottin-api-port", 1312, + "Remote bottin API server port (config:'bottin.api.port')") + viper.BindPFlag("bottin.api.port", rootCmd.PersistentFlags().Lookup("bottin-api-port")) + + // db.database + rootCmd.PersistentFlags().String("db-database", "bottin-agenda", "Postgres database (config:'db.database')") + viper.BindPFlag("db.database", rootCmd.PersistentFlags().Lookup("db-database")) + + // db.host + rootCmd.PersistentFlags().String("db-host", "db", "Postgres host (config:'db.host')") + viper.BindPFlag("db.host", rootCmd.PersistentFlags().Lookup("db-host")) + + // db.password + rootCmd.PersistentFlags().String("db-password", "bottin-agenda", "Postgres password (config:'db.password')") + viper.BindPFlag("db.password", rootCmd.PersistentFlags().Lookup("db-password")) + + // db.port + rootCmd.PersistentFlags().Int("db-port", 5432, "Postgres port (config:'db.port')") + viper.BindPFlag("db.port", rootCmd.PersistentFlags().Lookup("db-port")) + + // db.user + rootCmd.PersistentFlags().String("db-user", "bottin-agenda", "Postgres user (config:'db.user')") + viper.BindPFlag("db.user", rootCmd.PersistentFlags().Lookup("db-user")) } // initConfig reads in config file and ENV variables if set. diff --git a/cmd/web.go b/cmd/web.go new file mode 100644 index 0000000..45274da --- /dev/null +++ b/cmd/web.go @@ -0,0 +1,90 @@ +package cmd + +import ( + "crypto/subtle" + "embed" + "fmt" + "html/template" + "io" + "log" + + "git.agecem.com/agecem/bottin-agenda/data" + "git.agecem.com/agecem/bottin-agenda/models" + "git.agecem.com/agecem/bottin-agenda/web" + "git.agecem.com/agecem/bottin-agenda/web/webhandlers" + "github.com/labstack/echo/v4" + "github.com/labstack/echo/v4/middleware" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +var templatesFS embed.FS + +type Template struct { + templates *template.Template +} + +func (t *Template) Render(w io.Writer, name string, data interface{}, c echo.Context) error { + return t.templates.ExecuteTemplate(w, name, data) +} + +// webCmd represents the web command +var webCmd = &cobra.Command{ + Use: "web", + Short: "Démarrer le client web", + Args: cobra.ExactArgs(0), + Run: func(cmd *cobra.Command, args []string) { + var config models.Config + + err := viper.Unmarshal(&config) + if err != nil { + log.Fatalf("Error during webCmd#viper.Unmarshal(): %s", err) + } + + // Ping API server + + apiClient := data.NewApiClient(config.Web.Api.Key, config.Web.Api.Host, config.Web.Api.Protocol, config.Web.Api.Port) + + healthResult, err := apiClient.GetHealth() + if err != nil { + log.Fatalf("Error during webCmd#apiClient.GetHealth(): %s", err) + } + + log.Println(healthResult) + + e := echo.New() + + // Middlewares + + e.Pre(middleware.AddTrailingSlash()) + + e.Use(middleware.BasicAuth(func(user, password string, c echo.Context) (bool, error) { + usersMatch := subtle.ConstantTimeCompare([]byte(user), []byte(config.Web.User)) == 1 + passwordsMatch := subtle.ConstantTimeCompare([]byte(password), []byte(config.Web.Password)) == 1 + return usersMatch && passwordsMatch, nil + })) + + // Template + + t := &Template{ + templates: template.Must(template.ParseFS(templatesFS, "templates/*.html")), + } + + e.Renderer = t + + // Routes + + e.GET("/", webhandlers.GetIndex) + //e.POST("/transaction", webhandlers.PostTransaction) + + // Execution + + e.Logger.Fatal(e.Start( + fmt.Sprintf(":%d", config.Web.Port))) + }, +} + +func init() { + rootCmd.AddCommand(webCmd) + templatesFS = web.GetTemplates() +} diff --git a/data/apiclient.go b/data/apiclient.go index 6132a12..f56979f 100644 --- a/data/apiclient.go +++ b/data/apiclient.go @@ -1,9 +1,14 @@ package data import ( + "encoding/json" + "errors" "fmt" "io" + "io/ioutil" "net/http" + + "git.agecem.com/agecem/bottin-agenda/responses" ) type ApiClient struct { @@ -58,3 +63,30 @@ func (a *ApiClient) Call(method, route string, requestBody io.Reader, useKey boo return response, nil } + +// GetHealth allows checking for API server health +func (a *ApiClient) GetHealth() (string, error) { + var response responses.GetHealthResponse + + getHealthResponse, err := a.Call(http.MethodGet, "/v3/health", nil, true) + if err != nil { + return response.Message, err + } + + defer getHealthResponse.Body.Close() + + body, err := ioutil.ReadAll(getHealthResponse.Body) + if err != nil { + return response.Message, err + } + + if err := json.Unmarshal(body, &response); err != nil { + return response.Message, err + } + + if response.Message == "" { + return response.Message, errors.New("Could not confirm that API server is up, no response message") + } + + return response.Message, nil +} diff --git a/models/models.go b/models/models.go index ac30776..691afc0 100644 --- a/models/models.go +++ b/models/models.go @@ -17,3 +17,44 @@ type Transaction struct { GivenAt *time.Time `db:"given_at" json:"given_at"` IsPerpetual bool `db:"is_perpetual" json:"is_perpetual"` } + +type Config struct { + Web WebConfig `json:"web"` + Api ApiConfig `json:"api"` + Bottin BottinConfig `json:"bottin"` + Db DbConfig `json:"db"` +} + +type ApiConfig struct { + Key string `json:"key"` + Port int `json:"port"` +} + +type BottinConfig struct { + Api struct { + Host string `json:"host"` + Key string `json:"key"` + Protocol string `json:"protocol"` + Port int `json:"port"` + } `json:"api"` +} + +type DbConfig struct { + Database string `json:"database"` + Host string `json:"host"` + Password string `json:"password"` + Port int `json:"port"` + User string `json:"user"` +} + +type WebConfig struct { + Api struct { + Host string `json:"host"` + Key string `json:"key"` + Port int `json:"port"` + Protocol string `json:"protocol"` + } `json:"api"` + Password string `json:"password"` + Port int `json:"port"` + User string `json:"user"` +} diff --git a/web/embed.go b/web/embed.go new file mode 100644 index 0000000..465e5ec --- /dev/null +++ b/web/embed.go @@ -0,0 +1,10 @@ +package web + +import "embed" + +//go:embed templates/* +var templatesFS embed.FS + +func GetTemplates() embed.FS { + return templatesFS +} diff --git a/web/templates/index.html b/web/templates/index.html new file mode 100644 index 0000000..af30688 --- /dev/null +++ b/web/templates/index.html @@ -0,0 +1,117 @@ +{{ define "index-html" }} + + + + + + AGECEM | Agenda + + + + + + +

+ Distribution d'agendas aux membres de l'AGECEM +

+ +

+ 1) Si l'agenda demandé est perpétuel, cochez 'Perpétuel?:'
+
+ 2) Sélectionnez le champs 'Numéro étudiant:'
+
+ 3) Scannez la carte étudiante d'unE membre
+ -ou-
+ Entrez manuellement le code à 7 chiffres
+
+ 4) Si aucune erreur ne survient, la personne est libre de partir avec son agenda +

+ +
+ +
+ +

{{ .Result }}

+ + + +{{ end }} diff --git a/web/webhandlers/handlers.go b/web/webhandlers/handlers.go new file mode 100644 index 0000000..dce8399 --- /dev/null +++ b/web/webhandlers/handlers.go @@ -0,0 +1,43 @@ +package webhandlers + +import ( + "fmt" + "net/http" + + "git.agecem.com/agecem/bottin/v5/data" + "github.com/labstack/echo/v4" +) + +func GetIndex(c echo.Context) error { + return c.Render(http.StatusOK, "index-html", nil) +} + +func GetMembre(c echo.Context) error { + apiClient := data.NewApiClientFromViper() + + membreID := c.QueryParam("membre_id") + + membre, err := apiClient.GetMembre(membreID) + if err != nil { + return c.Render(http.StatusBadRequest, "index-html", struct { + Result string + }{ + Result: fmt.Sprintln("👎", err.Error()), + }) + } + + membreResult := fmt.Sprintf(`👍 + Membre trouvéE: [%s]`, membre.ID) + + if membre.PreferedName != "" { + membreResult = fmt.Sprintf("%s -> %s", membreResult, membre.PreferedName) + } else { + membreResult = fmt.Sprintf("%s -> %s, %s", membreResult, membre.LastName, membre.FirstName) + } + + return c.Render(http.StatusOK, "index-html", struct { + Result string + }{ + Result: membreResult, + }) +} From 2287f00e2926531bbcfd8ef5cce91a642a0c79ce Mon Sep 17 00:00:00 2001 From: Victor Lacasse-Beaudoin Date: Fri, 9 Jun 2023 01:11:55 -0400 Subject: [PATCH 2/4] Retirer webhandlers#GetMembre() --- web/webhandlers/handlers.go | 32 -------------------------------- 1 file changed, 32 deletions(-) diff --git a/web/webhandlers/handlers.go b/web/webhandlers/handlers.go index dce8399..8376dd8 100644 --- a/web/webhandlers/handlers.go +++ b/web/webhandlers/handlers.go @@ -1,43 +1,11 @@ package webhandlers import ( - "fmt" "net/http" - "git.agecem.com/agecem/bottin/v5/data" "github.com/labstack/echo/v4" ) func GetIndex(c echo.Context) error { return c.Render(http.StatusOK, "index-html", nil) } - -func GetMembre(c echo.Context) error { - apiClient := data.NewApiClientFromViper() - - membreID := c.QueryParam("membre_id") - - membre, err := apiClient.GetMembre(membreID) - if err != nil { - return c.Render(http.StatusBadRequest, "index-html", struct { - Result string - }{ - Result: fmt.Sprintln("👎", err.Error()), - }) - } - - membreResult := fmt.Sprintf(`👍 - Membre trouvéE: [%s]`, membre.ID) - - if membre.PreferedName != "" { - membreResult = fmt.Sprintf("%s -> %s", membreResult, membre.PreferedName) - } else { - membreResult = fmt.Sprintf("%s -> %s, %s", membreResult, membre.LastName, membre.FirstName) - } - - return c.Render(http.StatusOK, "index-html", struct { - Result string - }{ - Result: membreResult, - }) -} From 660d8826e2402b1bfbce3595950c2e5c8149c061 Mon Sep 17 00:00:00 2001 From: Victor Lacasse-Beaudoin Date: Fri, 9 Jun 2023 23:52:03 -0400 Subject: [PATCH 3/4] =?UTF-8?q?Impl=C3=A9menter=20POST=20/transaction?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ajouter data#ApiClient.InsertTransactions() Fix form action Ajouter séparateur entre description et formulaire Ajouter webhandlers#PostTransaction et PostTransactionResult --- cmd/api.go | 1 + cmd/web.go | 2 +- data/apiclient.go | 30 +++++++++++++++++ web/templates/index.html | 4 ++- web/webhandlers/handlers.go | 65 +++++++++++++++++++++++++++++++++++++ 5 files changed, 100 insertions(+), 2 deletions(-) diff --git a/cmd/api.go b/cmd/api.go index 224df3c..ad702dc 100644 --- a/cmd/api.go +++ b/cmd/api.go @@ -25,6 +25,7 @@ var apiCmd = &cobra.Command{ Short: "Démarrer le serveur API", Args: cobra.ExactArgs(0), Run: func(cmd *cobra.Command, args []string) { + // TODO migrer à viper.Unmarshal(&models.Config) apiKey = viper.GetString("api.key") apiPort = viper.GetInt("api.port") diff --git a/cmd/web.go b/cmd/web.go index 45274da..ed1d7b3 100644 --- a/cmd/web.go +++ b/cmd/web.go @@ -75,7 +75,7 @@ var webCmd = &cobra.Command{ // Routes e.GET("/", webhandlers.GetIndex) - //e.POST("/transaction", webhandlers.PostTransaction) + e.POST("/transaction/", webhandlers.PostTransaction) // Execution diff --git a/data/apiclient.go b/data/apiclient.go index f56979f..0971ba3 100644 --- a/data/apiclient.go +++ b/data/apiclient.go @@ -1,6 +1,7 @@ package data import ( + "bytes" "encoding/json" "errors" "fmt" @@ -8,6 +9,7 @@ import ( "io/ioutil" "net/http" + "git.agecem.com/agecem/bottin-agenda/models" "git.agecem.com/agecem/bottin-agenda/responses" ) @@ -90,3 +92,31 @@ func (a *ApiClient) GetHealth() (string, error) { return response.Message, nil } + +func (a *ApiClient) InsertTransactions(transactions []models.Transaction) ([]models.Transaction, error) { + var response responses.PostTransactionsResponse + + var buf bytes.Buffer + err := json.NewEncoder(&buf).Encode(transactions) + if err != nil { + return response.Data.Transactions, err + } + + postHealthResponse, err := a.Call(http.MethodPost, "/v3/transactions", &buf, true) + defer postHealthResponse.Body.Close() + + body, err := ioutil.ReadAll(postHealthResponse.Body) + if err != nil { + return response.Data.Transactions, err + } + + if err := json.Unmarshal(body, &response); err != nil { + return response.Data.Transactions, err + } + + if len(response.Data.Transactions) == 0 { + return response.Data.Transactions, fmt.Errorf(response.Message) + } + + return response.Data.Transactions, nil +} diff --git a/web/templates/index.html b/web/templates/index.html index af30688..9750a38 100644 --- a/web/templates/index.html +++ b/web/templates/index.html @@ -94,7 +94,9 @@ button { 4) Si aucune erreur ne survient, la personne est libre de partir avec son agenda

-
+
+ +
  • diff --git a/web/webhandlers/handlers.go b/web/webhandlers/handlers.go index 8376dd8..2db3120 100644 --- a/web/webhandlers/handlers.go +++ b/web/webhandlers/handlers.go @@ -1,11 +1,76 @@ package webhandlers import ( + "fmt" "net/http" + "git.agecem.com/agecem/bottin-agenda/data" + "git.agecem.com/agecem/bottin-agenda/models" "github.com/labstack/echo/v4" + "github.com/spf13/viper" ) +type PostTransactionResult struct { + Result string +} + func GetIndex(c echo.Context) error { return c.Render(http.StatusOK, "index-html", nil) } + +func PostTransaction(c echo.Context) error { + var config models.Config + var result PostTransactionResult + + err := viper.Unmarshal(&config) + if err != nil { + return c.Render(http.StatusInternalServerError, "index-html", PostTransactionResult{ + Result: fmt.Sprintln("👎", err.Error()), + }) + } + + // apiClient := data.NewApiClientkey host protocol port + apiClient := data.NewApiClient( + config.Web.Api.Key, + config.Web.Api.Host, + config.Web.Api.Protocol, + config.Web.Api.Port, + ) + + apiHealth, err := apiClient.GetHealth() + if err != nil { + result.Result = fmt.Sprintf("👎 %s | %s", apiHealth, err) + + return c.Render(http.StatusInternalServerError, "index-html", result) + } + + isPerpetual := c.FormValue("is_perpetual") == "on" + + membreId := c.FormValue("membre_id") + + if membreId == "" { + result.Result = "👎 Aucun numéro étudiant sélectionné. Assurez-vous de cliquer sur la case 'Numéro étudiant:' avant de scanner." + + return c.Render(http.StatusBadRequest, "index-html", result) + } + + transactions, err := apiClient.InsertTransactions( + []models.Transaction{{ + MembreID: membreId, + IsPerpetual: isPerpetual, + }}) + + if err != nil { + result.Result = fmt.Sprintf("👎 Erreur lors de l'insertion: %s", err) + + return c.Render(http.StatusInternalServerError, "index-html", result) + } + + agenda := "non-perpétuel" + if transactions[0].IsPerpetual { + agenda = "perpétuel" + } + result.Result = fmt.Sprintf("👍 Membre %s peut recevoir son agenda %s", transactions[0].MembreID, agenda) + + return c.Render(http.StatusOK, "index-html", result) +} From bacd4d737e8dee22955acc91ff23068d11b76cc4 Mon Sep 17 00:00:00 2001 From: Victor Lacasse-Beaudoin Date: Sat, 10 Jun 2023 00:07:51 -0400 Subject: [PATCH 4/4] =?UTF-8?q?Traduire=20messages=20d'erreurs=20en=20fran?= =?UTF-8?q?=C3=A7ais?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Il serait intéressant d'ajouter un méchanisme d'internationalisation des chaines. --- data/apiclient.go | 4 ++-- data/dataclient.go | 4 ++-- handlers/transaction.go | 32 ++++++++++++++++---------------- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/data/apiclient.go b/data/apiclient.go index 0971ba3..a1f6552 100644 --- a/data/apiclient.go +++ b/data/apiclient.go @@ -47,7 +47,7 @@ func (a *ApiClient) Call(method, route string, requestBody io.Reader, useKey boo if useKey { if a.Key == "" { - return response, fmt.Errorf("Call to API required a key but none was provided. See --help for instructions on providing an API key.") + return response, fmt.Errorf("L'appel au serveur API requiert une clé, mais aucune n'était donnée. Voir --help pour les instructions sur comment fournir une clé API.") } request.Header.Add("Authorization", fmt.Sprintf("Bearer %s", a.Key)) @@ -87,7 +87,7 @@ func (a *ApiClient) GetHealth() (string, error) { } if response.Message == "" { - return response.Message, errors.New("Could not confirm that API server is up, no response message") + return response.Message, errors.New("Impossible de confirmer l'état du serveur API.") } return response.Message, nil diff --git a/data/dataclient.go b/data/dataclient.go index 2398bd0..8715e89 100644 --- a/data/dataclient.go +++ b/data/dataclient.go @@ -87,7 +87,7 @@ func (d *DataClient) InsertTransactions(transactions []models.Transaction) ([]mo // Check values if transaction.MembreID == "" { tx.Rollback() - return rowsInserted, errors.New("Cannot insert transaction with no membre_id") + return rowsInserted, errors.New("Impossible d'insérer une transaction sans membre_id") } rows, err := tx.NamedQuery("INSERT INTO transactions (membre_id, given_at, is_perpetual) VALUES (:membre_id, current_timestamp, :is_perpetual) RETURNING id, membre_id, is_perpetual;", &transaction) @@ -126,7 +126,7 @@ func (d *DataClient) GetTransaction(membreID string, is_perpetual bool) (models. } if transaction.ID == "" { - return transaction, fmt.Errorf("No transaction found") + return transaction, fmt.Errorf("Aucune transaction trouvée") } return transaction, nil diff --git a/handlers/transaction.go b/handlers/transaction.go index ecd20d9..c6c6a15 100644 --- a/handlers/transaction.go +++ b/handlers/transaction.go @@ -19,7 +19,7 @@ func GetTransactions(c echo.Context) error { client, err := data.NewDataClientFromViper() if err != nil { - response.Message = fmt.Sprintf("Error during data.NewDataClientFromViper(): %s", err) + response.Message = fmt.Sprintf("Erreur pendant data.NewDataClientFromViper(): %s", err) return c.JSON(statusCode, response) } @@ -28,19 +28,19 @@ func GetTransactions(c echo.Context) error { transactions, err := client.ListTransactions() response.Data.Transactions = transactions if err != nil { - response.Message = fmt.Sprintf("Error during client.ListTransactions(): %s", err) + response.Message = fmt.Sprintf("Erreur pendant client.ListTransactions(): %s", err) return c.JSON(statusCode, response) } if len(transactions) == 0 { - response.Message = "No transactions found" + response.Message = "Aucune transaction trouvée" statusCode = http.StatusNotFound return c.JSON(statusCode, response) } - response.Message = "List successful" + response.Message = "Liste complétée" statusCode = http.StatusOK return c.JSON(statusCode, response) @@ -55,21 +55,21 @@ func PostTransactions(c echo.Context) error { if err := c.Bind(&transactions); err != nil { statusCode = http.StatusBadRequest - response.Message = fmt.Sprintf("Error during c.Bind(): %s", err) + response.Message = fmt.Sprintf("Erreur pendant c.Bind(): %s", err) return c.JSON(statusCode, response) } client, err := data.NewDataClientFromViper() if err != nil { - response.Message = fmt.Sprintf("Error during data.NewDataClientFromViper(): %s", err) + response.Message = fmt.Sprintf("Erreur pendant data.NewDataClientFromViper(): %s", err) return c.JSON(statusCode, response) } defer client.DB.Close() if len(transactions) == 0 { - response.Message = fmt.Sprintf("Nothing to do") + response.Message = fmt.Sprintf("Rien à faire") statusCode = http.StatusOK return c.JSON(statusCode, response) @@ -91,20 +91,20 @@ func PostTransactions(c echo.Context) error { // Check if membre_id exists according to bottin for _, transaction := range transactions { if transaction.MembreID == "" { - response.Message = fmt.Sprintf("Cannot insert transaction without a membre_id") + response.Message = fmt.Sprintf("Impossible d'insérer une transaction sans membre_id (numéro étudiant)") statusCode = http.StatusBadRequest return c.JSON(statusCode, response) } membre, err := bottinApiClient.GetMembre(transaction.MembreID) if err != nil { - response.Message = fmt.Sprintf("Error during bottinApiClient.GetMembre(): %s", err) + response.Message = fmt.Sprintf("Erreur pendant bottinApiClient.GetMembre(): %s", err) return c.JSON(statusCode, response) } if membre.ID == "" { - response.Message = fmt.Sprintf("Cannot insert transaction for non-existent membre %s", membre.ID) + response.Message = fmt.Sprintf("Aucun membre avec numéro étudiant '%s' dans le bottin de l'AGECEM", membre.ID) statusCode = http.StatusNotFound return c.JSON(statusCode, response) @@ -118,17 +118,17 @@ func PostTransactions(c echo.Context) error { transaction, err := client.GetTransaction(transaction.MembreID, transaction.IsPerpetual) if err != nil { if err.Error() != "sql: no rows in result set" { - response.Message = fmt.Sprintf("Error during client.GetTransaction(): %s", err) + response.Message = fmt.Sprintf("Erreur pendant client.GetTransaction(): %s", err) } } if transaction.ID != "" { - agendaType := "non-perpetual" + agendaType := "non-perpétuel" if transaction.IsPerpetual { - agendaType = "perpetual" + agendaType = "perpétuel" } - response.Message = fmt.Sprintf("Membre %s already received %s", transaction.MembreID, agendaType) + response.Message = fmt.Sprintf("Membre %s a déjà son agenda %s", transaction.MembreID, agendaType) statusCode = http.StatusBadRequest @@ -139,13 +139,13 @@ func PostTransactions(c echo.Context) error { insertedTransactions, err := client.InsertTransactions(transactions) response.Data.Transactions = insertedTransactions if err != nil { - response.Message = fmt.Sprintf("Error during client.InsertTransactions(): %s", err) + response.Message = fmt.Sprintf("Erreur pendant client.InsertTransactions(): %s", err) return c.JSON(statusCode, response) } statusCode = http.StatusCreated - response.Message = "Insert successful" + response.Message = "Insertion complétée" return c.JSON(statusCode, response) }