-
Notifications
You must be signed in to change notification settings - Fork 22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Subsribe and unsubscribe functionality #3035
Changes from 2 commits
f438919
f27d5c8
97c19ab
0093a38
301bc85
6f4e083
628cccf
973c8ba
9473b72
6f81753
9c6a76d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,8 @@ | ||
const mongoose = require("mongoose"); | ||
const ObjectId = mongoose.Types.ObjectId; | ||
|
||
const baseUrl = process.env.PLATFORM_STAGING_BASE_URL || process.env.PLATFORM_PRODUCTION_BASE_URL; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider using a configuration service for defining URLs. The use of environment variables directly in the code can lead to hard-coded configurations that are difficult to manage across different environments. Consider using a centralized configuration service or module that can handle these URLs, which can be easily adjusted without changing the codebase. |
||
|
||
Comment on lines
+4
to
+5
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider using a configuration service for defining URLs. The use of environment variables directly in the code can lead to hard-coded configurations that are difficult to manage across different environments. Consider using a centralized configuration service or module that can handle these URLs, which can be easily adjusted without changing the codebase. |
||
const emailTemplates = { | ||
EMAIL_GREETINGS: function (name) { | ||
return `<tr> | ||
|
@@ -23,7 +25,26 @@ | |
</table> | ||
`; | ||
}, | ||
EMAIL_FOOTER_TEMPLATE: function (email) { | ||
EMAIL_FOOTER_TEMPLATE: function (email, product, type, paramString) { | ||
let subscriptionBlock = ``; | ||
if (product && type && paramString) { | ||
const unSubsciptionUrl = `${baseUrl}/api/v2/users/unsubscribe/${product}/${type}?${paramString}`; | ||
subscriptionBlock = ` | ||
<span | ||
style="color: #667085; font-size: 14px; font-family: Inter; font-weight: 400; line-height: 20px; word-wrap: break-word;">. | ||
If you'd rather not receive this kind of email, you can </span> | ||
<a href=${unSubsciptionUrl} target="_blank" <span | ||
style="color: #135DFF; font-size: 14px; font-family: Inter; font-weight: 400; line-height: 20px; word-wrap: break-word;">unsubscribe</span> | ||
</a> | ||
<span | ||
style="color: #667085; font-size: 14px; font-family: Inter; font-weight: 400; line-height: 20px; word-wrap: break-word;"> | ||
or </span> | ||
<span | ||
style="color: #135DFF; font-size: 14px; font-family: Inter; font-weight: 400; line-height: 20px; word-wrap: break-word;">manage | ||
your email preferences.</span><br /><br /> | ||
`; | ||
} | ||
|
||
return ` | ||
<table style="width: 100%; text-align: center; padding-top: 32px; padding-bottom: 32px;"> | ||
<tr> | ||
|
@@ -53,17 +74,7 @@ | |
email was sent to</span> | ||
<span | ||
style="color: #135DFF; font-size: 14px; font-family: Inter; font-weight: 400; line-height: 20px; word-wrap: break-word;">${email}</span> | ||
<span | ||
style="color: #667085; font-size: 14px; font-family: Inter; font-weight: 400; line-height: 20px; word-wrap: break-word;">. | ||
If you'd rather not receive this kind of email, you can </span> | ||
<span | ||
style="color: #135DFF; font-size: 14px; font-family: Inter; font-weight: 400; line-height: 20px; word-wrap: break-word;">unsubscribe</span> | ||
<span | ||
style="color: #667085; font-size: 14px; font-family: Inter; font-weight: 400; line-height: 20px; word-wrap: break-word;"> | ||
or </span> | ||
<span | ||
style="color: #135DFF; font-size: 14px; font-family: Inter; font-weight: 400; line-height: 20px; word-wrap: break-word;">manage | ||
your email preferences.</span><br /><br /> | ||
${subscriptionBlock} | ||
<span | ||
style="color: #667085; font-size: 14px; font-family: Inter; font-weight: 400; line-height: 20px; word-wrap: break-word;">© | ||
2023 AirQo<br /><br /> | ||
|
@@ -76,11 +87,11 @@ | |
`; | ||
}, | ||
|
||
EMAIL_BODY: function (email, content, name) { | ||
const footerTemplate = this.EMAIL_FOOTER_TEMPLATE(email); | ||
EMAIL_BODY: function (email, content, name, product, type, paramString) { | ||
const footerTemplate = this.EMAIL_FOOTER_TEMPLATE(email, product, type, paramString); | ||
const headerTemplate = this.EMAIL_HEADER_TEMPLATE(); | ||
let greetings = this.EMAIL_GREETINGS(name); | ||
if (!name) { | ||
if (!name || name === "") { | ||
greetings = ``; | ||
} | ||
return `<!DOCTYPE html> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -195,11 +195,17 @@ const UserSchema = new Schema( | |
category: { | ||
type: String, | ||
}, | ||
notifications: { | ||
email: { type: Boolean, default: false }, | ||
push: { type: Boolean, default: false }, | ||
text: { type: Boolean, default: false }, | ||
phone: { type: Boolean, default: false }, | ||
mobile_notifications: { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is a good direction @BenjaminSsempala , love it! :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi @BenjaminSsempala ....on a second thought, we might want to rethink this and just maintain one notifications field.......since it already had "phone" in it, we might not need to create a new field for mobile. Happy to hear your additional thoughts on the same. |
||
email: { type: Boolean, default: true }, | ||
push: { type: Boolean, default: true }, | ||
text: { type: Boolean, default: true }, | ||
phone: { type: Boolean, default: true }, | ||
}, | ||
analytics_notifications: { | ||
email: { type: Boolean, default: true }, | ||
push: { type: Boolean, default: true }, | ||
text: { type: Boolean, default: true }, | ||
phone: { type: Boolean, default: true }, | ||
}, | ||
profilePicture: { | ||
type: String, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1041,8 +1041,8 @@ router.get( | |
); | ||
|
||
/*********************************** user notifications **********************/ | ||
router.post( | ||
"/subscribe/:type", | ||
router.get( | ||
"/subscribe/:product/:type", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi @BenjaminSsempala , thanks for this.
More than happy to dig further into this in a sync of sorts. Thanks again. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hello @Baalmart Even with the one account, the user would still have multiple platforms available to them e.g how slack has both web push notifications and mobile push notifications. |
||
oneOf([ | ||
[ | ||
query("tenant") | ||
|
@@ -1054,40 +1054,49 @@ router.post( | |
.toLowerCase() | ||
.isIn(["airqo"]) | ||
.withMessage("the tenant value is not among the expected ones"), | ||
query("email") | ||
.optional() | ||
.notEmpty() | ||
.withMessage("the email must not be empty if provided") | ||
.bail() | ||
.isEmail() | ||
.withMessage("this is not a valid email address") | ||
.trim(), | ||
query("mongo_user_id") | ||
.optional() | ||
.notEmpty() | ||
.withMessage("the mongo_user_id must not be empty if provided") | ||
.bail() | ||
.trim() | ||
.isMongoId() | ||
.withMessage("the mongo_user_id must be an object ID") | ||
.bail() | ||
.customSanitizer((value) => { | ||
return ObjectId(value); | ||
}), | ||
query("firebase_user_id") | ||
.optional() | ||
.notEmpty() | ||
.withMessage("the firebase_uid must not be empty if provided") | ||
.bail() | ||
.trim(), | ||
], | ||
]), | ||
oneOf([ | ||
body("email") | ||
.exists() | ||
.withMessage( | ||
"the user identifier is missing in request, consider using the email" | ||
) | ||
.bail() | ||
.notEmpty() | ||
.withMessage("the email must not be empty if provided") | ||
.bail() | ||
.isEmail() | ||
.withMessage("this is not a valid email address") | ||
.trim(), | ||
body("user_id") | ||
.exists() | ||
.withMessage( | ||
"the user identifier is missing in request, consider using the user_id" | ||
) | ||
.bail() | ||
.notEmpty() | ||
.withMessage("the user_id must not be empty if provided") | ||
.bail() | ||
.trim() | ||
.isMongoId() | ||
.withMessage("the user_id must be an object ID") | ||
.bail() | ||
.customSanitizer((value) => { | ||
return ObjectId(value); | ||
}), | ||
]), | ||
oneOf([ | ||
[ | ||
param("product") | ||
.exists() | ||
.withMessage("the product must be provided") | ||
.bail() | ||
.notEmpty() | ||
.withMessage("the product should not be empty if provided") | ||
.bail() | ||
.trim() | ||
.toLowerCase() | ||
.isIn(["analytics", "mobile", "website"]) | ||
.withMessage( | ||
"the product value is not among the expected ones: analytics, mobile and website" | ||
), | ||
param("type") | ||
.exists() | ||
.withMessage("the type must be provided") | ||
|
@@ -1105,8 +1114,8 @@ router.post( | |
]), | ||
createUserController.subscribeToNotifications | ||
); | ||
router.post( | ||
"/unsubscribe/:type", | ||
router.get( | ||
"/unsubscribe/:product/:type", | ||
oneOf([ | ||
[ | ||
query("tenant") | ||
|
@@ -1118,40 +1127,49 @@ router.post( | |
.toLowerCase() | ||
.isIn(["airqo"]) | ||
.withMessage("the tenant value is not among the expected ones"), | ||
query("email") | ||
.optional() | ||
.notEmpty() | ||
.withMessage("the email must not be empty if provided") | ||
.bail() | ||
.isEmail() | ||
.withMessage("this is not a valid email address") | ||
.trim(), | ||
query("mongo_user_id") | ||
.optional() | ||
.notEmpty() | ||
.withMessage("the mongo_user_id must not be empty if provided") | ||
.bail() | ||
.trim() | ||
.isMongoId() | ||
.withMessage("the mongo_user_id must be an object ID") | ||
.bail() | ||
.customSanitizer((value) => { | ||
return ObjectId(value); | ||
}), | ||
query("firebase_user_id") | ||
.optional() | ||
.notEmpty() | ||
.withMessage("the firebase_uid must not be empty if provided") | ||
.bail() | ||
.trim(), | ||
], | ||
]), | ||
oneOf([ | ||
body("email") | ||
.exists() | ||
.withMessage( | ||
"the user identifier is missing in request, consider using the email" | ||
) | ||
.bail() | ||
.notEmpty() | ||
.withMessage("the email must not be empty if provided") | ||
.bail() | ||
.isEmail() | ||
.withMessage("this is not a valid email address") | ||
.trim(), | ||
body("user_id") | ||
.exists() | ||
.withMessage( | ||
"the user identifier is missing in request, consider using the user_id" | ||
) | ||
.bail() | ||
.notEmpty() | ||
.withMessage("the user_id must not be empty if provided") | ||
.bail() | ||
.trim() | ||
.isMongoId() | ||
.withMessage("the user_id must be an object ID") | ||
.bail() | ||
.customSanitizer((value) => { | ||
return ObjectId(value); | ||
}), | ||
]), | ||
oneOf([ | ||
[ | ||
param("product") | ||
.exists() | ||
.withMessage("the product must be provided") | ||
.bail() | ||
.notEmpty() | ||
.withMessage("the product should not be empty if provided") | ||
.bail() | ||
.trim() | ||
.toLowerCase() | ||
.isIn(["analytics", "mobile", "website"]) | ||
.withMessage( | ||
"the product value is not among the expected ones: analytics, mobile and website" | ||
), | ||
param("type") | ||
.exists() | ||
.withMessage("the type must be provided") | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for this @BenjaminSsempala ,
environment
specific variables are handled underconfig/environments
. Could you shed more light on this?constants.PLATFORM_BASE_URL
orconstants.ANALYTICS_BASE_URL
. See attached screenshot for visual reference.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Baalmart , Yes, I failed to use the constants variable because of circular dependencies.