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

Backend Engineer Coding Challenge - Maridin San Miguel #338

Open
wants to merge 10 commits into
base: master
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: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,7 @@

out/

.idea
.idea

.env
togo
151 changes: 129 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,137 @@
### Requirements
# TOGO

- Implement one single API which accepts a todo task and records it
- There is a maximum **limit of N tasks per user** that can be added **per day**.
- Different users can have **different** maximum daily limit.
- Write integration (functional) tests
- Write unit tests
- Choose a suitable architecture to make your code simple, organizable, and maintainable
- Write a concise README
- How to run your code locally?
- A sample “curl” command to call your API
- How to run your unit tests locally?
- What do you love about your solution?
- What else do you want us to know about however you do not have enough time to complete?
Backend Engineer Coding Challenge

### Notes
- Language: GoLang
- Database: PostgreSQL

- We're using Golang at Manabie. **However**, we encourage you to use the programming language that you are most comfortable with because we want you to **shine** with all your skills and knowledge.
# A. How to run this code locally?

### How to submit your solution?
## Install Go

- Fork this repo and show us your development progress via a PR
Follow this link to install golang https://golang.org/doc/install

### Interesting facts about Manabie
## Setup Golang $GOPATH directory

- Monthly there are about 2 million lines of code changes (inserted/updated/deleted) committed into our GitHub repositories. To avoid **regression bugs**, we write different kinds of **automated tests** (unit/integration (functionality)/end2end) as parts of the definition of done of our assigned tasks.
- We nurture the cultural values: **knowledge sharing** and **good communication**, therefore good written documents and readable, organizable, and maintainable code are in our blood when we build any features to grow our products.
- We have **collaborative** culture at Manabie. Feel free to ask [email protected] any questions. We are very happy to answer all of them.
Follow this link to setup $GOPATH env https://golang.org/doc/gopath_code.html

Thank you for spending time to read and attempt our take-home assessment. We are looking forward to your submission.
## Clone this repository in $GOPATH/src directory

1. Navigate to gopath directory by executing command

```
$ cd $GOPATH/src
```

2. Clone the repository by executing command

```bash
$ git clone [this repository]
```

## Create `.env` file from the root folder with variables:

```
DATABASE_HOST=<database_host>
DATABASE_PORT=5432
DATABASE_USERNAME=<database_user>
DATABASE_PASSWORD=<database_password>
SSL_MODE=<database_ssl_mode>
```

## Install the database migration tool, [goose](https://github.com/pressly/goose) locally

```bash
$ go install github.com/pressly/goose/v3/cmd/goose@latest
```

For macOS users goose is available as a Homebrew Formulae:

```bash
$ brew install goose
```

## Run the database migration scripts

1. Create a database in your server with the name `psql_togo_db`

2. Navigate to the `migrations` directory

```bash
$ cd $GOPATH/src/togo/migrations
```

3. Check the migration script status

```bash
$ goose postgres "user=[your user] password=[your password] dbname=psql_togo_db host=[your host] sslmode=[your sslmode]" status
```

4. Run the migration sript to apply the changes in the database

```bash
$ goose postgres "user=[your user] password=[your password] dbname=psql_togo_db host=[your host] sslmode=[your sslmode]" up
```

## Run the API

### From the root directory `$GOPATH/src/togo/`, run without creating executable

```bash
$ cd $GOPATH/src/togo/
$ go run server.go
```

### Create executable file

1. Run the command `go build` to create an executable file
2. Execute the generate file by specifying the name e.g. `./togo`

# B. Sample “curl” command to call the API

1. Create User

```bash
curl -X POST http://localhost:8080/api/user -d '{"username":"readme","taskDailyLimit":2}'
```

2. Update User Task Daily Limit

```bash
curl -X PATCH http://localhost:8080/api/user -d '{"username":"readme","taskDailyLimit":1}'
```

3. Create Task

```bash
curl -X POST http://localhost:8080/api/task -d '{"username":"readme","title":"Sample title","description":"Sample description"}'
```

4. Delete User And Created Tasks

```bash
curl -X DELETE http://localhost:8080/api/user -d '{"username":"readme"}'
```

# C. How to run your unit tests locally?

1. Navigate to `rest` directory

```bash
$ cd $GOPATH/src/togo/rest
```

2. Run the command to execute the test

```bash
$ go test -v
```

# D. What do I love about my solution?

Deciding to make simple APIs that enabled us to create a user, update a user daily task limit differently, create task and, delete a user that completes the cycle of integration testing based on the requirements.

# E. What else do you want us to know about however you do not have enough time to complete?

- Completing the go testing coverage
- I can also create a GraphQL API endpoint with schema first approach written in Golang
30 changes: 30 additions & 0 deletions Requirements.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
### Requirements

