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
15 changes: 15 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
FROM golang:1.19

WORKDIR /app/manabie

COPY go.* ./

RUN go mod tidy

COPY . ./

RUN cd /app/manabie/cmd/ && go build -o /server

EXPOSE 9000

CMD ["/server"]
85 changes: 53 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,32 +1,53 @@
### 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
- 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?

### 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 trieu@manabie.com 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.
### Run Project.
Use docker run command:
- `docker-compose up`

### Run Test
- Step 1: `cd /test`
- Step 2: Run command `go test -v`
### List Api:
#### Call curl below or import file postman in package document.
- auth/register: register account.
``
curl --location --request POST 'localhost:9000/auth/register' \
--header 'Content-Type: application/json' \
--data-raw '{
"userName": "admin",
"passWord": "123456"
}'
``
- auth/login: login
``
curl --location --request POST 'localhost:9000/auth/login' \
--header 'Content-Type: application/json' \
--data-raw '{
"userName": "admin",
"passWord": "123456"
}'
``
- task/create: create task.
``
curl --location --request POST 'localhost:9000/task/create' \
--header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2ODcwMjAwMDEsImlzcyI6Im1hbmliaWUtdG9kbyIsIklkIjoyfQ.VYSoGawZE_6XrXRfurkswKYo1x1doAAmFDpuplrUFcU' \
--header 'Content-Type: application/json' \
--data-raw '{
"content": "Task test danh 4"
}'
``
- task/list: get task by created date.
``
curl --location --request GET 'localhost:9000/task/list?createdDate=2023-04-18' \
--header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2ODcwMjAwMDEsImlzcyI6Im1hbmliaWUtdG9kbyIsIklkIjoyfQ.VYSoGawZE_6XrXRfurkswKYo1x1doAAmFDpuplrUFcU'
``

### Solution
Limit the user to add work by day if the limit is reached then use redis cache to
reduce the load on the database, cache on redis with userId and expire at the end of the day.

### Technology used
- Golang version 1.19
- Postgres
- Redis
- Gin, Gorm
- Docker
- testcontainer
41 changes: 41 additions & 0 deletions cmd/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package main

import (
"github.com/gin-gonic/gin"
"github.com/redis/go-redis/v9"
"log"
"togo/pkg/config"
"togo/pkg/db_client"
"togo/pkg/routes"
"togo/pkg/services"
"togo/pkg/utils"
)

func main() {
c, err := config.LoadConfig()
if err != nil {
log.Fatalln("Failed at config", err)
}

r := gin.Default()
h := db_client.Init(c.DBUrl)
jwt := utils.JwtWrapper{
SecretKey: c.JWTSecretKey,
Issuer: "manibie-todo",
ExpirationHours: 24 * 60,
}

redisClient := redis.NewClient(&redis.Options{
Addr: c.RedisUrl,
Password: c.RedisPassWord,
DB: 0,
})

s := services.Server{
H: h,
Jwt: jwt,
Redis: redisClient,
}
routes.RegisterRoutes(r, &s)
r.Run(c.Port)
}
47 changes: 47 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
version: "3"

networks:
go-manabie-network:
driver: bridge
volumes:
postgres:

services:
database-postgres:
image: postgres
container_name: c-postgres
hostname: postgres
ports:
- "5432:5432"
restart: always
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=admin123
volumes:
- postgres:/var/lib/postgresql/data
- ./postgres/script_db.sql:/docker-entrypoint-initdb.d/script_db.sql
networks:
- go-manabie-network
cache:
image: redis:6.2-alpine
container_name: c-cache
restart: always
ports:
- '6379:6379'
command: redis-server --save 20 1 --loglevel warning --requirepass admin123
networks:
- go-manabie-network
api-manabie:
build:
dockerfile: Dockerfile
context: .
container_name: c-manabie
hostname: manabie
depends_on:
- database-postgres
- cache
restart: always
ports:
- "9000:9000"
networks:
- go-manabie-network
138 changes: 138 additions & 0 deletions document/ManabieTest.postman_collection.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
{
"info": {
"_postman_id": "9e203b2e-ed76-43fa-8fa1-6017da1b723f",
"name": "ManabieTest",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
},
"item": [
{
"name": "localhost:9001/auth/register",
"request": {
"method": "POST",
"header": [],
"body": {
"mode": "raw",
"raw": "{\n \"userName\": \"admin\",\n \"passWord\": \"123456\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "localhost:9001/auth/register",
"host": [
"localhost"
],
"port": "9001",
"path": [
"auth",
"register"
]
}
},
"response": []
},
{
"name": "localhost:9001/auth/login",
"request": {
"method": "POST",
"header": [],
"body": {
"mode": "raw",
"raw": "{\n \"userName\": \"admin\",\n \"passWord\": \"123456\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "localhost:9001/auth/login",
"host": [
"localhost"
],
"port": "9001",
"path": [
"auth",
"login"
]
}
},
"response": []
},
{
"name": "localhost:9001/task/create",
"request": {
"auth": {
"type": "bearer",
"bearer": [
{
"key": "token",
"value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2ODcwMjAwMDEsImlzcyI6Im1hbmliaWUtdG9kbyIsIklkIjoyfQ.VYSoGawZE_6XrXRfurkswKYo1x1doAAmFDpuplrUFcU",
"type": "string"
}
]
},
"method": "POST",
"header": [],
"body": {
"mode": "raw",
"raw": "{\n \"content\": \"Task test danh 4\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "localhost:9001/task/create",
"host": [
"localhost"
],
"port": "9001",
"path": [
"task",
"create"
]
}
},
"response": []
},
{
"name": "localhost:9001/task/list?createdDate=2023-04-18",
"request": {
"auth": {
"type": "bearer",
"bearer": [
{
"key": "token",
"value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2ODcwMjAwMDEsImlzcyI6Im1hbmliaWUtdG9kbyIsIklkIjoyfQ.VYSoGawZE_6XrXRfurkswKYo1x1doAAmFDpuplrUFcU",
"type": "string"
}
]
},
"method": "GET",
"header": [],
"url": {
"raw": "localhost:9001/task/list?createdDate=2023-04-18",
"host": [
"localhost"
],
"port": "9001",
"path": [
"task",
"list"
],
"query": [
{
"key": "createdDate",
"value": "2023-04-18"
}
]
}
},
"response": []
}
]
}
Loading