How to use transaction by sequelize in my node js - sql

While converting legacy long sql procedure to sequelizer, I met trouble to make transaction to my async functions.
I read sequelizer's transaction documents. But failed to understand clearly.
Here is my code.
const models = require('../models/models.js');
const sequelize = models.Sequelize;
async function isExistFeeHist(dansokSeqNo) {
log.debug("isExistFeeHist()");
let feeHist = await models.FeeHist.findOne({
where: {
DansokSeqNo: dansokSeqNo,
FeeStatus: {[Op.ne]: null}, //is not null
DelYN: false
}
});
return !!feeHist;
}
async function isPaid(dansokSeqNo) {
...
}
async function getNextDansokHistSerialNo(dansokSeqNo) {
...
}
async function getVBankSeqNo(dansokSeqNo) {
...
}
async function updateVBankList(dansokSeqNo, vBankSeqNo) {
...
}
//check if can cancel
async function checkCancelable(dansokSeqNo) {
log.debug("checkCancelable() ", dansokSeqNo);
if (await isExistFeeHist(dansokSeqNo)) {
let e = {status:400, message: 'already imposed dansokSeqNo ' + dansokSeqNo };
return Promise.reject({status:400, message: e.message });
}
if (await isPaid(dansokSeqNo)) {
let e = {status:400, message: 'already paid dansokSeqNo ' + dansokSeqNo };
return Promise.reject({status:400, message: e.message });
}
return Promise.resolve();
}
....
async function doCancel(dansokSeqNo, cancelCauseCode, histMemo) {
try {
await checkCancelable(dansokSeqNo);
//// <== Here I want to start transaction
let nextDansokSerialNo = await getNextDansokHistSerialNo(dansokSeqNo);
let dansokHist = await insertNewDansokHist(dansokSeqNo, nextDansokSerialNo, cancelCauseCode, histMemo);
await updateDansokHist(dansokSeqNo, cancelCauseCode);
let vBankSeqNo = await getVBankSeqNo(dansokSeqNo);
if (vBankSeqNo > 0) {
await updateVBankList(dansokSeqNo, vBankSeqNo);
let vBankList = await getVBankList(dansokSeqNo);
}
// <== Here I want to commit transaction
} catch (e) {
// <== Here I want to rollback transaction
return Promise.reject({status:e.status, message: e.message });
}
}
exports.cancelDansok = function (req, res) {
res.setHeader("Content-Type", "application/json; charset=utf-8");
...
jwtAcessAuth(accessToken)
.then((decoded) => {
log.info("jwt success, ", decoded);
worker = decoded.loginName;
return doCancel(dansokSeqNo, cancelCauseCode, histMemo);
})
.then(() => {
res.status(200).json({ message: 'cancelDansok success.' });
})
.catch(e => {
return res.status(e.status).json(e);
});
};
My function is assembled with several async functions. And it need to bind one transaction.
What is the best practice to use transaction in my several async await functions?

Here is the best example provided by Sequlize for Transaction :
All you need to care is pass transaction to next level of chaining
return sequelize.transaction(function (t) {
// chain all your queries here. make sure you return them.
return User.create({
firstName: 'Abraham',
lastName: 'Lincoln'
}, {transaction: t}).then(function (user) {
return user.setShooter({
firstName: 'John',
lastName: 'Boothe'
}, {transaction: t});
});
}).then(function (result) {
// Transaction has been committed
// result is whatever the result of the promise chain returned to the transaction callback
}).catch(function (err) {
// Transaction has been rolled back
// err is whatever rejected the promise chain returned to the transaction callback
});
For more details : Transactions

Related

Unhandled Promise Rejection when trying to call external function from async function

