Skip to content

Commit ad20d7c

Browse files
committed
basic user registration done
1 parent baafc2c commit ad20d7c

35 files changed

Lines changed: 579 additions & 162 deletions

File tree

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
const {makeUser} = require("../../../enterprise-business-rules/entities");
2+
const { UniqueConstraintError, InvalidPropertyError } = require("../../../interface-adapters/config/validators-errors/errors");
3+
const { makeHttpError } = require("../../../interface-adapters/config/validators-errors/http-error");
4+
5+
module.exports = {
6+
7+
addUserUseCase: ({dbUserHandler}) => {
8+
return async function addUser (userData){
9+
10+
11+
try {
12+
//validate the user data after sanitizing it.
13+
const validatedUser =await makeUser(userData);
14+
15+
/// check the existance of this user in DB
16+
const existingUser = await dbUserHandler.findUserByEmail(validatedUser);
17+
if(existingUser){
18+
console.log("user already exists: ", existingUser);
19+
existingUser;
20+
}
21+
22+
return await dbUserHandler.registerUser(validatedUser);
23+
} catch (error) {
24+
console.log(error);
25+
return makeHttpError({
26+
errorMessage: error.message,
27+
statusCode: 400
28+
})
29+
}
30+
31+
32+
33+
34+
35+
}
36+
}
37+
}
38+
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
const {addUserUseCase} = require("./add-user");
2+
const dbUserHandler = require("../../../framework-and-drivers/database-access/")
3+
4+
5+
const addUser = addUserUseCase({dbUserHandler})
6+
7+
module.exports = {addUser}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
const {makeUserModel} = require("./user-model");
2+
// const ModelValidator = require("../validate-models/validation-functions");
3+
const {normalise, validateUserData} = require("../validate-models/validation-functions");
4+
5+
// const modelValidator = new ModelValidator();
6+
// console.log(modelValidator.upperFirst({firstName: "avombrice"}));
7+
8+
// const makeUser = makeUserModel({normalise: modelValidator
9+
// .normalise, validateUserData: modelValidator.validateUserData});
10+
11+
12+
const makeUser = makeUserModel({normalise, validateUserData});
13+
14+
15+
module.exports = {
16+
makeUser
17+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
module.exports = {
2+
3+
makeUserModel: ({ normalise, validateUserData }) => {
4+
5+
return async function makeUser(userData) {
6+
7+
const validatedUserData =await validateUserData({...userData});
8+
const normalisedUserData = normalise(validatedUserData);
9+
return Object.freeze(normalisedUserData)
10+
}
11+
}
12+
}
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
2+
const { InvalidPropertyError } = require("../../interface-adapters/config/validators-errors/errors");
3+
const bcrypt = require("bcrypt");
4+
5+
6+
7+
/**
8+
* Validates the format of an email address.
9+
*
10+
* @param {string} email - The email address to validate.
11+
* @return {boolean} Indicates whether the email address is in a valid format.
12+
*/
13+
function validateEmail(email) {
14+
const re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/i;
15+
if (!re.test(String(email).toLowerCase())){
16+
throw new InvalidPropertyError(" Invalid Email ")
17+
} ;
18+
19+
return email;
20+
}
21+
22+
/**
23+
* Validates the name and replaces any '<' characters with '&lt;'.
24+
*
25+
* @param {string} name - The name to be validated.
26+
* @throws {InvalidPropertyError} If the name is less than 2 characters long.
27+
* @return {string} The validated name with '<' characters replaced, or 'No Name' if the name is falsy.
28+
*/
29+
function validateName(name) {
30+
if (name.length < 2) {
31+
throw new InvalidPropertyError(
32+
`A user's name must be at least 2 characters long.`
33+
)
34+
}
35+
36+
return name.replace(/</g, "&lt;") || "No Name";
37+
}
38+
39+
/**
40+
* Validates a phone number.
41+
*
42+
* @param {string} phone - The phone number to validate.
43+
* @throws {InvalidPropertyError} If the phone number is invalid.
44+
*/
45+
function validatePhone(phone) {
46+
const phoneRegex = /^\(?([0-9]{3})\)?[-. ]?([0-9]{3})[-. ]?([0-9]{3})$/;
47+
48+
// TODO : optimize phone number validation
49+
if (!phoneRegex.test(phone) || phone.length < 11) {
50+
throw new InvalidPropertyError(`A user's phone number is not valid.`);
51+
}
52+
53+
return phone;
54+
}
55+
56+
/**
57+
* Normalizes user data by capitalizing the first letter of first and last names, and converting email to lowercase.
58+
*
59+
* @param {string} firstName - The first name of the user.
60+
* @param {string} lastName - The last name of the user.
61+
* @param {string} email - The email address of the user.
62+
* @param {...Object} otherInfo - Additional user information.
63+
* @return {Object} The normalized user data.
64+
*/
65+
function normalise({ firstName, lastName, email, ...otherInfo }) {
66+
return {
67+
...otherInfo,
68+
firstName: firstName.charAt(0).toUpperCase()+ firstName.slice(1),
69+
lastName: lastName.charAt(0).toUpperCase()+ lastName.slice(1),
70+
email: email.toLowerCase()
71+
}
72+
}
73+
74+
75+
async function validatePassword(password) {
76+
if (password.length < 8) {
77+
throw new InvalidPropertyError(
78+
`A user's password must be at least 8 characters long.`
79+
)
80+
}
81+
const hashPw = await bcrypt.hash(password, 10); //generate salt and encrypt
82+
return hashPw;
83+
}
84+
85+
/**
86+
* Validates user data by calling individual validation functions for each field.
87+
*
88+
* @param {Object} userData - An object containing the user's data.
89+
* @param {string} userData.firstName - The user's first name.
90+
* @param {string} userData.lastName - The user's last name.
91+
* @param {string} userData.email - The user's email address.
92+
* @param {string} userData.mobile - The user's mobile number.
93+
* @param {string} userData.password - The user's password.
94+
* @return {Object} An object containing the validation results for each field.
95+
* - firstName: The result of validating the first name.
96+
* - lastName: The result of validating the last name.
97+
* - email: The result of validating the email address.
98+
* - mobile: The result of validating the mobile number.
99+
* - password: The result of validating the password.
100+
*/
101+
async function validateUserData({
102+
firstName,
103+
lastName,
104+
email,
105+
mobile,
106+
password
107+
}) {
108+
109+
if (!firstName && !lastName) {
110+
return res.status(400).json({ msg: 'user must have at least one name.' })
111+
}
112+
113+
if (!email) {
114+
return res.status(400).json({ msg: 'user must have an email.' })
115+
}
116+
117+
if (!password) {
118+
return res.status(400).json({ msg: 'user must have a password.' })
119+
}
120+
121+
return {
122+
email: validateEmail(email),
123+
mobile: validatePhone(mobile),
124+
password: await validatePassword(password),
125+
firstName: validateName(firstName),
126+
lastName: validateName(lastName),
127+
};
128+
}
129+
130+
131+
132+
module.exports = {validateUserData, normalise};

framework-and-drivers/database-access/db-connection.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
const MongoClient = require("mongodb").MongoClient;
2-
const { logEvents } = require("../../middlewares/loggers/logger")
2+
const { logEvents } = require("../../interface-adapters/middlewares/loggers/logger")
33
module.exports = {
44

5-
connection: async () => {
5+
dbconnection: async () => {
66
// The MongoClient is the object that references the connection to our
77
// datastore (Atlas, for example)
88
const client = new MongoClient(process.env.DB_URI);

framework-and-drivers/database-access/db-indexes.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ dotenv.config();
1212
.createIndexes([
1313
{ key: { id: 1 }, name: 'userId_idx' },
1414
{ key: { email: 1 }, name: 'email_idx' },
15-
])
15+
]);
1616
console.log(result)
1717
console.log('Database setup complete...')
1818
process.exit()
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
const dbConnection = require("./db-connection");
1+
const {dbconnection} = require("./db-connection");
22
const makeUserdb = require("./store-user")
33

4-
const userUseCase = makeUserdb({dbConnection});
4+
const dbUserHandler = makeUserdb({dbconnection});
55

6-
module.exports = userUseCase;
6+
module.exports = dbUserHandler;
Lines changed: 62 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,75 @@
1+
const { ReturnDocument } = require("mongodb");
12

3+
async function findUserByEmail(email, dbconnection) {
24

3-
module.exports = async function makeUser({ dbConnection }) {
4-
5-
6-
return Object.freeze({
7-
findAllUsers,
8-
findUserById,
9-
createUser,
10-
updateUser,
11-
deleteUser
12-
})
13-
14-
15-
async function findAllUsers() {
16-
const db = await dbConnection()
17-
const result = await db.collection('users').find({})
5+
const db = await dbconnection()
6+
try {
7+
const result = await db.collection('users').find({ email })
8+
// const found = await result.toArray()
9+
// console.log(" result from DB checking", result)
1810
return (await result.toArray()).map(({ _id: id, ...found }) => ({
1911
id,
2012
...found
21-
}))
13+
}))
14+
} catch (error) {
15+
console.log("error checking for thexistence of user in DB", error);
16+
return null;
2217
}
18+
}
2319

24-
async function findUserById() {
25-
const db = await dbConnection()
26-
const result = await db.collection('users').find({ _id })
27-
const found = await result.toArray()
28-
if (found.length === 0) {
29-
return null
30-
}
31-
const { _id: id, ...info } = found[0]
32-
return { id, ...info }
33-
}
20+
async function registerUser(userData, dbconnection) {
3421

35-
async function createUser({ id: _id = Id.makeId(), ...userData }) {
36-
const db = await dbConnection()
22+
const db = await dbconnection()
23+
try {
24+
//insert document and return the inserted document
3725
const result = await db
38-
.collection('users')
39-
.insertOne({ _id, ...userData })
40-
const { _id: id, ...insertedInfo } = result.ops[0]
41-
return { id, ...insertedInfo }
26+
.collection('users')
27+
.insertOne({...userData });
28+
return result
29+
30+
} catch (error) {
31+
32+
console.log("error registering the user to DB: ", error);
33+
if (error.code === 11000) {
34+
throw new UniqueConstraintError(error.message)
35+
}
36+
return null;
4237
}
38+
39+
}
4340

44-
async function updateUser({ id: _id, userData }) {
45-
const db = await dbConnection()
46-
const result = await db
47-
.collection('users')
48-
.updateOne({ _id }, { $set: { ...userData } })
49-
return result.modifiedCount > 0 ? { id: _id, ...userData } : null
50-
}
41+
// async function findAllUsers() {
42+
// const db = await dbconnection()
43+
// const result = await db.collection('users').find({})
44+
// return (await result.toArray()).map(({ _id: id, ...found }) => ({
45+
// id,
46+
// ...found
47+
// }))
48+
// }
5149

52-
async function deleteUser({ id: _id }) {
53-
const db = await dbConnection()
54-
const result = await db.collection('users').deleteOne({ _id })
55-
return result.deletedCount
56-
}
50+
51+
// async function updateUser({ id: _id, userData }) {
52+
// const db = await dbconnection()
53+
// const result = await db
54+
// .collection('users')
55+
// .updateOne({ _id }, { $set: { ...userData } })
56+
// return result.modifiedCount > 0 ? { id: _id, ...userData } : null
57+
// }
58+
59+
// async function deleteUser({ id: _id }) {
60+
// const db = await dbconnection()
61+
// const result = await db.collection('users').deleteOne({ _id })
62+
// return result.deletedCount
63+
// }
64+
65+
66+
module.exports = function makeUserdb({ dbconnection }) {
67+
68+
return Object.freeze({
69+
// findAllUsers,
70+
findUserByEmail: async({ email }) => findUserByEmail(email, dbconnection),
71+
registerUser: async(userData) => registerUser(userData, dbconnection),
72+
// updateUser,
73+
// deleteUser
74+
})
5775
}

0 commit comments

Comments
 (0)