Send message depending on length of array - javascript

I have a problem where I have example document in mongo atlas database:
{
"_id": {
"$oid": "5e517a946364cc48f0ccf1e7"
},
"firstName": "checkout1",
"lastName": "",
"companyName": "",
"phoneNumber": null,
"email": "exampleemail#gmail.com",
"country": "",
"adress": "",
"postcode": "",
"userId": "5daf414818d091616a0d917e",
"orderedItems": [{
"_id": "5e03b2072e0c98b9fa62388c",
"id": 3,
"title": "Blue shoes",
"img1": "product4/1.jpg",
"img2": "product4/2.jpg",
"cost": 70,
"color": "blue",
"quantity": 5
}],
"createdAt": {
"$date": "2020-02-22T19:01:40.228Z"
},
"updatedAt": {
"$date": "2020-02-22T19:01:40.228Z"
},
"__v": 0
}
I want to send a message with confirmation about purchased items and their quantity as shown below:
...
const {
...
email,
orderedItems
} = req.body;
var user = await User.findOne({ email: email });
let newCheckout = await Checkout.create({
...
email,
...
orderedItems,
userId: user._id
});
const htmlEmail = `
<div>Title of first ordered item: ${newCheckout.orderedItems[0].title}</div>
`;
const mailOptions = {
from: process.env.MY_TEST_EMAIL_ADRESS,
to: process.env.MY_EMAIL_ADRESS,
subject: 'new message',
replyTo: process.env.MY_EMAIL_ADRESS,
text: process.env.MY_TEST_EMAIL_ADRESS,
html: htmlEmail
};
transporter.sendMail(mailOptions, (err, info) => {});
...
I need this part of code:
const htmlEmail = `
<div>Title of first ordered item: ${newCheckout.orderedItems[0].title}</div>
`;
To map it similar to like in React where I can map the orderedItems array to div elements so in the end, the outcome message would look something like this (user would get all ordered item titles, and the number of div elements would depend on length of array):
<div>Item: ${newCheckout.orderedItems[0].title}</div>
<div>Item: ${newCheckout.orderedItems[1].title}</div>
<div>Item: ${newCheckout.orderedItems[2].title}</div>
My main question would be is it possible to do without template engines such as Jade, Pug, Mustache?

Template engines are not a requirement, just iterate by orderedItems:
const htmlMail = newCheckout.orderedItems.map(i => `<div>Item: ${i.title}</div>`).join('')

Yes.
orderedItems.map(item => `<div>Item: ${item.title}</div>`).join()
When you find yourself coming back to StackOverflow to ask about the next step in building your own custom rendering engine, take a minute to look at Knockout.
It will save you time in the long run, because you are going to want to make more and more customisations, and soon you have spent more time making a hack than you would have making it with a renderer.
Knockout is easy and you send a JSON object and it gets rendered on the client, and you can make customisations to the layout easily and quickly. You'll love it.

Related

Mongo db update a nested array with many elements in a nested array

