# README
GopherNet-golang
a platform for managing rentals of gopher burrows.
Project Structure
.
├── app_architecture.drawio.png
├── architecture-img.png
├── Architecture_project.md
├── Architecture_project.pdf
├── cloud_architecture.drawio.png
├── cloud_architecture.png
├── cmd
│ ├── commands.go
│ └── root.go
├── cover.out
├── curl_burrow_status.sh
├── curl_rent_burrow.sh
├── data
│ └── initial.json
├── docs
│ ├── docs.go
│ ├── swagger.json
│ └── swagger.yaml
├── dto
│ ├── burrow.go
│ └── http_responses.go
├── gain.pro senior go engineer assignment.pdf
├── go.mod
├── go.sum
├── LICENSE
├── main.go
├── makefile
├── mocks
│ └── fakes.go
├── README.md
├── report.txt
├── server
│ ├── gorm.db
│ ├── helper_func.go
│ ├── http_burrow.go
│ ├── http_burrow_test.go
│ └── http.go
├── services
│ ├── burrow_svc.go
│ ├── burrow_svc_test.go
│ └── gorm.db
├── storage
│ ├── model
│ │ └── burrow.go
│ ├── pg
│ │ ├── burrow.go
│ │ ├── db.go
│ │ └── db_test.go
│ └── storage.go
└── utils
└── helper_func.go
Installation
make postgres
make createdb
Usage
Usage:
gopherne [flags]
gopherne [command]
Available Commands:
completion Generate the autocompletion script for the specified shell
help Help about any command
load-burrows Load Burrows from initial.json
rest Run rest server
Flags:
-h, --help help for gopherne
Use "gopherne [command] --help" for more information about a command.
if you want to load the initial burrows:
go run main.go load-burrows
to test the rest server:
go run main.go rest
then in another terminal:
curl -X GET http://localhost:8080/burrows
or install jq:
# For Linux
sudo apt-get update && sudo apt-get install jq
# For Mac
brew install jq
# For Windows with chocolatey
chocolatey install jq
and run:
sudo chmod +x ./curl_burrow_status.sh
./curl_burrow_status.sh
sudo chmod +x ./curl_rent_burrow.sh
./curl_rent_burrow.sh 9
9 is the burrow id
- Install golangci-lint:
# binary will be $(go env GOPATH)/bin/golangci-lint
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.54.2
golangci-lint --version
- Run linter:
make run-lint
Solutions
Requirements 1:
I created command line application to load the initial burrows from data/initial.json
file.
Load the initial burrows:
go run main.go load-burrows
Requirements 2:
I used golang Ticker to run background task to Update the burrows status every 5 seconds.
func (svc *BurrowService) RunUpdateStatusTask(duration time.Duration) error {
burrows, err := svc.Storage.IndexBurrow() // Index the burrows
if err != nil {
return err
}
if len(burrows) == 0 {
return fmt.Errorf("no burrows found")
}
for _, burrow := range burrows {
b := burrow
go func() {
job := func() {
fmt.Println("Running the job at", time.Now())
fmt.Printf("Burrow name: %s\n", b.Name)
b.Age++
fmt.Printf("Burrow name: %s age increased to %d at % v \n", b.Name, b.Age, time.Now())
if b.Occupied {
b.Depth += (b.Depth * 0.9)
}
if b.Occupied && ((b.Age / 1440) >= 25) { //Burrow age (A, in minutes), with each burrow lasting exactly 25 days before collapsing.
b.Occupied = false
err := svc.Storage.UpdateBurrowAttributes(b.ID, map[string]interface{}{"occupied": false, "age": b.Age, "depth": b.Depth})
if err != nil {
log.Printf("Error updating burrow: %v\n", err)
}
} else {
err := svc.Storage.UpdateBurrowAttributes(b.ID, map[string]interface{}{"age": b.Age, "depth": b.Depth})
if err != nil {
log.Printf("Error updating burrow: %v\n", err)
}
}
log.Printf("Burrow state: %+v\n", b)
}
// Create a ticker that triggers every minute
ticker := time.NewTicker(duration)
// Run the job when the ticker triggers
for range ticker.C {
job() // Execute the job
}
}()
}
return nil
}
Requirements 3 & 4:
I created a rest server to manage the burrows.
to run the rest server:
go run main.go rest
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)
[GIN-debug] GET /api/v1/ping --> github.com/hamdiBouhani/GopherNet-golang/server.(*HttpService).ping-fm (4 handlers)
[GIN-debug] GET /api/v1/swagger/*any --> github.com/swaggo/gin-swagger.CustomWrapHandler.func1 (4 handlers)
[GIN-debug] GET /api/v1/burrows --> github.com/hamdiBouhani/GopherNet-golang/server.(*HttpService).BurrowStatus-fm (4 handlers)
[GIN-debug] PUT /api/v1/rent-burrow/:id --> github.com/hamdiBouhani/GopherNet-golang/server.(*HttpService).RentBurrow-fm (4 handlers)
Requirements 5:
I used golang Ticker to run background task to Create a report summarising the total depth and number of available burrows.
// Reporting: Create a report summarising the total depth and number of available
// burrows, as well as the largest and smallest burrows - by volume.
// You can assume the burrows are cylindrical (where width is the diameter).
// This reportshould be outputted as a plain text file every 10 minutes.
func (svc *BurrowService) Report(duration time.Duration) error {
// Get the current working directory
currentDir, err := os.Getwd()
if err != nil {
fmt.Printf("Failed to get the current working directory: %v\n", err)
return err
}
dataDir := strings.TrimSuffix(currentDir, "services")
// Create the absolute path to the JSON file
fileName := filepath.Join(dataDir, "report.txt")
// Create a ticker that triggers every minute
ticker := time.NewTicker(duration)
// Run the job when the ticker triggers
for range ticker.C {
burrows, err := svc.Storage.IndexBurrow()
if err != nil {
return err
}
if len(burrows) == 0 {
return fmt.Errorf("Report : no burrows found")
}
// Calculate total depth, number of burrows, and find the largest and smallest burrows
var totalDepth float64
var largestVolume float64 = 0
var smallestVolume float64 = math.MaxFloat64
var largestBurrow *model.Burrow
var smallestBurrow *model.Burrow
for _, burrow := range burrows {
totalDepth += burrow.Depth
volume := utils.CalculateVolume(burrow)
fmt.Printf("Burrow name: %s, Volume: %.2f\n", burrow.Name, volume)
if volume >= largestVolume {
largestVolume = volume
largestBurrow = burrow
}
if volume < smallestVolume {
smallestVolume = volume
smallestBurrow = burrow
}
}
// Prepare the report
report := fmt.Sprintf("Report (Generated at %s):\n", time.Now())
report += fmt.Sprintf("Total Depth: %.2f\n", totalDepth)
report += fmt.Sprintf("Number of Burrows: %d\n", len(burrows))
report += fmt.Sprintf("Largest Burrow - Name: %s, Volume: %.2f\n", largestBurrow.Name, largestVolume)
report += fmt.Sprintf("Smallest Burrow - Name: %s, Volume: %.2f\n", smallestBurrow.Name, smallestVolume)
// Open the file in write mode
file, err := os.OpenFile(fileName, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
fmt.Printf("Error opening file: %v\n", err)
return err
}
// Write the report to the file
if _, err := file.WriteString(report); err != nil {
fmt.Printf("Error writing to file: %v\n", err)
}
// Write the report to the file
if _, err := file.WriteString("--------------------------------\n"); err != nil {
fmt.Printf("Error writing to file: %v\n", err)
}
// Close the file
if err := file.Close(); err != nil {
fmt.Printf("Error closing file: %v\n", err)
}
fmt.Println("Report generated and saved to", fileName)
}
return nil
}
# Packages
No description provided by the author
Package docs Code generated by swaggo/swag.
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author