Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Master PR #2

Merged
merged 7 commits into from
Dec 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ jobs:
matrix:
microservices:
- user_service
- product_service
steps:
- uses: actions/checkout@v3
- name: Set up Go
Expand Down
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
.vscode/
*.env
*.exe
13 changes: 10 additions & 3 deletions run_services.sh
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
#!/bin/bash/

# Create common networks for the services
docker network create -d bridge shared-network
echo "Shared network created for microservices communication"

# Run User service
docker-compose -f src/user_service/docker-compose.yml up -d
echo "User services are up and running!"

# Run Product service
# docker-compose -f src/product_service/docker-compose.yml up -d
docker-compose -f src/product_service/docker-compose.yml up -d
echo "Product services are up and running!"

# RUn Order service
# docker-compose -f src/order_service/docker-compose.yml up -d
# Run Order service
#docker-compose -f src/order_service/docker-compose.yml up -d
#echo "Order services are up and running!"
11 changes: 11 additions & 0 deletions src/product_service/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
DB_NAME=products_db
DB_USER=db_user
DB_PASSWORD=1234
DB_URL=postgres://db_user:1234@products-db/products_db?sslmode=disable

CGO_ENABLED=1
PORT=8002
GIN_MODE=debug

INTERNAL_API_SECRET=1234_secret_4321
USER_SERVICE_HOST=user_service:8001
1 change: 0 additions & 1 deletion src/product_service/.gitignore

This file was deleted.

37 changes: 37 additions & 0 deletions src/product_service/api/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Common config modules

package api

import (
"database/sql"
"fmt"

"github.com/redis/go-redis/v9"
"github.com/thejasmeetsingh/go-ecommerce/product_service/internal/database"
)

type APIConfig struct {
DB *sql.DB
Queries *database.Queries
Cache *redis.Client
}

func GetDBConn(dbURL string) *sql.DB {
conn, err := sql.Open("postgres", dbURL)

if err != nil {
panic(fmt.Sprintf("Cannot connect to the database: %v", err))
}

return conn
}

func GetRedisClient() *redis.Client {
client := redis.NewClient(&redis.Options{
Addr: "products_cache:6379",
Password: "",
DB: 0,
})

return client
}
112 changes: 112 additions & 0 deletions src/product_service/api/crud.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package api

import (
"fmt"

"github.com/gin-gonic/gin"
"github.com/google/uuid"
log "github.com/sirupsen/logrus"
"github.com/thejasmeetsingh/go-ecommerce/product_service/internal/database"
)

// Create product
func CreateProductDB(apiCfg *APIConfig, ctx *gin.Context, params database.CreateProductParams) (database.Product, error) {
// Begin DB transaction
tx, err := apiCfg.DB.Begin()
if err != nil {
log.Fatal(err)
return database.Product{}, fmt.Errorf("something went wrong")
}
defer tx.Rollback()
qtx := apiCfg.Queries.WithTx(tx)

dbProduct, err := qtx.CreateProduct(ctx, params)

if err != nil {
log.Errorln(err)
return database.Product{}, fmt.Errorf("something went wrong")
}

// Commit the transaction
err = tx.Commit()
if err != nil {
log.Fatal(err)
return database.Product{}, fmt.Errorf("something went wrong")
}

return dbProduct, nil
}

// Get list of products
func GetProductListDB(apiCfg *APIConfig, ctx *gin.Context, params database.GetProductsParams) ([]database.GetProductsRow, error) {
products, err := apiCfg.Queries.GetProducts(ctx, params)
if err != nil {
return []database.GetProductsRow{}, fmt.Errorf("something went wrong")
}
return products, nil
}

// Get details of a specific product
func GetProductDetailDB(apiCfg *APIConfig, ctx *gin.Context, productID uuid.UUID) (database.Product, error) {
product, err := apiCfg.Queries.GetProductById(ctx, productID)
if err != nil {
log.Errorln(err)
return database.Product{}, fmt.Errorf("something went wrong")
}
return product, nil
}