I have a db that looks like this :
{
"_id": "637c648fb8fcfb2bc3071bb9",
"consultant_name": "Sam smith",
"consultantUsername": "sam",
"consultant_Password": "123",
"type": "consultant",
"clients": [
{
"client_name": "john",
"client_Username": "mouh",
"client_Password": "123",
"type": "client",
"documents": [
{
"name": "Passeport",
"description": "copie conforme certifié de tout le passeport",
"doc_upload": "Capture dâeÌcran, le 2022-11-28 aÌ 23.01.12.png",
"_id": "637c648fb8fcfb2bc3071bbb"
},
{
"name": "Acte de naissance",
"description": "Pour prouver la filiation",
"doc_upload": "_Abstract Aesthetic CD Album Cover Art.png 637c648fb8fcfb2bc3071bbc",
"_id": "637c648fb8fcfb2bc3071bbc"
}
],
"_id": "637c648fb8fcfb2bc3071bba"
},
As you can see, Sam smith has different clients and each of those clients have different documents. My goal is to be able to allow Sam to add one more client to his portfolio AND specify the documents (as many as he wants) of that new user created all at once (when Sam creates a new user in the db). The process I used is to update his client list by creating an new client.
Here’s the code to add a client (that works), please note that the document part doesn’t get updated :
router.put("/:id", async(req,res)=>{
try {
const updateUser = await User.findByIdAndUpdate(req.params.id, {
$push: {
clients:{
client_name: req.body.client_name,
client_Username: req.body.client_Username,
client_Password: req.body.client_Password,
//documents not updating even with one single entry
documents : [
{
name : req.body.docName,
description : req.body.docDescription,
doc_upload : req.body.doc_upload,
}
]
}
}
},{new:true});
res.status(200).json(updateUser);
}
catch(err) {
res.status(500).json(err);
}
});
So my instinct here would be to push documents using the $each operator, but because the document update doesn't work I'm kind of stuck. In an ideal world if you have the answer/reflexion to be able to update the document part with multiple values it would be appreciated. Any idea on what to do or where should I look first ?

Put items in DynamoDB without knowing the attributes

This feels like a really stupid question, but my lack of JS knowledge combined with lack of AWS knowledge has me in a tight spot! I'm just trying to get to grips with a basic AWS stack i.e. Lambda/Dynamo/API Gateway for some basic API work.
If I want a simple API endpoint to handle PUT requests e.g. https://my.endpoint.amazonaws.com/users. If I have a DynamoDB table with a composite primary key of userID and timestamp I could use the code snippet below to take the unknown data (attributes that weren't known when setting a schema), but this obviously doesn't work well
const dynamo = new AWS.DynamoDB.DocumentClient();
let requestJSON = JSON.parse(event.body);
await dynamo
.put({
TableName: "myDynamoTable",
Item: {
userID: requestJSON.userID,
timestamp: requestJSON.timestamp,
data: requestJSON.data
}
})
.promise();
I could send a PUT request with the following JSON
{
"userID": "aaaa1111",
"timestamp": 1649677057,
"data": {
"address": "Elm Street",
"name": "Glen"
}
}
but then address and name get shoved into a single DynamoDB attribute named data. How do I construct the node code to create a new attribute named address and one named name with the corresponding values, if I didn't know these attributes i.e. I want to use JSON in my request like below, but assuming I don't know this and can't use requestJSON.address
{
"userID": "aaaa1111",
"timestamp": 1649677057,
"address": "Elm Street",
"name": "Glen"
}
You can use the spread operator, for example:
const data = {
address: "Elm Street",
name: "Glen",
};
const item = {
userID: "aaaa1111",
timestamp: 1649677057,
...data,
};
console.log("item:", item);
If you insist on the API contract as
{
"userID": "aaaa1111",
"timestamp": 1649677057,
"data": {
"address": "Elm Street",
"name": "Glen"
}
}
then you can do mapping on the TypeScript level
const request: {userID: string, timestamp: number, data: any} = {
"userID": "aaaa1111",
"timestamp": 1649677057,
"data": {
"address": "Elm Street",
"name": "Glen"
}
};
const ddbObject: any = {
"userID": request.userID,
"timestamp": request.timestamp
};
Object
.keys(request.data)
.forEach(key => ddbObject[key] = request.data[key]);
Representation of ddbObject is
{
"userID": "aaaa1111",
"timestamp": 1649677057,
"address": "Elm Street",
"name": "Glen"
}

Sendgrid API: How to send bulk emails with individual template data?

Requested behaviour:
I would like to send out bulk emails using Sendgrid and Firestore Cloud functions together. The email should display some individual user data depending on the recipient (e.g. user id and user name).
Current State
I created a working cloud function. It gets the user data from firestore and sends out emails by using the sendgrid API to the given email addresses.
Issue
However, it sends the ids of all users to every email address, instead of only sending the id belonging to a certain subscriber. Example:
There are 3 subscribers with 3 ids in my firestore collection:
"abc", "pqr", "xyz"
The function should deliver three emails including the id belonging to the email adress. Instead, the function sends "abcpqrxyz" to every address right now.
My cloud function:
export const sendAllEmails = functions.https.onCall(async (event) => {
const subscriberSnapshots = await admin.firestore().collection('subscribers').get();
const emails = subscriberSnapshots.docs.map(snap => snap.data().email);
const ids = subscriberSnapshots.docs.map(snap => snap.id);
const individualMail = {
to: emails,
from: senderEmail,
templateId: TEMPLATE_ID,
dynamic_template_data: {
subject: event.subject,
text: event.text,
id: ids // sends all ids to everyone instead of a single id in every mail
}
await sendGridMail.send(individualMail);
return {success: true};
});
Do I need to loop over emails and IDs or does the SendGrid API have a smarter implementation for this behaviour?
You have to read over each one of these elements to get the value and construct the object.
Maybe the parameters are not being included correctly in the body of the request. Please note that the personalizations object contains the data provided for each one of these mails.
https://sendgrid.com/docs/ui/sending-email/how-to-send-an-email-with-dynamic-transactional-templates/#send-a-transactional-email
var data = JSON.stringify({
"personalizations": [
{
"to": [
{
"email": "john.doe#example.com",
"name": "John Doe"
}
],
"dynamic_template_data": {
"verb": "",
"adjective": "",
"noun": "",
"currentDayofWeek": ""
},
"subject": "Hello, World!"
}
],
"from": {
"email": "noreply#johndoe.com",
"name": "John Doe"
},
"reply_to": {
"email": "noreply#johndoe.com",
"name": "John Doe"
},
"template_id": "<<YOUR_TEMPLATE_ID>>"
});
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.addEventListener("readystatechange", function () {
if (this.readyState === this.DONE) {
console.log(this.responseText);
}
});
xhr.open("POST", "https://api.sendgrid.com/v3/mail/send");
xhr.setRequestHeader("authorization", "Bearer <<YOUR_API_KEY_HERE>>");
xhr.setRequestHeader("content-type", "application/json");
xhr.send(data);
You need to make multiple "personalizations" objects within the data. I'm not sure what the limitations are with the maximum amount of emails in one request. You might need to research that and check. However I tested with the following json data, and it worked for me:
let data = JSON.stringify({
"from": {
"email": senderEmail
},
"personalizations": [
{ //first individual starts here
"to": [
{
"email": "john.doe.1#example.com"
}
],
"dynamic_template_data": {
"subject": "The subject",
"text": "Event Text goes here",
"id": "abc" // the individual id you need goes here
}
},
{ //next individual starts here
"to": [
{
"email": "john.doe.2#example.com"
}
],
"dynamic_template_data": {
"subject": "The subject",
"text": "Event Text goes here",
"id": "pqr" // the individual id you need goes here
}
} //end - but one can add more indivuals
],
"template_id": "[your_template_id]" //add your template id here
});

How to create a query using typeorm query builder to filder data from the database?

I am just new to typeorm , I am using nodejs/nestjs , I am trying to create a query where in data from the database which is the course could be filter by instructor which is the firstname and lastname , filter by course_name and filter courses by subject. I tried using 'where' and 'orWhere' but cant seem to wrap things up, Anyone can give an advice of a better implementation ? Thank you. Much appreciated.
The query below is working , I am having trouble with the "where" condition
Code
async findAll(options: IPaginationOptions, query): Promise<Pagination<CourseEntity>> {
console.log('query :', query);
const courses = await getRepository(CourseEntity)
.createQueryBuilder('course')
.leftJoinAndSelect('course.user', 'user')
.leftJoinAndSelect('course.subject', 'subject')
// eslint-disable-next-line #typescript-eslint/camelcase
.where('course.course_name = :course_name', { course_name: query.course_name });
return paginate<CourseEntity>(courses, options);
}
here is the query param
query : { limit: '10', firstname: 'mark', lastname: 'gunn' , course_name: 'Comscie'}
Sample data from the database
{
"id": 4,
"course_name": "BS-IT",
"description": "BS-IT DBMS",
"created": "2020-03-19T16:40:46.000Z",
"updated": "2020-03-19T16:40:46.000Z",
"user": {
"id": 20,
"firstname": "Mark",
"lastname": "Gunn",
"role": "Instructor",
"email": "mark#gmail.com",
"isActive": false,
"created": "2020-03-19T16:06:21.000Z",
"updated": "2020-03-19T16:06:34.000Z"
},
"subject": {
"id": 2,
"subject_name": "IT 100",
"description": "Fundamandetals",
"created": "2020-03-18T03:58:34.000Z",
"updated": "2020-03-18T03:58:34.000Z"
}
}
It looks good. Just add other conditions with andWhere
For my project with Typeorm, I prefer use the Find Option Object (https://typeorm.io/#/find-options), because it's easier to generate request from an object but you can't do some advanced join (but in your case it's totally ok)
They are not better implementation just multiple solutions.

Return the first element in a nested array in using mongodb driver for node.js

I'm using the mongodb driver for node.js to query my MongoDB document store.
I have the following data in a document store named companies:
{
companyName: "My Company",
users: [
{
first: "Nick",
last: "Kewney",
email: test#user.com,
username: "this.username",
company: "Test Company",
}
],
_id: ObjectId("54a0831fcad79dbf082d65e0")
}
I want to query the store and find any users with the username 'this.username' and return the first.
My attempt below returns the single result but as an array.
db.companies.findOne({ "users.username": "this.username" }, {_id: 0, 'users.$': 1}, next);
Returns...
{
"users": [
{
"first": "Nick",
"last": "Kewney",
"email": "test#test.com",
"username": "this.username",
"company": "Test Company"
}
]
}
My desired result is only the first item, e.g.
{
"first": "Nick",
"last": "Kewney",
"email": "test#test.com",
"username": "this.username",
"company": "Test Company"
}
There are limitations to what can be done with the basic projection operations available to the .find() method of MongoDB. ,findOne() essentially just wraps .find() to return a single document and not a cursor. But these are basically the same.
If you want first manipulation in the server returned result, you need the .aggregate() method. This has a more detailed $project stage than can do further manipulation, along with other stage operators to get the result:
db.companies.aggregate(
[
// Query argument to match document
{ "$match": {
"users.username": "this.username"
}},
// Flatten array out
{ "$unwind": "$users" },
// Filter array
{ "$match": {
"users.username": "this.username"
}},
// Project wanted fields
{ "$project": {
"_id": 0,
"first": "$users.first",
"last": "$users.last",
"username": "$users.username",
"email": "$users.email",
"company": "$users.company"
}}
],
function(err,result) {
}
);
That will give you the result with a restructured document, and also return possible matches for multiple array items as separate results if you needed to.
But for my money, generally just live with what you get from the basic projection. Sure it's nested and also still an array with only 1 item in it, but this is easy to code to manipulate your response. Especially if you truly are using .findOne() in your intended code, as one result is not hard to manipulate there.

Categories

Resources