The error message:
WARN Possible Unhandled Promise Rejection (id: 1):
Error: INVALID_STATE_ERR
send#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:31745:26
initialiseWebsocket#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:100544:21
loadUserData$#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:100610:40
tryCatch#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:7739:23
invoke#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:7912:32
tryCatch#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:7739:23
invoke#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:7812:30
http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:7822:21
tryCallOne#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:28596:16
http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:28697:27
_callTimer#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:29113:17
_callImmediatesPass#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:29152:17
callImmediates#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:29370:33
__callImmediates#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:3279:35
http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:3057:34
__guard#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:3262:15
flushedQueue#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:3056:21
flushedQueue#[native code]
invokeCallbackAndReturnFlushedQueue#[native code]
The useEffect that is being accused of being a problem:
React.useEffect(() => {
// Fetch the token from storage then navigate to our appropriate place
const loadUserData = async () => {
let userData;
try {
userData = await retrieveUserData();
} catch (e) {}
if(userData){
dispatch({ type: 'RESTORE_USER_DATA', userData: userData });
getChatData(userData, setChats, dispatch);
if(userData && !websocketInitialised){
console.log('web init called from *load user data*')
setWebsocketInitialised(true)
initialiseWebsocket(userData);
}
}
else{
dispatch({ type: 'RESTORE_USER_DATA_FAILED'});
}
};
loadUserData();
}, []);
The initialliseWebsocket function
function initialiseWebsocket(userData){
console.log('sending websocket initialisation data.');
websocket.send(JSON.stringify({
'action': 'init',
'data' : {'token': userData.token}
}));
}
the useState that is used above
const [websocketInitialised, setWebsocketInitialised] = React.useState(false);
async function getChatData(userData, setChats, dispatch){
console.log("fetching chat data");
// if we fail to download chat data, pull the old one from FS
const loadOldChatData = async () => {
let chats;
try {
chats = await retrieveChats();
} catch (e) {}
if(chats){
setChats(chats);
console.log("loaded cached chat data") ;
}
else{
setChats([]);
}
};
const onSuccess = (response) => {
if(response['chats']){
storeChats(response['chats']);
setChats(response['chats']);
console.log("chat data synced");
}
else{
loadOldChatData();
}
};
const onFailure = (response) => {
loadOldChatData();
};
fetch(Settings.siteUrl + '/messenger/get_chats/', {
method: "GET",
headers: {
"Content-type": "application/json; charset=UTF-8",
"Authorization": "Token " + userData.token
},
})
.then(response => response.json())
.then(response => {onSuccess(response)})
.catch(response => {onFailure(response)})
}
retrieveUseData() is most likely not the problem as this only started occuring after I added the other code.
Am I not supposed to use states like this or am I supposed to use the async key worked on functions? I tried that but I still have the same issue. You can see on the 4 line of the errors it mentions the 'initialiseWebsocket' function. I am guessing that is the route cause. I assume the solution will be some async version of it...
This error tell us that you didn't or forget to handle error from async code.
I refectory your code a bit, Tell me if you got any error message from console.log(error);
React.useEffect(() => {
// Fetch the token from storage then navigate to our appropriate place
(async () => {
try {
let userData = await retrieveUserData();
dispatch({ type: 'RESTORE_USER_DATA', userData });
await getChatData(userData, setChats, dispatch);
if (websocketInitialised) return;
console.log('web init called from *load user data*')
setWebsocketInitialised(true)
initialiseWebsocket(userData);
} catch (error) {
console.log(error);
dispatch({ type: 'RESTORE_USER_DATA_FAILED' });
}
})();
}, []);
And you should rename getChatData to setChatData, I also simplify those code also...
async function getChatData(userData, setChats, _dispatch) {
try {
let response = await fetch(Settings.siteUrl + '/messenger/get_chats/', {
method: "GET",
headers: {
"Content-type": "application/json; charset=UTF-8",
"Authorization": "Token " + userData.token
},
}),
data = await response.json(),
chats = data['chats'];
if (!chats?.length) throw "empty chat data, pull the old one from FS";
storeChats(chats);
setChats(chats);
} catch (_) {
// if we fail to download chat data, pull the old one from FS
await retrieveChats()
.then(chats => setChats(chats))
.catch(() => setChats([]))
}
}
"I don't really understand what you are doing with the async stuff."
async/await is just syntax sugar of promise, It allow you to work with async operation in a synchronous manner, some rules of async/await
In other to use await keyword, you need an async function.
you can make any function asynchronous, just by adding async keyword
async function always return promise
Lets see an example:
let delay = (ms, msg, bool) => new Promise((res, rej) => setTimeout(!bool ? res : rej , ms,msg));
This helper function create a promise for our example, it take 3 arguments, it take millisecond as 1st arg, to delay, 2rd is the message as payload. 3nd is Boolean; it true, then it will reject.
let delay = (ms, msg, bool) => new Promise((res, rej) => setTimeout(!bool ? res : rej, ms, msg));
let log = console.log;
async function myAsyncFn() {
let hello = await delay(100, "hello,");
let world = await delay(300, " world!");
// we use a symbol '#' to indicate that, its from `myAsyncFn`
log("#" , hello + world, "printed from async operation");
}
myAsyncFn();
log("As you can see that, this message print first");
// we are creating an async function and called immediately, In other to use `await keyword`
(async () => {
try {
let resolved = await delay(300,"resolved");
console.log(">" , `it ${resolved}!`);
// this will reject and catch via `try/catch` block;
let _ = await delay(600, "Error", true);
log("It will not print!");
// ...
} catch (error) {
log(">" , `we can catch "${error}" with try/catch, as like any sync code!`);
}
})()
As you can see that with async/await its look like everything is synchronous right? even everything execute asynchronously!
You just need to use await keyword to make every async operation synchronous.

