1

I'm getting this problem and I can't get out of it. What I'm going to do it's to publish "a post" (json), save it into MongoDB.

exports.create_a_topic = function(req, res) {
return new Promise((resolve,reject) => {

var newTopic = new Topic({
    topic_type: req.body.topic_type,
    properties: req.body.properties[0]
});

newTopic.save()
.then(() => resolve({ status: 201, message: 'Topic published Successfully!' }))
    .catch((err) => {
        if (err.code == 11000) {
            reject({ status: 409, message: 'Topic Already published!' });
        } else {
            reject({ status: 500, message: 'Internal Server Error!' });
        }
    }); 
})};

Then, I'm going to get all the people who subscribed that kind of post. (Any user may have or not a filter on post's metadata)

exports.get_subscribers = function(req, res) { 
return new Promise((resolve,reject) => {
User.find({subscription: req.body.topic_type}, {'email': 1, 'queueName': 1, 'isFiltered': 1})
.then(users => {
        if (users.length == 0) {
            reject({ status: 404, message: 'Users Not Found!' });
        } else {
            //res.subscribers = {};
            res.subscribers = users;
            resolve({ status: 200, message: users });
        }
})
.catch(err => reject({ status: 500, message: 'Internal Server Error!' }));
})};

Finally, from the list of all the subscribers, I'm going to take just the one with a filter to apply it to the post's metadata and see if this user is interested in it. (User schema have email who has a references to Filter schema, so I'm going to do an aggregation between them to see if a user has a filter or not). If no one has a filter, I return the list of the subscribers without removing any, otherwise I'd remove the one not interested in the post). I'd still have to implement the remove part.

exports.apply_filter = function(req, res, subscribers)  {
var email_of_filters = [];

subscribers.forEach(function(value) {
        email_of_filters.push(value.email); 
});
console.log(email_of_filters);

return new Promise((resolve,reject) => {

    User.aggregate([
        {$project: {"email": "$email", "filter": "$filter"}},
        {$match: {isFiltered: {$ne: false}, email: {$in: email_of_filters}}},
        {$unwind: "$email"},
        {$lookup: 
            {from: "filters", localField: "email", foreignField: "email", as: "filter"}
        }
    ])

    .then(users_with_filters => {

        if (users_with_filters.length == 0) {
            resolve({ status: 200, message: subscribers });
        } else {
            reject({ status: 400, message: 'Error!' });
        }
    })
    .catch(err => reject({ status: 500, message: 'Internal server error!' }));
});
};

'get_subscribers' goes well if it is invoked alone. Same for 'create_a_topic'. Problem is encountered when invoking apply_filter.

Unhandled rejection rangerror: invalid status code: 0

I saw the following question on StackOverflow without resolving: 1, 2, 3. Any tip would be appreciated.

Route

app.post('/publish_topic', (req, res) => {
        project.create_a_topic(req, res)
            .then(result1 => {return project.get_subscribers(req, res);})
            .then(result2 => {return project.apply_filter(req, res, res.subscribers);})
            .then(result3 => {res.status(result3.status).json({ message: result3.message });})
            .catch(err => res.status(err.status >= 100 && err.status < 600 ? err.code : 500).send({message: err.message}))          
    });
JackLametta
  • 400
  • 1
  • 2
  • 21

2 Answers2

1

The thing that often gets me out of trouble is smaller, testable, promise-returning units. Taking the first function as example, make it a model function, leaving the the controller manage req and res. Have it just do the model work and return the promise directly from the db.

exports.create_a_topic = function(topicType, topicProperties) {
    let newTopic = new Topic({
        topic_type: topicType,
        properties: topicProperties
    });
    // important: return the promise returned by save...
    return newTopic.save();
})

Similarly with the second function...

exports.get_subscribers = function(topicType) { 
    return User.find({subscription: topicType}, {'email': 1, 'queueName': 1, 'isFiltered': 1});
})

Here's one that does both...

exports.create_a_topic_and_get_subscribers = function(topicType, topicProperties) {
    var result = {};
    return create_a_topic(topicType, topicProperties)
    .then((topic) => result.topic = topic)
    .then(() => get_subscribers(topicType) )
    .then((subscribers) => {
        result.subscribers = subscribers
        return result
    });
}

And so on. Leave the controller to pulling params out of req and arranging the response based on how the model functions work out. In other words...

app.post('/test_publish_topic', (req, res) => {
    let topicType = req.body.topic_type;
    let topicProperties = req.body.properties[0];
    project.create_a_topic_and_get_subscribers(topicType, topicProperties)
    .then((result) => res.status(200).json(result))
    .catch((error) => res.status(500).json(error))
});

The key is to create them as small promise-returning units. Build some routes that test the individuals. You can do chaining of the model units in the controller, and leave the manipulation of req and res to the controller.

danh
  • 62,181
  • 10
  • 95
  • 136
1

Found the problem that is in the route: instead of

.catch(err => res.status(err.status >= 100 && err.status < 600 ? err.code: 500).send({message: err.message}))

I should have used

.catch(err => res.status(err.status >= 100 && err.status < 600 ? **err.status**: 500).send({message: err.message}))

So, problem is

err.code

instead of

err.status

BTW, I will follow your tips above. Thanks to all!

JackLametta
  • 400
  • 1
  • 2
  • 21