first commit
This commit is contained in:
		
							
								
								
									
										37
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,37 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					# Created by https://www.toptal.com/developers/gitignore/api/go,visualstudiocode
 | 
				
			||||||
 | 
					# Edit at https://www.toptal.com/developers/gitignore?templates=go,visualstudiocode
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Go ###
 | 
				
			||||||
 | 
					# Binaries for programs and plugins
 | 
				
			||||||
 | 
					*.exe
 | 
				
			||||||
 | 
					*.exe~
 | 
				
			||||||
 | 
					*.dll
 | 
				
			||||||
 | 
					*.so
 | 
				
			||||||
 | 
					*.dylib
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Test binary, built with `go test -c`
 | 
				
			||||||
 | 
					*.test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Output of the go coverage tool, specifically when used with LiteIDE
 | 
				
			||||||
 | 
					*.out
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Dependency directories (remove the comment below to include it)
 | 
				
			||||||
 | 
					# vendor/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Go Patch ###
 | 
				
			||||||
 | 
					/vendor/
 | 
				
			||||||
 | 
					/Godeps/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### VisualStudioCode ###
 | 
				
			||||||
 | 
					.vscode/*
 | 
				
			||||||
 | 
					!.vscode/tasks.json
 | 
				
			||||||
 | 
					!.vscode/launch.json
 | 
				
			||||||
 | 
					*.code-workspace
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### VisualStudioCode Patch ###
 | 
				
			||||||
 | 
					# Ignore all local history of files
 | 
				
			||||||
 | 
					.history
 | 
				
			||||||
 | 
					.ionide
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# End of https://www.toptal.com/developers/gitignore/api/go,visualstudiocode
 | 
				
			||||||
							
								
								
									
										17
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					FROM golang:1.16.5 as builder
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					WORKDIR /go-microservice/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					COPY . .
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RUN CGO_ENABLED=0 go build -o microservice /go-microservice/main.go
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					FROM alpine:latest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					WORKDIR /go-microservice
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					COPY --from=builder /go-microservice/ /go-microservice/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					EXPOSE 9090
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CMD ./microservice
 | 
				
			||||||
							
								
								
									
										16
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					# Product Catalog API
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Blogs for Detailed Tutorial:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* <a href="https://learnai1.home.blog/2021/03/15/microservices-in-go/">Introductory Blog.</a> 
 | 
				
			||||||
 | 
					* <a href="https://learnai1.home.blog/2021/03/18/microservices-in-go-part-2/">Adding More API's.</a>
 | 
				
			||||||
 | 
					* <a href="https://learnai1.home.blog/2021/06/27/authentication-in-go-microservices/">Basic Authentication in Microservices.</a>
 | 
				
			||||||
 | 
					* <a href="https://learnai1.home.blog/2021/07/08/microservices-in-go-part-iv-docker-and-go-microservices/">Docker and Go Microservices.</a>
 | 
				
			||||||
 | 
					* <a href="https://learnai1.home.blog/2021/08/05/https-server-in-go/">HTTPS Server in Go.</a>
 | 
				
			||||||
 | 
					## Run API
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					``` bash
 | 
				
			||||||
 | 
					go run .\main.go
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Use the above command to run API on your local machine on port 9090.
 | 
				
			||||||
							
								
								
									
										30
									
								
								data/data.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								data/data.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					[
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        "id": "e7f31219-7986-11eb-bd65-98fa9b64e75b",
 | 
				
			||||||
 | 
					        "name": "iPhone 12 Pro",
 | 
				
			||||||
 | 
					        "description": "Pro variant of iPhone 12",
 | 
				
			||||||
 | 
					        "price": 100000,
 | 
				
			||||||
 | 
					        "isAvailable": false
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        "id": "f82fa5ec-7986-11eb-a8e3-98fa9b64e75b",
 | 
				
			||||||
 | 
					        "name": "iPhone 12 Pro Max",
 | 
				
			||||||
 | 
					        "description": "Pro max variant of iPhone 12",
 | 
				
			||||||
 | 
					        "price": 120000,
 | 
				
			||||||
 | 
					        "isAvailable": true
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        "id": "e7f35219-7986-11eb-bd65-98fa9b64e75b",
 | 
				
			||||||
 | 
					        "name": "iPhone 12 Mini",
 | 
				
			||||||
 | 
					        "description": "Mini variant of iPhone 12",
 | 
				
			||||||
 | 
					        "price": 60000,
 | 
				
			||||||
 | 
					        "isAvailable": true
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        "id": "e7f35219-8986-11eb-bd65-98fa9b64e75b",
 | 
				
			||||||
 | 
					        "name": "iPhone 12",
 | 
				
			||||||
 | 
					        "description": "Base variant of iPhone 12",
 | 
				
			||||||
 | 
					        "price": 78000,
 | 
				
			||||||
 | 
					        "isAvailable": true
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
							
								
								
									
										123
									
								
								entity/product.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								entity/product.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,123 @@
 | 
				
			|||||||
 | 
					package entity
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"io/ioutil"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//Product defines a structure for an item in product catalog
 | 
				
			||||||
 | 
					type Product struct {
 | 
				
			||||||
 | 
						ID          string  `json:"id"`
 | 
				
			||||||
 | 
						Name        string  `json:"name"`
 | 
				
			||||||
 | 
						Description string  `json:"description"`
 | 
				
			||||||
 | 
						Price       float64 `json:"price"`
 | 
				
			||||||
 | 
						IsAvailable bool    `json:"isAvailable"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ErrNoProduct is used if no product found
 | 
				
			||||||
 | 
					var ErrNoProduct = errors.New("no product found")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetProducts returns the JSON file content if available else returns an error.
 | 
				
			||||||
 | 
					func GetProducts() ([]byte, error) {
 | 
				
			||||||
 | 
						// Read JSON file
 | 
				
			||||||
 | 
						data, err := ioutil.ReadFile("./data/data.json")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return data, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetProduct takes id as input and returns the corresponding product, else it returns ErrNoProduct error.
 | 
				
			||||||
 | 
					func GetProduct(id string) (Product, error) {
 | 
				
			||||||
 | 
						// Read JSON file
 | 
				
			||||||
 | 
						data, err := ioutil.ReadFile("./data/data.json")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return Product{}, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// read products
 | 
				
			||||||
 | 
						var products []Product
 | 
				
			||||||
 | 
						err = json.Unmarshal(data, &products)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return Product{}, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// iterate through product array
 | 
				
			||||||
 | 
						for i := 0; i < len(products); i++ {
 | 
				
			||||||
 | 
							// if we find one product with the given ID
 | 
				
			||||||
 | 
							if products[i].ID == id {
 | 
				
			||||||
 | 
								// return product
 | 
				
			||||||
 | 
								return products[i], nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return Product{}, ErrNoProduct
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DeleteProduct takes id as input and deletes the corresponding product, else it returns ErrNoProduct error.
 | 
				
			||||||
 | 
					func DeleteProduct(id string) error {
 | 
				
			||||||
 | 
						// Read JSON file
 | 
				
			||||||
 | 
						data, err := ioutil.ReadFile("./data/data.json")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// read products
 | 
				
			||||||
 | 
						var products []Product
 | 
				
			||||||
 | 
						err = json.Unmarshal(data, &products)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// iterate through product array
 | 
				
			||||||
 | 
						for i := 0; i < len(products); i++ {
 | 
				
			||||||
 | 
							// if we find one product with the given ID
 | 
				
			||||||
 | 
							if products[i].ID == id {
 | 
				
			||||||
 | 
								products = removeElement(products, i)
 | 
				
			||||||
 | 
								// Write Updated JSON file
 | 
				
			||||||
 | 
								updatedData, err := json.Marshal(products)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								err = ioutil.WriteFile("./data/data.json", updatedData, os.ModePerm)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ErrNoProduct
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// AddProduct adds an input product to the product list in JSON document.
 | 
				
			||||||
 | 
					func AddProduct(product Product) error {
 | 
				
			||||||
 | 
						// Load existing products and append the data to product list
 | 
				
			||||||
 | 
						var products []Product
 | 
				
			||||||
 | 
						data, err := ioutil.ReadFile("./data/data.json")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// Load our JSON file to memory using array of products
 | 
				
			||||||
 | 
						err = json.Unmarshal(data, &products)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// Add new Product to our list
 | 
				
			||||||
 | 
						products = append(products, product)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Write Updated JSON file
 | 
				
			||||||
 | 
						updatedData, err := json.Marshal(products)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						err = ioutil.WriteFile("./data/data.json", updatedData, os.ModePerm)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// removeElement is used to remove element from product array at given index
 | 
				
			||||||
 | 
					func removeElement(arr []Product, index int) []Product {
 | 
				
			||||||
 | 
						ret := make([]Product, 0)
 | 
				
			||||||
 | 
						ret = append(ret, arr[:index]...)
 | 
				
			||||||
 | 
						return append(ret, arr[index+1:]...)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										5
									
								
								go.mod
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								go.mod
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					module github.com/HelloWorld/goProductAPI
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					go 1.16
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					require github.com/gorilla/mux v1.8.0
 | 
				
			||||||
							
								
								
									
										2
									
								
								go.sum
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								go.sum
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,2 @@
 | 
				
			|||||||
 | 
					github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
 | 
				
			||||||
 | 
					github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
 | 
				
			||||||
							
								
								
									
										166
									
								
								handlers/handlers.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										166
									
								
								handlers/handlers.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,166 @@
 | 
				
			|||||||
 | 
					package handlers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"crypto/sha256"
 | 
				
			||||||
 | 
						"crypto/subtle"
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"io/ioutil"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/HelloWorld/goProductAPI/entity"
 | 
				
			||||||
 | 
						"github.com/gorilla/mux"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetProductsHandler is used to get data inside the products defined on our product catalog
 | 
				
			||||||
 | 
					func GetProductsHandler() http.HandlerFunc {
 | 
				
			||||||
 | 
						return func(rw http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
 | 
							data, err := entity.GetProducts()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								rw.WriteHeader(http.StatusInternalServerError)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							// Write the body with JSON data
 | 
				
			||||||
 | 
							rw.Header().Add("content-type", "application/json")
 | 
				
			||||||
 | 
							rw.WriteHeader(http.StatusFound)
 | 
				
			||||||
 | 
							rw.Write(data)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetProductHandler is used to get data inside the products defined on our product catalog
 | 
				
			||||||
 | 
					func GetProductHandler() http.HandlerFunc {
 | 
				
			||||||
 | 
						return func(rw http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
 | 
							// Read product ID
 | 
				
			||||||
 | 
							productID := mux.Vars(r)["id"]
 | 
				
			||||||
 | 
							product, err := entity.GetProduct(productID)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								rw.WriteHeader(http.StatusInternalServerError)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							responseData, err := json.Marshal(product)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								// Check if it is No product error or any other error
 | 
				
			||||||
 | 
								if errors.Is(err, entity.ErrNoProduct) {
 | 
				
			||||||
 | 
									// Write Header if no related product found.
 | 
				
			||||||
 | 
									rw.WriteHeader(http.StatusNoContent)
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									rw.WriteHeader(http.StatusInternalServerError)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							// Write body with found product
 | 
				
			||||||
 | 
							rw.Header().Add("content-type", "application/json")
 | 
				
			||||||
 | 
							rw.WriteHeader(http.StatusFound)
 | 
				
			||||||
 | 
							rw.Write(responseData)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// CreateProductHandler is used to create a new product and add to our product store.
 | 
				
			||||||
 | 
					func CreateProductHandler() http.HandlerFunc {
 | 
				
			||||||
 | 
						return func(rw http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
 | 
							// Read incoming JSON from request body
 | 
				
			||||||
 | 
							data, err := ioutil.ReadAll(r.Body)
 | 
				
			||||||
 | 
							// If no body is associated return with StatusBadRequest
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								rw.WriteHeader(http.StatusBadRequest)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							// Check if data is proper JSON (data validation)
 | 
				
			||||||
 | 
							var product entity.Product
 | 
				
			||||||
 | 
							err = json.Unmarshal(data, &product)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								rw.WriteHeader(http.StatusExpectationFailed)
 | 
				
			||||||
 | 
								rw.Write([]byte("Invalid Data Format"))
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							err = entity.AddProduct(product)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								rw.WriteHeader(http.StatusInternalServerError)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							// return after writing Body
 | 
				
			||||||
 | 
							rw.WriteHeader(http.StatusCreated)
 | 
				
			||||||
 | 
							rw.Write([]byte("Added New Product"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DeleteProductHandler deletes the product with given ID.
 | 
				
			||||||
 | 
					func DeleteProductHandler() http.HandlerFunc {
 | 
				
			||||||
 | 
						return func(rw http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
 | 
							// Read product ID
 | 
				
			||||||
 | 
							productID := mux.Vars(r)["id"]
 | 
				
			||||||
 | 
							err := entity.DeleteProduct(productID)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								// Check if it is No product error or any other error
 | 
				
			||||||
 | 
								if errors.Is(err, entity.ErrNoProduct) {
 | 
				
			||||||
 | 
									// Write Header if no related product found.
 | 
				
			||||||
 | 
									rw.WriteHeader(http.StatusNoContent)
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									rw.WriteHeader(http.StatusInternalServerError)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							// Write Header with Accepted Status (done operation)
 | 
				
			||||||
 | 
							rw.WriteHeader(http.StatusAccepted)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// UpdateProductHandler updates the product with given ID.
 | 
				
			||||||
 | 
					func UpdateProductHandler() http.HandlerFunc {
 | 
				
			||||||
 | 
						return func(rw http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
 | 
							// Read product ID
 | 
				
			||||||
 | 
							productID := mux.Vars(r)["id"]
 | 
				
			||||||
 | 
							err := entity.DeleteProduct(productID)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								if errors.Is(err, entity.ErrNoProduct) {
 | 
				
			||||||
 | 
									rw.WriteHeader(http.StatusNoContent)
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									rw.WriteHeader(http.StatusInternalServerError)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							// Read incoming JSON from request body
 | 
				
			||||||
 | 
							data, err := ioutil.ReadAll(r.Body)
 | 
				
			||||||
 | 
							// If no body is associated return with StatusBadRequest
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								rw.WriteHeader(http.StatusBadRequest)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							// Check if data is proper JSON (data validation)
 | 
				
			||||||
 | 
							var product entity.Product
 | 
				
			||||||
 | 
							err = json.Unmarshal(data, &product)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								rw.WriteHeader(http.StatusExpectationFailed)
 | 
				
			||||||
 | 
								rw.Write([]byte("Invalid Data Format"))
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							// Addproduct with the requested body
 | 
				
			||||||
 | 
							err = entity.AddProduct(product)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								rw.WriteHeader(http.StatusInternalServerError)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							// Write Header if no related product found.
 | 
				
			||||||
 | 
							rw.WriteHeader(http.StatusAccepted)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func AuthHandler(h http.Handler) http.HandlerFunc {
 | 
				
			||||||
 | 
						return func(rw http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
 | 
							user, pass, ok := r.BasicAuth()
 | 
				
			||||||
 | 
							if ok {
 | 
				
			||||||
 | 
								username := sha256.Sum256([]byte(os.Getenv("USER_NAME")))
 | 
				
			||||||
 | 
								password := sha256.Sum256([]byte(os.Getenv("USER_PASS")))
 | 
				
			||||||
 | 
								userHash := sha256.Sum256([]byte(user))
 | 
				
			||||||
 | 
								passHash := sha256.Sum256([]byte(pass))
 | 
				
			||||||
 | 
								validUser := subtle.ConstantTimeCompare(userHash[:],username[:]) == 1
 | 
				
			||||||
 | 
								validPass := subtle.ConstantTimeCompare(passHash[:],password[:]) == 1
 | 
				
			||||||
 | 
								if validPass && validUser{
 | 
				
			||||||
 | 
								    h.ServeHTTP(rw,r)
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							http.Error(rw, "No/Invalid Credentials", http.StatusUnauthorized)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										32
									
								
								main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								main.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,32 @@
 | 
				
			|||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/HelloWorld/goProductAPI/handlers"
 | 
				
			||||||
 | 
						"github.com/gorilla/mux"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func main() {
 | 
				
			||||||
 | 
						// Create new Router
 | 
				
			||||||
 | 
						router := mux.NewRouter()
 | 
				
			||||||
 | 
						// route properly to respective handlers
 | 
				
			||||||
 | 
						router.Handle("/products", handlers.GetProductsHandler()).Methods("GET")
 | 
				
			||||||
 | 
						router.Handle("/products", handlers.CreateProductHandler()).Methods("POST")
 | 
				
			||||||
 | 
						router.Handle("/products/{id}", handlers.GetProductHandler()).Methods("GET")
 | 
				
			||||||
 | 
						router.Handle("/products/{id}", handlers.DeleteProductHandler()).Methods("DELETE")
 | 
				
			||||||
 | 
						router.Handle("/products/{id}", handlers.UpdateProductHandler()).Methods("PUT")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Create new server and assign the router
 | 
				
			||||||
 | 
						server := http.Server{
 | 
				
			||||||
 | 
							Addr:    ":9090",
 | 
				
			||||||
 | 
							Handler: handlers.AuthHandler(router),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						fmt.Println("Staring Product Catalog server on Port 9090")
 | 
				
			||||||
 | 
						// Start Server on defined port/host.
 | 
				
			||||||
 | 
						err := server.ListenAndServeTLS("server.crt", "server.key")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							fmt.Printf("Failed to start HTTPS server: %s", err.Error())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										22
									
								
								server.crt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								server.crt
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,22 @@
 | 
				
			|||||||
 | 
					-----BEGIN CERTIFICATE-----
 | 
				
			||||||
 | 
					MIIDtTCCAp2gAwIBAgIUDQgu9+HrJrc7TEelsB1TYp+zhPkwDQYJKoZIhvcNAQEL
 | 
				
			||||||
 | 
					BQAwajELMAkGA1UEBhMCSU4xDjAMBgNVBAgMBURlbGhpMRIwEAYDVQQHDAlOZXcg
 | 
				
			||||||
 | 
					RGVsaGkxFDASBgNVBAoMC0hlbGxvIFdvcmxkMQ0wCwYDVQQLDARCbG9nMRIwEAYD
 | 
				
			||||||
 | 
					VQQDDAlsb2NhbGhvc3QwHhcNMjEwODA1MDMzNDQ0WhcNMzEwODAzMDMzNDQ0WjBq
 | 
				
			||||||
 | 
					MQswCQYDVQQGEwJJTjEOMAwGA1UECAwFRGVsaGkxEjAQBgNVBAcMCU5ldyBEZWxo
 | 
				
			||||||
 | 
					aTEUMBIGA1UECgwLSGVsbG8gV29ybGQxDTALBgNVBAsMBEJsb2cxEjAQBgNVBAMM
 | 
				
			||||||
 | 
					CWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL8emPJ1
 | 
				
			||||||
 | 
					tw0TrfJZQeLzHO2x0O36UetzeJKdf//IKt3P89B+2GGL6DzPdntTBM9J4PAzLzXe
 | 
				
			||||||
 | 
					uR8uL53XDWdfvhCd9oMYsgJ4L1XE6q+oZA6zWGC4voUwUOJgpJyXt8fdDdn1G6X/
 | 
				
			||||||
 | 
					7Lyjw4u6KOkrFcwIPaM+MxgZGwICi1JDj7sYCOEw1gWsLrgD4XpN0Fky9QH1Uue+
 | 
				
			||||||
 | 
					sxCZrrXFxiSXt0Xq51sT5BXrCkPhRrHUyhBay+VtVa+Lr2Yq+UfT3VUWERgcoJkx
 | 
				
			||||||
 | 
					YluYXMbV4YYZ53bbgrW+29UT+NKNKTF2dFJazZ4r5OzZAD3ihikvoCIZUaVtgOpf
 | 
				
			||||||
 | 
					fctWqG5bKmVpSaMCAwEAAaNTMFEwHQYDVR0OBBYEFPiCPV5yetrvDF5caB0t4XWx
 | 
				
			||||||
 | 
					S+NeMB8GA1UdIwQYMBaAFPiCPV5yetrvDF5caB0t4XWxS+NeMA8GA1UdEwEB/wQF
 | 
				
			||||||
 | 
					MAMBAf8wDQYJKoZIhvcNAQELBQADggEBALiBoaqc9KxscNuU2mXTfXD4kv1bLy3C
 | 
				
			||||||
 | 
					zzGn3bBHMQuRfwkclkEZG0Cbmu+k30DtMhUt9DREM6fVhClUlR6my/0vC/EgUIm2
 | 
				
			||||||
 | 
					yYOF/dBIySH/JkZz0qRst5NvicwPaqUQoyIIzk93Da8eE0uGHW7eV4tsAVkB0RHT
 | 
				
			||||||
 | 
					i4/EkGKZ/vJKyj/RI4zWvMCEFZcGtDtKf4rYzmcA3t0P1gbWt2+Gkhz1/+i52oIr
 | 
				
			||||||
 | 
					w2M2CQ9qpgv3jvwCfOpZVQDRMZnLVpP/s8KyOwnVu+n8c/z+zJe+U3x5Sl0eamDA
 | 
				
			||||||
 | 
					7CQ6RcorzWWzAhAQkrH0qWnF+BfF7GJ8VoLku0dnTzYsAQ5L4XaYf/I=
 | 
				
			||||||
 | 
					-----END CERTIFICATE-----
 | 
				
			||||||
							
								
								
									
										27
									
								
								server.key
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								server.key
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					-----BEGIN RSA PRIVATE KEY-----
 | 
				
			||||||
 | 
					MIIEowIBAAKCAQEAvx6Y8nW3DROt8llB4vMc7bHQ7fpR63N4kp1//8gq3c/z0H7Y
 | 
				
			||||||
 | 
					YYvoPM92e1MEz0ng8DMvNd65Hy4vndcNZ1++EJ32gxiyAngvVcTqr6hkDrNYYLi+
 | 
				
			||||||
 | 
					hTBQ4mCknJe3x90N2fUbpf/svKPDi7oo6SsVzAg9oz4zGBkbAgKLUkOPuxgI4TDW
 | 
				
			||||||
 | 
					BawuuAPhek3QWTL1AfVS576zEJmutcXGJJe3RernWxPkFesKQ+FGsdTKEFrL5W1V
 | 
				
			||||||
 | 
					r4uvZir5R9PdVRYRGBygmTFiW5hcxtXhhhnndtuCtb7b1RP40o0pMXZ0UlrNnivk
 | 
				
			||||||
 | 
					7NkAPeKGKS+gIhlRpW2A6l99y1aoblsqZWlJowIDAQABAoIBAQClfS0a5Ws3246H
 | 
				
			||||||
 | 
					h1pR1gl6mLo9Fr/QjRAehFrNdNoJb4PDSdK7xJW38jy51M0ZYPNxiiCbGNxbb3az
 | 
				
			||||||
 | 
					yf9FP9YoNV+7bKrXEJKMRhKhP8JEKG+icNYoJgoju2NOZOEyIutXi7IBL3Yicftl
 | 
				
			||||||
 | 
					BjFelXwuTARzUeyUNUj5mJJjDTVr3oi7A/HyoEn66PmWSGgNB618tYxA55PwRgJO
 | 
				
			||||||
 | 
					hd+OakpW90kLb3RsySts9cpIh1eEJLTKafTR4uY7SrAvMjFa9Zn4MpRYXMEq43DC
 | 
				
			||||||
 | 
					382dlqYBPXMPN4YJEKZ5CmxFMaZ3a1Nr0G60RyQqwmB/fRWg+OZUOjjFOVllnOOY
 | 
				
			||||||
 | 
					5jnSIy9BAoGBAONwsIpaze2TeM8Ij4NxBqwRAh/2zb5t0okD/zbhf2sPyrEP8+hI
 | 
				
			||||||
 | 
					egG10kQh7t0MhRklSOmv3NfDxr998XfBjswXdnsoIvP+BmlLG01zRh/49cdBnOty
 | 
				
			||||||
 | 
					Za/zu5msyYspopPrDjwF+RtMHOXpXNuzFhxMYWdfOBO7/lOSka8ABFqZAoGBANce
 | 
				
			||||||
 | 
					VzQW6OFLAE5fwwkf5W4cjOGaSpkKq6KwBkQayvRrGQMf3pu7Q2pdlcoJIhGOy8Bc
 | 
				
			||||||
 | 
					N2iI7qE4P52MmAg51TyIDcrhTrMLkoMXLmeyBC1M6JT6t1yGTpPkf6ZdVFAZZEAp
 | 
				
			||||||
 | 
					3l2dv8pyPRpr5juE8YcabgF5deo3zxieB6bYlkebAoGAS63goHjsksQCa+luT49Z
 | 
				
			||||||
 | 
					aAHU0iv+dAH5DyxsTKemDUrY6CflwgHzzwPgLlmYMKeM1jwo0dF5y7XSOT/ADFg0
 | 
				
			||||||
 | 
					msan3v0Q/F0nZvvd3tyfld3yclXr0BBls7GHV/A9s/erqEqLlv9pz2J5LyuCgXxK
 | 
				
			||||||
 | 
					vCnSM2Jkt3RTgR2BKlj4GekCgYAxUxilrfcZ6WuZjOWYiwK9W7iF5i3ip4qxU/Er
 | 
				
			||||||
 | 
					3oTYxFHI4J7XUHnlwq2c1LlGE1rusXZW9sbYmqAjjOAzSqd1KLEY6s5zyVx/yGnw
 | 
				
			||||||
 | 
					huXkSTUvK8mtYnJUANmwGMhDUX8mIzOEfa5DSixuiX0R+qqy0sGUfvgli0RmHZ4d
 | 
				
			||||||
 | 
					iJ30rwKBgDuW1L0DJnH2n8e3uhJF8W1rKDynQdzGdPc4Bl8oSfvDMpIGz1cTecKV
 | 
				
			||||||
 | 
					+30AWz1ui5u1qlg7gsjnt6Z7g3ztXfHNP1gHws5lC8YsjAEOyZ2f6JHOGAmHOsRb
 | 
				
			||||||
 | 
					G1T9WS4wq4N1DJVJ1aQcgvF1BuejzUwyWTABgpbSQzCIaUXAeX9I
 | 
				
			||||||
 | 
					-----END RSA PRIVATE KEY-----
 | 
				
			||||||
		Reference in New Issue
	
	Block a user