hcdocs
hcdocs

Reputation: 1309

JavaScript loop to accommodate filtered array of objects received from a webhook

Goal

Capture each event sent through a webhook and turn it into a Slack post. Events include new blog posts, questions, discussions, wiki page, etc. (qualified as contents) and comments (qualified as comments) posted in an online community. Sometimes multiple events are sent in the webhook at once.

Attempted method

This simple JavaScript Azure Function is intended to

Results

The following code worked fine for a single webhook event until I attempted to add the for loop to accommodate multiple webhook events sent in one array.

Code

Example JSON from webhook

{
    "events": [{
            "TypeId": "9999-999e",
            "DateOccurred": "2018-12-15T20:39:42.2487557Z",
            "EventData": {
                "ActorUserId": 1234,
                "ContentId": "5678-999c",
                "ContentTypeId": "9012-999d",
                "WikiPageId": 3456,
                "WikiId": 1
            }
        },
        {
            "TypeId": "1111-111f",
            "DateOccurred": "2018-12-15T22:55:37.7846546Z",
            "EventData": {
                "ActorUserId": 2345,
                "ContentId": "2222-222b",
                "ContentTypeId": "3333-333a",
                "ForumReplyId": 4567,
                "ForumThreadId": 8901,
                "ForumId": 2
            }
        },
        {
            "TypeId": "9012-888f",
            "DateOccurred": "2018-12-15T22:44:57.7091846Z",
            "EventData": {
                "ActorUserId": 9876,
                "CommentId": "8900-123a"
            }
        }
    ]
}

Example JSON returned from API request

The slightly different structure in hierarchies is accurate.

(for contents)

{
    "Content": {
        "CreatedByUser": {
            "ProfileUrl": "https://<company>.telligenthosting.net/members/<user>",
            "Username": "<user>"
        },
        "HtmlName": "Title",
        "HtmlDescription": "Text",
        "Url": "https://<company>.telligenthosting.net/<link>"
    }
}

(for comments)

{
    "Comment": {
        "Content": {
            "CreatedByUser": {
                "ProfileUrl": "https://<company>.telligenthosting.net/members/<user>",
                "Username": "<user>"
            },
            "HtmlName": "Title",
            "HtmlDescription": "Text",
            "Url": "https://<company>.telligenthosting.net/<link>"
        }
    }
}

JavaScript file (as an Azure Function)

module.exports = function (context, data) {
    var json = data.body;

    var request = require('request');

    // Parse the webhook event JSON body

    var unparsed = JSON.stringify(json.events);
    var parsed = JSON.parse(unparsed);

    console.log(parsed) // RESULTS ARE AS EXPECTED (the JSON nested beneath `events`, beginning and ending with `[]`)

    for (var i = 0; i < parsed.length; i++) {

        // Parse out Id of webhook event (for all content types but comments)

        // This Id retrieves details about the content

        var ContentId, ContentTypeId;
        if (parsed[i].EventData.hasOwnProperty('ContentId')) {

            var ContentId = parsed[i].EventData.ContentId;
            var ContentTypeId = parsed[i].EventData.ContentTypeId;

            console.log(ContentTypeId); // RESULTS ARE NOT AS EXPECTED: Prints the same Id twice

            var options = {
                url: "https://<company>.telligenthosting.net/api.ashx/v2/genericcontent/" + ContentId + "/" + ContentTypeId + ".json",
                headers: {
                    "Rest-User-Token": "<token>",
                    "Content-Type": "application/json"
                }
            };
        };

        // Parse out Id of a webhook event (for comments only)

        // This Id retrieves details about a comment    

        var CommentId;
        if (parsed[i].EventData.hasOwnProperty('CommentId')) {

            var CommentId = parsed[i].EventData.CommentId;

            var options = {
                url: "https://<company>.telligenthosting.net/api.ashx/v2/comments/" + CommentId + ".json",
                headers: {
                    "Rest-User-Token": "<token>",
                    "Content-Type": "application/json"
                }
            };
        };

        function callback(error, response, body) {
            if (!error && response.statusCode == 200) {
                var info = JSON.parse(body);

                //For all content types but comments

                var username, profileUrl, subject, url, text;
                if (info.hasOwnProperty('Content')) {

                    username = info.Content.CreatedByUser.Username;
                    profileUrl = info.Content.CreatedByUser.ProfileUrl;
                    subject = info.Content.HtmlName;
                    url = info.Content.Url;
                    text = info.Content.HtmlDescription;
                };

                //For comments

                if (info.hasOwnProperty('Comment')) {

                    username = info.Comment.User.DisplayName;
                    profileUrl = info.Comment.User.ProfileUrl;
                    subject = info.Comment.Content.HtmlName;
                    url = info.Comment.Url;
                    text = info.Comment.Body;
                };
            };

            //Send to Slack

            function sendToSlack(theUsername, theIconEmoji) {

                var theUsername = "Bot";
                var theIconEmoji = ":bot:";

                var payload = {
                    attachments: [{
                        author_name: username,
                        author_link: profileUrl,
                        title: subject,
                        title_link: url,
                        text: text
                    }]
                };
                if (theUsername !== undefined) {
                    payload.username = theUsername;
                }
                if (theIconEmoji !== undefined) {
                    payload.icon_emoji = theIconEmoji;
                }
                var theRequest = {
                    url: urlWebHook,
                    method: "POST",
                    json: payload
                };
                request(theRequest, function (error, response, body) {});
            }

            var urlWebHook = "https://hooks.slack.com/services/<Id>";

            sendToSlack();
        };
    };
    request(options, callback);
};

Issue

As commented out in the code above, it appears that the loop is not going through each event but rather through the first event multiple times.

Much of what I read indicates for (var i = 0; i < json.length; i++) { is the proper approach but no matter what I try the Azure Function throws a 500 Internal Service Error and eventually times out. No information is provided in the debug console even though detailed logging is turned on.

Thank you

Thank you for any advice or education.

Upvotes: 0

Views: 318

Answers (2)

benas
benas

Reputation: 186

EventData is not defined because you're not constructing your object properly.

Here's how you might do it:

var json = require("./test.json");

var unparsedEvents = json.events;
for (let event of unparsedEvents) {
    var ContentId = event.EventData.ContentId;
    var ContentTypeId = event.EventData.ContentTypeId;
    var CommentId = event.EventData.CommentId;
    var options = new Object();

    console.log("ContentId:", ContentId);
    console.log("ContentTypeId:", ContentTypeId);
    console.log("CommentId:", CommentId);

    if (CommentId) {
        options.url = "https://<company>.telligenthosting.net/api.ashx/v2/comments/" + CommentId + ".json";
        options.headers = {
            "Rest-User-Token": "<token>",
            "Content-Type": "application/json",
        };
    } else {
        options.url = "https://<company>.telligenthosting.net/api.ashx/v2/genericcontent/" + ContentId + "/" + ContentTypeId + ".json";
        options.headers = {
            "Rest-User-Token": "<token>",
            "Content-Type": "application/json",
        };
    }

    console.log("options:", options);
    console.log();
}

Upvotes: 1

GarbageCoder456
GarbageCoder456

Reputation: 11

I believe you need to change parsed[0] to parsed[i]. Currently you are looping through the array but only accessing the first element, which is why you see the first event multiple times.

Upvotes: 1

Related Questions