- Implement one single API which accepts a todo task and records it
- There is a maximum **limit of N tasks per user** that can be added **per day**.
- Different users can have **different** maximum daily limit.
- Write integration (functional) tests
- Write unit tests
- Choose a suitable architecture to make your code simple, organizable, and maintainable
- Write a concise README
- How to run your code locally?
- A sample “curl” command to call your API
- How to run your unit tests locally?
- What do you love about your solution?
- What else do you want us to know about however you do not have enough time to complete?

### Notes

- We're using Golang at Manabie. **However**, we encourage you to use the programming language that you are most comfortable with because we want you to **shine** with all your skills and knowledge.

### How to submit your solution?

- Fork this repo and show us your development progress via a PR

### Interesting facts about Manabie

- Monthly there are about 2 million lines of code changes (inserted/updated/deleted) committed into our GitHub repositories. To avoid **regression bugs**, we write different kinds of **automated tests** (unit/integration (functionality)/end2end) as parts of the definition of done of our assigned tasks.
- We nurture the cultural values: **knowledge sharing** and **good communication**, therefore good written documents and readable, organizable, and maintainable code are in our blood when we build any features to grow our products.
- We have **collaborative** culture at Manabie. Feel free to ask [email protected] any questions. We are very happy to answer all of them.

Thank you for spending time to read and attempt our take-home assessment. We are looking forward to your submission.
15 changes: 15 additions & 0 deletions common/environment/environment.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package environment

import (
"log"

"github.com/joho/godotenv"
)

// Load for loading the .env file from dynamic path
func Load(path string) {
err := godotenv.Load(path)
if err != nil {
log.Fatalf("Error to load file at %s", path)
}
}
59 changes: 59 additions & 0 deletions common/response/response.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package response

import (
"encoding/json"
"net/http"
"togo/models"
)

// HandleStatusOK for status ok response
func HandleStatusOK(w http.ResponseWriter, message interface{}, data interface{}) {
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(models.SuccessResponse{
Status: "Success",
Code: http.StatusOK,
Message: message,
Data: data,
})
}

// HandleStatusCreated for status created response
func HandleStatusCreated(w http.ResponseWriter, message interface{}, data interface{}) {
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(models.SuccessResponse{
Status: "Success",
Code: http.StatusCreated,
Message: message,
Data: data,
})
}

// HandleStatusBadRequest for status bad request response
func HandleStatusBadRequest(w http.ResponseWriter, message interface{}) {
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(models.ErrorResponse{
Status: "Failed",
Code: http.StatusBadRequest,
Message: message,
})
}

// HandleStatusInternalServerError for status internal server error response
func HandleStatusInternalServerError(w http.ResponseWriter, message interface{}) {
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(models.ErrorResponse{
Status: "Failed",
Code: http.StatusInternalServerError,
Message: message,
})
}

// HandleStatusNotFound for not found error response
func HandleStatusNotFound(w http.ResponseWriter, message interface{}) {
w.WriteHeader(http.StatusNotFound)
json.NewEncoder(w).Encode(models.ErrorResponse{
Status: "Failed",
Code: http.StatusNotFound,
Message: message,
})
}
43 changes: 43 additions & 0 deletions db/db.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package db

import (
"fmt"
"log"
"os"
"time"

"gorm.io/driver/postgres"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)

// Connect for postgreSQL DB
func Connect() (*gorm.DB, error) {

dbname := "psql_togo_db"

host := os.Getenv("DATABASE_HOST")
port := os.Getenv("DATABASE_PORT")
username := os.Getenv("DATABASE_USERNAME")
password := os.Getenv("DATABASE_PASSWORD")
sslmode := os.Getenv("SSL_MODE")

dsn := fmt.Sprintf("host=%s port=%s dbname=%s user=%s password=%s sslmode=%s", host, port, dbname, username, password, sslmode)

newLogger := logger.New(
log.New(os.Stdout, "\r\n", log.LstdFlags),
logger.Config{
SlowThreshold: time.Second,
LogLevel: logger.Error,
IgnoreRecordNotFoundError: true,
Colorful: true,
},
)

return gorm.Open(postgres.Open(dsn),
&gorm.Config{
Logger: newLogger,
PrepareStmt: true,
SkipDefaultTransaction: true,
})
}
30 changes: 30 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
module togo

go 1.18

require (
github.com/gorilla/mux v1.8.0
github.com/joho/godotenv v1.4.0
github.com/satori/go.uuid v1.2.0
github.com/stretchr/testify v1.8.0
gorm.io/driver/postgres v1.3.7
gorm.io/gorm v1.23.6
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
github.com/jackc/pgconn v1.12.1 // indirect
github.com/jackc/pgio v1.0.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgproto3/v2 v2.3.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
github.com/jackc/pgtype v1.11.0 // indirect
github.com/jackc/pgx/v4 v4.16.1 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.4 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
golang.org/x/text v0.3.7 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
Loading