Query works but cant retrieve the data

I am new to Node.js (3 days total experience). I am using Node.js and the tedious package to query a database (azure SQL). I use the example as explained here: https://learn.microsoft.com/en-us/azure/azure-sql/database/connect-query-nodejs?tabs=macos
const connection = new Connection(config);
// Attempt to connect and execute queries if connection goes through
connection.on("connect", err => {
if (err) {
console.error(err.message);
} else {
console.log("Reading rows from the Table...");
// Read all rows from table
const request = new Request(
"SELECT * FROM clients",
(err, rowCount, columns) => {
if (err) {
console.error(err.message);
} else {
console.log(`${rowCount} row(s) returned`);
}
}
);
request.on("row", columns => {
columns.forEach(column => {
console.log("%s\t%s", column.metadata.colName, column.value);
});
});
connection.execSql(request);
}
});
I have two issues:
I do not know how to get the queried data into an object and
If I run the script it does print the items to the console, but it doesn't close the connection after it has done so. If I add a connection.close() at the bottom, it will close the connection before its done. I get the feeling that node.js executes everything at the same time (I am used to Python..).
Update
I found a way to close the connection, to my understanding the request object has several "events" that are predefined by the library. It seems I need to add the event "done" through request.on('done', ...) in order to make sure that it can even BE done. My updated code looks like this:
var connection = new Connection(config);
connection.connect(function(err) {
// If no error, then good to go...
executeStatement();
}
);
connection.on('debug', function(text) {
//remove commenting below to get full debugging.
//console.log(text);
}
);
function executeStatement() {
request = new Request("SELECT * FROM clients", function(err, rowCount) {
if (err) {
console.log(err);
} else {
console.log(rowCount + ' rows');
}
connection.close();
});
request.on('row', function(rows) {
_.forEach(rows, function(value, collection){
console.log(value)
console.log(value.value);
console.log(value.metadata.colName)
console.log(collection)
})
});
request.on('done', function(rowCount, more) {
console.log(rowCount + ' rows returned');
});
// In SQL Server 2000 you may need: connection.execSqlBatch(request);
connection.execSql(request);
}
Anyways, your help would be much appreciated!
Regards
Pieter
The package tedious is synchronous package, it uses the callback to return results. So when we call connection.close(), it will disable connection and stop the callback function. If will want to close the connection, I suggest you use async package to implement it.
For example
const { Connection, Request } = require("tedious");
const async = require("async");
const config = {
authentication: {
options: {
userName: "username", // update me
password: "password", // update me
},
type: "default",
},
server: "your_server.database.windows.net", // update me
options: {
database: "your_database", //update me
encrypt: true,
validateBulkLoadParameters: true,
},
};
const connection = new Connection(config);
let results=[]
function queryDatabase(callback) {
console.log("Reading rows from the Table...");
// Read all rows from table
const request = new Request("SELECT * FROM Person", (err, rowCount) => {
if (err) {
callback(err);
} else {
console.log(`${rowCount} row(s) returned`);
callback(null);
}
});
request.on("row", (columns) => {
let result={}
columns.forEach((column) => {
result[column.metadata.colName]=column.value
console.log("%s\t%s", column.metadata.colName, column.value);
});
// save result into an array
results.push(result)
});
connection.execSql(request);
}
function Complete(err, result) {
if (err) {
callback(err);
} else {
connection.close();
console.log("close connection");
}
}
connection.on("connect", function (err) {
if (err) {
console.log(err);
} else {
console.log("Connected");
// Execute all functions in the array serially
async.waterfall([queryDatabase], Complete);
}
});
connection.connect();
Besides, you also can use the package mssql. It supports asynchronous methods and depends on package tedious. We can directly call close after querying.
For example
const mssql = require("mssql");
const config = {
user: "username",
password: "password",
server: "your_server.database.windows.net",
database: "your_database",
options: {
encrypt: true,
enableArithAbort: true,
},
};
let pool = new mssql.ConnectionPool(config);
async function query() {
try {
await pool.connect();
const request = pool.request();
const result = await request.query("SELECT * FROM Person");
console.dir(result.recordset);
await pool.close();
console.log(pool.connected);
} catch (error) {
throw error;
}
}
query().catch((err) => {
throw err;
});
You can custom a class first and declare an Array to save ojects such as:
let sales = new Array();
class SalesLT{
constructor(catagryName,productName){
this.catagryName = catagryName;
this.productName = productName;
}
Here my sql statement returns 2 properties, so every time the loop takes out two elements from the ColumnValue[].
request.on("row", columns => {
for(let i=0; i<columns.length; i=i+2){
let sale = new SalesLT(columns[i].value,columns[i+1].value);
sales.push(sale);
}
sales.forEach( item => {
console.log("%s\t%s",item.catagryName, item.productName)
})
});
The code is as follows:
const { Connection, Request } = require("tedious");
let sales = new Array();
class SalesLT{
constructor(catagryName,productName){
this.catagryName = catagryName;
this.productName = productName;
}
}
// Create connection to database
const config = {
authentication: {
options: {
userName: "<***>", // update me
password: "<***>" // update me
},
type: "default"
},
server: "<****>.database.windows.net", // update me
options: {
database: "<***>", //update me
encrypt: true
}
};
const connection = new Connection(config);
// Attempt to connect and execute queries if connection goes through
connection.on ("connect", err => {
if (err) {
console.error(err.message);
} else {
queryDatabase();
}
});
function queryDatabase() {
console.log("Reading rows from the Table...");
// Read all rows from table
const request = new Request(
`SELECT TOP 2 pc.Name as CategoryName,
p.name as ProductName
FROM [SalesLT].[ProductCategory] pc
JOIN [SalesLT].[Product] p ON pc.productcategoryid = p.productcategoryid`,
(err, rowCount) => {
if (err) {
console.error(err.message);
} else {
console.log(`${rowCount} row(s) returned`);
}
connection.close();
}
);
request.on("row", columns => {
for(let i=0; i<columns.length; i=i+2){
let sale = new SalesLT(columns[i].value,columns[i+1].value);
sales.push(sale);
}
sales.forEach( item => {
console.log("%s\t%s",item.catagryName, item.productName)
})
});
connection.execSql(request);
}
this article should help you, to solve all the issues you are facing...which were the same I had when I started using Node :)
https://devblogs.microsoft.com/azure-sql/promises-node-tedious-azure-sql-oh-my/

