{"action":"created","comment":{"url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/pulls/comments/32230410","id":32230410,"diff_hunk":"@@ -0,0 +1,230 @@\n+var _ = require('lodash');\n+var Bluebird = require('bluebird');\n+\n+var mongo;\n+\n+\n+var webtask = function (context, req, res) {\n+    var action = validate_method({\n+        put_job: 'PUT',\n+        get_job: 'GET',\n+        destroy_job: 'DELETE',\n+        list_jobs: 'GET',\n+        job_history: 'GET',\n+        reserve_jobs: 'POST',\n+        renew_reservation: 'PUT',\n+        release_reservation: 'DELETE',\n+    });\n+    \n+    if (!validate_params(['MONGO_COLLECTION']))\n+        return;\n+    \n+    if (action === 'put_job') {\n+        // Create or update a scheduled webtask\n+        \n+        if (!validate_params(['container', 'name', 'schedule', 'token']))\n+            return;\n+            \n+        var now = new Date();\n+        \n+        var item = {\n+            state: 'active',\n+            schedule: context.data.schedule,\n+            token: context.data.token,\n+            container: context.data.container,\n+            name: context.data.name,\n+            last_run_at: null,\n+            next_available_at: now,\n+            last_result: null,\n+            run_count: 0,\n+            error_count: 0,\n+        };\n+        \n+        return withMongoCollection(context.data.MONGO_COLLECTION)\n+            .then(function (coll) {\n+                return coll.findOneAndUpdateAsync({\n+                    container: item.container,\n+                    name: item.name,\n+                }, item, {\n+                    returnOriginal: false,\n+                    upsert: true,\n+                });\n+            })\n+            .get('value')\n+            .then(function (data) {\n+                res.writeHead(200, {'Content-Type': 'application/json'});\n+                res.end(JSON.stringify(data));\n+            })\n+            .catch(function (err) {\n+                error(err.statusCode || 500, err);\n+            });\n+    } else if (action === 'reserve_jobs') {\n+        // Reserve a set of jobs for execution\n+        \n+        if (!validate_params(['count', 'ttl']))\n+            return;\n+        \n+        var now = new Date();\n+        var next_available_at = new Date(now.valueOf() + (parseInt(context.data.ttl, 10) * 1000))\n+        \n+        return withMongoCollection(context.data.MONGO_COLLECTION)\n+            .then(function (coll) {\n+                return Bluebird.resolve(_.range(context.data.count))\n+                    // TODO (ggoodman): Refactor this to use reduce to avoid calling N queries if reservation fails before N\n+                    .map(function (n) {\n+                        console.log(\"Attempting reservation\", n);\n+                        var filter = {\n+                            next_available_at: {\n+                                $lte: now\n+                            },\n+                            state: 'active',\n+                        };\n+                        var update = {\n+                            $set: {\n+                                next_available_at: next_available_at\n+                            }\n+                        };\n+                        \n+                        return coll.findOneAndUpdateAsync(filter, update, {\n+                            returnOriginal: false, // Return modified\n+                        })\n+                            .get('value'); // Only pull out the value\n+                        \n+                    }, {concurrency: 1}) // Serial mapping\n+                    .filter(Boolean);\n+                \n+            })\n+            .then(function (data) {\n+                res.writeHead(200, {'Content-Type': 'application/json'});\n+                res.end(JSON.stringify(data));\n+            })\n+            .catch(function (err) {\n+                error(err.statusCode || 500, err);\n+            });\n+    } else if (action === 'list_jobs') {\n+        // List all scheduled webtasks or only those in a container passed via query\n+        \n+        return withMongoCollection(context.data.MONGO_COLLECTION)\n+            .then(function (coll) {\n+                var query = {};\n+                \n+                // Both admin and user will hit this endpoint. When admin, container can be unset\n+                if (context.data.container) query.container = context.data.container;\n+                \n+                // TODO (ggoodman): Pagination logic\n+                return coll.findAsync(query)\n+                    .then(function (cursor) {\n+                        return Bluebird.promisify(cursor.toArray, cursor)();\n+                    });\n+            })\n+            .then(function (data) {\n+                res.writeHead(200, {'Content-Type': 'application/json'});\n+                res.end(JSON.stringify(data));\n+            })\n+            .catch(function (err) {\n+                error(err.statusCode || 500, err);\n+            });\n+    \n+    } else if (action === 'get_job') {","path":"webtasks/cron_mongodb.js","position":130,"original_position":128,"commit_id":"6e259eb1f35a4b32c620d86543c0fbaa3c85c66f","original_commit_id":"870fa058e26d7b54b7949e970e50d2ddc469d2e8","user":{"login":"ggoodman","id":50030,"avatar_url":"https://avatars.githubusercontent.com/u/50030?v=3","gravatar_id":"","url":"https://api.github.com/users/ggoodman","html_url":"https://github.com/ggoodman","followers_url":"https://api.github.com/users/ggoodman/followers","following_url":"https://api.github.com/users/ggoodman/following{/other_user}","gists_url":"https://api.github.com/users/ggoodman/gists{/gist_id}","starred_url":"https://api.github.com/users/ggoodman/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ggoodman/subscriptions","organizations_url":"https://api.github.com/users/ggoodman/orgs","repos_url":"https://api.github.com/users/ggoodman/repos","events_url":"https://api.github.com/users/ggoodman/events{/privacy}","received_events_url":"https://api.github.com/users/ggoodman/received_events","type":"User","site_admin":false},"body":"Since the former is working on a single item and the latter on an array of items, each time a step needs to be modified to account for the different nature, we introduce a branch. I prefer to keep the branch at the top level. As you will see in the `stripMongoId` commit, this means `.map(stripMongoId)` vs. `.filter(stripMongoId)` and the difference in query operator you mentioned.\r\n\r\nIf you feel strongly about this, I can parametrize the methods based on the action to reuse common logic.","created_at":"2015-06-11T15:06:04Z","updated_at":"2015-06-11T15:06:04Z","html_url":"https://github.com/auth0/auth0-sandbox-ext/pull/1#discussion_r32230410","pull_request_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/pulls/1","_links":{"self":{"href":"https://api.github.com/repos/auth0/auth0-sandbox-ext/pulls/comments/32230410"},"html":{"href":"https://github.com/auth0/auth0-sandbox-ext/pull/1#discussion_r32230410"},"pull_request":{"href":"https://api.github.com/repos/auth0/auth0-sandbox-ext/pulls/1"}}},"pull_request":{"url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/pulls/1","id":37395315,"html_url":"https://github.com/auth0/auth0-sandbox-ext/pull/1","diff_url":"https://github.com/auth0/auth0-sandbox-ext/pull/1.diff","patch_url":"https://github.com/auth0/auth0-sandbox-ext/pull/1.patch","issue_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/issues/1","number":1,"state":"open","locked":false,"title":"Add webtask to support persisting scheduled webtasks to mongodb","user":{"login":"ggoodman","id":50030,"avatar_url":"https://avatars.githubusercontent.com/u/50030?v=3","gravatar_id":"","url":"https://api.github.com/users/ggoodman","html_url":"https://github.com/ggoodman","followers_url":"https://api.github.com/users/ggoodman/followers","following_url":"https://api.github.com/users/ggoodman/following{/other_user}","gists_url":"https://api.github.com/users/ggoodman/gists{/gist_id}","starred_url":"https://api.github.com/users/ggoodman/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ggoodman/subscriptions","organizations_url":"https://api.github.com/users/ggoodman/orgs","repos_url":"https://api.github.com/users/ggoodman/repos","events_url":"https://api.github.com/users/ggoodman/events{/privacy}","received_events_url":"https://api.github.com/users/ggoodman/received_events","type":"User","site_admin":false},"body":"ATTN: @tjanczuk ","created_at":"2015-06-10T19:31:30Z","updated_at":"2015-06-11T15:06:04Z","closed_at":null,"merged_at":null,"merge_commit_sha":"a1e73f05bf197ec1b6c8d71fd2c84c19c82f0d5f","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/pulls/1/commits","review_comments_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/pulls/1/comments","review_comment_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/pulls/comments{/number}","comments_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/issues/1/comments","statuses_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/statuses/6e259eb1f35a4b32c620d86543c0fbaa3c85c66f","head":{"label":"auth0:dev-cron","ref":"dev-cron","sha":"6e259eb1f35a4b32c620d86543c0fbaa3c85c66f","user":{"login":"auth0","id":2824157,"avatar_url":"https://avatars.githubusercontent.com/u/2824157?v=3","gravatar_id":"","url":"https://api.github.com/users/auth0","html_url":"https://github.com/auth0","followers_url":"https://api.github.com/users/auth0/followers","following_url":"https://api.github.com/users/auth0/following{/other_user}","gists_url":"https://api.github.com/users/auth0/gists{/gist_id}","starred_url":"https://api.github.com/users/auth0/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/auth0/subscriptions","organizations_url":"https://api.github.com/users/auth0/orgs","repos_url":"https://api.github.com/users/auth0/repos","events_url":"https://api.github.com/users/auth0/events{/privacy}","received_events_url":"https://api.github.com/users/auth0/received_events","type":"Organization","site_admin":false},"repo":{"id":32882682,"name":"auth0-sandbox-ext","full_name":"auth0/auth0-sandbox-ext","owner":{"login":"auth0","id":2824157,"avatar_url":"https://avatars.githubusercontent.com/u/2824157?v=3","gravatar_id":"","url":"https://api.github.com/users/auth0","html_url":"https://github.com/auth0","followers_url":"https://api.github.com/users/auth0/followers","following_url":"https://api.github.com/users/auth0/following{/other_user}","gists_url":"https://api.github.com/users/auth0/gists{/gist_id}","starred_url":"https://api.github.com/users/auth0/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/auth0/subscriptions","organizations_url":"https://api.github.com/users/auth0/orgs","repos_url":"https://api.github.com/users/auth0/repos","events_url":"https://api.github.com/users/auth0/events{/privacy}","received_events_url":"https://api.github.com/users/auth0/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/auth0/auth0-sandbox-ext","description":"Webtask based extensions of Auth0 Webtasks","fork":false,"url":"https://api.github.com/repos/auth0/auth0-sandbox-ext","forks_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/forks","keys_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/keys{/key_id}","collaborators_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/teams","hooks_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/hooks","issue_events_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/issues/events{/number}","events_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/events","assignees_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/assignees{/user}","branches_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/branches{/branch}","tags_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/tags","blobs_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/git/refs{/sha}","trees_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/git/trees{/sha}","statuses_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/statuses/{sha}","languages_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/languages","stargazers_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/stargazers","contributors_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/contributors","subscribers_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/subscribers","subscription_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/subscription","commits_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/commits{/sha}","git_commits_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/git/commits{/sha}","comments_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/comments{/number}","issue_comment_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/issues/comments{/number}","contents_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/contents/{+path}","compare_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/compare/{base}...{head}","merges_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/merges","archive_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/downloads","issues_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/issues{/number}","pulls_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/pulls{/number}","milestones_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/milestones{/number}","notifications_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/labels{/name}","releases_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/releases{/id}","created_at":"2015-03-25T18:19:33Z","updated_at":"2015-04-10T22:53:17Z","pushed_at":"2015-06-11T14:41:35Z","git_url":"git://github.com/auth0/auth0-sandbox-ext.git","ssh_url":"git@github.com:auth0/auth0-sandbox-ext.git","clone_url":"https://github.com/auth0/auth0-sandbox-ext.git","svn_url":"https://github.com/auth0/auth0-sandbox-ext","homepage":null,"size":148,"stargazers_count":0,"watchers_count":0,"language":"JavaScript","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":1,"forks":0,"open_issues":1,"watchers":0,"default_branch":"master"}},"base":{"label":"auth0:master","ref":"master","sha":"52316b526782f522648c09fc74ad37ffd9215e2e","user":{"login":"auth0","id":2824157,"avatar_url":"https://avatars.githubusercontent.com/u/2824157?v=3","gravatar_id":"","url":"https://api.github.com/users/auth0","html_url":"https://github.com/auth0","followers_url":"https://api.github.com/users/auth0/followers","following_url":"https://api.github.com/users/auth0/following{/other_user}","gists_url":"https://api.github.com/users/auth0/gists{/gist_id}","starred_url":"https://api.github.com/users/auth0/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/auth0/subscriptions","organizations_url":"https://api.github.com/users/auth0/orgs","repos_url":"https://api.github.com/users/auth0/repos","events_url":"https://api.github.com/users/auth0/events{/privacy}","received_events_url":"https://api.github.com/users/auth0/received_events","type":"Organization","site_admin":false},"repo":{"id":32882682,"name":"auth0-sandbox-ext","full_name":"auth0/auth0-sandbox-ext","owner":{"login":"auth0","id":2824157,"avatar_url":"https://avatars.githubusercontent.com/u/2824157?v=3","gravatar_id":"","url":"https://api.github.com/users/auth0","html_url":"https://github.com/auth0","followers_url":"https://api.github.com/users/auth0/followers","following_url":"https://api.github.com/users/auth0/following{/other_user}","gists_url":"https://api.github.com/users/auth0/gists{/gist_id}","starred_url":"https://api.github.com/users/auth0/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/auth0/subscriptions","organizations_url":"https://api.github.com/users/auth0/orgs","repos_url":"https://api.github.com/users/auth0/repos","events_url":"https://api.github.com/users/auth0/events{/privacy}","received_events_url":"https://api.github.com/users/auth0/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/auth0/auth0-sandbox-ext","description":"Webtask based extensions of Auth0 Webtasks","fork":false,"url":"https://api.github.com/repos/auth0/auth0-sandbox-ext","forks_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/forks","keys_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/keys{/key_id}","collaborators_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/teams","hooks_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/hooks","issue_events_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/issues/events{/number}","events_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/events","assignees_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/assignees{/user}","branches_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/branches{/branch}","tags_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/tags","blobs_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/git/refs{/sha}","trees_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/git/trees{/sha}","statuses_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/statuses/{sha}","languages_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/languages","stargazers_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/stargazers","contributors_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/contributors","subscribers_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/subscribers","subscription_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/subscription","commits_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/commits{/sha}","git_commits_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/git/commits{/sha}","comments_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/comments{/number}","issue_comment_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/issues/comments{/number}","contents_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/contents/{+path}","compare_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/compare/{base}...{head}","merges_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/merges","archive_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/downloads","issues_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/issues{/number}","pulls_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/pulls{/number}","milestones_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/milestones{/number}","notifications_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/labels{/name}","releases_url":"https://api.github.com/repos/auth0/auth0-sandbox-ext/releases{/id}","created_at":"2015-03-25T18:19:33Z","updated_at":"2015-04-10T22:53:17Z","pushed_at":"2015-06-11T14:41:35Z","git_url":"git://github.com/auth0/auth0-sandbox-ext.git","ssh_url":"git@github.com:auth0/auth0-sandbox-ext.git","clone_url":"https://github.com/auth0/auth0-sandbox-ext.git","svn_url":"https://github.com/auth0/auth0-sandbox-ext","homepage":null,"size":148,"stargazers_count":0,"watchers_count":0,"language":"JavaScript","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":1,"forks":0,"open_issues":1,"watchers":0,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/auth0/auth0-sandbox-ext/pulls/1"},"html":{"href":"https://github.com/auth0/auth0-sandbox-ext/pull/1"},"issue":{"href":"https://api.github.com/repos/auth0/auth0-sandbox-ext/issues/1"},"comments":{"href":"https://api.github.com/repos/auth0/auth0-sandbox-ext/issues/1/comments"},"review_comments":{"href":"https://api.github.com/repos/auth0/auth0-sandbox-ext/pulls/1/comments"},"review_comment":{"href":"https://api.github.com/repos/auth0/auth0-sandbox-ext/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/auth0/auth0-sandbox-ext/pulls/1/commits"},"statuses":{"href":"https://api.github.com/repos/auth0/auth0-sandbox-ext/statuses/6e259eb1f35a4b32c620d86543c0fbaa3c85c66f"}}}}