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
78 changes: 64 additions & 14 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,19 +1,69 @@
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Test binary, built with `go test -c`
*.test
yarn.lock

# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Dependency directories (remove the comment below to include it)
# vendor/
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

out/
# Coverage directory used by tools like istanbul
coverage

.idea
# nyc test coverage
.nyc_output

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# TypeScript v1 declaration files
typings/

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variables file
.env

# next.js build output
.next

#Macbook files
.DS_Store

# Build
dist
6 changes: 6 additions & 0 deletions .sequelizerc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const path = require('path');

module.exports = {
config: path.resolve(__dirname, 'src', 'config', 'database.js'),
'migrations-path': path.resolve(__dirname, 'src', 'database', 'migrations'),
};
9 changes: 9 additions & 0 deletions LICENSE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
MIT License

Copyright (c) 2019 Rocketseat

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
87 changes: 65 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,73 @@
### Requirements
# Node.js project with Express, Sequelize, Jest and Postgres

- 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?
by [Buudld]

### Notes
### Directory structure

- 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.
```shell
src
├── app.js app entry point
├── /routes controller layer: api routes
├── /config config settings
├── /services service layer: business logic
├── /models data access layer: database models
test
├── /unit unit test suites
├── /integration test api routes
```

### How to submit your solution?
### Installation and execution

- Fork this repo and show us your development progress via a PR
1. Run yarn to install dependencies: `yarn`;
1. Config database credentials inside `/src/config/database.js`;
1. Create the table manually in order to start the server from the file `./database.sql`;
1. Run `yarn dev` to start the server.
1. Run `yarn test` to start the server.

### 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.
### API

Thank you for spending time to read and attempt our take-home assessment. We are looking forward to your submission.
- Sign-up: `http://localhost:3000/api/auth/signup`
- Login: `http://localhost:3000/api/auth/signin`
- Users: `http://localhost:3000/api/users`
- Task: `http://localhost:3000/api/task`

### Curl Sign Up
curl --location --request POST 'http://localhost:3000/api/auth/signup' \
--header 'Content-Type: application/json' \
--header 'Cookie: token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MzUsImlhdCI6MTY1NzM3Mzk3NywiZXhwIjoxNjU3Mzc3NTc3fQ.QzE-m84YuUiMdQ_FH9zarsiYqUSX-zH3-__1SMJ_i08' \
--data-raw '{
"name": "Đình Bửu",
"email": "dinhbuu1208@gmail.com",
"password": "admintodo",
"role": "Admin"
}'

### Curl Sign In
curl --location --request POST 'http://localhost:3000/api/auth/signin' \
--header 'Content-Type: application/json' \
--header 'Cookie: token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MzcsImlhdCI6MTY1NzM3NDAwNywiZXhwIjoxNjU3Mzc3NjA3fQ.Wed21w5ETRt9RHW4UNkyfDk-nVNJ-ATIBi2u2pHT4IU' \
--data-raw '{
"email": "dinhbuu1208@gmail.com",
"password": "admintodo"
}'

### Curl Create Task
curl --location --request POST 'http://localhost:3000/api/task' \
--header 'Content-Type: application/json' \
--data-raw '{
"title": "Test Todo Task",
"description": "Test Todo Task",
"text": "Test Todo Task"
}'

### What do you love about your solution?
1. This backend is written in Nodejs: I can write Javascript code outside the browser to create server-side web applications that are non-blocking, lightweight, fast, robust and scalable.
2. Separation of concern principle is applied: Each component has been given a particular role. The role of the components is mutually exclusive. This makes the project easy to be unit tested.
3. Feature encapsulation is adopted: The files or components that are related to a particular feature have been grouped unless those components are required in multiple features. This enhances the ability to share code across projects.
4. Centralised Error handling is done: We have created a framework where all the errors are handled centrally. This reduces the ambiguity in the development when the project grows larger.
5. Async execution is adopted: We have used async/await for the promises and made sure to use the non-blocking version of all the functions with few exceptions.
6. Unit test is favored: The tests have been written to test the functions and routes without the need of the database server. Integration tests has also been done but the unit test is favored.

### What else do you want us to know about however you do not have enough time to complete?
1. Writting skill unit test and intergation test not perfect.
194 changes: 194 additions & 0 deletions database.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
/*
Navicat Premium Data Transfer

Source Server : 2Mau-local
Source Server Type : PostgreSQL
Source Server Version : 130003
Source Host : localhost:5433
Source Catalog : togo
Source Schema : public

Target Server Type : PostgreSQL
Target Server Version : 130003
File Encoding : 65001

Date: 09/07/2022 21:01:13
*/


-- ----------------------------
-- Sequence structure for configs_id_seq
-- ----------------------------
DROP SEQUENCE IF EXISTS "public"."configs_id_seq";
CREATE SEQUENCE "public"."configs_id_seq"
INCREMENT 1
MINVALUE 1
MAXVALUE 2147483647
START 1
CACHE 1;

-- ----------------------------
-- Sequence structure for roles_id_seq
-- ----------------------------
DROP SEQUENCE IF EXISTS "public"."roles_id_seq";
CREATE SEQUENCE "public"."roles_id_seq"
INCREMENT 1
MINVALUE 1
MAXVALUE 2147483647
START 1
CACHE 1;

-- ----------------------------
-- Sequence structure for task_id_seq
-- ----------------------------
DROP SEQUENCE IF EXISTS "public"."task_id_seq";
CREATE SEQUENCE "public"."task_id_seq"
INCREMENT 1
MINVALUE 1
MAXVALUE 2147483647
START 1
CACHE 1;