// Update product details
func UpdateProductDetailDB(apiCfg *APIConfig, ctx *gin.Context, params database.UpdateProductDetailsParams) (database.Product, error) {
// Begin DB transaction
tx, err := apiCfg.DB.Begin()
if err != nil {
log.Fatal(err)
return database.Product{}, fmt.Errorf("something went wrong")
}
defer tx.Rollback()
qtx := apiCfg.Queries.WithTx(tx)

dbProduct, err := qtx.UpdateProductDetails(ctx, params)

if err != nil {
log.Errorln(err)
return database.Product{}, fmt.Errorf("something went wrong")
}

// Commit the transaction
err = tx.Commit()
if err != nil {
log.Fatal(err)
return database.Product{}, fmt.Errorf("something went wrong")
}

return dbProduct, nil
}

// Delete a product
func DeleteProductDetailDB(apiCfg *APIConfig, ctx *gin.Context, productID uuid.UUID) error {
// Begin DB transaction
tx, err := apiCfg.DB.Begin()
if err != nil {
log.Fatal(err)
return fmt.Errorf("something went wrong")
}
defer tx.Rollback()
qtx := apiCfg.Queries.WithTx(tx)

err = qtx.DeleteProduct(ctx, productID)
if err != nil {
log.Errorln(err)
return fmt.Errorf("something went wrong")
}

// Commit the transaction
err = tx.Commit()
if err != nil {
log.Fatal(err)
return fmt.Errorf("something went wrong")
}

return nil
}
44 changes: 44 additions & 0 deletions src/product_service/api/middleware.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package api

import (
"net/http"
"strings"

"github.com/gin-gonic/gin"
log "github.com/sirupsen/logrus"
"github.com/thejasmeetsingh/go-ecommerce/product_service/shared"
)

func Auth(apiCfg *APIConfig) gin.HandlerFunc {
return func(ctx *gin.Context) {
headerAuthToken := ctx.GetHeader("Authorization")

if headerAuthToken == "" {
ctx.JSON(http.StatusForbidden, gin.H{"message": "Authentication required"})
ctx.Abort()
return
}

// Split the token string
authToken := strings.Split(headerAuthToken, " ")

// Validate the token string
if len(authToken) != 2 || authToken[0] != "Bearer" {
ctx.JSON(http.StatusForbidden, gin.H{"message": "Invalid authentication format"})
ctx.Abort()
return
}

// Fetch userID and set to the context
userID, err := shared.GetUserFromToken(apiCfg.Cache, ctx, authToken[1])
if err != nil {
log.Errorln(err)
ctx.JSON(http.StatusForbidden, gin.H{"message": "Something went"})
ctx.Abort()
return
}

ctx.Set("userID", userID)
ctx.Next()
}
}
49 changes: 49 additions & 0 deletions src/product_service/api/models.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package api

import (
"time"

"github.com/google/uuid"
"github.com/thejasmeetsingh/go-ecommerce/product_service/internal/database"
)

type productDetail struct {
ID uuid.UUID `json:"id"`
CreatedAt time.Time `json:"created_at"`
ModifiedAt time.Time `json:"modified_at"`
Name string `json:"name"`
Price int32 `json:"price"`
Description string `json:"description"`
}

type productList struct {
ID uuid.UUID `json:"id"`
Name string `json:"name"`
Price int32 `json:"price"`
Description string `json:"description"`
}

func DatabaseProductToProduct(dbProduct database.Product) productDetail {
return productDetail{
ID: dbProduct.ID,
CreatedAt: dbProduct.CreatedAt,
ModifiedAt: dbProduct.ModifiedAt,
Name: dbProduct.Name,
Price: dbProduct.Price,
Description: dbProduct.Description,
}
}

func DatabaseProductToProductList(dbProducts []database.GetProductsRow) []productList {
var products []productList

for _, dbProduct := range dbProducts {
products = append(products, productList{
ID: dbProduct.ID,
Name: dbProduct.Name,
Price: dbProduct.Price,
Description: dbProduct.Description,
})
}
return products
}
Loading
Loading