How I can test in jest line with throw e?

How I can test in jest error case?
This is what I do:
I don't know if exist a method how to test this.
it ('the fetch fails and throw an error', async () => {
let response = {
status: 400,
body:
{
base : "RON",
date: "2019-08-01",
rates: {"error": 'error'}
}
};
fetch.mockReject(response)
try {
await fetchData();
} catch (e) {
expect(e).toEqual(response);
expect(await fetchData()).rejects.toThrow(e);
}
});
This is the code:
fetchData = async () => {
try {
const response = await fetch('https://api.exo/latest?base=RON');
const data = await response.json();
return data;
} catch (e) {
throw e;
}
};
expect.assertions to the rescue
it ('the fetch fails and throw an error', async () => {
expect.assertions(1);
let response = {
status: 400,
body: {
base : "RON",
date: "2019-08-01",
rates: {"error": 'error'}
}
};
fetch.mockReject(response)
try {
await fetchData();
} catch (e) {
expect(e).toEqual(response);
}
});
Test will fail once no exception is thrown. It has advantages over expect().toThrown:
you don't have to return Promise in your it() to make it work
it's easier to assert several related exceptions or sequential actions failed
it's easier to run partial matching over error caught(say with expect(e).toMatchObject({}) to skip some data you don't care about in current test case)
As for disadvantages - you have to update number manually after adding new assertions
You can do it in the following way:
async function throws () {
throw new Error('error')
}
test('promise throws', async () => {
await expect(throws()).rejects.toThrow()
})
test('the fetch fails with an error', async () => {
await expect(throws()).rejects.toThrow('error');
});
test('the fetch fails with an error', () => {
return expect(throws()).rejects.toMatch('error');
});
Read more docs.

passing Tedious connection as parameter

