Skip to content

Commit 1a25b91

Browse files
committed
WIP for blog post
1 parent 0fa8a23 commit 1a25b91

14 files changed

Lines changed: 211 additions & 186 deletions

File tree

application-business-rules/use-cases/products/product-handlers.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,10 @@ const createProductUseCase = ({ makeProductModelHandler }) => async function
3232
const findOneProductUseCase = ({ productValidation }) => async function
3333
findOneProductUseCaseHandler({ productId, logEvents, findOneProductDbHandler, errorHandlers }) {
3434
const { InvalidPropertyError } = errorHandlers;
35-
const { validateUUID } = productValidation;
35+
const { } = productValidation;
3636
try {
3737
// validate id
38-
const uuid = validateUUID(productId, InvalidPropertyError);
38+
const uuid = validateObjectId(productId, InvalidPropertyError);
3939
// store product in database mongodb
4040
const newProduct = await findOneProductDbHandler({ productId: uuid });
4141
return Object.freeze(newProduct)
@@ -73,10 +73,10 @@ const deleteProductUseCase = () => async function deleteProductUseCaseHandler({
7373

7474
const { findOneProductDbHandler, deleteProductDbHandler } = dbProductHandler;
7575
const { InvalidPropertyError } = errorHandlers;
76-
const { validateUUID } = productValidation;
76+
const { validateObjectId } = productValidation;
7777
try {
7878
// validate id
79-
const uuid = validateUUID(productId, InvalidPropertyError);
79+
const uuid = validateObjectId(productId, InvalidPropertyError);
8080
// check first that the product exists
8181
const existingProduct = await findOneProductDbHandler({ productId: uuid });
8282
if (!existingProduct) {
@@ -105,10 +105,10 @@ const updateProductUseCase = ({ makeProductModelHandler }) => async function
105105

106106
const { findOneProductDbHandler, updateProductDbHandler } = dbProductHandler;
107107
const { InvalidPropertyError } = errorHandlers;
108-
const { validateUUID } = productValidation;
108+
const { } = productValidation;
109109
try {
110110
// validate id
111-
const uuid = validateUUID(productId, InvalidPropertyError);
111+
const uuid = validateObjectId(productId, InvalidPropertyError);
112112
// check first that the product exists
113113
const existingProduct = await findOneProductDbHandler({ productId: uuid });
114114
if (!existingProduct) {

enterprise-business-rules/entities/blog-model.js

Whitespace-only changes.

enterprise-business-rules/entities/index.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,19 @@
22
// user and product validation
33
const userValidationData = require("../validate-models/user-validation-functions");
44
const productValidation = require("../validate-models/product-validation-fcts")();
5+
const { validateRatingModel } = require("../validate-models/rating-validation")();
56

67
//log errors
78
const { logEvents } = require("../../interface-adapters/middlewares/loggers/logger");
89

910
// user and product models
1011
const { makeUserModel } = require("./user-model");
1112
const productModels = require("./product-model");
13+
const { makeRatingProductModel } = require("./rating-model");
1214

1315
const makeUser = makeUserModel({ userValidationData, logEvents });
1416
const makeProductModelHandler = productModels.makeProductModel({ productValidation });
15-
const makeProductRatingModelHandler = productModels.makeRatingProductModel({ productValidation });
17+
const makeProductRatingModelHandler = makeRatingProductModel({ validateRatingModel });
1618

1719

1820
module.exports = {

enterprise-business-rules/entities/product-model.js

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ module.exports = {
44
makeProductModel: ({ productValidation }) => async function makeProductModelHandler({ productData, errorHandlers }) {
55

66
console.log(" hit makeProduct model: ");
7-
87
const { basicProductValidation } = productValidation;
98

109
try {
@@ -16,20 +15,4 @@ module.exports = {
1615
throw new Error(error.message);
1716
}
1817
},
19-
20-
//make rating model
21-
makeRatingProductModel: ({ productValidation }) => async function makeProductRatingModelHandler({ errorHandlers, ...ratingData }) {
22-
23-
console.log(" hit make Rating Product model: ");
24-
const { validateRatingModel } = productValidation;
25-
const { InvalidPropertyError } = errorHandlers;
26-
27-
try {
28-
const validatedRatingData = await validateRatingModel(ratingData, InvalidPropertyError);
29-
return Object.freeze(validatedRatingData)
30-
} catch (error) {
31-
console.log("Error from rating-model handler: ", error);
32-
throw new Error(error.message);
33-
}
34-
}
3518
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
module.exports = {
2+
3+
//make rating model
4+
makeRatingProductModel: ({ validateRatingModel }) => async function makeProductRatingModelHandler({ errorHandlers, ...ratingData }) {
5+
6+
console.log(" hit make Rating Product model: ");
7+
const { InvalidPropertyError } = errorHandlers;
8+
9+
try {
10+
const validatedRatingData = await validateRatingModel(ratingData, InvalidPropertyError);
11+
return Object.freeze(validatedRatingData)
12+
} catch (error) {
13+
console.log("Error from rating-model handler: ", error);
14+
throw new Error(error.message);
15+
}
16+
}
17+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
const productValidation = require("./product-validation-fcts")();
2+
3+
const { validateDescription, validateTitle, validateObjectId } = productValidation;
4+
5+
//validate cover image for only more optimized types
6+
const validateCoverImage = ({ cover_image, InvalidPropertyError }) => {
7+
8+
const ext = cover_image.split('.').pop();
9+
if (ext !== 'png' && ext !== 'jpeg' && ext !== 'jpg') {
10+
throw new InvalidPropertyError(
11+
`Invalid cover image.`
12+
)
13+
}
14+
return cover_image;
15+
}
16+
17+
//validate tags as an array of strings
18+
const validateTags = (tags, InvalidPropertyError) => {
19+
if (!tags || !tags.length) {
20+
throw new InvalidPropertyError(
21+
`Tags are required.`
22+
)
23+
}
24+
return tags ?? [];
25+
}
26+
27+
28+
//validate image_urls as an array of strings
29+
const validateImageUrls = ({ image_urls, InvalidPropertyError }) => {
30+
return image_urls.map(image => validateCoverImage({ coverImage: image, InvalidPropertyError }));
31+
}
32+
33+
34+
// create blog post model validation
35+
const blogPostValidation = ({ blogPostData, errorHandlers }) => {
36+
37+
// blog post props
38+
const {
39+
title,
40+
content,
41+
author,
42+
tags,
43+
image_urls,
44+
cover_image,
45+
} = blogPostData;
46+
47+
const { RequiredParameterError, InvalidPropertyError } = errorHandlers;
48+
const resultingBlogPostData = {};
49+
const errors = [];
50+
if (!title) {
51+
errors.push(`Blog post title is required`);
52+
} else resultingBlogPostData.title = validateTitle({ title, InvalidPropertyError });
53+
if (!content) {
54+
errors.push(`Blog post description is required`);
55+
} else resultingBlogPostData.content = validateDescription({ description: content, InvalidPropertyError });
56+
57+
if (!cover_image)
58+
errors.push(`Blog post cover image is required`);
59+
else resultingBlogPostData.cover_image = validateCoverImage({ cover_image, InvalidPropertyError });
60+
if (!author) {
61+
errors.push(`Blog post author is required`);
62+
} else resultingBlogPostData.author = validateTitle({ title: author, InvalidPropertyError });
63+
if (!tags) {
64+
errors.push(`Blog post tags are required`);
65+
} else resultingBlogPostData.tags = validateTags(tags, InvalidPropertyError);
66+
if (!image_urls) {
67+
errors.push(`Blog post image_urls are required`);
68+
} else resultingBlogPostData.image_urls = validateImageUrls({ image_urls, InvalidPropertyError });
69+
70+
if (errors.length) {
71+
throw new RequiredParameterError(errors.join("\n"));
72+
}
73+
74+
resultingBlogPostData.isLiked = false;
75+
resultingBlogPostData.isDisliked = false;
76+
resultingBlogPostData.likes = [];
77+
resultingBlogPostData.comments = [];
78+
resultingBlogPostData.numViews = 0;
79+
resultingBlogPostData.created_at = new Date().toISOString();
80+
resultingBlogPostData.lastModifiedDate = null;
81+
82+
console.log("successfully validated blog post: ");
83+
return resultingBlogPostData;
84+
}
85+
module.exports = Object.freeze({
86+
blogPostValidation,
87+
validateCoverImage,
88+
validateTags,
89+
validateImageUrls,
90+
validateTitle,
91+
validateDescription,
92+
})

enterprise-business-rules/validate-models/product-validation-fcts.js

Lines changed: 18 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@ function validateTitle({ title, InvalidPropertyError }) {
99
//validate title length
1010

1111
//validate and normalise product description
12-
function validateDescription({ descripton, InvalidPropertyError }) {
13-
if (descripton.length < 50) {
12+
function validateDescription({ description, InvalidPropertyError }) {
13+
if (description.length < 50) {
1414
throw new InvalidPropertyError(
1515
`A product's title must be at least 20 characters long.`
1616
)
1717
}
18-
return descripton.charAt(0).toUpperCase() + descripton.slice(1);
18+
return description.charAt(0).toUpperCase() + description.slice(1);
1919
}
2020

2121
/**
@@ -153,46 +153,21 @@ const CalculateTotalReviews = (totalRatings) => {
153153
}
154154

155155
//calculate average rating
156-
const calculateAverageRating = (totalRatings) => {
156+
const calculateAverageRating = (totalRatings, resultingProductData) => {
157157
if (!totalRatings || !totalRatings.length) return 0
158158
return totalRatings.reduce((acc, curr, idx) => acc + curr * (idx + 1), 0) / resultingProductData.totalReviews;
159159
}
160160

161-
// validate uuid as ObjectId
162-
const validateUUID = (id, InvalidPropertyError) => {
163-
// const regex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
164-
// console.log(id);
165-
const regex = /^[a-fA-F0-9]{24}$/; // ObjectId validation regex
166-
if (!regex.test(id)) {
167-
throw new InvalidPropertyError(`Invalid product ObjectId.`)
161+
// validate uuid as ObjectId
162+
const validateObjectId = (id, InvalidPropertyError) => {
163+
if (!/^[0-9a-fA-F]{24}$/.test(id)) {
164+
throw new InvalidPropertyError("Invalid product id.");
168165
}
169166
return id;
170-
}
171-
172-
// validate rating model
173-
const validateRatingModel = (ratingModel, InvalidPropertyError) => {
174-
const { productId, userId, ratingValue } = ratingModel;
175-
176-
// validate IDs
177-
const productUUID = validateUUID(productId, InvalidPropertyError);
178-
const userUUID = validateUUID(userId, InvalidPropertyError);
179-
180-
// validate rating value
181-
const validRatingValues = [1, 2, 3, 4, 5];
182-
if (!ratingValue || !validRatingValues.includes(ratingValue)) {
183-
throw new InvalidPropertyError(
184-
`Invalid rating value.`
185-
)
186-
}
187167

188-
return {
189-
productId: productUUID,
190-
userId: userUUID,
191-
ratingValue,
192-
date: new Date().toISOString(),
193-
};
194168
}
195169

170+
196171
//basic product validation
197172
const basicProductValidation = ({ productData, errorHandlers }) => {
198173

@@ -207,7 +182,7 @@ const basicProductValidation = ({ productData, errorHandlers }) => {
207182

208183
if (!productData.descripton) {
209184
errors.push(`Product descripton is required`);
210-
} else resultingProductData.descripton = validateDescription({ descripton: productData.descripton, InvalidPropertyError });
185+
} else resultingProductData.description = validateDescription({ description: productData.description, InvalidPropertyError });
211186

212187
if (!productData.price) {
213188
errors.push(`Product price is required`);
@@ -283,13 +258,13 @@ const basicProductValidation = ({ productData, errorHandlers }) => {
283258
resultingProductData.latestRating = null;
284259
resultingProductData.rateAverage = 0;
285260
resultingProductData.lastModified = new Date().toISOString();
286-
resultingProductData.instock = Boolean(resultingProductData.inventory && resultingProductData.inventory > 0)
261+
resultingProductData.instock = Boolean(resultingProductData.inventory)
287262
resultingProductData.brands = productData.brands || [];
288263

289264
if (errors.length) {
290265
throw new RequiredParameterError(errors.join(', '));
291266
}
292-
console.log("end of validations: ");
267+
console.log("successfully validated product: ");
293268
return resultingProductData;
294269
}
295270

@@ -298,7 +273,11 @@ module.exports = () => {
298273
basicProductValidation,
299274
CalculateTotalReviews,
300275
calculateAverageRating,
301-
validateUUID,
302-
validateRatingModel
276+
validateObjectId,
277+
validateTitle,
278+
validateNumber,
279+
validateCategory,
280+
validateColors,
281+
validateDescription
303282
})
304283
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
2+
const { validateObjectId,
3+
} = require("./product-validation-fcts")();
4+
5+
// validate rating model
6+
const validateRatingModel = ({ productId, userId, ratingValue }, InvalidPropertyError) => {
7+
// validate IDs
8+
validateObjectId,
9+
(productId, InvalidPropertyError);
10+
validateObjectId,
11+
(userId, InvalidPropertyError);
12+
13+
// validate rating value
14+
if (!ratingValue || ![1, 2, 3, 4, 5].includes(ratingValue)) {
15+
throw new InvalidPropertyError(
16+
`Invalid rating value.`
17+
)
18+
}
19+
20+
return {
21+
productId,
22+
userId,
23+
ratingValue,
24+
date: new Date().toISOString(),
25+
};
26+
}
27+
28+
module.exports = () => {
29+
return Object.freeze({
30+
validateObjectId,
31+
validateRatingModel
32+
})
33+
}

index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const userAndAuthRouter = require('./routes/auth-user.router.js');
99
const { logger } = require('./interface-adapters/middlewares/loggers/logger.js');
1010
const productRouter = require('./routes/product.routes.js');
1111
const createIndexFn = require('./interface-adapters/database-access/db-indexes.js');
12+
const blogRouter = require('./routes/blog.router.js');
1213

1314
const app = express();
1415

@@ -31,6 +32,7 @@ app.use(express.urlencoded({ extended: false }));
3132

3233
app.use('/users', userAndAuthRouter);
3334
app.use('/products', productRouter);
35+
app.use('/blogs', blogRouter);
3436

3537
app.use("/", (_, res) => {
3638
res.sendFile(path.join(__dirname, "public", "views", "index.html"));

0 commit comments

Comments
 (0)