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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@

out/

.idea
.idea
/data
9 changes: 9 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.PHONY: up

COMPOSE := docker-compose

up:
$(COMPOSE) up -d

down: ## Kill all containers
$(COMPOSE) kill
171 changes: 159 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,164 @@
## Setup

### 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?
1. Install Docker

- Install Docker Desktop `4.10.1` (current version) or Docker Engine `19.03.0` and Docker Compose `3.8`

2. Update your /etc/hosts

Put the following configurations on your host machine's /etc/hosts.

```
#Datastores
127.0.0.1 postgresql.manabie.todo
```

### How to run your code locally?

1. Run project:

#### Run:

make up

#### Migrate: (Used when the new database is initialized)

docker exec -it api.manabie.todo bash -c "make migrate-todo"

2. Use these commands to manage your containers:

#### Install

make install

#### Run

make run

#### Format

make format

#### Migrate DB

make migrate-todo

#### Drop DB

make drop-todo

### A sample “curl” command to call your API

1. Users:

- Get list user

```
curl --request GET 'http://localhost:8080/users'
```

2. Setting limit for user

- Show setting by userId

```
curl --location --request GET 'http://localhost:8080/users/1/settings'
```

- Create limit task by userId

```
curl --location --request POST 'http://localhost:8080/users/1/settings' \
--header 'Content-Type: application/json' \
--data-raw '{
"limit_task": 5
}'
```

- Update limit task by Id

```
curl --location --request PUT 'http://localhost:8080/settings/1' \
--header 'Content-Type: application/json' \
--data-raw '{
"limit_task": 10
}'
```

3. Show/Insert/Update/Delete todo task for user.

- List task by user-id

```
curl --location --request GET 'http://localhost:8080/users/1/tasks'
```

- Create task by user-id

```
curl --location --request POST 'http://localhost:8080/users/1/tasks' \
--header 'Content-Type: application/json' \
--data-raw '{
"content": "whatever",
"target_date": "2022-07-17"
}'
```

- Get task by id

```
curl --location --request GET 'http://localhost:8080/tasks/1'
```

- Update task by id

```
curl --location --request PUT 'http://localhost:8080/tasks/1' \
--header 'Content-Type: application/json' \
--data-raw '{
"id": 1,
"member_id": 1,
"content": "updated",
"target_date": "2022-07-17T00:00:00Z",
"created_at": "2022-07-17T14:44:46.981938Z"
}'
```

- Delete task by id

```
curl --location --request DELETE 'http://localhost:8080/tasks/1'
```

### How to run your unit tests locally?

- Run a command in a running container (container: `api.manabie.todo`)

#### Test (unit test)

make test

#### Test E2E

make test-e2e

### What do you love about your solution?

- Build projects in different environments by `Docker`.
- Ensure data consistency when using `Transactions` and `Locking`.
- E2E testing and mock testing.
- Clean Architecture in Go by Repository pattern. ([reference](https://github.com/bxcodec/go-clean-arch#the-diagram))

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

- Setup information (SECRET, PUBLIC) key by environment. (ansible, vault).
- Authentication and authorization for endpoints.
- Restore data when make test.

<hr />


### Notes

Expand Down
12 changes: 12 additions & 0 deletions api/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
FROM golang:1.17

ENV ROOT /api

# Create and change to the app directory.
WORKDIR $ROOT

COPY go.mod ./
COPY go.sum ./
COPY . .

RUN make install
34 changes: 34 additions & 0 deletions api/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
.PHONY: install

MIGRATE_TODO := migrate -source file://migrate/ddl -database 'postgres://postgres:password@postgresql.manabie.todo:5432/todo?sslmode=disable'

install:
go mod tidy
go install -tags "postgres" github.com/golang-migrate/migrate/v4/cmd/migrate

run:
go run main.go

format:
go fmt ./...

test:
go test -count=1 -cover ./...

test-e2e:
go test -count=1 -tags=e2e_test ./api/...

migrate-todo:
@echo "Migrating todo DB.."
@${MIGRATE_TODO} up
@echo "Done!"

down-todo:
@echo "Migrating(down) todo DB.."
@echo y | $(MIGRATE_TODO) down 1
@echo "Done!"

drop-todo:
@echo "Dropping todo DB.."
@echo y | $(MIGRATE_TODO) drop
@echo "Dropped!"
110 changes: 110 additions & 0 deletions api/api/settings/e2e_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// go:build ,!e2e_test

package settings

import (
"bytes"
"context"
"database/sql"
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"net/http"
"os"
"testing"

"manabie/todo/models"
"manabie/todo/pkg/db"
"manabie/todo/pkg/utils"
"manabie/todo/repository/user"

_ "github.com/lib/pq"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestMain(m *testing.M) {

if !flag.Parsed() {
flag.Parse()
}

if err := db.Setup(); err != nil {
panic(err)
}

exit := m.Run()
if err := db.Teardown(); err != nil {
panic(err)
}
os.Exit(exit)

}

func Test_E2E_Integration(t *testing.T) {
ur := user.NewUserRespository()

// Init data test
u := &models.User{
ID: utils.RamdomID(),
Email: "integration_test@test.com",
Name: "integration_test",
}
require.Nil(t, db.Transaction(context.Background(), nil, func(ctx context.Context, tx *sql.Tx) error {
return ur.Create(ctx, tx, u)
}))

// Create
{
got := &models.SettingCreateRequest{
LimitTask: 10,
}

data, _ := json.Marshal(got)
res, err := http.Post(fmt.Sprintf("http://localhost:8080/users/%d/settings", u.ID), "application/json", bytes.NewBuffer(data))

require.Nil(t, err)
require.NotNil(t, res)

assert.Equal(t, res.StatusCode, 200)
}
// Show
var st *models.Setting
{
r, err := http.Get(fmt.Sprintf("http://localhost:8080/users/%d/settings", u.ID))

require.Nil(t, err)
require.NotNil(t, r)

defer r.Body.Close()

assert.Equal(t, r.StatusCode, 200)

body, err := ioutil.ReadAll(r.Body)
require.Nil(t, err)

assert.NoError(t, json.Unmarshal(body, &st))
}
// Update
{
got := &models.SettingUpdateRequest{
LimitTask: 1,
}

data, _ := json.Marshal(got)

client := &http.Client{}
req, err := http.NewRequest(http.MethodPut, fmt.Sprintf("http://localhost:8080/settings/%d", st.ID), bytes.NewBuffer(data))
require.Nil(t, err)

// set the request header Content-Type for json
req.Header.Set("Content-Type", "application/json; charset=utf-8")
res, err := client.Do(req)

require.Nil(t, err)
require.NotNil(t, res)

assert.Equal(t, res.StatusCode, 200)
}
}
Loading