I am trying to use a simple suite of functions built utilizing the Tedious library to access a Microsoft SQL Server. Here is my "tools" file:
'use strict';
const tedious = require('tedious');
const q = require('q');
var Connection = tedious.Connection;
var Request = tedious.Request;
module.exports = {
connectSQL : function(config) {
var connection = new Connection(config);
connection.on('connect', function(err) {
if (err) {
console.log('FAIL ON CONNECT');
console.log(err);
} else {
try {
/* ----- */
return connection;
} catch (err) {
console.log(err);
return;
}
}
});
connection.on('error', function(err) {
if (err) {
console.log('FAIL ON ERROR');
console.log(err);
} else {
console.log("Error called with no err object.");
}
});
},
executeSQL: function(connection, requestString) {
var results = [];
var request = new Request( requestString , function(err, data) {
if (err) {
console.log(err);
} else {
console.log( data );
}
});
request.on('row', function(row) {
//console.log(row);
results.push( row );
});
request.on('requestCompleted', function(){
console.log('Finished');
return results;
});
connection.execSql(request);
}
}
I call these functions as follows in my server file.
const sqlTools = require('./sqlTools.js');
var connection = sqlTools.connectSQL(config);
sqlTools.executeSQL(connection, "select * from dbo.test");
However, I get the error "TypeError: Cannot read property 'execSql' of undefined", even if I make the program sleep for 10 seconds before calling my function sqlTools.executeSQL (obviously not ideal).
I was able to get this to work by calling the request within the sqlTools.connectSQL function (at the "/* ----- */"), but I want to re-use the Tedious connection to make multiple calls. Any suggestions? Thanks!
~~~~~~~EDIT~~~~~~~~~~
With help from akinjide I was able to implement callbacks that allow me to make a single call to my SQL database. However, I am struggling to implement promises to make subsequent calls. I changed my "tools" file as such:
'use strict';
const tedious = require('tedious');
const q = require('q');
var Connection = tedious.Connection;
var Request = tedious.Request;
module.exports = {
connectSQL: function(config) {
var deferred = q.defer();
var connection = new Connection(config);
connection.on('connect', function(err) {
if (err) {
deferred.reject( err );
} else {
deferred.resolve( connection );
}
});
connection.on('error', function(err) {
deferred.reject(err);
});
return deferred.promise;
},
executeSQL: function(connection, requestString, callback) {
var results = [];
const request = new Request(requestString, function(err) {
callback(err);
});
request.on('row', function(row) {
results.push(row);
});
request.on('requestCompleted', function() {
console.log('request completed!');
callback(null, results);
});
connection.execSql(request);
}
}
and I call this code like this...
var promise = sqlTools.connectSQL(config);
promise.then(function (connection) {
sqlTools.executeSQL(connection, "select * from dbo.test", function(err, results) {
if (err) {
console.log(err);
}
console.log(results);
});
}).catch(function (err) {
console.log(err);
}).then(function (connection) {
sqlTools.executeSQL(connection, "select * from dbo.test2", function(err, results) {
if (err) {
console.log(err);
}
console.log(results);
});
}).catch(function(err) {
console.log(err);
});
This returns the first call's results correctly, but unfortunately returns this error "TypeError: Cannot read property 'execSql' of undefined" for the second call as it is not recognizing the connection the second time around. Any suggestions?
A better approach would be to pass a node.js callback style function as an argument to connectSQL.
return keyword won't work within an asynchronous program.
'use strict';
const tedious = require('tedious');
const Connection = tedious.Connection;
const Request = tedious.Request;
module.exports = {
connectSQL: function(config, callback) {
const connection = new Connection(config);
connection.on('connect', function(err) {
if (err) {
callback(err);
} else {
callback(null, connection);
}
});
connection.on('error', function (err) {
callback(err);
});
},
executeSQL: function(connection, requestString, callback) {
let results = [];
const request = new Request(requestString, function(err) {
callback(err);
});
request.on('row', function(row) {
results.push(row);
});
request.on('requestCompleted', function(){
console.log('Finished');
callback(null, results);
});
connection.execSql(request);
}
}
Then you can require, use sqlTools.connectSQL passing two parameters config and function(err, connection) {}
const sqlTools = require('./sqlTools');
sqlTools.connectSQL(config, function(err, connection) {
if (err) {
console.log('FAIL ON CONNECT');
console.log(err);
}
sqlTools.executeSQL(connection, "select * from dbo.test", function (err, results) {
if (err) {
console.log(err);
}
console.log(results);
});
});

How to get socket.io to recognize when model has been updated

