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

Dat Nguyen Quoc Manabie Assignment #348

Open
wants to merge 4 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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
15 changes: 15 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
API_URL=localhost:8080/api
DB_USERNAME=postgres
DB_PASSWORD=postgres
DB_NAME=togo
DB_HOST=localhost
DB_PORT=5432
SERVER_HOST=0.0.0.0
#DB_URL=postgres://postgres:postgres@db:5432/togo?sslmode=disable
DB_URL=postgres://postgres:postgres@localhost:5432/togo?sslmode=disable
LOG_LEVEL=debug

REDIS_CLUSTER=false
REDIS_LOCATION=redis://localhost:6379

CLIENT_HOST=0.0.0.0:8080
78 changes: 78 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
include .env
export

.PHONY: help
help:
@egrep -h '\s##\s' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'

.DEFAULT_GOAL:= help

.PHONY: docker
docker/build: ## Build docker compose images first
docker-compose -f ./docker/docker-compose.yaml build

docker/up: ## Start docker compose
docker-compose -f ./docker/docker-compose.yaml up -d

docker/down: ## Stop and remove docker compose
docker-compose -f ./docker/docker-compose.yaml down

docker/clean: ## Clean all docker data
@make docker/down
rm -rf ./data

.PHONY: build
build/linux: ## Build server for linux
env GOOS=linux GOARCH=386 go build -ldflags="-s -w" -o ./bin/server ./cmd/server/

build/prod:
CGO_ENABLED=0 GOOS=linux go build -o ./bin/server ./cmd/server

build: ## Show build.sh help for building binnary package under cmd
CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o ./bin/server ./cmd/server/

.PHONY: run
run:
go run cmd/app/main.go

.PHONY: setup
setup: ## Run setup scripts to prepare development environment
@scripts/setup.sh

.PHONY: clean
clean: ## Clean project dir, data and docker
@scripts/clean.sh

.PHONY: lint
lint: ## Run linter
@scripts/lint.sh


.PHONY: db
db/migrate: ## Migrate database structure
@scripts/migrate.sh up

db/up: ## Apply all the migration to the latest version to the local database
@make db/migrate

db/down: ## Remove everything the database! (only for DEV)
@scripts/migrate.sh down

db/drop: ## Remove everything the database! (only for DEV)
@scripts/migrate.sh drop -f

db/gen:
sqlboiler psql --wipe --config ./db/sqlboiler.toml --add-soft-deletes

db/reset:
@make docker/down
docker volume rm -f docker_togo-volume
@make docker/up

# Basic commands: up/down/drop/force\ <version>
db/%:
@scripts/migrate.sh $*

.PHONY: test
test: ## Generate mock and run all test. To run specified tests, use `./scripts/test.sh <pattern>`)
@scripts/test.sh $*
44 changes: 44 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,47 @@
## Prerequisite

- Golang 1.16
- Make
- Docker/docker-compose

## How to run

- Docker startup, run :
`make docker/up`

- Database migration, run:
`make db/up`

- Start server, run:
`make run`

## Tools I used

- sqlboiler: this library generate repositories that handle database operations, with much faster speed than casual ORM
- migrate: this is used to run migration scripts, written in sql queries, to manage database schema easily

## My Design

My code base was designed following clean architecture with mainly 3 layers:

- UseCase: this layer is used to handle business logic
- Infrastructure: this layer contains packages related to infrastructure and does not involve into business logic, including database, midlewares, etc.
- Handler: this is the handler layer that expose API endpoint

### Directory Structure

- cmd: implemented necessary cmd, including main server application
- internal: wrapped and encapsulated source of code that will not be exposed
- db: includes sqlboiler configuration and migrations scripts
- docker: includes dockerfile and docker-compose file as well as neccessary configuration files
- bin: mostly executable plugins and generated/built applications
- scripts: bash scripts to handle operations quickly

## What I have done and have not done

- [ ] Covering the core functionality with unit test
(As not enough time, I have implemented an unit test to demonstrate how I do test)

### Requirements

- Implement one single API which accepts a todo task and records it
Expand Down
1 change: 1 addition & 0 deletions api/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
API_URL=localhost:8080/api
17 changes: 17 additions & 0 deletions api/task.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/usr/bin/env bash -l

. "$(git rev-parse --show-toplevel || echo ".")/scripts/common.sh"
. "$(git rev-parse --show-toplevel || echo ".")/api/.env"

cmd=$1
token="Bearer "$token

add_task() {
id=${1:-1}
title=${2:-task_title}
description=${3:-task_description}
priority=${4:-1}
http -vv POST $API_URL/tasks user_id:=$id title=$title description=$description priority:=$priority
}

"$@"
38 changes: 38 additions & 0 deletions api/user.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/usr/bin/env bash -l

. "$(git rev-parse --show-toplevel || echo ".")/scripts/common.sh"
. "$(git rev-parse --show-toplevel || echo ".")/api/.env"

cmd=$1
token="Bearer "$token

create_one() {
name=${1:-dat}
quota=${2-5}
email=${3:[email protected]}
http -vv $API_URL/users name=$name email=$email quota:=$quota
}

get_one() {
id=${1:-1}
http -vv $API_URL/users/$id
}


