Commit 0b674df5 authored by cicd's avatar cicd

completed

parents
Pipeline #14311 canceled with stages
## Project use nodejs version 15.0.1 and mysql in XAMPP##
## Generate data base ##
####
execute query in file mysql.sql
####
## Install package ##
```npm install```
## Generate model
```npm run gen-db```
## Run unit test ##
```npm test```
## Run project ##
```npm start```
## Task 1 ##
####
use the following api:
http://localhost:3000/api/v1.0/products : for list all products
http://localhost:3000/api/v1.0/products?currentPage=1&pageSize=2: for lis list all products with paging
http://localhost:3000/api/v1.0/products: with post method for create product
http://localhost:3000/api/v1.0/products/{code}: with put method for update product
http://localhost:3000/api/v1.0/products/p003002: with delete method for delete product by code
http://localhost:3000/api/v1.0/products?sort={name}&dir={desc/asc}: for get all product with sorting
####
File added
{
"Enviroment": "development",
"Port": 3000,
"Database": {
"DB_DATABASE": "inventory",
"DB_USER": "root",
"DB_PASS": null,
"DB_HOST": "127.0.0.1",
"DB_PORT": 3306
}
}
\ No newline at end of file
"use strict";
module.exports =
{
ROUTE_PREFIX: "api"
}
\ No newline at end of file
const ProductProvider = require('../../provider/productProvider');
const { sendOK, sendError } = require('../../helper/response');
class product {
constructor() {
this.producProvider = new ProductProvider()
}
init(router) {
const self = this;
console.log("route")
router
.route("")
.get(async function (req, res, next) {
await self.getProducts(req, res, next);
});
router
.route("/:code")
.get(async function (req, res, next) {
await self.getByCode(req, res, next);
});
router
.route("")
.post(async function (req, res, next) {
await self.createProduct(req, res, next);
});
router
.route("/:code")
.put(async function (req, res, next) {
await self.updateProduct(req, res, next);
});
router
.route("/:code")
.delete(async function (req, res, next) {
await self.deleteProduct(req, res, next);
});
}
async getProducts(req, res, next) {
const self = this;
try {
const currentPage = parseInt(req.query.currentPage) || 1
const pageSize = parseInt(req.query.pageSize) || 10
// sort & dir
let sort = req.query.sort || "id"
let dir = req.query.dir || "asc"
const lstResult = await self.producProvider.getAll(currentPage, pageSize, sort, dir);
lstResult['totalPages'] = Math.ceil(lstResult['count'] / pageSize);
lstResult['currentPage'] = currentPage
return res.status(200).json(sendOK(lstResult));
} catch (error) {
console.log(error);
res.status(500).json(sendError(error.message))
}
}
async getByCode(req, res, next) {
const self = this;
try {
const result = await self.producProvider.getByCode(req.params);
return res.status(200).json(sendOK(result));
} catch (error) {
console.log(error);
res.status(500).json(sendError(error.message))
}
}
async createProduct(req, res, next) {
const self = this;
try {
let body = req.body;
body.createdAt = new Date;
const result = await self.producProvider.post(body);
return res.status(200).json(sendOK(result));
} catch (error) {
console.log(error);
res.status(500).json(sendError(error.message))
}
}
async updateProduct(req, res, next) {
const self = this;
try {
let body = req.body;
body.updatedAt = new Date;
const result = await self.producProvider.put(body, req.params.code);
return res.status(200).json(sendOK(result));
} catch (error) {
console.log(error);
res.status(500).json(sendError(error.message))
}
}
async deleteProduct(req, res, next) {
const self = this;
try {
const result = await self.producProvider.delete(req.params.code);
return res.status(200).json(sendOK(result));
} catch (error) {
console.log(error);
res.status(500).json(sendError(error.message))
}
}
}
module.exports = product;
\ No newline at end of file
module.exports.sendOK = (responseData) => {
return {
"message": "Successfully",
"responseData": responseData,
"status": "success",
"timeStamp": new Date().toISOString().replace(/T/, ' ').replace(/\..+/, '')
}
}
module.exports.sendError = (violations) => {
return {
"message": "Something wrong",
"status": "fail",
"timeStamp": new Date().toISOString().replace(/T/, ' ').replace(/\..+/, ''),
"violations": violations
}
}
\ No newline at end of file
var DataTypes = require("sequelize").DataTypes;
var _product = require("./product");
function initModels(sequelize) {
var product = _product(sequelize, DataTypes);
return {
product,
};
}
module.exports = initModels;
module.exports.initModels = initModels;
module.exports.default = initModels;
const Sequelize = require('sequelize');
module.exports = function(sequelize, DataTypes) {
return sequelize.define('product', {
id: {
autoIncrement: true,
type: DataTypes.INTEGER.UNSIGNED,
allowNull: false,
primaryKey: true
},
code: {
type: DataTypes.STRING(9),
allowNull: false,
unique: "UX_product_code"
},
name: {
type: DataTypes.STRING(90),
allowNull: false
},
category: {
type: DataTypes.STRING(28),
allowNull: false
},
brand: {
type: DataTypes.STRING(28),
allowNull: true
},
type: {
type: DataTypes.STRING(21),
allowNull: true
},
description: {
type: DataTypes.STRING(180),
allowNull: true
}
}, {
sequelize,
tableName: 'product',
timestamps: true,
indexes: [
{
name: "PRIMARY",
unique: true,
using: "BTREE",
fields: [
{ name: "id" },
]
},
{
name: "UX_product_code",
unique: true,
using: "BTREE",
fields: [
{ name: "code" },
]
},
]
});
};
"use strict";
const CLASS_NAME = "productProvider";
const initSequelize = require('../services/dbServices');
class productProvider {
constructor(skipInitDb) {
if (skipInitDb === true) {
this.db = {};
} else {
this.db = initSequelize
}
}
async getByCode(code) {
const self = this;
return await self.db.product.findOne({
raw: true,
where: code
}).then(res => {
return res;
}).catch(err => {
console.log(err)
})
}
async getAll(currentPage, pageSize, sort, dir) {
const self = this;
return await self.db.product.findAndCountAll({
limit: pageSize,
offset: (currentPage - 1) * pageSize || 0,
order: [
[sort, dir]
],
raw: true,
}).then(res => {
return res;
}).catch(err => {
console.log(err)
})
}
async post(body) {
const self = this;
return await self.db.product.create(body)
.then(res => {
return res;
})
.catch(err => {
console.log(err)
})
}
async put(body, code) {
const self = this;
return await self.db.product
.update({
name: body.name,
description: body.description,
category: body.category,
brand: body.brand,
updated_at: body.updated_at,
type: body.type
}, { where: { code: code } })
.then((result) => {
return result;
})
.catch((err) => {
console.log(err)
});
}
async delete(code) {
const self = this;
return await self.db.product.destroy({
where: { code: code }
})
.then(res => {
return res;
})
.catch(err => {
console.log(err)
})
}
}
module.exports = productProvider;
\ No newline at end of file
'use strict';
const constant = require('./constant/constant.js');
const apiControllerPath = './controller';
var changeCase = require('change-case');
var express = require('express');
var requireDir = require('require-dir')
var fs = require('fs');
var path = require('path');
var Routes = function (app) {
var prefix = constant.ROUTE_PREFIX;
// Configure API controller paths
fs.readdirSync(path.resolve(__dirname, apiControllerPath)).forEach(dir => {
var fullPath = path.join(apiControllerPath, dir);
var routes = requireDir(fullPath);
Object.keys(routes).forEach(function (routeName) {
var router = express.Router();
var DynamicController = require("./" + path.join(fullPath, routeName));
var controller = new DynamicController();
controller.init(router);
app.use('/' + prefix + '/' + dir + '/' + changeCase.paramCase(routeName), router);
});
});
}
module.exports = Routes;
\ No newline at end of file
var nconf = require("nconf")
var initModels = require("../models/init-models");
const Sequelize = require("sequelize");
const sequelize = new Sequelize(nconf.get("Database:DB_DATABASE"), nconf.get("Database:DB_USER"),nconf.get("Database:DB_PASS"),
{
host: nconf.get("Database:DB_HOST"),
port: nconf.get("Database:DB_PORT"),
dialect: 'mysql',
operatorsAliases: 0
});
const initExport = initModels(sequelize);
module.exports = initExport;
\ No newline at end of file
'use strict'
var nconf = require('nconf');
nconf.argv()
.env()
.file({ file: './app/config/config.json' });
const express = require('express')
var nconf = require('nconf');
var bodyParser = require('body-parser');
var port = 3000;
var app = express();
app.use(bodyParser.json({ type: "application/json", limit: "50mb" }));
require('./app/route')(app);
const server = app.listen(port, function () {
console.log("Server listen on port: " + port);
})
module.exports = server;
CREATE TABLE `product` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`code` VARCHAR(9) NOT NULL,
`name` VARCHAR(90) NOT NULL,
`category` VARCHAR(28) NOT NULL,
`brand` VARCHAR(28) DEFAULT NULL,
`type` VARCHAR(21) DEFAULT NULL,
`description` VARCHAR(180) DEFAULT NULL,
`createdAt` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`updatedAt` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `UX_product_code` (`code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO `product` (`id`, `code`, `name`, `category`, `brand`, `type`, `description`) VALUES (1, "P001", "MASK ADULT Surgical 3 ply 50'S MEDICOS with box", "Health Accessories", "Medicos", "Hygiene", "Colour: Blue (ear loop outside, ear loop inside- random assigned), Green, Purple, White, Lime Green, Yellow, Pink");
INSERT INTO `product` (`id`, `code`, `name`, `category`, `brand`, `description`) VALUES (2, "P002", "Party Cosplay Player Unknown Battlegrounds Clothes Hallowmas PUBG", "Men's Clothing", "No Brand", "Suitable for adults and children.");
INSERT INTO `product` (`id`, `code`, `name`, `category`, `brand`, `type`, `description`) VALUES (3, "P003", "Xiaomi REDMI 8A Official Global Version 5000 mAh battery champion 31 days 2GB+32GB", "Mobile & Gadgets", "Xiaomi", "Mobile Phones", "Xiaomi Redmi 8A");
INSERT INTO `product` (`id`, `code`, `name`, `category`, `brand`, `type`, `description`) VALUES (4, "P004", "Naelofar Sofis - Printed Square", "Hijab", "Naelofar", "Multi Colour Floral", "Ornate Iris flower composition with intricate growing foliage");
INSERT INTO `product` (`id`, `code`, `name`, `category`, `brand`, `type`, `description`) VALUES (5, "P005", "Philips HR2051 / HR2056 / HR2059 Ice Crushing Blender Jar Mill", "Small Kitchen Appliances", "Philips", "Mixers & Blenders", "Philips HR2051 Blender (350W, 1.25L Plastic Jar, 4 stars stainless steel blade)");
INSERT INTO `product` (`id`, `code`, `name`, `category`, `brand`, `type`, `description`) VALUES (6, "P006", "Gemei GM-6005 Rechargeable Trimmer Hair Cutter Machine", "Hair Styling Tools", "Gemei", "Trimmer", "The GEMEI hair clipper is intended for professional use.");
INSERT INTO `product` (`id`, `code`, `name`, `category`, `brand`, `type`, `description`) VALUES (7, "P007", "Oreo Crumb Small Crushed Cookie Pieces 454g", "Snacks", "Oreo", "Biscuits & Cookies", "Oreo Crumb Small Crushed Cookie Pieces 454g - Retail & Wholesale New Stock Long Expiry!!!");
INSERT INTO `product` (`id`, `code`, `name`, `category`, `brand`, `description`) VALUES (8, "P008", "Non-contact Infrared Forehead Thermometer ABS", "Kids Health & Skincare", "No Brand", "Non-contact Infrared Forehead Thermometer ABS for Adults and Children with LCD Display Digital");
INSERT INTO `product` (`id`, `code`, `name`, `category`, `brand`, `type`, `description`) VALUES (9, "P009", "Nordic Marble Starry Sky Bedding Sets", "Bedding", "No Brand", "Bedding Sheets", "Printing process: reactive printing. Package:quilt cover ,bed sheet ,pillow case. Not include comforter or quilt.");
INSERT INTO `product` (`id`, `code`, `name`, `category`, `brand`, `type`, `description`) VALUES (10, "P010", "Samsung Galaxy Tab A 10.1""", "Mobile & Gadgets", "Samsung", "Tablets", "4GB RAM. - 64GB ROM. - 1.5 ghz Processor. - 10.1 inches LCD Display");
INSERT INTO `product` (`id`, `code`, `name`, `category`, `brand`, `type`, `description`) VALUES (11, "P011", "REALME 5 PRO 6+128GB", "Mobile & Gadgets", "Realme", "Mobile Phones", "REALME 5 PRO 6+128GB");
INSERT INTO `product` (`id`, `code`, `name`, `category`, `brand`, `type`, `description`) VALUES (12, "P012", "Nokia 2.3 - Cyan Green", "Mobile & Gadgets", "Nokia", "Mobile Phones", "Nokia smartphones get 2 years of software upgrades and 3 years of monthly security updates.");
INSERT INTO `product` (`id`, `code`, `name`, `category`, `brand`, `type`, `description`) VALUES (13, "P013", "AKEMI Cotton Select Fitted Bedsheet Set - Adore 730TC", "Bedding", "Akemi", "Bedding Sheets", "100% Cotton Twill. Super Single.");
INSERT INTO `product` (`id`, `code`, `name`, `category`, `brand`, `type`, `description`) VALUES (14, "P014", "Samsung Note10+ Phone", "Mobile & Gadgets", "OEM", "Mobile Phones", "OEM Phone Models");
INSERT INTO `product` (`id`, `code`, `name`, `category`, `brand`, `type`, `description`) VALUES (15, "P015", "Keknis Basic Wide Long Shawl", "Hijab", "No Brand", "Plain Shawl", "1.8m X 0.7m (+/-). Heavy chiffon (120 gsm).");
COMMIT;
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"name": "nodejstest",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
"start": "node index.js",
"gen-db": "sequelize-auto -o app/models -d inventory -h 127.0.0.1 -u root -p 3306 "
},
"dependencies": {
"axios": "^0.26.1",
"body-parser": "^1.20.0",
"change-case": "^4.1.2",
"cors": "^2.8.5",
"dotenv": "^16.0.0",
"express": "^4.17.3",
"fs": "^0.0.1-security",
"jest": "^27.5.1",
"mysql2": "^2.3.3",
"nconf": "^0.11.3",
"path": "^0.12.7",
"require-dir": "^1.2.0",
"sequelize-auto": "^0.8.8",
"supertest": "^6.2.2",
"uuid": "^8.3.2"
},
"author": "",
"license": "ISC",
"description": ""
}
\ No newline at end of file
const request = require('supertest');
const express = require('express');
const app =require('./index.js');
describe('API Products Endpoints', () => {
it('should return listing with records', async () => {
const res = await request(app).get('/api/products');
expect(200)
});
it('should delete existing product', async () => {
const res = await request(app).delete('/api/products/P005');
expect(res.status);
});
it('should update existing product', async () => {
const res = await request(app).put('/api/products/P003')
.send({
description: '*NULL*',
});
expect(res.status);
});
it('should create new product', async () => {
const res = await request(app).post('/api/products').send({
code: 'P101',
name: 'Tall Basket',
category: 'Home Decoration',
brand: null,
type: null,
description: 'The next super product of the year.',
});
expect(res.status);
});
}
)
module.exports = test;
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment