{"action":"created","comment":{"url":"https://api.github.com/repos/cozy/cozy-data-system/pulls/comments/42862376","id":42862376,"diff_hunk":"@@ -1,12 +1,417 @@\n-Client = require(\"request-json\").JsonClient\n+indexer = require 'cozy-indexer'\n+db = require('../helpers/db_connect_helper').db_connect()\n+async = require 'async'\n+locker = require '../lib/locker'\n \n-if process.env.INDEXER_PORT\n-    port = process.env.INDEXER_PORT\n-else if process.env.NODE_ENV is \"test\"\n-    port = 9092\n-else\n-    port = 9102\n+indexQueue = {}\n+batchInProgress = false\n+BATCH_SIZE = 100\n+batchCounter = 1\n+indexdefinitions = {}\n+indexdefinitionsID = {}\n+status = {}\n \n-indexerHost = process.env.INDEXER_HOST or 'localhost'\n+FETCH_AT_ONCE_FOR_REINDEX = BATCH_SIZE\n \n-module.exports = new Client \"http://#{indexerHost}:#{port}/\"\n\\ No newline at end of file\n+# indexfield added to all documents\n+# @TODO: figure out a way to add a common date field\n+commonIndexFields =\n+    \"docType\": filter: true, searchable: false\n+    \"tags\":    filter: true\n+\n+# prevent from adding or removing doc while\n+# a cleanup is in progress (cause fatal exception)\n+forgetDoc = locker.wrap 'indexfile', indexer.forget\n+addBatch  = locker.wrap 'indexfile', indexer.addBatch\n+cleanup   = locker.wrap 'indexfile', indexer.cleanup\n+\n+###*\n+# Initialize the indexer\n+#\n+# @return (callback) when initialization is complete\n+###\n+exports.initialize = (callback) ->\n+\n+    async.waterfall [\n+        (callback) ->\n+            indexer.store.open callback\n+\n+        (callback) -> # FETCH INDEX DEFINITIONS\n+            query = include_docs: true\n+            db.view \"indexdefinition/all\", query, (err, rows) ->\n+                return callback err if err\n+                for row in rows\n+                    docType = row.doc.targetDocType\n+                    definitionDocument = row.doc\n+                    for k, v of commonIndexFields\n+                        definitionDocument.ftsIndexedFields[k] = v\n+                    indexdefinitions[docType] = definitionDocument\n+                    indexdefinitionsID[row.id] = docType\n+                    status[docType] = {total: 0, done: 0}\n+\n+                registerDefaultIndexes callback\n+\n+        (callback) -> # REINDEX DOCTYPE IF INDEX DEFINITION NEWER\n+            docTypes = Object.keys indexdefinitions\n+            async.eachSeries docTypes, maybeReindexDocType, callback\n+\n+        (callback) -> # GET LAST PROPERLY INDEXED SEQNUMBER\n+            indexer.store.get 'indexedseq', (err, seq) ->\n+                callback err, seq\n+\n+        (seqno, callback) -> # GET CHANGES SINCE STORED seqno\n+            options = {include_docs: true, since: seqno, raw: true}\n+            db.changes options, (err, changes) ->\n+                callback err, changes\n+\n+        (changes, callback) -> # PUT THEM IN THE QUEUE\n+            for change in changes when not change.deleted\n+                exports.onDocumentUpdate change.doc, change.seq\n+            callback null\n+\n+    ], callback\n+\n+\n+###*\n+# Get a batch from the queue and index it\n+#\n+###\n+dequeue = ->\n+    # do nothing if there is already a batch in progress\n+    return null if batchInProgress\n+\n+    # try to find a docType with some docs to be indexed\n+    for docType, docs of indexQueue when docs.length > 0\n+        # Take BATCH_SIZE docs, leave the rest\n+        docs = docs[0..(BATCH_SIZE-1)]\n+        indexQueue[docType] = docs[BATCH_SIZE..]\n+        batchInProgress = true\n+        break # stop when we find one\n+\n+    # if there is nothing to do, we stop now\n+    return null unless batchInProgress\n+\n+    options = indexdefinitions[docType].ftsIndexedFields\n+    maxseqno = docs[docs.length-1]._seqno\n+    batchName = \"batch #{batchCounter++}\"\n+    console.log \"add #{batchName} of #{docs.length} #{docType}\"\n+    addBatch docs, options, (err) ->\n+        console.log \"#{batchName} done\", err?.stack or \"success\"\n+        checkpointSeqNumber maxseqno, (err) ->\n+            console.log \"checkpoint error\", err if err\n+            batchInProgress = false\n+            status[docType].done += docs.length\n+            setImmediate dequeue\n+\n+###*\n+# Destroy the index completely (used for tests)\n+#\n+###\n+exports.cleanup = (callback) ->\n+    cleanup callback\n+\n+###*\n+# To be called every time a document is updated\n+# used in lib/feed\n+#\n+###\n+exports.onDocumentUpdate = (doc, seqno) ->\n+    docType = doc.docType?.toLowerCase?()\n+    if docType of indexdefinitions\n+        doc._seqno = seqno\n+        indexQueue[docType] ?= []\n+        indexQueue[docType].push doc\n+        status[docType].total++\n+        setImmediate dequeue\n+\n+###*\n+# To be called every time a document is deleted\n+# used in lib/feed\n+#\n+###\n+exports.onDocumentDelete = (doc, seqno) ->\n+    if doc.docType is 'indexdefinition'\n+        docType = indexdefinitionsID[doc._id]\n+        delete indexdefinitions[docType]\n+\n+    else if doc.docType of indexdefinitions\n+        forgetDoc doc._id, ->\n+            console.log \"doc#{doc._id} unindexed\"\n+\n+###*\n+# Perform a search in the index\n+#\n+# @params docType {array} docTypes to search in, empty for all\n+# @params options {object} query options\n+# @params options.query {mixed} the search terms\n+# @params options.numPage {number} page number\n+# @params options.pageSize {number} number of result by page\n+# @params options.facets {object} see cozy-indexer doc\n+# @params options.filter {object} see cozy-indexer doc\n+#\n+# @return (callback) {object} see cozy-indexer doc\n+#\n+###\n+exports.search = (docTypes, options, callback) ->\n+    params =\n+        offset        : options.numPage or 0\n+        pageSize      : options.numByPage or 10\n+\n+    if typeof options.query is 'string'\n+        params.search = \"*\": [options.query]\n+    else if Array.isArray(options.query)\n+        params.search = \"*\": options.query\n+    else\n+        params.search = options.query\n+\n+    if options.facets\n+        params.facets = options.facets\n+\n+    if options.filter\n+        params.filter = options.filter\n+\n+    if docTypes.length > 0\n+        params.filter ?= {}\n+        params.filter.docType = docTypes.map (t) -> [t, t]\n+\n+    indexer.search params, (err, results) ->\n+        return next err if err\n+        callback null, results\n+\n+###*\n+# Register the indexdefintion for a docType\n+#\n+# @params docType {string} docType to register for\n+# @params indexdefinition {object} a map of field to index rules\n+#\n+# @return (callback) after reindexing or 10s, whichever comes first\n+#\n+###\n+exports.registerIndexDefinition = (docType, indexdefinition, callback) ->\n+\n+    callbackOnce = (cause) ->\n+        callback.apply this, arguments if callback\n+        callback = null\n+\n+    docType = docType.toLowerCase()\n+    existing = indexdefinitions[docType]\n+    changed = false\n+    status[docType] ?= {total: 0, done: 0}\n+\n+    if existing\n+        mergedFields = {}\n+        for k, v of existing.ftsIndexedFields when not commonIndexFields[k]\n+            mergedFields[k] = v\n+\n+        for field, fieldDef of indexdefinition\n+            oldFieldDef = existing.ftsIndexedFields[field]\n+            mergedFields[field] = indexer.mergeFieldDef oldFieldDef, fieldDef\n+            changed = true if mergedFields[field] isnt oldFieldDef\n+        definitionDocument = existing\n+        definitionDocument.ftsIndexedFields = mergedFields\n+    else\n+        definitionDocument =\n+            docType: \"indexdefinition\"\n+            ftsIndexedFields: indexdefinition\n+            targetDocType: docType\n+        changed = true\n+\n+    if changed\n+        db.save definitionDocument, (err, savedDoc) ->\n+            return callback err if err\n+            definitionDocument._id = savedDoc.id\n+            definitionDocument._rev = savedDoc.rev\n+            for k, v of commonIndexFields\n+                definitionDocument.ftsIndexedFields[k] = v\n+\n+            indexdefinitions[docType] = definitionDocument\n+            indexdefinitionsID[savedDoc.id] = docType\n+\n+\n+            setTimeout callbackOnce.bind(null, new Error('timeout')), 10000\n+\n+            reindexDocTypeAll docType, (err) ->\n+\n+                return callback err if err\n+                checkpointDocTypeRev docType, savedDoc.rev, callbackOnce\n+\n+    else\n+        console.log \"rev is different, but definition not changed\"\n+        setImmediate callbackOnce\n+\n+###*\n+# Store the indexdefintion rev within the index file\n+#\n+# @params docType {string} docType to register for\n+# @params rev {string} value to store\n+#\n+# @return (callback) when done\n+#\n+###\n+checkpointDocTypeRev = (docType, rev, callback) ->\n+    indexer.store.set \"indexedrev/#{docType}\", rev, callback\n+\n+###*\n+# Store the last indexed sequence number within the index file\n+#\n+# @params seqno {string} value to store\n+#\n+# @return (callback) when done\n+#\n+###\n+checkpointSeqNumber = (seqno, callback) ->\n+    indexer.store.set 'indexedseq', seqno, callback\n+\n+\n+###*\n+# Recursive function to reindex all docs for a given docType\n+# get doc in batch of FETCH_AT_ONCE_FOR_REINDEX and add them immediately\n+#\n+# Note, this function doesn't use the indexQueue used for realtime events\n+#\n+# @params docType {string} value to store\n+# @params definition {object} definition of fields to store\n+#\n+# @return (callback) when done\n+#\n+###\n+reindexDocTypeStep = (docType, definition, callback, skip = 0) ->","path":"server/lib/indexer.coffee","position":289,"original_position":288,"commit_id":"550bbe4832e60033c0c0874ef241c857cdf0d7f7","original_commit_id":"0648afee4a5a9316c44c578a20c4cebd6be15e36","user":{"login":"aenario","id":1580287,"avatar_url":"https://avatars.githubusercontent.com/u/1580287?v=3","gravatar_id":"","url":"https://api.github.com/users/aenario","html_url":"https://github.com/aenario","followers_url":"https://api.github.com/users/aenario/followers","following_url":"https://api.github.com/users/aenario/following{/other_user}","gists_url":"https://api.github.com/users/aenario/gists{/gist_id}","starred_url":"https://api.github.com/users/aenario/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/aenario/subscriptions","organizations_url":"https://api.github.com/users/aenario/orgs","repos_url":"https://api.github.com/users/aenario/repos","events_url":"https://api.github.com/users/aenario/events{/privacy}","received_events_url":"https://api.github.com/users/aenario/received_events","type":"User","site_admin":false},"body":"I find it more elegant for parameters which are never used outside of async recursion. \r\n(Which is also why it is not documented)","created_at":"2015-10-23T13:08:50Z","updated_at":"2015-10-23T13:08:50Z","html_url":"https://github.com/cozy/cozy-data-system/pull/166#discussion_r42862376","pull_request_url":"https://api.github.com/repos/cozy/cozy-data-system/pulls/166","_links":{"self":{"href":"https://api.github.com/repos/cozy/cozy-data-system/pulls/comments/42862376"},"html":{"href":"https://github.com/cozy/cozy-data-system/pull/166#discussion_r42862376"},"pull_request":{"href":"https://api.github.com/repos/cozy/cozy-data-system/pulls/166"}}},"pull_request":{"url":"https://api.github.com/repos/cozy/cozy-data-system/pulls/166","id":48569082,"html_url":"https://github.com/cozy/cozy-data-system/pull/166","diff_url":"https://github.com/cozy/cozy-data-system/pull/166.diff","patch_url":"https://github.com/cozy/cozy-data-system/pull/166.patch","issue_url":"https://api.github.com/repos/cozy/cozy-data-system/issues/166","number":166,"state":"open","locked":false,"title":"Indexer2","user":{"login":"aenario","id":1580287,"avatar_url":"https://avatars.githubusercontent.com/u/1580287?v=3","gravatar_id":"","url":"https://api.github.com/users/aenario","html_url":"https://github.com/aenario","followers_url":"https://api.github.com/users/aenario/followers","following_url":"https://api.github.com/users/aenario/following{/other_user}","gists_url":"https://api.github.com/users/aenario/gists{/gist_id}","starred_url":"https://api.github.com/users/aenario/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/aenario/subscriptions","organizations_url":"https://api.github.com/users/aenario/orgs","repos_url":"https://api.github.com/users/aenario/repos","events_url":"https://api.github.com/users/aenario/events{/privacy}","received_events_url":"https://api.github.com/users/aenario/received_events","type":"User","site_admin":false},"body":"Note : the person reviewing this should probably have a look at https://github.com/cozy/cozy-indexer too.","created_at":"2015-10-23T11:26:45Z","updated_at":"2015-10-23T13:08:50Z","closed_at":null,"merged_at":null,"merge_commit_sha":"4bc501d1c48f802fefd018699fadefd379b0f3b0","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/cozy/cozy-data-system/pulls/166/commits","review_comments_url":"https://api.github.com/repos/cozy/cozy-data-system/pulls/166/comments","review_comment_url":"https://api.github.com/repos/cozy/cozy-data-system/pulls/comments{/number}","comments_url":"https://api.github.com/repos/cozy/cozy-data-system/issues/166/comments","statuses_url":"https://api.github.com/repos/cozy/cozy-data-system/statuses/550bbe4832e60033c0c0874ef241c857cdf0d7f7","head":{"label":"aenario:indexer2","ref":"indexer2","sha":"550bbe4832e60033c0c0874ef241c857cdf0d7f7","user":{"login":"aenario","id":1580287,"avatar_url":"https://avatars.githubusercontent.com/u/1580287?v=3","gravatar_id":"","url":"https://api.github.com/users/aenario","html_url":"https://github.com/aenario","followers_url":"https://api.github.com/users/aenario/followers","following_url":"https://api.github.com/users/aenario/following{/other_user}","gists_url":"https://api.github.com/users/aenario/gists{/gist_id}","starred_url":"https://api.github.com/users/aenario/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/aenario/subscriptions","organizations_url":"https://api.github.com/users/aenario/orgs","repos_url":"https://api.github.com/users/aenario/repos","events_url":"https://api.github.com/users/aenario/events{/privacy}","received_events_url":"https://api.github.com/users/aenario/received_events","type":"User","site_admin":false},"repo":{"id":8723753,"name":"cozy-data-system","full_name":"aenario/cozy-data-system","owner":{"login":"aenario","id":1580287,"avatar_url":"https://avatars.githubusercontent.com/u/1580287?v=3","gravatar_id":"","url":"https://api.github.com/users/aenario","html_url":"https://github.com/aenario","followers_url":"https://api.github.com/users/aenario/followers","following_url":"https://api.github.com/users/aenario/following{/other_user}","gists_url":"https://api.github.com/users/aenario/gists{/gist_id}","starred_url":"https://api.github.com/users/aenario/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/aenario/subscriptions","organizations_url":"https://api.github.com/users/aenario/orgs","repos_url":"https://api.github.com/users/aenario/repos","events_url":"https://api.github.com/users/aenario/events{/privacy}","received_events_url":"https://api.github.com/users/aenario/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/aenario/cozy-data-system","description":"Data Layer for Cozy Cloud platform","fork":true,"url":"https://api.github.com/repos/aenario/cozy-data-system","forks_url":"https://api.github.com/repos/aenario/cozy-data-system/forks","keys_url":"https://api.github.com/repos/aenario/cozy-data-system/keys{/key_id}","collaborators_url":"https://api.github.com/repos/aenario/cozy-data-system/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/aenario/cozy-data-system/teams","hooks_url":"https://api.github.com/repos/aenario/cozy-data-system/hooks","issue_events_url":"https://api.github.com/repos/aenario/cozy-data-system/issues/events{/number}","events_url":"https://api.github.com/repos/aenario/cozy-data-system/events","assignees_url":"https://api.github.com/repos/aenario/cozy-data-system/assignees{/user}","branches_url":"https://api.github.com/repos/aenario/cozy-data-system/branches{/branch}","tags_url":"https://api.github.com/repos/aenario/cozy-data-system/tags","blobs_url":"https://api.github.com/repos/aenario/cozy-data-system/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/aenario/cozy-data-system/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/aenario/cozy-data-system/git/refs{/sha}","trees_url":"https://api.github.com/repos/aenario/cozy-data-system/git/trees{/sha}","statuses_url":"https://api.github.com/repos/aenario/cozy-data-system/statuses/{sha}","languages_url":"https://api.github.com/repos/aenario/cozy-data-system/languages","stargazers_url":"https://api.github.com/repos/aenario/cozy-data-system/stargazers","contributors_url":"https://api.github.com/repos/aenario/cozy-data-system/contributors","subscribers_url":"https://api.github.com/repos/aenario/cozy-data-system/subscribers","subscription_url":"https://api.github.com/repos/aenario/cozy-data-system/subscription","commits_url":"https://api.github.com/repos/aenario/cozy-data-system/commits{/sha}","git_commits_url":"https://api.github.com/repos/aenario/cozy-data-system/git/commits{/sha}","comments_url":"https://api.github.com/repos/aenario/cozy-data-system/comments{/number}","issue_comment_url":"https://api.github.com/repos/aenario/cozy-data-system/issues/comments{/number}","contents_url":"https://api.github.com/repos/aenario/cozy-data-system/contents/{+path}","compare_url":"https://api.github.com/repos/aenario/cozy-data-system/compare/{base}...{head}","merges_url":"https://api.github.com/repos/aenario/cozy-data-system/merges","archive_url":"https://api.github.com/repos/aenario/cozy-data-system/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/aenario/cozy-data-system/downloads","issues_url":"https://api.github.com/repos/aenario/cozy-data-system/issues{/number}","pulls_url":"https://api.github.com/repos/aenario/cozy-data-system/pulls{/number}","milestones_url":"https://api.github.com/repos/aenario/cozy-data-system/milestones{/number}","notifications_url":"https://api.github.com/repos/aenario/cozy-data-system/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/aenario/cozy-data-system/labels{/name}","releases_url":"https://api.github.com/repos/aenario/cozy-data-system/releases{/id}","created_at":"2013-03-12T08:57:58Z","updated_at":"2015-03-17T13:24:45Z","pushed_at":"2015-10-23T12:49:43Z","git_url":"git://github.com/aenario/cozy-data-system.git","ssh_url":"git@github.com:aenario/cozy-data-system.git","clone_url":"https://github.com/aenario/cozy-data-system.git","svn_url":"https://github.com/aenario/cozy-data-system","homepage":"cozycloud.cc","size":2070,"stargazers_count":0,"watchers_count":0,"language":"CoffeeScript","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"cozy:development","ref":"development","sha":"06ff117a94b8aa1cc37b1f9dd7e2d09db26fac42","user":{"login":"cozy","id":7690525,"avatar_url":"https://avatars.githubusercontent.com/u/7690525?v=3","gravatar_id":"","url":"https://api.github.com/users/cozy","html_url":"https://github.com/cozy","followers_url":"https://api.github.com/users/cozy/followers","following_url":"https://api.github.com/users/cozy/following{/other_user}","gists_url":"https://api.github.com/users/cozy/gists{/gist_id}","starred_url":"https://api.github.com/users/cozy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cozy/subscriptions","organizations_url":"https://api.github.com/users/cozy/orgs","repos_url":"https://api.github.com/users/cozy/repos","events_url":"https://api.github.com/users/cozy/events{/privacy}","received_events_url":"https://api.github.com/users/cozy/received_events","type":"Organization","site_admin":false},"repo":{"id":5093292,"name":"cozy-data-system","full_name":"cozy/cozy-data-system","owner":{"login":"cozy","id":7690525,"avatar_url":"https://avatars.githubusercontent.com/u/7690525?v=3","gravatar_id":"","url":"https://api.github.com/users/cozy","html_url":"https://github.com/cozy","followers_url":"https://api.github.com/users/cozy/followers","following_url":"https://api.github.com/users/cozy/following{/other_user}","gists_url":"https://api.github.com/users/cozy/gists{/gist_id}","starred_url":"https://api.github.com/users/cozy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cozy/subscriptions","organizations_url":"https://api.github.com/users/cozy/orgs","repos_url":"https://api.github.com/users/cozy/repos","events_url":"https://api.github.com/users/cozy/events{/privacy}","received_events_url":"https://api.github.com/users/cozy/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/cozy/cozy-data-system","description":"Data Layer for Cozy Platform","fork":false,"url":"https://api.github.com/repos/cozy/cozy-data-system","forks_url":"https://api.github.com/repos/cozy/cozy-data-system/forks","keys_url":"https://api.github.com/repos/cozy/cozy-data-system/keys{/key_id}","collaborators_url":"https://api.github.com/repos/cozy/cozy-data-system/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/cozy/cozy-data-system/teams","hooks_url":"https://api.github.com/repos/cozy/cozy-data-system/hooks","issue_events_url":"https://api.github.com/repos/cozy/cozy-data-system/issues/events{/number}","events_url":"https://api.github.com/repos/cozy/cozy-data-system/events","assignees_url":"https://api.github.com/repos/cozy/cozy-data-system/assignees{/user}","branches_url":"https://api.github.com/repos/cozy/cozy-data-system/branches{/branch}","tags_url":"https://api.github.com/repos/cozy/cozy-data-system/tags","blobs_url":"https://api.github.com/repos/cozy/cozy-data-system/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/cozy/cozy-data-system/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/cozy/cozy-data-system/git/refs{/sha}","trees_url":"https://api.github.com/repos/cozy/cozy-data-system/git/trees{/sha}","statuses_url":"https://api.github.com/repos/cozy/cozy-data-system/statuses/{sha}","languages_url":"https://api.github.com/repos/cozy/cozy-data-system/languages","stargazers_url":"https://api.github.com/repos/cozy/cozy-data-system/stargazers","contributors_url":"https://api.github.com/repos/cozy/cozy-data-system/contributors","subscribers_url":"https://api.github.com/repos/cozy/cozy-data-system/subscribers","subscription_url":"https://api.github.com/repos/cozy/cozy-data-system/subscription","commits_url":"https://api.github.com/repos/cozy/cozy-data-system/commits{/sha}","git_commits_url":"https://api.github.com/repos/cozy/cozy-data-system/git/commits{/sha}","comments_url":"https://api.github.com/repos/cozy/cozy-data-system/comments{/number}","issue_comment_url":"https://api.github.com/repos/cozy/cozy-data-system/issues/comments{/number}","contents_url":"https://api.github.com/repos/cozy/cozy-data-system/contents/{+path}","compare_url":"https://api.github.com/repos/cozy/cozy-data-system/compare/{base}...{head}","merges_url":"https://api.github.com/repos/cozy/cozy-data-system/merges","archive_url":"https://api.github.com/repos/cozy/cozy-data-system/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/cozy/cozy-data-system/downloads","issues_url":"https://api.github.com/repos/cozy/cozy-data-system/issues{/number}","pulls_url":"https://api.github.com/repos/cozy/cozy-data-system/pulls{/number}","milestones_url":"https://api.github.com/repos/cozy/cozy-data-system/milestones{/number}","notifications_url":"https://api.github.com/repos/cozy/cozy-data-system/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/cozy/cozy-data-system/labels{/name}","releases_url":"https://api.github.com/repos/cozy/cozy-data-system/releases{/id}","created_at":"2012-07-18T07:58:49Z","updated_at":"2015-08-12T10:26:23Z","pushed_at":"2015-10-23T12:49:44Z","git_url":"git://github.com/cozy/cozy-data-system.git","ssh_url":"git@github.com:cozy/cozy-data-system.git","clone_url":"https://github.com/cozy/cozy-data-system.git","svn_url":"https://github.com/cozy/cozy-data-system","homepage":"cozy.io","size":3193,"stargazers_count":22,"watchers_count":22,"language":"CoffeeScript","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":13,"mirror_url":null,"open_issues_count":12,"forks":13,"open_issues":12,"watchers":22,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/cozy/cozy-data-system/pulls/166"},"html":{"href":"https://github.com/cozy/cozy-data-system/pull/166"},"issue":{"href":"https://api.github.com/repos/cozy/cozy-data-system/issues/166"},"comments":{"href":"https://api.github.com/repos/cozy/cozy-data-system/issues/166/comments"},"review_comments":{"href":"https://api.github.com/repos/cozy/cozy-data-system/pulls/166/comments"},"review_comment":{"href":"https://api.github.com/repos/cozy/cozy-data-system/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/cozy/cozy-data-system/pulls/166/commits"},"statuses":{"href":"https://api.github.com/repos/cozy/cozy-data-system/statuses/550bbe4832e60033c0c0874ef241c857cdf0d7f7"}}}}