From 3add628087d93fbe8eff3b1af9a521905f62b69e Mon Sep 17 00:00:00 2001 From: Hazem Krimi Date: Thu, 29 May 2025 21:22:28 +0100 Subject: [PATCH] wip: client api with net/http poc --- .gitignore | 4 +++ cmd/root.go | 17 ++++++----- internal/api/api.go | 16 +++++++++++ internal/api/client.go | 60 +++++++++++++++++++++++++++++++++++++++ internal/api/routes.go | 12 ++++++++ internal/models/client.go | 54 ++++++++++++++++++++++++++++------- internal/models/db.go | 4 ++- 7 files changed, 148 insertions(+), 19 deletions(-) create mode 100644 .gitignore create mode 100644 internal/api/api.go create mode 100644 internal/api/client.go create mode 100644 internal/api/routes.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a6eeafa --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.db +.air.toml + +/tmp diff --git a/cmd/root.go b/cmd/root.go index 0b912ea..71539f7 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -5,9 +5,10 @@ package cmd import ( "fmt" + "net/http" "os" - "github.com/hazemKrimi/crimson-vault/internal/models" + "github.com/hazemKrimi/crimson-vault/internal/api" "github.com/spf13/cobra" ) @@ -24,12 +25,13 @@ to quickly create a Cobra application.`, // Uncomment the following line if your bare application // has an action associated with it: Run: func(cmd *cobra.Command, args []string) { - wrapper := models.DBWrapper{} - wrapper.Initialize() - wrapper.MigrateClients() - wrapper.CreateClient("Hello", "World", "12345678") - client := wrapper.GetClient(1) - fmt.Println(fmt.Sprintf("Name: %s, Country: %s, Phone: %s", client.Name, client.Country, client.Phone)) + apiWrapper := api.APIWrapper{} + mux := http.NewServeMux() + + apiWrapper.Initialize() + mux.Handle("/clients/", api.ClientRoutes(&apiWrapper)) + fmt.Println("Server listening on PORT 5000...") + http.ListenAndServe(":5000", mux) }, } @@ -37,6 +39,7 @@ to quickly create a Cobra application.`, // This is called by main.main(). It only needs to happen once to the rootCmd. func Execute() { err := rootCmd.Execute() + if err != nil { os.Exit(1) } diff --git a/internal/api/api.go b/internal/api/api.go new file mode 100644 index 0000000..8a20904 --- /dev/null +++ b/internal/api/api.go @@ -0,0 +1,16 @@ +package api + +import "github.com/hazemKrimi/crimson-vault/internal/models" + +type APIWrapper struct { + dbWrapper *models.DBWrapper +} + +func (api *APIWrapper) Initialize() { + wrapper := models.DBWrapper{} + + wrapper.Connect() + wrapper.MigrateClients() + + api.dbWrapper = &wrapper; +} diff --git a/internal/api/client.go b/internal/api/client.go new file mode 100644 index 0000000..0ccb5dd --- /dev/null +++ b/internal/api/client.go @@ -0,0 +1,60 @@ +package api + +import ( + "encoding/json" + "fmt" + "log" + "net/http" + "strconv" + + "github.com/hazemKrimi/crimson-vault/internal/models" +) + +func (api *APIWrapper) CreateClientHandler(writer http.ResponseWriter, request *http.Request) { + var body models.CreateClientBody + + if err := json.NewDecoder(request.Body).Decode(&body); err != nil { + http.Error(writer, "Invalid JSON", http.StatusBadRequest) + log.Println(fmt.Sprintf("Error creating Client: %v.", err)) + return + } + + client := api.dbWrapper.CreateClient(body) + + log.Println(fmt.Sprintf("Client created with ID %d.", client.ID)) + json.NewEncoder(writer).Encode(client) +} + +func (api *APIWrapper) GetClientsHandler(writer http.ResponseWriter, request *http.Request) { + idString := request.URL.Query().Get("id") + + if idString != "" { + id, err := strconv.Atoi(idString) + + if err != nil { + http.Error(writer, "Unexpected error getting Client.", http.StatusInternalServerError) + return + } + + var client models.Client + + if err := api.dbWrapper.GetClient(id, &client); err != nil { + http.Error(writer, "Client not found.", http.StatusNotFound) + return + } + + log.Println(fmt.Sprintf("Got client with ID %d.", client.ID)) + json.NewEncoder(writer).Encode(client) + return + } + + clients, err := api.dbWrapper.GetClients() + + if err != nil { + http.Error(writer, "Unexpected error getting Clients.", http.StatusInternalServerError) + return + } + + log.Println("Got all Clients.") + json.NewEncoder(writer).Encode(clients) +} diff --git a/internal/api/routes.go b/internal/api/routes.go new file mode 100644 index 0000000..b87040e --- /dev/null +++ b/internal/api/routes.go @@ -0,0 +1,12 @@ +package api + +import "net/http" + +func ClientRoutes(api *APIWrapper) (*http.ServeMux) { + mux := http.NewServeMux() + + mux.HandleFunc("GET /", api.GetClientsHandler) + mux.HandleFunc("POST /", api.CreateClientHandler) + + return mux +} diff --git a/internal/models/client.go b/internal/models/client.go index 4d0b933..b90a63e 100644 --- a/internal/models/client.go +++ b/internal/models/client.go @@ -1,24 +1,56 @@ package models -import "gorm.io/gorm" +import ( + "time" + + "gorm.io/gorm" +) type Client struct { - gorm.Model - Name string - Country string - Phone string + ID uint32 `json:"id"` + CreatedAt time.Time `json:"createAt"` + UpdatedAt time.Time `json:"updatedAt"` + DeletedAt gorm.DeletedAt `json:"deletedAt" gorm:"index"` + Name string `json:"name"` + Country string `json:"country"` + Phone string `json:"phone"` +} + +type CreateClientBody struct { + Name string `json:"name"` + Country string `json:"country"` + Phone string `json:"phone"` } func (wrapper *DBWrapper) MigrateClients() { wrapper.db.AutoMigrate(&Client{}) } -func (wrapper *DBWrapper) CreateClient(name string, country string, phone string) { - wrapper.db.Create(&Client{Name: name, Country: country, Phone: phone}) -} +func (wrapper *DBWrapper) CreateClient(body CreateClientBody) Client { + client := Client{Name: body.Name, Country: body.Country, Phone: body.Phone} -func (wrapper *DBWrapper) GetClient(id int) (Client) { - var client Client - wrapper.db.First(&client, id) + wrapper.db.Create(&client) return client } + +func (wrapper *DBWrapper) GetClients() ([]Client, error) { + var clients []Client + + result := wrapper.db.Find(&clients) + + if result.Error != nil { + return nil, result.Error + } + + return clients, nil +} + +func (wrapper *DBWrapper) GetClient(id int, client *Client) error { + result := wrapper.db.Where("id = ?", id).First(&client, id) + + if result.Error != nil { + return result.Error + } + + return nil +} diff --git a/internal/models/db.go b/internal/models/db.go index 3035112..9d3257a 100644 --- a/internal/models/db.go +++ b/internal/models/db.go @@ -11,10 +11,12 @@ type DBWrapper struct { db *gorm.DB } -func (wrapper *DBWrapper) Initialize() { +func (wrapper *DBWrapper) Connect() { db, err := gorm.Open(sqlite.Open("crimson_vault.db"), &gorm.Config{}) + if err != nil { log.Fatal(err) } + wrapper.db = db }