I'm relatively new to MEAN/Mongoose/socket.io and am missing something that seems like it'd be basic, but regardless, it's keeping me from moving forward.
I'm keeping track of a group's meetings and have the following Schema:
'use strict';
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var ScheduleSchema = new Schema({
description: String,
meetingPlace: String,
office: String,
start: { type: Date, default: Date.now },
end: { type: Date, default: Date.now },
participants: [{
type: Schema.Types.ObjectId,
ref: 'User'
}],
author: {
type: Schema.Types.ObjectId,
ref: 'User'
},
timestamp: { type: Date, default: Date.now },
active: Boolean
});
ScheduleSchema.statics = {
load: function(cb) {
this.find({})
.populate({path:'participants'})
.exec(cb);
}
};
module.exports = mongoose.model('Schedule', ScheduleSchema);
When I create a new item in the Schedule model, this code sets it in motion (from a client-side controller):
$http.post('/api/schedules', { description: info.description, participants: attendees, meetingPlace: info.meetingPlace, office: info.office, start: info.start, end: info.end, timestamp: new Date(), active: true });
And because of this code:
'use strict';
var express = require('express');
var controller = require('./schedule.controller');
var router = express.Router();
router.get('/', controller.index);
router.get('/:id', controller.show);
router.post('/', controller.create);
router.put('/:id', controller.update);
router.patch('/:id', controller.update);
router.delete('/:id', controller.destroy);
module.exports = router;
I think the request is being routed to controller.create, which is the following:
'use strict';
var _ = require('lodash');
var Schedule = require('./schedule.model');
// Get list of schedules
exports.index = function(req, res) {
Schedule.load(function (err, schedules) { /*.find*/
if(err) { return handleError(res, err); }
return res.json(200, schedules);
});
};
// Get a single schedule
exports.show = function(req, res) {
Schedule.findById(req.params.id, function (err, schedule) {
if(err) { return handleError(res, err); }
if(!schedule) { return res.send(404); }
return res.json(schedule);
});
};
// Creates a new schedule in the DB.
exports.create = function(req, res) {
var promise = Schedule.create(req.body, function(err, schedule) {
if(err) { return handleError(res, err); }
return res.json(201, schedule);
});
};
// Updates an existing schedule in the DB.
exports.update = function(req, res){
var updatedMeeting = req.body;
var id = updatedMeeting._id;
delete updatedMeeting._id;
Schedule.update({_id : id}, updatedMeeting, { }, function (err, numberAffected, raw) {
if (err) return res.json(500, err);
updatedMeeting._id = id;
return res.json(201, updatedMeeting);
});
};
// Deletes a schedule from the DB.
exports.destroy = function(req, res) {
Schedule.findById(req.params.id, function (err, schedule) {
if(err) { return handleError(res, err); }
if(!schedule) { return res.send(404); }
schedule.remove(function(err) {
if(err) { return handleError(res, err); }
return res.send(204);
});
});
};
function handleError(res, err) {
return res.send(500, err);
}
After exports.create is run, and I don't know how, but something sends it over to here:
'use strict';
var Schedule = require('./schedule.model');
exports.register = function(socket) {
Schedule.schema.post('save', function (doc) {
onSave(socket, doc);
});
Schedule.schema.post('remove', function (doc) {
onRemove(socket, doc);
});
Schedule.schema.post('update', function (doc) {
onUpdate(socket, doc);
});
}
function onSave(socket, doc, cb) {
console.log('**********onSave**********');
Schedule
.findOne({ _id : doc._id })
.populate('participants')
.exec(function (err, event) {
if (err) return handleError(err);
socket.emit('schedule:save', event);
});
}
function onRemove(socket, doc, cb) {
socket.emit('schedule:remove', doc);
}
function onUpdate(socket, doc, cb) {
console.log('**********onUpdate**********');
socket.emit('schedule:update', doc);
}
and the line socket.emit('schedule:save', event); is called.
So that's all great, but when I update the Schedule model, I can't get socket to emit 'schedule:update' because it never gets to the "onUpdate" function in the code snippet just above this.
Starting from the client-side call:
$http.patch('/api/schedules/' + info._id, { _id: info._id, description: info.description, participants: attendees, meetingPlace: info.meetingPlace, office: info.office, start: info.start, end: info.end, timestamp: new Date(), active: true });
and the router sends that to exports.update, the model is updated, but socket doesn't emit anything, so all the clients connected see the old schedule information.
How and where can I relay to socket.io that the model has been updated? I think what I'm missing is how Schedule.schema.post('some action... gets called within the exports.register function, but I've been looking at this for a while and could use a hand - thanks.