I need to send email to all user in array - javascript

I am working on a function to send emails to users, I already have my send mail function and it sends to a single user. I am trying to send to multiple users in an array now.
I am writing in Typescript and I am using mailgun as my email service provider
This is a sample send mail function:
const data = {
from: 'example#from.com',
to: 'example#reciever.com',
subject: 'Sample subject',
html: await email(), // I am getting the email template here
}
await mailgun.messages().send(data)
With the code above, I am sending email to a single user. The json below shows how I am receiving the list of emails:
[
{
"name": "User 1",
"email": "user1#gmail.com"
},
{
"name": "user 2",
"email": "user2#gmail.com"
}
]
I want to be able to send to multiple users and I want to do it the smart way. Please suggest the best solution with code if possible.
Thank you.

Assuming the send function is async and returns a promise:
async function sendMail(){
const myRecipients = [
{
"name": "User 1",
"email": "user1#gmail.com"
},
{
"name": "user 2",
"email": "user2#gmail.com"
}
]
const fromAddress = "my#email.address"
// map our recipient array to the message format expected by `send`
const myMessages = myRecipients.map(r => ({
from: fromAddress,
to: r.email,
subject: "somesubject",
html: "some html"
}))
// map our messages to a bunch of "in-flight" promises
const promises = myMessages.map(m => mailgun.messages().send(m))
// wait for all the promises to complete
await Promise.all(promises)
}

I assume you're using mailgun-js, which seems to have something called mailing list which you possibly could utilize. If not, then I would suggest that rather than simply iterating the users list and sending one email at the time (synchronously with await), trigger all emails to be sent asynchronously and then use Promise.all to know when all has been sent. Sample below is by no means tested (as i've never used mailgun) but it should give you an idea on how to implement it.
const users = [
{
"name": "User 1",
"email": "user1#gmail.com"
},
{
"name": "user 2",
"email": "user2#gmail.com"
}
];
const sendEmailToUser = async (user) => {
const data = {
from: 'example#from.com',
to: 'example#reciever.com',
subject: 'Sample subject',
html: 'await email()'
};
await mailgun.messages().send(data);
};
(async () => {
const sendEmailPromises = [];
for(const user of users) {
// Start sending all emails
sendEmailPromises.push(sendEmailToUser(user));
}
// Wait for all emails to be sent
await Promise.all(sendEmailPromises);
// Do something
})()

You will have to iterate the data structure anyway.
var users = [
{
"name": "User 1",
"email": "user1#gmail.com"
},
{
"name": "user 2",
"email": "user2#gmail.com"
}
];
users.forEach(function(user){
name = user.name;
email = user.email;
var data = {
from: 'example#from.com',
to: email,
subject: 'Sample subject',
html: await email(),
}
await mailgun.messages().send(data);
});

use a array map
i dont know about typescript.
you can get all elements like this
for ES6
user_list.map(user => console.log(user.name, user.email))

Related

how to convert an object with array values to strings and insert in my html

Below is the code i want to pass on to my errors array an send the values to my client side template in html
{
"email": [
"user with this email already exists."
]
}
i want something like this
"user with this email already exists"
i want to ignore the property name and just take the value which is the strings. i have tried using response.json() to convert it but it still returns error of response.json is not a function
const data = {
"email": [
"user with this email already exists."
]
};
console.log(Object.values(data).flat());
I think you could try something like this:
let errors = {
"email": [
"user with this email already exists.",
"error 2",
],
"password": [
"password is too weak",
]
};
let errorsStr = "";
for (let key in errors) {
for (let error of errors[key]) {
errorsStr += error + "; ";
}
}
console.log(errorsStr);
Or alternative solution:
let errors = {
"email": [
"user with this email already exists.",
"error 2",
],
"password": [
"password is too weak",
]
};
let errorsArray = Object.values(errors).flat();
let errorsStr = errorsArray.join("; ");
console.log(errorsStr);

What is the correct way to handle validation with json-schema in objection.js?

