Skip to content

Golang Assessment [ added two microservice ] #82

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

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
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
5 changes: 5 additions & 0 deletions golang/ASSIGMENT/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
generate-proto:
rm -f pb/proto/*.pb.go
protoc --go_out=pb/ --go_opt=paths=source_relative \
--go-grpc_out=pb/ --go-grpc_opt=paths=source_relative \
proto/*.proto
10 changes: 10 additions & 0 deletions golang/ASSIGMENT/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
version: '3.8'

services:
oms:
build: ./oms
# You can add other configurations for this service

ums:
build: ./ums
# You can add other configurations for this service
33 changes: 33 additions & 0 deletions golang/ASSIGMENT/oms/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Dockerfile References: https://docs.docker.com/engine/reference/builder/
# Start from the latest golang base image
FROM golang:1.21.4-alpine3.17 AS builder


RUN apk update
RUN apk add git gcc musl-dev
RUN apk add curl bash

WORKDIR /go/src/app

# Copy the Go application source code into the container
COPY . .

# Add Maintainer Info
LABEL maintainer="Arijit Nayak"
ENV APP_NAME "oms"

#You can define a variable with:
#ARG myvalue=3
# Create work env
RUN mkdir -p $GOPATH/src/$CODE_PATH/
ADD . $GOPATH/src/$CODE_PATH/
WORKDIR $GOPATH/src/$CODE_PATH/

# Run go clean?
RUN go install -tags dynamic ./...

# run the command to start es data service
CMD $GOPATH/bin/ums

# Expose port 8082 to the outside world
EXPOSE 5060
11 changes: 11 additions & 0 deletions golang/ASSIGMENT/oms/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
generate-proto:
rm -f pb/proto/*.pb.go
protoc --go_out=pb/ --go_opt=paths=source_relative \
--go-grpc_out=pb/ --go-grpc_opt=paths=source_relative \
proto/*.proto
db-init:
psql -c 'CREATE DATABASE "user-management-service"' -U $(user)
migrationup:
migrate -path db/migrations -database "postgres://$(user):$(password)@$(host):$(port)/user-management-service?sslmode=disable" -verbose up
migrationdown:
migrate -path db/migrations -database "postgres://$(user):$(password)@$(host):$(port)/user-management-service?sslmode=disable" -verbose down
22 changes: 22 additions & 0 deletions golang/ASSIGMENT/oms/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
module oms

go 1.21.4

require (
github.com/joho/godotenv v1.5.1
github.com/lib/pq v1.10.9
github.com/xhit/go-str2duration/v2 v2.1.0
github.myproto.com v0.0.0-00010101000000-000000000000
google.golang.org/grpc v1.59.0
)

require (
github.com/golang/protobuf v1.5.3 // indirect
golang.org/x/net v0.14.0 // indirect
golang.org/x/sys v0.11.0 // indirect
golang.org/x/text v0.12.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect
google.golang.org/protobuf v1.31.0 // indirect
)

replace github.myproto.com => ../pb/proto/
27 changes: 27 additions & 0 deletions golang/ASSIGMENT/oms/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc=
github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU=
golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M=
google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk=
google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
123 changes: 123 additions & 0 deletions golang/ASSIGMENT/oms/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package main

import (
"flag"
"log"
"os"
"os/signal"
"time"

"oms/pkg/db"
"oms/pkg/domain"
grpcPkg "oms/pkg/grpc"
"oms/pkg/grpchandler"

"github.com/joho/godotenv"
strToDuration "github.com/xhit/go-str2duration/v2"
)

var (
// Derived from ldflags -X
buildRevision string
buildVersion string
buildTime string
// general options
versionFlag bool
helpFlag bool
// timeout flag
timeout int
// grpc server port
grpcServerPort string
postgresURI string
// retryInterval for db connection
retryInterval string
// program controller
done = make(chan struct{})
errGrpc = make(chan error)
)

func init() {
err := godotenv.Load()
if err != nil {
log.Println(".env file not found")
}
flag.BoolVar(&versionFlag, "version", false, "show current version and exit")
flag.BoolVar(&helpFlag, "help", false, "show usage and exit")
flag.StringVar(&grpcServerPort, "grpcServerPort", ":5060", "grpc server port")
flag.IntVar(&timeout, "timeout", 1, "timeout for db connection")
postgresURI = os.Getenv("POSTGRESQL_DB_URL")
if postgresURI == "" {
log.Fatal("postgres db url not found")
}
if retryInterval = os.Getenv("RETRY_INTERVAL"); len(retryInterval) == 0 {
log.Println("retry interval for db connection not found !")
return
}
}

func setBuildVariables() {
if buildRevision == "" {
buildRevision = "dev"
}
if buildVersion == "" {
buildVersion = "dev"
}
if buildTime == "" {
buildTime = time.Now().UTC().Format(time.RFC3339)
}
}

func parseFlags() {
flag.Parse()
if helpFlag {
flag.Usage()
os.Exit(0)
}
if versionFlag {
log.Printf("%s %s %s\n", buildRevision, buildVersion, buildTime)
os.Exit(0)
}
}

func handleInterrupts() {
log.Println("start handle interrupts")
interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, os.Interrupt)
sig := <-interrupt
log.Printf("caught sig: %v", sig)
// close resource here
done <- struct{}{}
}

func main() {
log.SetFlags(log.LstdFlags | log.Lshortfile)
setBuildVariables()
parseFlags()
go handleInterrupts()
log.SetFlags(log.LstdFlags | log.Lshortfile)
retryIntervalDuration, err := strToDuration.ParseDuration(retryInterval)
if err != nil {
log.Fatal("unable to parse string to duration : ", err)
}
postgreSQL := db.Connect2DB(postgresURI, retryIntervalDuration)
defer postgreSQL.Close()

d := domain.NewOrderCliet(postgreSQL, timeout)

grpcHandler := grpchandler.NewGrpcHandler(d)
grpcServer := grpcPkg.NewGrpcServer(grpcServerPort, grpcHandler)
go func() {
log.Printf("GRPC sever running on port: %v\n", grpcServerPort)
errGrpc <- grpcServer.ListenAndServe()
}()
select {
case err := <-errGrpc:
log.Print("Grpc error", err)
case <-done:
log.Println("shutting down server ...")
}
time.AfterFunc(1*time.Second, func() {
close(done)
close(errGrpc)
})
}
15 changes: 15 additions & 0 deletions golang/ASSIGMENT/oms/pkg/db/db.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package db

import (
"context"
"database/sql"
)

// Database ...
type Database interface {
PingContext(ctx context.Context) error
Exec(query string, args ...interface{}) (sql.Result, error)
Close() error
QueryRow(query string, args ...interface{}) *sql.Row
Query(query string, args ...interface{}) (*sql.Rows, error)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
BEGIN;
DROP TABLE IF EXISTS "orders";
DROP FUNCTION IF EXISTS update_modified_column;
DROP TRIGGER [IF EXISTS] update_order
ON "orders" [ CASCADE | RESTRICT ];
COMMIT;
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
BEGIN;

CREATE TYPE ORDER_STAUTS IF NOT EXISTS AS ENUM('INITIALIZED','PROCESSING','SHIPPED','DELIVERED');

CREATE TABLE IF NOT EXISTS "orders" (
"ID" SERIAL PRIMARY KEY,
"buyerUserID" VARCHAR NOT NULL,
"status" ORDER_STAUTS NOT NULL,
"totalAmount" NUMERIC NOT NULL,
"products" JSONB,
"paymentID" VARCHAR NOT NULL,
"createdAt" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
"updatedAt" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
);

CREATE OR REPLACE FUNCTION update_modified_column()
RETURNS TRIGGER AS $$
BEGIN
NEW."updatedAt" = now();
RETURN NEW;
END;

CREATE TRIGGER update_order BEFORE UPDATE ON "orders" FOR EACH ROW EXECUTE PROCEDURE update_modified_column();

COMMIT;
30 changes: 30 additions & 0 deletions golang/ASSIGMENT/oms/pkg/db/postgres.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package db

import (
"database/sql"
"log"
"time"

_ "github.com/lib/pq"
)

// QuitRetry is the exit point for retrying from a infinite loop
var QuitRetry = make(chan bool)

// Connect2DB extablish the connection to the database
func Connect2DB(connStr string, dbRetry time.Duration) *sql.DB {
for {
select {
case <-QuitRetry:
return nil
case <-time.After(dbRetry):
DB, err := sql.Open("postgres", connStr)
if err != nil {
log.Println("failed to connect to database: ", err)
} else {
return DB
}
log.Printf("reconnecting to database, after: %v milliseconds\n", dbRetry)
}
}
}
31 changes: 31 additions & 0 deletions golang/ASSIGMENT/oms/pkg/domain/domain.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package domain

import (
"oms/pkg/models"

"oms/pkg/db"
)

// Order ...
type Order interface {
GetHealth() bool
CreateOrder(order *models.Order) (string, error)
GetOrdersByUserID(userID string) ([]models.Order, error)
DeleteOrderByUserID(userID string) error
DeleteOrderByOrderID(orderID string) error
GetOrder(orderID string) (*models.Order, error)
}

// UserClient ...
type OrderCliet struct {
DB db.Database
Timeout int
}

// NewUserClient ...
func NewOrderCliet(db db.Database, timeout int) *OrderCliet {
return &OrderCliet{
DB: db,
Timeout: timeout,
}
}
18 changes: 18 additions & 0 deletions golang/ASSIGMENT/oms/pkg/domain/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package domain

import "errors"

var (
// ErrOrderCreationFailed ...
ErrOrderCreationFailed = errors.New("failed to create the order")
// ErrInvalidData ...
ErrInvalidData = errors.New("invalid data received from the client")
// ErrOrderNotFound ...
ErrOrderNotFound = errors.New("requested order not found")
// ErrInternalError ...
ErrInternalError = errors.New("internal error")
// ErrFailedToDeleteOrders ...
ErrFailedToDeleteOrders = errors.New("failed to delete all the orders under the specified userID")
// ErrFailedToDeleteOrder ...
ErrFailedToDeleteOrder = errors.New("failed to delete the order")
)
19 changes: 19 additions & 0 deletions golang/ASSIGMENT/oms/pkg/domain/health.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package domain

import (
"context"
"log"
"time"
)

// GetHealth checks if the the database connection is live or not
func (d *OrderCliet) GetHealth() bool {
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(d.Timeout)*time.Second)
defer cancel()
err := d.DB.PingContext(ctx)
if err != nil {
log.Println("failed to connect to database : ", err)
return false
}
return true
}
Loading