├── Dockerfile ├── README.md ├── cmd └── main.go ├── controller └── product_controller.go ├── db └── conn.go ├── go.mod ├── go.sum ├── middleware └── auth.go ├── model ├── product.go └── response.go ├── repository └── product_repository.go └── usecase └── product_usecase.go /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.18 2 | 3 | WORKDIR /go/src/app 4 | 5 | COPY . . 6 | 7 | EXPOSE 8000 8 | 9 | RUN go build -o main cmd/main.go 10 | 11 | CMD ["./main"] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # API-GO: Product Management with JWT Verification 2 | 3 | This project is an API for product management developed in **Go** using the **Gin** framework. The architecture is modular, ensuring better organization and maintainability. 4 | 5 |
6 | 7 | --- 8 | 9 | ## 🚀 Project Initialization 10 | 11 | 12 | ### Pre-requisites 13 | 14 | Make sure you have installed: 15 | - **Golang**: [Install Golang](https://go.dev/doc/install) 16 | - **Docker**: [Install Docker](https://www.docker.com/products/docker-desktop) 17 | 18 | --- 19 | 20 | ### How to Run 21 | 22 | 1. Clone the project and navigate to the folder: 23 | ```bash 24 | git clone https://github.com/usuario/api-go.git 25 | cd api-go 26 | ``` 27 | 28 | 2. Configure the database connection: 29 | Edit the settings in the `db/conn.go` file to reflect your credentials. 30 | 31 | 3. Start the services with Docker: 32 | ```bash 33 | docker-compose up 34 | ``` 35 | 36 | 4. Access the API at the address: 37 | - `http://localhost:8000` 38 | 39 | --- 40 | 41 | ## 📚 Project Structure 42 | 43 | The structure is organized as follows: 44 | 45 | ```plaintext 46 | API-GO/ 47 | ├── cmd/ 48 | │ └── main.go # Main file to start the application 49 | ├── controller/ 50 | │ └── product_controller.go # Controllers responsible for the routes 51 | ├── db/ 52 | │ └── conn.go # Database configuration and connection 53 | ├── model/ 54 | │ ├── product.go # Product model 55 | │ └── response.go # Response structures for the API 56 | ├── repository/ 57 | │ └── product_repository.go # Layer for interacting with the database 58 | ├── usecase/ 59 | │ └── product_usecase.go # Business logic and application logic 60 | ├── .gitignore # Files ignored in version control 61 | ├── docker-compose.yml # Docker Compose configuration 62 | ├── Dockerfile # Docker container configuration 63 | ├── go.mod # Project dependencies 64 | ├── go.sum # Dependency hashes 65 | └── README.md # Project documentation 66 | ``` 67 | 68 | --- 69 | 70 | ## 📖 Endpoints 71 | 72 | ### **Ping** 73 | - **GET `/ping`** 74 | - Returns a test message. 75 | - **Response**: 76 | ```json 77 | { 78 | "message": "initial tests" 79 | } 80 | ``` 81 | 82 | --- 83 | 84 | ### **Products** 85 | - **GET `/products`** 86 | - Lists all registered products. 87 | - **Example Response**: 88 | ```json 89 | [ 90 | { 91 | "id": 1, 92 | "name": "Product 1", 93 | "price": 100.00 94 | }, 95 | { 96 | "id": 2, 97 | "name": "Product 2", 98 | "price": 150.00 99 | } 100 | ] 101 | ``` 102 | 103 | - **POST `/product`** 104 | - Creates a new product. 105 | - **Body**: 106 | ```json 107 | { 108 | "name": "Sample Product", 109 | "price": 100.00 110 | } 111 | ``` 112 | - **Example Response**: 113 | ```json 114 | { 115 | "id": 1, 116 | "name": "Sample Product", 117 | "price": 100.00 118 | } 119 | ``` 120 | 121 | - **GET `/product/:productId`** 122 | - Returns a specific product based on the ID. 123 | - **Example Response**: 124 | ```json 125 | { 126 | "id": 1, 127 | "name": "Sample Product", 128 | "price": 100.00 129 | } 130 | ``` 131 | 132 | - **PUT `/product`** 133 | - Updates an existing product. 134 | - **Body**: 135 | ```json 136 | { 137 | "id": 1, 138 | "name": "Updated Product", 139 | "price": 150.00 140 | } 141 | ``` 142 | - **Example Response**: 143 | ``` json 144 | { 145 | "id": 1, 146 | "name": "Updated Product", 147 | "price": 150.00 148 | } 149 | ``` 150 | 151 | - **DELETE `/product/:productId`** 152 | - Removes a product based on the ID. 153 | - **Example Response**: 154 | ``` json 155 | { 156 | "message": "Product successfully removed" 157 | } 158 | ``` 159 | 160 | --- 161 | 162 | ## 🛠️ Technologies Used 163 | 164 | - **Golang**: Main programming language. 165 | - **Gin**: Lightweight and fast framework for API development. 166 | - **Docker**: For container creation and management. 167 | - **PostgreSQL** (or another relational database): Configured in the `db` module. 168 | 169 |
170 | 171 | --- 172 | -------------------------------------------------------------------------------- /cmd/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "api-go/controller" 5 | "api-go/db" 6 | "api-go/middleware" 7 | "api-go/repository" 8 | "api-go/usecase" 9 | "github.com/gin-gonic/gin" 10 | ) 11 | 12 | func main() { 13 | server := gin.Default() 14 | 15 | dbConnection, err := db.ConnectDb() 16 | 17 | if err != nil { 18 | panic(err) 19 | return err 20 | } 21 | 22 | ProductRepository := repository.NewProductRepository(dbConnection) 23 | 24 | ProductUseCase := usecase.NewProductUseCase(ProductRepository) 25 | 26 | ProductController := controller.NewProductController(*ProductUseCase) 27 | 28 | server.GET("/ping-dev", func(ctx *gin.Context) { 29 | ctx.JSON(200, gin.H{ 30 | "message": "Servidor Está ativo!", 31 | }) 32 | }) 33 | 34 | /* 35 | routes server 36 | products 37 | */ 38 | 39 | protected := server.Group("/") 40 | protected.Use(middleware.AuthMiddleware()) 41 | { 42 | protected.GET("/products", ProductController.GetProducts) 43 | protected.POST("/product", ProductController.CreateProduct) 44 | protected.GET("/product/:productId", ProductController.GetProductsById) 45 | protected.PUT("/product", ProductController.UpdateProduct) 46 | protected.DELETE("/product/:productId", ProductController.DeleteProduct) 47 | } 48 | 49 | server.Run(":8000") 50 | 51 | } 52 | -------------------------------------------------------------------------------- /controller/product_controller.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | "api-go/model" 5 | "api-go/usecase" 6 | "net/http" 7 | "strconv" 8 | 9 | "github.com/gin-gonic/gin" 10 | ) 11 | 12 | type productController struct { 13 | productUseCase usecase.ProductUseCase 14 | } 15 | 16 | func NewProductController(usecase usecase.ProductUseCase) *productController { 17 | return &productController{ 18 | productUseCase: usecase, 19 | } 20 | } 21 | 22 | func (p *productController) GetProducts(ctx *gin.Context) { 23 | products, err := p.productUseCase.GetProducts() 24 | if err != nil { 25 | ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) 26 | return 27 | } 28 | 29 | ctx.JSON(http.StatusOK, products) 30 | } 31 | 32 | func (p *productController) CreateProduct(ctx *gin.Context) { 33 | 34 | var product model.Product 35 | err := ctx.BindJSON(&product) 36 | if err != nil { 37 | ctx.JSON(http.StatusBadRequest, err) 38 | return 39 | } 40 | 41 | insertedProduct, err := p.productUseCase.CreateProduct(product) 42 | if err != nil { 43 | ctx.JSON(http.StatusInternalServerError, err) 44 | return 45 | } 46 | 47 | ctx.JSON(http.StatusCreated, insertedProduct) 48 | } 49 | 50 | func (p *productController) GetProductsById(ctx *gin.Context) { 51 | 52 | id := ctx.Param("productId") 53 | if id == "" { 54 | response := model.Response{ 55 | Message: "Product ID cannot be null", 56 | } 57 | ctx.JSON(http.StatusBadRequest, response) 58 | return 59 | } 60 | 61 | productId, err := strconv.Atoi(id) 62 | if err != nil { 63 | response := model.Response{ 64 | Message: "Product Id need to be number", 65 | } 66 | ctx.JSON(http.StatusBadRequest, response) 67 | return 68 | } 69 | 70 | product, err := p.productUseCase.GetProductById(productId) 71 | if err != nil { 72 | ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) 73 | return 74 | } 75 | 76 | if product == nil { 77 | response := model.Response{ 78 | Message: "Product not existing in database", 79 | } 80 | ctx.JSON(http.StatusNotFound, response) 81 | return 82 | } 83 | 84 | ctx.JSON(http.StatusOK, product) 85 | } 86 | 87 | func (p *productController) UpdateProduct(ctx *gin.Context) { 88 | var product model.Product 89 | err := ctx.BindJSON(&product) 90 | if err != nil { 91 | ctx.JSON(http.StatusBadRequest, err) 92 | return 93 | } 94 | 95 | err = p.productUseCase.UpdateProduct(product) 96 | if err != nil { 97 | ctx.JSON(http.StatusInternalServerError, err) 98 | return 99 | } 100 | 101 | ctx.JSON(http.StatusOK, product) 102 | } 103 | 104 | func (p *productController) DeleteProduct(ctx *gin.Context) { 105 | id := ctx.Param("productId") 106 | if id == "" { 107 | response := model.Response{ 108 | Message: "Product ID cannot be null", 109 | } 110 | ctx.JSON(http.StatusBadRequest, response) 111 | return 112 | } 113 | 114 | productId, err := strconv.Atoi(id) 115 | if err != nil { 116 | response := model.Response{ 117 | Message: "Product Id need to be number", 118 | } 119 | ctx.JSON(http.StatusBadRequest, response) 120 | return 121 | } 122 | 123 | err = p.productUseCase.DeleteProduct(productId) 124 | if err != nil { 125 | ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) 126 | return 127 | } 128 | 129 | response := model.Response{ 130 | Message: "Product deleted successfully", 131 | } 132 | ctx.JSON(http.StatusOK, response) 133 | } 134 | -------------------------------------------------------------------------------- /db/conn.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | 7 | _ "github.com/lib/pq" 8 | ) 9 | 10 | /* 11 | configuration 12 | db 13 | */ 14 | 15 | /* 16 | var ( 17 | host = "go_db" string 18 | port = 5432 int 19 | user = "postgres" string 20 | password = "1234" int 21 | dbname = "postgres" 22 | 23 | )*/ 24 | 25 | const ( 26 | host = "go_db" 27 | port = 5432 28 | user = "postgres" 29 | password = "1234" 30 | dbname = "postgres" 31 | ) 32 | 33 | func ConnectDb() (*sql.DB, error) { 34 | psqlInfo := fmt.Sprintf("host=%s port=%d user=%s "+ 35 | "password=%s dbname=%s sslmode=disable", 36 | host, port, user, password, dbname) 37 | db, err := sql.Open("postgres", psqlInfo) 38 | if err != nil { 39 | panic(err) 40 | } 41 | 42 | err = db.Ping() 43 | if err != nil { 44 | panic(err) 45 | } 46 | 47 | //fmt.Println("connected to" + dbname) 48 | 49 | return db, nil 50 | } 51 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module api-go 2 | 3 | go 1.23.4 4 | 5 | require ( 6 | github.com/gin-gonic/gin v1.10.0 7 | github.com/lib/pq v1.10.9 8 | ) 9 | 10 | require ( 11 | github.com/bytedance/sonic v1.11.6 // indirect 12 | github.com/bytedance/sonic/loader v0.1.1 // indirect 13 | github.com/cloudwego/base64x v0.1.4 // indirect 14 | github.com/cloudwego/iasm v0.2.0 // indirect 15 | github.com/dgrijalva/jwt-go v3.2.0+incompatible // direct 16 | github.com/gabriel-vasile/mimetype v1.4.3 // indirect 17 | github.com/gin-contrib/sse v0.1.0 // indirect 18 | github.com/go-playground/locales v0.14.1 // indirect 19 | github.com/go-playground/universal-translator v0.18.1 // indirect 20 | github.com/go-playground/validator/v10 v10.20.0 // indirect 21 | github.com/goccy/go-json v0.10.2 // indirect 22 | github.com/json-iterator/go v1.1.12 // indirect 23 | github.com/klauspost/cpuid/v2 v2.2.7 // indirect 24 | github.com/leodido/go-urn v1.4.0 // indirect 25 | github.com/mattn/go-isatty v0.0.20 // indirect 26 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 27 | github.com/modern-go/reflect2 v1.0.2 // indirect 28 | github.com/pelletier/go-toml/v2 v2.2.2 // indirect 29 | github.com/twitchyliquid64/golang-asm v0.15.1 // indirect 30 | github.com/ugorji/go/codec v1.2.12 // indirect 31 | golang.org/x/arch v0.8.0 // indirect 32 | golang.org/x/crypto v0.23.0 // indirect 33 | golang.org/x/net v0.25.0 // indirect 34 | golang.org/x/sys v0.20.0 // indirect 35 | golang.org/x/text v0.15.0 // indirect 36 | google.golang.org/protobuf v1.34.1 // indirect 37 | gopkg.in/yaml.v3 v3.0.1 // indirect 38 | ) 39 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0= 2 | github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= 3 | github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= 4 | github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= 5 | github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= 6 | github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= 7 | github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= 8 | github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= 9 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 10 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 11 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 12 | github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= 13 | github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= 14 | github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= 15 | github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= 16 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= 17 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 18 | github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= 19 | github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= 20 | github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= 21 | github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= 22 | github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= 23 | github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= 24 | github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= 25 | github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= 26 | github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8= 27 | github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= 28 | github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= 29 | github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= 30 | github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= 31 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 32 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 33 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 34 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 35 | github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= 36 | github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= 37 | github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= 38 | github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= 39 | github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= 40 | github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= 41 | github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= 42 | github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= 43 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 44 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 45 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 46 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 47 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 48 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 49 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 50 | github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= 51 | github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= 52 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 53 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 54 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 55 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 56 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 57 | github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= 58 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 59 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 60 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 61 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 62 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 63 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 64 | github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= 65 | github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 66 | github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= 67 | github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= 68 | github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= 69 | github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= 70 | golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= 71 | golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= 72 | golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= 73 | golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= 74 | golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= 75 | golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= 76 | golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= 77 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 78 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 79 | golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= 80 | golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 81 | golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= 82 | golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 83 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 84 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 85 | google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= 86 | google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= 87 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 88 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 89 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 90 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 91 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 92 | nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= 93 | rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= 94 | -------------------------------------------------------------------------------- /middleware/auth.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "strings" 7 | 8 | "github.com/dgrijalva/jwt-go" 9 | "github.com/gin-gonic/gin" 10 | ) 11 | 12 | var jwtSecret = []byte("your_secret_key") 13 | 14 | func AuthMiddleware() gin.HandlerFunc { 15 | return func(c *gin.Context) { 16 | authHeader := c.GetHeader("Authorization") 17 | if authHeader == "" { 18 | c.JSON(http.StatusUnauthorized, gin.H{"error": "Authorization header required"}) 19 | c.Abort() 20 | return 21 | } 22 | 23 | tokenString := strings.Split(authHeader, "Bearer ")[1] 24 | token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { 25 | if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { 26 | return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) 27 | } 28 | return jwtSecret, nil 29 | }) 30 | 31 | if err != nil || !token.Valid { 32 | c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token"}) 33 | c.Abort() 34 | return 35 | } 36 | 37 | c.Next() 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /model/product.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | /* 4 | Product Struct 5 | */ 6 | 7 | type Product struct { 8 | ID int `json:"id"` 9 | Name string `json:"name"` 10 | Price float64 `json:"price"` 11 | } -------------------------------------------------------------------------------- /model/response.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type Response struct { 4 | Message string 5 | } 6 | -------------------------------------------------------------------------------- /repository/product_repository.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "api-go/model" 5 | "database/sql" 6 | "fmt" 7 | ) 8 | 9 | type ProductRepository struct { 10 | connection *sql.DB 11 | } 12 | 13 | func NewProductRepository(connection *sql.DB) ProductRepository { 14 | return ProductRepository{ 15 | connection: connection, 16 | } 17 | } 18 | 19 | func (pr *ProductRepository) GetProducts() ([]model.Product, error) { 20 | query := "SELECT id, product_name, price FROM product" 21 | rows, err := pr.connection.Query(query) 22 | if err != nil { 23 | fmt.Println(err) 24 | return []model.Product{}, err 25 | } 26 | 27 | var productList []model.Product 28 | var productObj model.Product 29 | 30 | for rows.Next() { 31 | err = rows.Scan( 32 | &productObj.ID, 33 | &productObj.Name, 34 | &productObj.Price) 35 | 36 | if err != nil { 37 | fmt.Println(err) 38 | return []model.Product{}, err 39 | } 40 | 41 | productList = append(productList, productObj) 42 | } 43 | 44 | rows.Close() 45 | 46 | return productList, nil 47 | } 48 | 49 | func (pr *ProductRepository) CreateProduct(product model.Product) (int, error) { 50 | 51 | var id int 52 | query, err := pr.connection.Prepare("INSERT INTO product" + 53 | "(product_name, price)" + 54 | "VALUES($1, $2) RETURNING id") 55 | if err != nil { 56 | fmt.Println(err) 57 | return 0, err 58 | } 59 | 60 | err = query.QueryRow(product.Name, product.Price).Scan(&id) 61 | if err != nil { 62 | fmt.Println(err) 63 | return 0, err 64 | } 65 | 66 | query.Close() 67 | return id, nil 68 | } 69 | 70 | func (pr *ProductRepository) GetProductById(id_product int) (*model.Product, error) { 71 | 72 | query, err := pr.connection.Prepare("SELECT * FROM product WHERE od = $1") 73 | if err != nil { 74 | fmt.Println(err) 75 | return nil, err 76 | } 77 | 78 | var produto model.Product 79 | 80 | err = query.QueryRow(id_product).Scan( 81 | &produto.ID, 82 | &produto.Name, 83 | &produto.Price, 84 | ) 85 | 86 | if err != nil { 87 | if err == sql.ErrNoRows { 88 | return nil, nil 89 | } 90 | 91 | return nil, err 92 | } 93 | 94 | query.Close() 95 | return &produto, nil 96 | } 97 | 98 | func (pr *ProductRepository) UpdateProduct(product model.Product) error { 99 | query, err := pr.connection.Prepare("UPDATE product SET product_name = $1, price = $2 WHERE id = $3") 100 | if err != nil { 101 | fmt.Println(err) 102 | return err 103 | } 104 | defer query.Close() 105 | 106 | _, err = query.Exec(product.Name, product.Price, product.ID) 107 | if err != nil { 108 | fmt.Println(err) 109 | return err 110 | } 111 | 112 | return nil 113 | } 114 | 115 | func (pr *ProductRepository) DeleteProduct(id int) error { 116 | query, err := pr.connection.Prepare("DELETE FROM product WHERE id = $1") 117 | if err != nil { 118 | fmt.Println(err) 119 | return err 120 | } 121 | defer query.Close() 122 | 123 | _, err = query.Exec(id) 124 | if err != nil { 125 | //fmt.Println(err) 126 | return err 127 | } 128 | 129 | return nil 130 | } 131 | -------------------------------------------------------------------------------- /usecase/product_usecase.go: -------------------------------------------------------------------------------- 1 | package usecase 2 | 3 | import ( 4 | "api-go/model" 5 | "api-go/repository" 6 | ) 7 | 8 | type ProductUseCase struct { 9 | repository repository.ProductRepository 10 | } 11 | 12 | func NewProductUseCase(repo repository.ProductRepository) *ProductUseCase { 13 | return &ProductUseCase{ 14 | repository: repo, 15 | } 16 | } 17 | 18 | func (pu *ProductUseCase) GetProducts() ([]model.Product, error) { 19 | return pu.repository.GetProducts() 20 | } 21 | 22 | func (pu *ProductUseCase) CreateProduct(product model.Product) (model.Product, error) { 23 | productId, err := pu.repository.CreateProduct(product) 24 | if err != nil { 25 | return model.Product{}, err 26 | } 27 | product.ID = productId 28 | return product, nil 29 | } 30 | 31 | func (pu *ProductUseCase) GetProductById(id_product int) (*model.Product, error) { 32 | product, err := pu.repository.GetProductById(id_product) 33 | if err != nil { 34 | return nil, err 35 | } 36 | return product, nil 37 | } 38 | 39 | func (pu *ProductUseCase) UpdateProduct(product model.Product) error { 40 | return pu.repository.UpdateProduct(product) 41 | } 42 | 43 | func (pu *ProductUseCase) DeleteProduct(id int) error { 44 | return pu.repository.DeleteProduct(id) 45 | } 46 | --------------------------------------------------------------------------------