I want to understand if using json-schema as my main validation tool would be a good choice. The main thing I'm not sure about is whether I should use it to validate the query params in my API besides it being used to validate DB input.
I'll try to provide a better explanation with code examples below:
Let's start with a json-schema for the User model:
{
"$schema": "http://json-schema.org/draft-07/schema",
"type": "object",
"title": "User",
"description": "User schema",
"required": ["email", "full_name", "password"],
"additionalProperties": false,
"properties": {
"id": {
"$id": "#/properties/id",
"title": "User ID",
"type": "integer"
},
"email": {
"$id": "#/properties/email",
"title": "User email. Must be unique",
"type": "string",
"format": "email"
},
"password": {
"$id": "#/properties/password",
"title": "Hashed password for the user",
"type": "string",
"maxLength": 128,
"pattern": "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[#$!%*?&])[A-Za-z\\d#$!%*?&]{8,}$"
},
"full_name": {
"$id": "#/properties/full_name",
"title": "User first name and last name",
"type": "string",
"maxLength": 128,
"pattern": "^[a-zA-Z]+(?:\\s[a-zA-Z]+)+$"
},
"image_url": {
"$id": "#/properties/image_url",
"title": "URL to user image",
"type": "string"
},
"created_at": {
"$id": "#/properties/created_at",
"title": "The creation date of the user",
"type": "string"
},
"updated_at": {
"$id": "#/properties/updated_at",
"title": "The date the user was last updated",
"type": "string"
}
}
}
As you can see, I'm using regex to validate the input for each field to ensure the format is correct. I can specify which fields are required which is very useful and I set additionalProperties to false which means that the schema/Objection will not accept properties that are not specified in the json schema.
Next let's take a look at an example of a registration API that I'm trying to use:
router.post("/registration", async (req, res, next) => {
try {
const { password, ...payload } = await User.query().insert(req.body);
const token = await jwt.sign(payload);
res.json({ user: payload, token });
} catch (error) {
next(error);
}
});
So there's no validation of the request body in the route itself, or really in any other place, I'm trying to delegate the validation entirely to json-schema. When the request comes in, the password is not hashed so it can pass the validation, but then I need a way of storing the hashed password.
Currently I'm using this solution but I'm not sure if it's smart/safe?
// User model
async $beforeInsert(queryContext) {
this.$setJson(
{ password: await bcrypt.hash(this.password, 12) },
{ skipValidation: true }
);
await super.$beforeInsert(queryContext);
}
This would enable the following:
validation to check for the correct params (full_name, email, password) and test whether the values are correct
after the validation passes, update the model with the hashed password and skip another validation as it's already been run
insert (safe?) data in the db
Now let's look at the login route:
router.post("/login", async (req, res, next) => {
try {
User.fromJson(req.body, { skipRequiredFields: ["full_name"] });
const { email, password } = req.body;
const user = await User.query().where({ email }).first();
if (!user) {
res.status(403);
throw new Error("Invalid credentials");
}
if (!(await bcrypt.compare(password, user.password))) {
res.status(403);
throw new Error("Invalid credentials");
}
const payload = { id: user.id, email, full_name: user.full_name };
const token = await jwt.sign(payload);
res.json({ user: payload, token });
} catch (error) {
next(error);
}
});
Because I want to leave the validation to the json-schema and I need to check the request payload, I have to create a model which is redundant for the route itself, it's only used to trigger the validation. Another reason to do this is because .where({ email }) doesn't trigger the validation and it throws an error when email is undefined.
Code to remove specific required fields from the schema when needed:
// User model
$beforeValidate(jsonSchema, _, { skipRequiredFields }) {
if (skipRequiredFields) {
return {
...jsonSchema,
required: jsonSchema.required.filter(
(fieldName) => !skipRequiredFields.includes(fieldName)
),
};
}
}
This allows me to remove required fields from the validation schema. In this case it works perfectly for the /login route as I don't want the request body to have a full_name field.
So far this has been working for me but it feel more like a workaround rather than an actual solution. I am also not necessarily sure about the fact that I have to tap in into Objection hooks and override the password like that. The last thing I don't like is the fact that I have to create a redundant model to trigger the validation but I also understand that triggering a validation (or having the option to) on .when() doesn't make much sense.
I'm new to Objection and this is not a production project but rather a side-project which I'm using to explore Objection along side other frameworks/libraries and I'd like to know if there are better ways of doing this or maybe my thinking is entirely wrong and I should have a separate validation for the request body and leave the json-schema as a db validation only?

