Skip to content

Commit ab82bfa

Browse files
authored
Merge pull request #19 from Johnpeace/ch-setup-postgresql-database-167264111
#167264111 Setup postgresql database
2 parents 37d0009 + 57e2a0b commit ab82bfa

File tree

18 files changed

+365
-205
lines changed

18 files changed

+365
-205
lines changed

.eslintrc.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@
1212
"consistent-return": 0,
1313
"mocha/no-exclusive-tests": 2,
1414
"prefer-destructuring": 0,
15-
"no-unused-vars": 0
15+
"no-unused-vars": 0,
16+
"max-len": 0,
17+
"no-undef": 0,
18+
"comma-dangle":0
1619
},
1720
"env": {
1821
"browser": true,

package.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"description": "Property Pro Lite is a platform where people can create and/or search properties for sale or rent",
55
"main": "server.js",
66
"scripts": {
7-
"heroku-prebuild": "NPM_CONFIG_PRODUCTION=false",
7+
"heroku-postbuild": "npm install --production",
88
"test": "NODE_ENV=TEST nyc mocha --require @babel/register ./test/*.spec.js --timeout 40000 --exit || true",
99
"coveralls": "nyc npm test && nyc report --reporter=text-lcov | coveralls",
1010
"start": "DEBUG=http babel-node server.js",
@@ -27,7 +27,10 @@
2727
"dotenv": "^8.0.0",
2828
"express": "^4.17.1",
2929
"jsonwebtoken": "^8.5.1",
30-
"regenerator-runtime": "^0.13.2"
30+
"moment": "^2.24.0",
31+
"pg": "^7.11.0",
32+
"regenerator-runtime": "^0.13.2",
33+
"socket.io": "^2.2.0"
3134
},
3235
"devDependencies": {
3336
"@babel/cli": "^7.4.4",
@@ -44,6 +47,7 @@
4447
"eslint-config-airbnb-base": "^13.1.0",
4548
"eslint-plugin-import": "^2.18.0",
4649
"eslint-plugin-mocha": "^5.3.0",
50+
"make-runnable": "^1.3.6",
4751
"mocha": "^6.1.4",
4852
"mocha-lcov-reporter": "^1.3.0",
4953
"morgan": "^1.9.1",

server/config/cloudinary.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import cloudinary from 'cloudinary';
2+
import { config } from 'dotenv';
3+
4+
config();
25

36
cloudinary.config({
47
cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { config } from 'dotenv';
44
config();
55

66
const userToken = (payload) => {
7-
const token = jwt.sign({ payload }, process.env.JWT_SECRET, {
7+
const token = jwt.sign(payload, process.env.JWT_SECRET, {
88
expiresIn: '24h',
99
});
1010
return token;

server/controllers/property.controller.js

Lines changed: 93 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,47 @@
1+
import moment from 'moment';
12
import Response from '../utils/helpers/response';
3+
import db from '../models/index';
24
import Id from '../utils/helpers/id';
35
import Data from '../db/property';
46
import PropertyModel from '../models/property.model';
57

68
class PropertyController {
79
static async create(req, res) {
810
try {
11+
const createQuery = `INSERT INTO
12+
property (owner, status, price, state, city, address, type, created_on, image_url, owner_email, owner_phone_number)
13+
VALUES($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) RETURNING *`;
914
const property = req.body;
10-
const newId = Id(Data);
11-
property.id = newId;
12-
property.owner = res.locals.user;
13-
property.created_on = new Date();
14-
const newProperty = new PropertyModel({ ...property });
15-
newProperty.create();
16-
return Response.handleSuccess(201, 'Successfully Created', newProperty.result, res);
15+
let { type } = property;
16+
const {
17+
price, state, city, address, image_url,
18+
} = property;
19+
property.owner = res.locals.user.id;
20+
property.status = 'available';
21+
property.created_on = moment(new Date());
22+
property.owner_email = res.locals.user.email;
23+
property.owner_phone_number = res.locals.user.phone_number;
24+
type = type.toLowerCase().trim();
25+
const values = [
26+
property.owner, property.status, price, state, city, address, type,
27+
property.created_on, image_url, property.owner_email, property.owner_phone_number
28+
];
29+
const { rows } = await db.query(createQuery, values);
30+
const obj = rows[0];
31+
property.id = obj.id;
32+
return Response.handleSuccess(201, 'Successfully Created', { ...property }, res);
1733
} catch (error) {
1834
return Response.handleError(500, error.toString(), res);
1935
}
2036
}
2137

2238
static async findAll(req, res) {
2339
try {
24-
const listOfProperties = await PropertyModel.findAll();
25-
if (listOfProperties) {
26-
return Response.handleSuccess(200, 'Got all property adverts successfully', listOfProperties, res);
40+
const selectQuery = 'SELECT * FROM property WHERE owner = $1';
41+
const owner = res.locals.user.id;
42+
const { rows, rowCount } = await db.query(selectQuery, [owner]);
43+
if (rowCount !== 0) {
44+
return Response.handleSuccess(200, 'Got all property adverts successfully', rows, res);
2745
}
2846
return Response.handleError(404, 'No property found', res);
2947
} catch (error) {
@@ -33,9 +51,14 @@ class PropertyController {
3351

3452
static async findOne(req, res) {
3553
try {
36-
const propertyId = parseInt(req.params.id, 10);
37-
const property = new PropertyModel(propertyId);
38-
if (await property.findOne()) return Response.handleSuccess(200, 'Got the specific property advert successfully', property.result, res);
54+
const selectQuery = 'SELECT * FROM property WHERE id = $1 AND owner = $2';
55+
const owner = res.locals.user.id;
56+
const id = parseInt(req.params.property_id, 10);
57+
const values = [id, owner];
58+
const { rows, rowCount } = await db.query(selectQuery, values);
59+
if (rowCount !== 0) {
60+
return Response.handleSuccess(200, 'Got the specific property advert successfully', rows[0], res);
61+
}
3962
return Response.handleError(404, 'Property not found', res);
4063
} catch (error) {
4164
return Response.handleError(500, error.toString(), res);
@@ -44,9 +67,15 @@ class PropertyController {
4467

4568
static async findByType(req, res) {
4669
try {
47-
const { type } = req.query;
48-
const property = new PropertyModel(type);
49-
if (await property.findByType()) return Response.handleSuccess(200, 'Got the property type successfully', property.result, res);
70+
const selectQuery = 'SELECT * FROM property WHERE type = $1 AND owner = $2';
71+
const owner = res.locals.user.id;
72+
let { type } = req.query;
73+
type = type.toLowerCase().trim();
74+
const values = [type, owner];
75+
const { rows, rowCount } = await db.query(selectQuery, values);
76+
if (rowCount !== 0) {
77+
return Response.handleSuccess(200, 'Got the property type successfully', rows, res);
78+
}
5079
return Response.handleError(404, 'Property type not found, check the property type query value', res);
5180
} catch (error) {
5281
return Response.handleError(500, error.toString(), res);
@@ -55,34 +84,68 @@ class PropertyController {
5584

5685
static async update(req, res) {
5786
try {
58-
const propertyId = parseInt(req.params.id, 10);
59-
const newProperty = req.body;
60-
newProperty.id = propertyId;
61-
const property = new PropertyModel({ ...newProperty });
62-
await property.updateProperty();
63-
return Response.handleSuccess(200, 'Updated Successfully', property.result, res);
87+
const findOneQuery = 'SELECT * FROM property WHERE id = $1 AND owner = $2';
88+
const updateQuery = `UPDATE property
89+
SET price = $1, state = $2, city = $3, address = $4, type = $5, image_url = $6
90+
WHERE id = $7 AND owner = $8 RETURNING *`;
91+
const owner = res.locals.user.id;
92+
const id = parseInt(req.params.property_id, 10);
93+
let values = [id, owner];
94+
const { rows, rowCount } = await db.query(findOneQuery, values);
95+
if (rowCount === 0) {
96+
return Response.handleError(404, 'Property not found', res);
97+
}
98+
const property = req.body;
99+
values = [
100+
property.price || rows[0].price,
101+
property.state || rows[0].state,
102+
property.city || rows[0].city,
103+
property.address || rows[0].address,
104+
property.type || rows[0].type,
105+
property.image_url || rows[0].image_url,
106+
rows[0].id,
107+
rows[0].owner
108+
];
109+
const response = await db.query(updateQuery, values);
110+
return Response.handleSuccess(200, 'Updated Successfully', response.rows[0], res);
64111
} catch (error) {
65112
return Response.handleError(500, error.toString(), res);
66113
}
67114
}
68115

69116
static async markSold(req, res) {
70117
try {
71-
const id = parseInt(req.params.id, 10);
72-
const soldProperty = { status: 'sold' };
73-
const property = new PropertyModel({ id, ...soldProperty });
74-
await property.updateProperty();
75-
return Response.handleSuccess(200, 'Mark as sold successfully', property.result, res);
118+
const updateOneQuery = `UPDATE property
119+
SET status = $1
120+
WHERE id = $2 AND owner = $3 RETURNING *`;
121+
const id = parseInt(req.params.property_id, 10);
122+
const owner = res.locals.user.id;
123+
const status = 'sold';
124+
const values = [status, id, owner];
125+
const { rows, rowCount } = await db.query(updateOneQuery, values);
126+
if (rowCount === 0) {
127+
return Response.handleError(404, 'Property not found', res);
128+
}
129+
return Response.handleSuccess(200, 'Mark as sold successfully', rows, res);
76130
} catch (error) {
77131
return Response.handleError(500, error.toString(), res);
78132
}
79133
}
80134

81135
static async delete(req, res) {
82136
try {
83-
const propertyId = parseInt(req.params.id, 10);
84-
const property = new PropertyModel(propertyId);
85-
if (await property.deleteProperty()) return Response.handleDelete(200, property.result, res);
137+
const deleteQuery = `DELETE FROM property WHERE id = $1 AND owner = $2
138+
RETURNING *`;
139+
const is_admin = res.locals.user.is_admin;
140+
const owner = res.locals.user.id;
141+
if (!is_admin) return Response.handleError(403, '!!!You do not have access to this endpoint', res);
142+
const id = parseInt(req.params.property_id, 10);
143+
const values = [id, owner];
144+
const { rowCount } = await db.query(deleteQuery, values);
145+
const result = { message: 'Deleted successfully' };
146+
if (rowCount !== 0) {
147+
return Response.handleDelete(200, result, res);
148+
}
86149
return Response.handleError(404, 'Property id not found', res);
87150
} catch (error) {
88151
return Response.handleError(500, error.toString(), res);

server/controllers/user.controller.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ class userController {
1010
const newUser = req.body;
1111
const hashedPassword = bcrypt.hashSync(newUser.password, bcrypt.genSaltSync(10), null);
1212
newUser.password = hashedPassword;
13-
const newId = Id(Users);
14-
newUser.id = newId;
13+
// const newId = Id(Users);
14+
// newUser.id = newId;
1515
const user = new UserModel({ ...newUser });
1616
if (!await user.signUp()) {
1717
return Response.handleError(409, 'Email already exist', res);

server/db/property.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ const property = [
1010
type: 'bungalow',
1111
created_on: new Date(),
1212
image_url: 'https://pixabay.com/photos/portrait-woman-lady-coffee-bar-4246954/',
13-
ownerEmail: 'olu@gmail.com',
14-
ownerPhoneNumber: '08089876543',
13+
owner_email: 'olu@gmail.com',
14+
owner_phone_number: '08089876543',
1515
},
1616
// {
1717
// id: 2,
@@ -24,8 +24,8 @@ const property = [
2424
// type: 'bungalow',
2525
// created_on: new Date(),
2626
// image_url: 'https://pixabay.com/photos/portrait-woman-lady-coffee-bar-4246954/',
27-
// ownerEmail: 'olu@gmail.com',
28-
// ownerPhoneNumber: '08089876543',
27+
// owner_email: 'olu@gmail.com',
28+
// owner_phone_number: '08089876543',
2929
// },
3030
// {
3131
// id: 3,
@@ -38,8 +38,8 @@ const property = [
3838
// type: 'duplex',
3939
// created_on: new Date(),
4040
// image_url: 'https://pixabay.com/photos/portrait-woman-lady-coffee-bar-4246954/',
41-
// ownerEmail: 'olu@gmail.com',
42-
// ownerPhoneNumber: '08089876543',
41+
// owner_email: 'olu@gmail.com',
42+
// owner_phone_number: '08089876543',
4343
// },
4444
];
4545

server/db/users.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const users = [
55
first_name: 'Peace',
66
last_name: 'Olowo',
77
password: '$2b$10$44HEDknkmYvqkCSPvL8CeevWL2XDCjHiYdEfJJmm1PqCEVUN1r3kO',
8-
phoneNumber: '08086180776',
8+
phone_number: '08086180776',
99
address: '2, ladipo street, oworo, lagos',
1010
is_admin: true,
1111
},

server/middlewares/authentication.js

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,7 @@ const userToken = async (req, res, next) => {
1212
if (!token) return Response.handleError(403, 'Token required, please sign in or register as a user', res);
1313
jwt.verify(token, process.env.JWT_SECRET, (error, user) => {
1414
if (error) return Response.handleError(401, error.message, res);
15-
const payload = user.payload;
16-
const obj = Object.values(payload).join('');
17-
const userId = Data.find((data) => {
18-
if (data.email === obj) return true;
19-
return true;
20-
});
21-
res.locals.user = userId.id;
15+
res.locals.user = user;
2216
next();
2317
});
2418
} catch (error) {

server/middlewares/property.authorization.js

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@ import Check from '../utils/helpers/check';
33
import Data from '../db/property';
44

55
class PropertyValidation {
6-
static async property(req, res, next) {
6+
static async create(req, res, next) {
77
try {
8+
const is_admin = res.locals.user.is_admin;
89
const {
910
price, state, city, address,
1011
} = req.body;
12+
if (!is_admin) return Response.handleError(403, '!!!You do not have access to this endpoint', res);
1113
if (!price || !state || !city || !address) {
1214
return Response.handleError(400, 'Please fill all the required fields', res);
1315
}
@@ -33,14 +35,16 @@ class PropertyValidation {
3335

3436
static async update(req, res, next) {
3537
try {
36-
const propertyId = parseInt(req.params.id, 10);
38+
const is_admin = res.locals.user.is_admin;
39+
const propertyId = parseInt(req.params.property_id, 10);
3740
const {
3841
price, state, city, address,
3942
} = req.body;
40-
const id = Data.some(data => data.id === propertyId);
41-
if (!id) {
42-
return Response.handleError(404, 'Property id not found', res);
43-
}
43+
if (!is_admin) return Response.handleError(403, '!!!You do not have access to this endpoint', res);
44+
// const id = Data.some(data => data.id === propertyId);
45+
// if (!id) {
46+
// return Response.handleError(404, 'Property id not found', res);
47+
// }
4448
if (await Check.checkFloat(price)) return Response.handleError(400, 'Enter valid price in numeric', res);
4549
if (await Check.checkName(state)) return Response.handleError(400, 'Enter valid state', res);
4650
if (await Check.checkName(city)) return Response.handleError(400, 'Enter valid city', res);
@@ -53,11 +57,13 @@ class PropertyValidation {
5357

5458
static async markSold(req, res, next) {
5559
try {
56-
const propertyId = parseInt(req.params.id, 10);
57-
const id = Data.some(data => data.id === propertyId);
58-
if (!id) {
59-
return Response.handleError(404, 'Property id not found', res);
60-
}
60+
const is_admin = res.locals.user.is_admin;
61+
const propertyId = parseInt(req.params.property_id, 10);
62+
if (!is_admin) return Response.handleError(403, '!!!You do not have access to this endpoint', res);
63+
// const id = Data.some(data => data.id === propertyId);
64+
// if (!id) {
65+
// return Response.handleError(404, 'Property id not found', res);
66+
// }
6167
next();
6268
} catch (error) {
6369
return Response.handleError(500, error.toString(), res);

0 commit comments

Comments
 (0)