Skip to content
Open
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
35 changes: 32 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,36 @@ https://github.com/veermuchandi/microservices-on-openshift.git \
oc expose svc/twitter-api
```

## 4. Create the Products API Microservice
> (Golang application)
This microservice is a Golang REST API that manages product data with full CRUD operations. It provides endpoints for creating, reading, updating, and deleting products, and integrates with other microservices in the architecture.

The Products API exposes the following REST endpoints:
- `GET /api/products` - List all products
- `POST /api/products` - Create a new product
- `GET /api/products/:id` - Get product by ID
- `PUT /api/products/:id` - Update product by ID
- `DELETE /api/products/:id` - Delete product by ID
- `GET /` - Health check endpoint

## 4. Create the frontend user registration application as a separate microservice
```sh
oc new-app -e EMAIL_SERVICE_URL="http://emailsvc-$OSE_PROJECT.$OSE_DOMAIN:8080" \
USER_SERVICE_URL="http://userregsvc-$OSE_PROJECT.$OSE_DOMAIN:8080" \
--context-dir='golang-products-api' \
https://github.com/veermuchandi/microservices-on-openshift.git \
--name='products-api' -l microservice=productssvc

oc expose svc/products-api
```

The service is configured with environment variables to communicate with other microservices:
- `EMAIL_SERVICE_URL` - Points to the Python email service for sending notifications
- `USER_SERVICE_URL` - Points to the Node.js user registration service for user validation

The Products API runs on port 8080 (like other microservices) and includes CORS support for frontend integration. It uses in-memory storage with sample products for demonstration purposes.


## 5. Create the frontend user registration application as a separate microservice
> (php application)
This microservice produces html+javascript to run in a browser and makes ajax calls to the backend User Registration service using REST APIs.
Note that we are setting an environment variable for userregsvc to access the backend using REST APIs.
Expand All @@ -158,15 +186,16 @@ $ oc expose svc/userreg
```
The service exposed in the above step is our application front end. You can find the URL by running ```oc get route```

## 5. Verification and Testing
## 6. Verification and Testing

> Visit http://userreg-msdev.apps.10.2.2.2.xip.io/ to see the php frontend.



## 6. Scaling applications
## 7. Scaling applications
> Suppose you have a huge traffic and you want to scale front end

```sh
oc scale dc/userreg --replicas=4
```

9 changes: 9 additions & 0 deletions golang-products-api/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module golang-products-api

go 1.21

require (
github.com/gin-contrib/cors v1.4.0
github.com/gin-gonic/gin v1.9.1
)

217 changes: 217 additions & 0 deletions golang-products-api/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
package main

import (
"net/http"
"os"
"strconv"
"time"

"github.com/gin-contrib/cors"
"github.com/gin-gonic/gin"
)

// Product represents a product in our system
type Product struct {
ID int `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Price float64 `json:"price"`
Category string `json:"category"`
InStock bool `json:"inStock"`
CreatedAt time.Time `json:"createdAt"`
}

// In-memory storage for products
var products []Product
var nextID int = 1

func main() {
// Initialize some sample products
initSampleProducts()

// Create Gin router
r := gin.Default()

// Configure CORS
config := cors.DefaultConfig()
config.AllowAllOrigins = true
config.AllowMethods = []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"}
config.AllowHeaders = []string{"Origin", "Content-Type", "Accept", "Authorization", "X-Requested-With"}
r.Use(cors.New(config))

// Health check endpoint
r.GET("/", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "Products API Works!",
"status": "healthy",
})
})

// API routes
api := r.Group("/api")
{
// GET /api/products - List all products
api.GET("/products", getProducts)

// POST /api/products - Create a new product
api.POST("/products", createProduct)

// GET /api/products/:id - Get product by ID
api.GET("/products/:id", getProductByID)

// PUT /api/products/:id - Update product by ID
api.PUT("/products/:id", updateProduct)

// DELETE /api/products/:id - Delete product by ID
api.DELETE("/products/:id", deleteProduct)
}

// Get port from environment or default to 8080
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}

// Start server
r.Run(":" + port)
}

func initSampleProducts() {
products = []Product{
{ID: 1, Name: "Laptop", Description: "High-performance laptop", Price: 999.99, Category: "Electronics", InStock: true, CreatedAt: time.Now()},
{ID: 2, Name: "Coffee Mug", Description: "Ceramic coffee mug", Price: 12.99, Category: "Kitchen", InStock: true, CreatedAt: time.Now()},
{ID: 3, Name: "Book", Description: "Programming guide", Price: 29.99, Category: "Books", InStock: false, CreatedAt: time.Now()},
}
nextID = 4
}

func getProducts(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"success": true,
"data": products,
"count": len(products),
})
}

func createProduct(c *gin.Context) {
var newProduct Product

if err := c.ShouldBindJSON(&newProduct); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"error": "Invalid JSON data",
})
return
}

// Set ID and creation time
newProduct.ID = nextID
newProduct.CreatedAt = time.Now()
nextID++

// Add to products slice
products = append(products, newProduct)

c.JSON(http.StatusCreated, gin.H{
"success": true,
"data": newProduct,
"message": "Product created successfully",
})
}

func getProductByID(c *gin.Context) {
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"error": "Invalid product ID",
})
return
}

for _, product := range products {
if product.ID == id {
c.JSON(http.StatusOK, gin.H{
"success": true,
"data": product,
})
return
}
}

c.JSON(http.StatusNotFound, gin.H{
"success": false,
"error": "Product not found",
})
}

func updateProduct(c *gin.Context) {
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"error": "Invalid product ID",
})
return
}

var updatedProduct Product
if err := c.ShouldBindJSON(&updatedProduct); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"error": "Invalid JSON data",
})
return
}

for i, product := range products {
if product.ID == id {
// Preserve ID and creation time
updatedProduct.ID = id
updatedProduct.CreatedAt = product.CreatedAt
products[i] = updatedProduct

c.JSON(http.StatusOK, gin.H{
"success": true,
"data": updatedProduct,
"message": "Product updated successfully",
})
return
}
}

c.JSON(http.StatusNotFound, gin.H{
"success": false,
"error": "Product not found",
})
}

func deleteProduct(c *gin.Context) {
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"error": "Invalid product ID",
})
return
}

for i, product := range products {
if product.ID == id {
// Remove product from slice
products = append(products[:i], products[i+1:]...)

c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "Product deleted successfully",
})
return
}
}

c.JSON(http.StatusNotFound, gin.H{
"success": false,
"error": "Product not found",
})
}

15 changes: 15 additions & 0 deletions golang-products-api/models/product.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package models

import "time"

// Product represents a product in the system
type Product struct {
ID int `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Price float64 `json:"price"`
Category string `json:"category"`
InStock bool `json:"inStock"`
CreatedAt time.Time `json:"createdAt"`
}

13 changes: 13 additions & 0 deletions installscripts/6.deployProductsAPI-Golang.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
oc project $OSE_SERVICES_PROJECT

export EMAIL_SERVICE_URL="http://emailsvc."$OSE_INFRA_PROJECT":8080"
export USER_SERVICE_URL="http://userregsvc."$OSE_SERVICES_PROJECT":8080"

oc new-app -e EMAIL_SERVICE_URL=$EMAIL_SERVICE_URL \
USER_SERVICE_URL=$USER_SERVICE_URL \
--context-dir='golang-products-api' \
https://github.com/debianmaster/microservices-on-openshift.git \
--name='products-api' -l microservice=productssvc

oc expose svc/products-api