How do I query an index properly with Dynamoose

I'm using Dynamoose to simplify my interactions with DynamoDB in a node.js application. I'm trying to write a query using Dynamoose's Model.query function that will search a table using an index, but it seems like Dynamoose is not including all of the info required to process the query and I'm not sure what I'm doing wrong.
Here's what the schema looks like:
const UserSchema = new dynamoose.Schema({
"user_id": {
"hashKey": true,
"type": String
},
"email": {
"type": String,
"index": {
"global": true,
"name": "email-index"
}
},
"first_name": {
"type": String,
"index": {
"global": true,
"name": "first_name-index"
}
},
"last_name": {
"type": String,
"index": {
"global": true,
"name": "last_name-index"
}
}
)
module.exports = dynamoose.model(config.usersTable, UserSchema)
I'd like to be able to search for users by their email address, so I'm writing a query that looks like this:
Users.query("email").contains(query.email)
.using("email-index")
.all()
.exec()
.then( results => {
res.status(200).json(results)
}).catch( err => {
res.status(500).send("Error searching for users: " + err)
})
I have a global secondary index defined for the email field:
When I try to execute this query, I'm getting the following error:
Error searching for users: ValidationException: Either the KeyConditions or KeyConditionExpression parameter must be specified in the request.
Using the Dynamoose debugging output, I can see that the query winds up looking like this:
aws:dynamodb:query:request - {
"FilterExpression": "contains (#a0, :v0)",
"ExpressionAttributeNames": {
"#a0": "email"
},
"ExpressionAttributeValues": {
":v0": {
"S": "mel"
}
},
"TableName": "user_qa",
"IndexName": "email-index"
}
I note that the actual query sent to DynamoDB does not contain KeyConditions or KeyConditionExpression, as the error message indicates. What am I doing wrong that prevents this query from being written correctly such that it executes the query against the global secondary index I've added for this table?
As it turns out, calls like .contains(text) are used as filters, not query parameters. DynamoDB can't figure out if the text in the index contains the text I'm searching for without looking at every single record, which is a scan, not a query. So it doesn't make sense to try to use .contains(text) in this context, even though it's possible to call it in a chain like the one I constructed. What I ultimately needed to do to make this work is turn my call into a table scan with the .contains(text) filter:
Users.scan({ email: { contains: query.email }}).all().exec().then( ... )
I am not familiar with Dynamoose too much but the following code below will do an update on a record using node.JS and DynamoDB. See the key parameter I have below; by the error message you got it seems you are missing this.
To my knowledge, you must specify a key for an UPDATE request. You can checks the AWS DynamoDB docs to confirm.
var params = {
TableName: table,
Key: {
"id": customerID,
},
UpdateExpression: "set customer_name= :s, customer_address= :p, customer_phone= :u, end_date = :u",
ExpressionAttributeValues: {
":s": customer_name,
":p": customer_address,
":u": customer_phone
},
ReturnValues: "UPDATED_NEW"
};
await docClient.update(params).promise();

send template mandrill javascript

I am new to programming and would like to send a template via the mandrill api. Sending a message works fine.
What do I have to change in my code to send a template?
In the mandrill documentation I see that I can call a template I have stored in my account with this
"template_name": "example template_name",
But I don't know how to integrate this properly in my code below.
I would appreciate any help you can give. For the purpose of understanding the easiest would be if you can show me how the code would have to look in order to send the template.
function log(obj) {
$('#response').text(JSON.stringify(obj));
}
var m = new mandrill.Mandrill('API Key');
var params = {
"message": {
"from_email":"example#domain.com",
"from_name": "FromExampleName",
"to":[{"email":"recipient1#domain.com", "name": "Name of Recipient"}],
"subject": "Mandrill API Test",
"html": "Sending a template doesn't work."
}
};
function sendTheMail() {
m.messages.send(params, function(res) {
log(res);
}, function(err) {
log(err);
});
}
it's solved.
The template has to be included like this
var params = {
"template_name": "templatename",
"template_content": [
{
"name": "example name",
"content": "example content"
}
],
"message": {
"from_email":"example#domain.com",
"to":[{"email":"recipient#domain.com}],
"subject": "Subject line",
"text": "text in the message"
}
};
Then send like this
function sendTheMail() {
// Send the email!
m.messages.sendTemplate(params, function(res) {
log(res);
}, function(err) {
log(err);
});
}

How to do complex conditionals or regex in jade

I'm having an interesting challenge and can't figure out how to go about solving it. I'm pulling a list of subscribers from my mailchimp and populating a members' page on my website with the data. One of the field in my sign up for says, "Website or LinkedIn profile url."
What I'd like to do is, check if they have provided a linkedin address, and if so drop their url in the linkedin member plugin, if not use the other data to create a simpler profile, name website, headline etc.
My problem is I can't say if data.websiteField.match(/linkedin/) within jade, so I either need to pass the data to some client side javascript which I'm having trouble with or do something else.
Here's a sample of data returned from mailchimp
[ // this first bit is just the fields
[
"email address",
"first name",
"last name",
"role",
"organization",
"headline",
"website",
"areas of interest",
"please email me about:",
"member_rating",
"optin_time",
"optin_ip",
"confirm_time",
"confirm_ip",
"latitude",
"longitude",
"gmtoff",
"dstoff",
"timezone",
"cc",
"region",
"last_changed"
],
[ // and here's the first user
"woop#booomshakala.com", // haha, just hiding some data
"Woop",
"Shak",
"Tester",
"ShakaCo",
"Whooooooper",
"http://linkedin.com/in/costamichailidis", // hit me up sometime
"Creativity, Innovation, Ideas and Science",
"Job Placement, Internships, Recruitment & Retention, Technology & MOOCs, Measurement & Evaluation, Documentation & Dissemination",
2,
"2013-03-28 19:28:55",
"173.52.122.111",
"2013-03-28 19:29:12",
"173.52.122.111",
"40.7648000",
"-73.7775000",
"-5",
"-4",
"America/New_York", "US", "NY", "2013-03-28 19:29:12"
]
]
Any help would rock!
Also, I'm using express. Does express make locals available in client side javascript?
Client Side Regex
Jade allows you execute arbitrary javascript using the - modifier at the start of the line
- if (profile.websiteField.match(/linkedin/)
// render linkedin profile here
- else
// render simple profile here
Server Side Regex
I think it would be simpler to format the profile information server-side and set a renderLinkedIn boolean field to true or false
function displaySignUpPage(req, res) {
var profile = formatMailChimpData()
// profile now looks like
// {
// "email address": "woop#booomshakala.com",
// "first name": "Noah",
// ...
// }
var linkedInRegex = /linkedin/;
profile.renderLinkedIn = linkedInRegex.test(profile.website) // set renderLinkedIn to true or false
// say your jade view is called signUpPage.jade
var pageData = {
title: 'Register',
profile: profile
}
res.render('signUpPage', pageData)
}
function formatMailChimpData() {
var mailChimpData = [
[
"email address",
"first name",
"last name",
"role",
"organization",
"headline",
"website"
// other fields truncated
],
[
"woop#booomshakala.com", // haha, just hiding some data
"Noah",
"Isaacson",
"Tester",
"ShakaCo",
"Whooooooper",
"http://www.linkedin.com/pub/noah-isaacson/59/6a2/553"
]
]
// mailChimp puts the keys as the first entry
var mailChimpFields = mailChimpData[0]
var mailChimpProfile = mailChimpData[1]
// make profile into key-value pairs
var keyValuePairs = mailChimpFields.map(function (field, index) {
var profileValue = mailChimpProfile[index]
var keyValuePair = [field, profileValue]
return keyValuePair
})
var profile = keyValuePairs.reduce(function(prev, pair) {
var key = pair[0]
var value = pair[1]
prev[key] = value
return prev
}, {})
return profile
}

Categories