Skip to content
This repository was archived by the owner on Jan 6, 2026. It is now read-only.
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
9 changes: 9 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
APP_HOST=
APP_PORT=8080
APP_SECRET=8c6c19688d9f94b3900042923aa94a99

DB_HOST=localhost
DB_PORT=5432
DB_NAME=manabie
DB_USERNAME=manabie
DB_PASSWORD=12345
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
*.out

# Dependency directories (remove the comment below to include it)
# vendor/
vendor/

out/

Expand Down
10 changes: 10 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
FROM golang:1.18.4
WORKDIR /go/src
COPY . /go/src
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -installsuffix cgo -o start cmd/main.go

FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=0 /go/src/start .
CMD ["./start"]
278 changes: 254 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,32 +1,262 @@
### Requirements
### About the app
This app is built in Golang and PostgreSQL (for storing data). Gorilla Mux is the default router which is using in the app and for integration test too.

- 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
- Using Docker to run locally
- Using Docker for database (if used) is mandatory.
- 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?
The app is inspired heavily by Laravel, so you would feel familiar if you had developed with Laravel. I also update JWT Authentication and full CRUD for this app.

### Notes
### How to run the app?
```shell
docker-compose up
```

- 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.
Default app port is `8080`, if you your `8080` port is not available, do not hesitate to change to another one in `docker-compose.yml`

### How to submit your solution?
### How to use the app?
1. #### Register new account

- Fork this repo and show us your development progress via a PR
Method: `POST`

### Interesting facts about Manabie
URL: `/api/register`

- 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 trieu@manabie.com any questions. We are very happy to answer all of them.
cURL:
```shell
curl --location --request POST 'http://localhost:8080/api/register' \
--header 'Content-Type: application/json' \
--data-raw '{
"email": "huuthuan.nguyen@hotmail.com",
"password": "123@pass!word"
}'
```

Thank you for spending time to read and attempt our take-home assessment. We are looking forward to your submission.
Payload:
```json
{
"email": "huuthuan.nguyen@hotmail.com",
"password": "123@pass!word"
}
```

Response:
```json
{
"status": 1,
"messages": [
"Successful."
],
"data": {
"id": 1,
"email": "huuthuan.nguyen@hotmail.com",
"is_active": true,
"daily_limit": 3,
"create_at": "2022-07-25T18:02:49.291850383Z",
"updated_at": "2022-07-25T18:02:49.291852258Z"
}
}
```

Default quota for your tasks is 3 per day, you can not change this.

2. #### Login with your email and password

Method: `POST`

URL: `/api/auth/login`

cURL:
```shell
curl --location --request POST 'http://localhost:8080/api/auth/login' \
--header 'Content-Type: application/json' \
--data-raw '{
"email": "huuthuan.nguyen@hotmail.com",
"password": "123@pass!word"
}'
```

Payload:
```json
{
"email": "huuthuan.nguyen@hotmail.com",
"password": "123@pass!word"
}
```

Response:
```json
{
"status": 1,
"messages": [
"Successful."
],
"data": {
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6Imh1dXRodWFuLm5ndXllbkBob3RtYWlsLmNvbSIsImV4cCI6MTY1ODg1ODY0NH0.vfglN4fOZ7NDat1hxznivNk9T4znCgw4l8K6XFemAYQ",
"expired_at": "2022-07-26T18:04:04.060592793Z"
}
}
```

3. #### Add new task

Method: `POST`

URL: `/api/tasks`

cURL:
```shell
curl --location --request POST 'http://localhost:8080/api/tasks' \
--header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6Imh1dXRodWFuLm5ndXllbkBob3RtYWlsLmNvbSIsImV4cCI6MTY1ODg1ODY0NH0.vfglN4fOZ7NDat1hxznivNk9T4znCgw4l8K6XFemAYQ' \
--header 'Content-Type: application/json' \
--data-raw '{
"content": "Do homework"
}'
```

Payload:
```json
{
"content": "Do homework"
}
```

Response
```json
{
"status": 1,
"messages": [
"Successful."
],
"data": {
"id": 1,
"content": "Do homework",
"published_date": "2022-07-25",
"status": 0,
"created_by": 1,
"create_at": "2022-07-25T18:04:30.128784138Z",
"updated_at": "2022-07-25T18:04:30.128785013Z"
}
}
```

4. #### Update your task by ID

Method: `PUT`

URL: `/api/tasks/:id`

