Mongoose middleware function not returning expected result - node.js

I have created a pre-init middleware function that is meant to format a Date field in my schema.
StadiumSchema.pre('init', function(next, stadium) {
let date = new Date(stadium.built);
let built = date.getDate() + '/' + date.getMonth() + '/' + date.getFullYear();
stadium.built = built;
console.log(stadium);
console.log(built);
next();
});
Within the function, it does show that stadium.built has been changed. But outside of the function that value stadium.built has not changed.

The order of arguments in the callback function is different as far as i remember, like:
StadiumSchema.pre('init', function(stadium, next) {
let date = new Date(stadium.built);
let built = date.getDate() + '/' + date.getMonth() + '/' + date.getFullYear();
stadium.built = built;
console.log(stadium);
console.log(built);
next();
});
btw, if you're not doing anything async job here, so you can also make it sync, like:
StadiumSchema.pre('init', function(stadium) {
let date = new Date(stadium.built);
let built = date.getDate() + '/' + date.getMonth() + '/' + date.getFullYear();
stadium.built = built;
//console.log(stadium);
//console.log(built);
});
However, please refer the docs for more information.

Related

Canva publish extension API : Endpoint never get call

I try to make a Canva App with a publish extension.
I just follow the Quick start (https://docs.developer.canva.com/apps/extensions/publish-extensions/quick-start) with Glitch and it work well on it.
But when I try to put in on my own public host name, with a other port (like http://mydomaine.com:3000) Canva NEVER call my endpoint. I just write a log file of every action on my app post and I never get a update on it, and when I try the app on Canva.com it just show me a error message.
//Copy from the Quick Start
app.post('/publish/resources/upload', async (request, response) => {
try{
writeLog("Uploading file");
await fs.ensureDir(path.join(__dirname, 'export'));
// Get the first asset from the "assets" array
const [asset] = request.body.assets;
// Download the asset
const image = await jimp.read(asset.url);
const filePath = path.join(__dirname, 'export', asset.name);
await image.writeAsync(filePath);
// Respond with the URL of the published design
response.send({
type: 'SUCCESS',
url: url.format({
protocol: request.protocol,
host: request.get('host'),
pathname: asset.name,
}),
});
} catch (err) {
writeLog("ERROR (app.post('/publish/resources/upload'): " + err);
}
});
//Just log on the log file
function writeLog(log){
// fs.appendFile(path.join(__dirname, '/log/' + `${month}/${date}/${year}` +'log.txt'), dateDisplay + "|" + log + "\n", (err) => {
// if (err) throw err;
// });
var today = new Date();
var time = today.getHours() + ":" + today.getMinutes() + ":" + today.getSeconds();
var date = today.getFullYear() + '-' + (today.getMonth() + 1) + '-' + today.getDate();
var dateTime = date + ' ' + time;
natifFS.appendFile('log.txt', dateTime + '| '+ log + "\n", (err) => {
if (err) throw err;
});
}
Last thing, when I try to call a post request on the same endpoint as Canva (/publish/resources/upload) with Postman, I get a update on my log.txt file
If anyone has idea, thank you.

MongoDB aggregation returns empty array with NodeJS

I want to create a script which is taking the average of the Volume for last 7(for example) days.
I'm stuck with aggregation stages since first stage I need to take Date for last 7 days and in second stage calculate Average of Volume
Package list:
Node-schedule - */1 * * * * (Runs the script every minute)
Binance API - Taking data from them.
Screenshot for showcasing how the document looks like in MongoDB.
Aggregation part of the Code.
const average = await dbo.collection(symbol).aggregate([{
'$match': {
'Date': { '$gte': new Date((new Date().getTime() - (7 * 24 * 60 * 60 * 1000))) }
},
},
{
'$group': {
_id: null,
'Volume': { '$avg': '$Volume' }
},
}
]).toArray();
This code returns me an empty array in terminal like this > []
Full Code here.
const { MongoClient } = require('mongodb');
const schedule = require('node-schedule');
const fetch = require("node-fetch");
const symbols = ["ADABTC", "AEBTC", "AIONBTC", "ALGOBTC", "ARDRBTC"];
//a descriptive name helps your future self and others understand code easier
const getBTCData = async symbol => { //make this function accept the current symbol
//async/await lets us write this much nicer and with less nested indents
let data = await fetch(`https://api.binance.com/api/v3/klines?symbol=${symbol}&interval=30m&limit=1`).then(res => res.json());
const btcusdtdata = data.map(d => {
return {
Open: parseFloat(d[1]),
High: parseFloat(d[2]),
Low: parseFloat(d[3]),
Close: parseFloat(d[4]),
Volume: parseFloat(d[5]),
Timespan: 30,
}
});
console.log(btcusdtdata);
saveToDatebase(symbol, btcusdtdata);
//recursive functions are complicated, we can get rid of it here
//by moving the responsibility to the caller
};
//helper function for an awaitable timeout
const sleep = ms => new Promise(res => setTimeout(res, ms));
const j = schedule.scheduleJob('*/1 * * * *', async() => {
//expand this function to be responsible for looping the data
for (let symbol of symbols) {
//we can pass symbol to getBTCData instead of making it
//responsible for figuring out which symbol it should get
await getBTCData(symbol);
await sleep(8000);
}
});
//make this a helper function so `saveToDatabase()` isn't also responsible for it
const getDateTime = () => {
let today = new Date();
let date = today.getFullYear() + '-' + (today.getMonth() + 1) + '-' + today.getDate();
let time = today.getHours() + ":" + today.getMinutes() + ":" + today.getSeconds();
return date + ' ' + time;
};
const saveToDatebase = async(symbol, BTCdata) => {
try {
const url = 'mongodb://username:password#ipadress:port/dbname';
let dateTime = getDateTime();
let db = await MongoClient.connect(url, { useUnifiedTopology: true });
const dbo = db.db('Crypto');
const myobj = Object.assign({ Name: symbol, Date: dateTime }, BTCdata[0]);
await dbo.collection(symbol).insertOne(myobj);
const average = await dbo.collection(symbol).aggregate([{
'$match': {
'Date': { '$gte': new Date((new Date().getTime() - (7 * 24 * 60 * 60 * 1000))) }
},
},
{
'$group': {
_id: null,
'Volume': { '$avg': '$Volume' }
},
}
]).toArray();
console.log('1 document inserted');
console.log(average);
db.close();
} catch (e) {
console.error(e)
}
};
EDIT1
If I delete $match part my script is working and I receive average of Volume.
Screenshot of terminal after success try without $match
EDIT2
According to the last answer I understand that I need to change Date format from string to object, but I really can't get how I can do it in this part?
const getDateTime = () => {
let today = new Date();
let date = today.getFullYear() + '-' + (today.getMonth() + 1) + '-' + today.getDate();
let time = today.getHours() + ":" + today.getMinutes() + ":" + today.getSeconds();
return date + ' ' + time;
};
EDIT3
After editing the Date format I receive a Document in MongoDB in strange Date format like - Date:2020-07-20T13:24:02.390+00:00
Code here:
const getDateTime = () => {
let today = new Date();
let date = today.getFullYear() + '-' + (today.getMonth() + 1) + '-' + today.getDate();
let time = today.getHours() + ":" + today.getMinutes() + ":" + today.getSeconds();
return new Date();
};
The problem is on the Date field format.
The getDateTime function returns a string so Mongo is managing the field as a string not as a Date object so the $gte check will compare string not dates.
You should change the function to getDateTime = () => new Date(). Mongo will manage the date correctly storing in UTF Timezone.
Tring to query a date-string in the $match field would be really difficult.
Edit:
To update the typing just:
const getDateTime = () => {
return new Date();
};

Get the HTML content of a page after the JS loading

I'm trying to get the results of a search in the Rust documentation. I made this code to do it :
let HTMLParser = require('node-html-parser');
let https = require('https');
const search = "foo";
let options = {
host: "doc.rust-lang.org",
path: "/std/index.html?search=" + search
};
let request = https.get(options, (res) => {
if (res.statusCode != 200) return console.log(`An error occured : ${res.statusCode}. Retry later.`);
res.setEncoding("utf8");
let output = "";
res.on("data", (chunk) => {
output += chunk
});
res.on("end", () => {
let root = HTMLParser.parse(output);
console.log(root.querySelector(".search-results")); // print "null" because the search is not done when the request response come
});
request.end();
});
But when I run this code, I get the HTML content of the index.html page like if I requested this page without the ?search="foo". I found that the page change dynamically with some JS when we search for something, and then the base content is set to hidden and the search div become visible. So it seems that the JS didn't load when I get the request result, but I needs it to get the results of the search in the documentation. I don't know how I can do that.
Thank you in advance for your answers !
The Rust doc page does not seem to hit a backend when a search is performed. I noticed this using the browser developer tools.
It looks like the page loads a search-index which contains the readily available docs. You can use this js to search for docs. The logic is written in the main.js.
Let me know if you are looking for more info, as I have not found out how the link generation on each doc item is created.
EDIT
All the logic required to build the url is in main.js. The method is as follows. If you take a close look at the aliases.js, main.js, storage.js and search-index.js files, you can reuse almost all of it to create the links and the required search outputs.
function buildHrefAndPath(item) {
var displayPath;
var href;
var type = itemTypes[item.ty];
var name = item.name;
if (type === 'mod') {
displayPath = item.path + '::';
href = rootPath + item.path.replace(/::/g, '/') + '/' + name + '/index.html'
} else if (type === 'primitive' || type === 'keyword') {
displayPath = '';
href = rootPath + item.path.replace(/::/g, '/') + '/' + type + '.' + name + '.html'
} else if (type === 'externcrate') {
displayPath = '';
href = rootPath + name + '/index.html'
} else if (item.parent !== undefined) {
var myparent = item.parent;
var anchor = '#' + type + '.' + name;
var parentType = itemTypes[myparent.ty];
if (parentType === 'primitive') {
displayPath = myparent.name + '::'
} else {
displayPath = item.path + '::' + myparent.name + '::'
}
href = rootPath + item.path.replace(/::/g, '/') + '/' + parentType + '.' + myparent.name + '.html' + anchor
} else {
displayPath = item.path + '::';
href = rootPath + item.path.replace(/::/g, '/') + '/' + type + '.' + name + '.html'
}
return [displayPath,
href]
}

Adding paths to file names that scanned with nodejs

I have this route in my app. Its scanning file names in some directories according to url parameter. Currently my JSON looks like;
[{"chapter":"642","paths":["00.png","01.png","02.png","03.png","04.png","05.png".....
Route:
app.get('/api/oku/:name', function (req, res) {
var files = fs.readdirSync('./public/manga/' + req.params.name);
var files = files.map(function(item){
var subfolders = fs.readdirSync('./public/manga/' + req.params.name + '/' + item);
return {
chapter: item,
paths: subfolders
}
})
res.json(files);
})
Simply I want to add paths to every image. Path that I want to add is;
"manga/" + req.params.name + "/" + item + "/"
So simply it will be looks like manga/Naruto/542/00.png
Try this:
app.get('/api/oku/:name', function (req, res) {
var files = fs.readdirSync('./public/manga/' + req.params.name).map(function(item){
var subfolders = fs.readdirSync('./public/manga/' + req.params.name + '/' + item);
return {
chapter: item,
paths: subfolders.map(function (i) {
return "manga/" + req.params.name + "/" + i + "/";
}
}
})
res.json(files);
})
Also, don't declare the files variable twice.

Express redirect based off URL

I'm using Express & Node.js.
router.get('/:year?/:month?/:day?', function(req, res) {
var date = moment();
if (req.params.year && req.params.month && req.params.day) {
var dateString = req.params.year + " " + req.params.month + " " + req.params.day;
var tempDate = moment(dateString, "DD MM YYYY");
if (tempDate.isValid()) {
date = tempDate;
}
}
.catch(function (err) {
console.error(err);
res.send("Error");
});
});
The above isn't my full code for my route. But I need some pointers. I'm using Moment.js(http://momentjs.com/) to grab the current date, format it and pass it through the url in the route.get request.
However, I don't want the user to be able to go to a past date. So if the date has already been and the user tries to navigate too it I want to redirect them back to the current date.
.then (function(){
var pastDate = function(req, res) {
if (req.query.[pastDate]) {
res.redirect('/');
} else {}
}
})
I don't think the the syntax is correct. But not sure if i'm looking on the right lines in how I'd do this.
Thanks for help.
I looked through your code and it looks really inconsisten and I don't fully understand what you're trying to do, but you can probably narrow it down to something like this:
router.get('/:year?/:month?/:day?', function (req, res) {
var year = req.params.year;
var month = req.params.month;
var day = req.params.day;
if (year && month && day) {
var requestDate = moment(day + ' ' + month + ' ' + year);
var today = moment();
if (requestDate.isValid() && !requestDate.isBefore(today)) {
// All set and valid, do stuff with the request
} else {
// Invalid date or the requested date is before today
// Redirecting to /year/month/day of this day, e.g /2015/04/31
return res.redirect('/' + today.year() + '/' + today.month() + '/' + today.date());
}
}
});
Moment has an isBefore method that is really handy for these kind of tasks.
You should use a bit more variables in your code so it's easier to work with! :)
You should be able to just do a less than comparison.
var goodDate = function() {
var now = moment();
if (this < now) {
return false
}
return true
}

Resources