update_one() {
id=${1:-1}
name=${2:-dat}
email=${3:[email protected]}
password=${4:-123456789}
http -vv PUT $API_URL/users/$id Authorization:"$token" name=$name email=$email password=$password
}

add_task() {
id=${1:-1}
title=${2:-task_title}
description=${3:-task_description}
priority=${4:-priority}
http -vv POST $API_URL/tasks/$id title=$title description=$description priority=$priority
}

"$@"
33 changes: 33 additions & 0 deletions cmd/app/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package main

import (
"log"

"github.com/datshiro/togo-manabie/internal/infras/app"
)

func main() {
server := app.NewApp()
// parse parameter and environment
server.Parse()

// config error handler
server.ConfigErrHandler()

// config log level
server.ConfigLogLevel()

// config log format
server.ConfigLogFormat()

//config middleware
server.ConfigMiddleware()

// register handlers
server.RegisterHandlers()

// run app
if err := server.Run(); err != nil {
log.Fatalf("fail to start, err=%v", err)
}
}
2 changes: 2 additions & 0 deletions db/migrations/01_init_db.down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
DROP SCHEMA IF EXISTS togo;

1 change: 1 addition & 0 deletions db/migrations/01_init_db.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
CREATE SCHEMA IF NOT EXISTS togo;
2 changes: 2 additions & 0 deletions db/migrations/02_init_table.down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
DROP TABLE IF EXISTS togo.tasks CASCADE;
DROP TABLE IF EXISTS togo.users CASCADE;
24 changes: 24 additions & 0 deletions db/migrations/02_init_table.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
CREATE TABLE IF NOT EXISTS togo.users (
id serial PRIMARY KEY,
email text NULL,
password text NOT NULL,
name text NOT NULL,
quota int NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
deleted_at TIMESTAMP
);

CREATE TABLE IF NOT EXISTS togo.tasks (
id serial PRIMARY KEY,
title text NOT NULL,
description text,
priority int NOT NULL DEFAULT 0,
is_done boolean NOT NULL DEFAULT FALSE,
user_id int NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
deleted_at TIMESTAMP,

CONSTRAINT task_user_fk FOREIGN KEY (user_id) REFERENCES togo.users(id)
);
14 changes: 14 additions & 0 deletions db/sqlboiler.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
output = "internal/interfaces/models"
pkgname = "models"
wipe = true
no-tests = true

[psql]
dbname = "togo"
host = "localhost"
port = 5432
user = "postgres"
pass = "postgres"
schema = "togo"
blacklist = ["migrations", "other"]
sslmode = "disable"
53 changes: 53 additions & 0 deletions docker/docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
version: '3'

services:
web:
build:
context: ../
dockerfile: docker/Dockerfile
image: togo-api
container_name: togo_api
ports:
- 3000:3000
depends_on:
db:
condition: service_healthy
networks:
- togo-network

db:
image: postgres
restart: always
container_name: postgres-db
ports:
- ${DB_PORT}:5432
volumes:
- togo-volume:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_USER: ${DB_USERNAME}
POSTGRES_DB: ${DB_NAME}
PGDATA: /var/lib/postgresql/data/pgdata
healthcheck:
test: [ "CMD", "pg_isready" ,"--dbname", "${DB_NAME}", "--host", "db" , "--port", "5432", "--username", "${DB_USERNAME}" ]
timeout: 5s
retries: 10
networks:
- togo-network

redis:
image: redis:latest
container_name: togo-redis
ports:
- "6379:6379"
networks:
- togo-network



volumes:
togo-volume:

networks:
togo-network:
driver: bridge
18 changes: 18 additions & 0 deletions docker/server.Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
FROM golang:1.16-buster AS build

WORKDIR /go/src/datshiro/togo

COPY . .

RUN CGO_ENABLED=0 GOOS=linux go build -o /usr/local/bin/server ./cmd/server


###

FROM alpine:3.9

COPY --from=0 /usr/local/bin/server /usr/local/bin/server
RUN apk add --no-cache ca-certificates

ENTRYPOINT ["server"]

40 changes: 40 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
module github.com/datshiro/togo-manabie

go 1.18

require (
github.com/DATA-DOG/go-sqlmock v1.4.1
github.com/friendsofgo/errors v0.9.2
github.com/go-redis/redis/v9 v9.0.0-beta.1
github.com/labstack/echo/v4 v4.7.2
github.com/labstack/gommon v0.3.1
github.com/lib/pq v1.10.6
github.com/stretchr/testify v1.8.0
github.com/volatiletech/null/v8 v8.1.2
github.com/volatiletech/sqlboiler/v4 v4.11.0
github.com/volatiletech/strmangle v0.0.4
)

require (
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/gofrs/uuid v3.2.0+incompatible // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/mattn/go-colorable v0.1.11 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/spf13/cast v1.4.1 // indirect
github.com/stretchr/objx v0.4.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.1 // indirect
github.com/volatiletech/inflect v0.0.1 // indirect
github.com/volatiletech/randomize v0.0.1 // indirect
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect
golang.org/x/net v0.0.0-20220225172249-27dd8689420f // indirect
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
Loading