cURL:
```shell
curl --location --request PUT 'http://localhost:8080/api/tasks/1' \
--header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6Imh1dXRodWFuLm5ndXllbkBob3RtYWlsLmNvbSIsImV4cCI6MTY1ODg1ODY0NH0.vfglN4fOZ7NDat1hxznivNk9T4znCgw4l8K6XFemAYQ' \
--header 'Content-Type: application/json' \
--data-raw '{
"content": "Do homework later"
}'
```

Payload:
```json
{
"content": "Do homework later"
}
```

Response:
```json
{
"status": 1,
"messages": [
"Successful."
],
"data": {
"id": 1,
"content": "Do homework later",
"published_date": "2022-07-25",
"status": 0,
"created_by": 1,
"create_at": "2022-07-25T18:04:30.128784138Z",
"updated_at": "2022-07-25T18:07:45.866218Z"
}
}
```

5. #### Remove your task by ID

Method: `DELETE`

URL: `/api/tasks/:id`

cURL:
```shell
curl --location --request DELETE 'http://localhost:8080/api/tasks/1' \
--header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6Imh1dXRodWFuLm5ndXllbkBob3RtYWlsLmNvbSIsImV4cCI6MTY1ODg1ODY0NH0.vfglN4fOZ7NDat1hxznivNk9T4znCgw4l8K6XFemAYQ'
```

Payload:
```json

```
Response
```json
```

6. #### List your tasks

Method: `GET`

URL: `/api/tasks`

cURL:
```shell
curl --location --request GET 'http://localhost:8080/api/tasks' \
--header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6Imh1dXRodWFuLm5ndXllbkBob3RtYWlsLmNvbSIsImV4cCI6MTY1ODg1ODY0NH0.vfglN4fOZ7NDat1hxznivNk9T4znCgw4l8K6XFemAYQ'
```

Payload:
```json

```

Response:
```json
{
"status": 1,
"messages": [
"Successful."
],
"data": {
"items": [
{
"id": 1,
"content": "Do homework later",
"published_date": "2022-07-25",
"status": 0,
"created_by": 1,
"create_at": "2022-07-25T18:04:30.128784138Z",
"updated_at": "2022-07-25T18:07:45.866218Z"
},
{
"id": 1,
"content": "Do laundry",
"published_date": "2022-07-25",
"status": 0,
"created_by": 1,
"create_at": "2022-07-25T18:04:30.128784138Z",
"updated_at": "2022-07-25T18:07:45.866218Z"
}
]
}
}
```

### How to run test locally?

Requisite: start your app with docker
```shell
APP_HOST= APP_PORT=8080 APP_SECRET=8c6c19688d9f94b3900042923aa94a99 DB_HOST=localhost DB_PORT=5432 DB_NAME=manabie DB_USERNAME=manabie DB_PASSWORD=12345 go test -v -count=1 ./test
```

### Final

- This app need to be optimized the database, use the aggregated column for checking exceeded daily limit instead of `count` query, because `count` query is so expensive when the records grow up.
- We need to move the testing bash script to docker-compose.yml too
- Add swagger instead of using Postman

Postman Collection: `https://www.getpostman.com/collections/2cdc08395f26e9047e4f`
60 changes: 60 additions & 0 deletions app/app.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package app

import (
"context"
"fmt"
"github.com/gorilla/mux"
"github.com/huuthuan-nguyen/manabie/app/handler"
"github.com/huuthuan-nguyen/manabie/app/router"
"github.com/huuthuan-nguyen/manabie/config"
"github.com/huuthuan-nguyen/manabie/migration"
"log"
"net/http"
"time"
)

type App struct {
router *mux.Router
handler *handler.Handler
ctx context.Context
server *http.Server
config *config.Config
}

func (app *App) Run() {

if err := app.Migrate(); err != nil {
log.Fatalf("Migrating fail:%s\n", err)
return
}

app.server = &http.Server{
Addr: fmt.Sprintf("%s:%s", app.config.Server.Host, app.config.Server.Port),
Handler: app.router,
WriteTimeout: 30 * time.Second,
ReadTimeout: 30 * time.Second,
}
defer app.server.Close()

log.Printf("Listening on %s:%v...\n", app.config.Server.Host, app.config.Server.Port)
log.Fatalln(app.server.ListenAndServe())
}

func NewApp(config *config.Config) *App {
c := context.Background()
h := handler.New(c, config)
r := router.NewRouter(config, h)

return &App{
router: r,
handler: h,
ctx: c,
config: config,
}
}

// Migrate /**
func (app *App) Migrate() error {
migrateEngine := migration.NewEngine(app.config)
return migrateEngine.Migrate()
}
Loading