-- ----------------------------
-- Sequence structure for users_id_seq
-- ----------------------------
DROP SEQUENCE IF EXISTS "public"."users_id_seq";
CREATE SEQUENCE "public"."users_id_seq"
INCREMENT 1
MINVALUE 1
MAXVALUE 2147483647
START 1
CACHE 1;

-- ----------------------------
-- Table structure for configs
-- ----------------------------
DROP TABLE IF EXISTS "public"."configs";
CREATE TABLE "public"."configs" (
"id" int4 NOT NULL DEFAULT nextval('configs_id_seq'::regclass),
"role" varchar(255) COLLATE "pg_catalog"."default" NOT NULL,
"limit" int4 NOT NULL,
"createdAt" timestamptz(6) NOT NULL,
"updatedAt" timestamptz(6) NOT NULL
)
;

-- ----------------------------
-- Records of configs
-- ----------------------------
INSERT INTO "public"."configs" VALUES (9, 'Admin', 100, '2022-07-07 03:47:49.977+00', '2022-07-07 03:47:49.977+00');

-- ----------------------------
-- Table structure for roles
-- ----------------------------
DROP TABLE IF EXISTS "public"."roles";
CREATE TABLE "public"."roles" (
"id" int4 NOT NULL DEFAULT nextval('roles_id_seq'::regclass),
"code" varchar(255) COLLATE "pg_catalog"."default" NOT NULL,
"status" bool NOT NULL,
"createdAt" timestamptz(6) NOT NULL,
"updatedAt" timestamptz(6) NOT NULL
)
;

-- ----------------------------
-- Records of roles
-- ----------------------------
INSERT INTO "public"."roles" VALUES (9, 'Admin', 't', '2022-07-07 03:47:49.981+00', '2022-07-07 03:47:49.981+00');

-- ----------------------------
-- Table structure for task
-- ----------------------------
DROP TABLE IF EXISTS "public"."task";
CREATE TABLE "public"."task" (
"id" int4 NOT NULL DEFAULT nextval('task_id_seq'::regclass),
"title" varchar(255) COLLATE "pg_catalog"."default" NOT NULL,
"description" varchar(255) COLLATE "pg_catalog"."default" NOT NULL,
"text" varchar(255) COLLATE "pg_catalog"."default" NOT NULL,
"author" int4 NOT NULL,
"createdAt" timestamptz(6) NOT NULL,
"updatedAt" timestamptz(6) NOT NULL
)
;

-- ----------------------------
-- Records of task
-- ----------------------------

-- ----------------------------
-- Table structure for users
-- ----------------------------
DROP TABLE IF EXISTS "public"."users";
CREATE TABLE "public"."users" (
"id" int4 NOT NULL DEFAULT nextval('users_id_seq'::regclass),
"name" varchar(255) COLLATE "pg_catalog"."default" NOT NULL,
"email" varchar(255) COLLATE "pg_catalog"."default" NOT NULL,
"password" varchar(255) COLLATE "pg_catalog"."default" NOT NULL,
"role" varchar(255) COLLATE "pg_catalog"."default" NOT NULL,
"createdAt" timestamptz(6) NOT NULL,
"updatedAt" timestamptz(6) NOT NULL
)
;

-- ----------------------------
-- Records of users
-- ----------------------------
INSERT INTO "public"."users" VALUES (2, 'Đình Bửu', 'dinhbuu1208@gmail.com', '$2a$08$FooFv5KZCP7.pmTAOWqrH.Cqa1TVS434GdWBILdukVzje64/6Ad9O', 'Admin', '2022-07-06 18:56:37.598+00', '2022-07-06 18:56:37.598+00');

-- ----------------------------
-- Alter sequences owned by
-- ----------------------------
ALTER SEQUENCE "public"."configs_id_seq"
OWNED BY "public"."configs"."id";
SELECT setval('"public"."configs_id_seq"', 20, true);

-- ----------------------------
-- Alter sequences owned by
-- ----------------------------
ALTER SEQUENCE "public"."roles_id_seq"
OWNED BY "public"."roles"."id";
SELECT setval('"public"."roles_id_seq"', 20, true);

-- ----------------------------
-- Alter sequences owned by
-- ----------------------------
ALTER SEQUENCE "public"."task_id_seq"
OWNED BY "public"."task"."id";
SELECT setval('"public"."task_id_seq"', 115, true);

-- ----------------------------
-- Alter sequences owned by
-- ----------------------------
ALTER SEQUENCE "public"."users_id_seq"
OWNED BY "public"."users"."id";
SELECT setval('"public"."users_id_seq"', 46, true);

-- ----------------------------
-- Primary Key structure for table configs
-- ----------------------------
ALTER TABLE "public"."configs" ADD CONSTRAINT "configs_pkey" PRIMARY KEY ("id");

-- ----------------------------
-- Uniques structure for table configs
-- ----------------------------
ALTER TABLE "public"."configs" ADD CONSTRAINT "roles_role_key" UNIQUE ("role");

-- ----------------------------
-- Primary Key structure for table roles
-- ----------------------------
ALTER TABLE "public"."roles" ADD CONSTRAINT "roles_pkey" PRIMARY KEY ("id");

-- ----------------------------
-- Primary Key structure for table task
-- ----------------------------
ALTER TABLE "public"."task" ADD CONSTRAINT "task_pkey" PRIMARY KEY ("id");

-- ----------------------------
-- Uniques structure for table users
-- ----------------------------
ALTER TABLE "public"."users" ADD CONSTRAINT "users_email_key" UNIQUE ("email");

-- ----------------------------
-- Primary Key structure for table users
-- ----------------------------
ALTER TABLE "public"."users" ADD CONSTRAINT "users_pkey" PRIMARY KEY ("id");
Loading